@forge/events 0.0.0-experimental-e05f7a2 → 0.0.0-experimental-64caa5a

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 (60) hide show
  1. package/CHANGELOG.md +154 -1
  2. package/README.md +1 -1
  3. package/out/__test__/jobProgress.test.d.ts +2 -0
  4. package/out/__test__/jobProgress.test.d.ts.map +1 -0
  5. package/out/__test__/jobProgress.test.js +110 -0
  6. package/out/__test__/queue.test.d.ts +2 -0
  7. package/out/__test__/queue.test.d.ts.map +1 -0
  8. package/out/__test__/queue.test.js +179 -0
  9. package/out/__test__/queueResponse.test.d.ts +2 -0
  10. package/out/__test__/queueResponse.test.d.ts.map +1 -0
  11. package/out/__test__/queueResponse.test.js +14 -0
  12. package/out/__test__/utils.d.ts +7 -0
  13. package/out/__test__/utils.d.ts.map +1 -0
  14. package/out/__test__/utils.js +37 -0
  15. package/out/errors.d.ts +9 -0
  16. package/out/errors.d.ts.map +1 -1
  17. package/out/errors.js +19 -1
  18. package/out/index.d.ts +3 -0
  19. package/out/index.d.ts.map +1 -1
  20. package/out/index.js +15 -0
  21. package/out/jobProgress.d.ts +9 -0
  22. package/out/jobProgress.d.ts.map +1 -0
  23. package/out/jobProgress.js +36 -0
  24. package/out/queries.d.ts +4 -2
  25. package/out/queries.d.ts.map +1 -1
  26. package/out/queries.js +13 -8
  27. package/out/queue.d.ts +4 -4
  28. package/out/queue.d.ts.map +1 -1
  29. package/out/queue.js +25 -25
  30. package/out/queueResponse.d.ts +5 -0
  31. package/out/queueResponse.d.ts.map +1 -0
  32. package/out/queueResponse.js +12 -0
  33. package/out/text.d.ts +14 -0
  34. package/out/text.d.ts.map +1 -0
  35. package/out/text.js +16 -0
  36. package/out/types.d.ts +11 -4
  37. package/out/types.d.ts.map +1 -1
  38. package/out/validators.d.ts +10 -4
  39. package/out/validators.d.ts.map +1 -1
  40. package/out/validators.js +69 -18
  41. package/package.json +7 -2
  42. package/src/__test__/jobProgress.test.ts +149 -0
  43. package/src/__test__/queue.test.ts +236 -0
  44. package/src/__test__/queueResponse.test.ts +14 -0
  45. package/src/__test__/utils.ts +47 -0
  46. package/src/errors.ts +18 -0
  47. package/src/index.ts +14 -0
  48. package/src/jobProgress.ts +48 -0
  49. package/src/queries.ts +15 -9
  50. package/src/queue.ts +28 -29
  51. package/src/queueResponse.ts +7 -0
  52. package/src/text.ts +15 -0
  53. package/src/types.ts +12 -4
  54. package/src/validators.ts +98 -31
  55. package/tsconfig.json +12 -9
  56. package/tsconfig.tsbuildinfo +107 -20
  57. package/out/__test__/index.test.d.ts +0 -2
  58. package/out/__test__/index.test.d.ts.map +0 -1
  59. package/out/__test__/index.test.js +0 -126
  60. package/src/__test__/index.test.ts +0 -168
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JobProgress = void 0;
4
+ const queries_1 = require("./queries");
5
+ const validators_1 = require("./validators");
6
+ class JobProgress {
7
+ constructor(id, apiClient) {
8
+ this.id = id;
9
+ this.apiClient = apiClient || global.api.asApp().__requestAtlassian;
10
+ }
11
+ async getStats() {
12
+ const [queueName, jobId] = this.id.split('#');
13
+ const getStatsRequest = {
14
+ queueName: queueName,
15
+ jobId: jobId,
16
+ time: new Date().toISOString()
17
+ };
18
+ validators_1.validateGetStatsPayload(getStatsRequest);
19
+ const response = await queries_1.post(queries_1.GET_STATS_PATH, getStatsRequest, this.apiClient);
20
+ await validators_1.validateGetStatsAPIResponse(response, getStatsRequest);
21
+ return response;
22
+ }
23
+ async cancel() {
24
+ const [queueName, jobId] = this.id.split('#');
25
+ const cancelJobRequest = {
26
+ queueName: queueName,
27
+ jobId: jobId,
28
+ time: new Date().toISOString()
29
+ };
30
+ validators_1.validateCancelJobRequest(cancelJobRequest);
31
+ const response = await queries_1.post(queries_1.CANCEL_JOB_PATH, cancelJobRequest, this.apiClient);
32
+ await validators_1.validateCancelJobAPIResponse(response, cancelJobRequest);
33
+ return response;
34
+ }
35
+ }
36
+ exports.JobProgress = JobProgress;
package/out/queries.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { PushBodyParams, APIRequest } from './types';
2
- export declare const getPushBody: (params: PushBodyParams) => APIRequest;
1
+ import { APIRequest, APIResponse, FetchMethod } from './types';
3
2
  export declare const PUSH_PATH = "/webhook/queue/publish/{cloudId}/{environmentId}/{appId}/{appVersion}";
3
+ export declare const GET_STATS_PATH = "/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}";
4
+ export declare const CANCEL_JOB_PATH = "/webhook/queue/cancel/{cloudId}/{environmentId}/{appId}/{appVersion}";
5
+ export declare const post: (endpoint: string, body: APIRequest, apiClient: FetchMethod) => Promise<APIResponse>;
4
6
  //# sourceMappingURL=queries.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD,eAAO,MAAM,WAAW,WAAY,cAAc,KAAG,UAMnD,CAAC;AAEH,eAAO,MAAM,SAAS,0EAA0E,CAAC"}
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/D,eAAO,MAAM,SAAS,0EAA0E,CAAC;AACjG,eAAO,MAAM,cAAc,wEAAwE,CAAC;AACpG,eAAO,MAAM,eAAe,yEAAyE,CAAC;AAEtG,eAAO,MAAM,IAAI,aAAoB,MAAM,QAAQ,UAAU,aAAa,WAAW,KAAG,OAAO,CAAC,WAAW,CAU1G,CAAC"}
package/out/queries.js CHANGED
@@ -1,11 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PUSH_PATH = exports.getPushBody = void 0;
4
- exports.getPushBody = (params) => ({
5
- queueName: params.queueName,
6
- type: params.type,
7
- schema: params.schema,
8
- payload: params.payload,
9
- time: new Date().toISOString()
10
- });
3
+ exports.post = exports.CANCEL_JOB_PATH = exports.GET_STATS_PATH = exports.PUSH_PATH = void 0;
11
4
  exports.PUSH_PATH = '/webhook/queue/publish/{cloudId}/{environmentId}/{appId}/{appVersion}';
5
+ exports.GET_STATS_PATH = '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}';
6
+ exports.CANCEL_JOB_PATH = '/webhook/queue/cancel/{cloudId}/{environmentId}/{appId}/{appVersion}';
7
+ exports.post = async (endpoint, body, apiClient) => {
8
+ const request = {
9
+ method: 'POST',
10
+ body: JSON.stringify(body),
11
+ headers: {
12
+ 'content-type': 'application/json'
13
+ }
14
+ };
15
+ return await apiClient(endpoint, request);
16
+ };
package/out/queue.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { APIResponse, FetchMethod, Payload, QueueParams } from './types';
1
+ import { FetchMethod, Payload, QueueParams, PushSettings } from './types';
2
+ import { JobProgress } from './jobProgress';
2
3
  export declare class Queue {
3
4
  private readonly apiClient;
4
5
  private queueParams;
5
6
  constructor(queueParams: QueueParams, apiClient?: FetchMethod);
6
- push(payloads: Payload | Payload[]): Promise<APIResponse>;
7
- private query;
8
- private buildRequest;
7
+ push(payloads: Payload | Payload[], pushSettings?: PushSettings): Promise<string>;
8
+ getJob(jobId: string): JobProgress;
9
9
  }
10
10
  //# sourceMappingURL=queue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAkB,WAAW,EAAc,MAAM,SAAS,CAAC;AAOrG,qBAAa,KAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,WAAW,CAAc;gBAErB,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,WAAW;IAMvD,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;YAajD,KAAK;IAKnB,OAAO,CAAC,YAAY;CASrB"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAe,MAAM,SAAS,CAAC;AAEvF,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,qBAAa,KAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,WAAW,CAAc;gBAErB,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,WAAW;IAMvD,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,EAAE,EAAE,YAAY,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BvF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW;CAGnC"}
package/out/queue.js CHANGED
@@ -1,41 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Queue = void 0;
4
+ const tslib_1 = require("tslib");
4
5
  const queries_1 = require("./queries");
5
6
  const validators_1 = require("./validators");
6
- async function getResponseBody(response, body) {
7
- await validators_1.validateAPIResponse(response, body);
8
- return response;
9
- }
7
+ const v4_1 = tslib_1.__importDefault(require("uuid/v4"));
8
+ const jobProgress_1 = require("./jobProgress");
10
9
  class Queue {
11
10
  constructor(queueParams, apiClient) {
12
- validators_1.validateQueueName(queueParams.queueName);
11
+ validators_1.validateQueueKey(queueParams.key);
13
12
  this.queueParams = queueParams;
14
13
  this.apiClient = apiClient || global.api.asApp().__requestAtlassian;
15
14
  }
16
- async push(payloads) {
17
- validators_1.validatePayloads(payloads);
18
- const queryParams = {
19
- payload: Array.isArray(payloads) ? payloads : [payloads],
20
- queueName: this.queueParams.queueName,
15
+ async push(payloads, pushSettings) {
16
+ validators_1.validatePushPayloads(payloads);
17
+ const queueName = this.queueParams.key;
18
+ const jobId = v4_1.default();
19
+ const pushRequest = {
20
+ queueName: queueName,
21
+ jobId: jobId,
22
+ type: 'avi:forge:app:event',
21
23
  schema: 'ari:cloud:ecosystem::forge/app-event',
22
- type: 'avi:forge:app:event'
24
+ payload: Array.isArray(payloads) ? payloads : [payloads],
25
+ time: new Date().toISOString()
23
26
  };
24
- const requestBody = queries_1.getPushBody(queryParams);
25
- return this.query(queries_1.PUSH_PATH, requestBody);
26
- }
27
- async query(endpoint, body) {
28
- const response = await this.apiClient(endpoint, this.buildRequest(body));
29
- return await getResponseBody(response, body);
30
- }
31
- buildRequest(requestBody) {
32
- return {
33
- method: 'POST',
34
- body: JSON.stringify(requestBody),
35
- headers: {
36
- 'content-type': 'application/json'
27
+ if (pushSettings) {
28
+ validators_1.validatePushSettings(pushSettings);
29
+ if (pushSettings.delayInSeconds) {
30
+ pushRequest.delayInSeconds = pushSettings.delayInSeconds;
37
31
  }
38
- };
32
+ }
33
+ const response = await queries_1.post(queries_1.PUSH_PATH, pushRequest, this.apiClient);
34
+ await validators_1.validatePushAPIResponse(response, pushRequest);
35
+ return `${queueName}#${jobId}`;
36
+ }
37
+ getJob(jobId) {
38
+ return new jobProgress_1.JobProgress(jobId, this.apiClient);
39
39
  }
40
40
  }
41
41
  exports.Queue = Queue;
@@ -0,0 +1,5 @@
1
+ export declare class QueueResponse {
2
+ protected _retry: boolean;
3
+ retry(): void;
4
+ }
5
+ //# sourceMappingURL=queueResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queueResponse.d.ts","sourceRoot":"","sources":["../src/queueResponse.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAa;IACxB,SAAS,CAAC,MAAM,UAAS;IAEzB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueueResponse = void 0;
4
+ class QueueResponse {
5
+ constructor() {
6
+ this._retry = false;
7
+ }
8
+ retry() {
9
+ this._retry = true;
10
+ }
11
+ }
12
+ exports.QueueResponse = QueueResponse;
package/out/text.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare const Text: {
2
+ error: {
3
+ invalidQueueName: string;
4
+ invalidDelayInSecondsSetting: string;
5
+ maxEventsAllowed: (maxEventsCount: number) => string;
6
+ maxPayloadAllowed: (maxPayloadSize: number) => string;
7
+ noEventsPushed: string;
8
+ rateLimitError: string;
9
+ invocationLimitReachedError: string;
10
+ jobIdEmpty: string;
11
+ jobDoesNotExit: (jobId: string, queueName: string) => string;
12
+ };
13
+ };
14
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI;;;;2CAIsB,MAAM,KAAG,MAAM;4CAEd,MAAM,KAAG,MAAM;;;;;gCAK3B,MAAM,aAAa,MAAM,KAAG,MAAM;;CAG7D,CAAC"}
package/out/text.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Text = void 0;
4
+ exports.Text = {
5
+ error: {
6
+ invalidQueueName: `Queue names can only contain alphanumeric characters, dashes and underscores.`,
7
+ invalidDelayInSecondsSetting: `The delayInSeconds setting must be between 0 and 900.`,
8
+ maxEventsAllowed: (maxEventsCount) => `This push contains more than the ${maxEventsCount} events allowed.`,
9
+ maxPayloadAllowed: (maxPayloadSize) => `The maximum payload size is ${maxPayloadSize}KB.`,
10
+ noEventsPushed: `No events pushed.`,
11
+ rateLimitError: `Too many requests.`,
12
+ invocationLimitReachedError: `The limit on cyclic invocation has been reached.`,
13
+ jobIdEmpty: `jobId cannot be empty.`,
14
+ jobDoesNotExit: (jobId, queueName) => `The job ${jobId} was not found for the queue ${queueName}.`
15
+ }
16
+ };
package/out/types.d.ts CHANGED
@@ -4,20 +4,27 @@ export declare type FetchMethod = (url: string, init: RequestInit) => Promise<AP
4
4
  export declare type Payload = string | number | boolean | {
5
5
  [key: string]: Payload;
6
6
  };
7
+ export interface PushSettings {
8
+ delayInSeconds: number;
9
+ }
7
10
  export interface QueueParams {
8
- queueName: string;
11
+ key: string;
9
12
  }
10
- export interface PushBodyParams {
13
+ export interface PushRequest extends APIRequest {
11
14
  payload: Payload[];
12
- queueName: string;
13
15
  schema: string;
14
16
  type: string;
17
+ delayInSeconds?: number;
15
18
  }
16
- export interface APIRequest extends PushBodyParams {
19
+ export interface APIRequest {
20
+ queueName: string;
21
+ jobId: string;
17
22
  time: string;
18
23
  }
19
24
  export interface FailedEvent {
20
25
  errorMessage: string;
21
26
  payload: Payload;
22
27
  }
28
+ export declare type GetStatsRequest = APIRequest;
29
+ export declare type CancelJobRequest = APIRequest;
23
30
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnD,oBAAY,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,IAAI,GAAG,QAAQ,GAAG,YAAY,CAAC,CAAC;AAC3G,oBAAY,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AACnF,oBAAY,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE7E,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnD,oBAAY,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,IAAI,GAAG,QAAQ,GAAG,YAAY,CAAC,CAAC;AAC3G,oBAAY,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AACnF,oBAAY,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB;AACD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,oBAAY,eAAe,GAAG,UAAU,CAAC;AACzC,oBAAY,gBAAgB,GAAG,UAAU,CAAC"}
@@ -1,5 +1,11 @@
1
- import { APIResponse, Payload, APIRequest } from './types';
2
- export declare const validateQueueName: (queueName: string) => void;
3
- export declare const validatePayloads: (payloads: Payload | Payload[]) => void;
4
- export declare const validateAPIResponse: (response: APIResponse, requestBody: APIRequest) => Promise<void>;
1
+ import { APIResponse, CancelJobRequest, GetStatsRequest, Payload, PushRequest, PushSettings } from './types';
2
+ export declare const validateQueueKey: (queueName: string) => void;
3
+ export declare const validatePushSettings: (settings: PushSettings) => void;
4
+ export declare const validatePushPayloads: (payloads: Payload | Payload[]) => void;
5
+ export declare const validateGetStatsPayload: (getStatsRequest: GetStatsRequest) => void;
6
+ export declare const validateCancelJobRequest: (cancelJobRequest: CancelJobRequest) => void;
7
+ export declare const validateAPIResponse: (response: APIResponse, expectedSuccessStatus: number) => Promise<void>;
8
+ export declare const validatePushAPIResponse: (response: APIResponse, requestBody: PushRequest) => Promise<void>;
9
+ export declare const validateGetStatsAPIResponse: (response: APIResponse, getStatsRequest: GetStatsRequest) => Promise<void>;
10
+ export declare const validateCancelJobAPIResponse: (response: APIResponse, cancelJobRequest: GetStatsRequest) => Promise<void>;
5
11
  //# sourceMappingURL=validators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAM3D,eAAO,MAAM,iBAAiB,cAAe,MAAM,SAIlD,CAAC;AAEF,eAAO,MAAM,gBAAgB,aAAc,OAAO,GAAG,OAAO,EAAE,SAa7D,CAAC;AAEF,eAAO,MAAM,mBAAmB,aAAoB,WAAW,eAAe,UAAU,kBAsCvF,CAAC"}
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM7G,eAAO,MAAM,gBAAgB,cAAe,MAAM,SAIjD,CAAC;AAEF,eAAO,MAAM,oBAAoB,aAAc,YAAY,SAI1D,CAAC;AAEF,eAAO,MAAM,oBAAoB,aAAc,OAAO,GAAG,OAAO,EAAE,SAajE,CAAC;AAEF,eAAO,MAAM,uBAAuB,oBAAqB,eAAe,SAKvE,CAAC;AAEF,eAAO,MAAM,wBAAwB,qBAAsB,gBAAgB,SAK1E,CAAC;AAEF,eAAO,MAAM,mBAAmB,aAAoB,WAAW,yBAAyB,MAAM,kBA6B7F,CAAC;AAEF,eAAO,MAAM,uBAAuB,aAAoB,WAAW,eAAe,WAAW,kBAkC5F,CAAC;AAEF,eAAO,MAAM,2BAA2B,aAAoB,WAAW,mBAAmB,eAAe,kBAMxG,CAAC;AAEF,eAAO,MAAM,4BAA4B,aAAoB,WAAW,oBAAoB,eAAe,kBAM1G,CAAC"}
package/out/validators.js CHANGED
@@ -1,52 +1,103 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateAPIResponse = exports.validatePayloads = exports.validateQueueName = void 0;
3
+ exports.validateCancelJobAPIResponse = exports.validateGetStatsAPIResponse = exports.validatePushAPIResponse = exports.validateAPIResponse = exports.validateCancelJobRequest = exports.validateGetStatsPayload = exports.validatePushPayloads = exports.validatePushSettings = exports.validateQueueKey = void 0;
4
4
  const errors_1 = require("./errors");
5
+ const text_1 = require("./text");
5
6
  const VALID_QUEUE_NAME_PATTERN = /^[a-zA-Z0-9-_]+$/;
6
- const MAXIMUM_EVENTS = 10;
7
+ const MAXIMUM_EVENTS = 50;
7
8
  const MAXIMUM_PAYLOAD_SIZE_KB = 200;
8
- exports.validateQueueName = (queueName) => {
9
+ exports.validateQueueKey = (queueName) => {
9
10
  if (!queueName || !VALID_QUEUE_NAME_PATTERN.test(queueName)) {
10
- throw new errors_1.InvalidQueueNameError('Queue name can only contain alphanumeric, dash and underscore characters');
11
+ throw new errors_1.InvalidQueueNameError(text_1.Text.error.invalidQueueName);
11
12
  }
12
13
  };
13
- exports.validatePayloads = (payloads) => {
14
+ exports.validatePushSettings = (settings) => {
15
+ if ((settings.delayInSeconds && settings.delayInSeconds > 900) || settings.delayInSeconds < 0) {
16
+ throw new errors_1.InvalidPushSettingsError(text_1.Text.error.invalidDelayInSecondsSetting);
17
+ }
18
+ };
19
+ exports.validatePushPayloads = (payloads) => {
14
20
  if (!payloads || (Array.isArray(payloads) && payloads.length === 0)) {
15
- throw new errors_1.NoEventsToPushError(`No events pushed`);
21
+ throw new errors_1.NoEventsToPushError(text_1.Text.error.noEventsPushed);
16
22
  }
17
23
  if (Array.isArray(payloads) && payloads.length > MAXIMUM_EVENTS) {
18
- throw new errors_1.TooManyEventsError(`Maximum of ${MAXIMUM_EVENTS} events allowed in a single push`);
24
+ throw new errors_1.TooManyEventsError(text_1.Text.error.maxEventsAllowed(MAXIMUM_EVENTS));
19
25
  }
20
26
  const payloadSizeKB = Buffer.byteLength(JSON.stringify(payloads)) / 1024;
21
27
  if (payloadSizeKB > MAXIMUM_PAYLOAD_SIZE_KB) {
22
- throw new errors_1.PayloadTooBigError(`The maximum payload size allowed is ${MAXIMUM_PAYLOAD_SIZE_KB}KB`);
28
+ throw new errors_1.PayloadTooBigError(text_1.Text.error.maxPayloadAllowed(MAXIMUM_PAYLOAD_SIZE_KB));
29
+ }
30
+ };
31
+ exports.validateGetStatsPayload = (getStatsRequest) => {
32
+ if (!getStatsRequest.jobId) {
33
+ throw new errors_1.JobDoesNotExistError(text_1.Text.error.jobIdEmpty);
23
34
  }
35
+ exports.validateQueueKey(getStatsRequest.queueName);
24
36
  };
25
- exports.validateAPIResponse = async (response, requestBody) => {
37
+ exports.validateCancelJobRequest = (cancelJobRequest) => {
38
+ if (!cancelJobRequest.jobId) {
39
+ throw new errors_1.JobDoesNotExistError(text_1.Text.error.jobIdEmpty);
40
+ }
41
+ exports.validateQueueKey(cancelJobRequest.queueName);
42
+ };
43
+ exports.validateAPIResponse = async (response, expectedSuccessStatus) => {
26
44
  if (response.status === 429) {
27
- throw new errors_1.RateLimitError(`Too many requests`);
45
+ throw new errors_1.RateLimitError(text_1.Text.error.rateLimitError);
28
46
  }
47
+ if (response.status === 405) {
48
+ throw new errors_1.InvocationLimitReachedError(text_1.Text.error.invocationLimitReachedError);
49
+ }
50
+ if (response.status != expectedSuccessStatus && response.status) {
51
+ let internalServerError;
52
+ try {
53
+ const responseBody = await response.json();
54
+ const errorMessage = responseBody.message ? `: ${responseBody.message}` : '';
55
+ const errors = responseBody.errors ? `: ${responseBody.errors.join(', ')}` : '';
56
+ internalServerError = new errors_1.InternalServerError(`${response.status} ${response.statusText}${errorMessage}${errors}`, responseBody.code, responseBody.details);
57
+ }
58
+ catch (ignore) {
59
+ internalServerError = new errors_1.InternalServerError(`${response.status} ${response.statusText}`, response.status);
60
+ }
61
+ throw internalServerError;
62
+ }
63
+ };
64
+ exports.validatePushAPIResponse = async (response, requestBody) => {
29
65
  if (response.status === 413) {
30
66
  const responseBody = await response.json();
31
67
  throw new errors_1.PayloadTooBigError(responseBody.errorMessage);
32
68
  }
33
69
  if (response.status === 202) {
34
70
  const responseBody = await response.json();
71
+ const defaultErrorMessage = 'Failed to process some events.';
72
+ const partialSuccessError = new errors_1.PartialSuccessError(defaultErrorMessage, []);
35
73
  if (responseBody.failedEvents && responseBody.failedEvents.length > 0) {
36
- throw new errors_1.PartialSuccessError(`Failed to process ${responseBody.failedEvents.length} events`, responseBody.failedEvents.map((failedEvent) => {
74
+ partialSuccessError.message = `Failed to process ${responseBody.failedEvents.length} event(s).`;
75
+ partialSuccessError.failedEvents = responseBody.failedEvents.map((failedEvent) => {
37
76
  return {
38
77
  errorMessage: failedEvent.errorMessage,
39
78
  payload: requestBody.payload[+failedEvent.index]
40
79
  };
41
- }));
80
+ });
42
81
  }
43
- else {
44
- throw new errors_1.PartialSuccessError(`Failed to process events`, []);
82
+ if (responseBody.errorMessage) {
83
+ partialSuccessError.message =
84
+ partialSuccessError.message !== defaultErrorMessage
85
+ ? `${partialSuccessError.message} ${responseBody.errorMessage}`
86
+ : responseBody.errorMessage;
45
87
  }
88
+ throw partialSuccessError;
46
89
  }
47
- if (response.status != 201 && response.status) {
48
- const responseBody = await response.json();
49
- const errorMessage = responseBody.message ? `: ${responseBody.message}` : '';
50
- throw new errors_1.InternalServerError(`${response.status} ${response.statusText}${errorMessage}`, responseBody.code, responseBody.details);
90
+ await exports.validateAPIResponse(response, 201);
91
+ };
92
+ exports.validateGetStatsAPIResponse = async (response, getStatsRequest) => {
93
+ if (response.status === 404) {
94
+ throw new errors_1.JobDoesNotExistError(text_1.Text.error.jobDoesNotExit(getStatsRequest.jobId, getStatsRequest.queueName));
95
+ }
96
+ await exports.validateAPIResponse(response, 200);
97
+ };
98
+ exports.validateCancelJobAPIResponse = async (response, cancelJobRequest) => {
99
+ if (response.status === 404) {
100
+ throw new errors_1.JobDoesNotExistError(text_1.Text.error.jobDoesNotExit(cancelJobRequest.jobId, cancelJobRequest.queueName));
51
101
  }
102
+ await exports.validateAPIResponse(response, 204);
52
103
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/events",
3
- "version": "0.0.0-experimental-e05f7a2",
3
+ "version": "0.0.0-experimental-64caa5a",
4
4
  "description": "Forge Async Event methods",
5
5
  "author": "Atlassian",
6
6
  "license": "UNLICENSED",
@@ -12,6 +12,11 @@
12
12
  "compile": "tsc -b -v"
13
13
  },
14
14
  "devDependencies": {
15
- "@types/node": "^12.12.63"
15
+ "@types/node": "^12.12.63",
16
+ "@types/uuid": "^3.4.7"
17
+ },
18
+ "dependencies": {
19
+ "@forge/api": "^0.0.0-experimental-64caa5a",
20
+ "uuid": "^3.4.0"
16
21
  }
17
22
  }
@@ -0,0 +1,149 @@
1
+ import { getApiClientMock, verifyApiClientCalledWith } from './utils';
2
+ import { InternalServerError, InvalidQueueNameError, JobDoesNotExistError, RateLimitError } from '../errors';
3
+ import { JobProgress } from '../jobProgress';
4
+
5
+ const getJobProgress = (apiClientMock: any, jobId: string) => new JobProgress(jobId, apiClientMock);
6
+
7
+ describe('JobProgress methods', () => {
8
+ describe('getStats', () => {
9
+ it('should call the queue/stats endpoint', async () => {
10
+ const apiClientMock = getApiClientMock(
11
+ {
12
+ success: 100,
13
+ inProgress: 50,
14
+ failed: 1
15
+ },
16
+ 200
17
+ );
18
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
19
+ const response = await jobProgress.getStats();
20
+ const { success, inProgress, failed } = await response.json();
21
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
22
+ queueName: 'test-queue-name',
23
+ jobId: 'test-job-id'
24
+ });
25
+ expect(success).toEqual(100);
26
+ expect(inProgress).toEqual(50);
27
+ expect(failed).toEqual(1);
28
+ });
29
+
30
+ it('should throw JobDoesNotExistError', async () => {
31
+ const apiClientMock = getApiClientMock(
32
+ {
33
+ message: 'Job Not Found',
34
+ code: 404
35
+ },
36
+ 404
37
+ );
38
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
39
+ await expect(jobProgress.getStats()).rejects.toThrow(
40
+ new JobDoesNotExistError(`The job test-job-id was not found for the queue test-queue-name.`)
41
+ );
42
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
43
+ queueName: 'test-queue-name',
44
+ jobId: 'test-job-id'
45
+ });
46
+ });
47
+
48
+ it('should throw RateLimitError', async () => {
49
+ const apiClientMock = getApiClientMock({}, 429);
50
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
51
+ await expect(jobProgress.getStats()).rejects.toThrow(new RateLimitError(`Too many requests.`));
52
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
53
+ queueName: 'test-queue-name',
54
+ jobId: 'test-job-id'
55
+ });
56
+ });
57
+
58
+ it('should throw InternalServerError', async () => {
59
+ const apiClientMock = getApiClientMock(
60
+ {
61
+ message: 'Service is not available',
62
+ code: 513,
63
+ details: 'The request processing has failed because of an unknown error, exception or failure'
64
+ },
65
+ 513
66
+ );
67
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
68
+ await expect(jobProgress.getStats()).rejects.toThrow(
69
+ new InternalServerError(`513 Status Text: Service is not available`, 513)
70
+ );
71
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
72
+ queueName: 'test-queue-name',
73
+ jobId: 'test-job-id'
74
+ });
75
+ });
76
+ });
77
+
78
+ describe('cancel', () => {
79
+ it('should call the queue/cancel endpoint', async () => {
80
+ const apiClientMock = getApiClientMock({}, 204);
81
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
82
+ const response = await jobProgress.cancel();
83
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/cancel/{cloudId}/{environmentId}/{appId}/{appVersion}', {
84
+ queueName: 'test-queue-name',
85
+ jobId: 'test-job-id'
86
+ });
87
+ expect(response.status).toEqual(204);
88
+ });
89
+
90
+ it('should throw JobDoesNotExistError', async () => {
91
+ const apiClientMock = getApiClientMock(
92
+ {
93
+ message: 'Job Not Found',
94
+ code: 404
95
+ },
96
+ 404
97
+ );
98
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
99
+ await expect(jobProgress.cancel()).rejects.toThrow(
100
+ new JobDoesNotExistError(`The job test-job-id was not found for the queue test-queue-name.`)
101
+ );
102
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/cancel/{cloudId}/{environmentId}/{appId}/{appVersion}', {
103
+ queueName: 'test-queue-name',
104
+ jobId: 'test-job-id'
105
+ });
106
+ });
107
+ });
108
+
109
+ it('should throw InternalServerError when WHP returns 422 response', async () => {
110
+ const apiClientMock = getApiClientMock(
111
+ {
112
+ errors: ['jobId must not be null', 'queueName must not be null']
113
+ },
114
+ 422
115
+ );
116
+ const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
117
+ await expect(jobProgress.getStats()).rejects.toThrow(
118
+ new InternalServerError(`422 Status Text: jobId must not be null, queueName must not be null`)
119
+ );
120
+
121
+ verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
122
+ queueName: 'test-queue-name',
123
+ jobId: 'test-job-id'
124
+ });
125
+ });
126
+
127
+ it('should throw errors when queueName or jobId is empty', async () => {
128
+ const apiClientMock = getApiClientMock(
129
+ {
130
+ success: 100,
131
+ inProgress: 50,
132
+ failed: 1
133
+ },
134
+ 200
135
+ );
136
+ const jobProgress1 = getJobProgress(apiClientMock, 'test-queue-name#');
137
+ await expect(jobProgress1.getStats()).rejects.toThrow(new JobDoesNotExistError(`jobId cannot be empty.`));
138
+
139
+ const jobProgress2 = getJobProgress(apiClientMock, '#test-job-id');
140
+ await expect(jobProgress2.getStats()).rejects.toThrow(
141
+ new InvalidQueueNameError('Queue names can only contain alphanumeric characters, dashes and underscores.')
142
+ );
143
+
144
+ const jobProgress3 = getJobProgress(apiClientMock, '');
145
+ await expect(jobProgress3.getStats()).rejects.toThrow(new JobDoesNotExistError(`jobId cannot be empty.`));
146
+
147
+ expect(apiClientMock).toHaveBeenCalledTimes(0);
148
+ });
149
+ });