- Notifications
You must be signed in to change notification settings - Fork1k
feat: notify on workspace creation#15934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
db5800d
bf5f229
a6e892a
80d2145
9736f13
1791404
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
DELETE FROM notification_templates WHERE id = '281fdf73-c6d6-4cbb-8ff5-888baf8a2fff'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
INSERT INTO notification_templates | ||
(id, name, title_template, body_template, "group", actions) | ||
VALUES ( | ||
'281fdf73-c6d6-4cbb-8ff5-888baf8a2fff', | ||
'Workspace Created', | ||
E'Workspace ''{{.Labels.workspace}}'' has been created', | ||
E'Hello {{.UserName}},\n\n'|| | ||
E'The workspace **{{.Labels.workspace}}** has been created from the template **{{.Labels.template}}** using version **{{.Labels.version}}**.', | ||
'Workspace Events', | ||
'[ | ||
{ | ||
"label": "See workspace", | ||
"url": "{{base_url}}/@{{.UserUsername}}/{{.Labels.workspace}}" | ||
} | ||
]'::jsonb | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -92,8 +92,31 @@ func (f *FakeEnqueuer) Clear() { | ||
f.sent = nil | ||
} | ||
func (f *FakeEnqueuer) Sent(matchers ...func(*FakeNotification) bool) []*FakeNotification { | ||
f.mu.Lock() | ||
defer f.mu.Unlock() | ||
sent := []*FakeNotification{} | ||
for _, notif := range f.sent { | ||
// Check this notification matches all given matchers | ||
matches := true | ||
for _, matcher := range matchers { | ||
if !matcher(notif) { | ||
matches = false | ||
break | ||
} | ||
} | ||
if matches { | ||
sent = append(sent, notif) | ||
} | ||
} | ||
return sent | ||
} | ||
func WithTemplateID(id uuid.UUID) func(*FakeNotification) bool { | ||
return func(n *FakeNotification) bool { | ||
return n.TemplateID == id | ||
} | ||
Comment on lines +118 to +121 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Nice :-) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
From: system@coder.com | ||
To: bobby@coder.com | ||
Subject: Workspace 'bobby-workspace' has been created | ||
Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 | ||
Date: Fri, 11 Oct 2024 09:03:06 +0000 | ||
Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 | ||
MIME-Version: 1.0 | ||
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 | ||
Content-Transfer-Encoding: quoted-printable | ||
Content-Type: text/plain; charset=UTF-8 | ||
Hello Bobby, | ||
The workspace bobby-workspace has been created from the template bobby-temp= | ||
late using version alpha. | ||
See workspace: http://test.com/@bobby/bobby-workspace | ||
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 | ||
Content-Transfer-Encoding: quoted-printable | ||
Content-Type: text/html; charset=UTF-8 | ||
<!doctype html> | ||
<html lang=3D"en"> | ||
<head> | ||
<meta charset=3D"UTF-8" /> | ||
<meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale= | ||
=3D1.0" /> | ||
<title>Workspace 'bobby-workspace' has been created</title> | ||
</head> | ||
<body style=3D"margin: 0; padding: 0; font-family: -apple-system, system-= | ||
ui, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarel= | ||
l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= | ||
; background: #f8fafc;"> | ||
<div style=3D"max-width: 600px; margin: 20px auto; padding: 60px; borde= | ||
r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= | ||
n: left; font-size: 14px; line-height: 1.5;"> | ||
<div style=3D"text-align: center;"> | ||
<img src=3D"https://coder.com/coder-logo-horizontal.png" alt=3D"Cod= | ||
er Logo" style=3D"height: 40px;" /> | ||
</div> | ||
<h1 style=3D"text-align: center; font-size: 24px; font-weight: 400; m= | ||
argin: 8px 0 32px; line-height: 1.5;"> | ||
Workspace 'bobby-workspace' has been created | ||
</h1> | ||
<div style=3D"line-height: 1.5;"> | ||
<p>Hello Bobby,</p> | ||
<p>The workspace <strong>bobby-workspace</strong> has been created from the= | ||
template <strong>bobby-template</strong> using version <strong>alpha</stro= | ||
ng>.</p> | ||
</div> | ||
<div style=3D"text-align: center; margin-top: 32px;"> | ||
=20 | ||
<a href=3D"http://test.com/@bobby/bobby-workspace" style=3D"display= | ||
: inline-block; padding: 13px 24px; background-color: #020617; color: #f8fa= | ||
fc; text-decoration: none; border-radius: 8px; margin: 0 4px;"> | ||
See workspace | ||
</a> | ||
=20 | ||
</div> | ||
<div style=3D"border-top: 1px solid #e2e8f0; color: #475569; font-siz= | ||
e: 12px; margin-top: 64px; padding-top: 24px; line-height: 1.6;"> | ||
<p>© 2024 Coder. All rights reserved - <a = | ||
href=3D"http://test.com" style=3D"color: #2563eb; text-decoration: none;">h= | ||
ttp://test.com</a></p> | ||
<p><a href=3D"http://test.com/settings/notifications" style=3D"colo= | ||
r: #2563eb; text-decoration: none;">Click here to manage your notification = | ||
settings</a></p> | ||
<p><a href=3D"http://test.com/settings/notifications?disabled=3D281= | ||
fdf73-c6d6-4cbb-8ff5-888baf8a2fff" style=3D"color: #2563eb; text-decoration= | ||
: none;">Stop receiving emails like this</a></p> | ||
</div> | ||
</div> | ||
</body> | ||
</html> | ||
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4-- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"_version": "1.1", | ||
"msg_id": "00000000-0000-0000-0000-000000000000", | ||
"payload": { | ||
"_version": "1.1", | ||
"notification_name": "Workspace Created", | ||
"notification_template_id": "00000000-0000-0000-0000-000000000000", | ||
"user_id": "00000000-0000-0000-0000-000000000000", | ||
"user_email": "bobby@coder.com", | ||
"user_name": "Bobby", | ||
"user_username": "bobby", | ||
"actions": [ | ||
{ | ||
"label": "See workspace", | ||
"url": "http://test.com/@bobby/bobby-workspace" | ||
} | ||
], | ||
"labels": { | ||
"template": "bobby-template", | ||
"version": "alpha", | ||
"workspace": "bobby-workspace" | ||
}, | ||
"data": null | ||
}, | ||
"title": "Workspace 'bobby-workspace' has been created", | ||
"title_markdown": "Workspace 'bobby-workspace' has been created", | ||
"body": "Hello Bobby,\n\nThe workspace bobby-workspace has been created from the template bobby-template using version alpha.", | ||
"body_markdown": "Hello Bobby,\n\nThe workspace **bobby-workspace** has been created from the template **bobby-template** using version **alpha**." | ||
} |
DanielleMaywood marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -571,6 +571,59 @@ func TestPostWorkspacesByOrganization(t *testing.T) { | ||
require.Equal(t, http.StatusConflict, apiErr.StatusCode()) | ||
}) | ||
t.Run("CreateSendsNotification", func(t *testing.T) { | ||
t.Parallel() | ||
enqueuer := notificationstest.FakeEnqueuer{} | ||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, NotificationsEnqueuer: &enqueuer}) | ||
user := coderdtest.CreateFirstUser(t, client) | ||
DanielleMaywood marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
memberClient, memberUser := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) | ||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) | ||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) | ||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) | ||
workspace := coderdtest.CreateWorkspace(t, memberClient, template.ID) | ||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, memberClient, workspace.LatestBuild.ID) | ||
sent := enqueuer.Sent(notificationstest.WithTemplateID(notifications.TemplateWorkspaceCreated)) | ||
require.Len(t, sent, 1) | ||
require.Equal(t, memberUser.ID, sent[0].UserID) | ||
require.Contains(t, sent[0].Targets, template.ID) | ||
require.Contains(t, sent[0].Targets, workspace.ID) | ||
require.Contains(t, sent[0].Targets, workspace.OrganizationID) | ||
require.Contains(t, sent[0].Targets, workspace.OwnerID) | ||
}) | ||
t.Run("CreateSendsNotificationToCorrectUser", func(t *testing.T) { | ||
t.Parallel() | ||
enqueuer := notificationstest.FakeEnqueuer{} | ||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, NotificationsEnqueuer: &enqueuer}) | ||
user := coderdtest.CreateFirstUser(t, client) | ||
_, memberUser := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) | ||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) | ||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) | ||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) | ||
ctx := testutil.Context(t, testutil.WaitShort) | ||
workspace, err := client.CreateUserWorkspace(ctx, memberUser.Username, codersdk.CreateWorkspaceRequest{ | ||
TemplateID: template.ID, | ||
Name: coderdtest.RandomUsername(t), | ||
}) | ||
require.NoError(t, err) | ||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) | ||
sent := enqueuer.Sent(notificationstest.WithTemplateID(notifications.TemplateWorkspaceCreated)) | ||
require.Len(t, sent, 1) | ||
require.Equal(t, memberUser.ID, sent[0].UserID) | ||
require.Contains(t, sent[0].Targets, template.ID) | ||
require.Contains(t, sent[0].Targets, workspace.ID) | ||
require.Contains(t, sent[0].Targets, workspace.OrganizationID) | ||
require.Contains(t, sent[0].Targets, workspace.OwnerID) | ||
}) | ||
t.Run("CreateWithAuditLogs", func(t *testing.T) { | ||
t.Parallel() | ||
auditor := audit.NewMock() | ||
@@ -3596,15 +3649,14 @@ func TestWorkspaceNotifications(t *testing.T) { | ||
// Then | ||
require.NoError(t, err, "mark workspace as dormant") | ||
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateWorkspaceDormant)) | ||
require.Len(t, sent, 1) | ||
require.Equal(t, sent[0].TemplateID, notifications.TemplateWorkspaceDormant) | ||
require.Equal(t, sent[0].UserID, workspace.OwnerID) | ||
require.Contains(t, sent[0].Targets, template.ID) | ||
require.Contains(t, sent[0].Targets, workspace.ID) | ||
require.Contains(t, sent[0].Targets, workspace.OrganizationID) | ||
require.Contains(t, sent[0].Targets, workspace.OwnerID) | ||
}) | ||
t.Run("InitiatorIsOwner", func(t *testing.T) { | ||
@@ -3635,7 +3687,7 @@ func TestWorkspaceNotifications(t *testing.T) { | ||
// Then | ||
require.NoError(t, err, "mark workspace as dormant") | ||
require.Len(t, notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateWorkspaceDormant)), 0) | ||
}) | ||
t.Run("ActivateDormantWorkspace", func(t *testing.T) { | ||
Uh oh!
There was an error while loading.Please reload this page.