@verdaccio/hooks 8.0.0-next-8.12 → 8.0.0-next-8.13
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/README.md +30 -24
- package/package.json +12 -8
- package/.babelrc +0 -3
- package/CHANGELOG.md +0 -1057
- package/src/index.ts +0 -1
- package/src/notify-request.ts +0 -43
- package/src/notify.ts +0 -139
- package/test/__helper/index.ts +0 -5
- package/test/notify-request.spec.ts +0 -83
- package/test/partials/config/yaml/notify/multiple.notify.yaml +0 -16
- package/test/partials/config/yaml/notify/single.header.notify.yaml +0 -5
- package/test/partials/config/yaml/notify/single.notify.yaml +0 -5
- package/test/partials/config/yaml/notify/single.packagePattern.notify.yaml +0 -7
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -20
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { handleNotify, notify, sendNotification } from './notify';
|
package/src/notify-request.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import buildDebug from 'debug';
|
|
2
|
-
import got from 'got-cjs';
|
|
3
|
-
|
|
4
|
-
import { HTTP_STATUS } from '@verdaccio/core';
|
|
5
|
-
import { logger } from '@verdaccio/logger';
|
|
6
|
-
|
|
7
|
-
const debug = buildDebug('verdaccio:hooks:request');
|
|
8
|
-
|
|
9
|
-
export type FetchOptions = {
|
|
10
|
-
body: string;
|
|
11
|
-
headers?: {};
|
|
12
|
-
method?: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export async function notifyRequest(url: string, options: FetchOptions): Promise<boolean> {
|
|
16
|
-
let response;
|
|
17
|
-
try {
|
|
18
|
-
debug('uri %o', url);
|
|
19
|
-
response = got.post(url, {
|
|
20
|
-
body: JSON.stringify(options.body),
|
|
21
|
-
method: 'POST',
|
|
22
|
-
headers: { 'Content-Type': 'application/json' },
|
|
23
|
-
});
|
|
24
|
-
debug('response.status %o', response.status);
|
|
25
|
-
const body = await response.json();
|
|
26
|
-
if (response.status >= HTTP_STATUS.BAD_REQUEST) {
|
|
27
|
-
throw new Error(body);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
logger.info(
|
|
31
|
-
{ content: options.body },
|
|
32
|
-
'The notification @{content} has been successfully dispatched'
|
|
33
|
-
);
|
|
34
|
-
return true;
|
|
35
|
-
} catch (err: any) {
|
|
36
|
-
debug('request error %o', err);
|
|
37
|
-
logger.error(
|
|
38
|
-
{ errorMessage: err?.message },
|
|
39
|
-
'notify service has thrown an error: @{errorMessage}'
|
|
40
|
-
);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
package/src/notify.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-undef */
|
|
2
|
-
import buildDebug from 'debug';
|
|
3
|
-
import Handlebars from 'handlebars';
|
|
4
|
-
|
|
5
|
-
import { logger } from '@verdaccio/logger';
|
|
6
|
-
import { Config, Notification, Package, RemoteUser } from '@verdaccio/types';
|
|
7
|
-
|
|
8
|
-
import { FetchOptions, notifyRequest } from './notify-request';
|
|
9
|
-
|
|
10
|
-
const debug = buildDebug('verdaccio:hooks');
|
|
11
|
-
|
|
12
|
-
export function compileTemplate(content, metadata) {
|
|
13
|
-
// FUTURE: multiple handlers
|
|
14
|
-
return new Promise((resolve, reject) => {
|
|
15
|
-
let handler;
|
|
16
|
-
try {
|
|
17
|
-
if (!handler) {
|
|
18
|
-
debug('compile default template handler %o', content);
|
|
19
|
-
const template: HandlebarsTemplateDelegate = Handlebars.compile(content);
|
|
20
|
-
return resolve(template(metadata));
|
|
21
|
-
}
|
|
22
|
-
} catch (error: any) {
|
|
23
|
-
debug('error template handler %o', error);
|
|
24
|
-
reject(error);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function handleNotify(
|
|
30
|
-
metadata: Partial<Package>,
|
|
31
|
-
notifyEntry,
|
|
32
|
-
remoteUser: Partial<RemoteUser>,
|
|
33
|
-
publishedPackage: string
|
|
34
|
-
): Promise<boolean> {
|
|
35
|
-
let regex;
|
|
36
|
-
if (metadata.name && notifyEntry.packagePattern) {
|
|
37
|
-
regex = new RegExp(notifyEntry.packagePattern, notifyEntry.packagePatternFlags || '');
|
|
38
|
-
if (!regex.test(metadata.name)) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let content;
|
|
44
|
-
// FIXME: publisher is not part of the expected types metadata
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
if (typeof metadata?.publisher === 'undefined' || metadata?.publisher === null) {
|
|
47
|
-
// @ts-ignore
|
|
48
|
-
metadata = { ...metadata, publishedPackage, publisher: { name: remoteUser.name as string } };
|
|
49
|
-
debug('template metadata %o', metadata);
|
|
50
|
-
content = await compileTemplate(notifyEntry.content, metadata);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const options: FetchOptions = {
|
|
54
|
-
body: JSON.stringify(content),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// provides fallback support, it's accept an Object {} and Array of {}
|
|
58
|
-
if (notifyEntry.headers && Array.isArray(notifyEntry.headers)) {
|
|
59
|
-
const header = {};
|
|
60
|
-
// FIXME: we can simplify this
|
|
61
|
-
notifyEntry.headers.map(function (item): void {
|
|
62
|
-
if (Object.is(item, item)) {
|
|
63
|
-
for (const key in item) {
|
|
64
|
-
/* eslint no-prototype-builtins: 0 */
|
|
65
|
-
if (item.hasOwnProperty(key)) {
|
|
66
|
-
header[key] = item[key];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
options.headers = header;
|
|
72
|
-
} else if (Object.is(notifyEntry.headers, notifyEntry.headers)) {
|
|
73
|
-
options.headers = notifyEntry.headers;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (!notifyEntry.endpoint) {
|
|
77
|
-
debug('error due endpoint is missing');
|
|
78
|
-
throw new Error('missing parameter');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return notifyRequest(notifyEntry.endpoint, {
|
|
82
|
-
method: notifyEntry.method,
|
|
83
|
-
...options,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function sendNotification(
|
|
88
|
-
metadata: Partial<Package>,
|
|
89
|
-
notify: Notification,
|
|
90
|
-
remoteUser: Partial<RemoteUser>,
|
|
91
|
-
publishedPackage: string
|
|
92
|
-
): Promise<boolean> {
|
|
93
|
-
return handleNotify(metadata, notify, remoteUser, publishedPackage) as Promise<any>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function notify(
|
|
97
|
-
metadata: Partial<Package>,
|
|
98
|
-
config: Partial<Config>,
|
|
99
|
-
remoteUser: Partial<RemoteUser>,
|
|
100
|
-
publishedPackage: string
|
|
101
|
-
): Promise<boolean[]> {
|
|
102
|
-
debug('init send notification');
|
|
103
|
-
if (config.notify) {
|
|
104
|
-
const isSingle = Object.keys(config.notify).includes('method');
|
|
105
|
-
if (isSingle) {
|
|
106
|
-
debug('send single notification');
|
|
107
|
-
try {
|
|
108
|
-
const response = await sendNotification(
|
|
109
|
-
metadata,
|
|
110
|
-
config.notify as Notification,
|
|
111
|
-
remoteUser,
|
|
112
|
-
publishedPackage
|
|
113
|
-
);
|
|
114
|
-
return [response];
|
|
115
|
-
} catch {
|
|
116
|
-
debug('error on sending single notification');
|
|
117
|
-
return [false];
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
debug('send multiples notification');
|
|
121
|
-
const results = await Promise.allSettled(
|
|
122
|
-
Object.keys(config.notify).map((keyId: string) => {
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
const item = config.notify[keyId];
|
|
125
|
-
debug('send item %o', item);
|
|
126
|
-
return sendNotification(metadata, item, remoteUser, publishedPackage);
|
|
127
|
-
})
|
|
128
|
-
).catch((error) => {
|
|
129
|
-
logger.error({ error }, 'notify request has failed: @error');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// @ts-ignore
|
|
133
|
-
return Object.keys(results).map((promiseValue) => results[promiseValue].value);
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
debug('no notifications configuration detected');
|
|
137
|
-
return [false];
|
|
138
|
-
}
|
|
139
|
-
}
|
package/test/__helper/index.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import nock from 'nock';
|
|
2
|
-
import { beforeEach, describe, expect, test } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { createRemoteUser, parseConfigFile } from '@verdaccio/config';
|
|
5
|
-
import { setup } from '@verdaccio/logger';
|
|
6
|
-
import { Config } from '@verdaccio/types';
|
|
7
|
-
|
|
8
|
-
import { notify } from '../src/notify';
|
|
9
|
-
import { parseConfigurationFile } from './__helper';
|
|
10
|
-
|
|
11
|
-
const parseConfigurationNotifyFile = (name) => {
|
|
12
|
-
return parseConfigurationFile(`notify/${name}`);
|
|
13
|
-
};
|
|
14
|
-
const singleHeaderNotificationConfig = parseConfigFile(
|
|
15
|
-
parseConfigurationNotifyFile('single.header.notify')
|
|
16
|
-
);
|
|
17
|
-
const multiNotificationConfig = parseConfigFile(parseConfigurationNotifyFile('multiple.notify'));
|
|
18
|
-
|
|
19
|
-
setup({});
|
|
20
|
-
|
|
21
|
-
const domain = 'http://slack-service';
|
|
22
|
-
|
|
23
|
-
const options = {
|
|
24
|
-
path: '/foo?auth_token=mySecretToken',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
describe('Notifications:: notifyRequest', () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
nock.cleanAll();
|
|
30
|
-
});
|
|
31
|
-
test('when sending a empty notification', async () => {
|
|
32
|
-
nock(domain).post(options.path).reply(200, { body: 'test' });
|
|
33
|
-
|
|
34
|
-
const notificationResponse = await notify({}, {}, createRemoteUser('foo', []), 'bar');
|
|
35
|
-
expect(notificationResponse).toEqual([false]);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('when sending a single notification', async () => {
|
|
39
|
-
nock(domain).post(options.path).reply(200, { body: 'test' });
|
|
40
|
-
const notificationResponse = await notify(
|
|
41
|
-
{},
|
|
42
|
-
singleHeaderNotificationConfig,
|
|
43
|
-
createRemoteUser('foo', []),
|
|
44
|
-
'bar'
|
|
45
|
-
);
|
|
46
|
-
expect(notificationResponse).toEqual([true]);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('when notification endpoint is missing', async () => {
|
|
50
|
-
nock(domain).post(options.path).reply(200, { body: 'test' });
|
|
51
|
-
const name = 'package';
|
|
52
|
-
const config: Partial<Config> = {
|
|
53
|
-
// @ts-ignore
|
|
54
|
-
notify: {
|
|
55
|
-
method: 'POST',
|
|
56
|
-
endpoint: undefined,
|
|
57
|
-
content: '',
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
const notificationResponse = await notify({ name }, config, createRemoteUser('foo', []), 'bar');
|
|
61
|
-
expect(notificationResponse).toEqual([false]);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('when multiple notifications', async () => {
|
|
65
|
-
nock(domain)
|
|
66
|
-
.post(options.path)
|
|
67
|
-
.once()
|
|
68
|
-
.reply(200, { body: 'test' })
|
|
69
|
-
.post(options.path)
|
|
70
|
-
.once()
|
|
71
|
-
.reply(400, {})
|
|
72
|
-
.post(options.path)
|
|
73
|
-
.once()
|
|
74
|
-
.reply(500, { message: 'Something bad happened' });
|
|
75
|
-
// mockClient.intercept(options).reply(200, { body: 'test' });
|
|
76
|
-
// mockClient.intercept(options).reply(400, {});
|
|
77
|
-
// mockClient.intercept(options).reply(500, { message: 'Something bad happened' });
|
|
78
|
-
|
|
79
|
-
const name = 'package';
|
|
80
|
-
const responses = await notify({ name }, multiNotificationConfig, { name: 'foo' }, 'bar');
|
|
81
|
-
expect(responses).toEqual([true, false, false]);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
notify:
|
|
2
|
-
'example-google-chat':
|
|
3
|
-
method: POST
|
|
4
|
-
headers: [{ 'Content-Type': 'application/json' }]
|
|
5
|
-
endpoint: http://slack-service/foo?auth_token=mySecretToken
|
|
6
|
-
content: '{"text":"New package published: `{{ name }}{{#each versions}} v{{version}}{{/each}}`"}'
|
|
7
|
-
'example-hipchat':
|
|
8
|
-
method: POST
|
|
9
|
-
headers: [{ 'Content-Type': 'application/json' }]
|
|
10
|
-
endpoint: http://slack-service/foo?auth_token=mySecretToken
|
|
11
|
-
content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}'
|
|
12
|
-
'example-stride':
|
|
13
|
-
method: POST
|
|
14
|
-
headers: [{ 'Content-Type': 'application/json' }, { 'authorization': 'Bearer secretToken' }]
|
|
15
|
-
endpoint: http://slack-service/foo?auth_token=mySecretToken
|
|
16
|
-
content: '{"body": {"version": 1,"type": "doc","content": [{"type": "paragraph","content": [{"type": "text","text": "New package published: * {{ name }}* Publisher name: * {{ publisher.name }}"}]}]}}'
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
notify:
|
|
2
|
-
method: POST
|
|
3
|
-
headers: [{ 'Content-Type': 'application/json' }]
|
|
4
|
-
endpoint: https://usagge.hipchat.com/v2/room/3729485/notification?auth_token=mySecretToken
|
|
5
|
-
content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}'
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
notify:
|
|
2
|
-
method: POST
|
|
3
|
-
headers: { 'Content-Type': 'application/json' }
|
|
4
|
-
packagePattern: package
|
|
5
|
-
packagePatternFlags: g
|
|
6
|
-
endpoint: https://usagge.hipchat.com/v2/room/3729485/notification?auth_token=mySecretToken
|
|
7
|
-
content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}'
|
package/tsconfig.build.json
DELETED
package/tsconfig.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.reference.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"rootDir": "./src",
|
|
5
|
-
"outDir": "./build"
|
|
6
|
-
},
|
|
7
|
-
"include": ["src/**/*"],
|
|
8
|
-
"exclude": ["src/**/*.test.ts"],
|
|
9
|
-
"references": [
|
|
10
|
-
{
|
|
11
|
-
"path": "../auth"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"path": "../config"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"path": "../logger/logger"
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
}
|