🐛 Add nitrate management methods (#11962)

This commit is contained in:
Marina López 2026-09-29 16:05:27 +02:00 committed by GitHub
parent eb2576b8c0
commit 90ab142f00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 132 additions and 0 deletions

View File

@ -78,6 +78,13 @@
AND t.is_default IS FALSE
AND t.deleted_at IS NULL;")
(def ^:private sql:get-member-teams
"SELECT t.id, t.name, t.is_default
FROM team AS t
JOIN team_profile_rel AS tpr ON t.id = tpr.team_id
WHERE tpr.profile_id = ?
AND t.deleted_at IS NULL;")
;; ---- API: get-penpot-version
(def ^:private schema:get-penpot-version-result
@ -107,6 +114,13 @@
(def ^:private schema:get-teams-result
[:vector schema:team])
(def ^:private schema:get-member-teams-result
[:vector
[:map
[:id ::sm/uuid]
[:name ::sm/text]
[:is-default ::sm/boolean]]])
(sv/defmethod ::get-teams
"List teams for which current user is owner"
{::doc/added "2.18"
@ -118,6 +132,51 @@
(->> (db/exec! cfg [sql:get-teams current-user-id])
(map #(select-keys % [:id :name])))))
(sv/defmethod ::get-member-teams
"List all active teams where the current user is a member"
{::doc/added "2.18"
::sm/params [:map]
::sm/result schema:get-member-teams-result
::nitrate/sso false}
[cfg {:keys [::rpc/profile-id]}]
(let [current-user-id (-> (profile/get-profile cfg profile-id) :id)]
(db/exec! cfg [sql:get-member-teams current-user-id])))
;; ---- API: update-profile-theme
(def ^:private schema:update-profile-theme
[:map {:title "update-profile-theme"}
[:theme [:enum "light" "dark" "system"]]])
(sv/defmethod ::update-profile-theme
"Update the current user's theme"
{::doc/added "2.18"
::sm/params schema:update-profile-theme
::db/transaction true
::nitrate/sso false}
[{:keys [::db/conn]} {:keys [::rpc/profile-id theme]}]
(profile/get-profile conn profile-id ::db/for-update true)
(db/update! conn :profile
{:theme theme}
{:id profile-id}
{::db/return-keys false})
nil)
;; ---- API: update-profile-props
(def ^:private schema:update-profile-props
[:map {:title "update-profile-props"}
[:props profile/schema:props-writeable]])
(sv/defmethod ::update-profile-props
"Merge writable properties into the current user's profile"
{::doc/added "2.18"
::sm/params schema:update-profile-props
::db/transaction true
::nitrate/sso false}
[cfg {:keys [::rpc/profile-id props]}]
(profile/update-profile-props cfg profile-id props))
;; ---- API: upload-organization-logo
(def ^:private schema:upload-organization-logo

View File

@ -17,6 +17,7 @@
[app.msgbus :as mbus]
[app.nitrate :as nitrate]
[app.rpc :as-alias rpc]
[app.rpc.commands.profile :as profile]
[app.util.ssrf :as ssrf]
[app.worker :as wrk]
[backend-tests.helpers :as th]
@ -225,6 +226,78 @@
(t/is (= #{(:name owned-team)}
(->> out :result (map :name) set))))))
(t/deftest get-member-teams-returns-all-active-memberships
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
(let [profile (th/create-profile* 1 {:is-active true})
other (th/create-profile* 2 {:is-active true})
default-team (th/db-get :team {:id (:default-team-id profile)})
owned-team (th/create-team* 1 {:profile-id (:id profile)})
member-team (th/create-team* 2 {:profile-id (:id other)})
_ (th/create-team-role* {:team-id (:id member-team)
:profile-id (:id profile)
:role :editor})
deleted-team (th/create-team* 3 {:profile-id (:id profile)})
_ (th/db-update! :team
{:deleted-at (ct/now)}
{:id (:id deleted-team)})
out (th/management-command! {::th/type :get-member-teams
::rpc/profile-id (:id profile)})]
(t/is (th/success? out))
(let [teams-by-id (->> out :result (d/index-by :id))]
(t/is (= #{(:id default-team) (:id owned-team) (:id member-team)}
(set (keys teams-by-id))))
(t/is (true? (get-in teams-by-id [(:id default-team) :is-default])))
(t/is (false? (get-in teams-by-id [(:id owned-team) :is-default])))
(t/is (false? (get-in teams-by-id [(:id member-team) :is-default])))))))
(t/deftest update-profile-theme-updates-only-theme
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
(let [profile (th/create-profile* 1 {:is-active true
:fullname "Nitrate User"
:lang "es"
:theme "light"})
_ (th/db-update! :profile
{:lang "es"}
{:id (:id profile)})
out (th/management-command! {::th/type :update-profile-theme
::rpc/profile-id (:id profile)
:theme "dark"})
saved (-> (th/db-get :profile {:id (:id profile)})
(profile/decode-row))]
(t/is (th/success? out))
(t/is (= "dark" (:theme saved)))
(t/is (= "Nitrate User" (:fullname saved)))
(t/is (= "es" (:lang saved))))))
(t/deftest update-profile-theme-rejects-invalid-theme
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
(let [profile (th/create-profile* 1 {:is-active true})
out (th/management-command! {::th/type :update-profile-theme
::rpc/profile-id (:id profile)
:theme "invalid"})
error (:error out)]
(t/is (th/ex-info? error))
(t/is (th/ex-of-type? error :validation))
(t/is (th/ex-of-code? error :params-validation)))))
(t/deftest update-profile-props-merges-onboarding-props
(with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}]
(let [profile (th/create-profile* 1 {:is-active true})
out (th/management-command! {::th/type :update-profile-props
::rpc/profile-id (:id profile)
:props {:nitrate-onboarding-viewed true
:onboarding-questions-answered true
:onboarding-questions
{:role "developer"
:company-size "2-100"}}})
saved (-> (th/db-get :profile {:id (:id profile)})
(profile/decode-row))]
(t/is (th/success? out))
(t/is (true? (get-in saved [:props :nitrate-onboarding-viewed])))
(t/is (true? (get-in saved [:props :onboarding-questions-answered])))
(t/is (= {:role "developer" :company-size "2-100"}
(get-in saved [:props :onboarding-questions]))))))
(t/deftest notify-team-change-publishes-event
(let [team-id (uuid/random)
organization-id (uuid/random)