@sparkdotfi/common-reporters 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/console/ConsoleReporter.js +41 -0
- package/dist/console/ConsoleReporter.test.js +40 -0
- package/dist/index.js +6 -0
- package/dist/pagerduty/PagerDutyClient.js +53 -0
- package/dist/pagerduty/PagerDutyReport.test.js +40 -0
- package/dist/pagerduty/PagerDutyReporter.js +28 -0
- package/dist/pagerduty/renderToPagerDuty.js +15 -0
- package/dist/slack/SlackReporter.js +29 -0
- package/dist/slack/SlackReporter.test.js +31 -0
- package/dist/slack/renderToSlack.js +51 -0
- package/dist/slack/renderToSlack.test.js +188 -0
- package/dist/templating.js +11 -0
- package/dist/types/console/ConsoleReporter.d.ts +11 -0
- package/dist/types/console/ConsoleReporter.d.ts.map +1 -0
- package/dist/types/console/ConsoleReporter.test.d.ts +2 -0
- package/dist/types/console/ConsoleReporter.test.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/pagerduty/PagerDutyClient.d.ts +13 -0
- package/dist/types/pagerduty/PagerDutyClient.d.ts.map +1 -0
- package/dist/types/pagerduty/PagerDutyReport.test.d.ts +2 -0
- package/dist/types/pagerduty/PagerDutyReport.test.d.ts.map +1 -0
- package/dist/types/pagerduty/PagerDutyReporter.d.ts +9 -0
- package/dist/types/pagerduty/PagerDutyReporter.d.ts.map +1 -0
- package/dist/types/pagerduty/renderToPagerDuty.d.ts +3 -0
- package/dist/types/pagerduty/renderToPagerDuty.d.ts.map +1 -0
- package/dist/types/slack/SlackReporter.d.ts +14 -0
- package/dist/types/slack/SlackReporter.d.ts.map +1 -0
- package/dist/types/slack/SlackReporter.test.d.ts +2 -0
- package/dist/types/slack/SlackReporter.test.d.ts.map +1 -0
- package/dist/types/slack/renderToSlack.d.ts +7 -0
- package/dist/types/slack/renderToSlack.d.ts.map +1 -0
- package/dist/types/slack/renderToSlack.test.d.ts +2 -0
- package/dist/types/slack/renderToSlack.test.d.ts.map +1 -0
- package/dist/types/templating.d.ts +12 -0
- package/dist/types/templating.d.ts.map +1 -0
- package/dist/types/types.d.ts +18 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { assertNever } from '@sparkdotfi/common-universal';
|
|
2
|
+
import { templating } from '../templating.js';
|
|
3
|
+
export class ConsoleReporter {
|
|
4
|
+
constructor(logger, logFunctionName) {
|
|
5
|
+
Object.defineProperty(this, "logFunctionName", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: logFunctionName
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "logger", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
17
|
+
this.logger = logger.for(this);
|
|
18
|
+
}
|
|
19
|
+
async report(report) {
|
|
20
|
+
const blocks = report.title ? [templating.text(`${report.title}\n`), ...report.content] : report.content;
|
|
21
|
+
this.logger[this.logFunctionName](renderToConsoleString(blocks));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function renderToConsoleString(content) {
|
|
25
|
+
const text = content
|
|
26
|
+
.map((block) => {
|
|
27
|
+
switch (block.type) {
|
|
28
|
+
case 'text':
|
|
29
|
+
return block.content;
|
|
30
|
+
case 'link':
|
|
31
|
+
return `${block.text} (${block.href})`;
|
|
32
|
+
default:
|
|
33
|
+
assertNever(block);
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
.join(' ');
|
|
37
|
+
return text
|
|
38
|
+
.split('\n')
|
|
39
|
+
.map((line) => line.trimStart())
|
|
40
|
+
.join('\n');
|
|
41
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect, mockFn, mockObject } from 'earl';
|
|
2
|
+
import { templating as t } from '../templating.js';
|
|
3
|
+
import { ConsoleReporter } from './ConsoleReporter.js';
|
|
4
|
+
describe(ConsoleReporter.name, () => {
|
|
5
|
+
it('joins title and content', async () => {
|
|
6
|
+
const logger = getMockLogger();
|
|
7
|
+
const reporter = new ConsoleReporter(logger, 'info');
|
|
8
|
+
await reporter.report({
|
|
9
|
+
title: 'some title',
|
|
10
|
+
content: [t.text('some description'), t.text('additional description')],
|
|
11
|
+
});
|
|
12
|
+
expect(logger.info).toHaveBeenOnlyCalledWith('some title\nsome description additional description');
|
|
13
|
+
});
|
|
14
|
+
it('allows for different logger function', async () => {
|
|
15
|
+
const logger = getMockLogger();
|
|
16
|
+
const reporter = new ConsoleReporter(logger, 'warn');
|
|
17
|
+
await reporter.report({
|
|
18
|
+
title: 'some title',
|
|
19
|
+
content: [t.text('some description')],
|
|
20
|
+
});
|
|
21
|
+
expect(logger.warn).toHaveBeenOnlyCalledWith('some title\nsome description');
|
|
22
|
+
expect(logger.info).not.toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
it('sends message without title', async () => {
|
|
25
|
+
const logger = getMockLogger();
|
|
26
|
+
const reporter = new ConsoleReporter(logger, 'info');
|
|
27
|
+
await reporter.report({
|
|
28
|
+
content: [t.text('some description'), t.text('additional description')],
|
|
29
|
+
});
|
|
30
|
+
expect(logger.info).toHaveBeenOnlyCalledWith('some description additional description');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
function getMockLogger() {
|
|
34
|
+
const mockLogger = mockObject({
|
|
35
|
+
info: mockFn(() => { }),
|
|
36
|
+
warn: mockFn(() => { }),
|
|
37
|
+
for: (_) => mockLogger,
|
|
38
|
+
});
|
|
39
|
+
return mockLogger;
|
|
40
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './templating.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { ConsoleReporter } from './console/ConsoleReporter.js';
|
|
4
|
+
export { PagerDutyClient } from './pagerduty/PagerDutyClient.js';
|
|
5
|
+
export { PagerDutyReporter } from './pagerduty/PagerDutyReporter.js';
|
|
6
|
+
export { SlackReporter } from './slack/SlackReporter.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { api } from '@pagerduty/pdjs';
|
|
2
|
+
export class PagerDutyClient {
|
|
3
|
+
constructor(apiKey, requesterEmail) {
|
|
4
|
+
Object.defineProperty(this, "apiKey", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: apiKey
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(this, "requesterEmail", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: requesterEmail
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "pdClient", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: void 0
|
|
21
|
+
});
|
|
22
|
+
this.pdClient = api({ token: apiKey });
|
|
23
|
+
}
|
|
24
|
+
async createIncident({ serviceId, title, description, uniqueKey, }) {
|
|
25
|
+
const res = await this.pdClient.post('/incidents', {
|
|
26
|
+
headers: {
|
|
27
|
+
From: this.requesterEmail,
|
|
28
|
+
},
|
|
29
|
+
data: {
|
|
30
|
+
incident: {
|
|
31
|
+
type: 'incident',
|
|
32
|
+
title,
|
|
33
|
+
body: {
|
|
34
|
+
type: 'incident_body',
|
|
35
|
+
details: description,
|
|
36
|
+
},
|
|
37
|
+
service: {
|
|
38
|
+
id: serviceId,
|
|
39
|
+
type: 'service_reference',
|
|
40
|
+
},
|
|
41
|
+
incident_key: uniqueKey,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
// ignore if incident already exists
|
|
46
|
+
if (res.status === 400 && res.data.error.code === 2002) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
throw new Error(`Failed to create incident: ${JSON.stringify(res.data)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Hash } from '@sparkdotfi/common-universal';
|
|
2
|
+
import { expect, mockFn, mockObject } from 'earl';
|
|
3
|
+
import { templating as t } from '../templating.js';
|
|
4
|
+
import { PagerDutyReporter } from './PagerDutyReporter.js';
|
|
5
|
+
describe(PagerDutyReporter.name, () => {
|
|
6
|
+
it('sends report', async () => {
|
|
7
|
+
const pdClient = getMockPagerDutyClient();
|
|
8
|
+
const reporter = new PagerDutyReporter(pdClient, 'service');
|
|
9
|
+
const content = [t.text('some content')];
|
|
10
|
+
const expectedHash = Hash.fromText('some content');
|
|
11
|
+
await reporter.report({
|
|
12
|
+
title: 'some title',
|
|
13
|
+
content,
|
|
14
|
+
});
|
|
15
|
+
expect(pdClient.createIncident).toHaveBeenOnlyCalledWith({
|
|
16
|
+
serviceId: 'service',
|
|
17
|
+
title: 'some title',
|
|
18
|
+
description: 'some content',
|
|
19
|
+
uniqueKey: expectedHash,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it('uses description for title if not provided', async () => {
|
|
23
|
+
const pdClient = getMockPagerDutyClient();
|
|
24
|
+
const reporter = new PagerDutyReporter(pdClient, 'service');
|
|
25
|
+
const content = [t.text('really long title'), t.text('with multiple parts'), t.link('href', 'and some link')];
|
|
26
|
+
const expectedDescription = 'really long title with multiple parts [href] and some link>';
|
|
27
|
+
await reporter.report({ content });
|
|
28
|
+
expect(pdClient.createIncident).toHaveBeenOnlyCalledWith({
|
|
29
|
+
serviceId: 'service',
|
|
30
|
+
title: 'really long title wi...',
|
|
31
|
+
description: expectedDescription,
|
|
32
|
+
uniqueKey: Hash.fromText(expectedDescription),
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
function getMockPagerDutyClient() {
|
|
37
|
+
return mockObject({
|
|
38
|
+
createIncident: mockFn(async (_data) => { }),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Hash } from '@sparkdotfi/common-universal';
|
|
2
|
+
import { renderToPagerdutyString } from './renderToPagerDuty.js';
|
|
3
|
+
export class PagerDutyReporter {
|
|
4
|
+
constructor(pagerDutyClient, serviceId) {
|
|
5
|
+
Object.defineProperty(this, "pagerDutyClient", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: pagerDutyClient
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "serviceId", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: serviceId
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async report(report) {
|
|
19
|
+
const description = renderToPagerdutyString(report.content);
|
|
20
|
+
const uniqueKey = Hash.fromText(description);
|
|
21
|
+
await this.pagerDutyClient.createIncident({
|
|
22
|
+
serviceId: this.serviceId,
|
|
23
|
+
title: report.title ?? `${description.slice(0, 20)}...`,
|
|
24
|
+
description,
|
|
25
|
+
uniqueKey,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { assertNever } from '@sparkdotfi/common-universal';
|
|
2
|
+
export function renderToPagerdutyString(node) {
|
|
3
|
+
return node
|
|
4
|
+
.map((block) => {
|
|
5
|
+
switch (block.type) {
|
|
6
|
+
case 'text':
|
|
7
|
+
return block.content;
|
|
8
|
+
case 'link':
|
|
9
|
+
return `[${block.href}] ${block.text}>`;
|
|
10
|
+
default:
|
|
11
|
+
assertNever(block);
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
.join(' ');
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { templating } from '../templating.js';
|
|
3
|
+
import { renderToSlackString } from './renderToSlack.js';
|
|
4
|
+
export class SlackReporter {
|
|
5
|
+
constructor(config, httpClient) {
|
|
6
|
+
Object.defineProperty(this, "config", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
writable: true,
|
|
10
|
+
value: config
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(this, "httpClient", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: httpClient
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async report(report) {
|
|
20
|
+
const text = renderToSlackString(this.getContentBlocks(report));
|
|
21
|
+
await this.httpClient.post(this.config.apiUrl, { text }, z.string());
|
|
22
|
+
}
|
|
23
|
+
getContentBlocks(report) {
|
|
24
|
+
if (report.title) {
|
|
25
|
+
return [templating.text(`${report.title}\n`, { bold: true }), ...report.content];
|
|
26
|
+
}
|
|
27
|
+
return report.content;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { expect, mockFn, mockObject } from 'earl';
|
|
2
|
+
import { templating as t } from '../templating.js';
|
|
3
|
+
import { SlackReporter } from './SlackReporter.js';
|
|
4
|
+
describe(SlackReporter.name, () => {
|
|
5
|
+
it('joins title and content', async () => {
|
|
6
|
+
const httpClient = getMockHttpClient();
|
|
7
|
+
const reporter = new SlackReporter({ apiUrl: 'url' }, httpClient);
|
|
8
|
+
await reporter.report({
|
|
9
|
+
title: 'some title',
|
|
10
|
+
content: [t.text('some description'), t.text('additional description')],
|
|
11
|
+
});
|
|
12
|
+
expect(httpClient.post).toHaveBeenOnlyCalledWith('url', {
|
|
13
|
+
text: '> *some title*\n> some description additional description',
|
|
14
|
+
}, expect.anything());
|
|
15
|
+
});
|
|
16
|
+
it('sends message without title', async () => {
|
|
17
|
+
const httpClient = getMockHttpClient();
|
|
18
|
+
const reporter = new SlackReporter({ apiUrl: 'url' }, httpClient);
|
|
19
|
+
await reporter.report({
|
|
20
|
+
content: [t.text('some description'), t.text('additional description')],
|
|
21
|
+
});
|
|
22
|
+
expect(httpClient.post).toHaveBeenOnlyCalledWith('url', {
|
|
23
|
+
text: '> some description additional description',
|
|
24
|
+
}, expect.anything());
|
|
25
|
+
});
|
|
26
|
+
function getMockHttpClient() {
|
|
27
|
+
return mockObject({
|
|
28
|
+
post: mockFn((_url, _body, _schema) => 'some string'),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { assertNever } from '@sparkdotfi/common-universal';
|
|
2
|
+
export function renderToSlackString(node) {
|
|
3
|
+
const text = node
|
|
4
|
+
.map((block) => {
|
|
5
|
+
switch (block.type) {
|
|
6
|
+
case 'text':
|
|
7
|
+
return applyFontStyle(block);
|
|
8
|
+
case 'link':
|
|
9
|
+
return `<${block.href}|${block.text}>`;
|
|
10
|
+
default:
|
|
11
|
+
assertNever(block);
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
.join(' ');
|
|
15
|
+
// we add block quotes to make the text more readable and aligned
|
|
16
|
+
const blockQuotes = text.split('\n').map((line) => {
|
|
17
|
+
return `> ${line.trim()}`;
|
|
18
|
+
});
|
|
19
|
+
return blockQuotes.join('\n');
|
|
20
|
+
}
|
|
21
|
+
export function applyFontStyle(block) {
|
|
22
|
+
const lines = block.content.split('\n');
|
|
23
|
+
if (lines.length === 1) {
|
|
24
|
+
return applyLineFontStyle(block.content, block.bold, block.italic);
|
|
25
|
+
}
|
|
26
|
+
return lines
|
|
27
|
+
.map((line) => {
|
|
28
|
+
const trimmedLine = line.trim();
|
|
29
|
+
if (trimmedLine.length === 0) {
|
|
30
|
+
return trimmedLine;
|
|
31
|
+
}
|
|
32
|
+
return applyLineFontStyle(trimmedLine, block.bold, block.italic);
|
|
33
|
+
})
|
|
34
|
+
.join('\n');
|
|
35
|
+
}
|
|
36
|
+
export function applyLineFontStyle(line, bold = false, italic = false) {
|
|
37
|
+
let result = line;
|
|
38
|
+
if (bold) {
|
|
39
|
+
result = applyBoldStyle(result);
|
|
40
|
+
}
|
|
41
|
+
if (italic) {
|
|
42
|
+
result = applyItalicStyle(result);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
function applyBoldStyle(text) {
|
|
47
|
+
return `*${text}*`;
|
|
48
|
+
}
|
|
49
|
+
function applyItalicStyle(text) {
|
|
50
|
+
return `_${text}_`;
|
|
51
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { expect } from 'earl';
|
|
2
|
+
import { templating as t } from '../templating.js';
|
|
3
|
+
import { applyFontStyle, renderToSlackString } from './renderToSlack.js';
|
|
4
|
+
describe(renderToSlackString.name, () => {
|
|
5
|
+
it('renders content blocks', () => {
|
|
6
|
+
const content = [
|
|
7
|
+
t.text('Hello'),
|
|
8
|
+
t.text('World', { bold: true }),
|
|
9
|
+
t.text('!\n'),
|
|
10
|
+
t.link('https://basescan.org', 'Link to tx explorer'),
|
|
11
|
+
];
|
|
12
|
+
const lines = ['> Hello *World* !', '> <https://basescan.org|Link to tx explorer>'];
|
|
13
|
+
expect(renderToSlackString(content)).toEqual(lines.join('\n'));
|
|
14
|
+
});
|
|
15
|
+
it('renders bold text with a newline correctly', () => {
|
|
16
|
+
const content = [t.text('Highlighted text\n', { bold: true }), t.text('text')];
|
|
17
|
+
expect(renderToSlackString(content)).toEqual('> *Highlighted text*\n' + '> text');
|
|
18
|
+
});
|
|
19
|
+
it('renders bold text with multiple newlines correctly', () => {
|
|
20
|
+
const content = [t.text('bold text\nmore bold text\n', { bold: true }), t.text('text')];
|
|
21
|
+
expect(renderToSlackString(content)).toEqual('> *bold text*\n' + '> *more bold text*\n' + '> text');
|
|
22
|
+
});
|
|
23
|
+
it('joins different blocks with space', () => {
|
|
24
|
+
const content = [
|
|
25
|
+
t.text('before link'),
|
|
26
|
+
t.link('https://google.com', 'click link'),
|
|
27
|
+
t.text('after link', { bold: true }),
|
|
28
|
+
];
|
|
29
|
+
expect(renderToSlackString(content)).toEqual('> before link <https://google.com|click link> *after link*');
|
|
30
|
+
});
|
|
31
|
+
it('renders empty new lines correctly', () => {
|
|
32
|
+
const content = [
|
|
33
|
+
t.text('First'),
|
|
34
|
+
t.text('line'),
|
|
35
|
+
t.text('\n'),
|
|
36
|
+
t.text('\n'),
|
|
37
|
+
t.text('Third line'),
|
|
38
|
+
t.text('\n'),
|
|
39
|
+
t.text('Fourth line'),
|
|
40
|
+
];
|
|
41
|
+
const lines = ['> First line', '> ', '> Third line', '> Fourth line'];
|
|
42
|
+
expect(renderToSlackString(content)).toEqual(lines.join('\n'));
|
|
43
|
+
});
|
|
44
|
+
it('renders new lines in correct order', () => {
|
|
45
|
+
const content = [
|
|
46
|
+
t.text('First'),
|
|
47
|
+
t.text('line'),
|
|
48
|
+
t.text('\nSecond line,'),
|
|
49
|
+
t.text('still second line\n'),
|
|
50
|
+
t.text('Third line'),
|
|
51
|
+
];
|
|
52
|
+
const lines = ['> First line', '> Second line, still second line', '> Third line'];
|
|
53
|
+
expect(renderToSlackString(content)).toEqual(lines.join('\n'));
|
|
54
|
+
});
|
|
55
|
+
describe(applyFontStyle.name, () => {
|
|
56
|
+
describe('bold text', () => {
|
|
57
|
+
it('renders correctly', () => {
|
|
58
|
+
const block = {
|
|
59
|
+
type: 'text',
|
|
60
|
+
content: 'bold text',
|
|
61
|
+
bold: true,
|
|
62
|
+
};
|
|
63
|
+
expect(applyFontStyle(block)).toEqual('*bold text*');
|
|
64
|
+
});
|
|
65
|
+
it('renders with newline', () => {
|
|
66
|
+
const block = {
|
|
67
|
+
type: 'text',
|
|
68
|
+
content: 'bold text\n',
|
|
69
|
+
bold: true,
|
|
70
|
+
};
|
|
71
|
+
expect(applyFontStyle(block)).toEqual('*bold text*\n');
|
|
72
|
+
});
|
|
73
|
+
it('renders with multiple newlines', () => {
|
|
74
|
+
const block = {
|
|
75
|
+
type: 'text',
|
|
76
|
+
content: 'bold text\nmore bold text\n',
|
|
77
|
+
bold: true,
|
|
78
|
+
};
|
|
79
|
+
expect(applyFontStyle(block)).toEqual('*bold text*\n*more bold text*\n');
|
|
80
|
+
});
|
|
81
|
+
it('renders with empty string and newline', () => {
|
|
82
|
+
const block = {
|
|
83
|
+
type: 'text',
|
|
84
|
+
content: '\n',
|
|
85
|
+
bold: true,
|
|
86
|
+
};
|
|
87
|
+
expect(applyFontStyle(block)).toEqual('\n');
|
|
88
|
+
});
|
|
89
|
+
it('ignores empty parts', () => {
|
|
90
|
+
const block = {
|
|
91
|
+
type: 'text',
|
|
92
|
+
content: ' \n ',
|
|
93
|
+
bold: true,
|
|
94
|
+
};
|
|
95
|
+
expect(applyFontStyle(block)).toEqual('\n');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('italic text', () => {
|
|
99
|
+
it('renders correctly', () => {
|
|
100
|
+
const block = {
|
|
101
|
+
type: 'text',
|
|
102
|
+
content: 'italic text',
|
|
103
|
+
italic: true,
|
|
104
|
+
};
|
|
105
|
+
expect(applyFontStyle(block)).toEqual('_italic text_');
|
|
106
|
+
});
|
|
107
|
+
it('renders with newline', () => {
|
|
108
|
+
const block = {
|
|
109
|
+
type: 'text',
|
|
110
|
+
content: 'italic text\n',
|
|
111
|
+
italic: true,
|
|
112
|
+
};
|
|
113
|
+
expect(applyFontStyle(block)).toEqual('_italic text_\n');
|
|
114
|
+
});
|
|
115
|
+
it('renders with multiple newlines', () => {
|
|
116
|
+
const block = {
|
|
117
|
+
type: 'text',
|
|
118
|
+
content: 'italic text\nmore italic text\n',
|
|
119
|
+
italic: true,
|
|
120
|
+
};
|
|
121
|
+
expect(applyFontStyle(block)).toEqual('_italic text_\n_more italic text_\n');
|
|
122
|
+
});
|
|
123
|
+
it('renders with empty string and newline', () => {
|
|
124
|
+
const block = {
|
|
125
|
+
type: 'text',
|
|
126
|
+
content: '\n',
|
|
127
|
+
italic: true,
|
|
128
|
+
};
|
|
129
|
+
expect(applyFontStyle(block)).toEqual('\n');
|
|
130
|
+
});
|
|
131
|
+
it('ignores empty parts', () => {
|
|
132
|
+
const block = {
|
|
133
|
+
type: 'text',
|
|
134
|
+
content: ' \n ',
|
|
135
|
+
italic: true,
|
|
136
|
+
};
|
|
137
|
+
expect(applyFontStyle(block)).toEqual('\n');
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe('bold and italics text', () => {
|
|
141
|
+
it('renders correctly', () => {
|
|
142
|
+
const block = {
|
|
143
|
+
type: 'text',
|
|
144
|
+
content: 'bold and italic text',
|
|
145
|
+
bold: true,
|
|
146
|
+
italic: true,
|
|
147
|
+
};
|
|
148
|
+
expect(applyFontStyle(block)).toEqual('_*bold and italic text*_');
|
|
149
|
+
});
|
|
150
|
+
it('renders with newline', () => {
|
|
151
|
+
const block = {
|
|
152
|
+
type: 'text',
|
|
153
|
+
content: 'bold and italic text\n',
|
|
154
|
+
bold: true,
|
|
155
|
+
italic: true,
|
|
156
|
+
};
|
|
157
|
+
expect(applyFontStyle(block)).toEqual('_*bold and italic text*_\n');
|
|
158
|
+
});
|
|
159
|
+
it('renders with multiple newlines', () => {
|
|
160
|
+
const block = {
|
|
161
|
+
type: 'text',
|
|
162
|
+
content: 'bold and italic text\nmore bold and italic text\n',
|
|
163
|
+
bold: true,
|
|
164
|
+
italic: true,
|
|
165
|
+
};
|
|
166
|
+
expect(applyFontStyle(block)).toEqual('_*bold and italic text*_\n_*more bold and italic text*_\n');
|
|
167
|
+
});
|
|
168
|
+
it('renders with empty string and newline', () => {
|
|
169
|
+
const block = {
|
|
170
|
+
type: 'text',
|
|
171
|
+
content: '\n',
|
|
172
|
+
bold: true,
|
|
173
|
+
italic: true,
|
|
174
|
+
};
|
|
175
|
+
expect(applyFontStyle(block)).toEqual('\n');
|
|
176
|
+
});
|
|
177
|
+
it('ignores empty parts', () => {
|
|
178
|
+
const block = {
|
|
179
|
+
type: 'text',
|
|
180
|
+
content: ' \n ',
|
|
181
|
+
bold: true,
|
|
182
|
+
italic: true,
|
|
183
|
+
};
|
|
184
|
+
expect(applyFontStyle(block)).toEqual('\n');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ILogger } from '@sparkdotfi/common-universal/logger';
|
|
2
|
+
import { IReporter, Report } from '../types.js';
|
|
3
|
+
export declare class ConsoleReporter implements IReporter {
|
|
4
|
+
private readonly logFunctionName;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(logger: ILogger, logFunctionName: LogFunctionName);
|
|
7
|
+
report(report: Report): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
type LogFunctionName = Exclude<keyof ILogger, 'configure' | 'for' | 'tag'>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=ConsoleReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsoleReporter.d.ts","sourceRoot":"","sources":["../../../src/console/ConsoleReporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,qCAAqC,CAAA;AAE7D,OAAO,EAAgB,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAE7D,qBAAa,eAAgB,YAAW,SAAS;IAK7C,OAAO,CAAC,QAAQ,CAAC,eAAe;IAJlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAG9B,MAAM,EAAE,OAAO,EACE,eAAe,EAAE,eAAe;IAK7C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAI5C;AAsBD,KAAK,eAAe,GAAG,OAAO,CAAC,MAAM,OAAO,EAAE,WAAW,GAAG,KAAK,GAAG,KAAK,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsoleReporter.test.d.ts","sourceRoot":"","sources":["../../../src/console/ConsoleReporter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './templating.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { ConsoleReporter } from './console/ConsoleReporter.js';
|
|
4
|
+
export { PagerDutyClient } from './pagerduty/PagerDutyClient.js';
|
|
5
|
+
export { PagerDutyReporter } from './pagerduty/PagerDutyReporter.js';
|
|
6
|
+
export { SlackReporter } from './slack/SlackReporter.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class PagerDutyClient {
|
|
2
|
+
readonly apiKey: string;
|
|
3
|
+
private readonly requesterEmail;
|
|
4
|
+
private readonly pdClient;
|
|
5
|
+
constructor(apiKey: string, requesterEmail: string);
|
|
6
|
+
createIncident({ serviceId, title, description, uniqueKey, }: {
|
|
7
|
+
serviceId: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
uniqueKey: string;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=PagerDutyClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PagerDutyClient.d.ts","sourceRoot":"","sources":["../../../src/pagerduty/PagerDutyClient.ts"],"names":[],"mappings":"AAEA,qBAAa,eAAe;IAIxB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAJjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAGtC,MAAM,EAAE,MAAM,EACN,cAAc,EAAE,MAAM;IAKnC,cAAc,CAAC,EACnB,SAAS,EACT,KAAK,EACL,WAAW,EACX,SAAS,GACV,EAAE;QACD,SAAS,EAAE,MAAM,CAAA;QACjB,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;CA8BlB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PagerDutyReport.test.d.ts","sourceRoot":"","sources":["../../../src/pagerduty/PagerDutyReport.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IReporter, Report } from '../types.js';
|
|
2
|
+
import { PagerDutyClient } from './PagerDutyClient.js';
|
|
3
|
+
export declare class PagerDutyReporter implements IReporter {
|
|
4
|
+
private readonly pagerDutyClient;
|
|
5
|
+
private readonly serviceId;
|
|
6
|
+
constructor(pagerDutyClient: PagerDutyClient, serviceId: string);
|
|
7
|
+
report(report: Report): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=PagerDutyReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PagerDutyReporter.d.ts","sourceRoot":"","sources":["../../../src/pagerduty/PagerDutyReporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAGtD,qBAAa,iBAAkB,YAAW,SAAS;IAE/C,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,eAAe,EAAE,eAAe,EAChC,SAAS,EAAE,MAAM;IAG9B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderToPagerDuty.d.ts","sourceRoot":"","sources":["../../../src/pagerduty/renderToPagerDuty.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,CAapE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HttpClient } from '@sparkdotfi/common-universal/http-client';
|
|
2
|
+
import { IReporter, Report } from '../types.js';
|
|
3
|
+
interface SlackReporterConfig {
|
|
4
|
+
apiUrl: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class SlackReporter implements IReporter {
|
|
7
|
+
private readonly config;
|
|
8
|
+
private readonly httpClient;
|
|
9
|
+
constructor(config: SlackReporterConfig, httpClient: HttpClient);
|
|
10
|
+
report(report: Report): Promise<void>;
|
|
11
|
+
private getContentBlocks;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=SlackReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackReporter.d.ts","sourceRoot":"","sources":["../../../src/slack/SlackReporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAA;AAGrE,OAAO,EAAgB,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAG7D,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAA;CACf;AAED,qBAAa,aAAc,YAAW,SAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,MAAM,EAAE,mBAAmB,EAC3B,UAAU,EAAE,UAAU;IAGnC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3C,OAAO,CAAC,gBAAgB;CAMzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackReporter.test.d.ts","sourceRoot":"","sources":["../../../src/slack/SlackReporter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ContentBlock } from '../types.js';
|
|
2
|
+
export declare function renderToSlackString(node: ContentBlock[]): string;
|
|
3
|
+
export declare function applyFontStyle(block: Extract<ContentBlock, {
|
|
4
|
+
type: 'text';
|
|
5
|
+
}>): string;
|
|
6
|
+
export declare function applyLineFontStyle(line: string, bold?: boolean, italic?: boolean): string;
|
|
7
|
+
//# sourceMappingURL=renderToSlack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderToSlack.d.ts","sourceRoot":"","sources":["../../../src/slack/renderToSlack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,CAoBhE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAcrF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,UAAQ,EAAE,MAAM,UAAQ,GAAG,MAAM,CASrF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderToSlack.test.d.ts","sourceRoot":"","sources":["../../../src/slack/renderToSlack.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ContentBlock } from './types.js';
|
|
2
|
+
declare function text(content: string, extra?: {
|
|
3
|
+
bold?: boolean;
|
|
4
|
+
}): ContentBlock;
|
|
5
|
+
declare function link(href: string, text: string): ContentBlock;
|
|
6
|
+
export declare const templating: {
|
|
7
|
+
text: typeof text;
|
|
8
|
+
link: typeof link;
|
|
9
|
+
newLine: ContentBlock;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=templating.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templating.d.ts","sourceRoot":"","sources":["../../src/templating.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC,iBAAS,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,YAAY,CAEvE;AAED,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAEtD;AAED,eAAO,MAAM,UAAU;;;;CAItB,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type ContentBlock = {
|
|
2
|
+
type: 'text';
|
|
3
|
+
content: string;
|
|
4
|
+
bold?: boolean;
|
|
5
|
+
italic?: boolean;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'link';
|
|
8
|
+
href: string;
|
|
9
|
+
text: string;
|
|
10
|
+
};
|
|
11
|
+
export interface Report {
|
|
12
|
+
title?: string;
|
|
13
|
+
content: ContentBlock[];
|
|
14
|
+
}
|
|
15
|
+
export interface IReporter {
|
|
16
|
+
report(report: Report): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,GACD;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAEL,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sparkdotfi/common-reporters",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=18.0.0"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/types/index.d.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/sparkdotfi/spark-app.git",
|
|
13
|
+
"directory": "packages/common-reporters"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"@sparkdotfi/local-spark-monorepo": "./src/index.ts",
|
|
18
|
+
"types": "./dist/types/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": ["dist"],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"lint": "eslint src",
|
|
25
|
+
"verify": "concurrently --names \"LINT,TEST,TYPECHECK,LINT-CUSTOM\" -c \"bgMagenta.bold,bgGreen.bold,bgBlue.bold,bgCyan.bold\" \"pnpm run lint\" \"pnpm run test --silent\" \"pnpm run typecheck\"",
|
|
26
|
+
"fix": "cd ../../ && pnpm run check:fix && cd - && pnpm run verify",
|
|
27
|
+
"test": "mocha src/**/*.test.ts",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"earl": "^1.3.0",
|
|
35
|
+
"mocha": "^10.8.2"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@pagerduty/pdjs": "^2.2.4"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@types/mocha": "^10.0.10",
|
|
42
|
+
"@sparkdotfi/common-universal": "workspace:^",
|
|
43
|
+
"zod": "^3.0"
|
|
44
|
+
}
|
|
45
|
+
}
|