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

Commiteec6c8c

Browse files
authored
feat: support custom notifications (#19751)
## DescriptionAdds support for sending an ad‑hoc custom notification to theauthenticated user via API and CLI. This is useful for surfacing theresult of scripts or long‑running tasks. Notifications are deliveredthrough the configured method and the dashboard Inbox, respectingexisting preferences and delivery settings.## Changes* New notification template: “Custom Notification” with a label for acustom title and a custom message.* New API endpoint: `POST /api/v2/notifications/custom` to send a customnotification to the requesting user.* New API endpoint: `GET /notifications/templates/custom` to get customnotification template.* New CLI subcommand: `coder notifications custom <title> <message>` tosend a custom notification to the requesting user.* Documentation updates: Add a “Custom notifications” section underAdministration > Monitoring > Notifications, including instructions onsending custom notifications and examples of when to use them.Closes:#19611
1 parent4c98dec commiteec6c8c

26 files changed

+1056
-30
lines changed

‎cli/notifications.go‎

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@ func (r *RootCmd) notifications() *serpent.Command {
1616
Short:"Manage Coder notifications",
1717
Long:"Administrators can use these commands to change notification settings.\n"+FormatExamples(
1818
Example{
19-
Description:"Pause Coder notifications. Administrators can temporarily stop notifiers from dispatching messages in case of the target outage (for example: unavailable SMTP server or Webhook not responding).",
19+
Description:"Pause Coder notifications. Administrators can temporarily stop notifiers from dispatching messages in case of the target outage (for example: unavailable SMTP server or Webhook not responding)",
2020
Command:"coder notifications pause",
2121
},
2222
Example{
2323
Description:"Resume Coder notifications",
2424
Command:"coder notifications resume",
2525
},
2626
Example{
27-
Description:"Send a test notification. Administrators can use this to verify the notification target settings.",
27+
Description:"Send a test notification. Administrators can use this to verify the notification target settings",
2828
Command:"coder notifications test",
2929
},
30+
Example{
31+
Description:"Send a custom notification to the requesting user. Sending notifications targeting other users or groups is currently not supported",
32+
Command:"coder notifications custom\"Custom Title\"\"Custom Message\"",
33+
},
3034
),
3135
Aliases: []string{"notification"},
3236
Handler:func(inv*serpent.Invocation)error {
@@ -36,6 +40,7 @@ func (r *RootCmd) notifications() *serpent.Command {
3640
r.pauseNotifications(),
3741
r.resumeNotifications(),
3842
r.testNotifications(),
43+
r.customNotifications(),
3944
},
4045
}
4146
returncmd
@@ -109,3 +114,30 @@ func (r *RootCmd) testNotifications() *serpent.Command {
109114
}
110115
returncmd
111116
}
117+
118+
func (r*RootCmd)customNotifications()*serpent.Command {
119+
client:=new(codersdk.Client)
120+
cmd:=&serpent.Command{
121+
Use:"custom <title> <message>",
122+
Short:"Send a custom notification",
123+
Middleware:serpent.Chain(
124+
serpent.RequireNArgs(2),
125+
r.InitClient(client),
126+
),
127+
Handler:func(inv*serpent.Invocation)error {
128+
err:=client.PostCustomNotification(inv.Context(), codersdk.CustomNotificationRequest{
129+
Content:&codersdk.CustomNotificationContent{
130+
Title:inv.Args[0],
131+
Message:inv.Args[1],
132+
},
133+
})
134+
iferr!=nil {
135+
returnxerrors.Errorf("unable to post custom notification: %w",err)
136+
}
137+
138+
_,_=fmt.Fprintln(inv.Stderr,"A custom notification has been sent.")
139+
returnnil
140+
},
141+
}
142+
returncmd
143+
}

‎cli/notifications_test.go‎

Lines changed: 101 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/database"
16+
"github.com/coder/coder/v2/coderd/database/dbgen"
1517
"github.com/coder/coder/v2/coderd/notifications"
1618
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
1719
"github.com/coder/coder/v2/codersdk"
@@ -166,3 +168,102 @@ func TestNotificationsTest(t *testing.T) {
166168
require.Len(t,sent,0)
167169
})
168170
}
171+
172+
funcTestCustomNotifications(t*testing.T) {
173+
t.Parallel()
174+
175+
t.Run("BadRequest",func(t*testing.T) {
176+
t.Parallel()
177+
178+
notifyEnq:=&notificationstest.FakeEnqueuer{}
179+
180+
ownerClient:=coderdtest.New(t,&coderdtest.Options{
181+
DeploymentValues:coderdtest.DeploymentValues(t),
182+
NotificationsEnqueuer:notifyEnq,
183+
})
184+
185+
// Given: A member user
186+
ownerUser:=coderdtest.CreateFirstUser(t,ownerClient)
187+
memberClient,_:=coderdtest.CreateAnotherUser(t,ownerClient,ownerUser.OrganizationID)
188+
189+
// When: The member user attempts to send a custom notification with empty title and message
190+
inv,root:=clitest.New(t,"notifications","custom","","")
191+
clitest.SetupConfig(t,memberClient,root)
192+
193+
// Then: an error is expected with no notifications sent
194+
err:=inv.Run()
195+
varsdkError*codersdk.Error
196+
require.Error(t,err)
197+
require.ErrorAsf(t,err,&sdkError,"error should be of type *codersdk.Error")
198+
require.Equal(t,http.StatusBadRequest,sdkError.StatusCode())
199+
require.Equal(t,"Invalid request body",sdkError.Message)
200+
201+
sent:=notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
202+
require.Len(t,sent,0)
203+
})
204+
205+
t.Run("SystemUserNotAllowed",func(t*testing.T) {
206+
t.Parallel()
207+
208+
notifyEnq:=&notificationstest.FakeEnqueuer{}
209+
210+
ownerClient,db:=coderdtest.NewWithDatabase(t,&coderdtest.Options{
211+
DeploymentValues:coderdtest.DeploymentValues(t),
212+
NotificationsEnqueuer:notifyEnq,
213+
})
214+
215+
// Given: A system user (prebuilds system user)
216+
_,token:=dbgen.APIKey(t,db, database.APIKey{
217+
UserID:database.PrebuildsSystemUserID,
218+
LoginType:database.LoginTypeNone,
219+
})
220+
systemUserClient:=codersdk.New(ownerClient.URL)
221+
systemUserClient.SetSessionToken(token)
222+
223+
// When: The system user attempts to send a custom notification
224+
inv,root:=clitest.New(t,"notifications","custom","Custom Title","Custom Message")
225+
clitest.SetupConfig(t,systemUserClient,root)
226+
227+
// Then: an error is expected with no notifications sent
228+
err:=inv.Run()
229+
varsdkError*codersdk.Error
230+
require.Error(t,err)
231+
require.ErrorAsf(t,err,&sdkError,"error should be of type *codersdk.Error")
232+
require.Equal(t,http.StatusForbidden,sdkError.StatusCode())
233+
require.Equal(t,"Forbidden",sdkError.Message)
234+
235+
sent:=notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
236+
require.Len(t,sent,0)
237+
})
238+
239+
t.Run("Success",func(t*testing.T) {
240+
t.Parallel()
241+
242+
notifyEnq:=&notificationstest.FakeEnqueuer{}
243+
244+
ownerClient:=coderdtest.New(t,&coderdtest.Options{
245+
DeploymentValues:coderdtest.DeploymentValues(t),
246+
NotificationsEnqueuer:notifyEnq,
247+
})
248+
249+
// Given: A member user
250+
ownerUser:=coderdtest.CreateFirstUser(t,ownerClient)
251+
memberClient,memberUser:=coderdtest.CreateAnotherUser(t,ownerClient,ownerUser.OrganizationID)
252+
253+
// When: The member user attempts to send a custom notification
254+
inv,root:=clitest.New(t,"notifications","custom","Custom Title","Custom Message")
255+
clitest.SetupConfig(t,memberClient,root)
256+
257+
// Then: we expect a custom notification to be sent to the member user
258+
err:=inv.Run()
259+
require.NoError(t,err)
260+
261+
sent:=notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateCustomNotification))
262+
require.Len(t,sent,1)
263+
require.Equal(t,memberUser.ID,sent[0].UserID)
264+
require.Len(t,sent[0].Labels,2)
265+
require.Equal(t,"Custom Title",sent[0].Labels["custom_title"])
266+
require.Equal(t,"Custom Message",sent[0].Labels["custom_message"])
267+
require.Equal(t,memberUser.ID.String(),sent[0].CreatedBy)
268+
})
269+
}

‎cli/testdata/coder_notifications_--help.golden‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ USAGE:
1212
from
1313
dispatching messages in case of the target outage (for example: unavailable
1414
SMTP
15-
server or Webhook not responding).:
15+
server or Webhook not responding):
1616

1717
$ coder notifications pause
1818

@@ -22,11 +22,17 @@ USAGE:
2222

2323
- Send a test notification. Administrators can use this to verify the
2424
notification
25-
target settings.:
25+
target settings:
2626

2727
$ coder notifications test
28+
29+
- Send a custom notification to the requesting user. Sending notifications
30+
targeting other users or groups is currently not supported:
31+
32+
$ coder notifications custom "Custom Title" "Custom Message"
2833

2934
SUBCOMMANDS:
35+
custom Send a custom notification
3036
pause Pause notifications
3137
resume Resume notifications
3238
test Send a test notification
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 custom <title> <message>
5+
6+
Send a custom notification
7+
8+
———
9+
Run `coder --help` for a list of global options.

‎coderd/apidoc/docs.go‎

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

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp