UNPKG

claire-framework

Version:

- được viết bằng TypeScript - hỗ trợ websocket và HTTP request - hỗ trợ CLI để generate base project (claire-cli)

820 lines 86.3 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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); //-- force postgres to parse int require('pg').defaults.parseInt8 = true; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const child_process_1 = require("child_process"); const sequelize_1 = require("sequelize"); const Utils_1 = require("../system/Utils"); const ModelMetadata_1 = require("../model/ModelMetadata"); const AbstractQuery_1 = require("../model/AbstractQuery"); const ClaireError_1 = require("../system/ClaireError"); const errors_1 = require("../system/errors"); const QueryOperator_1 = require("../model/QueryOperator"); const __1 = require(".."); const __2 = require(".."); const AbstractLogger_1 = require("../logger/AbstractLogger"); const FieldValidationMetadata_1 = require("../validator/FieldValidationMetadata"); const DataType_1 = require("../enum/DataType"); class ModelAdapter extends AbstractQuery_1.AbstractQuery { constructor(connection, model, metadata, schema, transaction) { super(model, metadata); this.currentSchema = schema; this.connection = connection; this.transaction = transaction; } updateConditionWithOptions(conditions, options) { if (options) { if (options.order) { //-- convert order to data object // @ts-ignore const dataOrder = Utils_1.Utils.getCleanObject(this.convertToDataObjects([options.order])[0]); Object.assign(conditions, { order: Object.keys(dataOrder).map((key) => ([key, dataOrder[key]])) }); } if (options.limit) { conditions = Object.assign(conditions, { limit: options.limit }); if (options.page) { conditions = Object.assign(conditions, { offset: options.limit * (options.page - 1) }); } } if (options.projection) { Object.assign(conditions, { attributes: this.getProjectionFields(options.projection), }); } } } saveOne(modelInstance) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.saveMany([modelInstance]); return result[0]; }); } saveMany(modelInstances) { return __awaiter(this, void 0, void 0, function* () { let primaryKey = this.currentMetadata.getPrimaryKey(); let dataObjects = this.convertToDataObjects(modelInstances); const defaultDataValues = this.convertToDataObjects([this.currentMetadata.getDefaultLogicValues()])[0]; dataObjects = dataObjects.map((obj) => (Object.assign(Object.assign({}, defaultDataValues), Utils_1.Utils.getCleanObject(obj)))); //-- use sequelize bulkCreate function let instances = yield this.currentSchema.modelConnection.bulkCreate(dataObjects, { transaction: this.transaction }); if (instances.length !== modelInstances.length) { throw new ClaireError_1.ClaireError(errors_1.BULK_OPERATION_ERROR); } let result = []; if (primaryKey.isAutoIncrement) { for (let i = 0; i < instances.length; i++) { result.push(Object.assign(this.convertToLogicObjects([instances[i]])[0], { [primaryKey.fieldLogicName]: instances[i][primaryKey.fieldDataName] })); } } else { for (let i = 0; i < instances.length; i++) { result.push(Object.assign(this.convertToLogicObjects([instances[i]])[0])); } } // @ts-ignore return result; }); } getOne(queries, options) { return __awaiter(this, void 0, void 0, function* () { let conditions = { where: this.getQueryObjectFromQueryConditions(queries || []), transaction: this.transaction, }; this.updateConditionWithOptions(conditions, options); let instance = yield this.currentSchema.modelConnection.findOne(conditions); if (!instance) { return undefined; } return this.convertToLogicObjects([instance], options && options.projection)[0]; }); } getMany(queries, options) { return __awaiter(this, void 0, void 0, function* () { let conditions = { where: this.getQueryObjectFromQueryConditions(queries), transaction: this.transaction }; this.updateConditionWithOptions(conditions, options); let instances = yield this.currentSchema.modelConnection.findAll(conditions); return this.convertToLogicObjects(instances, options && options.projection); }); } deleteOne(modelInstance) { return __awaiter(this, void 0, void 0, function* () { let primaryKey = this.currentMetadata.getPrimaryKey(); try { // @ts-ignore yield this.deleteMany([{ [primaryKey.fieldDataName]: QueryOperator_1.eq(modelInstance[primaryKey.fieldDataName]) }]); return modelInstance; } catch (err) { throw new ClaireError_1.ClaireError(errors_1.QUERY_ERROR, err.stack || String(err)); } }); } deleteMany(queries) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.currentSchema.modelConnection.destroy({ where: this.getQueryObjectFromQueryConditions(queries), transaction: this.transaction }); } catch (err) { throw new ClaireError_1.ClaireError(errors_1.QUERY_ERROR, err.stack || String(err)); } }); } updateOne(modelInstance) { return __awaiter(this, void 0, void 0, function* () { try { yield this.currentSchema.modelConnection.update(this.convertToDataObjects([modelInstance])[0], { // @ts-ignore where: this.getQueryObjectFromQueryConditions([{ [this.currentMetadata.getPrimaryKey().fieldDataName]: QueryOperator_1.eq(modelInstance[this.currentMetadata.getPrimaryKey().fieldLogicName]) }]), transaction: this.transaction }); return modelInstance; } catch (err) { throw new ClaireError_1.ClaireError(errors_1.QUERY_ERROR, err.stack || String(err)); } }); } updateMany(queries, update) { return __awaiter(this, void 0, void 0, function* () { //-- remove undefined fields from update because sequelize treat undefined as null try { let queryObject = this.getQueryObjectFromQueryConditions(queries); let dataUpdate = this.convertToDataObjects([update])[0]; let result = yield this.currentSchema.modelConnection.update(dataUpdate, { where: queryObject, transaction: this.transaction }); if (result[0] === 0) { //-- may the updated value does not update anything let result = yield this.currentSchema.modelConnection.findAndCountAll({ where: Object.assign({}, dataUpdate, queryObject), transaction: this.transaction }); return result.count; } return result[0]; } catch (err) { throw new ClaireError_1.ClaireError(errors_1.QUERY_ERROR, err.stack || String(err)); } }); } rawQuery(query) { return new Promise((resolve) => { this.connection.query(query, { transaction: this.transaction }).then(([results, metadata]) => { return resolve([results, metadata]); }); }); } getQueryObjectFromQueryConditions(queries) { if (!queries || !queries.length) { return {}; } const orQueries = []; for (const query of queries) { let result = {}; Object.keys(query).forEach((key) => { // @ts-ignore let operator = query[key]; let dataKey = this.currentMetadata.getFieldByLogicName(key).fieldDataName; switch (operator.operator) { case QueryOperator_1.Operator.COMMON_EQUALITY: result[dataKey] = { [sequelize_1.Op.eq]: operator.value }; break; case QueryOperator_1.Operator.COMMON_INEQUALITY: result[dataKey] = { [sequelize_1.Op.ne]: operator.value }; break; case QueryOperator_1.Operator.COMMON_BELONG: if (operator.value.length === 1 && operator.value[0] === null) { result[dataKey] = { [sequelize_1.Op.is]: null }; } else { result[dataKey] = { [sequelize_1.Op.in]: operator.value }; } break; case QueryOperator_1.Operator.STRING_CONTAIN: result[dataKey] = { [sequelize_1.Op.contains]: operator.value }; break; case QueryOperator_1.Operator.STRING_REGEX: result[dataKey] = { [sequelize_1.Op.regexp]: operator.value }; break; case QueryOperator_1.Operator.NUMBER_GT: result[dataKey] = { [sequelize_1.Op.gt]: operator.value }; break; case QueryOperator_1.Operator.NUMBER_GTE: result[dataKey] = { [sequelize_1.Op.gte]: operator.value }; break; case QueryOperator_1.Operator.NUMBER_LT: result[dataKey] = { [sequelize_1.Op.lt]: operator.value }; break; case QueryOperator_1.Operator.NUMBER_LTE: result[dataKey] = { [sequelize_1.Op.lte]: operator.value }; break; case QueryOperator_1.Operator.NUMBER_BETWEEN: result[dataKey] = { [sequelize_1.Op.and]: [{ [sequelize_1.Op.gte]: operator.value.start }, { [sequelize_1.Op.lte]: operator.value.end }] }; break; default: throw new ClaireError_1.ClaireError(errors_1.OPERATOR_NOT_FOUND); } }); orQueries.push(result); } return { [sequelize_1.Op.or]: orQueries }; } } class TransactionAdapter { constructor(transaction, connection, modelMetadata, schemas) { this.modelAdapters = new Map(); this.transaction = transaction; this.connection = connection; this.modelMetadata = modelMetadata; this.schemas = schemas; } use(model) { let modelAdapter = this.modelAdapters.get(model.name); if (!modelAdapter) { modelAdapter = new ModelAdapter(this.connection, model, this.modelMetadata.find((meta) => meta.modelName === model.name), this.schemas[model.name], this.transaction); this.modelAdapters.set(model.name, modelAdapter); } // @ts-ignore return modelAdapter; } commit() { return __awaiter(this, void 0, void 0, function* () { return this.transaction.commit(); }); } rollback() { return __awaiter(this, void 0, void 0, function* () { return this.transaction.rollback(); }); } } class DefaultSqlAdapter extends __1.AbstractDatabaseAdapter { constructor(sqlProvider, connectionString, migrationDirectory) { super(); this.connectionString = connectionString; this.migrationDirectory = migrationDirectory; switch (sqlProvider) { case __1.SqlProvider.MY_SQL: this.provider = "mysql"; break; case __1.SqlProvider.POSTGRES: this.provider = "postgres"; break; default: throw new ClaireError_1.ClaireError(errors_1.NOT_SUPPORTED); } this.databaseURL = `${this.provider}://${this.connectionString}`; this.connection = new sequelize_1.Sequelize(this.databaseURL, { logging: false, define: { timestamps: false } }); this.modelAdapters = new Map(); } use(model) { let modelAdapter = this.modelAdapters.get(model.name); if (!modelAdapter) { let modelMetaData = this.modelMetadata.find((meta) => meta.modelName === model.name); if (!modelMetaData) { throw new ClaireError_1.ClaireError(errors_1.MODEL_METADATA_NOT_FOUND, model.name); } modelAdapter = new ModelAdapter(this.connection, model, modelMetaData, this.schemas[model.name]); this.modelAdapters.set(model.name, modelAdapter); } // @ts-ignore return modelAdapter; } init() { const _super = Object.create(null, { init: { get: () => super.init } }); return __awaiter(this, void 0, void 0, function* () { yield _super.init.call(this); this.logger.debug("Init database adapter"); //-- create db if not existed this.logger.debug("Create database if not exist..."); yield new Promise((resolve, reject) => { child_process_1.exec(`${path_1.default.join(__dirname, DefaultSqlAdapter.SEQUELIZE_CLI_PATH, ".bin/sequelize")} --url=${this.databaseURL} db:create`, { env: process.env }, (err) => { if (err) { if (String(err).indexOf("already exists") >= 0) { return resolve(); } else { return reject(new ClaireError_1.ClaireError("CANNOT_CREATE_DB", String(err))); } } return resolve(); }); }); //-- connect and authenticate yield new Promise((resolve, reject) => { this.connection.authenticate() .then(() => { return resolve(); }) .catch((err) => { return reject(new ClaireError_1.ClaireError("DATABASE_CONNECTION_FAILED", err.stack || String(err))); }); }); //-- doing migration if (!!this.migrationDirectory) { this.logger.debug("Running migration..."); yield this.runMigration(); } this.logger.debug("Generating in-memory schema"); //-- inject modelConnection this.modelMetadata.forEach((metadata) => { this.schemas[metadata.modelName].modelConnection = DefaultSqlAdapter.generateSchemaObject(this.connection, metadata); }); this.logger.debug("Database adapter init succeeded"); }); } stop() { const _super = Object.create(null, { stop: { get: () => super.stop } }); return __awaiter(this, void 0, void 0, function* () { this.logger.debug("Disconnecting from database..."); yield this.connection.close(); yield _super.stop.call(this); }); } runMigration() { return __awaiter(this, void 0, void 0, function* () { let prototypeFilePath = path_1.default.join(this.migrationDirectory, "metadata.json"); let migrationDir = path_1.default.join(this.migrationDirectory, "scripts"); if (!fs_1.default.existsSync(migrationDir)) { this.logger.debug("Migration directory not exists, creating empty folder"); fs_1.default.mkdirSync(migrationDir, { recursive: true }); } let fileExists = fs_1.default.existsSync(prototypeFilePath); if (!fileExists) { //-- write default empty array of metadata this.logger.debug("Migration metadata does not exist, creating one"); fs_1.default.writeFileSync(prototypeFilePath, JSON.stringify([])); } //-- parse model metadata from json file this.logger.debug("Parsing migration metadata file"); let oldMetadataJSON = JSON.parse(String(fs_1.default.readFileSync(prototypeFilePath))); let oldMetadata = []; oldMetadataJSON.forEach((jsonData) => { let modelMetaData = new ModelMetadata_1.ModelMetadata(); modelMetaData.modelName = jsonData["modelName"]; modelMetaData.tableName = jsonData["tableName"]; jsonData["fields"].forEach((field) => { let fieldMetaData = new FieldValidationMetadata_1.FieldValidationMetadata(); fieldMetaData.fieldDataName = field["fieldDataName"]; fieldMetaData.isPrimaryKey = field["isPrimaryKey"]; fieldMetaData.isForeignKey = field["isForeignKey"]; fieldMetaData.isAutoIncrement = field["isAutoIncrement"]; fieldMetaData.isUnique = field["isUnique"]; fieldMetaData.referModel = field["referModel"]; fieldMetaData.innerDataType = field["innerDataType"]; fieldMetaData.nullable = field["nullable"]; fieldMetaData.defaultValue = field["defaultValue"]; modelMetaData.addField(fieldMetaData); }); oldMetadata.push(modelMetaData); }); //-- calculate differences between two array of prototypes this.logger.debug("Calculating differences"); let differences = []; this.modelMetadata.forEach((metadata) => { let matchedMetadata = oldMetadata.find((m) => m.modelName === metadata.modelName); differences.push(DefaultSqlAdapter.getDifferences(matchedMetadata, metadata)); }); oldMetadata.forEach((metadata) => { let matchedMetadata = this.modelMetadata.find((m) => m.modelName === metadata.modelName); if (!matchedMetadata) { differences.push(DefaultSqlAdapter.getDifferences(metadata, undefined)); } }); //-- generating scripts from difference let scripts = []; for (let i = 0; i < differences.length; i++) { let upTableQueries = []; let downTableQueries = []; let upConstraintQueries = []; let downConstraintQueries = []; let diff = differences[i]; if (diff.removed && diff.removed.modelName) { let currentModelName = diff.removed.modelName; let currentModel = this.modelMetadata.find((meta) => meta.modelName === currentModelName); //-- check whether to remove entire table or just the column if (!diff.added) { //-- remove entire table !! no more support auto removing table, please remove it manually // upTableQueries.push(`queryInterface.dropTable('${currentModel && currentModel.tableName}',{}),\n`); } else { //-- check to remove or update column let oldFields = diff.removed.fields; let newFields = diff.added.fields; for (let j = 0; j < oldFields.length; j++) { let oldField = oldFields[j]; let matchedField = newFields.find((f) => f.fieldDataName === oldField.fieldDataName); if (!matchedField) { //-- completely matched, remove field upTableQueries.push(`queryInterface.removeColumn('${currentModel && currentModel.tableName}','${oldField.fieldDataName}',),\n`); } else { if (oldField.isUnique === true) { //-- remove unique constraint upConstraintQueries.push(`queryInterface.removeConstraint('${currentModel && currentModel.tableName}','${diff.removed}_${oldField.fieldDataName}_un'),\n`); downConstraintQueries.push(`queryInterface.addConstraint('${currentModel && currentModel.tableName}',['${oldField.fieldDataName}'],{type:'unique',name:'${currentModel && currentModel.tableName}_${oldField.fieldDataName}_un'}),\n`); } if (oldField.isForeignKey === true) { let referModel = this.modelMetadata.find((meta) => meta.modelName === oldField.referModel); //-- remove foreign key constraint upConstraintQueries.push(`queryInterface.removeConstraint('${currentModel && currentModel.tableName}','${currentModel && currentModel.tableName}_${oldField.fieldDataName}_fk'),\n`); downConstraintQueries.push(`queryInterface.addConstraint('${currentModel && currentModel.tableName}',['${oldField.fieldDataName}'],{type:'foreign key',name:'${currentModel && currentModel.tableName}_${oldField.fieldDataName}_fk',references:{table:'${referModel && referModel.tableName}',field:'${referModel && referModel.getPrimaryKey().fieldDataName}'}}),\n`); } } } } } if (diff.added && diff.added.modelName) { let currentModelName = diff.added.modelName; let currentModel = this.modelMetadata.find((meta) => meta.modelName === currentModelName); let fieldsQuery = ""; let optionsQuery = ""; if (!diff.removed) { //-- create new table fieldsQuery += "{\n"; let tableFields = diff.added.fields; for (let j = 0; j < tableFields.length; j++) { let propertiesQuery = this.generatePropertiesQuery(tableFields[j]); fieldsQuery += `${tableFields[j].fieldDataName}:${propertiesQuery},\n`; } fieldsQuery += "}\n"; //-- check options optionsQuery += "{\n"; // let charset = diff.added.charset; // switch (charset) { // case Charset.UTF8: optionsQuery += `charset:'utf8',`; // break; // case Charset.ASCII: // optionsQuery += `charset:'ascii',`; // break; // default: // break; // } optionsQuery += "\n}"; upTableQueries.push(`queryInterface.createTable('${currentModel && currentModel.tableName}',${fieldsQuery},${optionsQuery}),\n`); downTableQueries.push(`queryInterface.dropTable('${currentModel && currentModel.tableName}',{}),\n`); //-- check to add constraint for (let j = 0; j < tableFields.length; j++) { //-- check for unique constraint if (tableFields[j].isUnique) { upConstraintQueries.push(`queryInterface.addConstraint('${currentModel && currentModel.tableName}',['${tableFields[j].fieldDataName}'],{type:'unique',name:'${currentModel && currentModel.tableName}_${tableFields[j].fieldDataName}_un'}),\n`); downConstraintQueries.push(`queryInterface.removeConstraint('${currentModel && currentModel.tableName}','${currentModel && currentModel.tableName}_${tableFields[j].fieldDataName}_un'),\n`); } if (tableFields[j].isForeignKey) { let referModel = this.modelMetadata.find((m) => m.modelName === tableFields[j].referModel); upConstraintQueries.push(`queryInterface.addConstraint('${currentModel && currentModel.tableName}',['${tableFields[j].fieldDataName}'],{type:'foreign key',name:'${currentModel && currentModel.tableName}_${tableFields[j].fieldDataName}_fk',references:{table:'${referModel && referModel.tableName}',field:'${referModel && referModel.getPrimaryKey().fieldDataName}'}}),\n`); downConstraintQueries.push(`queryInterface.removeConstraint('${currentModel && currentModel.tableName}','${currentModel && currentModel.tableName}_${tableFields[j].fieldDataName}_fk'),\n`); } } } else { //-- check to add or update column let tableFields = diff.added.fields; fieldsQuery += "{\n"; for (let j = 0; j < tableFields.length; j++) { let field = tableFields[j]; let propertiesQuery = this.generatePropertiesQuery(field); let oldMatchedField = diff.removed.getFieldByDataName(field.fieldDataName || ""); if (!oldMatchedField) { //-- add field upTableQueries.push(`queryInterface.addColumn('${currentModel && currentModel.tableName}','${field.fieldDataName}',${propertiesQuery}),\n`); downTableQueries.push(`queryInterface.removeColumn('${currentModel && currentModel.tableName}','${field.fieldDataName}',),\n`); } else { if (field.isUnique === true) { //-- add unique constraint upConstraintQueries.push(`queryInterface.addConstraint('${currentModel && currentModel.tableName}',['${field.fieldDataName}'],{type:'unique',name:'${currentModel && currentModel.tableName}_${field.fieldDataName}_un'}),\n`); downConstraintQueries.push(`queryInterface.removeConstraint('${currentModel && currentModel.tableName}','${currentModel && currentModel.tableName}_${field.fieldDataName}_un'),\n`); } if (field.isForeignKey === true) { //-- add foreign key constraint let referModel = this.modelMetadata.find((meta) => meta.modelName === field.referModel); upConstraintQueries.push(`queryInterface.addConstraint('${currentModel && currentModel.tableName}',['${field.fieldDataName}'],{type:'foreign key',name:'${currentModel && currentModel.tableName}_${field.fieldDataName}_fk',references:{table:'${referModel && referModel.tableName}',field:'${referModel && referModel.getPrimaryKey().fieldDataName}'}}),\n`); downConstraintQueries.push(`queryInterface.removeConstraint('${currentModel && currentModel.tableName}','${currentModel && currentModel.tableName}_${field.fieldDataName}_fk'),\n`); } if (field.innerDataType !== undefined || field.nullable !== undefined) { let currentField = currentModel.getFieldByDataName(field.fieldDataName); let oldField = oldMetadata.find((m) => m.modelName === currentModel.modelName).getFieldByDataName(field.fieldDataName); upTableQueries.push(`queryInterface.changeColumn('${currentModel && currentModel.tableName}','${field.fieldDataName}',${this.generatePropertiesQuery(Object.assign({}, currentField, field))}),\n`); downTableQueries.push(`queryInterface.changeColumn('${currentModel && currentModel.tableName}','${field.fieldDataName}',${this.generatePropertiesQuery(oldField)}),\n`); } } } fieldsQuery += "\n}\n"; } } if (upTableQueries.length || downTableQueries.length || upConstraintQueries.length || downConstraintQueries.length) { scripts.push({ upTable: upTableQueries, downTable: downTableQueries, upConstraint: upConstraintQueries, downConstraint: downConstraintQueries }); } } if (!scripts.length) { //-- exec the migration this.logger.debug("No difference, running migration"); yield new Promise((resolve, reject) => { child_process_1.exec(`${path_1.default.join(__dirname, DefaultSqlAdapter.SEQUELIZE_CLI_PATH, ".bin/sequelize")} --url=${this.databaseURL} --migrations-path=${migrationDir} db:migrate`, { env: process.env }, (err) => { if (err) { return reject(new ClaireError_1.ClaireError("MIGRATION_FAILED", err.stack || String(err))); } return resolve(); }); }); return; } else { //-- generate migration file let migrationFileNames = []; let migrationFunction = (scripts, index, migrationType) => __awaiter(this, void 0, void 0, function* () { if (!scripts.up.length && !scripts.down.length) return; let generationResult = yield new Promise((resolve) => { child_process_1.exec(`${path_1.default.join(__dirname, DefaultSqlAdapter.SEQUELIZE_CLI_PATH, ".bin/sequelize")} --migrations-path=${migrationDir} migration:generate --name auto-migration`, {}, (err, stdout) => { if (err) { return resolve(new ClaireError_1.ClaireError("INTERNAL_SYSTEM_ERROR", err.stack || String(err))); } return resolve(stdout); }); }); if (generationResult instanceof ClaireError_1.ClaireError) { return generationResult; } //-- use regex to capture generated file name let regex = new RegExp(`[0-9]*-auto-migration.js`); let fileName = regex.exec(generationResult); if (fileName === null) { return new ClaireError_1.ClaireError("MIGRATION_GENERATION_FAILED", generationResult); } //-- replace content of generated migration file let migrationFilePath = path_1.default.join(migrationDir, fileName[0]); //-- wait until the system populate the new file this.logger.debug("Waiting for system to create migration file: ", migrationFilePath); while (!fs_1.default.existsSync(migrationFilePath)) { yield new Promise((resolve) => { setTimeout(resolve, 1000); }); } let fileContent = String(fs_1.default.readFileSync(migrationFilePath)); let commentRegex = new RegExp(DefaultSqlAdapter.COMMENT_BLOCK_REGEX_PATTERN); let upScript = "return [\n" + scripts.up.reduce((accumulator, e) => accumulator + e, "") + "]" + DefaultSqlAdapter.SERIAL_PROMISE; let downScript = "return [\n" + scripts.down.reverse().reduce((accumulator, e) => accumulator + e, "") + "]" + DefaultSqlAdapter.SERIAL_PROMISE; fileContent = fileContent.replace(commentRegex, upScript); fileContent = fileContent.replace(commentRegex, downScript); this.logger.debug("writing to file", migrationFilePath); fs_1.default.writeFileSync(migrationFilePath, fileContent); //-- rename, use i to avoid file overlapping let newName = fileName[0].substr(0, fileName[0].length - 3) + `-${index}-${migrationType}.js`; this.logger.debug("renaming file", migrationFilePath); fs_1.default.renameSync(migrationFilePath, path_1.default.join(migrationDir, newName)); migrationFileNames.push(newName); return; }); for (let i = 0; i < scripts.length; i++) { yield migrationFunction({ up: scripts[i].upTable, down: scripts[i].downTable }, i, "0"); } for (let i = 0; i < scripts.length; i++) { yield migrationFunction({ up: scripts[i].upConstraint, down: scripts[i].downConstraint }, i, "1"); } //-- update metadata file this.logger.debug("updating metadata file"); fs_1.default.writeFileSync(prototypeFilePath, JSON.stringify(this.modelMetadata)); throw new ClaireError_1.ClaireError("MIGRATION_REVIEW_NEEDED", JSON.stringify(migrationFileNames)); } }); } generatePropertiesQuery(fieldProperties) { let propertiesQuery = "{"; //-- check field properties if (fieldProperties.isPrimaryKey === true) { propertiesQuery += `primaryKey:true,`; } if (fieldProperties.isForeignKey === true) { //-- get table name of the refer model let referModel = this.modelMetadata.find((meta) => meta.modelName === fieldProperties.referModel); propertiesQuery += `references:{model:'${referModel && referModel.tableName}',key:'${referModel && referModel.getPrimaryKey().fieldDataName}'},`; } if (fieldProperties.isAutoIncrement === true) { propertiesQuery += `autoIncrement:true,`; } propertiesQuery += `allowNull:${fieldProperties.nullable ? "true" : "false"},`; if (fieldProperties.defaultValue !== undefined && typeof fieldProperties.defaultValue !== "function") { propertiesQuery += `defaultValue:${fieldProperties.defaultValue},`; } if (fieldProperties.innerDataType !== undefined) { switch (fieldProperties.innerDataType) { case DataType_1.DataType.STRING: propertiesQuery += `type:Sequelize.STRING,`; break; case DataType_1.DataType.TEXT: propertiesQuery += `type:Sequelize.TEXT,`; break; case DataType_1.DataType.FLOAT: propertiesQuery += `type:Sequelize.FLOAT,`; break; case DataType_1.DataType.INTEGER: propertiesQuery += `type:Sequelize.INTEGER,`; break; case DataType_1.DataType.BIGINT: propertiesQuery += `type:Sequelize.BIGINT,`; break; case DataType_1.DataType.BOOL: propertiesQuery += `type:Sequelize.BOOLEAN,`; break; } } propertiesQuery += "}"; return propertiesQuery; } static getDifferences(oldMeta, newMeta) { let result = { removed: undefined, added: undefined, }; if (oldMeta === undefined) { result.added = newMeta; } else if (newMeta === undefined) { result.removed = oldMeta; } else { let removed = new ModelMetadata_1.ModelMetadata(); let added = new ModelMetadata_1.ModelMetadata(); //-- this value must be present removed.modelName = oldMeta.modelName; added.modelName = newMeta.modelName; //-- check tableName if (oldMeta.tableName !== newMeta.tableName) { removed.tableName = oldMeta.tableName; added.tableName = newMeta.tableName; } //-- check charset // if (oldMeta.charset !== newMeta.charset) { // removed.charset = oldMeta.charset; // added.charset = newMeta.charset; // } //-- check fields let oldMetaFields = oldMeta.fields; for (let i = 0; i < oldMetaFields.length; i++) { let oldField = oldMetaFields[i]; if (oldField.fieldDataName) { let matchedField = newMeta.getFieldByDataName(oldField.fieldDataName); if (!matchedField) { removed.addField(oldField); } else { let removedField = new FieldValidationMetadata_1.FieldValidationMetadata(); let addedField = new FieldValidationMetadata_1.FieldValidationMetadata(); removedField.fieldDataName = oldField.fieldDataName; addedField.fieldDataName = matchedField.fieldDataName; //-- comparing properties const properties = [ "isPrimaryKey", "isForeignKey", "isAutoIncrement", "isUnique", "referModel", "innerDataType", "nullable", "defaultValue" ]; for (const props of properties) { if (oldField[props] !== matchedField[props]) { // @ts-ignore removedField[props] = oldField[props]; // @ts-ignore addedField[props] = matchedField[props]; } } removed.addField(removedField); added.addField(addedField); } } } let newMetaFields = newMeta.fields; for (let i = 0; i < newMetaFields.length; i++) { let newField = newMetaFields[i]; if (newField.fieldDataName) { let matchedField = oldMeta.getFieldByDataName(newField.fieldDataName); if (!matchedField) { added.addField(newField); } } } result.removed = ModelMetadata_1.ModelMetadata.isNotEmptyMetadata(removed) ? removed : undefined; result.added = ModelMetadata_1.ModelMetadata.isNotEmptyMetadata(added) ? added : undefined; } return result; } static generateSchemaObject(builder, tablePrototype) { let schema; let schemaDefinition = {}; if (tablePrototype.tableName) { tablePrototype.fields.forEach((field) => { if (field.fieldDataName) { schemaDefinition[field.fieldDataName] = {}; //-- check type, prefer innerDataType if existed const dataType = field.innerDataType; switch (dataType) { case DataType_1.DataType.FLOAT: schemaDefinition[field.fieldDataName]["type"] = sequelize_1.FLOAT; break; case DataType_1.DataType.INTEGER: schemaDefinition[field.fieldDataName]["type"] = sequelize_1.INTEGER; break; case DataType_1.DataType.BIGINT: schemaDefinition[field.fieldDataName]["type"] = sequelize_1.BIGINT; break; case DataType_1.DataType.TEXT: schemaDefinition[field.fieldDataName]["type"] = sequelize_1.TEXT; break; case DataType_1.DataType.STRING: schemaDefinition[field.fieldDataName]["type"] = sequelize_1.STRING; break; case DataType_1.DataType.BOOL: schemaDefinition[field.fieldDataName]["type"] = sequelize_1.BOOLEAN; break; } //-- check default value if (!Utils_1.Utils.isNullOrUndefined(field.defaultValue) && typeof field.defaultValue !== "function") { schemaDefinition[field.fieldDataName]["defaultValue"] = field.defaultValue; } //-- check primaryKey if (field.isPrimaryKey === true) { schemaDefinition[field.fieldDataName]["primaryKey"] = true; } //-- check auto increment if (field.isAutoIncrement === true) { schemaDefinition[field.fieldDataName]["autoIncrement"] = true; } //-- check nullable schemaDefinition[field.fieldDataName]["allowNull"] = field.nullable; } }); schema = builder.define(tablePrototype.tableName, schemaDefinition, { tableName: tablePrototype.tableName }); } return schema; } createTransaction() { return __awaiter(this, void 0, void 0, function* () { let transaction = yield this.connection.transaction(); return new TransactionAdapter(transaction, this.connection, this.modelMetadata, this.schemas); }); } } DefaultSqlAdapter.SERIAL_PROMISE = ".reduce((promise, func) => promise.then((result) => func.then(Array.prototype.concat.bind(result))), Promise.resolve([]));"; DefaultSqlAdapter.COMMENT_BLOCK_REGEX_PATTERN = "/\\*(\\*(?!/)|[^*])*\\*\\/"; // private static readonly SEQUELIZE_CLI_PATH = "../../node_modules/"; /*dev-only*/ DefaultSqlAdapter.SEQUELIZE_CLI_PATH = "../../../"; __decorate([ __2.Inject(), __metadata("design:type", AbstractLogger_1.AbstractLogger) ], DefaultSqlAdapter.prototype, "logger", void 0); exports.DefaultSqlAdapter = DefaultSqlAdapter; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRGVmYXVsdFNxbEFkYXB0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvZGVmYXVsdC1pbXBsZW1lbnRhdGlvbnMvRGVmYXVsdFNxbEFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnQ0FBZ0M7QUFDaEMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO0FBRXhDLDRDQUFvQjtBQUNwQixnREFBd0I7QUFDeEIsaURBQW1DO0FBQ25DLHlDQUF1RjtBQUN2RiwyQ0FBc0M7QUFDdEMsMERBQXFEO0FBQ3JELDBEQUFxRDtBQUVyRCx1REFBa0Q7QUFDbEQsNkNBTTBCO0FBRTFCLDBEQUFvRDtBQUVwRCwwQkFBd0Q7QUFDeEQsMEJBQTBCO0FBQzFCLDZEQUF3RDtBQUN4RCxrRkFBNkU7QUFDN0UsK0NBQTBDO0FBSTFDLE1BQU0sWUFBZ0IsU0FBUSw2QkFBZ0I7SUFNMUMsWUFBWSxVQUFxQixFQUFFLEtBQTRDLEVBQUUsUUFBdUIsRUFBRSxNQUFnQyxFQUFFLFdBQWlCO1FBQ3pKLEtBQUssQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDdkIsSUFBSSxDQUFDLGFBQWEsR0FBRyxNQUFNLENBQUM7UUFDNUIsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7SUFDbkMsQ0FBQztJQUVTLDBCQUEwQixDQUFDLFVBQWUsRUFBRSxPQUtyRDtRQUNHLElBQUksT0FBTyxFQUFFO1lBQ1QsSUFBSSxPQUFPLENBQUMsS0FBSyxFQUFFO2dCQUNmLGlDQUFpQztnQkFDakMsYUFBYTtnQkFDYixNQUFNLFNBQVMsR0FBRyxhQUFLLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENB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