Skip to content

Commit f651ab9

Browse files
chore: add 'email' field to notifications (#16336)
Closes coder/internal#323 This PR adds an `email` field to the `data.owner` payload for workspace created and workspace manually updated notifications, as well as user account created/activated/suspended.
1 parent 447cc0d commit f651ab9

File tree

6 files changed

+56
-16
lines changed

6 files changed

+56
-16
lines changed

coderd/users.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
918918

919919
func (api *API) notifyUserStatusChanged(ctx context.Context, actingUserName string, targetUser database.User, status database.UserStatus) error {
920920
var labels map[string]string
921+
var data map[string]any
921922
var adminTemplateID, personalTemplateID uuid.UUID
922923
switch status {
923924
case database.UserStatusSuspended:
@@ -926,6 +927,9 @@ func (api *API) notifyUserStatusChanged(ctx context.Context, actingUserName stri
926927
"suspended_account_user_name": targetUser.Name,
927928
"initiator": actingUserName,
928929
}
930+
data = map[string]any{
931+
"user": map[string]any{"id": targetUser.ID, "name": targetUser.Name, "email": targetUser.Email},
932+
}
929933
adminTemplateID = notifications.TemplateUserAccountSuspended
930934
personalTemplateID = notifications.TemplateYourAccountSuspended
931935
case database.UserStatusActive:
@@ -934,6 +938,9 @@ func (api *API) notifyUserStatusChanged(ctx context.Context, actingUserName stri
934938
"activated_account_user_name": targetUser.Name,
935939
"initiator": actingUserName,
936940
}
941+
data = map[string]any{
942+
"user": map[string]any{"id": targetUser.ID, "name": targetUser.Name, "email": targetUser.Email},
943+
}
937944
adminTemplateID = notifications.TemplateUserAccountActivated
938945
personalTemplateID = notifications.TemplateYourAccountActivated
939946
default:
@@ -949,16 +956,16 @@ func (api *API) notifyUserStatusChanged(ctx context.Context, actingUserName stri
949956
// Send notifications to user admins and affected user
950957
for _, u := range userAdmins {
951958
// nolint:gocritic // Need notifier actor to enqueue notifications
952-
if _, err := api.NotificationsEnqueuer.Enqueue(dbauthz.AsNotifier(ctx), u.ID, adminTemplateID,
953-
labels, "api-put-user-status",
959+
if _, err := api.NotificationsEnqueuer.EnqueueWithData(dbauthz.AsNotifier(ctx), u.ID, adminTemplateID,
960+
labels, data, "api-put-user-status",
954961
targetUser.ID,
955962
); err != nil {
956963
api.Logger.Warn(ctx, "unable to notify about changed user's status", slog.F("affected_user", targetUser.Username), slog.Error(err))
957964
}
958965
}
959966
// nolint:gocritic // Need notifier actor to enqueue notifications
960-
if _, err := api.NotificationsEnqueuer.Enqueue(dbauthz.AsNotifier(ctx), targetUser.ID, personalTemplateID,
961-
labels, "api-put-user-status",
967+
if _, err := api.NotificationsEnqueuer.EnqueueWithData(dbauthz.AsNotifier(ctx), targetUser.ID, personalTemplateID,
968+
labels, data, "api-put-user-status",
962969
targetUser.ID,
963970
); err != nil {
964971
api.Logger.Warn(ctx, "unable to notify user about status change of their account", slog.F("affected_user", targetUser.Username), slog.Error(err))
@@ -1424,13 +1431,20 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
14241431
}
14251432

14261433
for _, u := range userAdmins {
1427-
// nolint:gocritic // Need notifier actor to enqueue notifications
1428-
if _, err := api.NotificationsEnqueuer.Enqueue(dbauthz.AsNotifier(ctx), u.ID, notifications.TemplateUserAccountCreated,
1434+
if _, err := api.NotificationsEnqueuer.EnqueueWithData(
1435+
// nolint:gocritic // Need notifier actor to enqueue notifications
1436+
dbauthz.AsNotifier(ctx),
1437+
u.ID,
1438+
notifications.TemplateUserAccountCreated,
14291439
map[string]string{
14301440
"created_account_name": user.Username,
14311441
"created_account_user_name": user.Name,
14321442
"initiator": req.accountCreatorName,
1433-
}, "api-users-create",
1443+
},
1444+
map[string]any{
1445+
"user": map[string]any{"id": user.ID, "name": user.Name, "email": user.Email},
1446+
},
1447+
"api-users-create",
14341448
user.ID,
14351449
); err != nil {
14361450
api.Logger.Warn(ctx, "unable to notify about created user", slog.F("created_user", user.Username), slog.Error(err))

coderd/users_test.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,19 @@ func TestNotifyUserStatusChanged(t *testing.T) {
392392
// Validate that each expected notification is present in notifyEnq.Sent()
393393
for _, expected := range expectedNotifications {
394394
found := false
395-
for _, sent := range notifyEnq.Sent() {
395+
for _, sent := range notifyEnq.Sent(notificationstest.WithTemplateID(expected.TemplateID)) {
396396
if sent.TemplateID == expected.TemplateID &&
397397
sent.UserID == expected.UserID &&
398398
slices.Contains(sent.Targets, member.ID) &&
399399
sent.Labels[label] == member.Username {
400400
found = true
401+
402+
require.IsType(t, map[string]any{}, sent.Data["user"])
403+
userData := sent.Data["user"].(map[string]any)
404+
require.Equal(t, member.ID, userData["id"])
405+
require.Equal(t, member.Name, userData["name"])
406+
require.Equal(t, member.Email, userData["email"])
407+
401408
break
402409
}
403410
}
@@ -858,11 +865,18 @@ func TestNotifyCreatedUser(t *testing.T) {
858865
require.NoError(t, err)
859866

860867
// then
861-
require.Len(t, notifyEnq.Sent(), 1)
862-
require.Equal(t, notifications.TemplateUserAccountCreated, notifyEnq.Sent()[0].TemplateID)
863-
require.Equal(t, firstUser.UserID, notifyEnq.Sent()[0].UserID)
864-
require.Contains(t, notifyEnq.Sent()[0].Targets, user.ID)
865-
require.Equal(t, user.Username, notifyEnq.Sent()[0].Labels["created_account_name"])
868+
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateUserAccountCreated))
869+
require.Len(t, sent, 1)
870+
require.Equal(t, notifications.TemplateUserAccountCreated, sent[0].TemplateID)
871+
require.Equal(t, firstUser.UserID, sent[0].UserID)
872+
require.Contains(t, sent[0].Targets, user.ID)
873+
require.Equal(t, user.Username, sent[0].Labels["created_account_name"])
874+
875+
require.IsType(t, map[string]any{}, sent[0].Data["user"])
876+
userData := sent[0].Data["user"].(map[string]any)
877+
require.Equal(t, user.ID, userData["id"])
878+
require.Equal(t, user.Name, userData["name"])
879+
require.Equal(t, user.Email, userData["email"])
866880
})
867881

868882
t.Run("UserAdminNotified", func(t *testing.T) {

coderd/workspacebuilds.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ func (api *API) notifyWorkspaceUpdated(
527527
"workspace": map[string]any{"id": workspace.ID, "name": workspace.Name},
528528
"template": map[string]any{"id": template.ID, "name": template.Name},
529529
"template_version": map[string]any{"id": version.ID, "name": version.Name},
530-
"owner": map[string]any{"id": owner.ID, "name": owner.Name},
530+
"owner": map[string]any{"id": owner.ID, "name": owner.Name, "email": owner.Email},
531531
"parameters": buildParameters,
532532
},
533533
"api-workspaces-updated",

coderd/workspacebuilds_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ func TestWorkspaceBuildWithUpdatedTemplateVersionSendsNotification(t *testing.T)
648648
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, NotificationsEnqueuer: notify})
649649
first := coderdtest.CreateFirstUser(t, client)
650650
templateAdminClient, templateAdmin := coderdtest.CreateAnotherUser(t, client, first.OrganizationID, rbac.RoleTemplateAdmin())
651-
userClient, _ := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
651+
userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
652652

653653
// Create a template with an initial version
654654
version := coderdtest.CreateTemplateVersion(t, templateAdminClient, first.OrganizationID, nil)
@@ -684,6 +684,12 @@ func TestWorkspaceBuildWithUpdatedTemplateVersionSendsNotification(t *testing.T)
684684
require.Contains(t, sent[0].Targets, workspace.ID)
685685
require.Contains(t, sent[0].Targets, workspace.OrganizationID)
686686
require.Contains(t, sent[0].Targets, workspace.OwnerID)
687+
688+
owner, ok := sent[0].Data["owner"].(map[string]any)
689+
require.True(t, ok, "notification data should have owner")
690+
require.Equal(t, user.ID, owner["id"])
691+
require.Equal(t, user.Name, owner["name"])
692+
require.Equal(t, user.Email, owner["email"])
687693
})
688694
}
689695

coderd/workspaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ func (api *API) notifyWorkspaceCreated(
809809
"workspace": map[string]any{"id": workspace.ID, "name": workspace.Name},
810810
"template": map[string]any{"id": template.ID, "name": template.Name},
811811
"template_version": map[string]any{"id": version.ID, "name": version.Name},
812-
"owner": map[string]any{"id": owner.ID, "name": owner.Name},
812+
"owner": map[string]any{"id": owner.ID, "name": owner.Name, "email": owner.Email},
813813
"parameters": buildParameters,
814814
},
815815
"api-workspaces-create",

coderd/workspaces_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,12 @@ func TestPostWorkspacesByOrganization(t *testing.T) {
639639
require.Contains(t, sent[0].Targets, workspace.ID)
640640
require.Contains(t, sent[0].Targets, workspace.OrganizationID)
641641
require.Contains(t, sent[0].Targets, workspace.OwnerID)
642+
643+
owner, ok := sent[0].Data["owner"].(map[string]any)
644+
require.True(t, ok, "notification data should have owner")
645+
require.Equal(t, memberUser.ID, owner["id"])
646+
require.Equal(t, memberUser.Name, owner["name"])
647+
require.Equal(t, memberUser.Email, owner["email"])
642648
})
643649

644650
t.Run("CreateWithAuditLogs", func(t *testing.T) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy