@mbc-cqrs-serverless/cli
Version:
a CLI to get started with MBC CQRS serverless framework
205 lines (202 loc) • 9.32 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
const core_1 = require("@angular-devkit/core");
const schematics_1 = require("@angular-devkit/schematics");
const ts = __importStar(require("typescript"));
const yaml_1 = require("yaml");
function main(options) {
return (tree, _context) => {
const filePath = (0, core_1.normalize)(`/src/${core_1.strings.dasherize(options.name)}/${core_1.strings.dasherize(options.name)}.module.ts`);
const isFileExists = tree.exists(filePath);
if (isFileExists) {
_context.logger.info(`Module file already exists at: ${filePath}`);
return;
}
updateMainModule(tree, options);
updateCdkInfraStack(tree, options);
updateCqrsTable(tree, options);
updateServerlessYaml(tree, options);
if (options.schema) {
updatePrismaSchema(tree, options);
}
return (0, schematics_1.chain)([createModule(options), createUnitTest(options)]);
};
}
// create rule
function createModule(options) {
return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)(`./files/${options.mode}`), [
options.schema ? (0, schematics_1.noop)() : (0, schematics_1.filter)((path) => !path.endsWith('.handler.ts')),
(0, schematics_1.template)({
...core_1.strings,
...options,
}),
(0, schematics_1.move)((0, core_1.normalize)(`/src/${core_1.strings.dasherize(options.name)}`)),
]));
}
function createUnitTest(options) {
return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./units'), [
(0, schematics_1.template)({
...core_1.strings,
...options,
specFileSuffix: 'spec',
}),
(0, schematics_1.move)((0, core_1.normalize)(`/test/unit/${core_1.strings.dasherize(options.name)}`)),
]));
}
// modify main.module.ts
function updateMainModule(tree, options) {
const mainModulePath = 'src/main.module.ts';
const isMainModulePathExists = tree.exists(mainModulePath);
if (isMainModulePathExists) {
const fileBuffer = tree.read(mainModulePath);
const content = fileBuffer.toString('utf-8');
const lines = content.split('\n');
lines.splice(5, 0, `import { ${core_1.strings.classify(options.name)}Module } from './${core_1.strings.dasherize(options.name)}/${core_1.strings.dasherize(options.name)}.module'`);
lines.splice(23, 0, ` ${core_1.strings.classify(options.name)}Module,`);
const newContent = lines.join('\n');
tree.overwrite(mainModulePath, newContent);
return tree;
}
}
// modify infra cdk
function updateCdkInfraStack(tree, options) {
const infraStackPath = 'infra/libs/infra-stack.ts';
const isInfraStackExists = tree.exists(infraStackPath);
if (isInfraStackExists) {
const fileBuffer = tree.read(infraStackPath);
const content = fileBuffer.toString('utf-8');
const sourceFile = ts.createSourceFile(infraStackPath, content, ts.ScriptTarget.Latest, true);
const updatedContent = updateTableNamesArray(sourceFile, [`${core_1.strings.dasherize(options.name)}-command`], content);
tree.overwrite(infraStackPath, updatedContent);
return tree;
}
}
function updateTableNamesArray(sourceFile, newTableNames, content) {
let updatedContent = content;
const visit = (node) => {
if (ts.isVariableDeclaration(node) &&
node.name.getText() === 'tableNames' &&
ts.isArrayLiteralExpression(node.initializer)) {
// Extract existing table names
const existingElements = node.initializer.elements.map((element) => element.getText().replace(/['"]/g, ''));
// Append new table names, ensuring no duplicates
const updatedTableNames = Array.from(new Set([...existingElements, ...newTableNames]));
// Generate the updated array string
const newArray = `[${updatedTableNames.map((name) => `'${name}'`).join(', ')}]`;
// Replace the existing array with the new array
updatedContent =
updatedContent.slice(0, node.initializer.getStart()) +
newArray +
updatedContent.slice(node.initializer.getEnd());
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
return updatedContent;
}
// modify cqrs.json
function updateCqrsTable(tree, options) {
const cqrsTablePath = 'prisma/dynamodbs/cqrs.json';
const isCqrsTableExists = tree.exists(cqrsTablePath);
if (isCqrsTableExists) {
const fileContent = tree.read(cqrsTablePath)?.toString();
const jsonContent = JSON.parse(fileContent.toString());
jsonContent.push(core_1.strings.dasherize(options.name));
tree.overwrite(cqrsTablePath, JSON.stringify(jsonContent, null, 2));
return tree;
}
}
// modify prisma.schema
function updatePrismaSchema(tree, options) {
const schemaPath = 'prisma/schema.prisma';
const isSchemaExists = tree.exists(schemaPath);
if (isSchemaExists) {
const fileContent = tree.read(schemaPath)?.toString('utf-8');
const stringToAppend = generateModelTemplate(options.name);
const updatedContent = fileContent + stringToAppend;
tree.overwrite(schemaPath, updatedContent);
return tree;
}
}
// modify serverless.yaml
function updateServerlessYaml(tree, options) {
const serverlessPath = 'infra-local/serverless.yml';
const isServerlessExists = tree.exists(serverlessPath);
if (isServerlessExists) {
const fileContent = tree.read(serverlessPath)?.toString('utf-8');
const newStreamEvent = {
type: 'dynamodb',
maximumRetryAttempts: 10,
arn: '${env:LOCAL_DDB_%%TABLE_NAME%%_STREAM}'.replace('%%TABLE_NAME%%', options.name.toUpperCase()),
filterPatterns: [{ eventName: ['INSERT'] }],
};
const doc = (0, yaml_1.parseDocument)(fileContent);
const mainFunction = doc.getIn(['functions', 'main']);
const events = mainFunction.get('events');
events.items.push({ stream: newStreamEvent });
const updatedYamlContent = (0, yaml_1.stringify)(doc);
tree.overwrite(serverlessPath, updatedYamlContent);
return tree;
}
}
const generateModelTemplate = (name) => `
model ${core_1.strings.classify(name)} {
id String @id
cpk String // コマンド用PK
csk String // コマンド用SK
pk String // データ用PK, ${name.toUpperCase()}#tenantCode (テナントコード)
sk String // データ用SK, マスタ種別コード#マスタコード
tenantCode String @map("tenant_code") // テナントコード, 【テナントコードマスタ】
seq Int @default(0) // 並び順, 採番機能を使用する
code String // レコードのコード, マスタ種別コード#マスタコード
name String // レコード名, 名前
version Int // バージョン
isDeleted Boolean @default(false) @map("is_deleted") // 削除フラグ
createdBy String @default("") @map("created_by") // 作成者
createdIp String @default("") @map("created_ip") // 作成IP, IPv6も考慮する
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) // 作成日時
updatedBy String @default("") @map("updated_by") // 更新者
updatedIp String @default("") @map("updated_ip") // 更新IP, IPv6も考慮する
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(0) // 更新日時
attributes Json? @map("attributes")
@@unique([cpk, csk])
@@unique([pk, sk])
@@unique([tenantCode, code])
@@index([tenantCode, name])
@@map("${core_1.strings.underscore(name)}s")
}
`;