From a6bf5f7bfdc04adfc3bb42d4508a4e9e1eaf8828 Mon Sep 17 00:00:00 2001 From: Mahendra Date: Fri, 3 Nov 2023 23:17:31 -0300 Subject: [PATCH] feat: add grafana on call as chat provider --- .../app/integrations/dtos/credentials.dto.ts | 25 ++++++ .../src/app/shared/dtos/subscriber-channel.ts | 25 ++++++ .../images/providers/dark/grafana-on-call.png | Bin 0 -> 31059 bytes .../providers/dark/square/grafana-on-call.svg | 12 +++ .../providers/light/grafana-on-call.png | Bin 0 -> 31059 bytes .../light/square/grafana-on-call.svg | 12 +++ .../multi-provider/sort-providers.ts | 1 + .../integration/integration.schema.ts | 5 ++ .../src/consts/providers/channels/chat.ts | 10 ++- .../credentials/provider-credentials.ts | 39 +++++++++ .../src/consts/providers/provider.enum.ts | 6 ++ .../integration/credential.interface.ts | 5 ++ packages/application-generic/package.json | 1 + .../src/factories/chat/chat.factory.ts | 2 + .../chat/handlers/grafana-on-call.handler.ts | 15 ++++ pnpm-lock.yaml | 49 +++++++++++ providers/grafana-on-call/.czrc | 3 + providers/grafana-on-call/.eslintrc.json | 3 + providers/grafana-on-call/.gitignore | 9 ++ providers/grafana-on-call/README.md | 11 +++ providers/grafana-on-call/jest.config.js | 8 ++ providers/grafana-on-call/package.json | 79 ++++++++++++++++++ providers/grafana-on-call/src/index.ts | 1 + .../src/lib/grafana-on-call.provider.spec.ts | 42 ++++++++++ .../src/lib/grafana-on-call.provider.ts | 44 ++++++++++ providers/grafana-on-call/tsconfig.json | 10 +++ .../grafana-on-call/tsconfig.module.json | 9 ++ 27 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 apps/web/public/static/images/providers/dark/grafana-on-call.png create mode 100644 apps/web/public/static/images/providers/dark/square/grafana-on-call.svg create mode 100644 apps/web/public/static/images/providers/light/grafana-on-call.png create mode 100644 apps/web/public/static/images/providers/light/square/grafana-on-call.svg create mode 100644 packages/application-generic/src/factories/chat/handlers/grafana-on-call.handler.ts create mode 100644 providers/grafana-on-call/.czrc create mode 100644 providers/grafana-on-call/.eslintrc.json create mode 100644 providers/grafana-on-call/.gitignore create mode 100644 providers/grafana-on-call/README.md create mode 100644 providers/grafana-on-call/jest.config.js create mode 100644 providers/grafana-on-call/package.json create mode 100644 providers/grafana-on-call/src/index.ts create mode 100644 providers/grafana-on-call/src/lib/grafana-on-call.provider.spec.ts create mode 100644 providers/grafana-on-call/src/lib/grafana-on-call.provider.ts create mode 100644 providers/grafana-on-call/tsconfig.json create mode 100644 providers/grafana-on-call/tsconfig.module.json diff --git a/apps/api/src/app/integrations/dtos/credentials.dto.ts b/apps/api/src/app/integrations/dtos/credentials.dto.ts index 969525bd7f1..95049a0a749 100644 --- a/apps/api/src/app/integrations/dtos/credentials.dto.ts +++ b/apps/api/src/app/integrations/dtos/credentials.dto.ts @@ -171,4 +171,29 @@ export class CredentialsDto implements ICredentials { @IsString() @IsOptional() instanceId?: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + alertUid?: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + title?: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + imageUrl?: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + state?: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + externalLink?: string; } diff --git a/apps/api/src/app/shared/dtos/subscriber-channel.ts b/apps/api/src/app/shared/dtos/subscriber-channel.ts index cb223671a7b..2c14d71907f 100644 --- a/apps/api/src/app/shared/dtos/subscriber-channel.ts +++ b/apps/api/src/app/shared/dtos/subscriber-channel.ts @@ -15,6 +15,31 @@ export class ChannelCredentials { description: 'Contains an array of the subscriber device tokens for a given provider. Used on Push integrations', }) deviceTokens?: string[]; + + @ApiPropertyOptional({ + description: 'alert_uid for grafana on-call webhook payload', + }) + alertUid?: string; + + @ApiPropertyOptional({ + description: 'title to be used with grafana on call webhook', + }) + title?: string; + + @ApiPropertyOptional({ + description: 'image_url property fo grafana on call webhook', + }) + imageUrl?: string; + + @ApiPropertyOptional({ + description: 'state property fo grafana on call webhook', + }) + state?: string; + + @ApiPropertyOptional({ + description: 'link_to_upstream_details property fo grafana on call webhook', + }) + externalUrl?: string; } export class SubscriberChannel { diff --git a/apps/web/public/static/images/providers/dark/grafana-on-call.png b/apps/web/public/static/images/providers/dark/grafana-on-call.png new file mode 100644 index 0000000000000000000000000000000000000000..caccf8b4aafbfa97d29acf4d02cc660366725df3 GIT binary patch literal 31059 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�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≤!0%UMNHZ0glb2^zV3fdcM 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 V=<&~b&L02(002ovPDHLkV1g(K79#)v literal 0 HcmV?d00001 diff --git a/apps/web/public/static/images/providers/dark/square/grafana-on-call.svg b/apps/web/public/static/images/providers/dark/square/grafana-on-call.svg new file mode 100644 index 00000000000..09579340d1d --- /dev/null +++ b/apps/web/public/static/images/providers/dark/square/grafana-on-call.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/static/images/providers/light/grafana-on-call.png b/apps/web/public/static/images/providers/light/grafana-on-call.png new file mode 100644 index 0000000000000000000000000000000000000000..caccf8b4aafbfa97d29acf4d02cc660366725df3 GIT binary patch literal 31059 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�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≤!0%UMNHZ0glb2^zV3fdcM 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 V=<&~b&L02(002ovPDHLkV1g(K79#)v literal 0 HcmV?d00001 diff --git a/apps/web/public/static/images/providers/light/square/grafana-on-call.svg b/apps/web/public/static/images/providers/light/square/grafana-on-call.svg new file mode 100644 index 00000000000..09579340d1d --- /dev/null +++ b/apps/web/public/static/images/providers/light/square/grafana-on-call.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/src/pages/integrations/components/multi-provider/sort-providers.ts b/apps/web/src/pages/integrations/components/multi-provider/sort-providers.ts index 26e3184cff5..0aa914f9839 100644 --- a/apps/web/src/pages/integrations/components/multi-provider/sort-providers.ts +++ b/apps/web/src/pages/integrations/components/multi-provider/sort-providers.ts @@ -16,6 +16,7 @@ const providers: Record = { ChatProviderIdEnum.MsTeams, ChatProviderIdEnum.Discord, ChatProviderIdEnum.Mattermost, + ChatProviderIdEnum.GrafanaOnCall, ], [ChannelTypeEnum.EMAIL]: [ EmailProviderIdEnum.SendGrid, diff --git a/libs/dal/src/repositories/integration/integration.schema.ts b/libs/dal/src/repositories/integration/integration.schema.ts index 5f42f9f89dc..492956caa67 100644 --- a/libs/dal/src/repositories/integration/integration.schema.ts +++ b/libs/dal/src/repositories/integration/integration.schema.ts @@ -53,6 +53,11 @@ const integrationSchema = new Schema( authenticateByToken: Schema.Types.Boolean, authenticationTokenKey: Schema.Types.String, instanceId: Schema.Types.String, + alertUid: Schema.Types.String, + title: Schema.Types.String, + imageUrl: Schema.Types.String, + state: Schema.Types.String, + externalLink: Schema.Types.String, }, active: { type: Schema.Types.Boolean, diff --git a/libs/shared/src/consts/providers/channels/chat.ts b/libs/shared/src/consts/providers/channels/chat.ts index 0d504bf8943..ee294985140 100644 --- a/libs/shared/src/consts/providers/channels/chat.ts +++ b/libs/shared/src/consts/providers/channels/chat.ts @@ -1,5 +1,5 @@ import { IConfigCredentials, IProviderConfig } from '../provider.interface'; -import { slackConfig } from '../credentials'; +import { grafanaOnCallConfig, slackConfig } from '../credentials'; import { ChatProviderIdEnum } from '../provider.enum'; import { ChannelTypeEnum } from '../../../types'; @@ -21,6 +21,14 @@ export const chatProviders: IProviderConfig[] = [ docReference: 'https://docs.novu.co/channels-and-providers/chat/discord', logoFileName: { light: 'discord.svg', dark: 'discord.svg' }, }, + { + id: ChatProviderIdEnum.GrafanaOnCall, + displayName: 'Grafana On Call Webhook', + channel: ChannelTypeEnum.CHAT, + credentials: grafanaOnCallConfig, + docReference: 'https://grafana.com/docs/oncall/latest/integrations/webhook/', + logoFileName: { light: 'grafana-on-call.png', dark: 'grafana-on-call.png' }, + }, { id: ChatProviderIdEnum.MsTeams, displayName: 'MSTeams', diff --git a/libs/shared/src/consts/providers/credentials/provider-credentials.ts b/libs/shared/src/consts/providers/credentials/provider-credentials.ts index 1ed1561e628..d3c36b32a00 100644 --- a/libs/shared/src/consts/providers/credentials/provider-credentials.ts +++ b/libs/shared/src/consts/providers/credentials/provider-credentials.ts @@ -475,6 +475,45 @@ export const slackConfig: IConfigCredentials[] = [ }, ]; +export const grafanaOnCallConfig: IConfigCredentials[] = [ + { + key: CredentialsKeyEnum.alertUid, + displayName: 'Alert UID', + type: 'string', + description: 'a unique alert ID for grouping, maps to alert_uid of grafana webhook body content', + required: false, + }, + { + key: CredentialsKeyEnum.title, + displayName: 'Title.', + type: 'string', + description: 'title for the alert', + required: false, + }, + { + key: CredentialsKeyEnum.imageUrl, + displayName: 'Image URL', + type: 'string', + description: 'a URL for an image attached to alert, maps to image_url of grafana webhook body content', + required: false, + }, + { + key: CredentialsKeyEnum.state, + displayName: 'Alert State', + type: 'string', + description: 'either "ok" or "alerting". Helpful for auto-resolving', + required: false, + }, + { + key: CredentialsKeyEnum.externalLink, + displayName: 'External Link', + type: 'string', + description: + 'link back to your monitoring system, maps to "link_to_upstream_details" of grafana webhook body content', + required: false, + }, +]; + export const fcmConfig: IConfigCredentials[] = [ { key: CredentialsKeyEnum.ServiceAccount, diff --git a/libs/shared/src/consts/providers/provider.enum.ts b/libs/shared/src/consts/providers/provider.enum.ts index e260d3c4051..c443248a28b 100644 --- a/libs/shared/src/consts/providers/provider.enum.ts +++ b/libs/shared/src/consts/providers/provider.enum.ts @@ -38,6 +38,11 @@ export enum CredentialsKeyEnum { ApiToken = 'apiToken', ApiURL = 'apiURL', AppID = 'appID', + alertUid = 'alertUid', + title = 'title', + imageUrl = 'imageUrl', + state = 'state', + externalLink = 'externalLink', } export enum EmailProviderIdEnum { @@ -100,6 +105,7 @@ export enum ChatProviderIdEnum { Mattermost = 'mattermost', Ryver = 'ryver', Zulip = 'zulip', + GrafanaOnCall = 'grafana-on-call', } export enum PushProviderIdEnum { diff --git a/libs/shared/src/entities/integration/credential.interface.ts b/libs/shared/src/entities/integration/credential.interface.ts index 903aa4e60fd..43461059350 100644 --- a/libs/shared/src/entities/integration/credential.interface.ts +++ b/libs/shared/src/entities/integration/credential.interface.ts @@ -36,4 +36,9 @@ export interface ICredentials { apiToken?: string; apiURL?: string; appID?: string; + alertUid?: string; + title?: string; + imageUrl?: string; + state?: string; + externalLink?: string; } diff --git a/packages/application-generic/package.json b/packages/application-generic/package.json index 4c0c7585778..e328c167e26 100644 --- a/packages/application-generic/package.json +++ b/packages/application-generic/package.json @@ -77,6 +77,7 @@ "@novu/mattermost": "^0.21.0", "@novu/messagebird": "^0.21.0", "@novu/ms-teams": "^0.21.0", + "@novu/grafana-on-call": "^0.21.0", "@novu/netcore": "^0.21.0", "@novu/nodemailer": "^0.21.0", "@novu/one-signal": "^0.21.0", diff --git a/packages/application-generic/src/factories/chat/chat.factory.ts b/packages/application-generic/src/factories/chat/chat.factory.ts index 8ff9beb5ba3..f132f73a2d3 100644 --- a/packages/application-generic/src/factories/chat/chat.factory.ts +++ b/packages/application-generic/src/factories/chat/chat.factory.ts @@ -4,6 +4,7 @@ import { IntegrationEntity } from '@novu/dal'; import { DiscordHandler } from './handlers/discord.handler'; import { MSTeamsHandler } from './handlers/msteams.handler'; import { MattermostHandler } from './handlers/mattermost.handler'; +import { GrafanaOnCallHandler } from './handlers/grafana-on-call.handler'; import { RyverHandler } from './handlers/ryver.handler'; import { ZulipHandler } from './handlers/zulip.handler'; @@ -15,6 +16,7 @@ export class ChatFactory implements IChatFactory { new MattermostHandler(), new RyverHandler(), new ZulipHandler(), + new GrafanaOnCallHandler(), ]; getHandler(integration: IntegrationEntity) { diff --git a/packages/application-generic/src/factories/chat/handlers/grafana-on-call.handler.ts b/packages/application-generic/src/factories/chat/handlers/grafana-on-call.handler.ts new file mode 100644 index 00000000000..571f2f1111e --- /dev/null +++ b/packages/application-generic/src/factories/chat/handlers/grafana-on-call.handler.ts @@ -0,0 +1,15 @@ +import { ICredentials } from '@novu/shared'; +import { ChannelTypeEnum } from '@novu/stateless'; +import { GrafanaOnCallChatProvider } from '@novu/grafana-on-call'; + +import { BaseChatHandler } from './base.handler'; + +export class GrafanaOnCallHandler extends BaseChatHandler { + constructor() { + super('grafana-on-call', ChannelTypeEnum.CHAT); + } + + buildProvider(credentials: ICredentials) { + this.provider = new GrafanaOnCallChatProvider(credentials); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a2edb0fb20..757d838c8ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2149,6 +2149,9 @@ importers: '@novu/generic-sms': specifier: ^0.21.0 version: link:../../providers/generic-sms + '@novu/grafana-on-call': + specifier: ^0.21.0 + version: link:../../providers/grafana-on-call '@novu/gupshup': specifier: ^0.21.0 version: link:../../providers/gupshup @@ -3885,6 +3888,52 @@ importers: specifier: 4.9.5 version: 4.9.5 + providers/grafana-on-call: + dependencies: + '@novu/stateless': + specifier: 0.16.3 + version: 0.16.3 + axios: + specifier: ^1.3.3 + version: 1.6.0 + uuid: + specifier: ^9.0.0 + version: 9.0.0 + devDependencies: + '@istanbuljs/nyc-config-typescript': + specifier: ~1.0.1 + version: 1.0.2(nyc@15.1.0) + '@types/jest': + specifier: ~27.5.2 + version: 27.5.2 + cspell: + specifier: ~6.19.2 + version: 6.19.2 + jest: + specifier: ~27.5.1 + version: 27.5.1(ts-node@10.9.1) + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 + nyc: + specifier: ~15.1.0 + version: 15.1.0 + prettier: + specifier: ~2.8.0 + version: 2.8.7 + rimraf: + specifier: ~3.0.2 + version: 3.0.2 + ts-jest: + specifier: ~27.1.5 + version: 27.1.5(@babel/core@7.23.2)(@types/jest@27.5.2)(jest@27.5.1)(typescript@4.9.5) + ts-node: + specifier: ~10.9.1 + version: 10.9.1(@types/node@14.18.42)(typescript@4.9.5) + typescript: + specifier: 4.9.5 + version: 4.9.5 + providers/gupshup: dependencies: '@novu/stateless': diff --git a/providers/grafana-on-call/.czrc b/providers/grafana-on-call/.czrc new file mode 100644 index 00000000000..d1bcc209ca1 --- /dev/null +++ b/providers/grafana-on-call/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/providers/grafana-on-call/.eslintrc.json b/providers/grafana-on-call/.eslintrc.json new file mode 100644 index 00000000000..ec40100be69 --- /dev/null +++ b/providers/grafana-on-call/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} diff --git a/providers/grafana-on-call/.gitignore b/providers/grafana-on-call/.gitignore new file mode 100644 index 00000000000..963d5292865 --- /dev/null +++ b/providers/grafana-on-call/.gitignore @@ -0,0 +1,9 @@ +.idea/* +.nyc_output +build +node_modules +test +src/**.js +coverage +*.log +package-lock.json diff --git a/providers/grafana-on-call/README.md b/providers/grafana-on-call/README.md new file mode 100644 index 00000000000..d92f85768c3 --- /dev/null +++ b/providers/grafana-on-call/README.md @@ -0,0 +1,11 @@ +# Novu GrafanaOnCall Provider + +A GrafanaOnCall chat provider library for [@novu/node](https://github.com/novuhq/novu) + +## Usage + +```javascript + import { GrafanaOnCallChatProvider } from '@novu/grafana-on-call'; + + const provider = new GrafanaOnCallChatProvider({ alertUid: "123", externalLink: "link", imageUrl: "url", state: "ok", title: "title" }); +``` diff --git a/providers/grafana-on-call/jest.config.js b/providers/grafana-on-call/jest.config.js new file mode 100644 index 00000000000..ac8bee2b638 --- /dev/null +++ b/providers/grafana-on-call/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + "moduleNameMapper": { + "axios": "axios/dist/node/axios.cjs" + } +}; diff --git a/providers/grafana-on-call/package.json b/providers/grafana-on-call/package.json new file mode 100644 index 00000000000..b286f0a121c --- /dev/null +++ b/providers/grafana-on-call/package.json @@ -0,0 +1,79 @@ +{ + "name": "@novu/grafana-on-call", + "version": "0.21.0", + "description": "A grafana-on-call wrapper for novu", + "main": "build/main/index.js", + "typings": "build/main/index.d.ts", + "module": "build/module/index.js", + "private": false, + "repository": "https://github.com/novuhq/novu", + "license": "MIT", + "keywords": [], + "scripts": { + "prebuild": "rimraf build", + "build": "run-p build:*", + "build:main": "tsc -p tsconfig.json", + "build:module": "tsc -p tsconfig.module.json", + "fix": "run-s fix:*", + "fix:prettier": "prettier \"src/**/*.ts\" --write", + "fix:lint": "eslint src --ext .ts --fix", + "test": "run-s test:*", + "lint": "eslint src --ext .ts", + "test:unit": "jest src", + "watch:build": "tsc -p tsconfig.json -w", + "watch:test": "jest src --watch", + "reset-hard": "git clean -dfx && git reset --hard && yarn", + "prepare-release": "run-s reset-hard test" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@novu/stateless": "0.16.3", + "axios": "^1.3.3", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "~1.0.1", + "@types/jest": "~27.5.2", + "cspell": "~6.19.2", + "jest": "~27.5.1", + "npm-run-all": "^4.1.5", + "nyc": "~15.1.0", + "prettier": "~2.8.0", + "rimraf": "~3.0.2", + "ts-jest": "~27.1.5", + "ts-node": "~10.9.1", + "typescript": "4.9.5" + }, + "files": [ + "build/main", + "build/module", + "!**/*.spec.*", + "!**/*.json", + "CHANGELOG.md", + "LICENSE", + "README.md" + ], + "ava": { + "failFast": true, + "timeout": "60s", + "typescript": { + "rewritePaths": { + "src/": "build/main/" + } + }, + "files": [ + "!build/module/**" + ] + }, + "prettier": { + "singleQuote": true + }, + "nyc": { + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": [ + "**/*.spec.js" + ] + } +} diff --git a/providers/grafana-on-call/src/index.ts b/providers/grafana-on-call/src/index.ts new file mode 100644 index 00000000000..d4f06b04ae6 --- /dev/null +++ b/providers/grafana-on-call/src/index.ts @@ -0,0 +1 @@ +export * from './lib/grafana-on-call.provider'; diff --git a/providers/grafana-on-call/src/lib/grafana-on-call.provider.spec.ts b/providers/grafana-on-call/src/lib/grafana-on-call.provider.spec.ts new file mode 100644 index 00000000000..2ed2f235029 --- /dev/null +++ b/providers/grafana-on-call/src/lib/grafana-on-call.provider.spec.ts @@ -0,0 +1,42 @@ +import { GrafanaOnCallChatProvider } from './grafana-on-call.provider'; +import axios from 'axios'; + +test('should trigger grafana-on-call library correctly', async () => { + const date = new Date(); + const fakePost = jest.fn(() => { + return { headers: { ['Date']: date } }; + }); + + jest.spyOn(axios, 'create').mockImplementation(() => { + return { + post: fakePost, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + }); + + const provider = new GrafanaOnCallChatProvider({ + alertUid: '123', + externalLink: 'link', + imageUrl: 'url', + state: 'ok', + title: 'title', + }); + + const testWebhookUrl = 'https://mycompany.webhook.grafana.com/'; + const testContent = 'warning!!'; + const res = await provider.sendMessage({ + webhookUrl: testWebhookUrl, + content: testContent, + }); + + expect(fakePost).toHaveBeenCalled(); + expect(fakePost).toHaveBeenCalledWith(testWebhookUrl, { + alert_uid: '123', + link_to_upstream_details: 'link', + image_url: 'url', + state: 'ok', + title: 'title', + message: testContent, + }); + expect(res).toEqual({ id: expect.any(String), date: date.toISOString() }); +}); diff --git a/providers/grafana-on-call/src/lib/grafana-on-call.provider.ts b/providers/grafana-on-call/src/lib/grafana-on-call.provider.ts new file mode 100644 index 00000000000..f21d66efcad --- /dev/null +++ b/providers/grafana-on-call/src/lib/grafana-on-call.provider.ts @@ -0,0 +1,44 @@ +import { + ChannelTypeEnum, + ISendMessageSuccessResponse, + IChatOptions, + IChatProvider, +} from '@novu/stateless'; +import axios from 'axios'; +import { v4 as uuid } from 'uuid'; + +export class GrafanaOnCallChatProvider implements IChatProvider { + id = 'grafana-on-call'; + channelType = ChannelTypeEnum.CHAT as ChannelTypeEnum.CHAT; + private axiosInstance = axios.create(); + constructor( + private config: { + alertUid?: string; + title?: string; + imageUrl?: string; + state?: string; + externalLink?: string; + } + ) {} + + async sendMessage( + options: IChatOptions + ): Promise { + const url = new URL(options.webhookUrl); + const body = { + alert_uid: this.config.alertUid, + title: this.config.title, + image_url: this.config.imageUrl, + state: this.config.state, + link_to_upstream_details: this.config.externalLink, + message: options.content, + }; + //response is just string "Ok." + const { headers } = await this.axiosInstance.post(url.toString(), body); + + return { + id: uuid(), + date: (headers.Date ? new Date(headers.Date) : new Date()).toISOString(), + }; + } +} diff --git a/providers/grafana-on-call/tsconfig.json b/providers/grafana-on-call/tsconfig.json new file mode 100644 index 00000000000..5b8120fea36 --- /dev/null +++ b/providers/grafana-on-call/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "build/main", + "rootDir": "src", + "types": ["node", "jest"] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules/**"] +} diff --git a/providers/grafana-on-call/tsconfig.module.json b/providers/grafana-on-call/tsconfig.module.json new file mode 100644 index 00000000000..79be3a5c40b --- /dev/null +++ b/providers/grafana-on-call/tsconfig.module.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "target": "esnext", + "outDir": "build/module", + "module": "esnext" + }, + "exclude": ["node_modules/**"] +}