1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

Feat: webhook markdown (#7658)

Add ability to format format event as Markdown in generic webhooks,
similar to Datadog integration.
Closes https://github.com/Unleash/unleash/issues/7646

Co-authored-by: Nuno Góis <github@nunogois.com>
This commit is contained in:
Tymoteusz Czech 2024-07-25 11:45:20 +02:00 committed by GitHub
parent 245c3e119d
commit 369518cd7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 71 additions and 2 deletions

View File

@ -2,6 +2,7 @@ import { TextField, Typography } from '@mui/material';
import type { AddonParameterSchema, AddonSchema } from 'openapi';
import type { ChangeEventHandler } from 'react';
import { styled } from '@mui/material';
import { Markdown } from 'component/common/Markdown/Markdown';
const MASKED_VALUE = '*****';
@ -62,7 +63,11 @@ export const IntegrationParameterTextField = ({
error={Boolean(error)}
onChange={setParameterValue(definition.name)}
variant='outlined'
helperText={definition.description}
helperText={
definition.description ? (
<Markdown>{definition.description}</Markdown>
) : undefined
}
/>
);
};

View File

@ -188,6 +188,7 @@
"@types/make-fetch-happen": "10.0.4",
"@types/memoizee": "0.4.11",
"@types/mime": "3.0.4",
"@types/mustache": "^4.2.5",
"@types/node": "20.14.10",
"@types/nodemailer": "6.4.15",
"@types/owasp-password-strength-test": "1.3.2",

View File

@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Webhook integration should allow for eventJson and eventMarkdown in bodyTemplate 1`] = `
"{
"json": "{\\"id\\":1,\\"createdAt\\":\\"2024-07-24T00:00:00.000Z\\",\\"createdByUserId\\":-1337,\\"type\\":\\"feature-created\\",\\"createdBy\\":\\"some@user.com\\",\\"featureName\\":\\"some-toggle\\",\\"project\\":\\"default\\",\\"data\\":{\\"name\\":\\"some-toggle\\",\\"enabled\\":false,\\"strategies\\":[{\\"name\\":\\"default\\"}]}}",
"markdown": "*some@user.com* created *[some-toggle](http://some-url.com/projects/default/features/some-toggle)* in project *[default](http://some-url.com/projects/default)*"
}"
`;

View File

@ -91,7 +91,7 @@ const webhookDefinition: IAddonDefinition = {
"json": {{{eventJson}}}
}`,
description:
"(Optional) You may format the body using a mustache template. If you don't specify anything, the format will similar to the events format (https://docs.getunleash.io/reference/api/legacy/unleash/admin/events). You can use {{{eventJson}}} to include entire serialized event.",
'(Optional) You may format the body using a mustache template. If you don\'t specify anything, the format will be similar to the [events format](https://docs.getunleash.io/reference/api/legacy/unleash/admin/events). You can use `{{{eventJson}}}` to include the entire serialized event, or `"{{eventMarkdown}}"` for the formatted description.',
type: 'textfield',
required: false,
sensitive: false,

View File

@ -112,6 +112,41 @@ describe('Webhook integration', () => {
expect(call.options.body).toBe('feature-created on toggle some-toggle');
});
test('should allow for eventJson and eventMarkdown in bodyTemplate', async () => {
const addon = new WebhookAddon(ARGS);
const event: IEvent = {
id: 1,
createdAt: new Date('2024-07-24T00:00:00.000Z'),
createdByUserId: SYSTEM_USER_ID,
type: FEATURE_CREATED,
createdBy: 'some@user.com',
featureName: 'some-toggle',
project: 'default',
data: {
name: 'some-toggle',
enabled: false,
strategies: [{ name: 'default' }],
},
};
const parameters = {
url: 'http://test.webhook.com/plain',
bodyTemplate:
'{\n "json": {{{eventJson}}},\n "markdown": "{{eventMarkdown}}"\n}',
contentType: 'text/plain',
};
addon.handleEvent(event, parameters, INTEGRATION_ID);
const call = fetchRetryCalls[0];
expect(fetchRetryCalls.length).toBe(1);
expect(call.url).toBe(parameters.url);
expect(call.options.headers['Content-Type']).toBe('text/plain');
expect(call.options.body).toMatchSnapshot();
expect(JSON.parse(JSON.parse(call.options.body).json)).toEqual(
serializeDates(event),
);
});
test('Should format event with "authorization"', () => {
const addon = new WebhookAddon(ARGS);
const event: IEvent = {

View File

@ -4,6 +4,11 @@ import definition from './webhook-definition';
import type { IEvent } from '../types/events';
import { type IAddonConfig, serializeDates } from '../types';
import type { IntegrationEventState } from '../features/integration-events/integration-events-store';
import {
type FeatureEventFormatter,
FeatureEventFormatterMd,
LinkStyle,
} from './feature-event-formatter-md';
interface IParameters {
url: string;
@ -14,8 +19,14 @@ interface IParameters {
}
export default class Webhook extends Addon {
private msgFormatter: FeatureEventFormatter;
constructor(args: IAddonConfig) {
super(definition, args);
this.msgFormatter = new FeatureEventFormatterMd(
args.unleashUrl,
LinkStyle.MD,
);
}
async handleEvent(
@ -37,6 +48,7 @@ export default class Webhook extends Addon {
event,
// Stringify twice to avoid escaping in Mustache
eventJson: JSON.stringify(JSON.stringify(event)),
eventMarkdown: this.msgFormatter.format(event).text,
};
let body: string | undefined;

View File

@ -2085,6 +2085,13 @@ __metadata:
languageName: node
linkType: hard
"@types/mustache@npm:^4.2.5":
version: 4.2.5
resolution: "@types/mustache@npm:4.2.5"
checksum: 10c0/624975c39068d47407eadb89628aaff5ef60f3b7a71eef92a254310896a4e90518a01dcf71d95779ab2c986034a6ca5403d22fea237c67ff87f2e2b3fb794ea6
languageName: node
linkType: hard
"@types/node-fetch@npm:*":
version: 2.6.2
resolution: "@types/node-fetch@npm:2.6.2"
@ -9759,6 +9766,7 @@ __metadata:
"@types/make-fetch-happen": "npm:10.0.4"
"@types/memoizee": "npm:0.4.11"
"@types/mime": "npm:3.0.4"
"@types/mustache": "npm:^4.2.5"
"@types/node": "npm:20.14.10"
"@types/nodemailer": "npm:6.4.15"
"@types/owasp-password-strength-test": "npm:1.3.2"