🐛 Fix incorrect message on sending invitation to a member

This commit is contained in:
Andrey Antukh 2023-07-10 16:56:34 +02:00
parent ea753da0ae
commit 23c8043f34
4 changed files with 52 additions and 49 deletions

View file

@ -57,6 +57,7 @@
- Fix focus handling on comments edition [Taiga #5560](https://tree.taiga.io/project/penpot/issue/5560) - Fix focus handling on comments edition [Taiga #5560](https://tree.taiga.io/project/penpot/issue/5560)
- Fix incorrect fullname use on registring user after OIDC authentication [Taiga #5517](https://tree.taiga.io/project/penpot/issue/5517) - Fix incorrect fullname use on registring user after OIDC authentication [Taiga #5517](https://tree.taiga.io/project/penpot/issue/5517)
- Fix incorrect modified-at on project after import file [Taiga #5268](https://tree.taiga.io/project/penpot/issue/5268) - Fix incorrect modified-at on project after import file [Taiga #5268](https://tree.taiga.io/project/penpot/issue/5268)
- Fix incorrect message after sending invitation to already member [Taiga 5599](https://tree.taiga.io/project/penpot/issue/5599)
### :arrow_up: Deps updates ### :arrow_up: Deps updates

View file

@ -10,6 +10,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@ -719,29 +720,22 @@
itoken)))) itoken))))
(s/def ::email ::us/email) (def ^:private schema:create-team-invitations
(s/def ::emails ::us/set-of-valid-emails) [:map {:title "create-team-invitations"}
(s/def ::create-team-invitations [:team-id ::sm/uuid]
(s/keys :req [::rpc/profile-id] [:role [::sm/one-of #{:owner :admin :editor}]]
:req-un [::team-id ::role] [:emails ::sm/set-of-emails]])
:opt-un [::email ::emails]))
(sv/defmethod ::create-team-invitations (sv/defmethod ::create-team-invitations
"A rpc call that allow to send a single or multiple invitations to "A rpc call that allow to send a single or multiple invitations to
join the team." join the team."
{::doc/added "1.17"} {::doc/added "1.17"
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id email emails role] :as params}] ::sm/params schema:create-team-invitations}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id emails role] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [perms (get-permissions conn profile-id team-id) (let [perms (get-permissions conn profile-id team-id)
profile (db/get-by-id conn :profile profile-id) profile (db/get-by-id conn :profile profile-id)
team (db/get-by-id conn :team team-id) team (db/get-by-id conn :team team-id)]
;; Members emails. We don't re-send inviation to already existing members
member? (into #{}
(map :email)
(db/exec! conn [sql:team-members team-id]))
emails (cond-> (or emails #{}) (string? email) (conj email))]
(run! (partial quotes/check-quote! conn) (run! (partial quotes/check-quote! conn)
(list {::quotes/id ::quotes/invitations-per-team (list {::quotes/id ::quotes/invitations-per-team
@ -764,9 +758,13 @@
:hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) :hint "looks like the profile has reported repeatedly as spam or has permanent bounces"))
(let [cfg (assoc cfg ::db/conn conn) (let [cfg (assoc cfg ::db/conn conn)
invitations (into [] members (->> (db/exec! conn [sql:team-members team-id])
(into #{} (map :email)))
invitations (into #{}
(comp (comp
(remove member?) ;; We don't re-send inviation to already existing members
(remove (partial contains? members))
(map (fn [email] (map (fn [email]
{:email (str/lower email) {:email (str/lower email)
:team team :team team
@ -774,7 +772,8 @@
:role role})) :role role}))
(keep (partial create-invitation cfg))) (keep (partial create-invitation cfg)))
emails)] emails)]
(with-meta invitations (with-meta {:total (count invitations)
:invitations invitations}
{::audit/props {:invitations (count invitations)}}))))) {::audit/props {:invitations (count invitations)}})))))

View file

@ -37,7 +37,7 @@
:role :editor}] :role :editor}]
;; invite external user without complaints ;; invite external user without complaints
(let [data (assoc data :email "foo@bar.com") (let [data (assoc data :emails ["foo@bar.com"])
out (th/command! data) out (th/command! data)
;; retrieve the value from the database and check its content ;; retrieve the value from the database and check its content
invitation (db/exec-one! invitation (db/exec-one!
@ -52,7 +52,7 @@
;; invite internal user without complaints ;; invite internal user without complaints
(th/reset-mock! mock) (th/reset-mock! mock)
(let [data (assoc data :email (:email profile2)) (let [data (assoc data :emails [(:email profile2)])
out (th/command! data)] out (th/command! data)]
(t/is (th/success? out)) (t/is (th/success? out))
(t/is (= 1 (:call-count (deref mock))))) (t/is (= 1 (:call-count (deref mock)))))
@ -60,7 +60,7 @@
;; invite user with complaint ;; invite user with complaint
(th/create-global-complaint-for pool {:type :complaint :email "foo@bar.com"}) (th/create-global-complaint-for pool {:type :complaint :email "foo@bar.com"})
(th/reset-mock! mock) (th/reset-mock! mock)
(let [data (assoc data :email "foo@bar.com") (let [data (assoc data :emails ["foo@bar.com"])
out (th/command! data)] out (th/command! data)]
(t/is (th/success? out)) (t/is (th/success? out))
(t/is (= 1 (:call-count (deref mock))))) (t/is (= 1 (:call-count (deref mock)))))
@ -79,7 +79,7 @@
(th/reset-mock! mock) (th/reset-mock! mock)
(th/create-global-complaint-for pool {:type :bounce :email "foo@bar.com"}) (th/create-global-complaint-for pool {:type :bounce :email "foo@bar.com"})
(let [data (assoc data :email "foo@bar.com") (let [data (assoc data :emails ["foo@bar.com"])
out (th/command! data)] out (th/command! data)]
(t/is (not (th/success? out))) (t/is (not (th/success? out)))
@ -92,7 +92,7 @@
;; invite internal user that is muted ;; invite internal user that is muted
(th/reset-mock! mock) (th/reset-mock! mock)
(let [data (assoc data :email (:email profile3)) (let [data (assoc data :emails [(:email profile3)])
out (th/command! data)] out (th/command! data)]
(t/is (not (th/success? out))) (t/is (not (th/success? out)))
@ -118,7 +118,7 @@
;; Try to invite a not existing user ;; Try to invite a not existing user
(let [data {::th/type :create-team-invitations (let [data {::th/type :create-team-invitations
::rpc/profile-id (:id profile1) ::rpc/profile-id (:id profile1)
:email "notexisting@example.com" :emails ["notexisting@example.com"]
:team-id (:id team) :team-id (:id team)
:role :editor} :role :editor}
out (th/command! data)] out (th/command! data)]
@ -126,15 +126,15 @@
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (th/success? out)) (t/is (th/success? out))
(t/is (= 1 (:call-count @mock))) (t/is (= 1 (:call-count @mock)))
(t/is (= 1 (-> out :result count))) (t/is (= 1 (-> out :result :total)))
(let [token (-> out :result first) (let [token (-> out :result :invitations first)
claims (tokens/decode sprops token)] claims (tokens/decode sprops token)]
(t/is (= :team-invitation (:iss claims))) (t/is (= :team-invitation (:iss claims)))
(t/is (= (:id profile1) (:profile-id claims))) (t/is (= (:id profile1) (:profile-id claims)))
(t/is (= :editor (:role claims))) (t/is (= :editor (:role claims)))
(t/is (= (:id team) (:team-id claims))) (t/is (= (:id team) (:team-id claims)))
(t/is (= (:email data) (:member-email claims))) (t/is (= (first (:emails data)) (:member-email claims)))
(t/is (nil? (:member-id claims))))) (t/is (nil? (:member-id claims)))))
(th/reset-mock! mock) (th/reset-mock! mock)
@ -142,7 +142,7 @@
;; Try to invite existing user ;; Try to invite existing user
(let [data {::th/type :create-team-invitations (let [data {::th/type :create-team-invitations
::rpc/profile-id (:id profile1) ::rpc/profile-id (:id profile1)
:email (:email profile2) :emails [(:email profile2)]
:team-id (:id team) :team-id (:id team)
:role :editor} :role :editor}
out (th/command! data)] out (th/command! data)]
@ -150,15 +150,15 @@
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (th/success? out)) (t/is (th/success? out))
(t/is (= 1 (:call-count @mock))) (t/is (= 1 (:call-count @mock)))
(t/is (= 1 (-> out :result count))) (t/is (= 1 (-> out :result :total)))
(let [token (-> out :result first) (let [token (-> out :result :invitations first)
claims (tokens/decode sprops token)] claims (tokens/decode sprops token)]
(t/is (= :team-invitation (:iss claims))) (t/is (= :team-invitation (:iss claims)))
(t/is (= (:id profile1) (:profile-id claims))) (t/is (= (:id profile1) (:profile-id claims)))
(t/is (= :editor (:role claims))) (t/is (= :editor (:role claims)))
(t/is (= (:id team) (:team-id claims))) (t/is (= (:id team) (:team-id claims)))
(t/is (= (:email data) (:member-email claims))) (t/is (= (first (:emails data)) (:member-email claims)))
(t/is (= (:id profile2) (:member-id claims))))) (t/is (= (:id profile2) (:member-id claims)))))
))) )))
@ -264,7 +264,7 @@
;; invite internal user without complaints ;; invite internal user without complaints
(with-redefs [app.config/flags #{}] (with-redefs [app.config/flags #{}]
(th/reset-mock! mock) (th/reset-mock! mock)
(let [data (assoc data :email (:email profile2)) (let [data (assoc data :emails [(:email profile2)])
out (th/command! data)] out (th/command! data)]
(t/is (th/success? out)) (t/is (th/success? out))
(t/is (= 0 (:call-count (deref mock))))) (t/is (= 0 (:call-count (deref mock)))))

View file

@ -31,8 +31,9 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc header (mf/defc header
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]
[{:keys [section team] :as props}] ::mf/wrap-props false}
[{:keys [section team]}]
(let [go-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) (let [go-members (mf/use-fn #(st/emit! (dd/go-to-team-members)))
go-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) go-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings)))
go-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) go-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations)))
@ -98,27 +99,29 @@
(mf/defc invite-members-modal (mf/defc invite-members-modal
{::mf/register modal/components {::mf/register modal/components
::mf/register-as :invite-members} ::mf/register-as :invite-members
::mf/wrap-props false}
[{:keys [team origin]}] [{:keys [team origin]}]
(let [members-map (mf/deref refs/dashboard-team-members) (let [members-map (mf/deref refs/dashboard-team-members)
perms (:permissions team)
perms (:permissions team) roles (mf/use-memo (mf/deps perms) #(get-available-roles perms))
initial (mf/use-memo (constantly {:role "editor" :team-id (:id team)}))
roles (mf/use-memo (mf/deps perms) #(get-available-roles perms)) form (fm/use-form :spec ::invite-member-form
initial (mf/use-memo (constantly {:role "editor" :team-id (:id team)})) :initial initial)
form (fm/use-form :spec ::invite-member-form error-text (mf/use-state "")
:initial initial)
error-text (mf/use-state "")
on-success
(fn []
(st/emit! (msg/success (tr "notifications.invitation-email-sent"))
(modal/hide)
(dd/fetch-team-invitations)))
current-data-emails (into #{} (dm/get-in @form [:clean-data :emails])) current-data-emails (into #{} (dm/get-in @form [:clean-data :emails]))
current-members-emails (into #{} (map (comp :email second)) members-map) current-members-emails (into #{} (map (comp :email second)) members-map)
on-success
(fn [form {:keys [total]}]
(when (pos? total)
(st/emit! (msg/success (tr "notifications.invitation-email-sent"))))
(st/emit! (modal/hide)
(dd/fetch-team-invitations)))
on-error on-error
(fn [{:keys [type code] :as error}] (fn [{:keys [type code] :as error}]
(cond (cond