8000 feat: expose Markdown fields in webhook payload (#14931) · coder/coder@0aa84b1 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 0aa84b1

Browse files
authored
feat: expose Markdown fields in webhook payload (#14931)
Fixes: #14930
1 parent 2f043d7 commit 0aa84b1

File tree

5 files changed

+41
-32
lines changed

5 files changed

+41
-32
lines changed

coderd/notifications/dispatch/webhook.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,43 +28,47 @@ type WebhookHandler struct {
2828

2929
// WebhookPayload describes the JSON payload to be delivered to the configured webhook endpoint.
3030
type WebhookPayload struct {
31-
Version string `json:"_version"`
32-
MsgID uuid.UUID `json:"msg_id"`
33-
Payload types.MessagePayload `json:"payload"`
34-
Title string `json:"title"`
35-
Body string `json:"body"`
31+
Version string `json:"_version"`
32+
MsgID uuid.UUID `json:"msg_id"`
33+
Payload types.MessagePayload `json:"payload"`
34+
Title string `json:"title"`
35+
TitleMarkdown string `json:"title_markdown"`
36+
Body string `json:"body"`
37+
BodyMarkdown string `json:"body_markdown"`
3638
}
3739

3840
func NewWebhookHandler(cfg codersdk.NotificationsWebhookConfig, log slog.Logger) *WebhookHandler {
3941
return &WebhookHandler{cfg: cfg, log: log, cl: &http.Client{}}
4042
}
4143

42-
func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
44+
func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) {
4345
if w.cfg.Endpoint.String() == "" {
4446
return nil, xerrors.New("webhook endpoint not defined")
4547
}
4648

47-
title, err := markdown.PlaintextFromMarkdown(titleTmpl)
49+
titlePlaintext, err := markdown.PlaintextFromMarkdown(titleMarkdown)
4850
if err != nil {
4951
return nil, xerrors.Errorf("render title: %w", err)
5052
}
51-
body, err := markdown.PlaintextFromMarkdown(bodyTmpl)
53+
bodyPlaintext, err := markdown.PlaintextFromMarkdown(bodyMarkdown)
5254
if err != nil {
5355
return nil, xerrors.Errorf("render body: %w", err)
5456
}
5557

56-
return w.dispatch(payload, title, body, w.cfg.Endpoint.String()), nil
58+
return w.dispatch(payload, titlePlaintext, titleMarkdown, bodyPlaintext, bodyMarkdown, w.cfg.Endpoint.String()), nil
5759
}
5860

59-
func (w *WebhookHandler) dispatch(msgPayload types.MessagePayload, title, body, endpoint string) DeliveryFunc {
61+
func (w *WebhookHandler) dispatch(msgPayload types.MessagePayload, titlePlaintext, titleMarkdown, bodyPlaintext, bodyMarkdown, endpoint string) DeliveryFunc {
6062
return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) {
6163
// Prepare payload.
6264
payload := WebhookPayload{
63-
Version: "1.0",
64-
MsgID: msgID,
65-
Title: title,
66-
Body: body,
67-
Payload: msgPayload,
65+
Version: "1.1",
66+
MsgID: msgID,
67+
Title: titlePlaintext,
68+
TitleMarkdown: titleMarkdown,
69+
Body: bodyPlaintext,
70+
BodyMarkdown: bodyMarkdown,
71+
Payload: msgPayload,
6872
}
6973
m, err := json.Marshal(payload)
7074
if err != nil {

coderd/notifications/dispatch/webhook_test.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,15 @@ func TestWebhook(t *testing.T) {
2828
t.Parallel()
2929

3030
const (
31-
titleTemplate = "this is the title ({{.Labels.foo}})"
32-
bodyTemplate = "this is the body ({{.Labels.baz}})"
31+
titlePlaintext = "this is the title"
32+
titleMarkdown = "this *is* _the_ title"
33+
bodyPlaintext = "this is the body"
34+
bodyMarkdown = "~this~ is the `body`"
3335
)
3436

3537
msgPayload := types.MessagePayload{
3638
Version: "1.0",
3739
NotificationName: "test",
38-
Labels: map[string]string{
39-
"foo": "bar",
40-
"baz": "quux",
41-
},
4240
}
4341

4442
tests := []struct {
@@ -61,6 +59,11 @@ func TestWebhook(t *testing.T) {
6159
assert.Equal(t, msgID, payload.MsgID)
6260
assert.Equal(t, msgID.String(), r.Header.Get("X-Message-Id"))
6361

62+
assert.Equal(t, titlePlaintext, payload.Title)
63+
assert.Equal(t, titleMarkdown, payload.TitleMarkdown)
64+
assert.Equal(t, bodyPlaintext, payload.Body)
65+
assert.Equal(t, bodyMarkdown, payload.BodyMarkdown)
66+
6467
w.WriteHeader(http.StatusOK)
6568
_, err = w.Write([]byte(fmt.Sprintf("received %s", payload.MsgID)))
6669
assert.NoError(t, err)
@@ -138,7 +141,7 @@ func TestWebhook(t *testing.T) {
138141
Endpoint: *serpent.URLOf(endpoint),
139142
}
140143
handler := dispatch.NewWebhookHandler(cfg, logger.With(slog.F("test", tc.name)))
141-
deliveryFn, err := handler.Dispatcher(msgPayload, titleTemplate, bodyTemplate)
144+
deliveryFn, err := handler.Dispatcher(msgPayload, titleMarkdown, bodyMarkdown)
142145
require.NoError(t, err)
143146

144147
retryable, err := deliveryFn(ctx, msgID)

coderd/notifications/notifications_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func TestWebhookDispatch(t *testing.T) {
249249

250250
// THEN: the webhook is received by the mock server and has the expected contents
251251
payload := testutil.RequireRecvCtx(testutil.Context(t, testutil.WaitShort), t, sent)
252-
require.EqualValues(t, "1.0", payload.Version)
252+
require.EqualValues(t, "1.1", payload.Version)
253253
require.Equal(t, *msgID, payload.MsgID)
254254
require.Equal(t, payload.Payload.Labels, input)
255255
require.Equal(t, payload.Payload.UserEmail, email)

docs/admin/notifications/slack.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ receiver.router.post("/v1/webhook", async (req, res) => {
9090
return res.status(400).send("Error: request body is missing");
9191
}
9292

93-
const { title, body } = req.body;
94-
if (!title || !body) {
95-
return res.status(400).send('Error: missing fields: "title", or "body"');
93+
const { title_markdown, body_markdown } = req.body;
94+
if (!title_markdown || !body_markdown) {
95+
return res
96+
.status(400)
97+
.send('Error: missing fields: "title_markdown", or "body_markdown"');
9698
}
9799

98100
const payload = req.body.payload;
@@ -118,11 +120,11 @@ receiver.router.post("/v1/webhook", async (req, res) => {
118120
blocks: [
119121
{
120122
type: "header",
121-
text: { type: "plain_text", text: title },
123+
text: { type: "mrkdwn", text: title_markdown },
122124
},
123125
{
124126
type: "section",
125-
text: { type: "mrkdwn", text: body },
127+
text: { type: "mrkdwn", text: body_markdown },
126128
},
127129
],
128130
};

docs/admin/notifications/teams.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ The process of setting up a Teams workflow consists of three key steps:
6767
}
6868
}
6969
},
70-
"title": {
70+
"title_markdown": {
7171
"type": "string"
7272
},
73-
"body": {
73+
"body_markdown": {
7474
"type": "string"
7575
}
7676
}
@@ -108,11 +108,11 @@ The process of setting up a Teams workflow consists of three key steps:
108108
},
109109
{
110110
"type": "TextBlock",
111-
"text": "**@{replace(body('Parse_JSON')?['title'], '"', '\"')}**"
111+
"text": "**@{replace(body('Parse_JSON')?['title_markdown'], '"', '\"')}**"
112112
},
113113
{
114114
"type": "TextBlock",
115-
"text": "@{replace(body('Parse_JSON')?['body'], '"', '\"')}",
115+
"text": "@{replace(body('Parse_JSON')?['body_markdown'], '"', '\"')}",
116116
"wrap": true
117117
},
118118
{

0 commit comments

Comments
 (0)
0