@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.
- package/CHANGELOG.md +154 -1
- package/README.md +1 -1
- package/out/__test__/jobProgress.test.d.ts +2 -0
- package/out/__test__/jobProgress.test.d.ts.map +1 -0
- package/out/__test__/jobProgress.test.js +110 -0
- package/out/__test__/queue.test.d.ts +2 -0
- package/out/__test__/queue.test.d.ts.map +1 -0
- package/out/__test__/queue.test.js +179 -0
- package/out/__test__/queueResponse.test.d.ts +2 -0
- package/out/__test__/queueResponse.test.d.ts.map +1 -0
- package/out/__test__/queueResponse.test.js +14 -0
- package/out/__test__/utils.d.ts +7 -0
- package/out/__test__/utils.d.ts.map +1 -0
- package/out/__test__/utils.js +37 -0
- package/out/errors.d.ts +9 -0
- package/out/errors.d.ts.map +1 -1
- package/out/errors.js +19 -1
- package/out/index.d.ts +3 -0
- package/out/index.d.ts.map +1 -1
- package/out/index.js +15 -0
- package/out/jobProgress.d.ts +9 -0
- package/out/jobProgress.d.ts.map +1 -0
- package/out/jobProgress.js +36 -0
- package/out/queries.d.ts +4 -2
- package/out/queries.d.ts.map +1 -1
- package/out/queries.js +13 -8
- package/out/queue.d.ts +4 -4
- package/out/queue.d.ts.map +1 -1
- package/out/queue.js +25 -25
- package/out/queueResponse.d.ts +5 -0
- package/out/queueResponse.d.ts.map +1 -0
- package/out/queueResponse.js +12 -0
- package/out/text.d.ts +14 -0
- package/out/text.d.ts.map +1 -0
- package/out/text.js +16 -0
- package/out/types.d.ts +11 -4
- package/out/types.d.ts.map +1 -1
- package/out/validators.d.ts +10 -4
- package/out/validators.d.ts.map +1 -1
- package/out/validators.js +69 -18
- package/package.json +7 -2
- package/src/__test__/jobProgress.test.ts +149 -0
- package/src/__test__/queue.test.ts +236 -0
- package/src/__test__/queueResponse.test.ts +14 -0
- package/src/__test__/utils.ts +47 -0
- package/src/errors.ts +18 -0
- package/src/index.ts +14 -0
- package/src/jobProgress.ts +48 -0
- package/src/queries.ts +15 -9
- package/src/queue.ts +28 -29
- package/src/queueResponse.ts +7 -0
- package/src/text.ts +15 -0
- package/src/types.ts +12 -4
- package/src/validators.ts +98 -31
- package/tsconfig.json +12 -9
- package/tsconfig.tsbuildinfo +107 -20
- package/out/__test__/index.test.d.ts +0 -2
- package/out/__test__/index.test.d.ts.map +0 -1
- package/out/__test__/index.test.js +0 -126
- package/src/__test__/index.test.ts +0 -168
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InternalServerError,
|
|
3
|
+
InvalidQueueNameError,
|
|
4
|
+
NoEventsToPushError,
|
|
5
|
+
PartialSuccessError,
|
|
6
|
+
PayloadTooBigError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
TooManyEventsError,
|
|
9
|
+
InvalidPushSettingsError,
|
|
10
|
+
InvocationLimitReachedError
|
|
11
|
+
} from '../errors';
|
|
12
|
+
import { getApiClientMock, getApiClientMockWithoutResponseBody, verifyApiClientCalledPushPathWith } from './utils';
|
|
13
|
+
import { Queue } from '../queue';
|
|
14
|
+
import { JobProgress } from '../jobProgress';
|
|
15
|
+
|
|
16
|
+
const getQueue = (apiClientMock: any, queueName: string) => new Queue({ key: queueName }, apiClientMock);
|
|
17
|
+
|
|
18
|
+
describe('Queue methods', () => {
|
|
19
|
+
describe('constructor', () => {
|
|
20
|
+
it('should throw InvalidQueueNameError', async () => {
|
|
21
|
+
const apiClientMock = getApiClientMock();
|
|
22
|
+
expect(() => getQueue(apiClientMock, 'invalid name')).toThrowError(
|
|
23
|
+
new InvalidQueueNameError('Queue names can only contain alphanumeric characters, dashes and underscores.')
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('push', () => {
|
|
29
|
+
it('should call the queue/publish endpoint', async () => {
|
|
30
|
+
const apiClientMock = getApiClientMock();
|
|
31
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
32
|
+
const payload = {
|
|
33
|
+
page: 1
|
|
34
|
+
};
|
|
35
|
+
await queue.push([payload]);
|
|
36
|
+
verifyApiClientCalledPushPathWith(apiClientMock, [payload], 'name');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should throw InvalidPushSettingsError for delay', async () => {
|
|
40
|
+
const apiClientMock = getApiClientMock();
|
|
41
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
42
|
+
await expect(queue.push(1, { delayInSeconds: 901 })).rejects.toThrow(
|
|
43
|
+
new InvalidPushSettingsError(`The delayInSeconds setting must be between 0 and 900.`)
|
|
44
|
+
);
|
|
45
|
+
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should throw NoEventsToPushError', async () => {
|
|
49
|
+
const apiClientMock = getApiClientMock();
|
|
50
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
51
|
+
await expect(queue.push([])).rejects.toThrow(new NoEventsToPushError(`No events pushed.`));
|
|
52
|
+
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should throw TooManyEventsError', async () => {
|
|
56
|
+
const apiClientMock = getApiClientMock();
|
|
57
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
58
|
+
await expect(queue.push([...Array(51).keys()])).rejects.toThrow(
|
|
59
|
+
new TooManyEventsError(`This push contains more than the 50 events allowed.`)
|
|
60
|
+
);
|
|
61
|
+
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should throw PayloadTooBigError', async () => {
|
|
65
|
+
const apiClientMock = getApiClientMock();
|
|
66
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
67
|
+
await expect(queue.push('x'.repeat(201 * 1024))).rejects.toThrow(
|
|
68
|
+
new PayloadTooBigError(`The maximum payload size is 200KB.`)
|
|
69
|
+
);
|
|
70
|
+
expect(apiClientMock).toHaveBeenCalledTimes(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should throw RateLimitError', async () => {
|
|
74
|
+
const apiClientMock = getApiClientMock({}, 429);
|
|
75
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
76
|
+
const payload = [...Array(10).keys()];
|
|
77
|
+
await expect(queue.push(payload)).rejects.toThrow(new RateLimitError(`Too many requests.`));
|
|
78
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should throw InvocationLimitReachedError', async () => {
|
|
82
|
+
const apiClientMock = getApiClientMock({}, 405);
|
|
83
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
84
|
+
const payload = [...Array(5).keys()];
|
|
85
|
+
await expect(queue.push(payload)).rejects.toThrow(
|
|
86
|
+
new InvocationLimitReachedError(`The limit on cyclic invocation has been reached.`)
|
|
87
|
+
);
|
|
88
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should throw PartialSuccessError when there are failed events', async () => {
|
|
92
|
+
const apiClientMock = getApiClientMock(
|
|
93
|
+
{
|
|
94
|
+
failedEvents: [
|
|
95
|
+
{
|
|
96
|
+
index: '0',
|
|
97
|
+
errorMessage: 'failed-1'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
index: 2,
|
|
101
|
+
errorMessage: 'failed-3'
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
202
|
|
106
|
+
);
|
|
107
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
108
|
+
const payload = [
|
|
109
|
+
{
|
|
110
|
+
content: 'payload-1'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
content: 'payload-2'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
content: 'payload-3'
|
|
117
|
+
}
|
|
118
|
+
];
|
|
119
|
+
await expect(queue.push(payload)).rejects.toThrow(
|
|
120
|
+
new PartialSuccessError(`Failed to process 2 event(s).`, [
|
|
121
|
+
{
|
|
122
|
+
errorMessage: 'failed-1',
|
|
123
|
+
payload: {
|
|
124
|
+
content: 'payload-1'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
errorMessage: 'failed-3',
|
|
129
|
+
payload: {
|
|
130
|
+
content: 'payload-3'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
])
|
|
134
|
+
);
|
|
135
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should throw PartialSuccessError when backend failed to create job stats', async () => {
|
|
139
|
+
const apiClientMock = getApiClientMock(
|
|
140
|
+
{
|
|
141
|
+
errorMessage: 'Failed to create stats for job name#12345'
|
|
142
|
+
},
|
|
143
|
+
202
|
|
144
|
+
);
|
|
145
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
146
|
+
const payload = [
|
|
147
|
+
{
|
|
148
|
+
content: 'payload-1'
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
content: 'payload-2'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
content: 'payload-3'
|
|
155
|
+
}
|
|
156
|
+
];
|
|
157
|
+
await expect(queue.push(payload)).rejects.toThrow(
|
|
158
|
+
new PartialSuccessError(`Failed to create stats for job name#12345`, [])
|
|
159
|
+
);
|
|
160
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should throw PartialSuccessError when there are failed events and backend failed to create job stats', async () => {
|
|
164
|
+
const apiClientMock = getApiClientMock(
|
|
165
|
+
{
|
|
166
|
+
errorMessage: 'Failed to create stats for job name#12345',
|
|
167
|
+
failedEvents: [
|
|
168
|
+
{
|
|
169
|
+
index: '0',
|
|
170
|
+
errorMessage: 'failed-1'
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
202
|
|
175
|
+
);
|
|
176
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
177
|
+
const payload = [
|
|
178
|
+
{
|
|
179
|
+
content: 'payload-1'
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
content: 'payload-2'
|
|
183
|
+
}
|
|
184
|
+
];
|
|
185
|
+
await expect(queue.push(payload)).rejects.toThrow(
|
|
186
|
+
new PartialSuccessError(`Failed to process 1 event(s). Failed to create stats for job name#12345`, [
|
|
187
|
+
{
|
|
188
|
+
errorMessage: 'failed-1',
|
|
189
|
+
payload: {
|
|
190
|
+
content: 'payload-1'
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
])
|
|
194
|
+
);
|
|
195
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should throw InternalServerError', async () => {
|
|
199
|
+
const apiClientMock = getApiClientMock(
|
|
200
|
+
{
|
|
201
|
+
message: 'AWS SQS timed out',
|
|
202
|
+
code: 500,
|
|
203
|
+
details: 'The request processing has failed because of an unknown error, exception or failure'
|
|
204
|
+
},
|
|
205
|
+
500
|
|
206
|
+
);
|
|
207
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
208
|
+
const payload = [...Array(9).keys()];
|
|
209
|
+
await expect(queue.push(payload)).rejects.toThrow(
|
|
210
|
+
new InternalServerError(
|
|
211
|
+
`500 Status Text: AWS SQS timed out`,
|
|
212
|
+
500,
|
|
213
|
+
'The request processing has failed because of an unknown error, exception or failure'
|
|
214
|
+
)
|
|
215
|
+
);
|
|
216
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should throw InternalServerError for error response without response body', async () => {
|
|
220
|
+
const apiClientMock = getApiClientMockWithoutResponseBody(504, 'Gateway Timeout');
|
|
221
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
222
|
+
const payload = [1];
|
|
223
|
+
await expect(queue.push(1)).rejects.toThrow(new InternalServerError(`504 Gateway Timeout`, 504));
|
|
224
|
+
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('getJob', () => {
|
|
229
|
+
it('should create a JobProgress by jobId', async () => {
|
|
230
|
+
const apiClientMock = getApiClientMock();
|
|
231
|
+
const queue = getQueue(apiClientMock, 'name');
|
|
232
|
+
const jobProgress = queue.getJob('test-job-id');
|
|
233
|
+
expect(jobProgress).toEqual(new JobProgress('test-job-id', apiClientMock));
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { QueueResponse } from '../queueResponse';
|
|
2
|
+
|
|
3
|
+
describe('QueueResponse', () => {
|
|
4
|
+
it('should contain property retry=false by default', async () => {
|
|
5
|
+
const res = new QueueResponse();
|
|
6
|
+
expect(JSON.stringify(res)).toEqual('{"_retry":false}');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('doRetry() should change property retry=true', async () => {
|
|
10
|
+
const res = new QueueResponse();
|
|
11
|
+
res.retry();
|
|
12
|
+
expect(JSON.stringify(res)).toEqual('{"_retry":true}');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Payload } from '../types';
|
|
2
|
+
|
|
3
|
+
export const getApiClientMock = (response?: any, statusCode = 201) => {
|
|
4
|
+
return jest.fn().mockReturnValue({
|
|
5
|
+
created: statusCode === 201,
|
|
6
|
+
status: statusCode,
|
|
7
|
+
statusText: 'Status Text',
|
|
8
|
+
json: jest.fn().mockResolvedValue(response)
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getApiClientMockWithoutResponseBody = (statusCode: number, statusText: string) => {
|
|
13
|
+
return jest.fn().mockReturnValue({
|
|
14
|
+
ok: statusCode === 200,
|
|
15
|
+
status: statusCode,
|
|
16
|
+
statusText: statusText
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const verifyApiClientCalledWith = (apiClientMock: jest.Mock, path: string, expectedBody: any) => {
|
|
21
|
+
expect(apiClientMock).toHaveBeenCalledWith(
|
|
22
|
+
path,
|
|
23
|
+
expect.objectContaining({
|
|
24
|
+
method: 'POST',
|
|
25
|
+
body: expect.any(String),
|
|
26
|
+
headers: {
|
|
27
|
+
'content-type': 'application/json'
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const [, { body }] = apiClientMock.mock.calls[0];
|
|
33
|
+
expect(JSON.parse(body)).toEqual(expect.objectContaining(expectedBody));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const verifyApiClientCalledPushPathWith = (
|
|
37
|
+
apiClientMock: jest.Mock,
|
|
38
|
+
payloads: Payload | Payload[],
|
|
39
|
+
name: string
|
|
40
|
+
) => {
|
|
41
|
+
verifyApiClientCalledWith(apiClientMock, '/webhook/queue/publish/{cloudId}/{environmentId}/{appId}/{appVersion}', {
|
|
42
|
+
queueName: name,
|
|
43
|
+
schema: 'ari:cloud:ecosystem::forge/app-event',
|
|
44
|
+
type: 'avi:forge:app:event',
|
|
45
|
+
payload: payloads
|
|
46
|
+
});
|
|
47
|
+
};
|
package/src/errors.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { FailedEvent } from './types';
|
|
2
2
|
|
|
3
|
+
export class InvalidPushSettingsError extends Error {
|
|
4
|
+
constructor(message: string) {
|
|
5
|
+
super(message);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
export class InvalidQueueNameError extends Error {
|
|
4
10
|
constructor(message: string) {
|
|
5
11
|
super(message);
|
|
@@ -49,3 +55,15 @@ export class InternalServerError extends Error {
|
|
|
49
55
|
errorCode?: number;
|
|
50
56
|
details?: string;
|
|
51
57
|
}
|
|
58
|
+
|
|
59
|
+
export class JobDoesNotExistError extends Error {
|
|
60
|
+
constructor(message: string) {
|
|
61
|
+
super(message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class InvocationLimitReachedError extends Error {
|
|
66
|
+
constructor(message: string) {
|
|
67
|
+
super(message);
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,15 @@
|
|
|
1
1
|
export { Queue } from './queue';
|
|
2
|
+
export {
|
|
3
|
+
InvalidQueueNameError,
|
|
4
|
+
TooManyEventsError,
|
|
5
|
+
PayloadTooBigError,
|
|
6
|
+
NoEventsToPushError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
PartialSuccessError,
|
|
9
|
+
InternalServerError,
|
|
10
|
+
JobDoesNotExistError,
|
|
11
|
+
InvalidPushSettingsError,
|
|
12
|
+
InvocationLimitReachedError
|
|
13
|
+
} from './errors';
|
|
14
|
+
export { JobProgress } from './jobProgress';
|
|
15
|
+
export { QueueResponse } from './queueResponse';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CANCEL_JOB_PATH, GET_STATS_PATH, post } from './queries';
|
|
2
|
+
import {
|
|
3
|
+
validateCancelJobAPIResponse,
|
|
4
|
+
validateCancelJobRequest,
|
|
5
|
+
validateGetStatsAPIResponse,
|
|
6
|
+
validateGetStatsPayload
|
|
7
|
+
} from './validators';
|
|
8
|
+
import { APIResponse, CancelJobRequest, FetchMethod, GetStatsRequest } from './types';
|
|
9
|
+
|
|
10
|
+
export class JobProgress {
|
|
11
|
+
private readonly apiClient: FetchMethod;
|
|
12
|
+
private id: string;
|
|
13
|
+
|
|
14
|
+
constructor(id: string, apiClient?: FetchMethod) {
|
|
15
|
+
this.id = id;
|
|
16
|
+
this.apiClient = apiClient || (global as any).api.asApp().__requestAtlassian;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getStats(): Promise<APIResponse> {
|
|
20
|
+
const [queueName, jobId] = this.id.split('#');
|
|
21
|
+
const getStatsRequest: GetStatsRequest = {
|
|
22
|
+
queueName: queueName,
|
|
23
|
+
jobId: jobId,
|
|
24
|
+
time: new Date().toISOString()
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
validateGetStatsPayload(getStatsRequest);
|
|
28
|
+
|
|
29
|
+
const response = await post(GET_STATS_PATH, getStatsRequest, this.apiClient);
|
|
30
|
+
await validateGetStatsAPIResponse(response, getStatsRequest);
|
|
31
|
+
return response;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async cancel(): Promise<APIResponse> {
|
|
35
|
+
const [queueName, jobId] = this.id.split('#');
|
|
36
|
+
const cancelJobRequest: CancelJobRequest = {
|
|
37
|
+
queueName: queueName,
|
|
38
|
+
jobId: jobId,
|
|
39
|
+
time: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
validateCancelJobRequest(cancelJobRequest);
|
|
43
|
+
|
|
44
|
+
const response = await post(CANCEL_JOB_PATH, cancelJobRequest, this.apiClient);
|
|
45
|
+
await validateCancelJobAPIResponse(response, cancelJobRequest);
|
|
46
|
+
return response;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/queries.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export const getPushBody = (params: PushBodyParams): APIRequest => ({
|
|
4
|
-
queueName: params.queueName,
|
|
5
|
-
type: params.type,
|
|
6
|
-
schema: params.schema,
|
|
7
|
-
payload: params.payload,
|
|
8
|
-
time: new Date().toISOString()
|
|
9
|
-
});
|
|
1
|
+
import { APIRequest, APIResponse, FetchMethod } from './types';
|
|
10
2
|
|
|
11
3
|
export const PUSH_PATH = '/webhook/queue/publish/{cloudId}/{environmentId}/{appId}/{appVersion}';
|
|
4
|
+
export const GET_STATS_PATH = '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}';
|
|
5
|
+
export const CANCEL_JOB_PATH = '/webhook/queue/cancel/{cloudId}/{environmentId}/{appId}/{appVersion}';
|
|
6
|
+
|
|
7
|
+
export const post = async (endpoint: string, body: APIRequest, apiClient: FetchMethod): Promise<APIResponse> => {
|
|
8
|
+
const request = {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
body: JSON.stringify(body),
|
|
11
|
+
headers: {
|
|
12
|
+
'content-type': 'application/json'
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return await apiClient(endpoint, request);
|
|
17
|
+
};
|
package/src/queue.ts
CHANGED
|
@@ -1,47 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
await validateAPIResponse(response, body);
|
|
7
|
-
return response;
|
|
8
|
-
}
|
|
1
|
+
import { PUSH_PATH, post } from './queries';
|
|
2
|
+
import { validatePushAPIResponse, validatePushPayloads, validateQueueKey, validatePushSettings } from './validators';
|
|
3
|
+
import { FetchMethod, Payload, QueueParams, PushSettings, PushRequest } from './types';
|
|
4
|
+
import uuid from 'uuid/v4';
|
|
5
|
+
import { JobProgress } from './jobProgress';
|
|
9
6
|
|
|
10
7
|
export class Queue {
|
|
11
8
|
private readonly apiClient: FetchMethod;
|
|
12
9
|
private queueParams: QueueParams;
|
|
13
10
|
|
|
14
11
|
constructor(queueParams: QueueParams, apiClient?: FetchMethod) {
|
|
15
|
-
|
|
12
|
+
validateQueueKey(queueParams.key);
|
|
16
13
|
this.queueParams = queueParams;
|
|
17
14
|
this.apiClient = apiClient || (global as any).api.asApp().__requestAtlassian;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
async push(payloads: Payload | Payload[]): Promise<
|
|
21
|
-
|
|
17
|
+
async push(payloads: Payload | Payload[], pushSettings?: PushSettings): Promise<string> {
|
|
18
|
+
validatePushPayloads(payloads);
|
|
19
|
+
const queueName = this.queueParams.key;
|
|
20
|
+
const jobId = uuid();
|
|
22
21
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const pushRequest: PushRequest = {
|
|
23
|
+
queueName: queueName,
|
|
24
|
+
jobId: jobId,
|
|
25
|
+
type: 'avi:forge:app:event',
|
|
26
26
|
schema: 'ari:cloud:ecosystem::forge/app-event',
|
|
27
|
-
|
|
27
|
+
payload: Array.isArray(payloads) ? payloads : [payloads],
|
|
28
|
+
time: new Date().toISOString()
|
|
28
29
|
};
|
|
29
|
-
const requestBody = getPushBody(queryParams);
|
|
30
|
-
return this.query(PUSH_PATH, requestBody);
|
|
31
|
-
}
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
if (pushSettings) {
|
|
32
|
+
validatePushSettings(pushSettings);
|
|
33
|
+
if (pushSettings.delayInSeconds) {
|
|
34
|
+
pushRequest.delayInSeconds = pushSettings.delayInSeconds;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const response = await post(PUSH_PATH, pushRequest, this.apiClient);
|
|
39
|
+
await validatePushAPIResponse(response, pushRequest);
|
|
40
|
+
return `${queueName}#${jobId}`;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
method: 'POST',
|
|
41
|
-
body: JSON.stringify(requestBody),
|
|
42
|
-
headers: {
|
|
43
|
-
'content-type': 'application/json'
|
|
44
|
-
}
|
|
45
|
-
};
|
|
43
|
+
getJob(jobId: string): JobProgress {
|
|
44
|
+
return new JobProgress(jobId, this.apiClient);
|
|
46
45
|
}
|
|
47
46
|
}
|
package/src/text.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const Text = {
|
|
2
|
+
error: {
|
|
3
|
+
invalidQueueName: `Queue names can only contain alphanumeric characters, dashes and underscores.`,
|
|
4
|
+
invalidDelayInSecondsSetting: `The delayInSeconds setting must be between 0 and 900.`,
|
|
5
|
+
maxEventsAllowed: (maxEventsCount: number): string =>
|
|
6
|
+
`This push contains more than the ${maxEventsCount} events allowed.`,
|
|
7
|
+
maxPayloadAllowed: (maxPayloadSize: number): string => `The maximum payload size is ${maxPayloadSize}KB.`,
|
|
8
|
+
noEventsPushed: `No events pushed.`,
|
|
9
|
+
rateLimitError: `Too many requests.`,
|
|
10
|
+
invocationLimitReachedError: `The limit on cyclic invocation has been reached.`,
|
|
11
|
+
jobIdEmpty: `jobId cannot be empty.`,
|
|
12
|
+
jobDoesNotExit: (jobId: string, queueName: string): string =>
|
|
13
|
+
`The job ${jobId} was not found for the queue ${queueName}.`
|
|
14
|
+
}
|
|
15
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -4,18 +4,23 @@ export type APIResponse = Pick<Response, 'json' | 'text' | 'arrayBuffer' | 'ok'
|
|
|
4
4
|
export type FetchMethod = (url: string, init: RequestInit) => Promise<APIResponse>;
|
|
5
5
|
export type Payload = string | number | boolean | { [key: string]: Payload };
|
|
6
6
|
|
|
7
|
+
export interface PushSettings {
|
|
8
|
+
delayInSeconds: number;
|
|
9
|
+
}
|
|
7
10
|
export interface QueueParams {
|
|
8
|
-
|
|
11
|
+
key: string;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
|
-
export interface
|
|
14
|
+
export interface PushRequest extends APIRequest {
|
|
12
15
|
payload: Payload[];
|
|
13
|
-
queueName: string;
|
|
14
16
|
schema: string;
|
|
15
17
|
type: string;
|
|
18
|
+
delayInSeconds?: number;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
export interface APIRequest
|
|
21
|
+
export interface APIRequest {
|
|
22
|
+
queueName: string;
|
|
23
|
+
jobId: string;
|
|
19
24
|
time: string;
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -23,3 +28,6 @@ export interface FailedEvent {
|
|
|
23
28
|
errorMessage: string;
|
|
24
29
|
payload: Payload;
|
|
25
30
|
}
|
|
31
|
+
|
|
32
|
+
export type GetStatsRequest = APIRequest;
|
|
33
|
+
export type CancelJobRequest = APIRequest;
|