UNPKG

code-auto

Version:
270 lines (230 loc) 11.9 kB
var Sequelize = require('sequelize'); var async = require('async'); var fs = require('graceful-fs-extra'); var path = require('path'); var mkdirp = require('mkdirp'); var dialects = require('./dialects'); var _ = Sequelize.Utils._; var SqlString = require('./sql-string'); function AutoCore(database, username, password, options) { if (options && options.dialect === 'sqlite' && !options.storage) options.storage = database; if (database instanceof Sequelize) { this.sequelize = database; } else { this.sequelize = new Sequelize(database, username, password, options || {}); } this.queryInterface = this.sequelize.getQueryInterface(); this.tables = {}; this.foreignKeys = {}; this.dialect = dialects[this.sequelize.options.dialect]; this.options = _.extend({ global: 'Sequelize', local: 'sequelize', spaces: false, indentation: 1, directory: './models', additional: {}, freezeTableName: true }, options || {}); } AutoCore.prototype.build = function(callback) { var self = this; function mapTable(table, _callback) { self.queryInterface.describeTable(table, self.options.schema).then(function(fields) { self.tables[table] = fields _callback(); }, _callback); } if (self.options.dialect === 'postgres' && self.options.schema) { var showTablesSql = this.dialect.showTablesQuery(self.options.schema); self.sequelize.query(showTablesSql, { raw: true, type: self.sequelize.QueryTypes.SHOWTABLES }).then(function(tableNames) { processTables(_.flatten(tableNames)) }, callback); } else { this.queryInterface.showAllTables().then(processTables, callback); } function processTables(__tables) { if (self.sequelize.options.dialect === 'mssql') __tables = _.map(__tables, 'tableName'); var tables; if (self.options.tables) tables = _.intersection(__tables, self.options.tables) else if (self.options.skipTables) tables = _.difference(__tables, self.options.skipTables) else tables = __tables async.each(tables, mapForeignKeys, mapTables); function mapTables(err) { if (err) console.error(err) async.each(tables, mapTable, callback); } } function mapForeignKeys(table, fn) { if (!self.dialect) return fn() var sql = self.dialect.getForeignKeysQuery(table, self.sequelize.config.database) self.sequelize.query(sql, { type: self.sequelize.QueryTypes.SELECT, raw: true }).then(function(res) { _.each(res, assignColumnDetails) fn() }, fn); function assignColumnDetails(ref) { // map sqlite's PRAGMA results ref = _.mapKeys(ref, function(value, key) { switch (key) { case 'from': return 'source_column'; case 'to': return 'target_column'; case 'table': return 'target_table'; default: return key; } }); ref = _.assign({ source_table: table, source_schema: self.sequelize.options.database, target_schema: self.sequelize.options.database }, ref); if (!_.isEmpty(_.trim(ref.source_column)) && !_.isEmpty(_.trim(ref.target_column))) { ref.isForeignKey = true ref.foreignSources = _.pick(ref, ['source_table', 'source_schema', 'target_schema', 'target_table', 'source_column', 'target_column']) } if (_.isFunction(self.dialect.isUnique) && self.dialect.isUnique(ref)) ref.isUnique = true if (_.isFunction(self.dialect.isPrimaryKey) && self.dialect.isPrimaryKey(ref)) ref.isPrimaryKey = true if (_.isFunction(self.dialect.isSerialKey) && self.dialect.isSerialKey(ref)) ref.isSerialKey = true self.foreignKeys[table] = self.foreignKeys[table] || {}; self.foreignKeys[table][ref.source_column] = _.assign({}, self.foreignKeys[table][ref.source_column], ref); } } } AutoCore.prototype.test = function(success, callback) { var self = this; self.sequelize.query("select 1", { raw: true, type: self.sequelize.QueryTypes.SHOWTABLES }).then(success, callback); } AutoCore.prototype.run = function(success, callback) { var self = this; var text = {}; var tables = []; this.build(generateText); function generateText(err) { if (err) { console.error(err); callback(err) } else { async.each(_.keys(self.tables), function(table, _callback) { var fields = _.keys(self.tables[table]); _.each(fields, function(field, i) { var additional = self.options.additional if (additional && additional.timestamps !== undefined && additional.timestamps) { if ((additional.createdAt && field === 'createdAt' || additional.createdAt === field) || (additional.updatedAt && field === 'updatedAt' || additional.updatedAt === field) || (additional.deletedAt && field === 'deletedAt' || additional.deletedAt === field)) { return true; } } // Find foreign key var foreignKey = self.foreignKeys[table] && self.foreignKeys[table][field] ? self.foreignKeys[table][field] : null if (_.isObject(foreignKey)) { self.tables[table][field].foreignKey = foreignKey } // column's attributes var fieldAttr = _.keys(self.tables[table][field]); // Serial key for postgres... var defaultVal = self.tables[table][field].defaultValue; // ENUMs for postgres... if (self.tables[table][field].type === "USER-DEFINED" && !!self.tables[table][field].special) { self.tables[table][field].type = "ENUM(" + self.tables[table][field].special.map(function(f) { return f; }).join(',') + ")"; } var isUnique = self.tables[table][field].foreignKey && self.tables[table][field].foreignKey.isUnique; _.each(fieldAttr, function(attr, x) { var isSerialKey = self.tables[table][field].foreignKey && _.isFunction(self.dialect.isSerialKey) && self.dialect.isSerialKey(self.tables[table][field].foreignKey) // We don't need the special attribute from postgresql describe table.. if (attr === "special") { return true; } if (attr === "foreignKey") { if (isSerialKey) { self.tables[table][field].autoIncrement = true; if (!self.tables[table][field].foreignKey.foreignSources) self.tables[table][field].foreignKey = null; } else return true; } else if (attr === "primaryKey") { if (self.tables[table][field][attr] === true && (!_.has(self.tables[table][field], 'foreignKey') || (_.has(self.tables[table][field], 'foreignKey') && !!self.tables[table][field].foreignKey.isPrimaryKey))) self.tables[table][field][attr] = true; else self.tables[table][field][attr] = false; return true; } else if (attr === "defaultValue") { if (self.sequelize.options.dialect === "mssql" && defaultVal && defaultVal.toLowerCase() === '(newid())') { defaultVal = null; // disable adding "default value" attribute for UUID fields if generating for MS SQL } var val_text = defaultVal; if (isSerialKey) return true; //mySql Bit fix if (self.tables[table][field].type.toLowerCase() === 'bit(1)') { val_text = defaultVal === "b'1'" ? 1 : 0; } // mssql bit fix else if (self.sequelize.options.dialect === "mssql" && self.tables[table][field].type.toLowerCase() === "bit") { val_text = defaultVal === "((1))" ? 1 : 0; } if (_.isString(defaultVal)) { var field_type = self.tables[table][field].type.toLowerCase(); if (field_type.indexOf('date') === 0 || field_type.indexOf('timestamp') === 0) { if (_.endsWith(defaultVal, '()')) { val_text = "sequelize.fn('" + defaultVal.replace(/\(\)$/, '') + "')" } else if (_.includes(['current_timestamp', 'current_date', 'current_time', 'localtime', 'localtimestamp'], defaultVal.toLowerCase())) { val_text = "sequelize.literal('" + defaultVal + "')" } else { val_text = val_text } } else { val_text = val_text } } if (defaultVal === null || defaultVal === undefined) { return true; } else { val_text = _.isString(val_text) && !val_text.match(/^sequelize\.[^(]+\(.*\)$/) ? SqlString.escape(_.trim(val_text, '"'), null, self.options.dialect) : val_text; // don't prepend N for MSSQL when building models... val_text = _.trimStart(val_text, 'N'); self.tables[table][field][attr] = val_text; } } }); if (isUnique) { self.tables[table][field].unique = true } }); }); success(self, callback); // if (self.options.directory) { // return self.write(text, callback); // } else { // return self.write(false, callback); // } //return callback(false, text); } } } AutoCore.prototype.write = function(attributes, directory, callback) { var tables = _.keys(attributes); var self = this; mkdirp.sync(path.resolve(directory)); async.each(tables, createFile, callback); function createFile(table, _callback) { fs.writeFile(path.resolve(path.join(directory, table + '.js')), attributes[table], _callback); } } module.exports = AutoCore;