all files / lib/ DatabaseManager.js

71.11% Statements 32/45
83.33% Branches 5/6
47.37% Functions 9/19
71.11% Lines 32/45
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177                                                                                                                                                                                                                                                                                     21× 21×   21×            
var _ = require('lodash')
  , Knex = require('knex')
  , Promise = require('bluebird')
  , multiRequire = require('./multi-require').multiRequire;
 
/**
 * Base class for database managers.
 *
 * Database manager is used to create/drop databases, run migrations for them and doing other "super user" stuff.
 *
 * @constructor
 * @param {object} config
 *    Database configuration. See `index.js` for example config and `database` feature for further description.
 */
function DatabaseManager(config) {
  this.config = config;
}
 
/**
 * Creates user which is used to access the database described in knex configuration.
 *
 * @returns {Promise} Resolves if success reject if user could not be created for some reason.
 */
DatabaseManager.prototype.createDbOwnerIfNotExist = function() {
  throw new Error(this.constructor.name + '.createDbOwner not implemented');
};
 
/**
 * Creates and initializes a database `databaseName`.
 *
 * Rejects promise if databaseName already exists or could not
 * be created.
 *
 * @param {String=} databaseName if not given, database from knex config is used
 * @returns {Promise}
 */
DatabaseManager.prototype.createDb = function(databaseName) {
  throw new Error(this.constructor.name + '.initDb not implemented');
};
 
/**
 * Drops the database `databaseName`.
 *
 * @param {String=} databaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.dropDb = function(databaseName) {
  throw new Error(this.constructor.name + '.dropDb not implemented');
};
 
/**
 * Makes copy of database.
 *
 * Good for backing up stuff before running migrations etc.
 *
 * @param {String} fromDatabaseName
 * @param {String} toDatabaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.copyDb = function(fromDatabaseName, toDatabaseName) {
  throw new Error(this.constructor.name + '.copyDb not implemented');
};
 
/**
 * Truncates all tables in the database `databaseName` and also resets all
 * sequences.
 *
 * @param {String=} databaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.truncateDb = function(databaseName, ignoreTables) {
  throw new Error(this.constructor.name + '.truncateDb not implemented');
};
 
/**
 * Updates the primary key sequences for all tables so that next insert selects
 * correct id (one that does not conflict with previous ids and is valid).
 *
 * This means that the next id will be greater (by 1) than currently largest
 * id in the table. If the table is empty, minimum value for the key sequence
 * will be used instead.
 *
 * This function assumes that the primary key for each table is called `id`.
 *
 * @param {String=} databaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.updateIdSequences = function(databaseName) {
  throw new Error(this.constructor.name + '.updateIdSequences not implemented');
};
 
/**
 * Populate database.
 *
 * @param {String=} databaseName
 * @param {String=} populatePathPattern
 * @returns {Promise}
 */
DatabaseManager.prototype.populateDb = function(databaseName, populatePathPattern) {
  databaseName = databaseName || this.config.knex.connection.database;
  populatePathPattern = populatePathPattern || this.config.populatePathPattern;
 
  var knex = this.knexInstance(databaseName);
  var modules = multiRequire(populatePathPattern).filterModule(_.isFunction).require();
  return Promise
    .all(_.map(modules, function (module) {
      return module.module(knex);
    }))
    .tap(function () { knex.destroy(); })
    .catch(function(err) {
      knex.destroy();
      throw err;
    });
};
 
/**
 * Runs migrations for database `databaseName`.
 *
 * @param {String=} databaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.migrateDb = function(databaseName) {
  var knex = this.knexInstance(databaseName);
  return knex.migrate.latest()
    .tap(function () { knex.destroy(); })
    .catch(function(err) {
      knex.destroy();
      throw err;
    });
};
 
/**
 * Gets the migration version of the database `databaseName`.
 *
 * If no migrations run returns 'none'
 * Otherwise returns first numbers of latest migration file ran
 * e.g. for 20141024070315_test_schema.js version will be
 * '20141024070315'
 *
 * @param {String=} databaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.dbVersion = function(databaseName) {
  var knex = this.knexInstance(databaseName);
  return knex.migrate.currentVersion()
    .tap(function () { knex.destroy(); })
    .catch(function(err) {
      knex.destroy();
      throw err;
    });
};
 
/**
 * Closes the manager.
 *
 * @returns {Promise}
 */
DatabaseManager.prototype.close = function() {
  throw new Error(this.constructor.name + '.close not implemented');
};
 
/**
 * @return {QueryBuilder} When query builder is executed, it should fail if ´databaseName´ does not exist.
 */
DatabaseManager.prototype.knexInstance = function(databaseName) {
  var config = _.clone(this.config);
  if (databaseName) {
    config.knex.connection.database = databaseName;
  }
  return Knex(config.knex);
};
 
module.exports = {
  default: DatabaseManager,
  DatabaseManager: DatabaseManager
}