Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd2419c8

Browse files
feat: add tool to send a test notification (#16611)
Relates to#16463Adds a CLI command, and API endpoint, to trigger a test notification foradministrators of a deployment.
1 parent833ca53 commitd2419c8

20 files changed

+438
-4
lines changed

‎cli/notifications.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ func (r *RootCmd) notifications() *serpent.Command {
2323
Description:"Resume Coder notifications",
2424
Command:"coder notifications resume",
2525
},
26+
Example{
27+
Description:"Send a test notification. Administrators can use this to verify the notification target settings.",
28+
Command:"coder notifications test",
29+
},
2630
),
2731
Aliases: []string{"notification"},
2832
Handler:func(inv*serpent.Invocation)error {
@@ -31,6 +35,7 @@ func (r *RootCmd) notifications() *serpent.Command {
3135
Children: []*serpent.Command{
3236
r.pauseNotifications(),
3337
r.resumeNotifications(),
38+
r.testNotifications(),
3439
},
3540
}
3641
returncmd
@@ -83,3 +88,24 @@ func (r *RootCmd) resumeNotifications() *serpent.Command {
8388
}
8489
returncmd
8590
}
91+
92+
func (r*RootCmd)testNotifications()*serpent.Command {
93+
client:=new(codersdk.Client)
94+
cmd:=&serpent.Command{
95+
Use:"test",
96+
Short:"Send a test notification",
97+
Middleware:serpent.Chain(
98+
serpent.RequireNArgs(0),
99+
r.InitClient(client),
100+
),
101+
Handler:func(inv*serpent.Invocation)error {
102+
iferr:=client.PostTestNotification(inv.Context());err!=nil {
103+
returnxerrors.Errorf("unable to post test notification: %w",err)
104+
}
105+
106+
_,_=fmt.Fprintln(inv.Stderr,"A test notification has been sent. If you don't receive the notification, check Coder's logs for any errors.")
107+
returnnil
108+
},
109+
}
110+
returncmd
111+
}

‎cli/notifications_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212

1313
"github.com/coder/coder/v2/cli/clitest"
1414
"github.com/coder/coder/v2/coderd/coderdtest"
15+
"github.com/coder/coder/v2/coderd/notifications"
16+
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
1517
"github.com/coder/coder/v2/codersdk"
1618
"github.com/coder/coder/v2/testutil"
1719
)
@@ -109,3 +111,59 @@ func TestPauseNotifications_RegularUser(t *testing.T) {
109111
require.NoError(t,err)
110112
require.False(t,settings.NotifierPaused)// still running
111113
}
114+
115+
funcTestNotificationsTest(t*testing.T) {
116+
t.Parallel()
117+
118+
t.Run("OwnerCanSendTestNotification",func(t*testing.T) {
119+
t.Parallel()
120+
121+
notifyEnq:=&notificationstest.FakeEnqueuer{}
122+
123+
// Given: An owner user.
124+
ownerClient:=coderdtest.New(t,&coderdtest.Options{
125+
DeploymentValues:coderdtest.DeploymentValues(t),
126+
NotificationsEnqueuer:notifyEnq,
127+
})
128+
_=coderdtest.CreateFirstUser(t,ownerClient)
129+
130+
// When: The owner user attempts to send the test notification.
131+
inv,root:=clitest.New(t,"notifications","test")
132+
clitest.SetupConfig(t,ownerClient,root)
133+
134+
// Then: we expect a notification to be sent.
135+
err:=inv.Run()
136+
require.NoError(t,err)
137+
138+
sent:=notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
139+
require.Len(t,sent,1)
140+
})
141+
142+
t.Run("MemberCannotSendTestNotification",func(t*testing.T) {
143+
t.Parallel()
144+
145+
notifyEnq:=&notificationstest.FakeEnqueuer{}
146+
147+
// Given: A member user.
148+
ownerClient:=coderdtest.New(t,&coderdtest.Options{
149+
DeploymentValues:coderdtest.DeploymentValues(t),
150+
NotificationsEnqueuer:notifyEnq,
151+
})
152+
ownerUser:=coderdtest.CreateFirstUser(t,ownerClient)
153+
memberClient,_:=coderdtest.CreateAnotherUser(t,ownerClient,ownerUser.OrganizationID)
154+
155+
// When: The member user attempts to send the test notification.
156+
inv,root:=clitest.New(t,"notifications","test")
157+
clitest.SetupConfig(t,memberClient,root)
158+
159+
// Then: we expect an error and no notifications to be sent.
160+
err:=inv.Run()
161+
varsdkError*codersdk.Error
162+
require.Error(t,err)
163+
require.ErrorAsf(t,err,&sdkError,"error should be of type *codersdk.Error")
164+
assert.Equal(t,http.StatusForbidden,sdkError.StatusCode())
165+
166+
sent:=notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
167+
require.Len(t,sent,0)
168+
})
169+
}

‎cli/testdata/coder_notifications_--help.golden

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,17 @@ USAGE:
1919
- Resume Coder notifications:
2020

2121
$ coder notifications resume
22+
23+
- Send a test notification. Administrators can use this to verify the
24+
notification
25+
target settings.:
26+
27+
$ coder notifications test
2228

2329
SUBCOMMANDS:
2430
pause Pause notifications
2531
resume Resume notifications
32+
test Send a test notification
2633

2734
———
2835
Run `coder --help` for a list of global options.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
coder v0.0.0-devel
2+
3+
USAGE:
4+
coder notifications test
5+
6+
Send a test notification
7+
8+
———
9+
Run `coder --help` for a list of global options.

‎coderd/apidoc/docs.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,7 @@ func New(options *Options) *API {
13701370
r.Get("/system",api.systemNotificationTemplates)
13711371
})
13721372
r.Get("/dispatch-methods",api.notificationDispatchMethods)
1373+
r.Post("/test",api.postTestNotification)
13731374
})
13741375
r.Route("/tailnet",func(r chi.Router) {
13751376
r.Use(apiKeyMiddleware)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DELETEFROM notification_templatesWHERE id='c425f63e-716a-4bf4-ae24-78348f706c3f';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
INSERT INTO notification_templates
2+
(id, name, title_template, body_template,"group", actions)
3+
VALUES (
4+
'c425f63e-716a-4bf4-ae24-78348f706c3f',
5+
'Test Notification',
6+
E'A test notification',
7+
E'Hi {{.UserName}},\n\n'||
8+
E'This is a test notification.',
9+
'Notification Events',
10+
'[
11+
{
12+
"label": "View notification settings",
13+
"url": "{{base_url}}/deployment/notifications?tab=settings"
14+
}
15+
]'::jsonb
16+
);

‎coderd/notifications.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ import (
1111

1212
"github.com/coder/coder/v2/coderd/audit"
1313
"github.com/coder/coder/v2/coderd/database"
14+
"github.com/coder/coder/v2/coderd/database/dbauthz"
1415
"github.com/coder/coder/v2/coderd/httpapi"
1516
"github.com/coder/coder/v2/coderd/httpmw"
17+
"github.com/coder/coder/v2/coderd/notifications"
1618
"github.com/coder/coder/v2/coderd/rbac"
19+
"github.com/coder/coder/v2/coderd/rbac/policy"
1720
"github.com/coder/coder/v2/codersdk"
1821
)
1922

@@ -163,6 +166,53 @@ func (api *API) notificationDispatchMethods(rw http.ResponseWriter, r *http.Requ
163166
})
164167
}
165168

169+
// @Summary Send a test notification
170+
// @ID send-a-test-notification
171+
// @Security CoderSessionToken
172+
// @Tags Notifications
173+
// @Success 200
174+
// @Router /notifications/test [post]
175+
func (api*API)postTestNotification(rw http.ResponseWriter,r*http.Request) {
176+
var (
177+
ctx=r.Context()
178+
key=httpmw.APIKey(r)
179+
)
180+
181+
if!api.Authorize(r,policy.ActionUpdate,rbac.ResourceDeploymentConfig) {
182+
httpapi.Forbidden(rw)
183+
return
184+
}
185+
186+
if_,err:=api.NotificationsEnqueuer.EnqueueWithData(
187+
//nolint:gocritic // We need to be notifier to send the notification.
188+
dbauthz.AsNotifier(ctx),
189+
key.UserID,
190+
notifications.TemplateTestNotification,
191+
map[string]string{},
192+
map[string]any{
193+
// NOTE(DanielleMaywood):
194+
// When notifications are enqueued, they are checked to be
195+
// unique within a single day. This means that if we attempt
196+
// to send two test notifications to the same user on
197+
// the same day, the enqueuer will prevent us from sending
198+
// a second one. We are injecting a timestamp to make the
199+
// notifications appear different enough to circumvent this
200+
// deduplication logic.
201+
"timestamp":api.Clock.Now(),
202+
},
203+
"send-test-notification",
204+
);err!=nil {
205+
api.Logger.Error(ctx,"send notification",slog.Error(err))
206+
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
207+
Message:"Failed to send test notification",
208+
Detail:err.Error(),
209+
})
210+
return
211+
}
212+
213+
httpapi.Write(ctx,rw,http.StatusOK,nil)
214+
}
215+
166216
// @Summary Get user notification preferences
167217
// @ID get-user-notification-preferences
168218
// @Security CoderSessionToken

