@forge/events 0.0.0-experimental-490cfcf → 0.0.0-experimental-51786e9
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 +156 -2
- package/out/__test__/invocationError.test.d.ts +2 -0
- package/out/__test__/invocationError.test.d.ts.map +1 -0
- package/out/__test__/invocationError.test.js +36 -0
- package/out/__test__/jobProgress.test.js +35 -26
- package/out/__test__/queue.test.js +46 -37
- package/out/__test__/utils.d.ts +1 -1
- package/out/__test__/utils.d.ts.map +1 -1
- package/out/__test__/utils.js +10 -6
- package/out/index.d.ts +3 -0
- package/out/index.d.ts.map +1 -1
- package/out/index.js +5 -0
- package/out/invocationError.d.ts +11 -0
- package/out/invocationError.d.ts.map +1 -0
- package/out/invocationError.js +22 -0
- package/out/invocationErrorCode.d.ts +9 -0
- package/out/invocationErrorCode.d.ts.map +1 -0
- package/out/invocationErrorCode.js +12 -0
- package/out/jobProgress.d.ts.map +1 -1
- package/out/jobProgress.js +8 -7
- package/out/queries.d.ts.map +1 -1
- package/out/queries.js +2 -1
- package/out/queue.d.ts.map +1 -1
- package/out/queue.js +8 -7
- package/out/queueResponse.d.ts +5 -1
- package/out/queueResponse.d.ts.map +1 -1
- package/out/queueResponse.js +9 -3
- package/out/retryOptions.d.ts +12 -0
- package/out/retryOptions.d.ts.map +1 -0
- package/out/retryOptions.js +9 -0
- package/out/validators.js +23 -14
- package/package.json +2 -2
- package/src/__test__/invocationError.test.ts +40 -0
- package/src/__test__/jobProgress.test.ts +31 -20
- package/src/__test__/queue.test.ts +40 -29
- package/src/__test__/utils.ts +1 -1
- package/src/index.ts +3 -0
- package/src/invocationError.ts +22 -0
- package/src/invocationErrorCode.ts +8 -0
- package/src/jobProgress.ts +2 -1
- package/src/queue.ts +2 -1
- package/src/queueResponse.ts +8 -2
- package/src/retryOptions.ts +14 -0
- package/tsconfig.tsbuildinfo +1 -1060
|
@@ -9,17 +9,22 @@ import {
|
|
|
9
9
|
InvalidPushSettingsError,
|
|
10
10
|
InvocationLimitReachedError
|
|
11
11
|
} from '../errors';
|
|
12
|
-
import {
|
|
12
|
+
import { getMockFetchMethod, getApiClientMockWithoutResponseBody, verifyApiClientCalledPushPathWith } from './utils';
|
|
13
13
|
import { Queue } from '../queue';
|
|
14
14
|
import { JobProgress } from '../jobProgress';
|
|
15
|
+
import { createRequestStargateAsApp } from '@forge/api';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
jest.mock('@forge/api', () => ({
|
|
18
|
+
createRequestStargateAsApp: jest.fn().mockReturnValue(getMockFetchMethod('done', 201))
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const getQueue = (queueName: string, apiClientMock?: any) => new Queue({ key: queueName }, apiClientMock);
|
|
17
22
|
|
|
18
23
|
describe('Queue methods', () => {
|
|
19
24
|
describe('constructor', () => {
|
|
20
25
|
it('should throw InvalidQueueNameError', async () => {
|
|
21
|
-
const apiClientMock =
|
|
22
|
-
expect(() => getQueue(
|
|
26
|
+
const apiClientMock = getMockFetchMethod();
|
|
27
|
+
expect(() => getQueue('invalid name', apiClientMock)).toThrowError(
|
|
23
28
|
new InvalidQueueNameError('Queue names can only contain alphanumeric characters, dashes and underscores.')
|
|
24
29
|
);
|
|
25
30
|
});
|
|
@@ -27,8 +32,8 @@ describe('Queue methods', () => {
|
|
|
27
32
|
|
|
28
33
|
describe('push', () => {
|
|
29
34
|
it('should call the queue/publish endpoint', async () => {
|
|
30
|
-
const apiClientMock =
|
|
31
|
-
const queue = getQueue(
|
|
35
|
+
const apiClientMock = getMockFetchMethod();
|
|
36
|
+
const queue = getQueue('name', apiClientMock);
|
|
32
37
|
const payload = {
|
|
33
38
|
page: 1
|
|
34
39
|
};
|
|
@@ -37,8 +42,8 @@ describe('Queue methods', () => {
|
|
|
37
42
|
});
|
|
38
43
|
|
|
39
44
|
it('should throw InvalidPushSettingsError for delay', async () => {
|
|
40
|
-
const apiClientMock =
|
|
41
|
-
const queue = getQueue(
|
|
45
|
+
const apiClientMock = getMockFetchMethod();
|
|
46
|
+
const queue = getQueue('name', apiClientMock);
|
|
42
47
|
await expect(queue.push(1, { delayInSeconds: 901 })).rejects.toThrow(
|
|
43
48
|
new InvalidPushSettingsError(`The delayInSeconds setting must be between 0 and 900.`)
|
|
44
49
|
);
|
|
@@ -46,15 +51,15 @@ describe('Queue methods', () => {
|
|
|
46
51
|
});
|
|
47
52
|
|
|
48
53
|
it('should throw NoEventsToPushError', async () => {
|
|
49
|
-
const apiClientMock =
|
|
50
|
-
const queue = getQueue(
|
|
54
|
+
const apiClientMock = getMockFetchMethod();
|
|
55
|
+
const queue = getQueue('name', apiClientMock);
|
|
51
56
|
await expect(queue.push([])).rejects.toThrow(new NoEventsToPushError(`No events pushed.`));
|
|
52
57
|
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
53
58
|
});
|
|
54
59
|
|
|
55
60
|
it('should throw TooManyEventsError', async () => {
|
|
56
|
-
const apiClientMock =
|
|
57
|
-
const queue = getQueue(
|
|
61
|
+
const apiClientMock = getMockFetchMethod();
|
|
62
|
+
const queue = getQueue('name', apiClientMock);
|
|
58
63
|
await expect(queue.push([...Array(51).keys()])).rejects.toThrow(
|
|
59
64
|
new TooManyEventsError(`This push contains more than the 50 events allowed.`)
|
|
60
65
|
);
|
|
@@ -62,8 +67,8 @@ describe('Queue methods', () => {
|
|
|
62
67
|
});
|
|
63
68
|
|
|
64
69
|
it('should throw PayloadTooBigError', async () => {
|
|
65
|
-
const apiClientMock =
|
|
66
|
-
const queue = getQueue(
|
|
70
|
+
const apiClientMock = getMockFetchMethod();
|
|
71
|
+
const queue = getQueue('name', apiClientMock);
|
|
67
72
|
await expect(queue.push('x'.repeat(201 * 1024))).rejects.toThrow(
|
|
68
73
|
new PayloadTooBigError(`The maximum payload size is 200KB.`)
|
|
69
74
|
);
|
|
@@ -71,16 +76,16 @@ describe('Queue methods', () => {
|
|
|
71
76
|
});
|
|
72
77
|
|
|
73
78
|
it('should throw RateLimitError', async () => {
|
|
74
|
-
const apiClientMock =
|
|
75
|
-
const queue = getQueue(
|
|
79
|
+
const apiClientMock = getMockFetchMethod({}, 429);
|
|
80
|
+
const queue = getQueue('name', apiClientMock);
|
|
76
81
|
const payload = [...Array(10).keys()];
|
|
77
82
|
await expect(queue.push(payload)).rejects.toThrow(new RateLimitError(`Too many requests.`));
|
|
78
83
|
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
it('should throw InvocationLimitReachedError', async () => {
|
|
82
|
-
const apiClientMock =
|
|
83
|
-
const queue = getQueue(
|
|
87
|
+
const apiClientMock = getMockFetchMethod({}, 405);
|
|
88
|
+
const queue = getQueue('name', apiClientMock);
|
|
84
89
|
const payload = [...Array(5).keys()];
|
|
85
90
|
await expect(queue.push(payload)).rejects.toThrow(
|
|
86
91
|
new InvocationLimitReachedError(`The limit on cyclic invocation has been reached.`)
|
|
@@ -89,7 +94,7 @@ describe('Queue methods', () => {
|
|
|
89
94
|
});
|
|
90
95
|
|
|
91
96
|
it('should throw PartialSuccessError when there are failed events', async () => {
|
|
92
|
-
const apiClientMock =
|
|
97
|
+
const apiClientMock = getMockFetchMethod(
|
|
93
98
|
{
|
|
94
99
|
failedEvents: [
|
|
95
100
|
{
|
|
@@ -104,7 +109,7 @@ describe('Queue methods', () => {
|
|
|
104
109
|
},
|
|
105
110
|
202
|
|
106
111
|
);
|
|
107
|
-
const queue = getQueue(
|
|
112
|
+
const queue = getQueue('name', apiClientMock);
|
|
108
113
|
const payload = [
|
|
109
114
|
{
|
|
110
115
|
content: 'payload-1'
|
|
@@ -136,13 +141,13 @@ describe('Queue methods', () => {
|
|
|
136
141
|
});
|
|
137
142
|
|
|
138
143
|
it('should throw PartialSuccessError when backend failed to create job stats', async () => {
|
|
139
|
-
const apiClientMock =
|
|
144
|
+
const apiClientMock = getMockFetchMethod(
|
|
140
145
|
{
|
|
141
146
|
errorMessage: 'Failed to create stats for job name#12345'
|
|
142
147
|
},
|
|
143
148
|
202
|
|
144
149
|
);
|
|
145
|
-
const queue = getQueue(
|
|
150
|
+
const queue = getQueue('name', apiClientMock);
|
|
146
151
|
const payload = [
|
|
147
152
|
{
|
|
148
153
|
content: 'payload-1'
|
|
@@ -161,7 +166,7 @@ describe('Queue methods', () => {
|
|
|
161
166
|
});
|
|
162
167
|
|
|
163
168
|
it('should throw PartialSuccessError when there are failed events and backend failed to create job stats', async () => {
|
|
164
|
-
const apiClientMock =
|
|
169
|
+
const apiClientMock = getMockFetchMethod(
|
|
165
170
|
{
|
|
166
171
|
errorMessage: 'Failed to create stats for job name#12345',
|
|
167
172
|
failedEvents: [
|
|
@@ -173,7 +178,7 @@ describe('Queue methods', () => {
|
|
|
173
178
|
},
|
|
174
179
|
202
|
|
175
180
|
);
|
|
176
|
-
const queue = getQueue(
|
|
181
|
+
const queue = getQueue('name', apiClientMock);
|
|
177
182
|
const payload = [
|
|
178
183
|
{
|
|
179
184
|
content: 'payload-1'
|
|
@@ -196,7 +201,7 @@ describe('Queue methods', () => {
|
|
|
196
201
|
});
|
|
197
202
|
|
|
198
203
|
it('should throw InternalServerError', async () => {
|
|
199
|
-
const apiClientMock =
|
|
204
|
+
const apiClientMock = getMockFetchMethod(
|
|
200
205
|
{
|
|
201
206
|
message: 'AWS SQS timed out',
|
|
202
207
|
code: 500,
|
|
@@ -204,7 +209,7 @@ describe('Queue methods', () => {
|
|
|
204
209
|
},
|
|
205
210
|
500
|
|
206
211
|
);
|
|
207
|
-
const queue = getQueue(
|
|
212
|
+
const queue = getQueue('name', apiClientMock);
|
|
208
213
|
const payload = [...Array(9).keys()];
|
|
209
214
|
await expect(queue.push(payload)).rejects.toThrow(
|
|
210
215
|
new InternalServerError(
|
|
@@ -218,17 +223,23 @@ describe('Queue methods', () => {
|
|
|
218
223
|
|
|
219
224
|
it('should throw InternalServerError for error response without response body', async () => {
|
|
220
225
|
const apiClientMock = getApiClientMockWithoutResponseBody(504, 'Gateway Timeout');
|
|
221
|
-
const queue = getQueue(
|
|
226
|
+
const queue = getQueue('name', apiClientMock);
|
|
222
227
|
const payload = [1];
|
|
223
228
|
await expect(queue.push(1)).rejects.toThrow(new InternalServerError(`504 Gateway Timeout`, 504));
|
|
224
229
|
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
225
230
|
});
|
|
231
|
+
|
|
232
|
+
it('requests stargate if no api client is provided', async () => {
|
|
233
|
+
const queue = getQueue('queue');
|
|
234
|
+
await queue.push({ test: 'stargate' });
|
|
235
|
+
expect(createRequestStargateAsApp()).toBeCalledTimes(1);
|
|
236
|
+
});
|
|
226
237
|
});
|
|
227
238
|
|
|
228
239
|
describe('getJob', () => {
|
|
229
240
|
it('should create a JobProgress by jobId', async () => {
|
|
230
|
-
const apiClientMock =
|
|
231
|
-
const queue = getQueue(
|
|
241
|
+
const apiClientMock = getMockFetchMethod();
|
|
242
|
+
const queue = getQueue('name', apiClientMock);
|
|
232
243
|
const jobProgress = queue.getJob('test-job-id');
|
|
233
244
|
expect(jobProgress).toEqual(new JobProgress('test-job-id', apiClientMock));
|
|
234
245
|
});
|
package/src/__test__/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Payload } from '../types';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const getMockFetchMethod = (response?: any, statusCode = 201) => {
|
|
4
4
|
return jest.fn().mockReturnValue({
|
|
5
5
|
created: statusCode === 201,
|
|
6
6
|
status: statusCode,
|
package/src/index.ts
CHANGED
|
@@ -13,3 +13,6 @@ export {
|
|
|
13
13
|
} from './errors';
|
|
14
14
|
export { JobProgress } from './jobProgress';
|
|
15
15
|
export { QueueResponse } from './queueResponse';
|
|
16
|
+
export { InvocationError } from './invocationError';
|
|
17
|
+
export { InvocationErrorCode } from './invocationErrorCode';
|
|
18
|
+
export { RetryOptions } from './retryOptions';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DEFAULT_RETRY_OPTIONS, MIN_RETRY_AFTER, RetryOptions } from './retryOptions';
|
|
2
|
+
import { Response } from './queueResponse';
|
|
3
|
+
|
|
4
|
+
export class InvocationError extends Response {
|
|
5
|
+
constructor(public retryOptions: RetryOptions = DEFAULT_RETRY_OPTIONS) {
|
|
6
|
+
super(true);
|
|
7
|
+
if (this.retryOptions.retryAfter !== undefined && this.retryOptions.retryAfter <= 0) {
|
|
8
|
+
this.retryOptions.retryAfter = MIN_RETRY_AFTER;
|
|
9
|
+
}
|
|
10
|
+
return (this.toJSON() as unknown) as InvocationError;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/***
|
|
14
|
+
* Function to convert class object to JSON object so that app function return this as part of lambda invocation.
|
|
15
|
+
*/
|
|
16
|
+
public toJSON(): { _retry: boolean; retryOptions: RetryOptions } {
|
|
17
|
+
return {
|
|
18
|
+
_retry: this._retry,
|
|
19
|
+
retryOptions: this.retryOptions
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export enum InvocationErrorCode {
|
|
2
|
+
FUNCTION_OUT_OF_MEMORY = 'FUNCTION_OUT_OF_MEMORY',
|
|
3
|
+
FUNCTION_TIME_OUT = 'FUNCTION_TIME_OUT',
|
|
4
|
+
FUNCTION_PLATFORM_UNKNOWN_ERROR = 'FUNCTION_PLATFORM_UNKNOWN_ERROR',
|
|
5
|
+
FUNCTION_PLATFORM_RATE_LIMITED = 'FUNCTION_PLATFORM_RATE_LIMITED',
|
|
6
|
+
FUNCTION_UPSTREAM_RATE_LIMITED = 'FUNCTION_UPSTREAM_RATE_LIMITED',
|
|
7
|
+
FUNCTION_RETRY_REQUEST = 'FUNCTION_RETRY_REQUEST'
|
|
8
|
+
}
|
package/src/jobProgress.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
validateGetStatsPayload
|
|
7
7
|
} from './validators';
|
|
8
8
|
import { APIResponse, CancelJobRequest, FetchMethod, GetStatsRequest } from './types';
|
|
9
|
+
import { createRequestStargateAsApp } from '@forge/api';
|
|
9
10
|
|
|
10
11
|
export class JobProgress {
|
|
11
12
|
private readonly apiClient: FetchMethod;
|
|
@@ -13,7 +14,7 @@ export class JobProgress {
|
|
|
13
14
|
|
|
14
15
|
constructor(id: string, apiClient?: FetchMethod) {
|
|
15
16
|
this.id = id;
|
|
16
|
-
this.apiClient = apiClient || (
|
|
17
|
+
this.apiClient = apiClient || createRequestStargateAsApp();
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
async getStats(): Promise<APIResponse> {
|
package/src/queue.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { validatePushAPIResponse, validatePushPayloads, validateQueueKey, valida
|
|
|
3
3
|
import { FetchMethod, Payload, QueueParams, PushSettings, PushRequest } from './types';
|
|
4
4
|
import uuid from 'uuid/v4';
|
|
5
5
|
import { JobProgress } from './jobProgress';
|
|
6
|
+
import { createRequestStargateAsApp } from '@forge/api';
|
|
6
7
|
|
|
7
8
|
export class Queue {
|
|
8
9
|
private readonly apiClient: FetchMethod;
|
|
@@ -11,7 +12,7 @@ export class Queue {
|
|
|
11
12
|
constructor(queueParams: QueueParams, apiClient?: FetchMethod) {
|
|
12
13
|
validateQueueKey(queueParams.key);
|
|
13
14
|
this.queueParams = queueParams;
|
|
14
|
-
this.apiClient = apiClient || (
|
|
15
|
+
this.apiClient = apiClient || createRequestStargateAsApp();
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
async push(payloads: Payload | Payload[], pushSettings?: PushSettings): Promise<string> {
|
package/src/queueResponse.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
export class
|
|
2
|
-
protected _retry
|
|
1
|
+
export class Response {
|
|
2
|
+
constructor(protected _retry: boolean) {}
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export class QueueResponse extends Response {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(false);
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
retry(): void {
|
|
5
11
|
this._retry = true;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { InvocationErrorCode } from './invocationErrorCode';
|
|
2
|
+
|
|
3
|
+
export const MIN_RETRY_AFTER = 1;
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_RETRY_OPTIONS = {
|
|
6
|
+
retryAfter: MIN_RETRY_AFTER,
|
|
7
|
+
retryReason: InvocationErrorCode.FUNCTION_RETRY_REQUEST
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface RetryOptions {
|
|
11
|
+
retryAfter: number;
|
|
12
|
+
retryReason: InvocationErrorCode;
|
|
13
|
+
retryData?: any;
|
|
14
|
+
}
|