New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

dbeasy

Package Overview
Dependencies
Maintainers
3
Versions
39
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dbeasy - npm Package Compare versions

Comparing version 0.10.1 to 0.11.1

migrator/sql/get_migrations.sql.hbs

4

client.js

@@ -225,2 +225,6 @@ "use strict";

if (!template) {
throw new Error('Statement not loaded as a template: ' + tplQuery.path);
}
var sql = template(templateParams);

@@ -227,0 +231,0 @@ query = {

255

migrator/index.js

@@ -5,5 +5,13 @@ 'use strict';

layoutModule = require('../layout'),
parseMigrations = require('./parse'),
storeModule = require('../store');
storeModule = require('../store'),
fs = Promise.promisifyAll(require('fs')),
path = require('path'),
assert = require('assert'),
moment = require('moment-timezone');
var CANDIDATE_STATUS = {
PENDING: 'PENDING',
MISSING: 'MISSING'
};
module.exports = function(client) {

@@ -13,3 +21,3 @@

var layout = layoutModule(client);
var loadedMigrations = {};
var candidateMigrations = [];
var templateVars = {};

@@ -34,2 +42,30 @@

function makeMigration(filePath, date, description, sql) {
assert(_.isString(filePath));
assert(_.isString(description));
assert(!_.isEmpty(description));
assert(_.isDate(date));
var migration = {
path: filePath,
date: date,
description: description,
template: sql
};
return migration;
}
function readMigration(filePath) {
var filename = path.basename(filePath, '.sql');
return fs.readFileAsync(filePath).then(function(data) {
return makeMigration(
filePath,
new Date(_.first(filename.split('_'))),
_.rest(filename.split('_')).join('_'),
data.toString()
);
});
}
function ensureMigrationTable(schema) {

@@ -44,18 +80,15 @@ return layout.ensureTable(schema + '.migration', {

function loadLastMigrationDate(schema) {
function getCommittedMigrations(schema) {
return ensureMigrationTable(schema)
.then(function() {
return client.execTemplate(
statements.getLastMigration,
{schema: schema});
})
.then(function(result) {
return result.length && result[0].date;
});
.then(function() {
return client.execTemplate(
statements.getMigrations,
{schema: schema});
});
}
function recordMigration(migration) {
function recordMigration(migration, schema) {
return client.execTemplate(
statements.recordMigration,
{schema: migration.schema},
{schema: schema},
migration);

@@ -79,10 +112,7 @@ }

migrator.loadMigrations = loadMigrations;
function loadMigrations(filePath, opts) {
return parseMigrations(filePath)
.then(function(migrations) {
if (!migrations.length) {
throw new Error('No migrations found: ' + filePath);
}
return addMigrations(migrations, opts);
migrator.loadMigration = loadMigration;
function loadMigration(filePath, opts) {
return readMigration(filePath)
.then(function(migration) {
return addMigration(migration, opts);
});

@@ -93,80 +123,139 @@ }

migrator.addMigrations = addMigrations;
function addMigrations(migrations, opts) {
opts = _.defaults(opts || {}, {
schema: 'public'
});
migrator.addMigration = addMigration;
function addMigration(migration) {
var prevMigrationDate = new Date('1970-01-01');
migrations = _.map(migrations, function(migration) {
var date = migration.date;
var date = migration.date;
if (isNaN(date.getTime()) || !_.isDate(date)) {
throw new TypeError(
'Invalid date object: ' + date);
}
if (isNaN(date.getTime()) || !_.isDate(date)) {
throw new TypeError(
'Invalid date object: ' + date);
}
if (prevMigrationDate.getTime() === date.getTime()) {
throw new Error(
'Migration has identical time stamp; bad: ' + date);
}
if (prevMigrationDate > date) {
throw new Error(
'Migrations must remain ordered by date; bad: ' + date);
}
prevMigrationDate = date;
if (prevMigrationDate.getTime() === date.getTime()) {
throw new Error(
'Migration has identical time stamp; bad: ' + date);
}
if (prevMigrationDate > date) {
throw new Error(
'Migrations must remain ordered by date; bad: ' + date);
}
prevMigrationDate = date;
return _.extend({}, {schema: opts.schema}, migration);
});
candidateMigrations.push(_.cloneDeep(migration));
}
loadedMigrations[opts.schema] = (
(loadedMigrations[opts.schema] || []).concat(migrations));
migrator.createMigration = createMigration;
function createMigration(name, dir) {
assert(name, 'migration name required');
assert(dir, 'migration directory required');
var timestamp =
moment().tz('America/Los_Angeles').format('YYYY-MM-DDTHH:mm:ss');
var filename =
timestamp + '_' + _.snakeCase(name);
var contents = [
'-- ',
'-- ' + _.snakeCase(name),
'-- ',
'',
''
].join('\n');
var path = dir + '/' + filename;
return fs.writeFileAsync(path, contents)
.then(function() {
return path;
});
}
migrator.getPending = getPending;
function getPending() {
return Promise.all(_.transform(
loadedMigrations,
function(results, setMigrations, schema) {
results.push(
loadLastMigrationDate(schema)
.then(function(lastDate) {
return _.filter(setMigrations, function(migration) {
return (!lastDate || (migration.date > lastDate));
});
}));
return results;
}, []))
.then(function(resultsBySet) {
return _.sortBy(_.flatten(resultsBySet), 'date');
});
migrator.redate = redate;
function redate(filePath) {
assert(filePath, 'a path is required');
var namePart = _.rest(path.basename(filePath).split('_')).join('_');
var dir = path.dirname(filePath);
var timestamp =
moment().tz('America/Los_Angeles').format('YYYY-MM-DDTHH:mm:ss');
var newPath = dir + '/' + timestamp + '_' + namePart;
return fs.renameAsync(filePath, newPath)
.then(function() {
return newPath;
});
}
migrator.up = migrator.runPending = runPending;
function runPending() {
migrator.getStatus = getStatus;
function getStatus(schema) {
return onReady.then(function() {
return getPending();
return getCommittedMigrations(schema);
})
.then(function(pendingMigrations) {
if (!pendingMigrations.length) {
client._logger.info('No pending migrations');
return;
}
return Promise.reduce(pendingMigrations, function(__, migration) {
client._logger.info(
'Running migration',
_.pick(migration, 'date', 'description'));
return (migration.template
? client.execTemplate(migration.template, templateVars)
: client.exec(migration.sql))
.then(function() {
return recordMigration(migration);
.then(function(committedMigrations) {
var candidate = _.map(candidateMigrations, function(m) {
return _.extend({}, m, {isCommitted: false});
});
}, null);
});
var committed = _.map(committedMigrations, function(m) {
return _.extend({}, m, {isCommitted: true});
});
var committedByDate = _.indexBy(committed, 'date');
var candidateByDate = _.indexBy(candidate, 'date');
var allByDate = _.extend({}, candidateByDate, committedByDate);
var allMigrations = _.sortBy(_.values(allByDate), 'date');
var hasMissing = false;
var hasPending = false;
var hasComitted = false;
allMigrations = _.map(allMigrations.reverse(), function(m) {
if (!hasComitted && !m.isCommitted) {
m.candidateStatus = CANDIDATE_STATUS.PENDING;
hasPending = true;
}
if (hasComitted && !m.isCommitted) {
m.candidateStatus = CANDIDATE_STATUS.MISSING;
hasMissing = true;
}
if (!hasComitted && m.isCommitted) {
hasComitted = true;
}
return m;
}).reverse();
// console.log("ARGS", allMigrations, hasPending, hasMissing);
return [allMigrations, hasPending, hasMissing];
});
}
migrator.up = migrator.runPending = runPending;
function runPending(schema) {
return onReady.then(function() {
return getStatus(schema);
})
.then(_.spread(function(migrations, hasPending, hasMissing) {
if (!hasPending && !hasMissing) {
client._logger.info('No pending migrations');
return;
}
if (hasMissing) {
throw new Error('One ore more migrations were missed');
}
return Promise.reduce(
_.filter(migrations, {candidateStatus: CANDIDATE_STATUS.PENDING}),
function(__, migration) {
client._logger.info(
'Running migration',
_.pick(migration, 'date', 'description'));
return (migration.template
? client.execTemplate(migration.template, templateVars)
: client.exec(migration.sql))
.then(function() {
return recordMigration(migration, schema);
});
}, null);
}));
}
return migrator;
};
'use strict';
var _ = require('lodash'),
_str = require('underscore.string'),
assert = require('assert'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs'));
fs = Promise.promisifyAll(require('fs')),
path = require('path');
function makeMigration(date, description) {
function makeMigration(filePath, date, description, sql) {
assert(_.isString(filePath));
assert(_.isString(description));

@@ -13,5 +14,6 @@ assert(!_.isEmpty(description));

var migration = {
path: filePath,
date: date,
description: description,
template: ''
template: sql
};

@@ -21,119 +23,14 @@ return migration;

function setSql(migration, sqlLines) {
assert(_.isArray(sqlLines));
module.exports = function(filePath) {
var filename = path.basename(filePath, '.sql');
// Throw away trailing comments, blanks, etc.
var lines = _.clone(sqlLines);
while(lines.length) {
if (!_.isEmpty(_.last(lines)) && !isComment(_.last(lines))) break;
lines.pop();
}
// If there was nothing but comments and blanks, we keep it.
migration.template = lines.length ? lines.join('\n') : sqlLines.join('\n');
return migration;
}
function readlines(filePath) {
var lines;
var lineNum = 0;
var onReady = fs.readFileAsync(filePath)
.then(function(content) {
lines = content.toString().split('\n');
return fs.readFileAsync(filePath).then(function(data) {
return makeMigration(
filePath,
new Date(_.first(filename.split('_'))),
_.rest(filename.split('_')).join('_'),
data.toString()
);
});
onReady.done();
return {
next: function() {
return onReady.then(function() {
lineNum += 1;
return [lines[lineNum - 1], lineNum];
});
}
};
}
function isComment(line) {
return /^--/.test(line);
}
function isMigrationComment(line) {
return /^-- *## /.test(line);
}
function parseMigrationComment(line) {
var match = /## +migration +([^ ]*) +(.*)/.exec(line);
var date = new Date(match[1]);
var description = match[2];
return makeMigration(date, description);
}
function error(lineNum, message) {
var err = new Error('Migration parse error: line ' + lineNum + ': ' + message);
Error.captureStackTrace(err, error);
throw err;
}
module.exports = function(filePath) {
var lineReader = readlines(filePath);
function parseMigrations(lineReader) {
var migrations = [];
// Discard any junk before first migration header
return (function next() {
return lineReader.next()
.spread(function(line, lineNum) {
if (line === undefined) {
return [];
}
line = _str.trim(line);
if (isMigrationComment(line)) {
return _parseMigrations(line, lineNum);
}
// Discard comments before migration header
if (isComment(line) || _.isEmpty(line)) {
return next();
}
error(lineNum, 'Unexpected characters before migration header: ' + line);
});
})();
// Parse migration starting with header line
function _parseMigrations(startLine, lineNum) {
var sqlLines = [startLine];
return Promise.try(parseMigrationComment, startLine)
.catch(function(err) {
error(
lineNum,
'Unable to parse migration header: ' + err.message);
})
.then(function(migration) {
// Read lines until EOF or next header
return (function next() {
return lineReader.next()
.spread(function(line, lineNum) {
if (isMigrationComment(line)) {
setSql(migration, sqlLines);
migrations.push(migration);
return _parseMigrations(line, lineNum);
}
if (line === undefined) {
migrations.push(setSql(migration, sqlLines));
return migrations;
}
sqlLines.push(line);
return next();
});
}());
});
}
}
return parseMigrations(lineReader);
};
{
"name": "dbeasy",
"version": "0.10.1",
"version": "0.11.1",
"description": "Promise-based wrapper for postgresql driver that makes easy what should be easy while protecting your foot.",

@@ -30,5 +30,7 @@ "main": "index.js",

"handlebars": "1.3.0",
"lodash": "^2.4.1",
"lodash": "^3.10.1",
"moment-timezone": "^0.5.4",
"pg": "4.5.0",
"pg-native": "1.10.0",
"sprintf-js": "^1.0.3",
"underscore.string": "2.3.3"

@@ -35,0 +37,0 @@ },

@@ -62,3 +62,3 @@ "use strict";

var migrator;
var migration;
var SCHEMA = 'school';

@@ -69,3 +69,3 @@ setup(function() {

migrator = makeMigrator(client);
return migrator.clearMigrations('school')
return migrator.clearMigrations(SCHEMA)
.then(function() {

@@ -81,5 +81,5 @@ return layout(client).dropNamespace('school');

sql: 'CREATE TABLE school.classroom();\nCREATE TABLE school.cafeteria();'
}], {schema: 'school'});
}]);
return migrator.runPending()
return migrator.runPending(SCHEMA)
.then(function() {

@@ -106,5 +106,5 @@ return Promise.all([

sql: 'CREATE TABLE school.cafeteria();'
}], {schema: 'school'});
}]);
return migrator.runPending()
return migrator.runPending(SCHEMA)
.then(function() {

@@ -122,2 +122,58 @@ return Promise.all([

test('Identify missed migrations', function() {
migrator.addMigrations([{
date: new Date('2014-11-12T01:24'),
description: 'noop',
sql: 'SELECT;'
},{
date: new Date('2014-12-12T01:24'),
description: 'noop',
sql: 'SELECT;'
}]);
return Promise.resolve()
.then(function() {
return migrator
.getStatus(SCHEMA)
.then(_.spread(function(items, hasPending, hasMissing) {
expect(hasPending).to.equal(true);
expect(hasMissing).to.equal(false);
}));
})
.then(function() {
return migrator
.runPending(SCHEMA)
.then(function() {
return migrator.getStatus(SCHEMA);
})
.then(_.spread(function(items, hasPending, hasMissing) {
expect(hasPending).to.equal(false);
expect(hasMissing).to.equal(false);
}));
})
.then(function() {
migrator = makeMigrator(client);
migrator.addMigrations([{
date: new Date('2014-11-13T01:24'),
description: 'noop',
sql: 'SELECT;'
}]);
return migrator
.getStatus(SCHEMA)
.then(_.spread(function(items, hasPending, hasMissing) {
expect(hasPending).to.equal(false);
expect(hasMissing).to.equal(true);
expect(items[1]).to.eql({
date: new Date('2014-11-13T01:24'),
description: 'noop',
sql: 'SELECT;',
isCommitted: false,
candidateStatus: 'MISSING'
});
}));
});
});
test('Do not allow misordered migrations', function() {

@@ -130,3 +186,3 @@ return Promise.try(function() {

},{
date: new Date('2014-11-12T01:24'),
date: new Date('2014-11-12T01:23'),
description: 'create rooms',

@@ -163,6 +219,6 @@ sql: ''

sql: 'CREATE TABLE school.classroom();'
}], {schema: 'school'});
return migrator.runPending()
}]);
return migrator.runPending(SCHEMA)
.then(function() {
return migrator.runPending();
return migrator.runPending(SCHEMA);
})

@@ -175,3 +231,3 @@ .then(function() {

}], {schema: 'school'});
return migrator.runPending();
return migrator.runPending(SCHEMA);
});

@@ -198,3 +254,3 @@

.then(function() {
return migrator.runPending();
return migrator.runPending(SCHEMA);
})

@@ -213,7 +269,5 @@ .then(function() {

migrator.templateVars['table'] = 'school.classroom';
return migrator.loadMigrations(
testSqlPath + '11_create_table.sql.hbs',
{schema: 'school'})
return migrator.loadMigrations(testSqlPath + '11_create_table.sql.hbs')
.then(function() {
return migrator.runPending();
return migrator.runPending(SCHEMA);
})

@@ -230,2 +284,2 @@ .then(function() {

});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc