@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +156 -2
  2. package/out/__test__/invocationError.test.d.ts +2 -0
  3. package/out/__test__/invocationError.test.d.ts.map +1 -0
  4. package/out/__test__/invocationError.test.js +36 -0
  5. package/out/__test__/jobProgress.test.js +35 -26
  6. package/out/__test__/queue.test.js +46 -37
  7. package/out/__test__/utils.d.ts +1 -1
  8. package/out/__test__/utils.d.ts.map +1 -1
  9. package/out/__test__/utils.js +10 -6
  10. package/out/index.d.ts +3 -0
  11. package/out/index.d.ts.map +1 -1
  12. package/out/index.js +5 -0
  13. package/out/invocationError.d.ts +11 -0
  14. package/out/invocationError.d.ts.map +1 -0
  15. package/out/invocationError.js +22 -0
  16. package/out/invocationErrorCode.d.ts +9 -0
  17. package/out/invocationErrorCode.d.ts.map +1 -0
  18. package/out/invocationErrorCode.js +12 -0
  19. package/out/jobProgress.d.ts.map +1 -1
  20. package/out/jobProgress.js +8 -7
  21. package/out/queries.d.ts.map +1 -1
  22. package/out/queries.js +2 -1
  23. package/out/queue.d.ts.map +1 -1
  24. package/out/queue.js +8 -7
  25. package/out/queueResponse.d.ts +5 -1
  26. package/out/queueResponse.d.ts.map +1 -1
  27. package/out/queueResponse.js +9 -3
  28. package/out/retryOptions.d.ts +12 -0
  29. package/out/retryOptions.d.ts.map +1 -0
  30. package/out/retryOptions.js +9 -0
  31. package/out/validators.js +23 -14
  32. package/package.json +2 -2
  33. package/src/__test__/invocationError.test.ts +40 -0
  34. package/src/__test__/jobProgress.test.ts +31 -20
  35. package/src/__test__/queue.test.ts +40 -29
  36. package/src/__test__/utils.ts +1 -1
  37. package/src/index.ts +3 -0
  38. package/src/invocationError.ts +22 -0
  39. package/src/invocationErrorCode.ts +8 -0
  40. package/src/jobProgress.ts +2 -1
  41. package/src/queue.ts +2 -1
  42. package/src/queueResponse.ts +8 -2
  43. package/src/retryOptions.ts +14 -0
  44. package/tsconfig.tsbuildinfo +1 -1060
@@ -9,17 +9,22 @@ import {
9
9
  InvalidPushSettingsError,
10
10
  InvocationLimitReachedError
11
11
  } from '../errors';
12
- import { getApiClientMock, getApiClientMockWithoutResponseBody, verifyApiClientCalledPushPathWith } from './utils';
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
- const getQueue = (apiClientMock: any, queueName: string) => new Queue({ key: queueName }, apiClientMock);
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 = getApiClientMock();
22
- expect(() => getQueue(apiClientMock, 'invalid name')).toThrowError(
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 = getApiClientMock();
31
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock();
41
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock();
50
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock();
57
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock();
66
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock({}, 429);
75
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock({}, 405);
83
- const queue = getQueue(apiClientMock, 'name');
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 = getApiClientMock(
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(apiClientMock, 'name');
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 = getApiClientMock(
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(apiClientMock, 'name');
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 = getApiClientMock(
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(apiClientMock, 'name');
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 = getApiClientMock(
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(apiClientMock, 'name');
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(apiClientMock, 'name');
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 = getApiClientMock();
231
- const queue = getQueue(apiClientMock, 'name');
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
  });
@@ -1,6 +1,6 @@
1
1
  import { Payload } from '../types';
2
2
 
3
- export const getApiClientMock = (response?: any, statusCode = 201) => {
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
+ }
@@ -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 || (global as any).api.asApp().__requestAtlassian;
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 || (global as any).api.asApp().__requestAtlassian;
15
+ this.apiClient = apiClient || createRequestStargateAsApp();
15
16
  }
16
17
 
17
18
  async push(payloads: Payload | Payload[], pushSettings?: PushSettings): Promise<string> {
@@ -1,5 +1,11 @@
1
- export class QueueResponse {
2
- protected _retry = false;
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
+ }