/*! * @license * Copyright Squiz Australia Pty Ltd. All Rights Reserved. */ import { AttributeValue, DynamoDBClient, UpdateItemCommand, UpdateItemCommandInput } from '@aws-sdk/client-dynamodb'; import { inject } from 'inversify'; import { defaultAutoRetry } from '../../../manifest/utils/defaultAutoRetry'; import { InjectTokens } from '../../constants/InjectTokens'; import { ENTITY_TYPES } from '../../constants/Repository.constants'; import { getRuntimeImageOrDefault } from '../../constants/RuntimeImage.constants'; import { CreateJobManifestRequest, JobManifest, JobManifestDto, JobManifestSk, UpdateJobManifestRequest, } from '../../manifest'; import { parsePartitionKey } from '../../ParsePartitionKey'; import { provideTransient } from '../../transientProvider'; import { Repository } from '../AbstractRepository'; @provideTransient(JobManifestRepository) export class JobManifestRepository extends Repository { public constructor( @inject(DynamoDBClient) dynamoDbClient: DynamoDBClient, @inject(InjectTokens.DynamoTableName) tableName: string, @inject(InjectTokens.Tenant) tenant: string, ) { super(dynamoDbClient, tableName, `${ENTITY_TYPES.manifest}~${tenant}`); } public async create(input: CreateJobManifestRequest): Promise { const newJobManifest: JobManifest = { ...input, autoRetry: defaultAutoRetry(input.autoRetry), type: ENTITY_TYPES.manifest, }; return super.create(newJobManifest); } async update(sk: JobManifestSk, input: UpdateJobManifestRequest): Promise { // Initialise update command const updateParams: UpdateItemCommandInput = { Key: { pk: { S: this.pk }, sk: { S: this.buildSk(sk) }, }, ReturnValues: 'ALL_NEW', TableName: this.tableName, }; const expressionAttributeNames: Record = {}; const expressionAttributeValues: Record = {}; // To avoid conflict with reserved keywords, refer to attributes with an additional '#' prefix if (input.concurrency) { expressionAttributeNames['#concurrency'] = 'concurrency'; expressionAttributeValues[':concurrency'] = { N: input.concurrency.toString() }; } if (input.entryFile) { expressionAttributeNames['#entryFile'] = 'entryFile'; expressionAttributeValues[':entryFile'] = { S: input.entryFile }; } if (input.timeoutSeconds) { expressionAttributeNames['#timeoutSeconds'] = 'timeoutSeconds'; expressionAttributeValues[':timeoutSeconds'] = { N: input.timeoutSeconds.toString() }; } if (input.image) { expressionAttributeNames['#image'] = 'image'; expressionAttributeValues[':image'] = { S: input.image }; } if (input.autoRetry !== undefined) { expressionAttributeNames['#autoRetry'] = 'autoRetry'; expressionAttributeValues[':autoRetry'] = { BOOL: input.autoRetry }; } const updateExpressionPairs = Object.keys(input).map((key) => `#${key} = :${key}`); const updateExpression = `SET ${updateExpressionPairs.join(', ')}`; updateParams.ExpressionAttributeNames = expressionAttributeNames; updateParams.ExpressionAttributeValues = expressionAttributeValues; updateParams.UpdateExpression = updateExpression; const updateItemCommand = new UpdateItemCommand(updateParams); const updatedJob = await this.dynamoDbClient.send(updateItemCommand); return this.convertToItem(updatedJob.Attributes as JobManifestDto); } convertToDynamoDbItem(item: JobManifest): JobManifestDto { return { concurrency: { N: item.concurrency.toString() }, entryFile: { S: item.entryFile }, entryFunction: { S: JSON.stringify(item.entryFunction) }, name: { S: item.name }, version: { S: item.version }, pk: { S: this.pk }, sk: { S: `${item.name}~${item.version}` }, timeoutSeconds: { N: item.timeoutSeconds.toString() }, image: { S: item.image }, autoRetry: { BOOL: item.autoRetry }, }; } convertToItem(dbItem: JobManifestDto): JobManifest { const { pk, name, version, concurrency, entryFile, entryFunction, timeoutSeconds } = dbItem; const image = (dbItem as Partial).image?.S; const autoRetry = (dbItem as Partial).autoRetry?.BOOL; return { concurrency: parseInt(concurrency.N as string), entryFile: entryFile.S as string, entryFunction: JSON.parse(entryFunction.S as string), name: name.S as string, version: version.S as string, timeoutSeconds: parseInt(timeoutSeconds.N as string), image: getRuntimeImageOrDefault(image), autoRetry: defaultAutoRetry(autoRetry), type: parsePartitionKey(pk.S as string).entityType, }; } protected buildSk(sk: JobManifestSk): string { return `${sk.name}~${sk.version}`; } }