@sqb/connect
Version:
Multi-dialect database connection framework written with TypeScript
328 lines (327 loc) • 13.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntityMetadata = void 0;
const builder_1 = require("@sqb/builder");
const orm_const_js_1 = require("../orm.const.js");
const orm_helper_js_1 = require("../util/orm.helper.js");
const association_js_1 = require("./association.js");
const association_field_metadata_js_1 = require("./association-field-metadata.js");
const column_field_metadata_js_1 = require("./column-field-metadata.js");
const embedded_field_metadata_js_1 = require("./embedded-field-metadata.js");
var EntityMetadata;
(function (EntityMetadata) {
function define(ctor) {
const own = getOwn(ctor);
if (own)
return own;
const baseMeta = get(ctor);
const meta = {
ctor: ctor,
name: ctor.name,
fields: {},
indexes: [],
foreignKeys: [],
eventListeners: {},
};
Reflect.defineMetadata(orm_const_js_1.ENTITY_METADATA_KEY, meta, ctor);
// Merge base entity columns into this one
if (baseMeta) {
EntityMetadata.mixin(meta, baseMeta);
}
return meta;
}
EntityMetadata.define = define;
function get(ctor) {
return Reflect.getMetadata(orm_const_js_1.ENTITY_METADATA_KEY, ctor);
}
EntityMetadata.get = get;
function getOwn(ctor) {
return Reflect.getOwnMetadata(orm_const_js_1.ENTITY_METADATA_KEY, ctor);
}
EntityMetadata.getOwn = getOwn;
function getField(entity, fieldName) {
return fieldName ? entity.fields[fieldName.toLowerCase()] : undefined;
}
EntityMetadata.getField = getField;
function getColumnField(entity, fieldName) {
const el = getField(entity, fieldName);
if (el && !(0, orm_helper_js_1.isColumnField)(el))
throw new Error(`"${el.name}" requested as "column" but it is "${el.kind}"`);
return el;
}
EntityMetadata.getColumnField = getColumnField;
function getEmbeddedField(entity, fieldName) {
const el = getField(entity, fieldName);
if (el && !(0, orm_helper_js_1.isEmbeddedField)(el))
throw new Error(`"${el.name}" requested as "embedded" but it is "${el.kind}"`);
return el;
}
EntityMetadata.getEmbeddedField = getEmbeddedField;
function getAssociationField(entity, fieldName) {
const el = getField(entity, fieldName);
if (el && !(0, orm_helper_js_1.isAssociationField)(el)) {
throw new Error(`"${el.name}" requested as "association" but it is "${el.kind}"`);
}
return el;
}
EntityMetadata.getAssociationField = getAssociationField;
function findField(entity, predicate) {
return Object.values(entity.fields).find(predicate);
}
EntityMetadata.findField = findField;
function getColumnFieldByFieldName(entity, fieldName) {
if (!fieldName)
return;
fieldName = fieldName.toLowerCase();
for (const prop of Object.values(entity.fields)) {
if ((0, orm_helper_js_1.isColumnField)(prop) && prop.fieldName.toLowerCase() === fieldName)
return prop;
}
}
EntityMetadata.getColumnFieldByFieldName = getColumnFieldByFieldName;
function getFieldNames(entity, filter) {
if (filter) {
const out = [];
for (const el of Object.values(entity.fields)) {
if (el && (!filter || filter(el)))
out.push(el.name);
}
return out;
}
// Create a cached name array
if (!Object.prototype.hasOwnProperty.call(entity, '_fieldNames')) {
Object.defineProperty(entity, '_fieldNames', {
enumerable: false,
configurable: true,
writable: true,
value: Object.values(entity.fields).map(m => m.name),
});
}
return entity._fieldNames;
}
EntityMetadata.getFieldNames = getFieldNames;
function getColumnFieldNames(entity) {
return getFieldNames(entity, orm_helper_js_1.isColumnField);
}
EntityMetadata.getColumnFieldNames = getColumnFieldNames;
function getEmbeddedFieldNames(entity) {
return getFieldNames(entity, orm_helper_js_1.isEmbeddedField);
}
EntityMetadata.getEmbeddedFieldNames = getEmbeddedFieldNames;
function getAssociationFieldNames(entity) {
return getFieldNames(entity, orm_helper_js_1.isAssociationField);
}
EntityMetadata.getAssociationFieldNames = getAssociationFieldNames;
function getNonAssociationFieldNames(entity) {
return getFieldNames(entity, x => !(0, orm_helper_js_1.isAssociationField)(x));
}
EntityMetadata.getNonAssociationFieldNames = getNonAssociationFieldNames;
function getInsertColumnNames(entity) {
return getFieldNames(entity, x => (0, orm_helper_js_1.isColumnField)(x) && !x.noInsert);
}
EntityMetadata.getInsertColumnNames = getInsertColumnNames;
function getUpdateColumnNames(entity) {
return getFieldNames(entity, x => (0, orm_helper_js_1.isColumnField)(x) && !x.noUpdate);
}
EntityMetadata.getUpdateColumnNames = getUpdateColumnNames;
function getPrimaryIndex(entity) {
return entity.indexes && entity.indexes.find(idx => idx.primary);
}
EntityMetadata.getPrimaryIndex = getPrimaryIndex;
function getPrimaryIndexColumns(entity) {
const idx = getPrimaryIndex(entity);
const out = [];
if (idx) {
for (const k of idx.columns) {
const col = getColumnField(entity, k);
if (!col)
throw new Error(`Data column "${k}" in primary index of ${entity.name} does not exists`);
out.push(col);
}
}
return out;
}
EntityMetadata.getPrimaryIndexColumns = getPrimaryIndexColumns;
async function getForeignKeyFor(src, trg) {
if (!src.foreignKeys)
return;
for (const f of src.foreignKeys) {
if ((await f.resolveTarget()) === trg)
return f;
}
}
EntityMetadata.getForeignKeyFor = getForeignKeyFor;
function addIndex(entity, index) {
entity.indexes = entity.indexes || [];
index = {
...index,
columns: Array.isArray(index.columns) ? index.columns : [index.columns],
};
if (index.primary)
entity.indexes.forEach(idx => delete idx.primary);
entity.indexes.push(index);
}
EntityMetadata.addIndex = addIndex;
function addForeignKey(entity, propertyKey, target, targetKey) {
entity.foreignKeys = entity.foreignKeys || [];
const fk = new association_js_1.Association(entity.name + '.' + propertyKey, {
source: entity.ctor,
sourceKey: propertyKey,
target,
targetKey,
});
entity.foreignKeys.push(fk);
}
EntityMetadata.addForeignKey = addForeignKey;
function addEventListener(entity, event, fn) {
if (typeof fn !== 'function')
throw new Error('Property must be a function');
entity.eventListeners = entity.eventListeners || {};
entity.eventListeners[event] = entity.eventListeners[event] || [];
entity.eventListeners[event].push(fn);
}
EntityMetadata.addEventListener = addEventListener;
function defineColumnField(entity, name, options = {}) {
delete entity._fieldNames;
let prop = EntityMetadata.getField(entity, name);
if ((0, orm_helper_js_1.isColumnField)(prop))
options = { ...prop, ...options };
if (!options.type) {
switch (options.dataType) {
case builder_1.DataType.BOOL:
options.type = Boolean;
break;
case builder_1.DataType.VARCHAR:
case builder_1.DataType.CHAR:
case builder_1.DataType.TEXT:
options.type = String;
break;
case builder_1.DataType.NUMBER:
case builder_1.DataType.DOUBLE:
case builder_1.DataType.FLOAT:
case builder_1.DataType.INTEGER:
case builder_1.DataType.SMALLINT:
options.type = Number;
break;
case builder_1.DataType.TIMESTAMP:
case builder_1.DataType.TIMESTAMPTZ:
options.type = Date;
break;
case builder_1.DataType.BINARY:
options.type = Buffer;
break;
default:
options.type = String;
}
}
if (!options.dataType) {
switch (options.type) {
case Boolean:
options.dataType = builder_1.DataType.BOOL;
break;
case Number:
options.dataType = builder_1.DataType.NUMBER;
break;
case Date:
options.dataType = builder_1.DataType.TIMESTAMP;
break;
case Array:
options.dataType = builder_1.DataType.VARCHAR;
options.isArray = true;
break;
case Buffer:
options.dataType = builder_1.DataType.BINARY;
break;
default:
options.dataType = builder_1.DataType.VARCHAR;
}
}
prop = column_field_metadata_js_1.ColumnFieldMetadata.create(entity, name, options);
entity.fields[name.toLowerCase()] = prop;
return prop;
}
EntityMetadata.defineColumnField = defineColumnField;
function defineEmbeddedField(entity, name, type, options) {
delete entity._fieldNames;
let prop = EntityMetadata.getField(entity, name);
if ((0, orm_helper_js_1.isEmbeddedField)(prop))
options = { ...prop, ...options };
prop = embedded_field_metadata_js_1.EmbeddedFieldMetadata.create(entity, name, type, options);
entity.fields[name.toLowerCase()] = prop;
return prop;
}
EntityMetadata.defineEmbeddedField = defineEmbeddedField;
function defineAssociationField(entity, propertyKey, association, options) {
delete entity._fieldNames;
const prop = association_field_metadata_js_1.AssociationFieldMetadata.create(entity, propertyKey, association, options);
let l = association;
let i = 1;
while (l) {
l.name = entity.name + '.' + propertyKey + '#' + i++;
l = l.next;
}
entity.fields[propertyKey.toLowerCase()] = prop;
return prop;
}
EntityMetadata.defineAssociationField = defineAssociationField;
function setPrimaryKeys(entity, column, options) {
addIndex(entity, {
...options,
columns: Array.isArray(column) ? column : [column],
unique: true,
primary: true,
});
}
EntityMetadata.setPrimaryKeys = setPrimaryKeys;
function mixin(derived, base, filter) {
const hasField = (k) => !filter || filter(k);
delete derived._fieldNames;
if (!derived.tableName) {
derived.tableName = base.tableName;
derived.schema = base.schema;
derived.comment = base.comment;
}
// Copy indexes
if (base.indexes && base.indexes.length) {
const hasPrimaryIndex = !!getPrimaryIndex(derived);
for (const idx of base.indexes) {
if (!idx.columns.find(x => !hasField(x))) {
if (hasPrimaryIndex)
addIndex(derived, { ...idx, primary: undefined });
else
addIndex(derived, idx);
}
}
}
// Copy foreign indexes
if (base.foreignKeys && base.foreignKeys.length) {
derived.foreignKeys = derived.foreignKeys || [];
for (const fk of base.foreignKeys) {
if (!fk.sourceKey || hasField(fk.sourceKey)) {
const newFk = new association_js_1.Association(fk.name, {
...fk,
source: derived.ctor,
});
derived.foreignKeys.push(newFk);
}
}
}
// Copy event listeners
if (base.eventListeners) {
for (const [event, arr] of Object.entries(base.eventListeners)) {
arr.forEach(fn => EntityMetadata.addEventListener(derived, event, fn));
}
}
// Copy fields
derived.fields = derived.fields || {};
for (const [n, p] of Object.entries(base.fields)) {
if (!hasField(n))
continue;
const o = Object.assign({}, p);
o.entity = derived;
Object.setPrototypeOf(o, Object.getPrototypeOf(p));
derived.fields[n] = o;
}
}
EntityMetadata.mixin = mixin;
})(EntityMetadata || (exports.EntityMetadata = EntityMetadata = {}));