larvitdbmigration
Advanced tools
Comparing version 1.3.3 to 2.0.0
255
index.js
'use strict'; | ||
const async = require('async'), | ||
mysql = require('mysql2'), | ||
const jsonStringify = require('json-stringify-safe'), | ||
topLogPrefix = 'larvitdbmigration: index.js - ', | ||
log = require('winston'), | ||
fs = require('fs'), | ||
db = require('larvitdb'), | ||
_ = require('lodash'); | ||
fs = require('fs'); | ||
exports = module.exports = function(options) { | ||
options = options || {}; | ||
function DbMigration(options) { | ||
const logPrefix = topLogPrefix + 'DbMigration() - ', | ||
that = this; | ||
that.options = options = options || {}; | ||
if (options.tableName === undefined) options.tableName = 'db_version'; | ||
if (options.migrationScriptsPath === undefined) options.migrationScriptsPath = './dbmigration'; | ||
log.verbose('larvitdbmigration: Started with options: ' + JSON.stringify(options)); | ||
// Resolve ./ paths to be relative to application path | ||
if (options.migrationScriptsPath.substring(0, 2) === './') { | ||
options.migrationScriptsPath = process.cwd() + '/' + options.migrationScriptsPath.substring(2); | ||
if (options.dbType === undefined) { | ||
throw new Error('Missing options.dbType'); | ||
} | ||
function getLock(cb) { | ||
try { | ||
const tasks = []; | ||
that.dbTypeFile = __dirname + '/dbType/' + options.dbType + '.js'; | ||
let dbCon; | ||
if ( ! fs.existsSync(that.dbTypeFile)) { | ||
throw new Error('Invalid options.dbType "' + options.dbType + '", missing file: "' + dbTypeFile + '"'); | ||
} | ||
tasks.push(function(cb) { | ||
db.pool.getConnection(function(err, res) { | ||
if (err) { | ||
log.error('larvitdbmigration: getLock() - getConnection() err: ' + err.message); | ||
} | ||
if (options.dbType !== 'larvitdb' && options.dbType !== 'elasticsearch') { | ||
throw new Error('Invalid options.dbType: "' + options.dbType + '"'); | ||
} | ||
dbCon = res; | ||
cb(err); | ||
}); | ||
}); | ||
if (options.dbDriver === undefined) { | ||
throw new Error('Missing options.dbDriver'); | ||
} | ||
tasks.push(function(cb) { | ||
dbCon.query('LOCK TABLES `' + options.tableName + '` WRITE;', cb); | ||
}); | ||
log.verbose(logPrefix + 'Started with options: ' + jsonStringify(options)); | ||
tasks.push(function(cb) { | ||
dbCon.query('SELECT running FROM `' + options.tableName + '`', function(err, rows) { | ||
if (err) { | ||
log.error('larvitdbmigration: getLock() - SQL err: ' + err.message); | ||
cb(err); | ||
return; | ||
} | ||
if (rows.length === 0) { | ||
const err = 'No database records in ' + options.tableName; | ||
log.error('larvitdbmigration: getLock() - ' + err.message); | ||
cb(err); | ||
return; | ||
} | ||
if (rows[0].running === 0) { | ||
cb(); | ||
} else { | ||
dbCon.query('UNLOCK TABLES;', function(err) { | ||
if (err) { | ||
log.error('larvitdbmigration: getLock() - SQL err: ' + err.message); | ||
cb(err); | ||
return; | ||
} | ||
log.info('larvitdbmigration: getLock() - Another process is running the migrations for table ' + options.tableName + ', wait and try again soon.'); | ||
setTimeout(function() { | ||
getLock(cb); | ||
}, 500); | ||
}); | ||
} | ||
}); | ||
}); | ||
tasks.push(function(cb) { | ||
dbCon.query('UPDATE `' + options.tableName + '` SET running = 1', cb); | ||
}); | ||
tasks.push(function(cb) { | ||
dbCon.query('UNLOCK TABLES;', cb); | ||
}); | ||
tasks.push(function(cb) { | ||
dbCon.release(); | ||
cb(); | ||
}); | ||
async.series(tasks, cb); | ||
} catch(err) { | ||
log.error('larvitdbmigration: getLock() - Error from driver: ' + err.message); | ||
cb(err); | ||
} | ||
// Resolve ./ paths to be relative to application path | ||
if (options.migrationScriptsPath.substring(0, 2) === './') { | ||
options.migrationScriptsPath = process.cwd() + '/' + options.migrationScriptsPath.substring(2); | ||
} | ||
return function(cb) { | ||
const tasks = []; | ||
// Set functions from dbDriver | ||
that.getLock = require(that.dbTypeFile).getLock; | ||
that.rmLock = require(that.dbTypeFile).rmLock; | ||
that.run = require(that.dbTypeFile).run; | ||
that.runScripts = require(that.dbTypeFile).runScripts; | ||
} | ||
let curVer; | ||
function runScripts(startVersion, cb) { | ||
log.verbose('larvitdbmigration: runScripts() - Started with startVersion: "' + startVersion + '" in path: "' + options.migrationScriptsPath + '" for table ' + options.tableName); | ||
try { | ||
fs.readdir(options.migrationScriptsPath, function(err, items) { | ||
const sql = 'UPDATE `' + options.tableName + '` SET version = ' + parseInt(startVersion) + ';'; | ||
let localDbConf; | ||
if (err) { | ||
log.info('larvitdbmigration: runScripts() - Could not read migration script path "' + options.migrationScriptsPath + '"'); | ||
cb(); | ||
return; | ||
} | ||
for (let i = 0; items[i] !== undefined; i ++) { | ||
if (items[i] === startVersion + '.js') { | ||
log.info('larvitdbmigration: runScripts() - Found js migration script #' + startVersion + ' for table ' + options.tableName + ', running it now.'); | ||
require(options.migrationScriptsPath + '/' + startVersion + '.js')(function(err) { | ||
if (err) { | ||
log.error('larvitdbmigration: runScripts() - Got error running migration script ' + options.migrationScriptsPath + '/' + startVersion + '.js' + ': ' + err.message); | ||
cb(err); | ||
return; | ||
} | ||
log.debug('larvitdbmigration: runScripts() - Js migration script #' + startVersion + ' for table ' + options.tableName + ' ran. Updating database version and moving on.'); | ||
db.query(sql, function(err) { | ||
if (err) { cb(err); return; } | ||
runScripts(parseInt(startVersion) + 1, cb); | ||
}); | ||
}); | ||
return; | ||
} else if (items[i] === startVersion + '.sql') { | ||
let dbCon; | ||
log.info('larvitdbmigration: runScripts() - Found sql migration script #' + startVersion + ' for table ' + options.tableName + ', running it now.'); | ||
localDbConf = _.cloneDeep(db.conf); | ||
localDbConf.multipleStatements = true; | ||
dbCon = mysql.createConnection(localDbConf); | ||
dbCon.query(fs.readFileSync(options.migrationScriptsPath + '/' + items[i]).toString(), function(err) { | ||
if (err) { | ||
log.error('larvitdbmigration: Migration file: ' + items[i] + ' SQL error: ' + err.message); | ||
cb(err); | ||
return; | ||
} | ||
log.info('larvitdbmigration: runScripts() - Sql migration script #' + startVersion + ' for table ' + options.tableName + ' ran. Updating database version and moving on.'); | ||
db.query(sql, function(err) { | ||
if (err) { cb(err); return; } | ||
dbCon.end(); | ||
runScripts(parseInt(startVersion) + 1, cb); | ||
}); | ||
}); | ||
return; | ||
} | ||
} | ||
log.info('larvitdbmigration: runScripts() - Database migrated and done. Final version is ' + (startVersion - 1) + ' in table ' + options.tableName); | ||
// If we end up here, it means there are no more migration scripts to run | ||
cb(); | ||
}); | ||
} catch(err) { | ||
log.error('larvitdbmigration: runScripts() - Uncaught error: ' + err.message); | ||
cb(err); | ||
} | ||
} | ||
// Create table if it does not exist | ||
tasks.push(function(cb) { | ||
const sql = 'CREATE TABLE IF NOT EXISTS `' + options.tableName + '` (`id` tinyint(1) unsigned NOT NULL DEFAULT \'1\', `version` int(10) unsigned NOT NULL DEFAULT \'0\', `running` tinyint(3) unsigned NOT NULL DEFAULT \'0\', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin COMMENT=\'Used for automatic database versioning. Do not modify!\';'; | ||
db.query(sql, cb); | ||
}); | ||
// Update old version of table (for seamless updating of old versions of this module) | ||
tasks.push(function(cb) { | ||
db.query('DESCRIBE `' + options.tableName + '`', function(err, rows) { | ||
if (err) { cb(err); return; } | ||
if (rows.length === 2 && rows[0].Field === 'version' && rows[1].Field === 'running') { | ||
// Old version detected! Update! | ||
db.query('ALTER TABLE `' + options.tableName + '` ADD `id` tinyint(1) unsigned NOT NULL DEFAULT \'1\' FIRST;', function(err) { | ||
if (err) { cb(err); return; } | ||
db.query('ALTER TABLE `' + options.tableName + '` ADD PRIMARY KEY `id` (`id`);', cb); | ||
}); | ||
} else { | ||
// Nothing to do, continue | ||
cb(); | ||
} | ||
}); | ||
}); | ||
// Insert first record if it does not exist | ||
tasks.push(function(cb) { | ||
db.query('INSERT IGNORE INTO `' + options.tableName + '` VALUES(1, 0, 0);', cb); | ||
}); | ||
// Lock table by setting the running column to 1 | ||
tasks.push(getLock); | ||
// Get current version | ||
tasks.push(function(cb) { | ||
db.query('SELECT version FROM `' + options.tableName + '`;', function(err, rows) { | ||
if (err) { cb(err); return; } | ||
curVer = parseInt(rows[0].version); | ||
log.info('larvitdbmigration: Current database version for table ' + options.tableName + ' is ' + curVer); | ||
cb(); | ||
}); | ||
}); | ||
// Run scripts | ||
tasks.push(function(cb) { | ||
try { | ||
runScripts(curVer + 1, cb); | ||
} catch(err) { | ||
log.error('larvitdbmigration: Error from driver: ' + err.message); | ||
cb(err); | ||
} | ||
}); | ||
// Unlock table | ||
tasks.push(function(cb) { | ||
db.query('UPDATE `' + options.tableName + '` SET running = 0;', cb); | ||
}); | ||
async.series(tasks, cb); | ||
}; | ||
}; | ||
exports = module.exports = DbMigration; |
@@ -11,5 +11,9 @@ { | ||
"async": "^2.0.1", | ||
"elasticsearch": "^12.1.3", | ||
"json-stringify-safe": "^5.0.1", | ||
"larvitdb": "^1.2.2", | ||
"larvitutils": "^1.1.1", | ||
"lodash": "^4.7.0", | ||
"mysql2": "^1.0.0-rc.11", | ||
"request": "^2.81.0", | ||
"winston": "^2.2.0" | ||
@@ -20,3 +24,4 @@ }, | ||
"eslint": "^3.4.0", | ||
"mocha": "^3.0.2" | ||
"mocha": "^3.0.2", | ||
"mocha-eslint": "^3.0.1" | ||
}, | ||
@@ -27,3 +32,4 @@ "keywords": [ | ||
"mariadb", | ||
"migration" | ||
"migration", | ||
"elasticsearch" | ||
], | ||
@@ -36,3 +42,3 @@ "main": "index.js", | ||
}, | ||
"version": "1.3.3", | ||
"version": "2.0.0", | ||
"readmeFilename": "README.md", | ||
@@ -39,0 +45,0 @@ "scripts": { |
123
README.md
@@ -7,6 +7,9 @@ [![Build Status](https://travis-ci.org/larvit/larvitdbmigration.svg?branch=master)](https://travis-ci.org/larvit/larvitdbmigration) [![Dependencies](https://david-dm.org/larvit/larvitdbmigration.svg)](https://david-dm.org/larvit/larvitdbmigration.svg) | ||
At the moment only MariaDB(/MySQL) is supported. | ||
Supported databases: | ||
A table by default called db_version will be created, containing a single integer. | ||
* MariaDB(/MySQL) | ||
* Elasticsearch | ||
A table/index by default called db_version will be created, containing a single integer. | ||
Scripts will be placed by default in process.cwd()/dbmigration/<version>.js | ||
@@ -16,2 +19,8 @@ | ||
## Installation | ||
```bash | ||
npm i --save larvitdbmigration | ||
``` | ||
## Usage | ||
@@ -23,8 +32,14 @@ | ||
#### MariaDb and MySQL | ||
```javascript | ||
'use strict'; | ||
const dbMigration = require('larvitdbmigration'); | ||
const DbMigration = require('larvitdbmigration'), | ||
options = {}, | ||
db = require('larvitdb'); | ||
dbMigration({ | ||
let dbMigration; | ||
db.setup({ | ||
'host': '127.0.0.1', | ||
@@ -34,7 +49,14 @@ 'user': 'foo', | ||
'database': 'baz' | ||
})(function(err) { | ||
if (err) { | ||
throw err; | ||
} | ||
}); | ||
options.dbType = 'larvitdb'; | ||
options.dbDriver = db; | ||
options.tableName = 'db_version'; // Optional - used as index name for elasticsearch | ||
options.migrationScriptsPath = './dbmigration'; // Optional | ||
dbMigration = new DbMigration(options); | ||
dbMigration.run(function (err) { | ||
if (err) throw err; | ||
// Now database is migrated and ready for use! | ||
@@ -44,46 +66,26 @@ }); | ||
If larvitdb already is initiated someplace else, you can omit the database config, like this: | ||
#### Elasticsearch | ||
```javascript | ||
const dbMigration = require('larvitdbmigration'), | ||
db = require('larvitdb'); | ||
'use strict'; | ||
db.setup({ | ||
'host': '127.0.0.1', | ||
'user': 'foo', | ||
'password': 'bar', | ||
'database': 'baz' | ||
}); | ||
const elasticsearch = require('elasticsearch'), | ||
DbMigration = require('larvitdbmigration'), | ||
options = {}, | ||
es = new elasticsearch.Client({'host': '127.0.0.1:9200'}); | ||
dbMigration()(function(err) { | ||
if (err) { | ||
throw err; | ||
} | ||
let dbMigration; | ||
// Now database is migrated and ready for use! | ||
}); | ||
``` | ||
options.dbType = 'elasticsearch'; | ||
options.dbDriver = es; | ||
options.tableName = 'db_version'; // Optional - used as index name | ||
options.migrationScriptsPath = './dbmigration'; // Optional | ||
To use custom table name and/or script path, just change | ||
dbMigration = new DbMigration(options); | ||
```javascript | ||
dbMigration({ | ||
'host': '127.0.0.1', | ||
'user': 'bar', | ||
'password': 'bar', | ||
'database': 'bar' | ||
})(function(err) { | ||
``` | ||
dbMigration.run(function (err) { | ||
if (err) throw err; | ||
to | ||
```javascript | ||
dbMigration({ | ||
'host': '127.0.0.1', | ||
'user': 'bar', | ||
'password': 'bar', | ||
'database': 'bar', | ||
'tableName': 'db_version', | ||
'migrationScriptsPath': './dbmigration' | ||
})(function(err) { | ||
// Now database is migrated and ready for use! | ||
}); | ||
``` | ||
@@ -101,3 +103,3 @@ | ||
#### Javascript | ||
#### MariaDB / MySQL, Javascript | ||
@@ -109,5 +111,5 @@ Create the file process.cwd()/migrationScriptsPath/1.js with this content: | ||
const db = require('db'); | ||
exports = module.exports = function (cb) { | ||
const db = this.options.dbDriver; | ||
exports = module.exports = function(cb) { | ||
db.query('ALTER TABLE bloj CHANGE nisse hasse int(11);', cb); | ||
@@ -117,2 +119,29 @@ }; | ||
#### Elasticsearch | ||
Create the file process.cwd()/migrationScriptsPath/1.js with this content: | ||
```javascript | ||
'use strict'; | ||
exports = module.exports = function (cb) { | ||
const es = this.options.dbDriver; | ||
es.indices.putMapping({ | ||
'index': 'foo', | ||
'type': 'bar', | ||
'body': { | ||
'bar': { | ||
'properties': { | ||
'names': { | ||
'type': 'string', | ||
'position_increment_gap': 100 | ||
} | ||
} | ||
} | ||
} | ||
}, cb); | ||
}; | ||
``` | ||
#### SQL | ||
@@ -119,0 +148,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
31372
18
759
152
9
3
23
1
+ Addedelasticsearch@^12.1.3
+ Addedjson-stringify-safe@^5.0.1
+ Addedlarvitutils@^1.1.1
+ Addedrequest@^2.81.0
+ Addedajv@6.12.6(transitive)
+ Addedansi-regex@2.1.1(transitive)
+ Addedansi-styles@2.2.1(transitive)
+ Addedasap@2.0.6(transitive)
+ Addedasn1@0.2.6(transitive)
+ Addedassert-plus@1.0.0(transitive)
+ Addedasynckit@0.4.0(transitive)
+ Addedaws-sign2@0.7.0(transitive)
+ Addedaws4@1.13.2(transitive)
+ Addedbcrypt-pbkdf@1.0.2(transitive)
+ Addedcaseless@0.12.0(transitive)
+ Addedchalk@1.1.3(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addedcore-util-is@1.0.2(transitive)
+ Addeddashdash@1.14.1(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedecc-jsbn@0.1.2(transitive)
+ Addedelasticsearch@12.1.3(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedextend@3.0.2(transitive)
+ Addedextsprintf@1.3.0(transitive)
+ Addedfast-deep-equal@3.1.3(transitive)
+ Addedfast-json-stable-stringify@2.1.0(transitive)
+ Addedforever-agent@0.6.1(transitive)
+ Addedform-data@2.3.3(transitive)
+ Addedgetpass@0.1.7(transitive)
+ Addedhar-schema@2.0.0(transitive)
+ Addedhar-validator@5.1.5(transitive)
+ Addedhas-ansi@2.0.0(transitive)
+ Addedhttp-signature@1.2.0(transitive)
+ Addedis-typedarray@1.0.0(transitive)
+ Addedjsbn@0.1.1(transitive)
+ Addedjson-schema@0.4.0(transitive)
+ Addedjson-schema-traverse@0.4.1(transitive)
+ Addedjson-stringify-safe@5.0.1(transitive)
+ Addedjsprim@1.4.2(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedoauth-sign@0.9.0(transitive)
+ Addedperformance-now@2.1.0(transitive)
+ Addedpromise@7.3.1(transitive)
+ Addedpsl@1.10.0(transitive)
+ Addedpunycode@2.3.1(transitive)
+ Addedqs@6.5.3(transitive)
+ Addedrequest@2.88.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsshpk@1.18.0(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedsupports-color@2.0.0(transitive)
+ Addedtough-cookie@2.5.0(transitive)
+ Addedtunnel-agent@0.6.0(transitive)
+ Addedtweetnacl@0.14.5(transitive)
+ Addeduri-js@4.4.1(transitive)
+ Addeduuid@3.4.0(transitive)
+ Addedverror@1.10.0(transitive)