♻️ Refactor nitrate audit method and tests

Keep the audit refactor lint-clean after rebasing onto develop.

AI-assisted-by: gpt-5
This commit is contained in:
Andrey Antukh 2026-07-09 14:30:32 +02:00 committed by Marina López
parent 9f029de578
commit 9cb039070f
4 changed files with 1593 additions and 1196 deletions

View File

@ -154,7 +154,7 @@
;; COLLECTOR API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare ^:private prepare-context-from-request)
(declare prepare-context-from-request)
;; Defines a service that collects the audit/activity log using
;; internal database. Later this audit log can be transferred to
@ -183,7 +183,7 @@
(def valid-event?
(sm/validator schema:event))
(defn- prepare-context-from-request
(defn prepare-context-from-request
"Prepare backend event context from request"
[request]
(let [client-event-origin (get-client-event-origin request)
@ -412,7 +412,7 @@
(update :ip-addr d/nilv "0.0.0.0")
(update :props d/nilv {})
(update :context d/nilv {})
(assoc :source "backend")
(update :source d/nilv "backend")
(d/without-nils))]
(submit* cfg event)))
@ -429,7 +429,7 @@
(update :profile-id d/nilv uuid/zero)
(update :props d/nilv {})
(update :context d/nilv {})
(assoc :source "backend")
(update :source d/nilv "backend")
(select-keys event-keys)
(check-event))]
(db/run! cfg append-audit-entry event))))

View File

@ -20,6 +20,7 @@
[app.config :as cf]
[app.db :as db]
[app.email :as eml]
[app.http :as-alias http]
[app.http.session :as session]
[app.loggers.audit :as audit]
[app.media :as media]
@ -783,38 +784,42 @@ RETURNING id, deleted_at;")
[:map {:title "NitrateAuditEvent"}
[:name [:and [:string {:max 250}]
[:re #"[\d\w-]{1,50}"]]]
[:type {:optional true} ::sm/text]
[:profile-id ::sm/uuid]
[:props {:optional true} [:map-of :keyword :any]]])
[:props {:optional true} [:map-of :keyword :any]]
[:context {:optional true} [:map-of :keyword :any]]])
(def ^:private schema:push-audit-events-params
[:map {:title "PushAuditEventsParams"}
[:events [:vector schema:nitrate-audit-event]]])
(defn- submit-nitrate-audit-event
[cfg {:keys [name profile-id props]}]
(let [now (ct/now)]
(audit/submit* cfg {:type "action"
:name name
:profile-id profile-id
:props (or props {})
:context {}
:tracked-at now
:created-at now
:source "nitrate"
:ip-addr "0.0.0.0"})))
(sv/defmethod ::push-audit-events
"Push audit events from Nitrate to Penpot audit log"
"Push audit events from nitrate (strictly for nitrate backend
events)"
{::doc/added "2.19"
::audit/skip true
::sm/params schema:push-audit-events-params
::rpc/auth false}
[{:keys [::db/pool] :as cfg} {:keys [events]}]
(let [telemetry? (contains? cf/flags :telemetry)
audit-log? (contains? cf/flags :audit-log)
enabled? (and (not (db/read-only? pool))
(or audit-log? telemetry?))]
(when (and enabled? (seq events))
(run! (partial submit-nitrate-audit-event cfg) events))
[cfg {:keys [::rpc/request-at events] :as params}]
(let [request (-> params meta ::http/request)
context' (-> (audit/prepare-context-from-request request)
(assoc :request-id (::rpc/request-id params)))
ip-addr (::rpc/ip-addr params)]
(run! (fn [{:keys [type name profile-id props context] :as event}]
(let [context (-> (merge context context')
(d/without-nils))]
(audit/submit cfg {:type (d/nilv type "action")
:name name
:profile-id profile-id
:props (or props {})
:context context
:tracked-at request-at
:ip-addr ip-addr})))
events)
nil))

View File

