sequelize-models-postgis
Version:
Node.js SequelizeJS ORM model utilities
258 lines (222 loc) • 7.39 kB
JavaScript
"use strict";
const debug = require("debug")("sequelize-models");
const Sequelize = require("sequelize");
const _ = require("lodash");
const path = require("path");
const co = require("co");
const Promise = require("bluebird");
const utils = require("./utils.js");
const fs = require("fs");
const readdir = Promise.promisify(require("readdir-plus"));
const Op = Sequelize.Op;
/**
* SequelizeModels
*/
class SequelizeModels {
/**
* SequelizeModels constructor.
* @constructor
* @param {Object} opts - configuration options
* @param {Object} opts.connection - database connections params
* @param {Srtring} opts.connection.host - hostname or ipadress of the server runing database instance
* @param {Srtring} opts.connection.dialect - dialect of database engine to use
* @param {Srtring} opts.connection.username - username to connect with the database instance
* @param {Srtring} opts.connection.password - password to connect with the database instance
* @param {Srtring} opts.connection.schema - database schema name to use with the connection
* @param {Srtring} opts.connection.port - database instance server running port default 3306
* @param {Object} opts.models - options to models definition
* @param {String} opts.models.path - path to read the model definitions
* @param {Boolean} opts.models.autoLoad - enable or disable autoloading models from database
* @param {Object} opts.sequelizeOptions - parameters that will be passed directly to Sequelize
* constructor
*/
constructor(opts) {
debug("constructor %o", opts);
this.opts = opts || {};
if (!this.opts.connection.port) {
this.opts.connection.port = 3306;
}
if (!this.opts.connection) {
throw new Error("Connection configuration is required");
}
if (!this.opts.connection.host) {
throw new Error("Connection host is required");
}
if (!this.opts.connection.dialect) {
throw new Error("Connection dialect is required");
}
if (!this.opts.connection.username) {
throw new Error("Connection username is required");
}
if (undefined === this.opts.connection.password) {
throw new Error("Connection password is required");
}
if (!this.opts.connection.schema) {
throw new Error("Connection schema name is required");
}
if (
"mysql" !== this.opts.connection.dialect.toLowerCase() &&
"postgres" !== this.opts.connection.dialect.toLowerCase()
) {
throw new Error("Only MySQL and PSQL dialects are supported");
}
}
/**
* Get Sequelize Database instance passing all args in this.opts.sequelizeOptions
* @return {Object} a sequelize instance already connected to database.
*/
getSequelizeInstance() {
debug("getSequelizeInstance");
var dbOptions = {
host: this.opts.connection.host,
dialect: this.opts.connection.dialect,
port: this.opts.connection.port,
};
// Default pool configuration
dbOptions.pool = {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
};
var sequelizeOptions = _.extend(this.opts.sequelizeOptions, dbOptions);
sequelizeOptions.operatorsAliases = {
$eq: Op.eq,
$ne: Op.ne,
$gte: Op.gte,
$gt: Op.gt,
$lte: Op.lte,
$lt: Op.lt,
$not: Op.not,
$in: Op.in,
$notIn: Op.notIn,
$is: Op.is,
$like: Op.like,
$notLike: Op.notLike,
$iLike: Op.iLike,
$notILike: Op.notILike,
$regexp: Op.regexp,
$notRegexp: Op.notRegexp,
$iRegexp: Op.iRegexp,
$notIRegexp: Op.notIRegexp,
$between: Op.between,
$notBetween: Op.notBetween,
$overlap: Op.overlap,
$contains: Op.contains,
$contained: Op.contained,
$adjacent: Op.adjacent,
$strictLeft: Op.strictLeft,
$strictRight: Op.strictRight,
$noExtendRight: Op.noExtendRight,
$noExtendLeft: Op.noExtendLeft,
$and: Op.and,
$or: Op.or,
$any: Op.any,
$all: Op.all,
$values: Op.values,
$col: Op.col
};
return new Sequelize(
this.opts.connection.schema,
this.opts.connection.username,
this.opts.connection.password,
sequelizeOptions
);
}
/**
* Read and load models definition from directory
* @return {Promise} a priise wich will be resolved with all models defs
*/
getModelDefinitions() {
debug("getModelDefinitions");
var modelsDefs = [];
if (!this.opts.models || !this.opts.models.path) {
return Promise.resolve(modelsDefs);
}
var modelsDirectory = path.join(path.resolve(), this.opts.models.path);
try {
fs.accessSync(modelsDirectory, fs.F_OK);
} catch (e) {
return Promise.resolve(modelsDefs);
}
return new Promise(resolve => {
readdir(modelsDirectory, {
recursive: false,
return: "fullPaths",
filter: {
file: /\.(js)$/i,
},
}).then(files => {
modelsDefs = files.map(file => {
return {
name: utils.camelize(path.basename(/^(.+?).js$/.exec(file)[1])),
object: require(file),
};
});
return resolve(modelsDefs);
});
});
}
/**
* Get Sequelize model definitions ready.
* @return {Promise} get all models from the database schema
*/
getSchema(options) {
let exportTableNames = [];
if (options) {
exportTableNames = options.exportTableNames ? options.exportTableNames: [];
}
debug("getSchema");
const ignoreGeometryTableNames = [
"geography_columns",
"geometry_columns",
"raster_overviews",
"raster_columns",
"spatial_ref_sys",
"layer",
"topology",
];
var _this = this;
let dialectPath = path.join(
__dirname,
"dialects",
this.opts.connection.dialect
);
let Dialect = require(dialectPath);
let dbInstance = this.getSequelizeInstance();
let dialect = new Dialect(Sequelize, dbInstance, this.opts.connection);
return co(function*() {
let modelDefs = yield _this.getModelDefinitions();
modelDefs = modelDefs.concat(
ignoreGeometryTableNames.map(tName => ({
name: utils.camelize(tName),
object: {
tableName: tName,
attributes: {},
},
}))
);
let autoload = true;
if (false === _this.opts.models.autoLoad) {
autoload = false;
}
debug("options passed to load models");
let models = yield dialect.loadModels(modelDefs, autoload);
debug('models', models);
models = Object.keys(models).reduce((p, modelName) => {
debug('exportTableNames.indexOf(modelName)', modelName, exportTableNames.indexOf(modelName));
if (exportTableNames.indexOf(modelName) !== -1) {
p[modelName] = models[modelName];
return p;
}
return p;
}, {});
debug('after models', models);
return {
models: models,
db: dbInstance,
};
});
}
}
module.exports = SequelizeModels;