‎coderd/notifications/events.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,8 @@ var (
3939

4040
TemplateWorkspaceBuildsFailedReport=uuid.MustParse("34a20db2-e9cc-4a93-b0e4-8569699d7a00")
4141
)
42+
43+
// Notification-related events.
44+
var (
45+
TemplateTestNotification=uuid.MustParse("c425f63e-716a-4bf4-ae24-78348f706c3f")
46+
)

‎coderd/notifications/notifications_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,16 @@ func TestNotificationTemplates_Golden(t *testing.T) {
11251125
},
11261126
},
11271127
},
1128+
{
1129+
name:"TemplateTestNotification",
1130+
id:notifications.TemplateTestNotification,
1131+
payload: types.MessagePayload{
1132+
UserName:"Bobby",
1133+
UserEmail:"bobby@coder.com",
1134+
UserUsername:"bobby",
1135+
Labels:map[string]string{},
1136+
},
1137+
},
11281138
}
11291139

11301140
// We must have a test case for every notification_template. This is enforced below:
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
From: system@coder.com
2+
To: bobby@coder.com
3+
Subject: A test notification
4+
Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48
5+
Date: Fri, 11 Oct 2024 09:03:06 +0000
6+
Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
7+
MIME-Version: 1.0
8+
9+
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
10+
Content-Transfer-Encoding: quoted-printable
11+
Content-Type: text/plain; charset=UTF-8
12+
13+
Hi Bobby,
14+
15+
This is a test notification.
16+
17+
18+
View notification settings: http://test.com/deployment/notifications?tab=3D=
19+
settings
20+
21+
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
22+
Content-Transfer-Encoding: quoted-printable
23+
Content-Type: text/html; charset=UTF-8
24+
25+
<!doctype html>
26+
<html lang=3D"en">
27+
<head>
28+
<meta charset=3D"UTF-8" />
29+
<meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=
30+
=3D1.0" />
31+
<title>A test notification</title>
32+
</head>
33+
<body style=3D"margin: 0; padding: 0; font-family: -apple-system, system-=
34+
ui, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarel=
35+
l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617=
36+
; background: #f8fafc;">
37+
<div style=3D"max-width: 600px; margin: 20px auto; padding: 60px; borde=
38+
r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig=
39+
n: left; font-size: 14px; line-height: 1.5;">
40+
<div style=3D"text-align: center;">
41+
<img src=3D"https://coder.com/coder-logo-horizontal.png" alt=3D"Cod=
42+
er Logo" style=3D"height: 40px;" />
43+
</div>
44+
<h1 style=3D"text-align: center; font-size: 24px; font-weight: 400; m=
45+
argin: 8px 0 32px; line-height: 1.5;">
46+
A test notification
47+
</h1>
48+
<div style=3D"line-height: 1.5;">
49+
<p>Hi Bobby,</p>
50+
51+
<p>This is a test notification.</p>
52+
</div>
53+
<div style=3D"text-align: center; margin-top: 32px;">
54+
=20
55+
<a href=3D"http://test.com/deployment/notifications?tab=3Dsettings"=
56+
style=3D"display: inline-block; padding: 13px 24px; background-color: #020=
57+
617; color: #f8fafc; text-decoration: none; border-radius: 8px; margin: 0 4=
58+
px;">
59+
View notification settings
60+
</a>
61+
=20
62+
</div>
63+
<div style=3D"border-top: 1px solid #e2e8f0; color: #475569; font-siz=
64+
e: 12px; margin-top: 64px; padding-top: 24px; line-height: 1.6;">
65+
<p>&copy;&nbsp;2024&nbsp;Coder. All rights reserved&nbsp;-&nbsp;<a =
66+
href=3D"http://test.com" style=3D"color: #2563eb; text-decoration: none;">h=
67+
ttp://test.com</a></p>
68+
<p><a href=3D"http://test.com/settings/notifications" style=3D"colo=
69+
r: #2563eb; text-decoration: none;">Click here to manage your notification =
70+
settings</a></p>
71+
<p><a href=3D"http://test.com/settings/notifications?disabled=3Dc42=
72+
5f63e-716a-4bf4-ae24-78348f706c3f" style=3D"color: #2563eb; text-decoration=
73+
: none;">Stop receiving emails like this</a></p>
74+
</div>
75+
</div>
76+
</body>
77+
</html>
78+
79+
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4--

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp