@vinka/repo
Version:
Database and repo utilities
205 lines • 8.6 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const defaultOptions = {
dialect: 'postgres',
logging: false,
};
class SequelizeConnector {
/**
* Constructs a SequelizeConnector. Provides convenience for creating Sequelize clients and performing migrations.
* Use createClient to create a Sequelize client. After that you can use this connector's migration methods to
* perform migrations on a database.
*
* @param sequelizeConstructor a Sequelize constructor function.
* @param poolConstructor a Postgres Pool constructor function.
* @param config the database configuration adapted for Sequelize and Postgres.
* @param logger a Logger object.
*/
constructor(sequelizeConstructor, poolConstructor, config, logger) {
this.SequelizeConstructor = sequelizeConstructor;
this.PoolConstructor = poolConstructor;
this.logger = logger;
// take shallow copy
this.config = Object.assign({}, config);
this.config.options = Object.assign(Object.assign({}, defaultOptions), config.options);
}
/**
* Creates a Sequelize client connected to database specified in config,
* creates the database if it does not exist, and sets the created client to
* an internal member variable.
*
* @param [modelMap] the models to associate with the client. If not
* provided, the caller needs to initialize models separately.
*/
connect(modelMap) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.testDbExistsOrThrow(this.config);
const client = this.createSequelizeClient(this.config, modelMap);
this.sequelizeClient = client;
return client;
}
catch (err) {
if (err.message.match(new RegExp(`"${this.config.db}" does not exist`))) {
this.logger.debug(`failed to connect to database ${this.config.db}, trying to create it`);
yield this.createDatabase(this.config);
return this.connect(modelMap);
}
else {
throw err;
}
}
});
}
/**
* Creates a new database with name from config.db. Uses database with name
* from config.masterDb to create the new database. The master database must
* already exist.
*
* @param config the configuration for the postgres pool used internally.
*/
createDatabase(config) {
return __awaiter(this, void 0, void 0, function* () {
const pool = yield this.createPgPool(config, config.masterDb);
try {
yield pool.query(`CREATE DATABASE "${config.db}"`);
this.logger.debug(`created database ${config.db}`);
}
catch (err) {
this.logger.error(`encountered error when creating database ${config.db}, message: ${err.message}`);
throw err;
}
finally {
yield pool.end();
}
});
}
/**
* Creates postgres connection pool, which can be used to connect to a database.
* @param config the configuration for the pool.
* @param dbName the name of the database the pool is connected to. Default is taken from config.db.
*/
createPgPool(config, dbName = config.db) {
const options = {
user: config.user,
password: config.pass,
host: config.options.host,
port: config.options.port,
database: dbName,
max: 1,
ssl: config.ssl ? { rejectUnauthorized: false } : false,
};
return new this.PoolConstructor(options);
}
/**
* Creates a Sequelize client and associates user defined models with the
* client.
*
* @param config the configuration for the client.
* @param [modelMap] the models to associate with the client. If not
* provided, the caller needs to initialize models separately.
*/
createSequelizeClient(config, modelMap) {
const options = Object.assign(Object.assign({}, config.options), { ssl: config.ssl,
// Ref.: https://github.com/brianc/node-postgres/issues/2009
// reject unauthorized
dialectOptions: {
ssl: config.ssl
? {
require: config.ssl,
rejectUnauthorized: false,
}
: false,
} });
if (config.options.logging && this.logger.debug) {
options.logging = (...args) => this.logger.debug(args.join(' '));
}
this.logger.debug(`connecting to database with options ${JSON.stringify(options)}`);
const sequelize = new this.SequelizeConstructor(config.db, config.user, config.pass, options);
if (modelMap) {
modelMap.init(sequelize);
}
this.logger.info(`connected to database ${config.db}`);
return sequelize;
}
/**
* Migrates a database down with Umzug.
* @param umzugConstructor the constructor for Umzug.
* @param to 0 (default) to revert all migrations, or the name of the migration to migrate down to.
* @param client the sequelize client used for the migration. Optional if createClient has been called.
*/
migrateDown(umzugConstructor, to = 0, client = this.sequelizeClient) {
return __awaiter(this, void 0, void 0, function* () {
if (client) {
return yield this.createUmzug(umzugConstructor, client).down({ to });
}
else {
throw Error('trying to migrate before client is initialized. method createClient not called succesfully?');
}
});
}
/**
* Migrates a database up with Umzug.
* @param umzugConstructor the constructor for Umzug.
* @param client the sequelize client used for the migration. Optional if createClient has been called.
*/
migrateUp(umzugConstructor, client = this.sequelizeClient) {
return __awaiter(this, void 0, void 0, function* () {
if (client) {
return yield this.createUmzug(umzugConstructor, client).up();
}
else {
throw Error('trying to migrate before client is initialized. method createClient not called succesfully?');
}
});
}
/**
* Tests that a client can be created from a Pool with config or throws.
* @param config the config to create the pool with. The pool provides the client.
*/
testDbExistsOrThrow(config) {
return __awaiter(this, void 0, void 0, function* () {
this.logger.debug(`trying to connect to database ${config.db}`);
const pool = yield this.createPgPool(config);
try {
(yield pool.connect()).release();
}
catch (err) {
throw err;
}
finally {
pool.end();
}
});
}
/**
* Creates an Umzug instance.
* @param umzugConstructor the Umzug constructor.
* @param client the sequelize client used in the Umzug.
*/
createUmzug(umzugConstructor, client) {
return new umzugConstructor({
storage: 'sequelize',
storageOptions: {
sequelize: client,
},
logging: (...args) => this.logger.debug(args.join(' ')),
migrations: {
path: 'migrations',
pattern: /\.js/,
params: [client.getQueryInterface(), this.SequelizeConstructor],
},
});
}
}
exports.default = SequelizeConnector;
//# sourceMappingURL=index.js.map