@ -79,59 +79,66 @@
:enable-auto-file-snapshot
:disable-file-validation])
(defn state-init
(defn init-config
([next]
(init-config nil next))
([extra-flags next]
(let [flags (into default-flags extra-flags)]
(with-redefs [app.config/flags (flags/parse flags/default flags)
app.config/config config
app.loggers.audit/submit (constantly nil)
app.auth/derive-password identity
app.auth/verify-password (fn [a b] {:valid (= a b)})
app.common.features/get-enabled-features
(fn [& _] app.common.features/supported-features)]
(cf/validate! :exit-on-error? false)
(fs/create-dir "/tmp/penpot")
(next)))))
(defn init-system
[next]
(with-redefs [app.config/flags (flags/parse flags/default default-flags)
app.config/config config
app.loggers.audit/submit (constantly nil)
app.auth/derive-password identity
app.auth/verify-password (fn [a b] {:valid (= a b)})
app.common.features/get-enabled-features (fn [& _] app.common.features/supported-features)]
(let [templates [{:id "test"
:name "test"
:file-uri "test"
:thumbnail-uri "test"
:path (-> "backend_tests/test_files/template.penpot" io/resource fs/path)}]
system (-> (merge main/system-config main/worker-config)
(assoc-in [:app.redis/client :app.redis/uri] (:redis-uri config))
(assoc-in [::db/pool ::db/uri] (:database-uri config))
(assoc-in [::db/pool ::db/username] (:database-username config))
(assoc-in [::db/pool ::db/password] (:database-password config))
(assoc-in [:app.rpc/methods :app.setup/templates] templates)
(assoc-in [:app.rpc/methods :app.setup/templates] templates)
(update :app.rpc/rlimit assoc
:app.loggers.mattermost/reporter nil
:app.loggers.database/reporter nil)
(update :app.rpc/methods assoc
:app.setup/templates templates
:app.loggers.mattermost/reporter nil
:app.loggers.database/reporter nil)
(dissoc :app.srepl/server
:app.http/server
:app.http/route
:app.setup/templates
:app.http.oauth/handler
:app.notifications/handler
:app.loggers.mattermost/reporter
:app.loggers.database/reporter
:app.worker/cron
:app.worker/dispatcher
[:app.main/default :app.worker/runner]
[:app.main/webhook :app.worker/runner]))
_ (ig/load-namespaces system)
system (-> (ig/expand system) (ig/init))]
(try
(binding [*system* system
*pool* (:app.db/pool system)]
(next))
(finally
(ig/halt! system)))))
(cf/validate! :exit-on-error? false)
(fs/create-dir "/tmp/penpot")
(let [templates [{:id "test"
:name "test"
:file-uri "test"
:thumbnail-uri "test"
:path (-> "backend_tests/test_files/template.penpot" io/resource fs/path)}]
system (-> (merge main/system-config main/worker-config)
(assoc-in [:app.redis/client :app.redis/uri] (:redis-uri config))
(assoc-in [::db/pool ::db/uri] (:database-uri config))
(assoc-in [::db/pool ::db/username] (:database-username config))
(assoc-in [::db/pool ::db/password] (:database-password config))
(assoc-in [:app.rpc/methods :app.setup/templates] templates)
(assoc-in [:app.rpc/methods :app.setup/templates] templates)
(update :app.rpc/rlimit assoc
:app.loggers.mattermost/reporter nil
:app.loggers.database/reporter nil)
(update :app.rpc/methods assoc
:app.setup/templates templates
:app.loggers.mattermost/reporter nil
:app.loggers.database/reporter nil)
(dissoc :app.srepl/server
:app.http/server
:app.http/route
:app.setup/templates
:app.http.oauth/handler
:app.notifications/handler
:app.loggers.mattermost/reporter
:app.loggers.database/reporter
:app.worker/cron
:app.worker/dispatcher
[:app.main/default :app.worker/runner]
[:app.main/webhook :app.worker/runner]))
_ (ig/load-namespaces system)
system (-> (ig/expand system)
(ig/init))]
(try
(binding [*system* system
*pool* (:app.db/pool system)]
(next))
(finally
(ig/halt! system))))))
(def state-init
(t/compose-fixtures init-config init-system))
(defn database-reset
[next]
@ -386,29 +393,15 @@
(assoc :app.rpc/request-at (ct/now)))))))
(defn management-command!
([data]
(management-command! data nil))
([{:keys [::type] :as data} flags-to-add]
(let [flags (reduce conj cf/flags (or flags-to-add []))
resolve-management-methods
(requiring-resolve 'app.rpc/resolve-management-methods)
methods
(with-redefs [cf/flags flags]
(resolve-management-methods *system*))
[_ method-fn]
(get methods type)]
(when-not method-fn
(ex/raise :type :assertion
:code :rpc-method-not-found
:hint (str/ffmt "management rpc method '%' not found" (name type))))
(try-on! (method-fn (-> data
(dissoc ::type)
(assoc :app.rpc/request-at (ct/now))))))))
[{:keys [::type] :as data}]
(let [[_ method-fn] (get-in *system* [:app.rpc/management-methods type])]
(when-not method-fn
(ex/raise :type :assertion
:code :rpc-method-not-found
:hint (str/ffmt "management rpc method '%' not found" (name type))))
(try-on! (method-fn (-> data
(dissoc ::type)
(assoc :app.rpc/request-at (ct/now)))))))
(defn run-task!
([name]

File diff suppressed because it is too large Load Diff