@squiz/job-runner-lib 2.10.0 → 2.10.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 +12 -0
- package/lib/domains/job/errors/ConflictError.d.ts +14 -0
- package/lib/domains/job/errors/Error.constants.d.ts +4 -0
- package/lib/domains/job/errors/index.d.ts +1 -0
- package/lib/domains/job/model/Base/Base.d.ts +5 -0
- package/lib/domains/job/model/JobExecution/JobExecution.d.ts +15 -3
- package/lib/domains/job/repository/JobExecutionRepository/JobExecutionRepository.d.ts +3 -6
- package/lib/domains/job/repository/JobExecutionRepository/utils/convertDynamoDbItemToJobExecution/convertDynamoDbItemToJobExecution.spec.d.ts +1 -0
- package/lib/index.js +11 -11
- package/lib/index.js.map +4 -4
- package/package.json +4 -3
- package/src/domains/job/errors/ConflictError.ts +19 -0
- package/src/domains/job/errors/Error.constants.ts +4 -0
- package/src/domains/job/errors/index.ts +1 -0
- package/src/domains/job/model/Base/Base.ts +6 -0
- package/src/domains/job/model/JobExecution/JobExecution.ts +17 -3
- package/src/domains/job/model/JobExecution/mocks/JobExecution.mock.ts +1 -0
- package/src/domains/job/model/JobExecution/mocks/JobExecutionDto.mock.ts +3 -0
- package/src/domains/job/model/JobExecution/mocks/JobExecutionRecord.mock.ts +1 -0
- package/src/domains/job/repository/JobExecutionRepository/JobExecutionRepository.spec.ts +77 -16
- package/src/domains/job/repository/JobExecutionRepository/JobExecutionRepository.ts +19 -15
- package/src/domains/job/repository/JobExecutionRepository/utils/convertDynamoDbItemToJobExecution/convertDynamoDbItemToJobExecution.spec.ts +48 -0
- package/src/domains/job/repository/JobExecutionRepository/utils/convertDynamoDbItemToJobExecution/convertDynamoDbItemToJobExecution.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squiz/job-runner-lib",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"private": false,
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@faker-js/faker": "8.0.2",
|
|
21
21
|
"@smithy/util-stream": "4.5.17",
|
|
22
|
-
"@squiz/virus-scanner-lib": "1.68.
|
|
22
|
+
"@squiz/virus-scanner-lib": "1.68.1",
|
|
23
23
|
"@types/cli-color": "2.0.6",
|
|
24
24
|
"@types/fs-extra": "11.0.1",
|
|
25
25
|
"@types/jest": "28.1.8",
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
"@aws-sdk/client-s3": "3.1003.0",
|
|
35
35
|
"@aws-sdk/client-sqs": "3.1003.0",
|
|
36
36
|
"@aws-sdk/s3-request-presigner": "3.1003.0",
|
|
37
|
+
"@aws-sdk/util-dynamodb": "3.996.2",
|
|
37
38
|
"@squiz/dx-common-lib": "1.66.1",
|
|
38
39
|
"@squiz/dx-json-schema-lib": "1.86.0",
|
|
39
40
|
"@squiz/dx-logger-lib": "1.64.0",
|
|
40
41
|
"@squiz/optimization-logger": "1.11.2",
|
|
41
|
-
"axios": "1.
|
|
42
|
+
"axios": "1.19.0",
|
|
42
43
|
"cli-color": "2.0.4",
|
|
43
44
|
"fs-extra": "11.1.0",
|
|
44
45
|
"immer": "10.1.3",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Squiz Australia Pty Ltd. All Rights Reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ERRORS } from './Error.constants';
|
|
7
|
+
import { ErrorWithHttpStatusCode } from './ErrorWithHttpStatusCode';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Conflict error (409).
|
|
11
|
+
* @export
|
|
12
|
+
* @class ConflictError
|
|
13
|
+
* @extends {ErrorWithHttpStatusCode}
|
|
14
|
+
*/
|
|
15
|
+
export class ConflictError extends ErrorWithHttpStatusCode {
|
|
16
|
+
constructor(details: string, message = ERRORS.conflict.name) {
|
|
17
|
+
super(ERRORS.conflict.code, message, details);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -48,3 +48,9 @@ export type ListParameters<TFilters> = {
|
|
|
48
48
|
consistentRead?: boolean;
|
|
49
49
|
limit?: number;
|
|
50
50
|
};
|
|
51
|
+
|
|
52
|
+
export type UpdateOptions = {
|
|
53
|
+
conditionExpression?: string;
|
|
54
|
+
expressionAttributeValues?: Record<string, unknown>;
|
|
55
|
+
expressionAttributeNames?: Record<string, string>;
|
|
56
|
+
};
|
|
@@ -62,6 +62,13 @@ export interface JobExecution {
|
|
|
62
62
|
*/
|
|
63
63
|
ecsTaskId: string | null;
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* The last version of the ECS task event which was handled.
|
|
67
|
+
* @type {(number | null)}
|
|
68
|
+
* @memberof JobExecution
|
|
69
|
+
*/
|
|
70
|
+
lastEcsTaskEventVersion: number | null;
|
|
71
|
+
|
|
65
72
|
/**
|
|
66
73
|
* The time the job run was started.
|
|
67
74
|
* @type {(string | null)}
|
|
@@ -184,21 +191,28 @@ export interface JobExecutionRecord {
|
|
|
184
191
|
|
|
185
192
|
/**
|
|
186
193
|
* The version of the job script to execute.
|
|
187
|
-
* @type
|
|
194
|
+
* @type {(string)}
|
|
188
195
|
* @memberof JobExecutionRecord
|
|
189
196
|
*/
|
|
190
197
|
version: string;
|
|
191
198
|
|
|
192
199
|
/**
|
|
193
200
|
* The ECS task ID of the job run.
|
|
194
|
-
* @type
|
|
201
|
+
* @type {(string | null)}
|
|
195
202
|
* @memberof JobExecutionRecord
|
|
196
203
|
*/
|
|
197
204
|
ecsTaskId: string | null;
|
|
198
205
|
|
|
206
|
+
/**
|
|
207
|
+
* The last version of the ECS task event which was handled.
|
|
208
|
+
* @type {(number | null)}
|
|
209
|
+
* @memberof JobExecution
|
|
210
|
+
*/
|
|
211
|
+
lastEcsTaskEventVersion: number | null;
|
|
212
|
+
|
|
199
213
|
/**
|
|
200
214
|
* The time the job run was started.
|
|
201
|
-
* @type
|
|
215
|
+
* @type {(string | null)}
|
|
202
216
|
* @memberof JobExecutionRecord
|
|
203
217
|
*/
|
|
204
218
|
timeStarted: string | null;
|
|
@@ -24,6 +24,7 @@ export const createMockJobExecution = (partialRecord: Partial<JobExecution> = {}
|
|
|
24
24
|
return {
|
|
25
25
|
context: faker.string.uuid(),
|
|
26
26
|
ecsTaskId: null,
|
|
27
|
+
lastEcsTaskEventVersion: null,
|
|
27
28
|
id: `${timeQueued}-${faker.string.uuid()}`,
|
|
28
29
|
jobName,
|
|
29
30
|
status: JOB_EXECUTION_STATUS.queued,
|
|
@@ -34,6 +34,9 @@ export const createMockJobExecutionDto = (partialRecord: Partial<JobExecutionRec
|
|
|
34
34
|
S: partialRecord.context ?? faker.string.uuid(),
|
|
35
35
|
},
|
|
36
36
|
ecsTaskId: partialRecord.ecsTaskId ? { S: partialRecord.ecsTaskId } : { NULL: true },
|
|
37
|
+
lastEcsTaskEventVersion: partialRecord.lastEcsTaskEventVersion
|
|
38
|
+
? { N: partialRecord.lastEcsTaskEventVersion.toString() }
|
|
39
|
+
: { NULL: true },
|
|
37
40
|
gsi1pk: {
|
|
38
41
|
S: `${ENTITY_TYPES.jobExecution}~${status}`,
|
|
39
42
|
},
|
|
@@ -28,6 +28,7 @@ export const createMockJobExecutionRecord = (partialRecord: Partial<JobExecution
|
|
|
28
28
|
return {
|
|
29
29
|
context: faker.string.uuid(),
|
|
30
30
|
ecsTaskId: null,
|
|
31
|
+
lastEcsTaskEventVersion: null,
|
|
31
32
|
gsi1pk: `${ENTITY_TYPES.jobExecution}~${status}`,
|
|
32
33
|
id: id,
|
|
33
34
|
jobName: jobName,
|
|
@@ -207,6 +207,7 @@ describe('JobExecutionRepository', (): void => {
|
|
|
207
207
|
it(`should return job execution by timeQueued and id and handle any null values`, async (): Promise<void> => {
|
|
208
208
|
const mockJobExecutionRecord = createMockJobExecutionDto({
|
|
209
209
|
ecsTaskId: 'ecs-task-id',
|
|
210
|
+
lastEcsTaskEventVersion: 1,
|
|
210
211
|
timeFinished: new Date().toISOString(),
|
|
211
212
|
timeStarted: new Date().toISOString(),
|
|
212
213
|
version: '1.0.0',
|
|
@@ -225,6 +226,7 @@ describe('JobExecutionRepository', (): void => {
|
|
|
225
226
|
expect(response).toStrictEqual({
|
|
226
227
|
context: mockJobExecutionRecord.context.S as string,
|
|
227
228
|
ecsTaskId: mockJobExecutionRecord.ecsTaskId.S as string,
|
|
229
|
+
lastEcsTaskEventVersion: parseInt(mockJobExecutionRecord.lastEcsTaskEventVersion.N as string),
|
|
228
230
|
id: mockJobExecutionRecord.id.S as string,
|
|
229
231
|
jobName: mockJobExecutionRecord.jobName.S as string,
|
|
230
232
|
status: mockJobExecutionRecord.status.S as string,
|
|
@@ -452,6 +454,7 @@ describe('JobExecutionRepository', (): void => {
|
|
|
452
454
|
const updatedAttributes: UpdateJobExecutionParameters = {
|
|
453
455
|
input: faker.lorem.words(),
|
|
454
456
|
ecsTaskId: faker.string.uuid(),
|
|
457
|
+
lastEcsTaskEventVersion: faker.number.int(),
|
|
455
458
|
version: faker.system.semver(),
|
|
456
459
|
timeStarted: faker.date.recent().toISOString(),
|
|
457
460
|
timeFinished: null,
|
|
@@ -471,20 +474,22 @@ describe('JobExecutionRepository', (): void => {
|
|
|
471
474
|
expect(result).toEqual(updatedJobExecution);
|
|
472
475
|
expect(dynamoClient).toHaveReceivedCommandWith(UpdateItemCommand, {
|
|
473
476
|
ExpressionAttributeNames: {
|
|
474
|
-
'#
|
|
475
|
-
'#
|
|
476
|
-
'#
|
|
477
|
-
'#
|
|
478
|
-
'#
|
|
479
|
-
'#
|
|
477
|
+
'#updateExpr0': 'input',
|
|
478
|
+
'#updateExpr1': 'ecsTaskId',
|
|
479
|
+
'#updateExpr2': 'lastEcsTaskEventVersion',
|
|
480
|
+
'#updateExpr3': 'version',
|
|
481
|
+
'#updateExpr4': 'timeStarted',
|
|
482
|
+
'#updateExpr5': 'timeFinished',
|
|
483
|
+
'#updateExpr6': 'context',
|
|
480
484
|
},
|
|
481
485
|
ExpressionAttributeValues: {
|
|
482
|
-
':
|
|
483
|
-
':
|
|
484
|
-
':
|
|
485
|
-
':
|
|
486
|
-
':
|
|
487
|
-
':
|
|
486
|
+
':updateExpr0': { S: updatedAttributes.input },
|
|
487
|
+
':updateExpr1': { S: updatedAttributes.ecsTaskId! },
|
|
488
|
+
':updateExpr2': { N: `${updatedAttributes.lastEcsTaskEventVersion}` },
|
|
489
|
+
':updateExpr3': { S: updatedAttributes.version! },
|
|
490
|
+
':updateExpr4': { S: updatedAttributes.timeStarted! },
|
|
491
|
+
':updateExpr5': { NULL: true },
|
|
492
|
+
':updateExpr6': { S: updatedAttributes.context! },
|
|
488
493
|
},
|
|
489
494
|
Key: {
|
|
490
495
|
pk: { S: `jobExecution~${tenant}` },
|
|
@@ -493,7 +498,7 @@ describe('JobExecutionRepository', (): void => {
|
|
|
493
498
|
ReturnValues: 'ALL_NEW',
|
|
494
499
|
TableName: tableName,
|
|
495
500
|
UpdateExpression:
|
|
496
|
-
'SET #
|
|
501
|
+
'SET #updateExpr0 = :updateExpr0, #updateExpr1 = :updateExpr1, #updateExpr2 = :updateExpr2, #updateExpr3 = :updateExpr3, #updateExpr4 = :updateExpr4, #updateExpr5 = :updateExpr5, #updateExpr6 = :updateExpr6',
|
|
497
502
|
});
|
|
498
503
|
});
|
|
499
504
|
|
|
@@ -517,12 +522,68 @@ describe('JobExecutionRepository', (): void => {
|
|
|
517
522
|
ExpressionAttributeNames: {
|
|
518
523
|
'#gsi1pk': 'gsi1pk',
|
|
519
524
|
'#lsi1sk': 'lsi1sk',
|
|
520
|
-
'#
|
|
525
|
+
'#updateExpr0': 'status',
|
|
521
526
|
},
|
|
522
527
|
ExpressionAttributeValues: {
|
|
523
528
|
':gsi1pk': { S: `jobExecution~${updatedJobExecution.status}` },
|
|
524
529
|
':lsi1sk': { S: `${updatedJobExecution.status}~${updatedJobExecution.jobName}` },
|
|
525
|
-
':
|
|
530
|
+
':updateExpr0': { S: updatedJobExecution.status },
|
|
531
|
+
},
|
|
532
|
+
Key: {
|
|
533
|
+
pk: { S: `jobExecution~${tenant}` },
|
|
534
|
+
sk: { S: `${jobExecution.jobName}~${jobExecution.id}` },
|
|
535
|
+
},
|
|
536
|
+
ReturnValues: 'ALL_NEW',
|
|
537
|
+
TableName: tableName,
|
|
538
|
+
UpdateExpression: 'SET #updateExpr0 = :updateExpr0, #lsi1sk = :lsi1sk, #gsi1pk = :gsi1pk',
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('Marshalls expression attribute values provided for conditional updates', async () => {
|
|
543
|
+
const jobExecution = createMockJobExecution({ status: JOB_EXECUTION_STATUS.queued });
|
|
544
|
+
const updatedJobExecution = createMockJobExecution({
|
|
545
|
+
...jobExecution,
|
|
546
|
+
status: JOB_EXECUTION_STATUS.running,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
dynamoClient.on(UpdateItemCommand).resolvesOnce({
|
|
550
|
+
Attributes: createMockJobExecutionDto(updatedJobExecution),
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
const result = await jobExecutionRepository.update(
|
|
554
|
+
jobExecution,
|
|
555
|
+
{
|
|
556
|
+
status: updatedJobExecution.status,
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
conditionExpression: '#ecsTaskId = :ecsTaskId AND #status = :status',
|
|
560
|
+
expressionAttributeNames: {
|
|
561
|
+
'#status': 'status',
|
|
562
|
+
'#ecsTaskId': 'ecsTaskId',
|
|
563
|
+
},
|
|
564
|
+
expressionAttributeValues: {
|
|
565
|
+
ecsTaskId: null,
|
|
566
|
+
status: JOB_EXECUTION_STATUS.queued,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
expect(result).toEqual(updatedJobExecution);
|
|
572
|
+
expect(dynamoClient).toHaveReceivedCommandWith(UpdateItemCommand, {
|
|
573
|
+
ConditionExpression: '#ecsTaskId = :ecsTaskId AND #status = :status',
|
|
574
|
+
ExpressionAttributeNames: {
|
|
575
|
+
'#ecsTaskId': 'ecsTaskId',
|
|
576
|
+
'#gsi1pk': 'gsi1pk',
|
|
577
|
+
'#lsi1sk': 'lsi1sk',
|
|
578
|
+
'#status': 'status',
|
|
579
|
+
'#updateExpr0': 'status',
|
|
580
|
+
},
|
|
581
|
+
ExpressionAttributeValues: {
|
|
582
|
+
':gsi1pk': { S: 'jobExecution~running' },
|
|
583
|
+
':lsi1sk': { S: `running~${jobExecution.jobName}` },
|
|
584
|
+
':updateExpr0': { S: 'running' },
|
|
585
|
+
ecsTaskId: { NULL: true },
|
|
586
|
+
status: { S: 'queued' },
|
|
526
587
|
},
|
|
527
588
|
Key: {
|
|
528
589
|
pk: { S: `jobExecution~${tenant}` },
|
|
@@ -530,7 +591,7 @@ describe('JobExecutionRepository', (): void => {
|
|
|
530
591
|
},
|
|
531
592
|
ReturnValues: 'ALL_NEW',
|
|
532
593
|
TableName: tableName,
|
|
533
|
-
UpdateExpression: 'SET #
|
|
594
|
+
UpdateExpression: 'SET #updateExpr0 = :updateExpr0, #lsi1sk = :lsi1sk, #gsi1pk = :gsi1pk',
|
|
534
595
|
});
|
|
535
596
|
});
|
|
536
597
|
});
|
|
@@ -35,6 +35,8 @@ import { provideTransient } from '../../transientProvider';
|
|
|
35
35
|
import { Repository } from '../AbstractRepository';
|
|
36
36
|
import { InjectTokens } from '../../constants/InjectTokens';
|
|
37
37
|
import { convertDynamoDbItemToJobExecution } from './utils';
|
|
38
|
+
import { UpdateOptions } from '../../model/Base';
|
|
39
|
+
import { marshall } from '@aws-sdk/util-dynamodb';
|
|
38
40
|
|
|
39
41
|
@provideTransient(JobExecutionRepository)
|
|
40
42
|
export class JobExecutionRepository extends Repository<JobExecution, JobExecutionDto, JobExecutionSk> {
|
|
@@ -52,6 +54,7 @@ export class JobExecutionRepository extends Repository<JobExecution, JobExecutio
|
|
|
52
54
|
const newJobExecution: JobExecution = {
|
|
53
55
|
type: `${ENTITY_TYPES.jobExecution}-${createParams.jobName}`,
|
|
54
56
|
ecsTaskId: null,
|
|
57
|
+
lastEcsTaskEventVersion: null,
|
|
55
58
|
timeStarted: null,
|
|
56
59
|
timeFinished: null,
|
|
57
60
|
input: null,
|
|
@@ -240,11 +243,7 @@ export class JobExecutionRepository extends Repository<JobExecution, JobExecutio
|
|
|
240
243
|
async update(
|
|
241
244
|
sk: JobExecutionSk,
|
|
242
245
|
input: UpdateJobExecutionParameters,
|
|
243
|
-
options?:
|
|
244
|
-
ConditionExpression?: string;
|
|
245
|
-
ExpressionAttributeValues?: Record<string, AttributeValue>;
|
|
246
|
-
ExpressionAttributeNames?: Record<string, string>;
|
|
247
|
-
},
|
|
246
|
+
options?: UpdateOptions,
|
|
248
247
|
): Promise<JobExecution> {
|
|
249
248
|
// Initialise update command
|
|
250
249
|
const updateParams: UpdateItemCommandInput = {
|
|
@@ -259,11 +258,13 @@ export class JobExecutionRepository extends Repository<JobExecution, JobExecutio
|
|
|
259
258
|
const expressionAttributeNames: Record<string, string> = {};
|
|
260
259
|
const expressionAttributeValues: Record<string, AttributeValue> = {};
|
|
261
260
|
const updateExpressionPairs: Array<string> = [];
|
|
261
|
+
let index = 0;
|
|
262
262
|
|
|
263
263
|
// To avoid conflict with reserved keywords, refer to attributes with an additional '#' prefix
|
|
264
|
-
for (const
|
|
265
|
-
const
|
|
266
|
-
|
|
264
|
+
for (const property of Object.keys(input)) {
|
|
265
|
+
const key = `updateExpr${index++}`;
|
|
266
|
+
const val = input[property as keyof UpdateJobExecutionParameters];
|
|
267
|
+
expressionAttributeNames[`#${key}`] = property;
|
|
267
268
|
|
|
268
269
|
if (val === null) {
|
|
269
270
|
expressionAttributeValues[`:${key}`] = { NULL: true };
|
|
@@ -275,7 +276,7 @@ export class JobExecutionRepository extends Repository<JobExecution, JobExecutio
|
|
|
275
276
|
|
|
276
277
|
updateExpressionPairs.push(`#${key} = :${key}`);
|
|
277
278
|
|
|
278
|
-
if (
|
|
279
|
+
if (property === 'status') {
|
|
279
280
|
expressionAttributeNames[`#lsi1sk`] = 'lsi1sk';
|
|
280
281
|
expressionAttributeValues[`:lsi1sk`] = { S: `${val || ''}~${sk.jobName}` };
|
|
281
282
|
expressionAttributeNames[`#gsi1pk`] = 'gsi1pk';
|
|
@@ -290,23 +291,23 @@ export class JobExecutionRepository extends Repository<JobExecution, JobExecutio
|
|
|
290
291
|
updateParams.UpdateExpression = `SET ${updateExpressionPairs.join(', ')}`;
|
|
291
292
|
|
|
292
293
|
// Add condition expression if provided
|
|
293
|
-
if (options?.
|
|
294
|
-
updateParams.ConditionExpression = options.
|
|
294
|
+
if (options?.conditionExpression) {
|
|
295
|
+
updateParams.ConditionExpression = options.conditionExpression;
|
|
295
296
|
}
|
|
296
297
|
|
|
297
|
-
if (options?.
|
|
298
|
+
if (options?.expressionAttributeValues) {
|
|
298
299
|
// Merge existing expression attribute values with the ones from options
|
|
299
300
|
updateParams.ExpressionAttributeValues = {
|
|
300
301
|
...updateParams.ExpressionAttributeValues,
|
|
301
|
-
...options.
|
|
302
|
+
...marshall(options.expressionAttributeValues),
|
|
302
303
|
};
|
|
303
304
|
}
|
|
304
305
|
|
|
305
306
|
// Add expression attribute names if provided
|
|
306
|
-
if (options?.
|
|
307
|
+
if (options?.expressionAttributeNames) {
|
|
307
308
|
updateParams.ExpressionAttributeNames = {
|
|
308
309
|
...updateParams.ExpressionAttributeNames,
|
|
309
|
-
...options.
|
|
310
|
+
...options.expressionAttributeNames,
|
|
310
311
|
};
|
|
311
312
|
}
|
|
312
313
|
|
|
@@ -325,6 +326,9 @@ export class JobExecutionRepository extends Repository<JobExecution, JobExecutio
|
|
|
325
326
|
return {
|
|
326
327
|
context: { S: item.context },
|
|
327
328
|
ecsTaskId: item.ecsTaskId ? { S: item.ecsTaskId } : { NULL: true },
|
|
329
|
+
lastEcsTaskEventVersion: item.lastEcsTaskEventVersion
|
|
330
|
+
? { N: item.lastEcsTaskEventVersion.toString() }
|
|
331
|
+
: { NULL: true },
|
|
328
332
|
gsi1pk: { S: `${ENTITY_TYPES.jobExecution}~${item.status}` },
|
|
329
333
|
id: { S: item.id },
|
|
330
334
|
jobName: { S: item.jobName },
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import { JOB_EXECUTION_STATUS } from '../../../../constants/JobExecutionStatus.constants';
|
|
3
|
+
import { JobExecutionDto } from '../../../../model/JobExecution';
|
|
4
|
+
import { convertDynamoDbItemToJobExecution } from './convertDynamoDbItemToJobExecution';
|
|
5
|
+
import { ENTITY_TYPES } from '../../../../constants/Repository.constants';
|
|
6
|
+
|
|
7
|
+
describe('convertDynamoDbItemToJobExecution', () => {
|
|
8
|
+
it('Converts minimal execution Dynamo record to model object', () => {
|
|
9
|
+
const tenant = faker.string.uuid();
|
|
10
|
+
const dto = {
|
|
11
|
+
context: { S: faker.lorem.words() },
|
|
12
|
+
ecsTaskId: { NULL: true },
|
|
13
|
+
lastEcsTaskEventVersion: undefined,
|
|
14
|
+
id: { S: faker.string.uuid() },
|
|
15
|
+
status: { S: JOB_EXECUTION_STATUS.queued },
|
|
16
|
+
timeFinished: { NULL: true },
|
|
17
|
+
timeQueued: { S: faker.date.recent().toISOString() },
|
|
18
|
+
timeStarted: { S: faker.date.recent().toISOString() },
|
|
19
|
+
pk: { S: `${ENTITY_TYPES.jobExecution}~${tenant}` },
|
|
20
|
+
jobName: { S: faker.lorem.words() },
|
|
21
|
+
version: { S: faker.system.semver() },
|
|
22
|
+
input: undefined,
|
|
23
|
+
retryCount: undefined,
|
|
24
|
+
lastFailureReason: undefined,
|
|
25
|
+
retryAfter: undefined,
|
|
26
|
+
} as Partial<JobExecutionDto> as JobExecutionDto;
|
|
27
|
+
|
|
28
|
+
const execution = convertDynamoDbItemToJobExecution(dto);
|
|
29
|
+
|
|
30
|
+
expect(execution).toEqual({
|
|
31
|
+
context: dto.context.S,
|
|
32
|
+
ecsTaskId: null,
|
|
33
|
+
id: dto.id.S,
|
|
34
|
+
input: null,
|
|
35
|
+
jobName: dto.jobName.S,
|
|
36
|
+
lastEcsTaskEventVersion: null,
|
|
37
|
+
lastFailureReason: null,
|
|
38
|
+
retryAfter: null,
|
|
39
|
+
retryCount: 0,
|
|
40
|
+
status: dto.status.S,
|
|
41
|
+
timeFinished: null,
|
|
42
|
+
timeQueued: dto.timeQueued.S,
|
|
43
|
+
timeStarted: dto.timeStarted.S,
|
|
44
|
+
type: `${ENTITY_TYPES.jobExecution}-${dto.jobName.S}`,
|
|
45
|
+
version: dto.version.S,
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -6,6 +6,7 @@ export const convertDynamoDbItemToJobExecution = (dbItem: JobExecutionDto): JobE
|
|
|
6
6
|
return {
|
|
7
7
|
context: dbItem.context.S as string,
|
|
8
8
|
ecsTaskId: dbItem.ecsTaskId.S ? (dbItem.ecsTaskId.S as string) : null,
|
|
9
|
+
lastEcsTaskEventVersion: dbItem.lastEcsTaskEventVersion?.N ? parseInt(dbItem.lastEcsTaskEventVersion.N, 10) : null,
|
|
9
10
|
id: dbItem.id.S as string,
|
|
10
11
|
jobName: dbItem.jobName.S as string,
|
|
11
12
|
status: dbItem.status.S as JOB_EXECUTION_STATUS,
|