@mbc-cqrs-serverless/core
Version:
CQRS and event base core
217 lines • 9.81 kB
JavaScript
"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 __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var CommandEventHandler_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandEventHandler = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const sfn_name_enum_1 = require("../command-events/sfn-name.enum");
const data_store_1 = require("../data-store");
const key_1 = require("../helpers/key");
const queue_1 = require("../queue");
const step_function_service_1 = require("../step-func/step-function.service");
const command_module_definition_1 = require("./command.module-definition");
const command_service_1 = require("./command.service");
const data_service_1 = require("./data.service");
const status_enum_1 = require("./enums/status.enum");
const history_service_1 = require("./history.service");
let CommandEventHandler = CommandEventHandler_1 = class CommandEventHandler {
constructor(options, commandService, dataService, historyService, s3Service, snsService, config, sfnService) {
this.options = options;
this.commandService = commandService;
this.dataService = dataService;
this.historyService = historyService;
this.s3Service = s3Service;
this.snsService = snsService;
this.config = config;
this.sfnService = sfnService;
this.logger = new common_1.Logger(`${CommandEventHandler_1.name}:${this.options.tableName}`);
this.alarmTopicArn = this.config.get('SNS_ALARM_TOPIC_ARN');
}
async execute(event) {
this.logger.debug('executing::', event);
await this.commandService.updateStatus(event.commandKey, (0, status_enum_1.getCommandStatus)(event.stepStateName, status_enum_1.CommandStatus.STATUS_STARTED), event.commandRecord.requestId);
try {
const ret = await this.handleStepState(event);
await this.commandService.updateStatus(event.commandKey, (0, status_enum_1.getCommandStatus)(event.stepStateName, status_enum_1.CommandStatus.STATUS_FINISHED), event.commandRecord.requestId);
return ret;
}
catch (error) {
await this.commandService.updateStatus(event.commandKey, (0, status_enum_1.getCommandStatus)(event.stepStateName, status_enum_1.CommandStatus.STATUS_FAILED), event.commandRecord.requestId);
await this.publishAlarm(event, error.stack);
throw error;
}
}
async handleStepState(event) {
switch (event.stepStateName) {
case sfn_name_enum_1.DataSyncCommandSfnName.CHECK_VERSION:
return await this.checkVersion(event);
case sfn_name_enum_1.DataSyncCommandSfnName.WAIT_PREV_COMMAND:
return await this.waitConfirmToken(event);
case sfn_name_enum_1.DataSyncCommandSfnName.SET_TTL_COMMAND:
return await this.setTtlCommand(event);
case sfn_name_enum_1.DataSyncCommandSfnName.HISTORY_COPY:
return await this.historyCopy(event);
case sfn_name_enum_1.DataSyncCommandSfnName.TRANSFORM_DATA:
return await this.transformData(event);
case sfn_name_enum_1.DataSyncCommandSfnName.SYNC_DATA:
return await this.syncData(event);
case sfn_name_enum_1.DataSyncCommandSfnName.FINISH:
return await this.checkNextToken(event);
default:
throw new Error('step function state not found!');
}
}
async waitConfirmToken(event) {
this.logger.debug('waitConfirmToken::', event);
await this.commandService.updateTaskToken(event.commandKey, event.taskToken);
return {
result: {
token: event.taskToken,
},
};
}
async checkVersion(event) {
this.logger.debug('Checking version::', event.commandRecord);
const sk = (0, key_1.removeSortKeyVersion)(event.commandRecord.sk);
const data = await this.dataService.getItem({
pk: event.commandRecord.pk,
sk,
});
this.logger.debug('Checking version for data::', data);
const commandVersion = event.commandRecord.version;
const nextVersion = 1 + (data?.version || 0);
const oldCommand = await this.commandService.getItem({
pk: event.commandRecord.pk,
sk: (0, key_1.addSortKeyVersion)(sk, commandVersion - 1),
});
if (nextVersion === commandVersion) {
return {
result: 0,
};
}
if (nextVersion < commandVersion) {
if (!oldCommand) {
return {
result: 0,
};
}
// wait for previous version is stable
return {
result: 1,
};
}
const errorDetails = {
result: -1,
error: 'version is not match',
cause: 'next version must be ' + nextVersion + ' but got ' + commandVersion,
};
await this.publishAlarm(event, errorDetails);
return errorDetails;
}
async setTtlCommand(event) {
this.logger.debug('setTtlCommand:: ', event.commandRecord);
await this.commandService.updateTtl({
pk: event.commandRecord.pk,
sk: event.commandRecord.sk,
});
return {
result: 'ok',
};
}
async historyCopy(event) {
this.logger.debug('historyCopy:: ', event.commandRecord);
await this.historyService.publish({
pk: event.commandRecord.pk,
sk: (0, key_1.removeSortKeyVersion)(event.commandRecord.sk),
});
return {
result: 'ok',
};
}
async transformData(event) {
this.logger.debug('transformData:: ', event.commandRecord);
return this.commandService.dataSyncHandlers.map((cls) => ({
prevStateName: event.stepStateName,
result: cls.constructor.name,
}));
}
async syncData(event) {
this.logger.debug('syncData:: ', event.commandRecord);
const handlerName = event.input?.result;
if (!handlerName) {
throw new Error('SyncDataHandler not found!');
}
const handler = this.commandService.getDataSyncHandler(handlerName);
if (!handler) {
throw new Error('SyncDataHandler empty!');
}
const commandModel = await event.getFullCommandRecord(this.s3Service);
return handler.up(commandModel);
}
async checkNextToken(event) {
this.logger.debug('checkNextToken:: ', event.commandRecord);
const nextCommand = await this.commandService.getNextCommand(event.commandKey);
if (!nextCommand) {
this.logger.debug('No next command version found. Chain ends.');
return null;
}
if (nextCommand.taskToken) {
this.logger.log(`Found waiting command v${nextCommand.version}. Resuming...`);
try {
await this.sfnService.resumeExecution(nextCommand.taskToken, {
result: 'resumed_by_prev_version',
prevVersion: event.commandRecord.version,
});
}
catch (e) {
this.logger.warn(`Could not resume command v${nextCommand.version}: ${e instanceof Error ? e.message : 'Unknown error'}`);
}
}
else {
this.logger.warn(`Next command v${nextCommand.version} found but no token. Status: ${nextCommand.status}`);
}
return null;
}
async publishAlarm(event, errorDetails) {
this.logger.debug('event', event);
const alarm = {
action: 'sfn-alarm',
id: `${event.commandKey.pk}#${event.commandKey.sk}`,
table: this.options.tableName,
pk: event.commandKey.pk,
sk: event.commandKey.sk,
tenantCode: event.commandKey.pk.substring(event.commandKey.pk.indexOf('#') + 1),
content: {
errorMessage: errorDetails,
sfnId: event.context.Execution.Id,
},
};
this.logger.error('alarm:::', alarm);
await this.snsService.publish(alarm, this.alarmTopicArn);
}
};
exports.CommandEventHandler = CommandEventHandler;
exports.CommandEventHandler = CommandEventHandler = CommandEventHandler_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)(command_module_definition_1.MODULE_OPTIONS_TOKEN)),
__metadata("design:paramtypes", [Object, command_service_1.CommandService,
data_service_1.DataService,
history_service_1.HistoryService,
data_store_1.S3Service,
queue_1.SnsService,
config_1.ConfigService,
step_function_service_1.StepFunctionService])
], CommandEventHandler);
//# sourceMappingURL=command.event.handler.js.map