UNPKG

@sqb/connect

Version:

Multi-dialect database connection framework written with TypeScript

328 lines (327 loc) 13.3 kB
"use strict"; 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 = {}));