knex-migrator 4.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ const program = require('commander');
4
+ const utils = require('../lib/utils');
5
+
6
+ const logging = require('../logging');
7
+ let knexMigrator;
8
+
9
+ utils.getKnexMigrator({path: process.cwd()})
10
+ .then(function (KnexMigrator) {
11
+ program
12
+ .option('--mgpath <path>')
13
+ .option('--force')
14
+ .option('--v <version>', 'The version to rollback to.')
15
+ .parse(process.argv);
16
+
17
+ try {
18
+ knexMigrator = new KnexMigrator({knexMigratorFilePath: program.mgpath, executedFromShell: true});
19
+ } catch (err) {
20
+ logging.error(err);
21
+ process.exit(1);
22
+ }
23
+
24
+ return knexMigrator.rollback({force: program.force, version: program.v})
25
+ .then(function () {
26
+ logging.info('Rollback was successful.');
27
+ });
28
+ })
29
+ .catch(function (err) {
30
+ logging.error(err.message);
31
+
32
+ if (err.help) {
33
+ logging.info(err.help);
34
+ }
35
+
36
+ process.exit(1);
37
+ });
@@ -0,0 +1,204 @@
1
+ const knex = require('knex'),
2
+ Promise = require('bluebird'),
3
+ omit = require('lodash/omit'),
4
+ debug = require('debug')('knex-migrator:database'),
5
+ errors = require('./errors');
6
+
7
+ /**
8
+ * @NOTE: Knex-migrator only supports knex query builder.
9
+ *
10
+ * @param options
11
+ * @returns {Knex.QueryBuilder | Knex}
12
+ */
13
+ exports.connect = function connect(options) {
14
+ options = options || {};
15
+ const client = options.client;
16
+
17
+ if (client === 'sqlite3') {
18
+ options.useNullAsDefault = options.useNullAsDefault || false;
19
+ }
20
+
21
+ if (client === 'mysql') {
22
+ options.connection.timezone = options.connection.timezone || 'UTC';
23
+ options.connection.charset = options.connection.charset || 'utf8mb4';
24
+ options.connection.collation = options.connection.collation || 'utf8mb4_general_ci';
25
+ }
26
+
27
+ return knex(options);
28
+ };
29
+
30
+ /**
31
+ * If you instantiate knex, you won't know if the connection works.
32
+ * This helper functions is used to test the connection. It's basically a "test query".
33
+ *
34
+ * @param connection
35
+ * @returns {Bluebird<R> | Bluebird<any> | Promise<T>}
36
+ */
37
+ exports.ensureConnectionWorks = (connection) => {
38
+ return connection.raw('SELECT 1+1 as RESULT;')
39
+ .catch((err) => {
40
+ if (err.code === 'ENOTFOUND' || err.code === 'ETIMEDOUT' || err.code === 'EAI_AGAIN') {
41
+ throw new errors.DatabaseError({
42
+ message: 'Invalid database host.',
43
+ help: 'Please double check your database config.',
44
+ err: err
45
+ });
46
+ }
47
+
48
+ throw new errors.DatabaseError({
49
+ message: err.message,
50
+ help: 'Unknown database error',
51
+ err: err
52
+ });
53
+ });
54
+ };
55
+
56
+ /**
57
+ * @description Helper to create a transaction.
58
+ * @param callback
59
+ * @returns {*}
60
+ */
61
+ module.exports.createTransaction = function (connection, callback) {
62
+ return connection.transaction(callback);
63
+ };
64
+
65
+ /**
66
+ * @description Helper to create the migration table.
67
+ *
68
+ * @TODO: https://github.com/TryGhost/knex-migrator/issues/118
69
+ * @TODO: https://github.com/TryGhost/knex-migrator/issues/91
70
+ * @returns {Bluebird<R> | Bluebird<any> | * | Promise<T>}
71
+ */
72
+ exports.createMigrationsTable = function createMigrationsTable(connection) {
73
+ return connection('migrations')
74
+ .catch(function (err) {
75
+ // CASE: table does not exist
76
+ if (err.errno === 1 || err.errno === 1146) {
77
+ debug('Creating table: migrations');
78
+
79
+ return connection.schema.createTable('migrations', function (table) {
80
+ table.increments().primary();
81
+ table.string('name');
82
+ table.string('version');
83
+ table.string('currentVersion');
84
+ });
85
+ }
86
+
87
+ throw err;
88
+ });
89
+ };
90
+
91
+ /**
92
+ * Knex-migrator has an inbuilt feature to create a database if it does not exist yet.
93
+ *
94
+ * @param dbConfig
95
+ * @returns {*}
96
+ */
97
+ exports.createDatabaseIfNotExist = function createDatabaseIfNotExist(dbConfig) {
98
+ const name = dbConfig.connection.database,
99
+ charset = dbConfig.connection.charset || 'utf8mb4',
100
+ collation = dbConfig.connection.collation || 'utf8mb4_general_ci';
101
+
102
+ // @NOTE: Skip, because sqlite3 is a file based database.
103
+ if (dbConfig.client === 'sqlite3') {
104
+ return Promise.resolve();
105
+ } else if (dbConfig.client !== 'mysql') {
106
+ return Promise.reject(new errors.KnexMigrateError({
107
+ message: 'Database is not supported.'
108
+ }));
109
+ }
110
+
111
+ const connection = exports.connect({
112
+ client: dbConfig.client,
113
+ connection: omit(dbConfig.connection, ['database'])
114
+ });
115
+
116
+ debug('Create database', name);
117
+
118
+ return exports.ensureConnectionWorks(connection)
119
+ .then(function () {
120
+ return connection.raw('CREATE DATABASE `' + name + '` CHARACTER SET ' + charset + ' COLLATE ' + collation + ';');
121
+ })
122
+ .catch(function (err) {
123
+ // CASE: DB exists
124
+ if (err.errno === 1007) {
125
+ return Promise.resolve();
126
+ }
127
+
128
+ throw new errors.DatabaseError({
129
+ message: err.message,
130
+ err: err,
131
+ code: 'DATABASE_CREATION_FAILED'
132
+ });
133
+ })
134
+ .finally(function () {
135
+ return new Promise(function (resolve, reject) {
136
+ connection.destroy(function (err) {
137
+ if (err) {
138
+ return reject(err);
139
+ }
140
+
141
+ debug('Destroy connection');
142
+ resolve();
143
+ });
144
+ });
145
+ });
146
+ };
147
+
148
+ /**
149
+ * Drops a database. Is called when you call `knex-migrator reset`.
150
+ *
151
+ * @param options
152
+ * @returns {*}
153
+ */
154
+ exports.drop = function drop(options) {
155
+ options = options || {};
156
+
157
+ const connection = options.connection,
158
+ dbConfig = options.dbConfig;
159
+
160
+ if (dbConfig.client === 'mysql') {
161
+ debug('Drop database: ' + dbConfig.connection.database);
162
+
163
+ return connection.raw('DROP DATABASE `' + dbConfig.connection.database + '`;')
164
+ .catch(function (err) {
165
+ // CASE: database does not exist, skip
166
+ if (err.errno === 1049) {
167
+ return Promise.resolve();
168
+ }
169
+
170
+ return Promise.reject(new errors.KnexMigrateError({
171
+ err: err
172
+ }));
173
+ });
174
+ } else if (dbConfig.client === 'sqlite3') {
175
+ // @NOTE: sqlite3 does not support "DROP DATABASE". We have to drop each table instead.
176
+ // @NOTE: We cannot just remove the sqlite3 file, because any database connection will get invalid.
177
+ return connection.raw('SELECT name FROM sqlite_master WHERE type="table";')
178
+ .then(function (tables) {
179
+ return Promise.each(tables, function (table) {
180
+ if (table.name === 'sqlite_sequence') {
181
+ debug('Skip drop table: ' + table.name);
182
+ return Promise.resolve();
183
+ }
184
+
185
+ debug('Drop table: ' + table.name);
186
+ return connection.schema.dropTableIfExists(table.name);
187
+ });
188
+ })
189
+ .catch(function (err) {
190
+ // CASE: database file was never initialised
191
+ if (err.errno === 10) {
192
+ return Promise.resolve();
193
+ }
194
+
195
+ return Promise.reject(new errors.KnexMigrateError({
196
+ err: err
197
+ }));
198
+ });
199
+ } else {
200
+ return Promise.reject(new errors.KnexMigrateError({
201
+ message: 'Database client not supported: ' + dbConfig.client
202
+ }));
203
+ }
204
+ };
package/lib/errors.js ADDED
@@ -0,0 +1,84 @@
1
+ const util = require('util');
2
+ const each = require('lodash/each');
3
+ const errors = require('ghost-ignition').errors;
4
+
5
+ function KnexMigrateError(options) {
6
+ options = options || {};
7
+ errors.IgnitionError.call(this, options);
8
+ }
9
+
10
+ const knexMigratorErrors = {
11
+ MigrationExistsError: function MigrationExistsError(options) {
12
+ KnexMigrateError.call(this, Object.assign({
13
+ id: 100,
14
+ errorType: 'MigrationExistsError'
15
+ }, options));
16
+ },
17
+ DatabaseIsNotOkError: function DatabaseIsNotOkError(options) {
18
+ KnexMigrateError.call(this, Object.assign({
19
+ id: 200,
20
+ errorType: 'DatabaseIsNotOkError',
21
+ help: 'If knex-migrator is not installed, please run "npm install -g knex-migrator" \nRead more here: https://github.com/TryGhost/knex-migrator'
22
+ }, options));
23
+ },
24
+ MigrationScriptError: function MigrationScriptError(options) {
25
+ KnexMigrateError.call(this, Object.assign({
26
+ id: 300,
27
+ errorType: 'MigrationScriptError'
28
+ }, options));
29
+ },
30
+ RollbackError: function RollbackError(options) {
31
+ KnexMigrateError.call(this, Object.assign({
32
+ id: 400,
33
+ errorType: 'RollbackError'
34
+ }, options));
35
+ },
36
+ MigrationsAreLockedError: function MigrationsAreLockedError(options) {
37
+ KnexMigrateError.call(this, Object.assign({
38
+ id: 500,
39
+ errorType: 'MigrationsAreLockedError'
40
+ }, options));
41
+ },
42
+ LockError: function LockError(options) {
43
+ KnexMigrateError.call(this, Object.assign({
44
+ id: 500,
45
+ errorType: 'LockError'
46
+ }, options));
47
+ },
48
+ UnlockError: function UnlockError(options) {
49
+ KnexMigrateError.call(this, Object.assign({
50
+ id: 500,
51
+ errorType: 'UnlockError'
52
+ }, options));
53
+ },
54
+ DatabaseError: function DatabaseError(options) {
55
+ KnexMigrateError.call(this, Object.assign({
56
+ id: 500,
57
+ errorType: 'DatabaseError'
58
+ }, options));
59
+ },
60
+ IrreversibleMigrationError: function IrreversibleMigrationError(options) {
61
+ KnexMigrateError.call(this, Object.assign({
62
+ id: 500,
63
+ errorType: 'IrreversibleMigrationError'
64
+ }, options));
65
+ }
66
+ };
67
+
68
+ util.inherits(KnexMigrateError, errors.IgnitionError);
69
+
70
+ each(knexMigratorErrors, function (error) {
71
+ util.inherits(error, KnexMigrateError);
72
+ });
73
+
74
+ // we need to inherit all general errors from KnexMigrateError, otherwise we have to check instanceof IgnitionError
75
+ each(errors, function (error) {
76
+ if (error.name === 'IgnitionError' || typeof error === 'object') {
77
+ return;
78
+ }
79
+
80
+ util.inherits(error, KnexMigrateError);
81
+ });
82
+
83
+ module.exports = Object.assign(knexMigratorErrors, errors);
84
+ module.exports.KnexMigrateError = KnexMigrateError;