🐛 Send organization invitations to unregistered emails (#12937)

send-template drops nil values before enqueuing the mail job, but the
invite-to-organization schema required the :user-name key. Inviting an
email with no Penpot account left :user-name nil, the key was dropped
and the job failed its schema check, so the invitation email was never
sent. :user-name is now optional in invite-to-organization and in
renewal-notice, which has the same shape.

New test renders both templates through the queue with a nil
:user-name.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Juanfran 2026-11-10 13:44:26 +01:00 committed by GitHub
parent 2e6c799bba
commit ae24118caf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 2 deletions

View File

@ -475,7 +475,7 @@
(def ^:private schema:invite-to-organization
[:map
[:invited-by ::sm/text]
[:user-name [:maybe ::sm/text]]
[:user-name {:optional true} [:maybe ::sm/text]]
[:token ::sm/text]
[:organization schema:organization-data]])
@ -497,7 +497,7 @@
(def ^:private schema:renewal-notice
[:map
[:user-name [:maybe :string]]
[:user-name {:optional true} [:maybe :string]]
[:renewal-date ::sm/text]
[:estimated-amount ::sm/text]
[:organizations [:vector schema:organization-data]]])

View File

@ -300,6 +300,32 @@
((::jobs/handler job-def) nil params)
(t/is (= (emails/render factory recipe) @sent))))))
;; `send-template` drops nil values before enqueuing, so a nil
;; `:user-name` (e.g. inviting an unregistered email) must still render.
(t/deftest send-template-renders-with-nil-user-name
(doseq [[template factory recipe]
[["invite-to-organization" emails/invite-to-organization
{:to "invitee@example.com"
:public-uri (cf/get :public-uri)
:invited-by "Owner User"
:user-name nil
:organization {:name "Acme Inc"}
:token "test-token"}]
["renewal-notice" emails/renewal-notice
{:to "billing@example.com"
:public-uri (cf/get :public-uri)
:user-name nil
:renewal-date "2026-12-01"
:estimated-amount "$10.00"
:organizations [{:name "Acme"}]}]]]
(t/testing template
(let [sent (atom nil)
job-def (templated-job-def (fn [_ email] (reset! sent email)))
job-id (submit-templated job-def (assoc recipe ::emails/template template))
params (jobs/decode-params job-def (:params (th/db-get :job {:id job-id})))]
((::jobs/handler job-def) nil params)
(t/is (= (emails/render factory recipe) @sent))))))
(t/deftest send-templated-mail-handler-rejects-unknown-template
(let [job-def (templated-job-def (fn [_] nil))
code (try