UNPKG

@mbc-cqrs-serverless/task

Version:
239 lines 10.5 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var TaskService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskService = void 0; const core_1 = require("@mbc-cqrs-serverless/core"); const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); const ulid_1 = require("ulid"); const task_entity_1 = require("./entity/task.entity"); const task_list_entity_1 = require("./entity/task-list.entity"); const enums_1 = require("./enums"); const status_enum_1 = require("./enums/status.enum"); const event_1 = require("./event"); let TaskService = TaskService_1 = class TaskService { constructor(dynamoDbService, snsService, config) { this.dynamoDbService = dynamoDbService; this.snsService = snsService; this.config = config; this.logger = new common_1.Logger(TaskService_1.name); this.tableName = dynamoDbService.getTableName('tasks'); this.alarmTopicArn = this.config.get('SNS_ALARM_TOPIC_ARN'); } async createTask(dto, options) { const sourceIp = options.invokeContext?.event?.requestContext?.http?.sourceIp; const userContext = (0, core_1.getUserContext)(options.invokeContext); const taskCode = (0, ulid_1.ulid)(); const pk = `TASK${core_1.KEY_SEPARATOR}${dto.tenantCode}`; const sk = `${dto.taskType}${core_1.KEY_SEPARATOR}${taskCode}`; const item = { id: `${pk}${core_1.KEY_SEPARATOR}${sk}`, pk, sk, version: 0, code: taskCode, type: dto.taskType, name: dto.name || dto.taskType, tenantCode: dto.tenantCode, status: status_enum_1.TaskStatusEnum.CREATED, input: dto.input, requestId: options.invokeContext?.context?.awsRequestId, createdAt: new Date(), updatedAt: new Date(), createdBy: userContext.userId, updatedBy: userContext.userId, createdIp: sourceIp, updatedIp: sourceIp, }; await this.dynamoDbService.putItem(this.tableName, item); return new task_entity_1.TaskEntity(item); } async createStepFunctionTask(dto, options) { const sourceIp = options.invokeContext?.event?.requestContext?.http?.sourceIp; const userContext = (0, core_1.getUserContext)(options.invokeContext); const taskCode = (0, ulid_1.ulid)(); const pk = `${enums_1.TaskTypesEnum.SFN_TASK}${core_1.KEY_SEPARATOR}${dto.tenantCode}`; const sk = `${dto.taskType}${core_1.KEY_SEPARATOR}${taskCode}`; const item = { id: `${pk}${core_1.KEY_SEPARATOR}${sk}`, pk, sk, version: 0, code: taskCode, type: dto.taskType, name: dto.name || dto.taskType, tenantCode: dto.tenantCode, status: status_enum_1.TaskStatusEnum.CREATED, input: dto.input, requestId: options.invokeContext?.context?.awsRequestId, createdAt: new Date(), updatedAt: new Date(), createdBy: userContext.userId, updatedBy: userContext.userId, createdIp: sourceIp, updatedIp: sourceIp, }; await this.dynamoDbService.putItem(this.tableName, item); return new task_entity_1.TaskEntity(item); } async createSubTask(event) { const subTasks = []; await Promise.all(event.taskEvent.taskEntity.input.map((input, index) => { const pk = event.taskEvent.taskKey.pk; const sk = `${event.taskEvent.taskKey.sk}${core_1.KEY_SEPARATOR}${index}`; const taskCode = (0, ulid_1.ulid)(); const item = new task_entity_1.TaskEntity({ id: `${pk}${core_1.KEY_SEPARATOR}${sk}`, pk, sk, version: 0, code: taskCode, type: event.taskEvent.taskEntity.type, name: event.taskEvent.taskEntity.name, tenantCode: event.taskEvent.taskEntity.tenantCode, status: status_enum_1.TaskStatusEnum.CREATED, input, requestId: event.taskEvent.taskEntity.requestId, createdAt: new Date(), updatedAt: new Date(), createdBy: event.taskEvent.taskEntity.createdBy, updatedBy: event.taskEvent.taskEntity.updatedBy, createdIp: event.taskEvent.taskEntity.createdIp, updatedIp: event.taskEvent.taskEntity.updatedIp, }); subTasks.push(item); return this.dynamoDbService.putItem(this.tableName, item); })); return subTasks; } async getAllSubTask(subTask) { const parentKey = subTask.sk .split(core_1.KEY_SEPARATOR) .slice(0, -1) .join(core_1.KEY_SEPARATOR); this.logger.debug('TaskService getAllSubTask parentKey:', parentKey); const allItems = []; let lastSk = undefined; do { const res = await this.dynamoDbService.listItemsByPk(this.tableName, subTask.pk, { skExpression: 'begins_with(sk, :typeCode)', skAttributeValues: { ':typeCode': `${parentKey}${core_1.KEY_SEPARATOR}`, }, }, lastSk); allItems.push(...(res?.items || []).map((item) => new task_entity_1.TaskEntity(item))); lastSk = res.lastSk; this.logger.debug('TaskService getAllSubTask lastSk:', lastSk); } while (lastSk); return allItems; } async updateStepFunctionTask(key, attributes, status, notifyId) { await this.dynamoDbService.updateItem(this.tableName, key, { set: { attributes, status }, }); // notification via SNS await this.snsService.publish({ action: 'task-status', ...key, table: this.tableName, id: notifyId || `${key.pk}#${key.sk}`, tenantCode: key.pk.substring(key.pk.indexOf('#') + 1), content: { attributes, status }, }); } async getTask(key) { const item = await this.dynamoDbService.getItem(this.tableName, key); return new task_entity_1.TaskEntity(item); } async updateStatus(key, status, attributes, notifyId) { await this.dynamoDbService.updateItem(this.tableName, key, { set: { status, attributes }, }); // notification via SNS await this.snsService.publish({ action: 'task-status', ...key, table: this.tableName, id: notifyId || `${key.pk}#${key.sk}`, tenantCode: key.pk.substring(key.pk.indexOf('#') + 1), content: { status, attributes }, }); } async updateSubTaskStatus(key, status, attributes, notifyId) { await this.dynamoDbService.updateItem(this.tableName, key, { set: { status, attributes }, }); // notification via SNS -> insert to queue await this.snsService.publish({ action: 'sub-task-status', ...key, table: this.tableName, id: notifyId || `${key.pk}#${key.sk}`, tenantCode: key.pk.substring(key.pk.indexOf('#') + 1), content: { status, attributes }, }); } async listItemsByPk(tenantCode, type, options) { if (!['TASK', 'SFN_TASK'].includes(type)) { throw new common_1.NotFoundException(`The type of task, must be either "TASK" or "SFN_TASK"`); } const pk = `${type}${core_1.KEY_SEPARATOR}${tenantCode}`; const { lastSk, items } = await this.dynamoDbService.listItemsByPk(this.tableName, pk, options?.sk, options?.startFromSk, options?.limit, options?.order); return new task_list_entity_1.TaskListEntity({ lastSk, items: items.map((item) => new task_entity_1.TaskEntity(item)), }); } async publishAlarm(event, errorDetails) { this.logger.debug('event', event); const taskKey = event instanceof event_1.TaskQueueEvent ? event.taskEvent.taskKey : event.taskKey; const tenantCode = taskKey.pk.substring(taskKey.pk.indexOf(core_1.KEY_SEPARATOR) + 1); const alarm = { action: 'sfn-alarm', id: `${taskKey.pk}#${taskKey.sk}`, table: this.tableName, pk: taskKey.pk, sk: taskKey.sk, tenantCode, content: { errorMessage: errorDetails, }, }; this.logger.error('alarm:::', alarm); await this.snsService.publish(alarm, this.alarmTopicArn); } async formatTaskStatus(tasks) { const result = { subTaskCount: tasks.length, subTaskSucceedCount: this.countTaskStatus(tasks, status_enum_1.TaskStatusEnum.COMPLETED), subTaskFailedCount: this.countTaskStatus(tasks, status_enum_1.TaskStatusEnum.FAILED), subTaskRunningCount: this.countTaskStatus(tasks, status_enum_1.TaskStatusEnum.PROCESSING), subTasks: tasks.map((task) => ({ pk: task.pk, sk: task.sk, status: task.status, })), }; return result; } countTaskStatus(tasks, status) { return tasks.filter((task) => task.status === status).length; } }; exports.TaskService = TaskService; exports.TaskService = TaskService = TaskService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [core_1.DynamoDbService, core_1.SnsService, config_1.ConfigService]) ], TaskService); //# sourceMappingURL=task.service.js.map