From 2f5a19519edaff595e2d7877c6ec283176d2b87f Mon Sep 17 00:00:00 2001 From: Thomas Kugi Date: Tue, 12 Jan 2021 19:08:34 +0100 Subject: [PATCH 1/6] saving state to migration, so that every dev can start using this tool without setup fixed a bug with integers in mysql removed validate from attributes to save added supported JSONTYPE, DOUBLE added correct escape of postfix for blob added usage description --- README.md | 12 + package-lock.json | 2 +- package.json | 14 +- src/index.ts | 2 +- src/utils/getLastMigrationState.ts | 2 +- src/utils/getTablesFromModels.ts | 2 +- src/utils/reverseSequelizeColType.ts | 345 ++++++++++++++------------- src/utils/writeMigration.ts | 123 +++++++--- 8 files changed, 291 insertions(+), 211 deletions(-) diff --git a/README.md b/README.md index 427aed8..008f3d8 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,18 @@ module.exports = { then you can apply this `npx sequelize db:migrate --to 00000001-noname.js` +## Possible Usage Scenario +Make sure to have writeMigration in your System under development and that sequelize is all set up + +If you change a model and re-run the backend there should be a new file under `db/migrations`, but the database +won't update automatically. There are easy but important steps: +1) Rename the file's name as well as the content (Info: name), so that everyone knows what this migration is about +2) Migrate your database `sequelize db:migrate` +3) Re-Serve the backend. You Should see 'No changes found'. +4) Test the automatically created file's down function `sequelize db:migrate:undo` +5) If there are any troubles, fix the auto-generated file (ordering!) +6) Run `sequelize db:migrate:undo` and continue your amazing work + ## Documentation not ready yet. diff --git a/package-lock.json b/package-lock.json index 349c190..c4472ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "sequelize-typescript-migration", - "version": "0.0.1-beta.2", + "version": "0.0.1-beta.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6664be5..6e1ddb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "sequelize-typescript-migration", - "version": "0.0.1-beta.3", + "name": "sequelize-typescript-migration-v2", + "version": "0.0.2-beta.3", "description": "migration tool for sequelize & typescript users", "keywords": [ "sequelize", @@ -9,10 +9,10 @@ "migration", "makemigration" ], - "homepage": "https://github.com/kimjbstar/sequelize-typescript-migration", + "homepage": "https://github.com/syon-development/sequelize-typescript-migration", "bugs": { - "url": "https://github.com/kimjbstar/sequelize-typescript-migration/issues", - "email": "kimjbstar@gmail.com" + "url": "https://github.com/syon-development/sequelize-typescript-migrationissues", + "email": "thomas.kugi@syon.at" }, "main": "dist/index.js", "scripts": { @@ -25,8 +25,8 @@ "url": "git://github.com/kimjbstar/sequelize-typescript-migration.git" }, "author": { - "name": "kimjbstar", - "email": "kimjbstar@gmail.com" + "name": "Thomas Kugi", + "email": "thomas.kugi@syon.at" }, "license": "MIT", "dependencies": { diff --git a/src/index.ts b/src/index.ts index 3b4b0f9..19701e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -105,7 +105,7 @@ export class SequelizeTypescriptMigration { } const info = await writeMigration( - currentState.revision, + currentState, migration, options ); diff --git a/src/utils/getLastMigrationState.ts b/src/utils/getLastMigrationState.ts index 74692a5..03ea85d 100644 --- a/src/utils/getLastMigrationState.ts +++ b/src/utils/getLastMigrationState.ts @@ -9,7 +9,7 @@ export default async function getLastMigrationState(sequelize: Sequelize) { const lastRevision: number = lastExecutedMigration !== undefined - ? lastExecutedMigration["name"].split("-")[0] + ? parseInt(lastExecutedMigration["name"].split("-")[0]) : -1; const [ diff --git a/src/utils/getTablesFromModels.ts b/src/utils/getTablesFromModels.ts index 94f2926..2dc12e8 100644 --- a/src/utils/getTablesFromModels.ts +++ b/src/utils/getTablesFromModels.ts @@ -64,7 +64,7 @@ export default function reverseModels( "references", "onUpdate", "onDelete", - "validate", + //"validate", ].forEach(key => { if (attribute[key] !== undefined) { rowAttribute[key] = attribute[key]; diff --git a/src/utils/reverseSequelizeColType.ts b/src/utils/reverseSequelizeColType.ts index 74927b9..bc56bdf 100644 --- a/src/utils/reverseSequelizeColType.ts +++ b/src/utils/reverseSequelizeColType.ts @@ -1,8 +1,5 @@ -import { - DataType as SequelizeTypescriptDataType, - Sequelize, -} from "sequelize-typescript"; -import { DataType, NUMBER, STRING, DATE, INTEGER } from "sequelize"; +import {Sequelize,} from "sequelize-typescript"; +import {DataType} from "sequelize"; // interface IDataType { // key: string; @@ -26,182 +23,188 @@ import { DataType, NUMBER, STRING, DATE, INTEGER } from "sequelize"; // } export default function reverseSequelizeColType( - sequelize: Sequelize, - attrType: any, - prefix = "Sequelize." + sequelize: Sequelize, + attrType: any, + prefix = "Sequelize." ) { - if (attrType.constructor.name === "VIRTUAL") { - return `${prefix}VIRTUAL`; - } + if (attrType.constructor.name === "VIRTUAL") { + return `${prefix}VIRTUAL`; + } + + if (attrType.constructor.name === "CHAR") { + if (!attrType.options) { + return `${prefix}CHAR`; + } + const postfix = attrType.options.binary ? ".BINARY" : ""; + return `${prefix}CHAR${postfix}`; + } + + if (attrType.constructor.name === "STRING") { + if (attrType.options === undefined) { + return `${prefix}STRING`; + } + + if (attrType.options.binary !== undefined) { + return `${prefix}STRING.BINARY`; + } + const length = + attrType.options.length !== undefined + ? `(${attrType.options.length})` + : ""; + return `${prefix}STRING${length}`; + } - if (attrType.constructor.name === "CHAR") { - if (!attrType.options) { - return `${prefix}CHAR`; + if (attrType.constructor.name === "TEXT") { + if (!attrType.options.length) { + return `${prefix}TEXT`; + } + const postfix = `('${attrType.options.length.toLowerCase()}')`; + return `${prefix}TEXT(${postfix})`; } - const postfix = attrType.options.binary ? ".BINARY" : ""; - return `${prefix}CHAR${postfix}`; - } - if (attrType.constructor.name === "STRING") { - if (attrType.options === undefined) { - return `${prefix}STRING`; + if (attrType.constructor.name === "DECIMAL") { + const params = []; + + if (attrType.options.precision) { + params.push(attrType.options.precision); + } + if (attrType.options.scale) { + params.push(attrType.options.scale); + } + const postfix = params.length > 0 ? `(${params.join(",")})` : ""; + return `${prefix}DECIMAL${postfix}`; } - if (attrType.options.binary !== undefined) { - return `${prefix}STRING.BINARY`; + if ( + ["TINYINT", "SMALLINT", "MEDIUMINT", "INTEGER", "BIGINT"].indexOf( + attrType.constructor.name + ) >= 0 + ) { + const params = []; + + if (attrType.options.length) { + params.push(attrType.options.length); + } + if (attrType.options.decimals) { + params.push(attrType.options.decimals); + } + let postfix = params.length > 0 ? `(${params.join(",")})` : ""; + + if (attrType.options.zerofill) { + postfix += ".ZEROFILL"; + } + + if (attrType.options.unsigned) { + postfix += ".UNSIGNED"; + } + + return `${prefix}${attrType.key}${postfix}`; } - const length = - attrType.options.length !== undefined - ? `(${attrType.options.length})` - : ""; - return `${prefix}STRING${length}`; - } - if (attrType.constructor.name === "TEXT") { - if (!attrType.options.length) { - return `${prefix}TEXT`; + if (attrType.constructor.name === "DATE") { + const length = attrType.options.length + ? `(${attrType.options.length})` + : ""; + return `${prefix}DATE${length}`; + } + + if (attrType.constructor.name === "DATEONLY") { + return `${prefix}DATEONLY`; + } + if (attrType.constructor.name === "JSONTYPE") { + return `${prefix}JSON`; } - const postfix = `('${attrType.options.length.toLowerCase()}')`; - return `${prefix}TEXT(${postfix})`; - } - if (attrType.constructor.name === "DECIMAL") { - const params = []; - if (attrType.options.precision) { - params.push(attrType.options.precision); + if (attrType.constructor.name === "BLOB") { + const postfix = `'${attrType.options.length.toLowerCase()}'`; + return `${prefix}BLOB(${postfix})`; } - if (attrType.options.scale) { - params.push(attrType.options.scale); - } - const postfix = params.length > 0 ? `(${params.join(",")})` : ""; - return `${prefix}DECIMAL${postfix}`; - } - - if ( - ["TINYINT", "SMALLINT", "MEDIUMINT", "INTEGER", "BIGINT"].indexOf( - attrType.constructor.name - ) >= 0 - ) { - const params = []; - - if (attrType.options.length) { - params.push(attrType.options.length); - } - if (attrType.options.decimals) { - params.push(attrType.options.decimals); - } - let postfix = params.length > 0 ? `(${params.join(",")})` : ""; - - if (attrType.options.zerofill) { - postfix += ".ZEROFILL"; - } - - if (attrType.options.unsigned) { - postfix += ".UNSIGNED"; - } - - return `${prefix}${attrType.key}${postfix}`; - } - - if (attrType.constructor.name === "DATE") { - const length = attrType.options.length - ? `(${attrType.options.length})` - : ""; - return `${prefix}DATE${length}`; - } - - if (attrType.constructor.name === "DATEONLY") { - return `${prefix}DATEONLY`; - } - - if (attrType.constructor.name === "BLOB") { - const postfix = `(${attrType.options.length.toLowerCase()})`; - return `${prefix}BLOB(${postfix})`; - } - - if (attrType.constructor.name === "ENUM") { - return `${prefix}ENUM('${attrType.options.values.join("', '")}')`; - } - - if (attrType.constructor.name === "GEOMETRY") { - if (attrType.options.type == undefined) { - return `${prefix}GEOMETRY`; - } - const type = attrType.options.type.toUpperCase(); - const srid = attrType.options.srid; - const postfixItems = [`'${type}'`]; - if (srid !== undefined) { - postfixItems.push(attrType.options.srid.toString()); - } - return `${prefix}GEOMETRY(${postfixItems.join(",")})`; - } - - if (attrType.constructor.name === "GEOGRAPHY") { - if (attrType.options.type == undefined) { - return `${prefix}GEOGRAPHY`; - } - const type = attrType.options.type.toUpperCase(); - const srid = attrType.options.srid; - const postfixItems = [`'${type}'`]; - if (srid !== undefined) { - postfixItems.push(attrType.options.srid.toString()); - } - return `${prefix}GEOGRAPHY(${postfixItems.join(",")})`; - } - - // ARRAY ( PostgreSQL only ) - if (attrType.constructor.name === "ARRAY") { - const type: DataType = attrType.options.type; - const innerType = reverseSequelizeColType(sequelize, attrType); - return `${prefix}ARRAY(${innerType})`; - } - - // RANGE ( PostgreSQL only ) - if (attrType.constructor.name === "RANGE") { - const type: DataType = attrType.options.subtype; - const innerType = reverseSequelizeColType(sequelize, attrType); - return `${prefix}RANGE(${innerType})`; - } - - let seqType; - [ - "BOOLEAN", - "TIME", - "HSTORE", - "JSON", - "JSONB", - "NOW", - "UUID", - "UUIDV1", - "UUIDV4", - "CIDR", - "INET", - "MACADDR", - "CITEXT", - ].forEach(typeName => { - if (attrType.constructor.name === typeName) { - seqType = `${prefix}${typeName}`; - } - }); - if (seqType) { - return seqType; - } - - // not supported - console.log("not supported ..." + attrType.constructor.name); - return `${prefix}VIRTUAL`; - // handle function - // if ( - // attrType.options !== undefined && - // typeof attrType.options.toString === "function" - // ) { - // seqType = attrType.options.toString(sequelize); - // } - - // if (typeof type.toString === "function") { - // seqType = type.toString(sequelize); - // } - - // return seqType; + + if (attrType.constructor.name === "ENUM") { + return `${prefix}ENUM('${attrType.options.values.join("', '")}')`; + } + + if (attrType.constructor.name === "GEOMETRY") { + if (attrType.options.type == undefined) { + return `${prefix}GEOMETRY`; + } + const type = attrType.options.type.toUpperCase(); + const srid = attrType.options.srid; + const postfixItems = [`'${type}'`]; + if (srid !== undefined) { + postfixItems.push(attrType.options.srid.toString()); + } + return `${prefix}GEOMETRY(${postfixItems.join(",")})`; + } + + if (attrType.constructor.name === "GEOGRAPHY") { + if (attrType.options.type == undefined) { + return `${prefix}GEOGRAPHY`; + } + const type = attrType.options.type.toUpperCase(); + const srid = attrType.options.srid; + const postfixItems = [`'${type}'`]; + if (srid !== undefined) { + postfixItems.push(attrType.options.srid.toString()); + } + return `${prefix}GEOGRAPHY(${postfixItems.join(",")})`; + } + + // ARRAY ( PostgreSQL only ) + if (attrType.constructor.name === "ARRAY") { + const type: DataType = attrType.options.type; + const innerType = reverseSequelizeColType(sequelize, attrType); + return `${prefix}ARRAY(${innerType})`; + } + + // RANGE ( PostgreSQL only ) + if (attrType.constructor.name === "RANGE") { + const type: DataType = attrType.options.subtype; + const innerType = reverseSequelizeColType(sequelize, attrType); + return `${prefix}RANGE(${innerType})`; + } + + let seqType; + [ + "BOOLEAN", + "TIME", + "HSTORE", + "JSON", + "JSONB", + "NOW", + "UUID", + "UUIDV1", + "UUIDV4", + "JSONTYPE", + "DOUBLE", + "CIDR", + "INET", + "MACADDR", + "CITEXT", + ].forEach(typeName => { + if (attrType.constructor.name === typeName) { + seqType = `${prefix}${typeName}`; + } + }); + if (seqType) { + return seqType; + } + + // not supported + console.log("not supported ..." + attrType.constructor.name); + return `${prefix}VIRTUAL`; + // handle function + // if ( + // attrType.options !== undefined && + // typeof attrType.options.toString === "function" + // ) { + // seqType = attrType.options.toString(sequelize); + // } + + // if (typeof type.toString === "function") { + // seqType = type.toString(sequelize); + // } + + // return seqType; } diff --git a/src/utils/writeMigration.ts b/src/utils/writeMigration.ts index 45cd494..ab8024b 100644 --- a/src/utils/writeMigration.ts +++ b/src/utils/writeMigration.ts @@ -2,31 +2,96 @@ import * as beautify from "js-beautify"; import * as fs from "fs"; import * as path from "path"; import removeCurrentRevisionMigrations from "./removeCurrentRevisionMigrations"; -export default async function writeMigration(revision, migration, options) { - await removeCurrentRevisionMigrations(revision, options.outDir, options); - const name = options.migrationName || "noname"; - const comment = options.comment || ""; - let commands = `var migrationCommands = [ \n${migration.commandsUp.join( - ", \n" - )} \n];\n`; - let commandsDown = `var rollbackCommands = [ \n${migration.commandsDown.join( - ", \n" - )} \n];\n`; +export default async function writeMigration(currentState, migration, options) { + await removeCurrentRevisionMigrations(currentState.revision, options.outDir, options); - const actions = ` * ${migration.consoleOut.join("\n * ")}`; + const name = options.migrationName || "noname"; + const comment = options.comment || ""; - commands = beautify(commands); - commandsDown = beautify(commandsDown); - const info = { - revision, - name, - created: new Date(), - comment, - }; + let myState = JSON.stringify(currentState); + const searchRegExp = /'/g; + const replaceWith = "\\'"; + myState = myState.replace(searchRegExp, replaceWith); - const template = `'use strict'; + const versionCommands = ` + { + fn: "createTable", + params: [ + "SequelizeMetaMigrations", + { + "revision": { + "primaryKey": true, + "type": Sequelize.UUID + }, + "name": { + "allowNull": false, + "type": Sequelize.STRING + }, + "state": { + "allowNull": false, + "type": Sequelize.JSON + }, + }, + {} + ] + }, + { + fn: "bulkDelete", + params: [ + "SequelizeMetaMigrations", + [{ + revision: info.revision + }], + {} + ] + }, + { + fn: "bulkInsert", + params: [ + "SequelizeMetaMigrations", + [{ + revision: info.revision, + name: info.name, + state: '${myState}' + }], + {} + ] + }, + ` + + const versionDownCommands = ` + { + fn: "bulkDelete", + params: [ + "SequelizeMetaMigrations", + [{ + revision: info.revision, + }], + {} + ] + }, + `; + + + let commands = `var migrationCommands = [\n${versionCommands}\n\n \n${migration.commandsUp.join(", \n")} \n];\n`; + let commandsDown = `var rollbackCommands = [\n${versionDownCommands}\n\n \n${migration.commandsDown.join(", \n")} \n];\n`; + + + const actions = ` * ${migration.consoleOut.join("\n * ")}`; + + commands = beautify(commands); + commandsDown = beautify(commandsDown); + + const info = { + revision: currentState.revision, + name, + created: new Date(), + comment, + }; + + const template = `'use strict'; var Sequelize = require('sequelize'); @@ -85,16 +150,16 @@ module.exports = { }; `; - const revisionNumber = revision.toString().padStart(8, "0"); + const revisionNumber = currentState.revision.toString().padStart(8, "0"); - const filename = path.join( - options.outDir, - `${ - revisionNumber + (name !== "" ? `-${name.replace(/[\s-]/g, "_")}` : "") - }.js` - ); + const filename = path.join( + options.outDir, + `${ + revisionNumber + (name !== "" ? `-${name.replace(/[\s-]/g, "_")}` : "") + }.js` + ); - fs.writeFileSync(filename, template); + fs.writeFileSync(filename, template); - return { filename, info, revisionNumber }; + return {filename, info, revisionNumber}; } From 21c93d348ae9d8c2482d202d287e3d527d99938a Mon Sep 17 00:00:00 2001 From: Thomas Kugi Date: Tue, 12 Jan 2021 19:51:53 +0100 Subject: [PATCH 2/6] fixed package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6e1ddb3..2418b1e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ ], "homepage": "https://github.com/syon-development/sequelize-typescript-migration", "bugs": { - "url": "https://github.com/syon-development/sequelize-typescript-migrationissues", + "url": "https://github.com/syon-development/sequelize-typescript-migration/issues", "email": "thomas.kugi@syon.at" }, "main": "dist/index.js", @@ -22,7 +22,7 @@ "bin": "./dist/index.js", "repository": { "type": "git", - "url": "git://github.com/kimjbstar/sequelize-typescript-migration.git" + "url": "git://github.com/syon-development/sequelize-typescript-migration" }, "author": { "name": "Thomas Kugi", From 36e663c6df2123505c982fa9e619c18350d8053f Mon Sep 17 00:00:00 2001 From: Thomas Kugi Date: Wed, 13 Jan 2021 19:03:34 +0100 Subject: [PATCH 3/6] return success if no migrations are generated in order to allow something like: await writeMigration(); in dev codebase --- package.json | 3 +- src/index.ts | 253 +++++++++++++++++++++++++-------------------------- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index 2418b1e..02ec3b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sequelize-typescript-migration-v2", - "version": "0.0.2-beta.3", + "version": "0.0.2-beta.4", "description": "migration tool for sequelize & typescript users", "keywords": [ "sequelize", @@ -17,6 +17,7 @@ "main": "dist/index.js", "scripts": { "build": "tsc", + "prepare": "tsc", "start": "node dist/index.js" }, "bin": "./dist/index.js", diff --git a/src/index.ts b/src/index.ts index 19701e0..1df3a6d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ -import { Sequelize } from "sequelize-typescript"; +import {Sequelize} from "sequelize-typescript"; import * as beautify from "js-beautify"; import * as fs from "fs"; -import { IMigrationState } from "./constants"; -import { ModelCtor, Model, QueryInterface } from "sequelize/types"; +import {IMigrationState} from "./constants"; +import {Model, ModelCtor, QueryInterface} from "sequelize/types"; import getTablesFromModels from "./utils/getTablesFromModels"; import getDiffActionsFromTables from "./utils/getDiffActionsFromTables"; import getMigration from "./utils/getMigration"; @@ -11,134 +11,133 @@ import getLastMigrationState from "./utils/getLastMigrationState"; import writeMigration from "./utils/writeMigration"; export interface IMigrationOptions { - /** - * directory where migration file saved. We recommend that you specify this path to sequelize migration path. - */ - outDir: string; - /** - * if true, it doesn't generate files but just prints result action. - */ - preview?: boolean; - /** - * migration file name, default is "noname" - */ - migrationName?: string; - /** - * comment of migration. - */ - comment?: string; - debug?: boolean; + /** + * directory where migration file saved. We recommend that you specify this path to sequelize migration path. + */ + outDir: string; + /** + * if true, it doesn't generate files but just prints result action. + */ + preview?: boolean; + /** + * migration file name, default is "noname" + */ + migrationName?: string; + /** + * comment of migration. + */ + comment?: string; + debug?: boolean; } export class SequelizeTypescriptMigration { - /** - * generates migration file including up, down code - * after this, run 'npx sequelize-cli db:migrate'. - * @param sequelize sequelize-typescript instance - * @param options options - */ - public static makeMigration = async ( - sequelize: Sequelize, - options: IMigrationOptions - ) => { - options.preview = options.preview || false; - if (fs.existsSync(options.outDir) === false) { - return Promise.reject({ - msg: `${options.outDir} not exists. check path and if you did 'npx sequelize init' you must use path used in sequelize migration path`, - }); - } - await sequelize.authenticate(); - - const models: { - [key: string]: ModelCtor; - } = sequelize.models; - - const queryInterface: QueryInterface = sequelize.getQueryInterface(); - - await createMigrationTable(sequelize); - const lastMigrationState = await getLastMigrationState(sequelize); - - const previousState: IMigrationState = { - revision: - lastMigrationState !== undefined ? lastMigrationState["revision"] : 0, - version: - lastMigrationState !== undefined ? lastMigrationState["version"] : 1, - tables: - lastMigrationState !== undefined ? lastMigrationState["tables"] : {}, - }; - const currentState: IMigrationState = { - revision: previousState.revision + 1, - tables: getTablesFromModels(sequelize, models), - }; - - const upActions = getDiffActionsFromTables( - previousState.tables, - currentState.tables - ); - const downActions = getDiffActionsFromTables( - currentState.tables, - previousState.tables - ); - - const migration = getMigration(upActions); - const tmp = getMigration(downActions); - - migration.commandsDown = tmp.commandsUp; - - if (migration.commandsUp.length === 0) { - console.log("No changes found"); - process.exit(0); - } - - // log - migration.consoleOut.forEach(v => { - console.log(`[Actions] ${v}`); - }); - if (options.preview) { - console.log("Migration result:"); - console.log(beautify(`[ \n${migration.commandsUp.join(", \n")} \n];\n`)); - console.log("Undo commands:"); - console.log( - beautify(`[ \n${migration.commandsDown.join(", \n")} \n];\n`) - ); - return Promise.resolve({ msg: "success without save" }); - } - - const info = await writeMigration( - currentState, - migration, - options - ); - - console.log( - `New migration to revision ${currentState.revision} has been saved to file '${info.filename}'` - ); - - // save current state, Ugly hack, see https://github.com/sequelize/sequelize/issues/8310 - const rows = [ - { - revision: currentState.revision, - name: info.info.name, - state: JSON.stringify(currentState), - }, - ]; - - try { - await queryInterface.bulkDelete("SequelizeMetaMigrations", { - revision: currentState.revision, - }); - await queryInterface.bulkInsert("SequelizeMetaMigrations", rows); - - console.log(`Use sequelize CLI: + /** + * generates migration file including up, down code + * after this, run 'npx sequelize-cli db:migrate'. + * @param sequelize sequelize-typescript instance + * @param options options + */ + public static makeMigration = async ( + sequelize: Sequelize, + options: IMigrationOptions + ) => { + options.preview = options.preview || false; + if (fs.existsSync(options.outDir) === false) { + return Promise.reject({ + msg: `${options.outDir} not exists. check path and if you did 'npx sequelize init' you must use path used in sequelize migration path`, + }); + } + await sequelize.authenticate(); + + const models: { + [key: string]: ModelCtor; + } = sequelize.models; + + const queryInterface: QueryInterface = sequelize.getQueryInterface(); + + await createMigrationTable(sequelize); + const lastMigrationState = await getLastMigrationState(sequelize); + + const previousState: IMigrationState = { + revision: + lastMigrationState !== undefined ? lastMigrationState["revision"] : 0, + version: + lastMigrationState !== undefined ? lastMigrationState["version"] : 1, + tables: + lastMigrationState !== undefined ? lastMigrationState["tables"] : {}, + }; + const currentState: IMigrationState = { + revision: previousState.revision + 1, + tables: getTablesFromModels(sequelize, models), + }; + + const upActions = getDiffActionsFromTables( + previousState.tables, + currentState.tables + ); + const downActions = getDiffActionsFromTables( + currentState.tables, + previousState.tables + ); + + const migration = getMigration(upActions); + const tmp = getMigration(downActions); + + migration.commandsDown = tmp.commandsUp; + + if (migration.commandsUp.length === 0) { + return Promise.resolve({msg: "success: no changes found"}); + } + + // log + migration.consoleOut.forEach(v => { + console.log(`[Actions] ${v}`); + }); + if (options.preview) { + console.log("Migration result:"); + console.log(beautify(`[ \n${migration.commandsUp.join(", \n")} \n];\n`)); + console.log("Undo commands:"); + console.log( + beautify(`[ \n${migration.commandsDown.join(", \n")} \n];\n`) + ); + return Promise.resolve({msg: "success without save"}); + } + + const info = await writeMigration( + currentState, + migration, + options + ); + + console.log( + `New migration to revision ${currentState.revision} has been saved to file '${info.filename}'` + ); + + // save current state, Ugly hack, see https://github.com/sequelize/sequelize/issues/8310 + const rows = [ + { + revision: currentState.revision, + name: info.info.name, + state: JSON.stringify(currentState), + }, + ]; + + try { + await queryInterface.bulkDelete("SequelizeMetaMigrations", { + revision: currentState.revision, + }); + await queryInterface.bulkInsert("SequelizeMetaMigrations", rows); + + console.log(`Use sequelize CLI: npx sequelize db:migrate --to ${info.revisionNumber}-${ - info.info.name - }.js ${`--migrations-path=${options.outDir}`} `); + info.info.name + }.js ${`--migrations-path=${options.outDir}`} `); - return Promise.resolve({ msg: "success" }); - } catch (err) { - if (options.debug) console.error(err); - } + return Promise.resolve({msg: "success"}); + } catch (err) { + if (options.debug) console.error(err); + } - return Promise.resolve({ msg: "success anyway.." }); - }; + return Promise.resolve({msg: "success anyway.."}); + }; } From af24038f50385e5bcdd8cd5bc5a86499293ac7dc Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 21 Apr 2021 01:25:25 -0300 Subject: [PATCH 4/6] fix bug on execute query get last --- src/utils/getLastMigrationState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/getLastMigrationState.ts b/src/utils/getLastMigrationState.ts index 03ea85d..bca3c8c 100644 --- a/src/utils/getLastMigrationState.ts +++ b/src/utils/getLastMigrationState.ts @@ -3,7 +3,7 @@ export default async function getLastMigrationState(sequelize: Sequelize) { const [ lastExecutedMigration, ] = await sequelize.query( - "SELECT name FROM SequelizeMeta ORDER BY name desc limit 1", + `SELECT name FROM "SequelizeMeta" ORDER BY name desc limit 1`, { type: "SELECT" } ); From 1589f5631b4b2311707656d3f883c2175a22f4cb Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 21 Apr 2021 01:31:03 -0300 Subject: [PATCH 5/6] fix another query --- src/utils/getLastMigrationState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/getLastMigrationState.ts b/src/utils/getLastMigrationState.ts index bca3c8c..1f7c182 100644 --- a/src/utils/getLastMigrationState.ts +++ b/src/utils/getLastMigrationState.ts @@ -15,7 +15,7 @@ export default async function getLastMigrationState(sequelize: Sequelize) { const [ lastMigration, ] = await sequelize.query( - `SELECT state FROM SequelizeMetaMigrations where revision = '${lastRevision}'`, + `SELECT state FROM "SequelizeMetaMigrations" where revision = '${lastRevision}'`, { type: "SELECT" } ); return lastMigration ? lastMigration["state"] : undefined; From f22a909452e9d5b02f224c5609bd3be2631f0287 Mon Sep 17 00:00:00 2001 From: danielborbavareladossantos Date: Wed, 21 Jun 2023 11:05:18 -0300 Subject: [PATCH 6/6] =?UTF-8?q?corre=C3=A7=C3=A3o=20bluebird=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02ec3b4..637dcd0 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "typescript": "^3.8.3" }, "devDependencies": { - "@types/bluebird": "^3.5.30", + "@types/bluebird": "3.5.5", "@types/deep-diff": "^1.0.0", "@types/inflection": "^1.5.28", "@types/js-beautify": "^1.8.2",