Skip to content

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy