@browserless.io/browserless 2.4.0 → 2.5.0-beta-2
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/CHANGELOG.md +7 -0
- package/bin/browserless.js +17 -127
- package/bin/scaffold/README.md +95 -14
- package/build/browserless.d.ts +5 -3
- package/build/browserless.js +10 -6
- package/build/browsers/index.d.ts +5 -3
- package/build/browsers/index.js +6 -4
- package/build/data/selectors.json +1 -1
- package/build/exports.d.ts +1 -0
- package/build/exports.js +1 -0
- package/build/file-system.d.ts +2 -3
- package/build/file-system.js +10 -21
- package/build/file-system.spec.js +35 -18
- package/build/hooks.d.ts +18 -4
- package/build/hooks.js +33 -4
- package/build/limiter.d.ts +3 -2
- package/build/limiter.js +5 -3
- package/build/limiter.spec.js +45 -26
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +8 -8
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/management/http/metrics-total.get.js +1 -1
- package/build/routes/management/http/metrics.get.js +2 -2
- package/build/sdk-utils.d.ts +13 -0
- package/build/sdk-utils.js +94 -0
- package/build/server.d.ts +3 -2
- package/build/server.js +6 -4
- package/build/types.d.ts +2 -2
- package/build/utils.js +6 -10
- package/external/after.js +1 -1
- package/external/before.js +1 -1
- package/external/browser.js +1 -1
- package/external/page.js +1 -1
- package/package.json +7 -7
- package/src/browserless.ts +21 -3
- package/src/browsers/index.ts +9 -6
- package/src/exports.ts +1 -0
- package/src/file-system.spec.ts +43 -18
- package/src/file-system.ts +16 -30
- package/src/hooks.ts +46 -4
- package/src/limiter.spec.ts +82 -112
- package/src/limiter.ts +3 -3
- package/src/routes/management/http/metrics-total.get.ts +3 -3
- package/src/routes/management/http/metrics.get.ts +2 -2
- package/src/sdk-utils.ts +136 -0
- package/src/server.ts +4 -3
- package/src/shared/content.http.ts +0 -1
- package/src/types.ts +2 -2
- package/src/utils.ts +10 -11
- package/static/docs/swagger.json +11 -11
- package/static/docs/swagger.min.json +10 -10
- package/static/function/client.js +76 -63
package/src/browserless.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
FirefoxPlaywright,
|
|
12
12
|
HTTPRoute,
|
|
13
13
|
HTTPServer,
|
|
14
|
+
Hooks,
|
|
14
15
|
IBrowserlessStats,
|
|
15
16
|
Limiter,
|
|
16
17
|
Metrics,
|
|
@@ -48,6 +49,7 @@ export class Browserless extends EventEmitter {
|
|
|
48
49
|
protected browserManager: BrowserManager;
|
|
49
50
|
protected config: Config;
|
|
50
51
|
protected fileSystem: FileSystem;
|
|
52
|
+
protected hooks: Hooks;
|
|
51
53
|
protected limiter: Limiter;
|
|
52
54
|
protected metrics: Metrics;
|
|
53
55
|
protected monitoring: Monitoring;
|
|
@@ -67,6 +69,7 @@ export class Browserless extends EventEmitter {
|
|
|
67
69
|
browserManager,
|
|
68
70
|
config,
|
|
69
71
|
fileSystem,
|
|
72
|
+
hooks,
|
|
70
73
|
limiter,
|
|
71
74
|
metrics,
|
|
72
75
|
monitoring,
|
|
@@ -77,6 +80,7 @@ export class Browserless extends EventEmitter {
|
|
|
77
80
|
browserManager?: Browserless['browserManager'];
|
|
78
81
|
config?: Browserless['config'];
|
|
79
82
|
fileSystem?: Browserless['fileSystem'];
|
|
83
|
+
hooks?: Browserless['hooks'];
|
|
80
84
|
limiter?: Browserless['limiter'];
|
|
81
85
|
metrics?: Browserless['metrics'];
|
|
82
86
|
monitoring?: Browserless['monitoring'];
|
|
@@ -88,13 +92,21 @@ export class Browserless extends EventEmitter {
|
|
|
88
92
|
this.config = config || new Config();
|
|
89
93
|
this.metrics = metrics || new Metrics();
|
|
90
94
|
this.token = token || new Token(this.config);
|
|
95
|
+
this.hooks = hooks || new Hooks();
|
|
91
96
|
this.webhooks = webhooks || new WebHooks(this.config);
|
|
92
|
-
this.browserManager =
|
|
97
|
+
this.browserManager =
|
|
98
|
+
browserManager || new BrowserManager(this.config, this.hooks);
|
|
93
99
|
this.monitoring = monitoring || new Monitoring(this.config);
|
|
94
100
|
this.fileSystem = fileSystem || new FileSystem(this.config);
|
|
95
101
|
this.limiter =
|
|
96
102
|
limiter ||
|
|
97
|
-
new Limiter(
|
|
103
|
+
new Limiter(
|
|
104
|
+
this.config,
|
|
105
|
+
this.metrics,
|
|
106
|
+
this.monitoring,
|
|
107
|
+
this.webhooks,
|
|
108
|
+
this.hooks,
|
|
109
|
+
);
|
|
98
110
|
this.router =
|
|
99
111
|
router || new Router(this.config, this.browserManager, this.limiter);
|
|
100
112
|
}
|
|
@@ -129,7 +141,11 @@ export class Browserless extends EventEmitter {
|
|
|
129
141
|
|
|
130
142
|
if (metricsPath) {
|
|
131
143
|
this.debug(`Saving metrics to "${metricsPath}"`);
|
|
132
|
-
this.fileSystem.append(
|
|
144
|
+
this.fileSystem.append(
|
|
145
|
+
metricsPath,
|
|
146
|
+
JSON.stringify(aggregatedStats),
|
|
147
|
+
false,
|
|
148
|
+
);
|
|
133
149
|
}
|
|
134
150
|
};
|
|
135
151
|
|
|
@@ -190,6 +206,7 @@ export class Browserless extends EventEmitter {
|
|
|
190
206
|
this.router.shutdown(),
|
|
191
207
|
this.token.shutdown(),
|
|
192
208
|
this.webhooks.shutdown(),
|
|
209
|
+
this.hooks.shutdown(),
|
|
193
210
|
]);
|
|
194
211
|
}
|
|
195
212
|
|
|
@@ -350,6 +367,7 @@ export class Browserless extends EventEmitter {
|
|
|
350
367
|
this.metrics,
|
|
351
368
|
this.token,
|
|
352
369
|
this.router,
|
|
370
|
+
this.hooks,
|
|
353
371
|
);
|
|
354
372
|
|
|
355
373
|
await this.server.start();
|
package/src/browsers/index.ts
CHANGED
|
@@ -16,21 +16,21 @@ import {
|
|
|
16
16
|
Config,
|
|
17
17
|
FirefoxPlaywright,
|
|
18
18
|
HTTPManagementRoutes,
|
|
19
|
+
Hooks,
|
|
19
20
|
NotFound,
|
|
20
21
|
Request,
|
|
21
22
|
ServerError,
|
|
22
23
|
WebkitPlaywright,
|
|
23
24
|
availableBrowsers,
|
|
24
|
-
browserHook,
|
|
25
25
|
convertIfBase64,
|
|
26
26
|
createLogger,
|
|
27
27
|
exists,
|
|
28
28
|
generateDataDir,
|
|
29
29
|
makeExternalURL,
|
|
30
30
|
noop,
|
|
31
|
-
pageHook,
|
|
32
31
|
parseBooleanParam,
|
|
33
32
|
} from '@browserless.io/browserless';
|
|
33
|
+
import { Page } from 'puppeteer-core';
|
|
34
34
|
import { deleteAsync } from 'del';
|
|
35
35
|
import path from 'path';
|
|
36
36
|
|
|
@@ -47,7 +47,10 @@ export class BrowserManager {
|
|
|
47
47
|
WebkitPlaywright.name,
|
|
48
48
|
];
|
|
49
49
|
|
|
50
|
-
constructor(
|
|
50
|
+
constructor(
|
|
51
|
+
protected config: Config,
|
|
52
|
+
protected hooks: Hooks,
|
|
53
|
+
) {}
|
|
51
54
|
|
|
52
55
|
private browserIsChrome = (b: BrowserInstance) =>
|
|
53
56
|
this.chromeBrowsers.some((chromeBrowser) => b instanceof chromeBrowser);
|
|
@@ -63,8 +66,8 @@ export class BrowserManager {
|
|
|
63
66
|
}
|
|
64
67
|
};
|
|
65
68
|
|
|
66
|
-
protected onNewPage = async (req: Request, page:
|
|
67
|
-
await
|
|
69
|
+
protected onNewPage = async (req: Request, page: Page) => {
|
|
70
|
+
await this.hooks.page({ meta: req.parsed, page });
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
/**
|
|
@@ -463,7 +466,7 @@ export class BrowserManager {
|
|
|
463
466
|
this.browsers.set(browser, connectionMeta);
|
|
464
467
|
|
|
465
468
|
await browser.launch(launchOptions as object);
|
|
466
|
-
await
|
|
469
|
+
await this.hooks.browser({ browser, meta: req.parsed });
|
|
467
470
|
|
|
468
471
|
browser.on('newPage', async (page) => {
|
|
469
472
|
await this.onNewPage(req, page);
|
package/src/exports.ts
CHANGED
package/src/file-system.spec.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Config, FileSystem,
|
|
1
|
+
import { Config, FileSystem, noop } from '@browserless.io/browserless';
|
|
2
2
|
import { readFile, unlink } from 'fs/promises';
|
|
3
3
|
import { expect } from 'chai';
|
|
4
4
|
|
|
5
5
|
const filePath = '/tmp/_browserless_test_fs_';
|
|
6
6
|
|
|
7
7
|
describe('File-System', () => {
|
|
8
|
-
afterEach(async () => unlink(filePath));
|
|
8
|
+
afterEach(async () => unlink(filePath).catch(noop));
|
|
9
9
|
|
|
10
10
|
it('saves and encodes files', async () => {
|
|
11
11
|
const mySecretContents = 'pony-foo';
|
|
@@ -13,46 +13,71 @@ describe('File-System', () => {
|
|
|
13
13
|
config.setToken('browserless.io');
|
|
14
14
|
const f = new FileSystem(config);
|
|
15
15
|
|
|
16
|
-
await f.append(filePath, mySecretContents);
|
|
16
|
+
await f.append(filePath, mySecretContents, true);
|
|
17
17
|
|
|
18
|
-
expect(await f.read(filePath)).to.eql([mySecretContents]);
|
|
18
|
+
expect(await f.read(filePath, true)).to.eql([mySecretContents]);
|
|
19
19
|
const rawText = (await readFile(filePath)).toString();
|
|
20
20
|
|
|
21
21
|
expect(rawText.toString()).to.not.include(mySecretContents);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
it('
|
|
24
|
+
it('saves files without encoding', async () => {
|
|
25
|
+
const mySecretContents = 'pony-foo';
|
|
26
|
+
const config = new Config();
|
|
27
|
+
config.setToken('browserless.io');
|
|
28
|
+
const f = new FileSystem(config);
|
|
29
|
+
|
|
30
|
+
await f.append(filePath, mySecretContents, false);
|
|
31
|
+
|
|
32
|
+
expect(await f.read(filePath, false)).to.eql([mySecretContents]);
|
|
33
|
+
const rawText = (await readFile(filePath)).toString();
|
|
34
|
+
|
|
35
|
+
expect(rawText.toString()).to.include(mySecretContents);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('appends newlines to files and encodes them', async () => {
|
|
25
39
|
const mySecretContents = 'pony-foo';
|
|
26
40
|
const moreSecretContents = 'pony-pony-foo-foo';
|
|
27
41
|
const config = new Config();
|
|
28
42
|
config.setToken('browserless.io');
|
|
29
43
|
const f = new FileSystem(config);
|
|
30
44
|
|
|
31
|
-
await f.append(filePath, mySecretContents);
|
|
45
|
+
await f.append(filePath, mySecretContents, true);
|
|
32
46
|
|
|
33
|
-
expect(await f.read(filePath)).to.eql([mySecretContents]);
|
|
47
|
+
expect(await f.read(filePath, true)).to.eql([mySecretContents]);
|
|
34
48
|
|
|
35
|
-
await f.append(filePath, moreSecretContents);
|
|
49
|
+
await f.append(filePath, moreSecretContents, true);
|
|
36
50
|
|
|
37
|
-
expect(await f.read(filePath)).to.eql([
|
|
51
|
+
expect(await f.read(filePath, true)).to.eql([
|
|
38
52
|
mySecretContents,
|
|
39
53
|
moreSecretContents,
|
|
40
54
|
]);
|
|
55
|
+
const rawText = (await readFile(filePath)).toString();
|
|
56
|
+
|
|
57
|
+
expect(rawText).to.not.include(mySecretContents);
|
|
58
|
+
expect(rawText).to.not.include(moreSecretContents);
|
|
41
59
|
});
|
|
42
60
|
|
|
43
|
-
it('
|
|
61
|
+
it('appends newlines to files and does not encode them', async () => {
|
|
62
|
+
const mySecretContents = 'pony-foo';
|
|
63
|
+
const moreSecretContents = 'pony-pony-foo-foo';
|
|
44
64
|
const config = new Config();
|
|
45
65
|
config.setToken('browserless.io');
|
|
46
66
|
const f = new FileSystem(config);
|
|
47
|
-
const mySecretContents = 'pony-foo';
|
|
48
67
|
|
|
49
|
-
await f.append(filePath, mySecretContents);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
await f.append(filePath, mySecretContents, false);
|
|
69
|
+
|
|
70
|
+
expect(await f.read(filePath, false)).to.eql([mySecretContents]);
|
|
71
|
+
|
|
72
|
+
await f.append(filePath, moreSecretContents, false);
|
|
73
|
+
|
|
74
|
+
expect(await f.read(filePath, false)).to.eql([
|
|
75
|
+
mySecretContents,
|
|
76
|
+
moreSecretContents,
|
|
77
|
+
]);
|
|
78
|
+
const rawText = (await readFile(filePath)).toString();
|
|
54
79
|
|
|
55
|
-
expect(
|
|
56
|
-
expect(
|
|
80
|
+
expect(rawText).to.include(mySecretContents);
|
|
81
|
+
expect(rawText).to.include(moreSecretContents);
|
|
57
82
|
});
|
|
58
83
|
});
|
package/src/file-system.ts
CHANGED
|
@@ -15,28 +15,8 @@ export class FileSystem extends EventEmitter {
|
|
|
15
15
|
constructor(protected config: Config) {
|
|
16
16
|
super();
|
|
17
17
|
this.currentAESKey = config.getAESKey();
|
|
18
|
-
this.config.on('token', this.handleTokenChange);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
private handleTokenChange = async () => {
|
|
22
|
-
this.log(`Token has changed, updating file-system contents`);
|
|
23
|
-
const start = Date.now();
|
|
24
|
-
const newAESKey = this.config.getAESKey();
|
|
25
|
-
await Promise.all(
|
|
26
|
-
Array.from(this.fsMap).map(async ([filePath, contents]) => {
|
|
27
|
-
const newlyEncoded = encrypt(
|
|
28
|
-
contents.join('\n'),
|
|
29
|
-
Buffer.from(newAESKey),
|
|
30
|
-
);
|
|
31
|
-
return writeFile(filePath, newlyEncoded);
|
|
32
|
-
}),
|
|
33
|
-
).catch((e) => {
|
|
34
|
-
this.log(`Error in setting new token: "${e}"`);
|
|
35
|
-
});
|
|
36
|
-
this.log(`Successfully updated file encodings in ${Date.now() - start}ms`);
|
|
37
|
-
this.currentAESKey = this.config.getAESKey();
|
|
38
|
-
};
|
|
39
|
-
|
|
40
20
|
/**
|
|
41
21
|
* Appends contents to a file-path for persistance. File contents are
|
|
42
22
|
* encrypted before being saved to disk. Reads happen via the in-memory
|
|
@@ -46,15 +26,19 @@ export class FileSystem extends EventEmitter {
|
|
|
46
26
|
* @param newContent A string of new content to add to the file
|
|
47
27
|
* @returns void
|
|
48
28
|
*/
|
|
49
|
-
append = async (
|
|
50
|
-
|
|
29
|
+
append = async (
|
|
30
|
+
path: string,
|
|
31
|
+
newContent: string,
|
|
32
|
+
shouldEncode: boolean,
|
|
33
|
+
): Promise<void> => {
|
|
34
|
+
const contents = await this.read(path, shouldEncode);
|
|
51
35
|
|
|
52
36
|
contents.push(newContent);
|
|
53
37
|
this.fsMap.set(path, contents);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
38
|
+
|
|
39
|
+
const encoded = shouldEncode
|
|
40
|
+
? await encrypt(contents.join('\n'), this.currentAESKey)
|
|
41
|
+
: contents.join('\n');
|
|
58
42
|
|
|
59
43
|
return writeFile(path, encoded.toString());
|
|
60
44
|
};
|
|
@@ -66,16 +50,18 @@ export class FileSystem extends EventEmitter {
|
|
|
66
50
|
* @param path The filepath of the contents to read
|
|
67
51
|
* @returns Promise of the contents separated by newlines
|
|
68
52
|
*/
|
|
69
|
-
read = async (path: string): Promise<string[]> => {
|
|
53
|
+
read = async (path: string, encoded: boolean): Promise<string[]> => {
|
|
70
54
|
const hasKey = this.fsMap.has(path);
|
|
71
55
|
|
|
72
56
|
if (hasKey) {
|
|
73
57
|
return this.fsMap.get(path) as string[];
|
|
74
58
|
}
|
|
75
59
|
const contents = (await readFile(path).catch(() => '')).toString();
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
60
|
+
const decoded =
|
|
61
|
+
encoded && contents.length
|
|
62
|
+
? await decrypt(contents, this.currentAESKey)
|
|
63
|
+
: contents;
|
|
64
|
+
const splitContents = decoded.length ? decoded.split('\n') : [];
|
|
79
65
|
|
|
80
66
|
this.fsMap.set(path, splitContents);
|
|
81
67
|
|
package/src/hooks.ts
CHANGED
|
@@ -1,8 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AfterResponse,
|
|
3
|
+
BeforeRequest,
|
|
4
|
+
BrowserHook,
|
|
5
|
+
PageHook,
|
|
6
|
+
} from '@browserless.io/browserless';
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
|
|
9
|
+
// KEPT for backwards compatibility reasons since some downstream
|
|
10
|
+
// docker images will override these files to inject their own hook
|
|
11
|
+
// behaviors
|
|
1
12
|
// @ts-ignore
|
|
2
|
-
|
|
13
|
+
import { default as afterRequest } from '../external/after.js';
|
|
3
14
|
// @ts-ignore
|
|
4
|
-
|
|
15
|
+
import { default as beforeRequest } from '../external/before.js';
|
|
5
16
|
// @ts-ignore
|
|
6
|
-
|
|
17
|
+
import { default as browserHook } from '../external/browser.js';
|
|
7
18
|
// @ts-ignore
|
|
8
|
-
|
|
19
|
+
import { default as pageHook } from '../external/page.js';
|
|
20
|
+
|
|
21
|
+
export class Hooks extends EventEmitter {
|
|
22
|
+
before(args: BeforeRequest): Promise<boolean> {
|
|
23
|
+
return beforeRequest(args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
after(args: AfterResponse): Promise<unknown> {
|
|
27
|
+
return afterRequest(args);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
page(args: PageHook): Promise<unknown> {
|
|
31
|
+
return pageHook(args);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
browser(args: BrowserHook): Promise<unknown> {
|
|
35
|
+
return browserHook(args);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Implement any browserless-core-specific shutdown logic here.
|
|
40
|
+
* Calls the empty-SDK stop method for downstream implementations.
|
|
41
|
+
*/
|
|
42
|
+
public shutdown = async () => {
|
|
43
|
+
await this.stop();
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Left blank for downstream SDK modules to optionally implement.
|
|
48
|
+
*/
|
|
49
|
+
public stop = () => {};
|
|
50
|
+
}
|
package/src/limiter.spec.ts
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Config,
|
|
3
|
+
Hooks,
|
|
3
4
|
Limiter,
|
|
4
5
|
Metrics,
|
|
5
6
|
Monitoring,
|
|
6
7
|
WebHooks,
|
|
7
8
|
sleep,
|
|
8
9
|
} from '@browserless.io/browserless';
|
|
10
|
+
import Sinon, { spy } from 'sinon';
|
|
9
11
|
import { expect } from 'chai';
|
|
10
|
-
import { spy } from 'sinon';
|
|
11
12
|
|
|
12
13
|
const asyncNoop = () => Promise.resolve(undefined);
|
|
13
14
|
const noop = () => undefined;
|
|
14
|
-
const webHooks =
|
|
15
|
-
|
|
16
|
-
callFailedHealthURL: spy(),
|
|
17
|
-
callQueueAlertURL: spy(),
|
|
18
|
-
callRejectAlertURL: spy(),
|
|
19
|
-
callTimeoutAlertURL: spy(),
|
|
20
|
-
};
|
|
15
|
+
const webHooks = Sinon.createStubInstance(WebHooks);
|
|
16
|
+
const hooks = Sinon.createStubInstance(Hooks);
|
|
21
17
|
|
|
22
18
|
describe(`Limiter`, () => {
|
|
23
19
|
afterEach(() => {
|
|
@@ -26,9 +22,13 @@ describe(`Limiter`, () => {
|
|
|
26
22
|
webHooks.callRejectAlertURL.resetHistory();
|
|
27
23
|
webHooks.callTimeoutAlertURL.resetHistory();
|
|
28
24
|
webHooks.callErrorAlertURL.resetHistory();
|
|
25
|
+
hooks.before.resetHistory();
|
|
26
|
+
hooks.after.resetHistory();
|
|
27
|
+
hooks.browser.resetHistory();
|
|
28
|
+
hooks.page.resetHistory();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('limits and queues function calls and calls queue alert urls', async () => {
|
|
31
|
+
it('limits and queues function calls, calls hooks, and calls queue alert urls', async () => {
|
|
32
32
|
return new Promise((resolve, reject) => {
|
|
33
33
|
const config = new Config();
|
|
34
34
|
config.setQueueAlertURL('https://example.com');
|
|
@@ -40,12 +40,7 @@ describe(`Limiter`, () => {
|
|
|
40
40
|
config.setQueued(1);
|
|
41
41
|
config.setTimeout(-1);
|
|
42
42
|
|
|
43
|
-
const limiter = new Limiter(
|
|
44
|
-
config,
|
|
45
|
-
metrics,
|
|
46
|
-
monitoring,
|
|
47
|
-
webHooks as unknown as WebHooks,
|
|
48
|
-
);
|
|
43
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
49
44
|
const handler = spy();
|
|
50
45
|
const job = limiter.limit(handler, asyncNoop, asyncNoop, noop);
|
|
51
46
|
|
|
@@ -56,6 +51,7 @@ describe(`Limiter`, () => {
|
|
|
56
51
|
|
|
57
52
|
limiter.addEventListener('end', () => {
|
|
58
53
|
try {
|
|
54
|
+
expect(hooks.after.called).to.be.true;
|
|
59
55
|
expect(handler.calledTwice).to.be.true;
|
|
60
56
|
expect(webHooks.callQueueAlertURL.calledOnce).to.be.true;
|
|
61
57
|
expect(metrics.get().queued).to.equal(1);
|
|
@@ -69,27 +65,36 @@ describe(`Limiter`, () => {
|
|
|
69
65
|
});
|
|
70
66
|
}).timeout(5000);
|
|
71
67
|
|
|
72
|
-
it('passes through arguments', () =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
it('passes through arguments', () =>
|
|
69
|
+
new Promise((resolve, reject) => {
|
|
70
|
+
const args = ['one', 'two', 'three'];
|
|
71
|
+
const config = new Config();
|
|
72
|
+
const metrics = new Metrics();
|
|
73
|
+
const monitoring = new Monitoring(config);
|
|
74
|
+
config.setConcurrent(1);
|
|
75
|
+
config.setQueued(0);
|
|
76
|
+
config.setTimeout(-1);
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
79
|
+
const handler = spy();
|
|
80
|
+
const job = limiter.limit(handler, asyncNoop, asyncNoop, noop);
|
|
81
|
+
// @ts-ignore will fix later
|
|
82
|
+
job(...args);
|
|
83
|
+
expect(handler.args[0]).to.eql(args);
|
|
84
|
+
|
|
85
|
+
limiter.addEventListener('end', () => {
|
|
86
|
+
try {
|
|
87
|
+
expect(hooks.after.args[0][0]).to.have.property('start');
|
|
88
|
+
expect(hooks.after.args[0][0]).to.have.property(
|
|
89
|
+
'status',
|
|
90
|
+
'successful',
|
|
91
|
+
);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return reject(e);
|
|
94
|
+
}
|
|
95
|
+
resolve(undefined);
|
|
96
|
+
});
|
|
97
|
+
}));
|
|
93
98
|
|
|
94
99
|
it('waits to run jobs until the first are done', async () => {
|
|
95
100
|
const config = new Config();
|
|
@@ -99,12 +104,7 @@ describe(`Limiter`, () => {
|
|
|
99
104
|
config.setQueued(1);
|
|
100
105
|
config.setTimeout(-1);
|
|
101
106
|
|
|
102
|
-
const limiter = new Limiter(
|
|
103
|
-
config,
|
|
104
|
-
metrics,
|
|
105
|
-
monitoring,
|
|
106
|
-
webHooks as unknown as WebHooks,
|
|
107
|
-
);
|
|
107
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
108
108
|
const handlerOne = () => new Promise((r) => setTimeout(r, 50));
|
|
109
109
|
const handlerTwo = spy();
|
|
110
110
|
|
|
@@ -129,12 +129,7 @@ describe(`Limiter`, () => {
|
|
|
129
129
|
config.setQueued(1);
|
|
130
130
|
config.setTimeout(-1);
|
|
131
131
|
|
|
132
|
-
const limiter = new Limiter(
|
|
133
|
-
config,
|
|
134
|
-
metrics,
|
|
135
|
-
monitoring,
|
|
136
|
-
webHooks as unknown as WebHooks,
|
|
137
|
-
);
|
|
132
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
138
133
|
const spy = () => new Promise((_r, rej) => rej(error));
|
|
139
134
|
|
|
140
135
|
const job = limiter.limit(spy, asyncNoop, asyncNoop, noop);
|
|
@@ -150,6 +145,7 @@ describe(`Limiter`, () => {
|
|
|
150
145
|
error,
|
|
151
146
|
);
|
|
152
147
|
expect(webHooks.callErrorAlertURL.calledOnce).to.be.true;
|
|
148
|
+
expect(hooks.after.args[0][0]).to.have.property('status', 'error');
|
|
153
149
|
});
|
|
154
150
|
});
|
|
155
151
|
});
|
|
@@ -163,12 +159,7 @@ describe(`Limiter`, () => {
|
|
|
163
159
|
config.setQueued(0);
|
|
164
160
|
config.setTimeout(-1);
|
|
165
161
|
|
|
166
|
-
const limiter = new Limiter(
|
|
167
|
-
config,
|
|
168
|
-
metrics,
|
|
169
|
-
monitoring,
|
|
170
|
-
webHooks as unknown as WebHooks,
|
|
171
|
-
);
|
|
162
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
172
163
|
|
|
173
164
|
const handler = spy();
|
|
174
165
|
const onError = spy();
|
|
@@ -184,37 +175,41 @@ describe(`Limiter`, () => {
|
|
|
184
175
|
expect(onError.args[0]).to.eql(args);
|
|
185
176
|
});
|
|
186
177
|
|
|
187
|
-
it('calls a timeout handler with arguments if a job takes too long', (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
let timer: NodeJS.Timer;
|
|
197
|
-
const limiter = new Limiter(
|
|
198
|
-
config,
|
|
199
|
-
metrics,
|
|
200
|
-
monitoring,
|
|
201
|
-
webHooks as unknown as WebHooks,
|
|
202
|
-
);
|
|
203
|
-
const handler = () =>
|
|
204
|
-
new Promise((d) => (timer = global.setTimeout(d, 1000)));
|
|
205
|
-
|
|
206
|
-
const onTimeout = (...calledArgs: unknown[]) => {
|
|
207
|
-
clearTimeout(timer as unknown as number);
|
|
208
|
-
expect(calledArgs).to.eql(args);
|
|
209
|
-
expect(webHooks.callTimeoutAlertURL.calledOnce).to.be.true;
|
|
210
|
-
r(null);
|
|
211
|
-
};
|
|
178
|
+
it('calls a timeout handler with arguments if a job takes too long', () =>
|
|
179
|
+
new Promise((resolve, reject) => {
|
|
180
|
+
const args = ['one', 'two', 'three'];
|
|
181
|
+
const config = new Config();
|
|
182
|
+
const metrics = new Metrics();
|
|
183
|
+
const monitoring = new Monitoring(config);
|
|
184
|
+
config.setConcurrent(1);
|
|
185
|
+
config.setQueued(0);
|
|
186
|
+
config.setTimeout(10);
|
|
212
187
|
|
|
213
|
-
|
|
188
|
+
let timer: NodeJS.Timer;
|
|
189
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
190
|
+
const handler = () =>
|
|
191
|
+
new Promise((d) => (timer = global.setTimeout(d, 1000)));
|
|
214
192
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
193
|
+
const onTimeout = (...calledArgs: unknown[]) => {
|
|
194
|
+
clearTimeout(timer as unknown as number);
|
|
195
|
+
expect(calledArgs).to.eql(args);
|
|
196
|
+
expect(webHooks.callTimeoutAlertURL.calledOnce).to.be.true;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const job = limiter.limit(handler, noop, onTimeout, noop);
|
|
200
|
+
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
job(...args);
|
|
203
|
+
|
|
204
|
+
limiter.addEventListener('end', () => {
|
|
205
|
+
try {
|
|
206
|
+
expect(hooks.after.args[0][0]).to.have.property('status', 'timedout');
|
|
207
|
+
} catch (e) {
|
|
208
|
+
return reject(e);
|
|
209
|
+
}
|
|
210
|
+
resolve(undefined);
|
|
211
|
+
});
|
|
212
|
+
}));
|
|
218
213
|
|
|
219
214
|
it('allows overriding the timeouts', async () => {
|
|
220
215
|
const config = new Config();
|
|
@@ -224,12 +219,7 @@ describe(`Limiter`, () => {
|
|
|
224
219
|
config.setQueued(0);
|
|
225
220
|
config.setTimeout(1);
|
|
226
221
|
|
|
227
|
-
const limiter = new Limiter(
|
|
228
|
-
config,
|
|
229
|
-
metrics,
|
|
230
|
-
monitoring,
|
|
231
|
-
webHooks as unknown as WebHooks,
|
|
232
|
-
);
|
|
222
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
233
223
|
const onTimeout = spy();
|
|
234
224
|
const handler = async () => new Promise((r) => setTimeout(r, 10));
|
|
235
225
|
|
|
@@ -252,12 +242,7 @@ describe(`Limiter`, () => {
|
|
|
252
242
|
config.setQueued(0);
|
|
253
243
|
config.setTimeout(20);
|
|
254
244
|
|
|
255
|
-
const limiter = new Limiter(
|
|
256
|
-
config,
|
|
257
|
-
metrics,
|
|
258
|
-
monitoring,
|
|
259
|
-
webHooks as unknown as WebHooks,
|
|
260
|
-
);
|
|
245
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
261
246
|
const handler = () => new Promise((r) => setTimeout(r, 1));
|
|
262
247
|
const timeout = spy();
|
|
263
248
|
const job = limiter.limit(handler, noop, timeout, noop);
|
|
@@ -276,12 +261,7 @@ describe(`Limiter`, () => {
|
|
|
276
261
|
config.setQueued(0);
|
|
277
262
|
config.setTimeout(-1);
|
|
278
263
|
|
|
279
|
-
const limiter = new Limiter(
|
|
280
|
-
config,
|
|
281
|
-
metrics,
|
|
282
|
-
monitoring,
|
|
283
|
-
webHooks as unknown as WebHooks,
|
|
284
|
-
);
|
|
264
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
285
265
|
|
|
286
266
|
const handler = spy();
|
|
287
267
|
const job = limiter.limit(handler, noop, noop, noop);
|
|
@@ -312,12 +292,7 @@ describe(`Limiter`, () => {
|
|
|
312
292
|
config.setQueued(10);
|
|
313
293
|
config.setTimeout(-1);
|
|
314
294
|
|
|
315
|
-
const limiter = new Limiter(
|
|
316
|
-
config,
|
|
317
|
-
metrics,
|
|
318
|
-
monitoring,
|
|
319
|
-
webHooks as unknown as WebHooks,
|
|
320
|
-
);
|
|
295
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
321
296
|
|
|
322
297
|
const handler = spy();
|
|
323
298
|
const job = limiter.limit(handler, noop, noop, noop);
|
|
@@ -343,12 +318,7 @@ describe(`Limiter`, () => {
|
|
|
343
318
|
config.setQueued(10);
|
|
344
319
|
config.setTimeout(-1);
|
|
345
320
|
|
|
346
|
-
const limiter = new Limiter(
|
|
347
|
-
config,
|
|
348
|
-
metrics,
|
|
349
|
-
monitoring,
|
|
350
|
-
webHooks as unknown as WebHooks,
|
|
351
|
-
);
|
|
321
|
+
const limiter = new Limiter(config, metrics, monitoring, webHooks, hooks);
|
|
352
322
|
|
|
353
323
|
const handler = spy();
|
|
354
324
|
const job = limiter.limit(handler, noop, noop, noop);
|