@mbc-cqrs-serverless/master
Version:
Master data management such as setting, sequence, etc.
242 lines • 11.8 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 MasterSfnTaskEventHandler_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MasterSfnTaskEventHandler = void 0;
const core_1 = require("@mbc-cqrs-serverless/core");
const sequence_1 = require("@mbc-cqrs-serverless/sequence");
const common_1 = require("@nestjs/common");
const lodash_1 = require("lodash");
const constants_1 = require("../constants");
const dto_1 = require("../dto");
const helpers_1 = require("../helpers");
const master_module_definition_1 = require("../master.module-definition");
const services_1 = require("../services");
const master_sfn_task_event_1 = require("./master-sfn-task.event");
const BATCH_SIZE = 100;
let MasterSfnTaskEventHandler = MasterSfnTaskEventHandler_1 = class MasterSfnTaskEventHandler {
constructor(dynamoDbService, prismaService, masterSettingService, masterDataService, dataService, sequencesService) {
this.dynamoDbService = dynamoDbService;
this.prismaService = prismaService;
this.masterSettingService = masterSettingService;
this.masterDataService = masterDataService;
this.dataService = dataService;
this.sequencesService = sequencesService;
this.logger = new common_1.Logger(MasterSfnTaskEventHandler_1.name);
this.sequenceTableName = this.dynamoDbService.getTableName('sequences');
}
async execute(event) {
const invokeContext = (0, core_1.extractInvokeContext)();
const masterCopyDto = event.input?.input;
this.logger.debug('sfn-event:masterCopyDto:', masterCopyDto);
const { masterSettingId, targetTenants, copyType, dataCopyOption } = masterCopyDto;
const targetTenant = `${targetTenants[0]}`;
const setting = await this.fetchSetting(masterSettingId);
const masterCode = this.getMasterCodeFromSetting(setting);
if (copyType === dto_1.CopyType.SETTING_ONLY || copyType === dto_1.CopyType.BOTH) {
await this.copySettingToTenant(setting, targetTenant, invokeContext);
}
if (copyType === dto_1.CopyType.DATA_ONLY || copyType === dto_1.CopyType.BOTH) {
const isCopySequence = await this.shouldCopySequence(setting, targetTenant);
if (isCopySequence) {
await this.copySeqToTenant(setting, targetTenant);
}
const dataToCopy = await this.fetchMasterData(masterCode, dataCopyOption);
await this.copyDataToTenant(dataToCopy, targetTenant, invokeContext);
}
this.logger.debug('Completed copy process for tenant:', targetTenant);
return { message: 'Copy successfully', event };
}
async fetchSetting(id) {
const setting = await this.dataService.getItem((0, helpers_1.parseId)(id));
this.logger.debug('sfn-event-setting', setting);
return setting;
}
async fetchMasterData(masterCode, dataCopyOption, tenant = 'meltec') {
const where = {
masterType: constants_1.DATA_SK_PREFIX,
masterTypeCode: masterCode,
pk: `MASTER${core_1.KEY_SEPARATOR}${tenant}`,
};
if (dataCopyOption?.mode === dto_1.DataCopyMode.PARTIAL) {
where.id = { in: dataCopyOption.id };
}
this.logger.debug('sfn-event-whereCondition', where);
const data = await this.prismaService.master.findMany({ where });
this.logger.debug('sfn-event-dataToCopy', data.length);
return data;
}
async copySettingToTenant(setting, tenantCode, invokeContext) {
const sk = setting.sk;
const pk = (0, helpers_1.generateMasterPk)(tenantCode);
const masterCode = this.getMasterCodeFromSetting(setting);
const tenantSetting = await this.dataService.getItem({ pk, sk });
this.logger.debug('sfn-event-copySettingToTenant-tenantSetting', tenantSetting);
if (tenantSetting && tenantSetting.isDeleted === false) {
this.logger.debug('sfn-event-copySettingToTenant-updateSetting', {
pk,
sk,
});
await this.masterSettingService.updateSetting({ pk, sk }, {
name: setting.name,
code: masterCode,
settingValue: setting.attributes,
tenantCode,
}, {
invokeContext,
});
}
else {
this.logger.debug('sfn-event-copySettingToTenant-createTenantSetting', {
pk,
sk,
});
await this.masterSettingService.createTenantSetting({
name: setting.name,
code: masterCode,
settingValue: setting.attributes,
tenantCode,
}, { invokeContext });
}
}
async copyDataToTenant(dataToCopy, tenantCode, invokeContext) {
const chunks = (0, lodash_1.chunk)(dataToCopy, BATCH_SIZE);
for (const batch of chunks) {
await Promise.all(batch.map(async (data) => {
const parts = data.sk.split(core_1.KEY_SEPARATOR);
const sk = parts.length > 1 && parts[1].trim() === ''
? `${data.sk}${data.masterCode}`
: data.sk;
const pk = (0, helpers_1.generateMasterPk)(tenantCode);
const tenantData = await this.dataService.getItem({ pk, sk });
this.logger.debug('sfn-event-copyDataToTenant-tenantData', tenantData);
// des tenant data is exist and not deleted => update des data same as src data
if (tenantData && tenantData.isDeleted === false) {
this.logger.debug('sfn-event-copyDataToTenant-update', { pk, sk });
return this.masterDataService.update({ pk, sk }, {
name: data.name,
attributes: data.attributes,
isDeleted: data.isDeleted,
seq: data.seq,
}, {
invokeContext,
});
}
// src data is deleted => do nothing
if (data.isDeleted === true)
return;
// src data is exist => create des data
return this.masterDataService.create({
code: data.masterCode,
tenantCode,
name: data.name,
settingCode: data.masterTypeCode,
attributes: data.attributes,
seq: data.seq,
}, { invokeContext });
}));
}
}
async shouldCopySequence(setting, tenantCode) {
const fields = setting?.attributes?.['fields'] || [];
const codeField = fields.find((f) => f.physicalName === 'code');
if (codeField?.dataType !== 'auto_number') {
this.logger.debug('Sequence not required: code field not auto_number');
return false;
}
const jcciSeqKey = this.generateSequenceKey(tenantCode, setting);
const { seq: jcciSeq = 0 } = (await this.sequencesService.getCurrentSequence(jcciSeqKey)) ?? {};
this.logger.debug('sfn-event-shouldCopySequence-jcciSeq', {
jcciSeqKey,
jcciSeq,
});
if (jcciSeq === 0) {
this.logger.debug('Skipping sequence copy: JCCI sequence is 0');
return false;
}
const tenantSeqKey = this.generateSequenceKey(tenantCode, setting);
const { seq: tenantSeq } = (await this.sequencesService.getCurrentSequence(tenantSeqKey)) ?? {};
this.logger.debug('sfn-event-shouldCopySequence-tenantSeq', {
tenantSeqKey,
tenantSeq,
});
if (tenantSeq === undefined || tenantSeq === null) {
this.logger.debug('Tenant sequence missing: copying sequence required');
return true;
}
if (jcciSeq > tenantSeq) {
this.logger.debug('Tenant sequence is behind: copying required');
return true;
}
this.logger.debug('Tenant sequence is up to date or ahead: no copy needed');
return false;
}
async copySeqToTenant(setting, tenantCode) {
const fields = setting?.attributes?.['fields'] || [];
const codeField = fields.find((f) => f.physicalName === 'code');
const typeCode = codeField.formatCode ?? codeField.dataFormat;
const jcciSeqKey = this.generateSequenceKey(tenantCode, setting);
const { seq: jcciSeq = 0 } = (await this.sequencesService.getCurrentSequence(jcciSeqKey)) ?? {};
const tenantSeqKey = this.generateSequenceKey(tenantCode, setting);
const { seq: tenantSeq = 0 } = (await this.sequencesService.getCurrentSequence(tenantSeqKey)) ?? {};
const distance = jcciSeq - tenantSeq;
this.logger.debug('Copying sequence gap:', {
jcciSeq,
tenantSeq,
distance,
});
this.logger.debug('putItem', {
pk: tenantSeqKey.pk,
sk: tenantSeqKey.sk,
code: tenantSeqKey.sk,
name: tenantSeqKey.sk.split(core_1.KEY_SEPARATOR).at(-1),
seq: jcciSeq,
tenantCode,
type: typeCode,
});
this.logger.debug('this.sequenceTableName', this.sequenceTableName);
await this.dynamoDbService.putItem(this.sequenceTableName, {
pk: tenantSeqKey.pk,
sk: tenantSeqKey.sk,
code: tenantSeqKey.sk,
name: tenantSeqKey.sk.split(core_1.KEY_SEPARATOR).at(-1),
seq: jcciSeq,
tenantCode,
type: typeCode,
});
}
generateSequenceKey(tenantCode, setting) {
const fields = setting?.attributes?.['fields'] || [];
const codeField = fields.find((f) => f.physicalName === 'code');
const seqSk = codeField.formatCode ?? codeField.dataFormat;
const pk = (0, helpers_1.sequencePk)(tenantCode);
const masterCode = this.getMasterCodeFromSetting(setting);
const sk = (0, helpers_1.genSequenceSk)(seqSk, masterCode, sequence_1.RotateByEnum.NONE);
return { pk, sk };
}
getMasterCodeFromSetting(setting) {
const parts = setting.sk.split(core_1.KEY_SEPARATOR);
return parts[1];
}
};
exports.MasterSfnTaskEventHandler = MasterSfnTaskEventHandler;
exports.MasterSfnTaskEventHandler = MasterSfnTaskEventHandler = MasterSfnTaskEventHandler_1 = __decorate([
(0, core_1.EventHandler)(master_sfn_task_event_1.MasterSfnTaskEvent),
__param(1, (0, common_1.Inject)(master_module_definition_1.PRISMA_SERVICE)),
__metadata("design:paramtypes", [core_1.DynamoDbService, Object, services_1.MasterSettingService,
services_1.MasterDataService,
core_1.DataService,
sequence_1.SequencesService])
], MasterSfnTaskEventHandler);
//# sourceMappingURL=master-sfn-task.handler.js.map