mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: expose Markdown fields in webhook payload (#14931)
Fixes: https://github.com/coder/coder/issues/14930
This commit is contained in:
@@ -28,43 +28,47 @@ type WebhookHandler struct {
|
||||
|
||||
// WebhookPayload describes the JSON payload to be delivered to the configured webhook endpoint.
|
||||
type WebhookPayload struct {
|
||||
Version string `json:"_version"`
|
||||
MsgID uuid.UUID `json:"msg_id"`
|
||||
Payload types.MessagePayload `json:"payload"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Version string `json:"_version"`
|
||||
MsgID uuid.UUID `json:"msg_id"`
|
||||
Payload types.MessagePayload `json:"payload"`
|
||||
Title string `json:"title"`
|
||||
TitleMarkdown string `json:"title_markdown"`
|
||||
Body string `json:"body"`
|
||||
BodyMarkdown string `json:"body_markdown"`
|
||||
}
|
||||
|
||||
func NewWebhookHandler(cfg codersdk.NotificationsWebhookConfig, log slog.Logger) *WebhookHandler {
|
||||
return &WebhookHandler{cfg: cfg, log: log, cl: &http.Client{}}
|
||||
}
|
||||
|
||||
func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
|
||||
func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) {
|
||||
if w.cfg.Endpoint.String() == "" {
|
||||
return nil, xerrors.New("webhook endpoint not defined")
|
||||
}
|
||||
|
||||
title, err := markdown.PlaintextFromMarkdown(titleTmpl)
|
||||
titlePlaintext, err := markdown.PlaintextFromMarkdown(titleMarkdown)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("render title: %w", err)
|
||||
}
|
||||
body, err := markdown.PlaintextFromMarkdown(bodyTmpl)
|
||||
bodyPlaintext, err := markdown.PlaintextFromMarkdown(bodyMarkdown)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("render body: %w", err)
|
||||
}
|
||||
|
||||
return w.dispatch(payload, title, body, w.cfg.Endpoint.String()), nil
|
||||
return w.dispatch(payload, titlePlaintext, titleMarkdown, bodyPlaintext, bodyMarkdown, w.cfg.Endpoint.String()), nil
|
||||
}
|
||||
|
||||
func (w *WebhookHandler) dispatch(msgPayload types.MessagePayload, title, body, endpoint string) DeliveryFunc {
|
||||
func (w *WebhookHandler) dispatch(msgPayload types.MessagePayload, titlePlaintext, titleMarkdown, bodyPlaintext, bodyMarkdown, endpoint string) DeliveryFunc {
|
||||
return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) {
|
||||
// Prepare payload.
|
||||
payload := WebhookPayload{
|
||||
Version: "1.0",
|
||||
MsgID: msgID,
|
||||
Title: title,
|
||||
Body: body,
|
||||
Payload: msgPayload,
|
||||
Version: "1.1",
|
||||
MsgID: msgID,
|
||||
Title: titlePlaintext,
|
||||
TitleMarkdown: titleMarkdown,
|
||||
Body: bodyPlaintext,
|
||||
BodyMarkdown: bodyMarkdown,
|
||||
Payload: msgPayload,
|
||||
}
|
||||
m, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
|
||||
@@ -28,17 +28,15 @@ func TestWebhook(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
titleTemplate = "this is the title ({{.Labels.foo}})"
|
||||
bodyTemplate = "this is the body ({{.Labels.baz}})"
|
||||
titlePlaintext = "this is the title"
|
||||
titleMarkdown = "this *is* _the_ title"
|
||||
bodyPlaintext = "this is the body"
|
||||
bodyMarkdown = "~this~ is the `body`"
|
||||
)
|
||||
|
||||
msgPayload := types.MessagePayload{
|
||||
Version: "1.0",
|
||||
NotificationName: "test",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"baz": "quux",
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@@ -61,6 +59,11 @@ func TestWebhook(t *testing.T) {
|
||||
assert.Equal(t, msgID, payload.MsgID)
|
||||
assert.Equal(t, msgID.String(), r.Header.Get("X-Message-Id"))
|
||||
|
||||
assert.Equal(t, titlePlaintext, payload.Title)
|
||||
assert.Equal(t, titleMarkdown, payload.TitleMarkdown)
|
||||
assert.Equal(t, bodyPlaintext, payload.Body)
|
||||
assert.Equal(t, bodyMarkdown, payload.BodyMarkdown)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err = w.Write([]byte(fmt.Sprintf("received %s", payload.MsgID)))
|
||||
assert.NoError(t, err)
|
||||
@@ -138,7 +141,7 @@ func TestWebhook(t *testing.T) {
|
||||
Endpoint: *serpent.URLOf(endpoint),
|
||||
}
|
||||
handler := dispatch.NewWebhookHandler(cfg, logger.With(slog.F("test", tc.name)))
|
||||
deliveryFn, err := handler.Dispatcher(msgPayload, titleTemplate, bodyTemplate)
|
||||
deliveryFn, err := handler.Dispatcher(msgPayload, titleMarkdown, bodyMarkdown)
|
||||
require.NoError(t, err)
|
||||
|
||||
retryable, err := deliveryFn(ctx, msgID)
|
||||
|
||||
@@ -249,7 +249,7 @@ func TestWebhookDispatch(t *testing.T) {
|
||||
|
||||
// THEN: the webhook is received by the mock server and has the expected contents
|
||||
payload := testutil.RequireRecvCtx(testutil.Context(t, testutil.WaitShort), t, sent)
|
||||
require.EqualValues(t, "1.0", payload.Version)
|
||||
require.EqualValues(t, "1.1", payload.Version)
|
||||
require.Equal(t, *msgID, payload.MsgID)
|
||||
require.Equal(t, payload.Payload.Labels, input)
|
||||
require.Equal(t, payload.Payload.UserEmail, email)
|
||||
|
||||
Reference in New Issue
Block a user