@forge/events 1.1.0-next.0 → 2.0.0-next.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 +14 -0
- package/README.md +32 -26
- package/out/__test__/jobProgress.test.js +15 -16
- package/out/__test__/queue.test.js +89 -40
- package/out/__test__/utils.js +1 -1
- package/out/errors.d.ts +17 -19
- package/out/errors.d.ts.map +1 -1
- package/out/errors.js +21 -34
- package/out/index.d.ts +2 -1
- package/out/index.d.ts.map +1 -1
- package/out/jobProgress.d.ts +11 -4
- package/out/jobProgress.d.ts.map +1 -1
- package/out/jobProgress.js +9 -9
- package/out/queue.d.ts +2 -2
- package/out/queue.d.ts.map +1 -1
- package/out/queue.js +8 -13
- package/out/text.d.ts +2 -0
- package/out/text.d.ts.map +1 -1
- package/out/text.js +2 -0
- package/out/types.d.ts +24 -9
- package/out/types.d.ts.map +1 -1
- package/out/validators.d.ts +4 -4
- package/out/validators.d.ts.map +1 -1
- package/out/validators.js +22 -11
- package/package.json +1 -1
- package/src/__test__/jobProgress.test.ts +16 -16
- package/src/__test__/queue.test.ts +103 -44
- package/src/__test__/utils.ts +1 -1
- package/src/errors.ts +20 -43
- package/src/index.ts +2 -1
- package/src/jobProgress.ts +17 -12
- package/src/queue.ts +10 -16
- package/src/text.ts +2 -0
- package/src/types.ts +30 -7
- package/src/validators.ts +27 -10
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @forge/events
|
|
2
2
|
|
|
3
|
+
## 2.0.0-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 882de38: Fix async events job progress operations
|
|
8
|
+
|
|
9
|
+
## 2.0.0-next.1
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- a89e05a: Restrict async events payload to objects
|
|
14
|
+
- 81174c3: Change `queue.push()` to accept events containing body and delay
|
|
15
|
+
- 9227948: Simplify JobProgress.getStats to return stats directly and remove unneeded response from JobProgress.cancel
|
|
16
|
+
|
|
3
17
|
## 1.1.0-next.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,39 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
# Forge Events
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Library for [asynchronous data processing](https://developer.atlassian.com/platform/forge/runtime-reference/async-events-api/) in Forge.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import fetch, { RequestInit } from 'node-fetch';
|
|
5
|
+
## Requirements
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
See [Set up Forge](https://developer.atlassian.com/platform/forge/set-up-forge/) for details of the software required to develop Forge apps.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
## Usage
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
const appContextAri = 'ari:cloud:jira::site/...';
|
|
14
|
-
const token = '...';
|
|
11
|
+
### Pushing events to the queue
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
const url = API_BASE + path;
|
|
13
|
+
```typescript
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
async function pushEvent() {
|
|
16
|
+
const { jobId } = await queue.push({
|
|
17
|
+
body: {
|
|
18
|
+
hello: 'world'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
init.headers = Object.assign(init.headers!, extraHeaders);
|
|
27
|
-
return fetch(url, init);
|
|
22
|
+
const jobProgress = queue.getJob(jobId);
|
|
23
|
+
const { success, inProgress, failed } = await jobProgress.getStats();
|
|
28
24
|
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Consuming events from the queue
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { AsyncEvent } from '@forge/events';
|
|
29
31
|
|
|
30
|
-
async function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
export async function eventListener(event: AsyncEvent, context) {
|
|
33
|
+
const jobProgress = queue.getJob(event.jobId);
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// process the event
|
|
37
|
+
} catch (error) {
|
|
38
|
+
await jobProgress.cancel();
|
|
34
39
|
}
|
|
35
|
-
await queue.push([payloads])
|
|
36
40
|
}
|
|
37
|
-
|
|
38
|
-
demo();
|
|
39
41
|
```
|
|
42
|
+
|
|
43
|
+
## Support
|
|
44
|
+
|
|
45
|
+
See [Get help](https://developer.atlassian.com/platform/forge/get-help/) for how to get help and provide feedback.
|
|
@@ -5,9 +5,10 @@ const errors_1 = require("../errors");
|
|
|
5
5
|
const jobProgress_1 = require("../jobProgress");
|
|
6
6
|
const api_1 = require("@forge/api");
|
|
7
7
|
jest.mock('@forge/api', () => ({
|
|
8
|
-
__requestAtlassianAsApp: (0, utils_1.getMockFetchMethod)(
|
|
8
|
+
__requestAtlassianAsApp: (0, utils_1.getMockFetchMethod)({ done: true }, 200)
|
|
9
9
|
}));
|
|
10
|
-
const
|
|
10
|
+
const queueParams = { key: 'test-queue-name' };
|
|
11
|
+
const getJobProgress = (jobId, apiClientMock) => new jobProgress_1.JobProgress(queueParams, jobId, apiClientMock);
|
|
11
12
|
describe('JobProgress methods', () => {
|
|
12
13
|
describe('getStats', () => {
|
|
13
14
|
it('should call the queue/stats endpoint', async () => {
|
|
@@ -16,9 +17,8 @@ describe('JobProgress methods', () => {
|
|
|
16
17
|
inProgress: 50,
|
|
17
18
|
failed: 1
|
|
18
19
|
}, 200);
|
|
19
|
-
const jobProgress = getJobProgress('test-
|
|
20
|
-
const
|
|
21
|
-
const { success, inProgress, failed } = await response.json();
|
|
20
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
21
|
+
const { success, inProgress, failed } = await jobProgress.getStats();
|
|
22
22
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/stats/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
23
23
|
queueName: 'test-queue-name',
|
|
24
24
|
jobId: 'test-job-id'
|
|
@@ -32,7 +32,7 @@ describe('JobProgress methods', () => {
|
|
|
32
32
|
message: 'Job Not Found',
|
|
33
33
|
code: 404
|
|
34
34
|
}, 404);
|
|
35
|
-
const jobProgress = getJobProgress('test-
|
|
35
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
36
36
|
await expect(jobProgress.getStats()).rejects.toThrow(new errors_1.JobDoesNotExistError(`The job test-job-id was not found for the queue test-queue-name.`));
|
|
37
37
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/stats/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
38
38
|
queueName: 'test-queue-name',
|
|
@@ -41,7 +41,7 @@ describe('JobProgress methods', () => {
|
|
|
41
41
|
});
|
|
42
42
|
it('should throw RateLimitError', async () => {
|
|
43
43
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({}, 429);
|
|
44
|
-
const jobProgress = getJobProgress('test-
|
|
44
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
45
45
|
await expect(jobProgress.getStats()).rejects.toThrow(new errors_1.RateLimitError(`Too many requests.`));
|
|
46
46
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/stats/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
47
47
|
queueName: 'test-queue-name',
|
|
@@ -54,7 +54,7 @@ describe('JobProgress methods', () => {
|
|
|
54
54
|
code: 513,
|
|
55
55
|
details: 'The request processing has failed because of an unknown error, exception or failure'
|
|
56
56
|
}, 513);
|
|
57
|
-
const jobProgress = getJobProgress('test-
|
|
57
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
58
58
|
await expect(jobProgress.getStats()).rejects.toThrow(new errors_1.InternalServerError(`513 Status Text: Service is not available`, 513));
|
|
59
59
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/stats/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
60
60
|
queueName: 'test-queue-name',
|
|
@@ -65,20 +65,19 @@ describe('JobProgress methods', () => {
|
|
|
65
65
|
describe('cancel', () => {
|
|
66
66
|
it('should call the queue/cancel endpoint', async () => {
|
|
67
67
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({}, 204);
|
|
68
|
-
const jobProgress = getJobProgress('test-
|
|
69
|
-
|
|
68
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
69
|
+
await jobProgress.cancel();
|
|
70
70
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/cancel/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
71
71
|
queueName: 'test-queue-name',
|
|
72
72
|
jobId: 'test-job-id'
|
|
73
73
|
});
|
|
74
|
-
expect(response.status).toEqual(204);
|
|
75
74
|
});
|
|
76
75
|
it('should throw JobDoesNotExistError', async () => {
|
|
77
76
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({
|
|
78
77
|
message: 'Job Not Found',
|
|
79
78
|
code: 404
|
|
80
79
|
}, 404);
|
|
81
|
-
const jobProgress = getJobProgress('test-
|
|
80
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
82
81
|
await expect(jobProgress.cancel()).rejects.toThrow(new errors_1.JobDoesNotExistError(`The job test-job-id was not found for the queue test-queue-name.`));
|
|
83
82
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/cancel/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
84
83
|
queueName: 'test-queue-name',
|
|
@@ -90,7 +89,7 @@ describe('JobProgress methods', () => {
|
|
|
90
89
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({
|
|
91
90
|
errors: ['jobId must not be null', 'queueName must not be null']
|
|
92
91
|
}, 422);
|
|
93
|
-
const jobProgress = getJobProgress('test-
|
|
92
|
+
const jobProgress = getJobProgress('test-job-id', apiClientMock);
|
|
94
93
|
await expect(jobProgress.getStats()).rejects.toThrow(new errors_1.InternalServerError(`422 Status Text: jobId must not be null, queueName must not be null`));
|
|
95
94
|
(0, utils_1.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/stats/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
96
95
|
queueName: 'test-queue-name',
|
|
@@ -103,16 +102,16 @@ describe('JobProgress methods', () => {
|
|
|
103
102
|
inProgress: 50,
|
|
104
103
|
failed: 1
|
|
105
104
|
}, 200);
|
|
106
|
-
const jobProgress1 = getJobProgress('
|
|
105
|
+
const jobProgress1 = getJobProgress('', apiClientMock);
|
|
107
106
|
await expect(jobProgress1.getStats()).rejects.toThrow(new errors_1.JobDoesNotExistError(`jobId cannot be empty.`));
|
|
108
|
-
const jobProgress2 =
|
|
107
|
+
const jobProgress2 = new jobProgress_1.JobProgress({ key: '!' }, 'test-job-id', apiClientMock);
|
|
109
108
|
await expect(jobProgress2.getStats()).rejects.toThrow(new errors_1.InvalidQueueNameError('Queue names can only contain alphanumeric characters, dashes and underscores.'));
|
|
110
109
|
const jobProgress3 = getJobProgress('', apiClientMock);
|
|
111
110
|
await expect(jobProgress3.getStats()).rejects.toThrow(new errors_1.JobDoesNotExistError(`jobId cannot be empty.`));
|
|
112
111
|
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
113
112
|
});
|
|
114
113
|
it('requests stargate if no api client is provided', async () => {
|
|
115
|
-
const jobProgress = getJobProgress('
|
|
114
|
+
const jobProgress = getJobProgress('job-id');
|
|
116
115
|
await jobProgress.getStats();
|
|
117
116
|
expect(api_1.__requestAtlassianAsApp).toHaveBeenCalledTimes(1);
|
|
118
117
|
});
|
|
@@ -16,20 +16,51 @@ describe('Queue methods', () => {
|
|
|
16
16
|
expect(() => getQueue('invalid name', apiClientMock)).toThrowError(new errors_1.InvalidQueueNameError('Queue names can only contain alphanumeric characters, dashes and underscores.'));
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
|
+
const PAYLOAD = { body: { page: 1 } };
|
|
19
20
|
describe('push', () => {
|
|
20
|
-
it(
|
|
21
|
+
it.each([
|
|
22
|
+
['single payload', PAYLOAD, [PAYLOAD]],
|
|
23
|
+
['payload array', [PAYLOAD, PAYLOAD], [PAYLOAD, PAYLOAD]]
|
|
24
|
+
])('should call the queue/publish endpoint when given %s', async (_, payload, expectedPayload) => {
|
|
21
25
|
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
22
26
|
const queue = getQueue('name', apiClientMock);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
await queue.push(payload);
|
|
28
|
+
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, expectedPayload, 'name');
|
|
29
|
+
});
|
|
30
|
+
it.each([
|
|
31
|
+
['number', 1],
|
|
32
|
+
['string', 'test'],
|
|
33
|
+
['boolean', true],
|
|
34
|
+
['array', [1, 2, 3]],
|
|
35
|
+
['null', null],
|
|
36
|
+
['undefined', undefined],
|
|
37
|
+
['array with non-object', [1]],
|
|
38
|
+
['array with an array', [[2, 3]]]
|
|
39
|
+
])('rejects non-object (%s) event', async (_, payload) => {
|
|
40
|
+
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
41
|
+
const queue = getQueue('name', apiClientMock);
|
|
42
|
+
await expect(queue.push(payload)).rejects.toThrow(new errors_1.InvalidPayloadError(`Event must be an object.`));
|
|
43
|
+
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
44
|
+
});
|
|
45
|
+
it.each([
|
|
46
|
+
['number', 1],
|
|
47
|
+
['string', 'test'],
|
|
48
|
+
['boolean', true],
|
|
49
|
+
['array', [1, 2, 3]],
|
|
50
|
+
['null', null],
|
|
51
|
+
['undefined', undefined],
|
|
52
|
+
['array with non-object', [1]],
|
|
53
|
+
['array with an array', [[2, 3]]]
|
|
54
|
+
])('rejects non-object (%s) event body', async (_, body) => {
|
|
55
|
+
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
56
|
+
const queue = getQueue('name', apiClientMock);
|
|
57
|
+
await expect(queue.push({ body })).rejects.toThrow(new errors_1.InvalidPayloadError(`Event body must be an object.`));
|
|
58
|
+
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
28
59
|
});
|
|
29
60
|
it('should throw InvalidPushSettingsError for delay', async () => {
|
|
30
61
|
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
31
62
|
const queue = getQueue('name', apiClientMock);
|
|
32
|
-
await expect(queue.push(
|
|
63
|
+
await expect(queue.push({ ...PAYLOAD, delayInSeconds: 901 })).rejects.toThrow(new errors_1.InvalidPushSettingsError(`The delayInSeconds setting must be between 0 and 900.`));
|
|
33
64
|
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
34
65
|
});
|
|
35
66
|
it('should throw NoEventsToPushError', async () => {
|
|
@@ -41,28 +72,26 @@ describe('Queue methods', () => {
|
|
|
41
72
|
it('should throw TooManyEventsError', async () => {
|
|
42
73
|
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
43
74
|
const queue = getQueue('name', apiClientMock);
|
|
44
|
-
await expect(queue.push(
|
|
75
|
+
await expect(queue.push(Array(51).fill(PAYLOAD))).rejects.toThrow(new errors_1.TooManyEventsError(`This push contains more than the 50 events allowed.`));
|
|
45
76
|
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
46
77
|
});
|
|
47
78
|
it('should throw PayloadTooBigError', async () => {
|
|
48
79
|
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
49
80
|
const queue = getQueue('name', apiClientMock);
|
|
50
|
-
await expect(queue.push('x'.repeat(201 * 1024))).rejects.toThrow(new errors_1.PayloadTooBigError(`The maximum payload size is 200KB.`));
|
|
81
|
+
await expect(queue.push({ body: { content: 'x'.repeat(201 * 1024) } })).rejects.toThrow(new errors_1.PayloadTooBigError(`The maximum payload size is 200KB.`));
|
|
51
82
|
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
52
83
|
});
|
|
53
84
|
it('should throw RateLimitError', async () => {
|
|
54
85
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({}, 429);
|
|
55
86
|
const queue = getQueue('name', apiClientMock);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, payload, 'name');
|
|
87
|
+
await expect(queue.push(PAYLOAD)).rejects.toThrow(new errors_1.RateLimitError(`Too many requests.`));
|
|
88
|
+
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, [PAYLOAD], 'name');
|
|
59
89
|
});
|
|
60
90
|
it('should throw InvocationLimitReachedError', async () => {
|
|
61
91
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({}, 405);
|
|
62
92
|
const queue = getQueue('name', apiClientMock);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, payload, 'name');
|
|
93
|
+
await expect(queue.push(PAYLOAD)).rejects.toThrow(new errors_1.InvocationLimitReachedError(`The limit on cyclic invocation has been reached.`));
|
|
94
|
+
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, [PAYLOAD], 'name');
|
|
66
95
|
});
|
|
67
96
|
it('should throw PartialSuccessError when there are failed events', async () => {
|
|
68
97
|
const apiClientMock = (0, utils_1.getMockFetchMethod)({
|
|
@@ -80,26 +109,36 @@ describe('Queue methods', () => {
|
|
|
80
109
|
const queue = getQueue('name', apiClientMock);
|
|
81
110
|
const payload = [
|
|
82
111
|
{
|
|
83
|
-
|
|
112
|
+
body: {
|
|
113
|
+
content: 'payload-1'
|
|
114
|
+
}
|
|
84
115
|
},
|
|
85
116
|
{
|
|
86
|
-
|
|
117
|
+
body: {
|
|
118
|
+
content: 'payload-2'
|
|
119
|
+
}
|
|
87
120
|
},
|
|
88
121
|
{
|
|
89
|
-
|
|
122
|
+
body: {
|
|
123
|
+
content: 'payload-3'
|
|
124
|
+
}
|
|
90
125
|
}
|
|
91
126
|
];
|
|
92
|
-
await expect(queue.push(payload)).rejects.toThrow(new errors_1.PartialSuccessError(`Failed to process 2 event(s).`, [
|
|
127
|
+
await expect(queue.push(payload)).rejects.toThrow(new errors_1.PartialSuccessError(`Failed to process 2 event(s).`, { jobId: 'some-job-id' }, [
|
|
93
128
|
{
|
|
94
129
|
errorMessage: 'failed-1',
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
event: {
|
|
131
|
+
body: {
|
|
132
|
+
content: 'payload-1'
|
|
133
|
+
}
|
|
97
134
|
}
|
|
98
135
|
},
|
|
99
136
|
{
|
|
100
137
|
errorMessage: 'failed-3',
|
|
101
|
-
|
|
102
|
-
|
|
138
|
+
event: {
|
|
139
|
+
body: {
|
|
140
|
+
content: 'payload-3'
|
|
141
|
+
}
|
|
103
142
|
}
|
|
104
143
|
}
|
|
105
144
|
]));
|
|
@@ -112,16 +151,22 @@ describe('Queue methods', () => {
|
|
|
112
151
|
const queue = getQueue('name', apiClientMock);
|
|
113
152
|
const payload = [
|
|
114
153
|
{
|
|
115
|
-
|
|
154
|
+
body: {
|
|
155
|
+
content: 'payload-1'
|
|
156
|
+
}
|
|
116
157
|
},
|
|
117
158
|
{
|
|
118
|
-
|
|
159
|
+
body: {
|
|
160
|
+
content: 'payload-2'
|
|
161
|
+
}
|
|
119
162
|
},
|
|
120
163
|
{
|
|
121
|
-
|
|
164
|
+
body: {
|
|
165
|
+
content: 'payload-3'
|
|
166
|
+
}
|
|
122
167
|
}
|
|
123
168
|
];
|
|
124
|
-
await expect(queue.push(payload)).rejects.toThrow(new errors_1.PartialSuccessError(`Failed to create stats for job name#12345`, []));
|
|
169
|
+
await expect(queue.push(payload)).rejects.toThrow(new errors_1.PartialSuccessError(`Failed to create stats for job name#12345`, { jobId: 'some-job-id' }, []));
|
|
125
170
|
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, payload, 'name');
|
|
126
171
|
});
|
|
127
172
|
it('should throw PartialSuccessError when there are failed events and backend failed to create job stats', async () => {
|
|
@@ -137,17 +182,23 @@ describe('Queue methods', () => {
|
|
|
137
182
|
const queue = getQueue('name', apiClientMock);
|
|
138
183
|
const payload = [
|
|
139
184
|
{
|
|
140
|
-
|
|
185
|
+
body: {
|
|
186
|
+
content: 'payload-1'
|
|
187
|
+
}
|
|
141
188
|
},
|
|
142
189
|
{
|
|
143
|
-
|
|
190
|
+
body: {
|
|
191
|
+
content: 'payload-2'
|
|
192
|
+
}
|
|
144
193
|
}
|
|
145
194
|
];
|
|
146
|
-
await expect(queue.push(payload)).rejects.toThrow(new errors_1.PartialSuccessError(`Failed to process 1 event(s). Failed to create stats for job name#12345`, [
|
|
195
|
+
await expect(queue.push(payload)).rejects.toThrow(new errors_1.PartialSuccessError(`Failed to process 1 event(s). Failed to create stats for job name#12345`, { jobId: 'some-job-id' }, [
|
|
147
196
|
{
|
|
148
197
|
errorMessage: 'failed-1',
|
|
149
|
-
|
|
150
|
-
|
|
198
|
+
event: {
|
|
199
|
+
body: {
|
|
200
|
+
content: 'payload-1'
|
|
201
|
+
}
|
|
151
202
|
}
|
|
152
203
|
}
|
|
153
204
|
]));
|
|
@@ -160,20 +211,18 @@ describe('Queue methods', () => {
|
|
|
160
211
|
details: 'The request processing has failed because of an unknown error, exception or failure'
|
|
161
212
|
}, 500);
|
|
162
213
|
const queue = getQueue('name', apiClientMock);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, payload, 'name');
|
|
214
|
+
await expect(queue.push(PAYLOAD)).rejects.toThrow(new errors_1.InternalServerError(`500 Status Text: AWS SQS timed out`, 500, 'The request processing has failed because of an unknown error, exception or failure'));
|
|
215
|
+
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, [PAYLOAD], 'name');
|
|
166
216
|
});
|
|
167
217
|
it('should throw InternalServerError for error response without response body', async () => {
|
|
168
218
|
const apiClientMock = (0, utils_1.getApiClientMockWithoutResponseBody)(504, 'Gateway Timeout');
|
|
169
219
|
const queue = getQueue('name', apiClientMock);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, payload, 'name');
|
|
220
|
+
await expect(queue.push(PAYLOAD)).rejects.toThrow(new errors_1.InternalServerError(`504 Gateway Timeout`, 504));
|
|
221
|
+
(0, utils_1.verifyApiClientCalledPushPathWith)(apiClientMock, [PAYLOAD], 'name');
|
|
173
222
|
});
|
|
174
223
|
it('requests stargate if no api client is provided', async () => {
|
|
175
224
|
const queue = getQueue('queue');
|
|
176
|
-
await queue.push({ test: 'stargate' });
|
|
225
|
+
await queue.push({ body: { test: 'stargate' } });
|
|
177
226
|
expect(api_1.__requestAtlassianAsApp).toHaveBeenCalledTimes(1);
|
|
178
227
|
});
|
|
179
228
|
});
|
|
@@ -182,7 +231,7 @@ describe('Queue methods', () => {
|
|
|
182
231
|
const apiClientMock = (0, utils_1.getMockFetchMethod)();
|
|
183
232
|
const queue = getQueue('name', apiClientMock);
|
|
184
233
|
const jobProgress = queue.getJob('test-job-id');
|
|
185
|
-
expect(jobProgress).toEqual(new jobProgress_1.JobProgress('test-job-id', apiClientMock));
|
|
234
|
+
expect(jobProgress).toEqual(new jobProgress_1.JobProgress({ key: 'name' }, 'test-job-id', apiClientMock));
|
|
186
235
|
});
|
|
187
236
|
});
|
|
188
237
|
});
|
package/out/__test__/utils.js
CHANGED
|
@@ -33,7 +33,7 @@ exports.verifyApiClientCalledWith = verifyApiClientCalledWith;
|
|
|
33
33
|
const verifyApiClientCalledPushPathWith = (apiClientMock, payloads, name) => {
|
|
34
34
|
(0, exports.verifyApiClientCalledWith)(apiClientMock, '/webhook/queue/publish/{contextAri}/{environmentId}/{appId}/{appVersion}', {
|
|
35
35
|
queueName: name,
|
|
36
|
-
schema: 'ari:cloud:ecosystem::forge/app-event',
|
|
36
|
+
schema: 'ari:cloud:ecosystem::forge/app-event-2',
|
|
37
37
|
type: 'avi:forge:app:event',
|
|
38
38
|
payload: payloads
|
|
39
39
|
});
|
package/out/errors.d.ts
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
|
-
import { FailedEvent } from './types';
|
|
2
|
-
export declare class
|
|
1
|
+
import { FailedEvent, PushResult } from './types';
|
|
2
|
+
export declare class EventsError extends Error {
|
|
3
3
|
constructor(message: string);
|
|
4
4
|
}
|
|
5
|
-
export declare class
|
|
6
|
-
constructor(message: string);
|
|
5
|
+
export declare class InvalidPushSettingsError extends EventsError {
|
|
7
6
|
}
|
|
8
|
-
export declare class
|
|
9
|
-
constructor(message: string);
|
|
7
|
+
export declare class InvalidQueueNameError extends EventsError {
|
|
10
8
|
}
|
|
11
|
-
export declare class
|
|
12
|
-
constructor(message: string);
|
|
9
|
+
export declare class InvalidPayloadError extends EventsError {
|
|
13
10
|
}
|
|
14
|
-
export declare class
|
|
15
|
-
constructor(message: string);
|
|
11
|
+
export declare class TooManyEventsError extends EventsError {
|
|
16
12
|
}
|
|
17
|
-
export declare class
|
|
18
|
-
constructor(message: string);
|
|
13
|
+
export declare class PayloadTooBigError extends EventsError {
|
|
19
14
|
}
|
|
20
|
-
export declare class
|
|
21
|
-
|
|
15
|
+
export declare class NoEventsToPushError extends EventsError {
|
|
16
|
+
}
|
|
17
|
+
export declare class RateLimitError extends EventsError {
|
|
18
|
+
}
|
|
19
|
+
export declare class PartialSuccessError extends EventsError {
|
|
20
|
+
result: PushResult;
|
|
22
21
|
failedEvents: FailedEvent[];
|
|
22
|
+
constructor(message: string, result: PushResult, failedEvents: FailedEvent[]);
|
|
23
23
|
}
|
|
24
|
-
export declare class InternalServerError extends
|
|
24
|
+
export declare class InternalServerError extends EventsError {
|
|
25
25
|
constructor(message: string, errorCode?: number, details?: string);
|
|
26
26
|
errorCode?: number;
|
|
27
27
|
details?: string;
|
|
28
28
|
}
|
|
29
|
-
export declare class JobDoesNotExistError extends
|
|
30
|
-
constructor(message: string);
|
|
29
|
+
export declare class JobDoesNotExistError extends EventsError {
|
|
31
30
|
}
|
|
32
|
-
export declare class InvocationLimitReachedError extends
|
|
33
|
-
constructor(message: string);
|
|
31
|
+
export declare class InvocationLimitReachedError extends EventsError {
|
|
34
32
|
}
|
|
35
33
|
//# sourceMappingURL=errors.d.ts.map
|
package/out/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAElD,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAG5B;AAED,qBAAa,wBAAyB,SAAQ,WAAW;CAAG;AAE5D,qBAAa,qBAAsB,SAAQ,WAAW;CAAG;AAEzD,qBAAa,mBAAoB,SAAQ,WAAW;CAAG;AAEvD,qBAAa,kBAAmB,SAAQ,WAAW;CAAG;AAEtD,qBAAa,kBAAmB,SAAQ,WAAW;CAAG;AAEtD,qBAAa,mBAAoB,SAAQ,WAAW;CAAG;AAEvD,qBAAa,cAAe,SAAQ,WAAW;CAAG;AAElD,qBAAa,mBAAoB,SAAQ,WAAW;IAGzC,MAAM,EAAE,UAAU;IAClB,YAAY,EAAE,WAAW,EAAE;gBAFlC,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,UAAU,EAClB,YAAY,EAAE,WAAW,EAAE;CAIrC;AAED,qBAAa,mBAAoB,SAAQ,WAAW;gBACtC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAMjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,oBAAqB,SAAQ,WAAW;CAAG;AAExD,qBAAa,2BAA4B,SAAQ,WAAW;CAAG"}
|
package/out/errors.js
CHANGED
|
@@ -1,51 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InvocationLimitReachedError = exports.JobDoesNotExistError = exports.InternalServerError = exports.PartialSuccessError = exports.RateLimitError = exports.NoEventsToPushError = exports.PayloadTooBigError = exports.TooManyEventsError = exports.InvalidQueueNameError = exports.InvalidPushSettingsError = void 0;
|
|
4
|
-
class
|
|
3
|
+
exports.InvocationLimitReachedError = exports.JobDoesNotExistError = exports.InternalServerError = exports.PartialSuccessError = exports.RateLimitError = exports.NoEventsToPushError = exports.PayloadTooBigError = exports.TooManyEventsError = exports.InvalidPayloadError = exports.InvalidQueueNameError = exports.InvalidPushSettingsError = exports.EventsError = void 0;
|
|
4
|
+
class EventsError extends Error {
|
|
5
5
|
constructor(message) {
|
|
6
6
|
super(message);
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
+
exports.EventsError = EventsError;
|
|
10
|
+
class InvalidPushSettingsError extends EventsError {
|
|
11
|
+
}
|
|
9
12
|
exports.InvalidPushSettingsError = InvalidPushSettingsError;
|
|
10
|
-
class InvalidQueueNameError extends
|
|
11
|
-
constructor(message) {
|
|
12
|
-
super(message);
|
|
13
|
-
}
|
|
13
|
+
class InvalidQueueNameError extends EventsError {
|
|
14
14
|
}
|
|
15
15
|
exports.InvalidQueueNameError = InvalidQueueNameError;
|
|
16
|
-
class
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
class InvalidPayloadError extends EventsError {
|
|
17
|
+
}
|
|
18
|
+
exports.InvalidPayloadError = InvalidPayloadError;
|
|
19
|
+
class TooManyEventsError extends EventsError {
|
|
20
20
|
}
|
|
21
21
|
exports.TooManyEventsError = TooManyEventsError;
|
|
22
|
-
class PayloadTooBigError extends
|
|
23
|
-
constructor(message) {
|
|
24
|
-
super(message);
|
|
25
|
-
}
|
|
22
|
+
class PayloadTooBigError extends EventsError {
|
|
26
23
|
}
|
|
27
24
|
exports.PayloadTooBigError = PayloadTooBigError;
|
|
28
|
-
class NoEventsToPushError extends
|
|
29
|
-
constructor(message) {
|
|
30
|
-
super(message);
|
|
31
|
-
}
|
|
25
|
+
class NoEventsToPushError extends EventsError {
|
|
32
26
|
}
|
|
33
27
|
exports.NoEventsToPushError = NoEventsToPushError;
|
|
34
|
-
class RateLimitError extends
|
|
35
|
-
constructor(message) {
|
|
36
|
-
super(message);
|
|
37
|
-
}
|
|
28
|
+
class RateLimitError extends EventsError {
|
|
38
29
|
}
|
|
39
30
|
exports.RateLimitError = RateLimitError;
|
|
40
|
-
class PartialSuccessError extends
|
|
41
|
-
|
|
31
|
+
class PartialSuccessError extends EventsError {
|
|
32
|
+
result;
|
|
33
|
+
failedEvents;
|
|
34
|
+
constructor(message, result, failedEvents) {
|
|
42
35
|
super(message);
|
|
36
|
+
this.result = result;
|
|
43
37
|
this.failedEvents = failedEvents;
|
|
44
38
|
}
|
|
45
|
-
failedEvents;
|
|
46
39
|
}
|
|
47
40
|
exports.PartialSuccessError = PartialSuccessError;
|
|
48
|
-
class InternalServerError extends
|
|
41
|
+
class InternalServerError extends EventsError {
|
|
49
42
|
constructor(message, errorCode, details) {
|
|
50
43
|
super(message);
|
|
51
44
|
this.errorCode = errorCode;
|
|
@@ -55,15 +48,9 @@ class InternalServerError extends Error {
|
|
|
55
48
|
details;
|
|
56
49
|
}
|
|
57
50
|
exports.InternalServerError = InternalServerError;
|
|
58
|
-
class JobDoesNotExistError extends
|
|
59
|
-
constructor(message) {
|
|
60
|
-
super(message);
|
|
61
|
-
}
|
|
51
|
+
class JobDoesNotExistError extends EventsError {
|
|
62
52
|
}
|
|
63
53
|
exports.JobDoesNotExistError = JobDoesNotExistError;
|
|
64
|
-
class InvocationLimitReachedError extends
|
|
65
|
-
constructor(message) {
|
|
66
|
-
super(message);
|
|
67
|
-
}
|
|
54
|
+
class InvocationLimitReachedError extends EventsError {
|
|
68
55
|
}
|
|
69
56
|
exports.InvocationLimitReachedError = InvocationLimitReachedError;
|
package/out/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { Queue } from './queue';
|
|
2
2
|
export { InvalidQueueNameError, TooManyEventsError, PayloadTooBigError, NoEventsToPushError, RateLimitError, PartialSuccessError, InternalServerError, JobDoesNotExistError, InvalidPushSettingsError, InvocationLimitReachedError } from './errors';
|
|
3
|
-
export { JobProgress } from './jobProgress';
|
|
3
|
+
export { JobProgress, JobStats } from './jobProgress';
|
|
4
4
|
export { QueueResponse } from './queueResponse';
|
|
5
5
|
export { InvocationError } from './invocationError';
|
|
6
6
|
export { InvocationErrorCode } from './invocationErrorCode';
|
|
7
7
|
export { RetryOptions } from './retryOptions';
|
|
8
|
+
export { AsyncEvent, PushEvent, PushResult } from './types';
|
|
8
9
|
export { appEvents, AppEvent, AppEventPublishResult, AppEventPublishSuccess, AppEventPublishFailure, AppEventPublishError, AppEventErrorType } from './appEvents';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/out/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,2BAA2B,EAC5B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,2BAA2B,EAC5B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EACL,SAAS,EACT,QAAQ,EACR,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,aAAa,CAAC"}
|
package/out/jobProgress.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { FetchMethod
|
|
1
|
+
import { FetchMethod } from '@forge/api';
|
|
2
|
+
import { QueueParams } from './types';
|
|
3
|
+
export declare type JobStats = {
|
|
4
|
+
success: number;
|
|
5
|
+
inProgress: number;
|
|
6
|
+
failed: number;
|
|
7
|
+
};
|
|
2
8
|
export declare class JobProgress {
|
|
9
|
+
private readonly queueParams;
|
|
3
10
|
private readonly id;
|
|
4
11
|
private readonly apiClient;
|
|
5
|
-
constructor(id: string, apiClient?: FetchMethod);
|
|
6
|
-
getStats(): Promise<
|
|
7
|
-
cancel(): Promise<
|
|
12
|
+
constructor(queueParams: QueueParams, id: string, apiClient?: FetchMethod);
|
|
13
|
+
getStats(): Promise<JobStats>;
|
|
14
|
+
cancel(): Promise<void>;
|
|
8
15
|
}
|
|
9
16
|
//# sourceMappingURL=jobProgress.d.ts.map
|
package/out/jobProgress.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobProgress.d.ts","sourceRoot":"","sources":["../src/jobProgress.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,WAAW,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"jobProgress.d.ts","sourceRoot":"","sources":["../src/jobProgress.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,WAAW,EAAE,MAAM,YAAY,CAAC;AAQlE,OAAO,EAAqC,WAAW,EAAE,MAAM,SAAS,CAAC;AAEzE,oBAAY,QAAQ,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAFT,WAAW,EAAE,WAAW,EACxB,EAAE,EAAE,MAAM,EACV,SAAS,GAAE,WAAqC;IAG7D,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAe7B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAY9B"}
|