mirror of
https://github.com/coder/coder.git
synced 2025-03-14 10:09:57 +00:00
feat: add tool to send a test notification (#16611)
Relates to https://github.com/coder/coder/issues/16463 Adds a CLI command, and API endpoint, to trigger a test notification for administrators of a deployment.
This commit is contained in:
@ -23,6 +23,10 @@ func (r *RootCmd) notifications() *serpent.Command {
|
||||
Description: "Resume Coder notifications",
|
||||
Command: "coder notifications resume",
|
||||
},
|
||||
Example{
|
||||
Description: "Send a test notification. Administrators can use this to verify the notification target settings.",
|
||||
Command: "coder notifications test",
|
||||
},
|
||||
),
|
||||
Aliases: []string{"notification"},
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
@ -31,6 +35,7 @@ func (r *RootCmd) notifications() *serpent.Command {
|
||||
Children: []*serpent.Command{
|
||||
r.pauseNotifications(),
|
||||
r.resumeNotifications(),
|
||||
r.testNotifications(),
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
@ -83,3 +88,24 @@ func (r *RootCmd) resumeNotifications() *serpent.Command {
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (r *RootCmd) testNotifications() *serpent.Command {
|
||||
client := new(codersdk.Client)
|
||||
cmd := &serpent.Command{
|
||||
Use: "test",
|
||||
Short: "Send a test notification",
|
||||
Middleware: serpent.Chain(
|
||||
serpent.RequireNArgs(0),
|
||||
r.InitClient(client),
|
||||
),
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
if err := client.PostTestNotification(inv.Context()); err != nil {
|
||||
return xerrors.Errorf("unable to post test notification: %w", err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(inv.Stderr, "A test notification has been sent. If you don't receive the notification, check Coder's logs for any errors.")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/notifications"
|
||||
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
@ -109,3 +111,59 @@ func TestPauseNotifications_RegularUser(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.False(t, settings.NotifierPaused) // still running
|
||||
}
|
||||
|
||||
func TestNotificationsTest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OwnerCanSendTestNotification", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notifyEnq := ¬ificationstest.FakeEnqueuer{}
|
||||
|
||||
// Given: An owner user.
|
||||
ownerClient := coderdtest.New(t, &coderdtest.Options{
|
||||
DeploymentValues: coderdtest.DeploymentValues(t),
|
||||
NotificationsEnqueuer: notifyEnq,
|
||||
})
|
||||
_ = coderdtest.CreateFirstUser(t, ownerClient)
|
||||
|
||||
// When: The owner user attempts to send the test notification.
|
||||
inv, root := clitest.New(t, "notifications", "test")
|
||||
clitest.SetupConfig(t, ownerClient, root)
|
||||
|
||||
// Then: we expect a notification to be sent.
|
||||
err := inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
|
||||
require.Len(t, sent, 1)
|
||||
})
|
||||
|
||||
t.Run("MemberCannotSendTestNotification", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notifyEnq := ¬ificationstest.FakeEnqueuer{}
|
||||
|
||||
// Given: A member user.
|
||||
ownerClient := coderdtest.New(t, &coderdtest.Options{
|
||||
DeploymentValues: coderdtest.DeploymentValues(t),
|
||||
NotificationsEnqueuer: notifyEnq,
|
||||
})
|
||||
ownerUser := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
memberClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, ownerUser.OrganizationID)
|
||||
|
||||
// When: The member user attempts to send the test notification.
|
||||
inv, root := clitest.New(t, "notifications", "test")
|
||||
clitest.SetupConfig(t, memberClient, root)
|
||||
|
||||
// Then: we expect an error and no notifications to be sent.
|
||||
err := inv.Run()
|
||||
var sdkError *codersdk.Error
|
||||
require.Error(t, err)
|
||||
require.ErrorAsf(t, err, &sdkError, "error should be of type *codersdk.Error")
|
||||
assert.Equal(t, http.StatusForbidden, sdkError.StatusCode())
|
||||
|
||||
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
|
||||
require.Len(t, sent, 0)
|
||||
})
|
||||
}
|
||||
|
@ -19,10 +19,17 @@ USAGE:
|
||||
- Resume Coder notifications:
|
||||
|
||||
$ coder notifications resume
|
||||
|
||||
- Send a test notification. Administrators can use this to verify the
|
||||
notification
|
||||
target settings.:
|
||||
|
||||
$ coder notifications test
|
||||
|
||||
SUBCOMMANDS:
|
||||
pause Pause notifications
|
||||
resume Resume notifications
|
||||
test Send a test notification
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
|
9
cli/testdata/coder_notifications_test_--help.golden
vendored
Normal file
9
cli/testdata/coder_notifications_test_--help.golden
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder notifications test
|
||||
|
||||
Send a test notification
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
19
coderd/apidoc/docs.go
generated
19
coderd/apidoc/docs.go
generated
@ -1787,6 +1787,25 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/notifications/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Notifications"
|
||||
],
|
||||
"summary": "Send a test notification",
|
||||
"operationId": "send-a-test-notification",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oauth2-provider/apps": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
17
coderd/apidoc/swagger.json
generated
17
coderd/apidoc/swagger.json
generated
@ -1554,6 +1554,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/notifications/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": ["Notifications"],
|
||||
"summary": "Send a test notification",
|
||||
"operationId": "send-a-test-notification",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oauth2-provider/apps": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -1370,6 +1370,7 @@ func New(options *Options) *API {
|
||||
r.Get("/system", api.systemNotificationTemplates)
|
||||
})
|
||||
r.Get("/dispatch-methods", api.notificationDispatchMethods)
|
||||
r.Post("/test", api.postTestNotification)
|
||||
})
|
||||
r.Route("/tailnet", func(r chi.Router) {
|
||||
r.Use(apiKeyMiddleware)
|
||||
|
@ -0,0 +1 @@
|
||||
DELETE FROM notification_templates WHERE id = 'c425f63e-716a-4bf4-ae24-78348f706c3f';
|
16
coderd/database/migrations/000295_test_notification.up.sql
Normal file
16
coderd/database/migrations/000295_test_notification.up.sql
Normal file
@ -0,0 +1,16 @@
|
||||
INSERT INTO notification_templates
|
||||
(id, name, title_template, body_template, "group", actions)
|
||||
VALUES (
|
||||
'c425f63e-716a-4bf4-ae24-78348f706c3f',
|
||||
'Test Notification',
|
||||
E'A test notification',
|
||||
E'Hi {{.UserName}},\n\n'||
|
||||
E'This is a test notification.',
|
||||
'Notification Events',
|
||||
'[
|
||||
{
|
||||
"label": "View notification settings",
|
||||
"url": "{{base_url}}/deployment/notifications?tab=settings"
|
||||
}
|
||||
]'::jsonb
|
||||
);
|
@ -11,9 +11,12 @@ import (
|
||||
|
||||
"github.com/coder/coder/v2/coderd/audit"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/notifications"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/rbac/policy"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
@ -163,6 +166,53 @@ func (api *API) notificationDispatchMethods(rw http.ResponseWriter, r *http.Requ
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Send a test notification
|
||||
// @ID send-a-test-notification
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Notifications
|
||||
// @Success 200
|
||||
// @Router /notifications/test [post]
|
||||
func (api *API) postTestNotification(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
key = httpmw.APIKey(r)
|
||||
)
|
||||
|
||||
if !api.Authorize(r, policy.ActionUpdate, rbac.ResourceDeploymentConfig) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NotificationsEnqueuer.EnqueueWithData(
|
||||
//nolint:gocritic // We need to be notifier to send the notification.
|
||||
dbauthz.AsNotifier(ctx),
|
||||
key.UserID,
|
||||
notifications.TemplateTestNotification,
|
||||
map[string]string{},
|
||||
map[string]any{
|
||||
// NOTE(DanielleMaywood):
|
||||
// When notifications are enqueued, they are checked to be
|
||||
// unique within a single day. This means that if we attempt
|
||||
// to send two test notifications to the same user on
|
||||
// the same day, the enqueuer will prevent us from sending
|
||||
// a second one. We are injecting a timestamp to make the
|
||||
// notifications appear different enough to circumvent this
|
||||
// deduplication logic.
|
||||
"timestamp": api.Clock.Now(),
|
||||
},
|
||||
"send-test-notification",
|
||||
); err != nil {
|
||||
api.Logger.Error(ctx, "send notification", slog.Error(err))
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Failed to send test notification",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// @Summary Get user notification preferences
|
||||
// @ID get-user-notification-preferences
|
||||
// @Security CoderSessionToken
|
||||
|
@ -39,3 +39,8 @@ var (
|
||||
|
||||
TemplateWorkspaceBuildsFailedReport = uuid.MustParse("34a20db2-e9cc-4a93-b0e4-8569699d7a00")
|
||||
)
|
||||
|
||||
// Notification-related events.
|
||||
var (
|
||||
TemplateTestNotification = uuid.MustParse("c425f63e-716a-4bf4-ae24-78348f706c3f")
|
||||
)
|
||||
|
@ -1125,6 +1125,16 @@ func TestNotificationTemplates_Golden(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TemplateTestNotification",
|
||||
id: notifications.TemplateTestNotification,
|
||||
payload: types.MessagePayload{
|
||||
UserName: "Bobby",
|
||||
UserEmail: "bobby@coder.com",
|
||||
UserUsername: "bobby",
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// We must have a test case for every notification_template. This is enforced below:
|
||||
|
79
coderd/notifications/testdata/rendered-templates/smtp/TemplateTestNotification.html.golden
vendored
Normal file
79
coderd/notifications/testdata/rendered-templates/smtp/TemplateTestNotification.html.golden
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
From: system@coder.com
|
||||
To: bobby@coder.com
|
||||
Subject: A test notification
|
||||
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
|
||||
|
||||
Hi Bobby,
|
||||
|
||||
This is a test notification.
|
||||
|
||||
|
||||
View notification settings: http://test.com/deployment/notifications?tab=3D=
|
||||
settings
|
||||
|
||||
--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>A test notification</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;">
|
||||
A test notification
|
||||
</h1>
|
||||
<div style=3D"line-height: 1.5;">
|
||||
<p>Hi Bobby,</p>
|
||||
|
||||
<p>This is a test notification.</p>
|
||||
</div>
|
||||
<div style=3D"text-align: center; margin-top: 32px;">
|
||||
=20
|
||||
<a href=3D"http://test.com/deployment/notifications?tab=3Dsettings"=
|
||||
style=3D"display: inline-block; padding: 13px 24px; background-color: #020=
|
||||
617; color: #f8fafc; text-decoration: none; border-radius: 8px; margin: 0 4=
|
||||
px;">
|
||||
View notification settings
|
||||
</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=3Dc42=
|
||||
5f63e-716a-4bf4-ae24-78348f706c3f" style=3D"color: #2563eb; text-decoration=
|
||||
: none;">Stop receiving emails like this</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4--
|
25
coderd/notifications/testdata/rendered-templates/webhook/TemplateTestNotification.json.golden
vendored
Normal file
25
coderd/notifications/testdata/rendered-templates/webhook/TemplateTestNotification.json.golden
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"_version": "1.1",
|
||||
"msg_id": "00000000-0000-0000-0000-000000000000",
|
||||
"payload": {
|
||||
"_version": "1.1",
|
||||
"notification_name": "Test Notification",
|
||||
"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": "View notification settings",
|
||||
"url": "http://test.com/deployment/notifications?tab=settings"
|
||||
}
|
||||
],
|
||||
"labels": {},
|
||||
"data": null
|
||||
},
|
||||
"title": "A test notification",
|
||||
"title_markdown": "A test notification",
|
||||
"body": "Hi Bobby,\n\nThis is a test notification.",
|
||||
"body_markdown": "Hi Bobby,\n\nThis is a test notification."
|
||||
}
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/notifications"
|
||||
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
@ -317,3 +318,58 @@ func TestNotificationDispatchMethods(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationTest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OwnerCanSendTestNotification", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
notifyEnq := ¬ificationstest.FakeEnqueuer{}
|
||||
ownerClient := coderdtest.New(t, &coderdtest.Options{
|
||||
DeploymentValues: coderdtest.DeploymentValues(t),
|
||||
NotificationsEnqueuer: notifyEnq,
|
||||
})
|
||||
|
||||
// Given: A user with owner permissions.
|
||||
_ = coderdtest.CreateFirstUser(t, ownerClient)
|
||||
|
||||
// When: They attempt to send a test notification.
|
||||
err := ownerClient.PostTestNotification(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Then: We expect a notification to have been sent.
|
||||
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
|
||||
require.Len(t, sent, 1)
|
||||
})
|
||||
|
||||
t.Run("MemberCannotSendTestNotification", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
notifyEnq := ¬ificationstest.FakeEnqueuer{}
|
||||
ownerClient := coderdtest.New(t, &coderdtest.Options{
|
||||
DeploymentValues: coderdtest.DeploymentValues(t),
|
||||
NotificationsEnqueuer: notifyEnq,
|
||||
})
|
||||
|
||||
// Given: A user without owner permissions.
|
||||
ownerUser := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
memberClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, ownerUser.OrganizationID)
|
||||
|
||||
// When: They attempt to send a test notification.
|
||||
err := memberClient.PostTestNotification(ctx)
|
||||
|
||||
// Then: We expect a forbidden error with no notifications sent
|
||||
var sdkError *codersdk.Error
|
||||
require.Error(t, err)
|
||||
require.ErrorAsf(t, err, &sdkError, "error should be of type *codersdk.Error")
|
||||
require.Equal(t, http.StatusForbidden, sdkError.StatusCode())
|
||||
|
||||
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
|
||||
require.Len(t, sent, 0)
|
||||
})
|
||||
}
|
||||
|
@ -193,6 +193,20 @@ func (c *Client) GetNotificationDispatchMethods(ctx context.Context) (Notificati
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) PostTestNotification(ctx context.Context) error {
|
||||
res, err := c.Request(ctx, http.MethodPost, "/api/v2/notifications/test", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateNotificationTemplateMethod struct {
|
||||
Method string `json:"method,omitempty" example:"webhook"`
|
||||
}
|
||||
|
@ -1038,6 +1038,11 @@
|
||||
"description": "Resume notifications",
|
||||
"path": "reference/cli/notifications_resume.md"
|
||||
},
|
||||
{
|
||||
"title": "notifications test",
|
||||
"description": "Send a test notification",
|
||||
"path": "reference/cli/notifications_test.md"
|
||||
},
|
||||
{
|
||||
"title": "open",
|
||||
"description": "Open a workspace",
|
||||
|
20
docs/reference/api/notifications.md
generated
20
docs/reference/api/notifications.md
generated
@ -182,6 +182,26 @@ Status Code **200**
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Send a test notification
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X POST http://coder-server:8080/api/v2/notifications/test \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`POST /notifications/test`
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|--------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get user notification preferences
|
||||
|
||||
### Code samples
|
||||
|
14
docs/reference/cli/notifications.md
generated
14
docs/reference/cli/notifications.md
generated
@ -26,11 +26,17 @@ server or Webhook not responding).:
|
||||
- Resume Coder notifications:
|
||||
|
||||
$ coder notifications resume
|
||||
|
||||
- Send a test notification. Administrators can use this to verify the notification
|
||||
target settings.:
|
||||
|
||||
$ coder notifications test
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Name | Purpose |
|
||||
|--------------------------------------------------|----------------------|
|
||||
| [<code>pause</code>](./notifications_pause.md) | Pause notifications |
|
||||
| [<code>resume</code>](./notifications_resume.md) | Resume notifications |
|
||||
| Name | Purpose |
|
||||
|--------------------------------------------------|--------------------------|
|
||||
| [<code>pause</code>](./notifications_pause.md) | Pause notifications |
|
||||
| [<code>resume</code>](./notifications_resume.md) | Resume notifications |
|
||||
| [<code>test</code>](./notifications_test.md) | Send a test notification |
|
||||
|
10
docs/reference/cli/notifications_test.md
generated
Normal file
10
docs/reference/cli/notifications_test.md
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<!-- DO NOT EDIT | GENERATED CONTENT -->
|
||||
# notifications test
|
||||
|
||||
Send a test notification
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
coder notifications test
|
||||
```
|
Reference in New Issue
Block a user