sequelize-auto
Version:
Automatically generate bare sequelize models from your database.
133 lines • 7.13 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AutoRelater = void 0;
const lodash_1 = __importDefault(require("lodash"));
const types_1 = require("./types");
/** Constructs entity relationships from TableData.foreignKeys and populates TableData.relations */
class AutoRelater {
constructor(options) {
this.caseModel = options.caseModel || 'o';
this.caseProp = options.caseProp || 'o';
this.singularize = options.singularize;
this.pkSuffixes = options.pkSuffixes || [];
if (!this.pkSuffixes || this.pkSuffixes.length == 0) {
this.pkSuffixes = ["id"];
}
this.relations = [];
this.usedChildNames = new Set();
}
/** Create Relations from the foreign keys, and add to TableData */
buildRelations(td) {
const fkTables = lodash_1.default.keys(td.foreignKeys).sort();
fkTables.forEach(t => {
const fkFields = td.foreignKeys[t];
const fkFieldNames = lodash_1.default.keys(fkFields);
fkFieldNames.forEach(fkFieldName => {
const spec = fkFields[fkFieldName];
if (spec.isForeignKey) {
this.addRelation(t, fkFieldName, spec, fkFields);
}
});
});
td.relations = lodash_1.default.sortBy(this.relations, ['parentTable', 'childTable']);
return td;
}
/** Create a Relation object for the given foreign key */
addRelation(table, fkFieldName, spec, fkFields) {
const [schemaName, tableName] = (0, types_1.qNameSplit)(table);
const schema = schemaName;
const modelName = (0, types_1.recase)(this.caseModel, tableName, this.singularize);
const targetModel = (0, types_1.recase)(this.caseModel, spec.foreignSources.target_table, this.singularize);
const alias = this.getAlias(fkFieldName, spec.foreignSources.target_table, spec.foreignSources.source_table);
const childAlias = this.getChildAlias(fkFieldName, spec.foreignSources.source_table, spec.foreignSources.target_table);
const sourceProp = (0, types_1.recase)(this.caseProp, fkFieldName);
// use "hasOne" cardinality if this FK is also a single-column Primary or Unique key; else "hasMany"
const isOne = ((spec.isPrimaryKey && !lodash_1.default.some(fkFields, f => f.isPrimaryKey && f.source_column !== fkFieldName) ||
(!!spec.isUnique && !lodash_1.default.some(fkFields, f => f.isUnique === spec.isUnique && f.source_column !== fkFieldName))));
this.relations.push({
parentId: sourceProp,
parentModel: targetModel,
parentProp: alias,
parentTable: (0, types_1.qNameJoin)(spec.foreignSources.target_schema || schema, spec.foreignSources.target_table),
childModel: modelName,
childProp: isOne ? (0, types_1.singularize)(childAlias) : (0, types_1.pluralize)(childAlias),
childTable: (0, types_1.qNameJoin)(spec.foreignSources.source_schema || schema, spec.foreignSources.source_table),
isOne: isOne,
isM2M: false
});
if (spec.isPrimaryKey) {
// if FK is also part of the PK, see if there is a "many-to-many" junction
const otherKeys = lodash_1.default.filter(fkFields, k => k.isForeignKey && k.isPrimaryKey && k.source_column !== fkFieldName);
if (otherKeys.length === 1) {
const otherKey = otherKeys[0];
const otherModel = (0, types_1.recase)(this.caseModel, otherKey.foreignSources.target_table, this.singularize);
const otherProp = this.getAlias(otherKey.source_column, otherKey.foreignSources.target_table, otherKey.foreignSources.source_table, true);
const otherId = (0, types_1.recase)(this.caseProp, otherKey.source_column);
this.relations.push({
parentId: sourceProp,
parentModel: targetModel,
parentProp: (0, types_1.pluralize)(alias),
parentTable: (0, types_1.qNameJoin)(spec.foreignSources.target_schema || schema, spec.foreignSources.target_table),
childModel: otherModel,
childProp: (0, types_1.pluralize)(otherProp),
childTable: (0, types_1.qNameJoin)(otherKey.foreignSources.target_schema || schema, otherKey.foreignSources.target_table),
childId: otherId,
joinModel: modelName,
isOne: isOne,
isM2M: true
});
}
}
}
/** Convert foreign key name into alias name for belongsTo relations */
getAlias(fkFieldName, modelName, targetModel, isM2M = false) {
let name = this.trimId(fkFieldName);
if (name === fkFieldName || isM2M) {
name = fkFieldName + "_" + modelName;
}
// singularize in case one column name is the singularized form of another column in the same model
let singleName = (0, types_1.singularize)(name);
if (isM2M) {
if (this.usedChildNames.has(modelName + "." + singleName)) {
name = name + "_" + targetModel;
}
this.usedChildNames.add(modelName + "." + (0, types_1.singularize)(name));
}
else {
if (this.usedChildNames.has(targetModel + "." + singleName)) {
name = name + "_" + modelName;
}
this.usedChildNames.add(targetModel + "." + (0, types_1.singularize)(name));
}
return (0, types_1.recase)(this.caseProp, name, true);
}
/** Convert foreign key name into alias name for hasMany/hasOne relations */
getChildAlias(fkFieldName, modelName, targetModel) {
let name = modelName;
// usedChildNames prevents duplicate names in same model
if (this.usedChildNames.has(targetModel + "." + (0, types_1.singularize)(name))) {
name = this.trimId(fkFieldName);
name = name + "_" + modelName;
}
// singularize in case one column name is the singularized form of another column in the same model
name = (0, types_1.singularize)(name);
this.usedChildNames.add(targetModel + "." + name);
return (0, types_1.recase)(this.caseProp, name, true);
}
trimId(name) {
this.pkSuffixes.forEach(suffix => {
if (name.length > (suffix.length + 1) && name.toLowerCase().endsWith(suffix.toLowerCase())) {
name = name.substring(0, name.length - suffix.length);
}
});
if (name.endsWith("_")) {
name = name.substring(0, name.length - 1);
}
return name;
}
}
exports.AutoRelater = AutoRelater;
//# sourceMappingURL=auto-relater.js.map