mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
✨ Send more enriched events to nitrate log (#12210)
* ✨ Send more enriched events to nitrate log * ✨ Fix mr
This commit is contained in:
parent
7745c1f052
commit
76e9c132ef
@ -11,6 +11,7 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.transit :as t]
|
||||
[app.common.uri :as u]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.http.client :as http]
|
||||
@ -80,6 +81,7 @@
|
||||
"add-team-to-organization"
|
||||
"cancel-organization-invitation"
|
||||
"change-organization-advanced-permission"
|
||||
"create-export-binfile-job"
|
||||
"create-file"
|
||||
"create-organization"
|
||||
"create-organization-attribute"
|
||||
@ -90,8 +92,10 @@
|
||||
"create-team-invitation"
|
||||
"create-team-invitations"
|
||||
"create-webhook"
|
||||
"delete-file"
|
||||
"delete-font"
|
||||
"delete-organization"
|
||||
"delete-organization-member"
|
||||
"delete-project"
|
||||
"delete-team"
|
||||
"delete-team-invitation"
|
||||
@ -108,6 +112,8 @@
|
||||
"rename-organization"
|
||||
"rename-project"
|
||||
"restore-deleted-team-files"
|
||||
"success-activate-organization-sso"
|
||||
"success-apply-organization-sso-changes"
|
||||
"update-organization-invitation"
|
||||
"update-organization-permissions"
|
||||
"update-team-invitation"
|
||||
@ -116,12 +122,96 @@
|
||||
"update-team-photo"
|
||||
"verify-token"})
|
||||
|
||||
;; Props id key -> email prop filled from profile.email (nitrate path only).
|
||||
;; Some producers store ids as UUID strings (e.g. :user-who-send-invitation).
|
||||
(def ^:private id->email-prop
|
||||
{:profile-id :profile-email
|
||||
:user-id :user-email
|
||||
:member-id :member-email
|
||||
:user-who-send-invitation :user-who-send-invitation-email})
|
||||
|
||||
(defn- collect-team-ids-needing-name
|
||||
[events]
|
||||
(into #{}
|
||||
(comp (map :props)
|
||||
(keep (fn [props]
|
||||
(when-let [team-id (and (not (contains? props :team-name))
|
||||
(uuid/coerce (:team-id props)))]
|
||||
team-id))))
|
||||
events))
|
||||
|
||||
(defn- collect-profile-ids-needing-email
|
||||
[events]
|
||||
(into #{}
|
||||
(mapcat (fn [{:keys [props]}]
|
||||
(keep (fn [[id-key email-key]]
|
||||
(when-not (contains? props email-key)
|
||||
(uuid/coerce (get props id-key))))
|
||||
id->email-prop)))
|
||||
events))
|
||||
|
||||
(defn- load-team-names
|
||||
[conn ids]
|
||||
(if (seq ids)
|
||||
(let [arr (db/create-array conn "uuid" ids)
|
||||
rows (db/exec! conn ["SELECT id, name FROM team WHERE id = ANY(?)" arr])]
|
||||
(into {} (map (juxt :id :name) rows)))
|
||||
{}))
|
||||
|
||||
(defn- load-profile-emails
|
||||
[conn ids]
|
||||
(if (seq ids)
|
||||
(let [arr (db/create-array conn "uuid" ids)
|
||||
rows (db/exec! conn ["SELECT id, email FROM profile WHERE id = ANY(?)" arr])]
|
||||
(into {} (map (juxt :id :email) rows)))
|
||||
{}))
|
||||
|
||||
(defn- maybe-assoc-team-name
|
||||
[event team-names]
|
||||
(let [props (:props event)]
|
||||
(if-let [team-name (and (not (contains? props :team-name))
|
||||
(when-let [team-id (uuid/coerce (:team-id props))]
|
||||
(get team-names team-id)))]
|
||||
(assoc-in event [:props :team-name] team-name)
|
||||
event)))
|
||||
|
||||
(defn- maybe-assoc-emails
|
||||
[event emails]
|
||||
(update event :props
|
||||
(fn [props]
|
||||
(reduce-kv (fn [props id-key email-key]
|
||||
(if-let [email (and (not (contains? props email-key))
|
||||
(when-let [id (uuid/coerce (get props id-key))]
|
||||
(get emails id)))]
|
||||
(assoc props email-key email)
|
||||
props))
|
||||
props
|
||||
id->email-prop))))
|
||||
|
||||
(defn- enrich-events-for-nitrate
|
||||
"Enrich allowlisted events before nitrate ingest:
|
||||
1. Fill missing :team-name from team id (including soft-deleted).
|
||||
2. Fill missing emails from matching id keys (including soft-deleted
|
||||
profiles): :profile-email, :user-email, :member-email,
|
||||
:user-who-send-invitation-email."
|
||||
[{:keys [::db/conn]} events]
|
||||
(let [team-ids (collect-team-ids-needing-name events)
|
||||
profile-ids (collect-profile-ids-needing-email events)
|
||||
team-names (load-team-names conn team-ids)
|
||||
emails (load-profile-emails conn profile-ids)]
|
||||
(mapv (fn [event]
|
||||
(-> event
|
||||
(maybe-assoc-team-name team-names)
|
||||
(maybe-assoc-emails emails)))
|
||||
events)))
|
||||
|
||||
(defn- send-to-nitrate!
|
||||
[cfg rows]
|
||||
(when-let [events (->> rows
|
||||
(filterv #(contains? event-names-for-nitrate (:name %)))
|
||||
(not-empty))]
|
||||
(nitrate/call cfg :ingest-audit-log {:events events})))
|
||||
(nitrate/call cfg :ingest-audit-log
|
||||
{:events (enrich-events-for-nitrate cfg events)})))
|
||||
|
||||
(defn- mark-archived!
|
||||
[{:keys [::db/conn]} rows]
|
||||
|
||||
@ -1095,7 +1095,8 @@
|
||||
:profile-id profile-id})
|
||||
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:project-id (:project-id file)
|
||||
{::audit/props {:team-id (:id team)
|
||||
:project-id (:project-id file)
|
||||
:name (:name file)
|
||||
:created-at (:created-at file)
|
||||
:modified-at (:modified-at file)}})))
|
||||
|
||||
@ -110,6 +110,25 @@
|
||||
;; EXPORT
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- single-team-id
|
||||
"The team id when every `file-ids` entry resolves and they share one
|
||||
team. Otherwise nil."
|
||||
[cfg file-ids]
|
||||
(when (seq file-ids)
|
||||
(db/run! cfg
|
||||
(fn [{:keys [::db/conn]}]
|
||||
(let [arr (db/create-array conn "uuid" file-ids)
|
||||
rows (db/exec! conn
|
||||
["SELECT f.id AS file_id, t.id AS team_id
|
||||
FROM file AS f
|
||||
JOIN project AS p ON (p.id = f.project_id)
|
||||
JOIN team AS t ON (t.id = p.team_id)
|
||||
WHERE f.id = ANY(?)" arr])
|
||||
teams (into #{} (map :team-id) rows)]
|
||||
(when (and (= (count file-ids) (count rows))
|
||||
(= 1 (count teams)))
|
||||
(first teams)))))))
|
||||
|
||||
(sv/defmethod ::create-export-binfile-job
|
||||
"Create a durable job that exports a set of files as a `.penpot`
|
||||
package.
|
||||
@ -146,11 +165,13 @@
|
||||
::quotes/profile-id profile-id})
|
||||
|
||||
(let [summary (get-job-summary cfg (submit-job cfg job-def :export-binfile
|
||||
params profile-id))]
|
||||
params profile-id))
|
||||
team-id (single-team-id cfg file-ids)]
|
||||
(with-meta summary
|
||||
{::audit/props {:job-id (:id summary)
|
||||
:files (count file-ids)
|
||||
:export-type (d/name (:export-type params))}}))))
|
||||
{::audit/props (cond-> {:job-id (:id summary)
|
||||
:files (count file-ids)
|
||||
:export-type (d/name (:export-type params))}
|
||||
team-id (assoc :team-id team-id))}))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; IMPORT
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.profile :as profile]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
[app.rpc.permissions :as perms]
|
||||
[app.rpc.quotes :as quotes]
|
||||
[app.setup :as-alias setup]
|
||||
@ -867,11 +868,13 @@
|
||||
:code :non-deletable-team
|
||||
:hint "impossible to delete default team"))
|
||||
|
||||
(let [delay (ldel/get-deletion-delay team)
|
||||
team (db/update! conn :team
|
||||
{:deleted-at (ct/in-future delay)}
|
||||
{:id team-id}
|
||||
{::db/return-keys true})]
|
||||
(let [delay (ldel/get-deletion-delay team)
|
||||
organization-id (dm/get-in team [:organization :id])
|
||||
team-name (:name team)
|
||||
team (db/update! conn :team
|
||||
{:deleted-at (ct/in-future delay)}
|
||||
{:id team-id}
|
||||
{::db/return-keys true})]
|
||||
|
||||
;; Api call to nitrate
|
||||
(when (contains? cf/flags :admin-console)
|
||||
@ -882,7 +885,10 @@
|
||||
::jobs/params {:object :team
|
||||
:deleted-at (:deleted-at team)
|
||||
:id team-id}})
|
||||
team)))
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:organization-id organization-id
|
||||
:team-id team-id
|
||||
:team-name team-name}}))))
|
||||
|
||||
(def ^:private schema:delete-team
|
||||
[:map {:title "delete-team"}
|
||||
@ -894,8 +900,7 @@
|
||||
::sm/params schema:delete-team
|
||||
::db/transaction true}
|
||||
[cfg {:keys [::rpc/profile-id id] :as params}]
|
||||
(delete-team cfg {:team-id id :profile-id profile-id})
|
||||
nil)
|
||||
(delete-team cfg {:team-id id :profile-id profile-id}))
|
||||
|
||||
;; --- Mutation: Team Update Role
|
||||
|
||||
|
||||
@ -26,18 +26,25 @@
|
||||
(def ^:private archive-uri "http://nexus.example/archive")
|
||||
|
||||
(defn- insert-audit-row!
|
||||
[profile-id name]
|
||||
(th/db-insert! :audit-log
|
||||
{:id (uuid/next)
|
||||
:name name
|
||||
:type "action"
|
||||
:source "backend"
|
||||
:profile-id profile-id
|
||||
:ip-addr (db/inet "127.0.0.1")
|
||||
:props (db/tjson {:team-id (uuid/next)})
|
||||
:context (db/tjson {})
|
||||
:tracked-at (ct/now)
|
||||
:created-at (ct/now)}))
|
||||
([profile-id name]
|
||||
(insert-audit-row! profile-id name {:team-id (uuid/next)}))
|
||||
([profile-id name props]
|
||||
(th/db-insert! :audit-log
|
||||
{:id (uuid/next)
|
||||
:name name
|
||||
:type "action"
|
||||
:source "backend"
|
||||
:profile-id profile-id
|
||||
:ip-addr (db/inet "127.0.0.1")
|
||||
:props (db/tjson props)
|
||||
:context (db/tjson {})
|
||||
:tracked-at (ct/now)
|
||||
:created-at (ct/now)})))
|
||||
|
||||
(defn- nitrate-event-props
|
||||
[nitrate-mock]
|
||||
(let [[_ _ params] (:call-args @nitrate-mock)]
|
||||
(:props (first (:events params)))))
|
||||
|
||||
(t/deftest archive-events-sends-to-nitrate-then-nexus
|
||||
;; Create profile before enabling :admin-console — system nitrate
|
||||
@ -231,3 +238,113 @@
|
||||
(catch Throwable cause
|
||||
cause)))]
|
||||
(t/is (= :task-not-configured (:code data))))))
|
||||
|
||||
(t/deftest archive-events-enriches-team-name-and-emails
|
||||
(let [actor (th/create-profile* 1 {:is-active true})
|
||||
member (th/create-profile* 2 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id actor)})]
|
||||
(with-redefs [cf/flags #{:admin-console}]
|
||||
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
|
||||
(insert-audit-row! (:id actor) "create-project"
|
||||
{:team-id (:id team)
|
||||
:profile-id (:id actor)
|
||||
:user-id (:id member)
|
||||
:member-id (:id member)
|
||||
;; Producers store this as a string UUID.
|
||||
:user-who-send-invitation (str (:id actor))})
|
||||
(th/run-task! :audit-log-archive {})
|
||||
(t/is (:called? @nitrate-mock))
|
||||
(let [props (nitrate-event-props nitrate-mock)]
|
||||
(t/is (= (:name team) (:team-name props)))
|
||||
(t/is (= (:email actor) (:profile-email props)))
|
||||
(t/is (= (:email member) (:user-email props)))
|
||||
(t/is (= (:email member) (:member-email props)))
|
||||
(t/is (= (:email actor) (:user-who-send-invitation-email props))))
|
||||
(let [rows (th/db-exec! ["select * from audit_log where archived_at is not null"])]
|
||||
(t/is (= 1 (count rows))))))))
|
||||
|
||||
(t/deftest archive-events-enriches-soft-deleted-team-and-profile
|
||||
(let [actor (th/create-profile* 1 {:is-active true})
|
||||
other (th/create-profile* 2 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id actor)})]
|
||||
(th/db-update! :team {:deleted-at (ct/now)} {:id (:id team)})
|
||||
(th/db-update! :profile {:deleted-at (ct/now)} {:id (:id other)})
|
||||
(with-redefs [cf/flags #{:admin-console}]
|
||||
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
|
||||
(insert-audit-row! (:id actor) "create-project"
|
||||
{:team-id (:id team)
|
||||
:profile-id (:id other)})
|
||||
(th/run-task! :audit-log-archive {})
|
||||
(let [props (nitrate-event-props nitrate-mock)]
|
||||
(t/is (= (:name team) (:team-name props)))
|
||||
(t/is (= (:email other) (:profile-email props))))
|
||||
(let [rows (th/db-exec! ["select * from audit_log where archived_at is not null"])]
|
||||
(t/is (= 1 (count rows))))))))
|
||||
|
||||
(t/deftest archive-events-does-not-overwrite-existing-enrich-props
|
||||
(let [actor (th/create-profile* 1 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id actor)})]
|
||||
(with-redefs [cf/flags #{:admin-console}]
|
||||
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
|
||||
(insert-audit-row! (:id actor) "create-project"
|
||||
{:team-id (:id team)
|
||||
:team-name "already-set-name"
|
||||
:profile-id (:id actor)
|
||||
:profile-email "already-set@example.com"
|
||||
:user-id (:id actor)
|
||||
:user-email "already-user@example.com"
|
||||
:member-id (:id actor)
|
||||
:member-email "already-member@example.com"
|
||||
:user-who-send-invitation (str (:id actor))
|
||||
:user-who-send-invitation-email "already-inviter@example.com"})
|
||||
(th/run-task! :audit-log-archive {})
|
||||
(let [props (nitrate-event-props nitrate-mock)]
|
||||
(t/is (= "already-set-name" (:team-name props)))
|
||||
(t/is (= "already-set@example.com" (:profile-email props)))
|
||||
(t/is (= "already-user@example.com" (:user-email props)))
|
||||
(t/is (= "already-member@example.com" (:member-email props)))
|
||||
(t/is (= "already-inviter@example.com"
|
||||
(:user-who-send-invitation-email props))))))))
|
||||
|
||||
(t/deftest archive-events-enrich-skips-missing-and-non-uuid-ids
|
||||
(let [actor (th/create-profile* 1 {:is-active true})]
|
||||
(with-redefs [cf/flags #{:admin-console}]
|
||||
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
|
||||
(insert-audit-row! (:id actor) "create-project"
|
||||
{:team-id (uuid/next)
|
||||
:profile-id (uuid/next)
|
||||
:user-id "not-a-uuid"
|
||||
:member-id 42})
|
||||
(th/run-task! :audit-log-archive {})
|
||||
(t/is (:called? @nitrate-mock))
|
||||
(let [props (nitrate-event-props nitrate-mock)]
|
||||
(t/is (nil? (:team-name props)))
|
||||
(t/is (nil? (:profile-email props)))
|
||||
(t/is (nil? (:user-email props)))
|
||||
(t/is (nil? (:member-email props)))
|
||||
(t/is (= "not-a-uuid" (:user-id props)))
|
||||
(t/is (= 42 (:member-id props))))
|
||||
(let [rows (th/db-exec! ["select * from audit_log where archived_at is not null"])]
|
||||
(t/is (= 1 (count rows))))))))
|
||||
|
||||
(t/deftest archive-events-forwards-export-job-not-legacy-export
|
||||
(let [actor (th/create-profile* 1 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id actor)})]
|
||||
(with-redefs [cf/flags #{:admin-console}]
|
||||
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
|
||||
(insert-audit-row! (:id actor) "create-export-binfile-job"
|
||||
{:team-id (:id team)
|
||||
:job-id (uuid/next)
|
||||
:files 1
|
||||
:export-type "detach-libraries"})
|
||||
(insert-audit-row! (:id actor) "export-binfile"
|
||||
{:file-id (uuid/next)})
|
||||
(th/run-task! :audit-log-archive {})
|
||||
(let [[_ _ params] (:call-args @nitrate-mock)
|
||||
events (:events params)
|
||||
props (:props (first events))]
|
||||
(t/is (= ["create-export-binfile-job"] (mapv :name events)))
|
||||
(t/is (= (:id team) (:team-id props)))
|
||||
(t/is (= (:name team) (:team-name props))))
|
||||
(let [rows (th/db-exec! ["select * from audit_log where archived_at is not null"])]
|
||||
(t/is (= 2 (count rows))))))))
|
||||
|
||||
@ -173,11 +173,44 @@
|
||||
out (create-export-binfile-job (:id profile) #{file-id})
|
||||
props (::audit/props (meta (:result out)))]
|
||||
|
||||
(t/testing "the audit of the call points at the job, not at its files"
|
||||
(t/testing "the audit of the call points at the job and its team"
|
||||
(t/is (= (:id (:result out)) (:job-id props)))
|
||||
(t/is (= 1 (:files props)))
|
||||
(t/is (= "detach-libraries" (:export-type props)))
|
||||
(t/is (= #{:job-id :files :export-type} (set (keys props)))))))
|
||||
(t/is (= (:default-team-id profile) (:team-id props)))
|
||||
(t/is (= #{:job-id :files :export-type :team-id} (set (keys props))))
|
||||
(t/is (not (contains? (:result out) :team-id))))))
|
||||
|
||||
(t/deftest create-export-binfile-job-records-the-team-when-files-share-one
|
||||
(let [profile (th/create-profile* 1)
|
||||
file-a (th/create-file* 1 {:profile-id (:id profile)
|
||||
:project-id (:default-project-id profile)})
|
||||
file-b (th/create-file* 2 {:profile-id (:id profile)
|
||||
:project-id (:default-project-id profile)})
|
||||
out (create-export-binfile-job (:id profile)
|
||||
#{(:id file-a) (:id file-b)})
|
||||
props (::audit/props (meta (:result out)))]
|
||||
(t/is (th/success? out))
|
||||
(t/is (= (:default-team-id profile) (:team-id props)))
|
||||
(t/is (= 2 (:files props)))
|
||||
(t/is (not (contains? (:result out) :team-id)))))
|
||||
|
||||
(t/deftest create-export-binfile-job-omits-the-team-when-files-span-teams
|
||||
(let [profile (th/create-profile* 1)
|
||||
team (th/create-team* 1 {:profile-id (:id profile)})
|
||||
project (th/create-project* 1 {:profile-id (:id profile)
|
||||
:team-id (:id team)})
|
||||
file-a (th/create-file* 1 {:profile-id (:id profile)
|
||||
:project-id (:default-project-id profile)})
|
||||
file-b (th/create-file* 2 {:profile-id (:id profile)
|
||||
:project-id (:id project)})
|
||||
out (create-export-binfile-job (:id profile)
|
||||
#{(:id file-a) (:id file-b)})
|
||||
props (::audit/props (meta (:result out)))]
|
||||
(t/is (th/success? out))
|
||||
(t/is (nil? (:team-id props)))
|
||||
(t/is (= #{:job-id :files :export-type} (set (keys props))))
|
||||
(t/is (not (contains? (:result out) :team-id)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; IMPORT
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
[app.db.sql :as sql]
|
||||
[app.features.fdata :as fdata]
|
||||
[app.http :as http]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.storage :as sto]
|
||||
@ -811,6 +812,20 @@
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (th/ex-of-type? error :not-found))))
|
||||
|
||||
(t/deftest delete-file-audit-includes-team-id
|
||||
;; Assert result metadata directly: wrap-audit is inactive in the
|
||||
;; default test flags, so mocking audit/submit would never see this.
|
||||
(let [profile (th/create-profile* 1)
|
||||
file (th/create-file* 1 {:project-id (:default-project-id profile)
|
||||
:profile-id (:id profile)})
|
||||
result (db/tx-run! th/*system*
|
||||
(fn [cfg]
|
||||
(#'files/delete-file cfg {:id (:id file)
|
||||
:profile-id (:id profile)})))
|
||||
props (::audit/props (meta result))]
|
||||
(t/is (= (:default-team-id profile) (:team-id props)))
|
||||
(t/is (= (:project-id file) (:project-id props)))))
|
||||
|
||||
(t/deftest permissions-checks-set-file-shared
|
||||
(let [profile1 (th/create-profile* 1)
|
||||
profile2 (th/create-profile* 2)
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
[app.db :as db]
|
||||
[app.email.blacklist :as email.blacklist]
|
||||
[app.http :as http]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.nitrate :as nitrate]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
@ -928,6 +929,38 @@
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 7 (:processed result)))))))
|
||||
|
||||
(t/deftest delete-team-audit-includes-organization-id
|
||||
;; Assert result metadata directly: wrap-audit is inactive in the
|
||||
;; default test flags, so mocking audit/submit would never see this.
|
||||
(let [owner (th/create-profile* 1 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id owner)})
|
||||
organization-id (uuid/next)
|
||||
organization {:id organization-id
|
||||
:name "Acme"
|
||||
:slug "acme"
|
||||
:owner-id (:id owner)
|
||||
:avatar-bg-url "https://example.com/avatar.svg"
|
||||
:permissions {:delete-teams "onlyOwners"}}]
|
||||
(with-redefs [cf/flags (conj cf/flags :admin-console)
|
||||
nitrate/call
|
||||
(fn [_cfg method params]
|
||||
(case method
|
||||
:get-team-organization
|
||||
(when (= (:id team) (:team-id params))
|
||||
{:id (:id team)
|
||||
:is-your-penpot false
|
||||
:organization organization})
|
||||
:delete-team nil
|
||||
nil))]
|
||||
(let [result (db/tx-run! th/*system*
|
||||
(fn [cfg]
|
||||
(teams/delete-team cfg {:team-id (:id team)
|
||||
:profile-id (:id owner)})))
|
||||
props (::audit/props (meta result))]
|
||||
(t/is (= organization-id (:organization-id props)))
|
||||
(t/is (= (:id team) (:team-id props)))
|
||||
(t/is (= (:name team) (:team-name props)))))))
|
||||
|
||||
(t/deftest create-team-access-request
|
||||
(with-mocks [mock {:target 'app.email/send :return nil}]
|
||||
(let [owner (th/create-profile* 1 {:is-active true :email "owner@bar.com"})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user