mirror of
https://github.com/penpot/penpot.git
synced 2026-11-12 13:00:37 +00:00
✨ Add the file to plugin and MCP audit events (#12972)
* ✨ Add the file and MCP session to plugin audit events Events sent by plugins and the MCP plugin only carried their origin, so the multi-file and dashboard MCP workflows could not be measured, nor duplicated events from several open tabs spotted. The context of se/event and se/add-event now carries the file-id of the file the action ran in (absent on the dashboard) and, for the MCP, the mcp-session-id of the tab connection that ran it. Closes #12902 AI-assisted-by: claude-opus-5-5 * ♻️ Drop the MCP session id from plugin audit events Every audit event already carries the session-id of the tab in its context, and the MCP session id is only a hash of it, so it adds nothing to group events by tab. AI-assisted-by: claude-opus-5-5 * 🐛 Use a reified event in the system events test ptk/data-event does not implement IWithMeta, so add-event failed on it. Plugin actions emit reified events, which carry metadata. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
ca616a39ed
commit
0d05cfaf5e
@ -10,31 +10,35 @@
|
||||
[app.main.store :as st]
|
||||
[app.plugins.register :as r]))
|
||||
|
||||
(defn- get-origin
|
||||
[plugin-id]
|
||||
(if (= plugin-id r/mcp-plugin-id) "mcp" "plugin"))
|
||||
|
||||
(defn- get-context
|
||||
"Returns the audit context of an action run by a plugin: the file it ran
|
||||
in (none on the dashboard) and, for plugins other than the MCP, the
|
||||
plugin name and URL. Events drop the nil values."
|
||||
[state plugin-id]
|
||||
(let [file-id (:current-file-id state)]
|
||||
(if (= plugin-id r/mcp-plugin-id)
|
||||
{:file-id file-id}
|
||||
(let [plugin-data (r/get-plugin-data state plugin-id)]
|
||||
{:file-id file-id
|
||||
:plugin-name (:name plugin-data)
|
||||
:plugin-url (:url plugin-data)}))))
|
||||
|
||||
;; Formats an event from the plugin system
|
||||
(defn event
|
||||
[plugin-id name & {:as props}]
|
||||
(if (= plugin-id r/mcp-plugin-id)
|
||||
(-> props
|
||||
(assoc ::ev/name name)
|
||||
(assoc ::ev/origin "mcp")
|
||||
(ev/event))
|
||||
|
||||
(let [plugin-data (r/get-plugin-data @st/state plugin-id)]
|
||||
(-> props
|
||||
(assoc ::ev/name name)
|
||||
(assoc ::ev/origin "plugin")
|
||||
(assoc ::ev/context
|
||||
{:plugin-name (:name plugin-data)
|
||||
:plugin-url (:url plugin-data)})
|
||||
(ev/event)))))
|
||||
(-> props
|
||||
(assoc ::ev/name name)
|
||||
(assoc ::ev/origin (get-origin plugin-id))
|
||||
(assoc ::ev/context (get-context @st/state plugin-id))
|
||||
(ev/event)))
|
||||
|
||||
(defn add-event
|
||||
[event plugin-id]
|
||||
(let [plugin-data (r/get-plugin-data @st/state plugin-id)]
|
||||
(with-meta
|
||||
event
|
||||
(if (= plugin-id r/mcp-plugin-id)
|
||||
{::ev/origin "mcp"}
|
||||
{::ev/origin "plugin"
|
||||
::ev/context {:plugin-name (:name plugin-data)
|
||||
:plugin-url (:url plugin-data)}}))))
|
||||
(with-meta
|
||||
event
|
||||
{::ev/origin (get-origin plugin-id)
|
||||
::ev/context (get-context @st/state plugin-id)}))
|
||||
|
||||
52
frontend/test/frontend_tests/plugins/system_events_test.cljs
Normal file
52
frontend/test/frontend_tests/plugins/system_events_test.cljs
Normal file
@ -0,0 +1,52 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
(ns frontend-tests.plugins.system-events-test
|
||||
(:require
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.register :as r]
|
||||
[app.plugins.system-events :as se]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def ^:private plugin-id "00000000-0000-0000-0000-000000000000")
|
||||
|
||||
(defn- set-state
|
||||
[file-id]
|
||||
(set! st/state
|
||||
(atom {:current-file-id file-id
|
||||
:profile {:props {:plugins {:data {plugin-id {:name "Plugin"
|
||||
:url "https://plugin.test"}}}}}})))
|
||||
|
||||
(t/deftest mcp-event-carries-file
|
||||
(let [file-id (random-uuid)]
|
||||
(set-state file-id)
|
||||
(let [data (deref (se/event r/mcp-plugin-id "create-version" :file-id file-id))]
|
||||
(t/is (= "mcp" (::ev/origin data)))
|
||||
(t/is (= {:file-id file-id} (::ev/context data)))
|
||||
(t/is (= file-id (:file-id data))))))
|
||||
|
||||
(t/deftest plugin-event-carries-file-and-plugin
|
||||
(let [file-id (random-uuid)]
|
||||
(set-state file-id)
|
||||
(t/is (= {:file-id file-id
|
||||
:plugin-name "Plugin"
|
||||
:plugin-url "https://plugin.test"}
|
||||
(::ev/context (deref (se/event plugin-id "add-flow")))))))
|
||||
|
||||
(t/deftest mcp-add-event-carries-file
|
||||
(let [file-id (random-uuid)]
|
||||
(set-state file-id)
|
||||
(t/is (= {::ev/origin "mcp"
|
||||
::ev/context {:file-id file-id}}
|
||||
(meta (se/add-event (ptk/reify ::test) r/mcp-plugin-id))))))
|
||||
|
||||
(t/deftest event-has-no-file-on-the-dashboard
|
||||
(set-state nil)
|
||||
(t/is (nil? (-> (deref (se/event r/mcp-plugin-id "create-project"))
|
||||
::ev/context
|
||||
:file-id))))
|
||||
@ -101,6 +101,7 @@
|
||||
[frontend-tests.plugins.read-only-test]
|
||||
[frontend-tests.plugins.register-test]
|
||||
[frontend-tests.plugins.shape-bugfixes-test]
|
||||
[frontend-tests.plugins.system-events-test]
|
||||
[frontend-tests.plugins.text-test]
|
||||
[frontend-tests.plugins.tokens-test]
|
||||
[frontend-tests.plugins.user-test]
|
||||
@ -301,6 +302,7 @@
|
||||
'frontend-tests.plugins.read-only-test
|
||||
'frontend-tests.plugins.register-test
|
||||
'frontend-tests.plugins.shape-bugfixes-test
|
||||
'frontend-tests.plugins.system-events-test
|
||||
'frontend-tests.plugins.text-test
|
||||
'frontend-tests.plugins.tokens-test
|
||||
'frontend-tests.plugins.user-test
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user