UNPKG

@themost/jspa

Version:
1,424 lines (1,380 loc) 50.1 kB
import { SchemaLoaderStrategy } from '@themost/data'; import { DataError } from '@themost/common'; import 'reflect-metadata'; class Duration extends String { constructor(value) { super(value); } } class Counter extends Number { constructor(value) { super(value); if (Counter.isCounter(this.valueOf()) === false) { throw new TypeError('Expected a valid counter.'); } } static isCounter(value) { return value != null && Number.isInteger(value); } } class Language extends String { constructor(value) { super(value); } } class Text extends String { constructor(value) { super(value); } } function ifNull(value, defaultValue) { if (value == null) { return defaultValue; } return value; } /** * Integer data type is a 32-bit signed two's complement integer. */ class Integer extends Number { constructor(value) { super(Integer.isInteger(ifNull(value, 0)) ? ifNull(value, 0) : NaN); } static isInteger(value) { return /^[+-]?\d+$/.test(value); } } /** * An integer containing only positive values (1,2,..) */ class PositiveInteger extends Number { constructor(value) { super(PositiveInteger.isPositiveInteger(ifNull(value, 1)) ? ifNull(value, 1) : NaN); } static isPositiveInteger(value) { return /^[+]?[1-9][0-9]*$/.test(value); } } /** * An integer containing only non-positive values (..,-2,-1,0) */ class NonPositiveInteger extends Number { constructor(value) { super(NonPositiveInteger.isNonPositiveInteger(ifNull(value, 0)) ? ifNull(value, 0) : NaN); } static isNonPositiveInteger(value) { return /^[-][0-9]*$/.test(value); } } /** * An integer containing only negative values (..,-2,-1) */ class NegativeInteger extends Number { constructor(value) { super(NegativeInteger.isNegativeInteger(ifNull(value, -1)) ? ifNull(value, -1) : NaN); } static isNegativeInteger(value) { return /^[-][1-9][0-9]*$/.test(value); } } /** * An integer containing only non-negative values (0,1,2,..) */ class NonNegativeInteger extends Number { constructor(value) { super(NonNegativeInteger.isNonNegativeInteger(ifNull(value, 0)) ? ifNull(value, 0) : NaN); } static isNonNegativeInteger(value) { return /^[+]?[0-9]*$/.test(value); } } class DateTime extends Date { constructor(value) { super(value); } } class EntityNotFoundException extends Error { constructor(msg) { super(msg || 'The specified entity cannot be found or is inaccessible'); } } class SymbolTypeNotSupportedException extends Error { constructor(msg) { super(msg || 'The current decorator does not support Symbol type property key'); } } var ColumnType; (function (ColumnType) { ColumnType["Float"] = "Float"; ColumnType["Boolean"] = "Boolean"; ColumnType["Date"] = "Date"; ColumnType["DateTime"] = "DateTime"; ColumnType["Integer"] = "Integer"; ColumnType["Short"] = "Short"; ColumnType["Counter"] = "Counter"; ColumnType["Duration"] = "Duration"; ColumnType["Number"] = "Number"; ColumnType["Text"] = "Text"; ColumnType["Time"] = "Time"; ColumnType["URL"] = "URL"; ColumnType["Language"] = "Language"; ColumnType["Model"] = "Model"; ColumnType["Guid"] = "Guid"; ColumnType["Object"] = "Object"; ColumnType["NegativeInteger"] = "NegativeInteger"; ColumnType["NegativeNumber"] = "NegativeNumber"; ColumnType["NonNegativeInteger"] = "NonNegativeInteger"; ColumnType["NonNegativeNumber"] = "NonNegativeNumber"; ColumnType["NonPositiveInteger"] = "NonPositiveInteger"; ColumnType["NonPositiveNumber"] = "NonPositiveNumber"; ColumnType["PositiveInteger"] = "PositiveInteger"; ColumnType["PositiveNumber"] = "PositiveNumber"; ColumnType["Email"] = "Email"; ColumnType["AbsoluteURI"] = "AbsoluteURI"; ColumnType["RelativeURI"] = "RelativeURI"; })(ColumnType || (ColumnType = {})); var InheritanceType; (function (InheritanceType) { InheritanceType[InheritanceType["Joined"] = 0] = "Joined"; InheritanceType[InheritanceType["SingleTable"] = 1] = "SingleTable"; InheritanceType[InheritanceType["TablePerClass"] = 2] = "TablePerClass"; })(InheritanceType || (InheritanceType = {})); var FetchType; (function (FetchType) { /** * Defines that data must be eagerly fetched. */ FetchType["Eager"] = "EAGER"; /** * Defines that data can be lazily fetched. */ FetchType["Lazy"] = "LAZY"; })(FetchType || (FetchType = {})); /** * Defines the set of cascadable operations that are propagated to the associated entity. */ var CascadeType; (function (CascadeType) { CascadeType[CascadeType["All"] = 31] = "All"; CascadeType[CascadeType["Detach"] = 1] = "Detach"; CascadeType[CascadeType["Merge"] = 2] = "Merge"; CascadeType[CascadeType["Persist"] = 4] = "Persist"; CascadeType[CascadeType["Refresh"] = 8] = "Refresh"; CascadeType[CascadeType["Remove"] = 16] = "Remove"; })(CascadeType || (CascadeType = {})); class OneToOneAssociationParser { constructor(model, target) { this.model = model; this.target = target; // } parse(column) { const oneToOneColumn = column; if (oneToOneColumn.oneToOne) { this.target.nullable = oneToOneColumn.oneToOne.optional != null ? oneToOneColumn.oneToOne.optional : true; this.target.many = true; this.target.multiplicity = 'ZeroOrOne'; if (oneToOneColumn.oneToOne.fetchType === FetchType.Eager) { this.target.expandable = true; } const tableAnnotation = column; if (tableAnnotation.joinTable) { // set parentModel (parentField is the primary key) if (tableAnnotation.joinTable.joinColumns.length != 1) { throw new DataError('E_ANNOTATION', 'One-to-one association joinColumns must be a single-item array.', null, this.model.name, this.target.name); } if (tableAnnotation.joinTable.inverseJoinColumns.length != 1) { throw new DataError('E_ANNOTATION', 'One-to-one association inverseJoinColumns must be a single-item array.', null, this.model.name, this.target.name); } this.target.mapping = { associationType: 'junction', associationAdapter: tableAnnotation.joinTable.name, parentModel: this.model.name, parentField: tableAnnotation.joinTable.joinColumns[0].referencedColumnName, associationObjectField: tableAnnotation.joinTable.joinColumns[0].name, childModel: typeof column.type === 'string' ? column.type : column.type.name, childField: tableAnnotation.joinTable.inverseJoinColumns[0].referencedColumnName, associationValueField: tableAnnotation.joinTable.inverseJoinColumns[0].name }; } else { this.target.mapping = { associationType: 'association', parentModel: this.model.name }; if (oneToOneColumn.oneToOne.mappedBy == null) { throw new DataError('E_ANNOTATION', 'One-to-one association must have a mapped column', null, this.model.name, this.target.name); } const targetEntity = oneToOneColumn.oneToOne.targetEntity; // set childModel, childfield if (targetEntity) { if (typeof targetEntity === 'string') { this.target.mapping.childModel = targetEntity; this.target.mapping.childField = oneToOneColumn.oneToOne.mappedBy; } else if (typeof targetEntity === 'function') { const targetEntityType = targetEntity; if (targetEntityType.Entity == null) { throw new DataError('E_ANNOTATION', 'Target entity type cannot be empty', null, this.model.name, this.target.name); } this.target.mapping.childModel = targetEntityType.Entity.name; this.target.mapping.childField = oneToOneColumn.oneToOne.mappedBy; } else { throw new DataError('E_ANNOTATION', 'Target entity has an invalid type. Expected string or class', null, this.model.name, this.target.name); } } else if (this.target.type) { this.target.mapping.childModel = this.target.type; this.target.mapping.childField = oneToOneColumn.oneToOne.mappedBy; } else { throw new DataError('E_ANNOTATION', 'One-to-one association target type cannot be found.', null, this.model.name, this.target.name); } } // set cascade type if (oneToOneColumn.oneToOne.cascadeType) { if ((oneToOneColumn.oneToOne.cascadeType & CascadeType.Remove) === CascadeType.Remove) { this.target.mapping.cascade = 'delete'; } else if ((oneToOneColumn.oneToOne.cascadeType & CascadeType.Persist) === CascadeType.Persist) { this.target.nested = true; } } // set privileges if (Array.isArray(oneToOneColumn.oneToOne.privileges)) { this.target.mapping.privileges = oneToOneColumn.oneToOne.privileges; } } } } class OneToManyAnnotationParser { constructor(model, target) { this.model = model; this.target = target; // } parse(column) { const oneToManyColumn = column; if (oneToManyColumn.oneToMany) { this.target.many = true; this.target.nullable = true; if (oneToManyColumn.oneToMany.fetchType === FetchType.Eager) { this.target.expandable = true; } // set association type this.target.mapping = { associationType: 'association' }; // set cascade if (oneToManyColumn.oneToMany.cascadeType != null) { if ((oneToManyColumn.oneToMany.cascadeType & CascadeType.Remove) === CascadeType.Remove) { this.target.mapping.cascade = 'delete'; } else if ((oneToManyColumn.oneToMany.cascadeType & CascadeType.Persist) === CascadeType.Persist) { this.target.nested = true; } } if (Array.isArray(oneToManyColumn.oneToMany.privileges)) { this.target.mapping.privileges = oneToManyColumn.oneToMany.privileges; } } } } class ManyToManyAnnotationParser { constructor(model, target) { this.model = model; this.target = target; // } parse(column) { const manyToManyColumn = column; if (manyToManyColumn.manyToMany) { this.target.many = true; this.target.nullable = true; this.target.mapping = { associationType: 'junction' }; // set exapndable if (manyToManyColumn.manyToMany.fetchType === FetchType.Eager) { this.target.expandable = true; } if (manyToManyColumn.type) { new ColumnAnnotationParser(this.model).getTypeString(manyToManyColumn.type); Object.assign(this.target.mapping, { parentModel: this.model.name, }); } else if (manyToManyColumn.manyToMany.targetEntity) { // eslint-disable-next-line @typescript-eslint/no-unused-vars new ColumnAnnotationParser(this.model).getTypeString(manyToManyColumn.manyToMany.targetEntity); Object.assign(this.target.mapping, { parentModel: manyToManyColumn.manyToMany.targetEntity, childModel: this.model.name }); } if (manyToManyColumn.manyToMany.mappedBy != null) { // todo: add parentModel, parentField, childModel, childField Object.assign(this.target.mapping, { mappedBy: manyToManyColumn.manyToMany.mappedBy }); } const joinTableColumn = column; if (joinTableColumn.joinTable) { Object.assign(this.target.mapping, { associationAdapter: joinTableColumn.joinTable.name }); if (Array.isArray(joinTableColumn.joinTable.joinColumns)) { const joinColumn = joinTableColumn.joinTable.joinColumns[0]; if (joinColumn) { this.target.mapping.associationObjectField = joinColumn.name; } } if (Array.isArray(joinTableColumn.joinTable.inverseJoinColumns)) { const inverseJoinColumn = joinTableColumn.joinTable.inverseJoinColumns[0]; if (inverseJoinColumn) { this.target.mapping.associationValueField = inverseJoinColumn.name; } } if (Array.isArray(manyToManyColumn.manyToMany.privileges)) { this.target.mapping.privileges = manyToManyColumn.manyToMany.privileges; } } } } } class ElementCollectionAnnotationParser { constructor(model, target) { this.model = model; this.target = target; // } parse(column) { const annotation = column; if (annotation.elementCollection) { this.target.many = true; this.target.nullable = annotation.elementCollection.optional === false ? false : true; this.target.mapping = { associationType: 'junction', cascade: 'delete', associationObjectField: 'object', associationValueField: 'value' }; // set exapndable if (annotation.elementCollection.fetchType === FetchType.Eager) { this.target.expandable = true; } this.target.type = new ColumnAnnotationParser(this.model).getTypeString(annotation.elementCollection.targetClass); Object.assign(this.target.mapping, { parentModel: this.model.name }); const joinTableColumn = column; if (joinTableColumn.joinTable) { Object.assign(this.target.mapping, { associationAdapter: joinTableColumn.joinTable.name }); if (Array.isArray(joinTableColumn.joinTable.joinColumns)) { const joinColumn = joinTableColumn.joinTable.joinColumns[0]; if (joinColumn && joinColumn.name) { this.target.mapping.associationObjectField = joinColumn.name; } if (joinColumn && joinColumn.referencedColumnName) { this.target.mapping.parentField = joinColumn.referencedColumnName; } } if (Array.isArray(joinTableColumn.joinTable.inverseJoinColumns)) { const inverseJoinColumn = joinTableColumn.joinTable.inverseJoinColumns[0]; if (inverseJoinColumn && inverseJoinColumn.name) { this.target.mapping.associationValueField = inverseJoinColumn.name; } } if (Array.isArray(annotation.elementCollection.privileges)) { this.target.mapping.privileges = annotation.elementCollection.privileges; } } } } } class ColumnAnnotationParser { constructor(model) { this.model = model; // } getTypeString(type) { let columnType; if (typeof type === 'string') { return type; } else { const targetType = type; if (targetType.Entity) { columnType = targetType.Entity.name; } else { columnType = targetType.name; } return columnType; } } } class EntityLoaderStrategy extends SchemaLoaderStrategy { constructor(config) { super(config); this.imports = []; this.models = new Map(); } getModelDefinition(name) { const model = this.models.get(name); if (model == null) { return null; } if (typeof model === 'function') { const modelDefinition = this.getModelFromEntityClass(model); this.models.set(name, modelDefinition); return modelDefinition; } return model; } setModelDefinition(data) { this.models = this.models || new Map(); this.models.set(data.name, data); return this; } reload() { this.readSync(); } readSync() { const models = new Map(); for (const module of this.imports) { Object.keys(module).forEach((member) => { if (Object.prototype.hasOwnProperty.call(module, member)) { const exportedMember = module[member]; if (typeof exportedMember === 'function') { const entityAnnotation = exportedMember; if (entityAnnotation.Entity && entityAnnotation.Entity.name) { models.set(entityAnnotation.Entity.name, exportedMember); } } } }); } this.models = models; return Array.from(this.models.keys()); } getModels() { if (this.models == null) { return this.readSync(); } return Array.from(this.models.keys()); } // eslint-disable-next-line @typescript-eslint/no-explicit-any getModelFromEntityClass(entityClass) { if (Object.prototype.hasOwnProperty.call(entityClass, 'Entity') === false) { return null; } // get entity type annotation const entityType = entityClass; // prepare schema const result = { name: entityType.Entity.name, version: entityType.Entity.version || '1.0.0', abstract: false, hidden: true, caching: 'none', inherits: null, implements: null, fields: [], constraints: [], eventListeners: [], privileges: [ { mask: 15, type: 'global' }, { mask: 15, type: 'global', account: 'Administrators' } ] }; if (entityType.Entity && entityType.Entity.privileges && entityType.Entity.privileges.length > 0) { result.privileges = entityType.Entity.privileges; } // set inherits if (entityClass.__proto__) { const inheritedModel = this.getModelFromEntityClass(entityClass.__proto__); if (inheritedModel != null) { // validate inherited entity table const entityInheritance = entityClass.__proto__; if (entityInheritance.Inheritance && entityInheritance.Inheritance.strategy === InheritanceType.SingleTable) { throw new DataError('E_INHERITANCE', 'Single table inheritance is not supported by @themost/jspa. Please use Joined or TablePerClass strategies', null, result.name); } if (entityInheritance.Inheritance && entityInheritance.Inheritance.strategy === InheritanceType.TablePerClass) { result.implements = inheritedModel.name; } else { result.inherits = inheritedModel.name; } } } const edmEntityType = entityType; if (edmEntityType && edmEntityType.entityTypeDecorator) { result.hidden = false; } // get table annotation if (Object.prototype.hasOwnProperty.call(entityClass, 'Table') === true) { const entityTable = entityClass; if (entityTable.Table != null) { // set source result.source = entityTable.Table.name; // todo: set view if (Array.isArray(entityTable.Table.uniqueConstraints)) { // set constraints result.constraints = entityTable.Table.uniqueConstraints.map((item) => { return { type: 'unique', fields: item.columnNames }; }); } } } if (Object.prototype.hasOwnProperty.call(entityClass, 'Column') === true) { const entityColumns = entityClass; if (entityColumns.Column) { for (const column of entityColumns.Column.values()) { // set column type let columnType; if (typeof column.type === 'string') { columnType = column.type; } else if (typeof column.type === 'function') { const targetType = column.type; if (targetType.Entity) { columnType = targetType.Entity.name; } else { columnType = targetType.name; } } const field = { name: column.name, type: columnType, nullable: column.nullable, readonly: Object.prototype.hasOwnProperty.call(column, 'insertable') ? !column.insertable : false, editable: Object.prototype.hasOwnProperty.call(column, 'updatable') ? column.updatable : true }; // set additional type if (column.additionalType) { let columnAdditionalType; if (typeof column.additionalType === 'string') { columnAdditionalType = column.additionalType; } else if (typeof column.additionalType === 'function') { const targetType = column.additionalType; if (targetType.Entity) { columnAdditionalType = targetType.Entity.name; } else { columnAdditionalType = targetType.name; } } field.additionalType = columnAdditionalType; } // set description if (column.length) { field.description = column.description; } // set size if (column.length) { field.size = column.length; } // set scale if (Object.prototype.hasOwnProperty.call(column, 'scale')) { field.scale = column.scale; } // set validation const columnValidation = column; if (columnValidation.validation) { // eslint-disable-next-line @typescript-eslint/no-explicit-any field.validation = columnValidation.validation; } // set primary const idColumn = column; if (idColumn.id) { field.primary = true; if (field.type == null) { // field type has not been set // set default type to Counter (auto increment identity) field.type = 'Counter'; } } // set nested const embeddedColumn = column; if (embeddedColumn.embedded) { field.nested = true; } // set expandable const manyToOneColumn = column; if (manyToOneColumn.manyToOne && manyToOneColumn.manyToOne.fetchType === FetchType.Eager) { field.expandable = true; } const manyToManyColumn = column; if (manyToManyColumn.manyToMany) { new ManyToManyAnnotationParser(result, field).parse(column); } const elementCollectionColumn = column; if (elementCollectionColumn.elementCollection) { new ElementCollectionAnnotationParser(result, field).parse(column); } // one-to-many const oneToManyColumn = column; if (oneToManyColumn.oneToMany) { new OneToManyAnnotationParser(result, field).parse(column); } // one-to-one const oneToOneColumn = column; if (oneToOneColumn.oneToOne) { new OneToOneAssociationParser(result, field).parse(column); } result.fields.push(field); } } } result.eventListeners = [ { type: '@themost/data/previous-state-listener' }, { type: '@themost/jspa/listener' } ]; // set class Object.assign(result, { DataObjectClass: entityClass }); return result; } } var PrivilegeMask; (function (PrivilegeMask) { PrivilegeMask[PrivilegeMask["Read"] = 1] = "Read"; PrivilegeMask[PrivilegeMask["Create"] = 2] = "Create"; PrivilegeMask[PrivilegeMask["Update"] = 4] = "Update"; PrivilegeMask[PrivilegeMask["Delete"] = 8] = "Delete"; PrivilegeMask[PrivilegeMask["Execute"] = 16] = "Execute"; PrivilegeMask[PrivilegeMask["Full"] = 31] = "Full"; })(PrivilegeMask || (PrivilegeMask = {})); function Permission(items) { return (target) => { const targetItem = target; if (Array.isArray(items) && items.length === 0) { targetItem.privileges = items; } else { targetItem.privileges = [ { mask: PrivilegeMask.Full, type: 'global' } ]; } }; } function Entity(annotation) { return (target) => { const entityType = target; let embeddable = false; if (entityType.Entity && entityType.Entity.name === target.name) { embeddable = entityType.Entity.embeddable; } entityType.Entity = Object.assign({ name: target.name, version: '1.0.0' }, annotation); if (embeddable) { Object.assign(entityType.Entity, { embeddable }); } // set privileges Permission(annotation && annotation.privileges); }; } function Table(annotation) { return (target) => { const table = target; table.Table = Object.assign({ name: `${target.name}Base` }, annotation); }; } function Column(annotation) { return (target, propertyKey) => { if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; const column = columns.Column.get(propertyKey); const value = Object.assign({ name: propertyKey, nullable: true, insertable: true, updatable: true }, column, annotation); if (value.type == null) { // get metadata // eslint-disable-next-line @typescript-eslint/no-explicit-any const r = Reflect.getMetadata('design:type', target, propertyKey); if (r && r.name) { value.type = r.name; } } if (value.entity == null) { const targetEntity = target.constructor; if (targetEntity.Entity) { value.entity = targetEntity.Entity.name; } } columns.Column.set(propertyKey, value); }; } function Basic() { return Column(); } function JoinTable(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set value property Object.assign(column, { joinTable: annotation }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function Id() { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set generated value property Object.assign(column, { id: true }); // finally, reset column annotation columns.Column.set(propertyKey, column); }; } var GenerationType; (function (GenerationType) { GenerationType["Auto"] = "AUTO"; GenerationType["Identity"] = "IDENTITY"; GenerationType["Sequence"] = "SEQUENCE"; GenerationType["Table"] = "TABLE"; })(GenerationType || (GenerationType = {})); function GeneratedValue(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; const value = Object.assign({ strategy: GenerationType.Identity }, annotation); let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set generated value property Object.assign(column, { generatedValue: value, nullable: false, updatable: false }); // finally, reset column annotation columns.Column.set(propertyKey, column); }; } function ManyToOne(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get value const value = Object.assign({ fetchType: FetchType.Eager, optional: true }, annotation); Permission(annotation.privileges); // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set value property Object.assign(column, { manyToOne: value }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function ManyToMany(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get value const value = Object.assign({ fetchType: FetchType.Lazy, optional: true }, annotation); // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // try to find target entity let targetEntity; if (typeof annotation.targetEntity === 'function') { targetEntity = annotation.targetEntity.name; } else if (typeof annotation.targetEntity === 'string') { targetEntity = annotation.targetEntity; } else { // get type from reflect metadata const r = Reflect.getMetadata('design:type', target, propertyKey); if (r && r.name) { targetEntity = r.name; } } Permission(annotation.privileges); // set value property Object.assign(column, { manyToMany: value }); if (targetEntity) { Object.assign(column, { type: targetEntity }); } // finally, set column annotation columns.Column.set(propertyKey, column); }; } function OneToMany(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } // get columns const columns = target.constructor; // prepare annotation const oneToMany = Object.assign({ fetchType: FetchType.Lazy, optional: true }, annotation); // get column let column = columns.Column.get(propertyKey); if (column == null) { Column()(target, propertyKey); column = columns.Column.get(propertyKey); } Permission(annotation.privileges); // set value property Object.assign(column, { oneToMany }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function OneToOne(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get value const value = Object.assign({ fetchType: FetchType.Lazy, optional: true }, annotation); // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // try to find target entity let targetEntity; if (typeof annotation.targetEntity === 'function') { targetEntity = annotation.targetEntity.name; } else if (typeof annotation.targetEntity === 'string') { targetEntity = annotation.targetEntity; } else { // get type from reflect metadata const r = Reflect.getMetadata('design:type', target, propertyKey); if (r && r.name) { targetEntity = r.name; } } if (targetEntity == null) { throw new EntityNotFoundException(); } Permission(annotation.privileges); // set value property Object.assign(column, { oneToOne: value, type: targetEntity }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function Formula(closure) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set value property Object.assign(column, { formula: { closure } }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function ColumnDefault(closure) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set value property Object.assign(column, { columnDefault: { closure } }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function Inheritance(annotation) { return (target) => { const entityInheritance = target; entityInheritance.Inheritance = Object.assign({ strategy: InheritanceType.Joined }, annotation); }; } function AttributeOverride(annotation) { return (target) => { if (Object.prototype.hasOwnProperty.call(target, 'AttributeOverrides') === false) { Object.assign(target, { AttributeOverrides: [] }); } const entity = target; entity.AttributeOverrides.push(annotation); }; } function Embedded() { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } Column({ name: propertyKey })(target, propertyKey); const columns = target.constructor; const column = columns.Column.get(propertyKey); Object.assign(column, { embedded: true }); columns.Column.set(propertyKey, column); }; } function Embeddable() { return (target) => { Entity({ embeddable: true })(target); }; } function EntityListeners(...value) { return (target) => { if (Object.prototype.hasOwnProperty.call(target, 'EntityListeners') === false) { Object.assign(target, { EntityListeners: [] }); } const entityClass = target; entityClass.EntityListeners.push(...value); }; } function SetCallbackMethod(method) { return (target, propertyKey, propertyDescriptor) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'CallbackMethods') === false) { Object.assign(target.constructor, { CallbackMethods: [] }); } const entityClass = target.constructor; entityClass.CallbackMethods.push({ type: method, name: `${target.constructor.name}.${propertyKey}`, callback: propertyDescriptor.value }); }; } function PreLoad() { return SetCallbackMethod(PreLoad); } function PostLoad() { return SetCallbackMethod(PostLoad); } function PrePersist() { return SetCallbackMethod(PrePersist); } function PostPersist() { return SetCallbackMethod(PostPersist); } function PreUpdate() { return SetCallbackMethod(PreUpdate); } function PostUpdate() { return SetCallbackMethod(PostUpdate); } function PreRemove() { return SetCallbackMethod(PreRemove); } function PostRemove() { return SetCallbackMethod(PostRemove); } function PreInit() { return SetCallbackMethod(PreInit); } function PostInit() { return SetCallbackMethod(PostInit); } function CollectionTable(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // set value property Object.assign(column, { joinTable: annotation }); // finally, set column annotation columns.Column.set(propertyKey, column); }; } function ElementCollection(annotation) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; // get value const value = Object.assign({ fetchType: FetchType.Lazy, optional: true }, annotation); // get column let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } // try to find target entity let targetClass; if (typeof annotation.targetClass === 'function') { targetClass = annotation.targetClass.name; } else if (typeof annotation.targetClass === 'string') { targetClass = annotation.targetClass; } else { // get type from reflect metadata // eslint-disable-next-line @typescript-eslint/no-explicit-any const r = Reflect.getMetadata('design:type', target, propertyKey); if (r && r.name) { targetClass = r.name; } } // eslint-disable-next-line @typescript-eslint/no-explicit-any Permission(annotation.privileges)(value); // set value property Object.assign(column, { elementCollection: value }); if (targetClass) { Object.assign(column, { type: targetClass }); } // finally, set column annotation columns.Column.set(propertyKey, column); }; } function tryGetColumn(target, propertyKey) { if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; let column = columns.Column.get(propertyKey); if (column == null) { column = { name: propertyKey }; } return column; } function trySetColumn(target, propertyKey, column) { if (Object.prototype.hasOwnProperty.call(target.constructor, 'Column') === false) { Object.assign(target.constructor, { Column: new Map() }); } const columns = target.constructor; columns.Column.set(propertyKey, column); } function Pattern(pattern, patternMessage) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } // get column annotation const column = tryGetColumn(target, propertyKey); const value = { pattern, patternMessage }; // assign value Object.assign(column, { validation: value }); // set column annotation trySetColumn(target, propertyKey, column); }; } function Size(minLength, maxLength, message) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } // get column annotation const column = tryGetColumn(target, propertyKey); const value = { minLength, maxLength, message }; // assign value Object.assign(column, { validation: value }); // set column annotation trySetColumn(target, propertyKey, column); }; } function Min(minValue, message) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } // get column annotation const column = tryGetColumn(target, propertyKey); const value = { minValue, message }; // assign value Object.assign(column, { validation: value }); // set column annotation trySetColumn(target, propertyKey, column); }; } function Max(maxValue, message) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } // get column annotation const column = tryGetColumn(target, propertyKey); const value = { maxValue, message }; // assign value Object.assign(column, { validation: value }); // set column annotation trySetColumn(target, propertyKey, column); }; } function Range(minValue, maxValue, message) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } // get column annotation const column = tryGetColumn(target, propertyKey); const value = { minValue, maxValue, message }; // assign value Object.assign(column, { validation: value }); // set column annotation trySetColumn(target, propertyKey, column); }; } function Validate(validator, message) { return (target, propertyKey) => { if (typeof propertyKey === 'symbol') { throw new SymbolTypeNotSupportedException(); } // get column annotation const column = tryGetColumn(target, propertyKey); const value = { validator, message }; // assign value Object.assign(column, { validation: value }); // set column annotation trySetColumn(target, propertyKey, column); }; } export { AttributeOverride, Basic, CascadeType, CollectionTable, Column, ColumnDefault, ColumnType, Counter, DateTime, Duration, ElementCollection, Embeddable, Embedded, Entity, EntityListeners, EntityLoaderStrategy, EntityNotFoundException, FetchType, Formula, GeneratedValue, GenerationType, Id, Inheritance, InheritanceType, Integer, JoinTable, Language, ManyToMany, ManyToOne, Max, Min, NegativeInteger, NonNegativeInteger, NonPositiveInteger, OneToMany, OneToOne, Pattern, Permission, PositiveInteger, PostInit, PostLoad, PostPersist, PostRemove, PostUpdate, PreInit, PreLoad, PrePersist, PreRemove, PreUpdate, PrivilegeMask, Range, Size, SymbolTypeNotSupportedException, Table, T