import { EntitySchema } from "./EntitySchema" import { MetadataArgsStorage } from "../metadata-args/MetadataArgsStorage" import { TableMetadataArgs } from "../metadata-args/TableMetadataArgs" import { ColumnMetadataArgs } from "../metadata-args/ColumnMetadataArgs" import { IndexMetadataArgs } from "../metadata-args/IndexMetadataArgs" import { RelationMetadataArgs } from "../metadata-args/RelationMetadataArgs" import { JoinColumnMetadataArgs } from "../metadata-args/JoinColumnMetadataArgs" import { JoinTableMetadataArgs } from "../metadata-args/JoinTableMetadataArgs" import { JoinTableOptions } from "../decorator/options/JoinTableOptions" import { JoinTableMultipleColumnsOptions } from "../decorator/options/JoinTableMultipleColumnsOptions" import { ColumnMode } from "../metadata-args/types/ColumnMode" import { GeneratedMetadataArgs } from "../metadata-args/GeneratedMetadataArgs" import { UniqueMetadataArgs } from "../metadata-args/UniqueMetadataArgs" import { CheckMetadataArgs } from "../metadata-args/CheckMetadataArgs" import { ExclusionMetadataArgs } from "../metadata-args/ExclusionMetadataArgs" import { EntitySchemaColumnOptions } from "./EntitySchemaColumnOptions" import { EntitySchemaOptions } from "./EntitySchemaOptions" import { EntitySchemaEmbeddedError } from "./EntitySchemaEmbeddedError" import { InheritanceMetadataArgs } from "../metadata-args/InheritanceMetadataArgs" import { RelationIdMetadataArgs } from "../metadata-args/RelationIdMetadataArgs" /** * Transforms entity schema into metadata args storage. * The result will be just like entities read from decorators. */ export class EntitySchemaTransformer { // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Transforms entity schema into new metadata args storage object. */ transform(schemas: EntitySchema[]): MetadataArgsStorage { const metadataArgsStorage = new MetadataArgsStorage() schemas.forEach((entitySchema) => { const options = entitySchema.options // add table metadata args from the schema const tableMetadata: TableMetadataArgs = { target: options.target || options.name, name: options.tableName, database: options.database, schema: options.schema, type: options.type || "regular", orderBy: options.orderBy, synchronize: options.synchronize, withoutRowid: !!options.withoutRowid, expression: options.expression, } metadataArgsStorage.tables.push(tableMetadata) const { inheritance } = options if (inheritance) { metadataArgsStorage.inheritances.push({ target: options.target, pattern: inheritance.pattern ?? "STI", column: inheritance.column ? typeof inheritance.column === "string" ? { name: inheritance.column } : inheritance.column : undefined, } as InheritanceMetadataArgs) } this.transformColumnsRecursive(options, metadataArgsStorage) }) return metadataArgsStorage } private transformColumnsRecursive( options: EntitySchemaOptions, metadataArgsStorage: MetadataArgsStorage, ): void { // add columns metadata args from the schema Object.keys(options.columns).forEach((columnName) => { const column = options.columns[columnName]! const regularColumn = column as EntitySchemaColumnOptions let mode: ColumnMode = "regular" if (regularColumn.createDate) mode = "createDate" if (regularColumn.updateDate) mode = "updateDate" if (regularColumn.deleteDate) mode = "deleteDate" if (regularColumn.version) mode = "version" if (regularColumn.treeChildrenCount) mode = "treeChildrenCount" if (regularColumn.treeLevel) mode = "treeLevel" if (regularColumn.objectId) mode = "objectId" const columnArgs: ColumnMetadataArgs = { target: options.target || options.name, mode: mode, propertyName: columnName, options: { type: regularColumn.type, name: regularColumn.objectId ? "_id" : regularColumn.name, primaryKeyConstraintName: regularColumn.primaryKeyConstraintName, length: regularColumn.length, width: regularColumn.width, nullable: regularColumn.nullable, readonly: regularColumn.readonly, update: regularColumn.update, select: regularColumn.select, insert: regularColumn.insert, primary: regularColumn.primary, unique: regularColumn.unique, comment: regularColumn.comment, default: regularColumn.default, onUpdate: regularColumn.onUpdate, precision: regularColumn.precision, scale: regularColumn.scale, zerofill: regularColumn.zerofill, unsigned: regularColumn.unsigned, charset: regularColumn.charset, collation: regularColumn.collation, enum: regularColumn.enum, enumName: regularColumn.enumName, asExpression: regularColumn.asExpression, generatedType: regularColumn.generatedType, hstoreType: regularColumn.hstoreType, array: regularColumn.array, transformer: regularColumn.transformer, spatialFeatureType: regularColumn.spatialFeatureType, srid: regularColumn.srid, }, } metadataArgsStorage.columns.push(columnArgs) if (regularColumn.generated) { const generationArgs: GeneratedMetadataArgs = { target: options.target || options.name, propertyName: columnName, strategy: typeof regularColumn.generated === "string" ? regularColumn.generated : "increment", } metadataArgsStorage.generations.push(generationArgs) } if (regularColumn.unique) metadataArgsStorage.uniques.push({ target: options.target || options.name, columns: [columnName], }) }) // add relation metadata args from the schema if (options.relations) { Object.keys(options.relations).forEach((relationName) => { const relationSchema = options.relations![relationName]! const relation: RelationMetadataArgs = { target: options.target || options.name, propertyName: relationName, relationType: relationSchema.type, isLazy: relationSchema.lazy || false, type: relationSchema.target, inverseSideProperty: relationSchema.inverseSide, isTreeParent: relationSchema.treeParent, isTreeChildren: relationSchema.treeChildren, options: { eager: relationSchema.eager || false, cascade: relationSchema.cascade, nullable: relationSchema.nullable, onDelete: relationSchema.onDelete, onUpdate: relationSchema.onUpdate, deferrable: relationSchema.deferrable, // primary: relationSchema.primary, createForeignKeyConstraints: relationSchema.createForeignKeyConstraints, persistence: relationSchema.persistence, orphanedRowAction: relationSchema.orphanedRowAction, }, } metadataArgsStorage.relations.push(relation) // add join column if (relationSchema.joinColumn) { if (typeof relationSchema.joinColumn === "boolean") { const joinColumn: JoinColumnMetadataArgs = { target: options.target || options.name, propertyName: relationName, } metadataArgsStorage.joinColumns.push(joinColumn) } else { const joinColumnsOptions = Array.isArray( relationSchema.joinColumn, ) ? relationSchema.joinColumn : [relationSchema.joinColumn] for (const joinColumnOption of joinColumnsOptions) { const joinColumn: JoinColumnMetadataArgs = { target: options.target || options.name, propertyName: relationName, name: joinColumnOption.name, referencedColumnName: joinColumnOption.referencedColumnName, foreignKeyConstraintName: joinColumnOption.foreignKeyConstraintName, } metadataArgsStorage.joinColumns.push(joinColumn) } } } // add join table if (relationSchema.joinTable) { if (typeof relationSchema.joinTable === "boolean") { const joinTable: JoinTableMetadataArgs = { target: options.target || options.name, propertyName: relationName, } metadataArgsStorage.joinTables.push(joinTable) } else { const joinTable: JoinTableMetadataArgs = { target: options.target || options.name, propertyName: relationName, name: relationSchema.joinTable.name, database: relationSchema.joinTable.database, schema: relationSchema.joinTable.schema, joinColumns: (( relationSchema.joinTable as JoinTableOptions ).joinColumn ? [ ( relationSchema.joinTable as JoinTableOptions ).joinColumn!, ] : ( relationSchema.joinTable as JoinTableMultipleColumnsOptions ).joinColumns) as any, inverseJoinColumns: (( relationSchema.joinTable as JoinTableOptions ).inverseJoinColumn ? [ ( relationSchema.joinTable as JoinTableOptions ).inverseJoinColumn!, ] : ( relationSchema.joinTable as JoinTableMultipleColumnsOptions ).inverseJoinColumns) as any, } metadataArgsStorage.joinTables.push(joinTable) } } }) } // add relation id metadata args from the schema if (options.relationIds) { Object.keys(options.relationIds).forEach((relationIdName) => { const relationIdOptions = options.relationIds![relationIdName]! const relationId: RelationIdMetadataArgs = { propertyName: relationIdName, relation: relationIdOptions.relationName, target: options.target || options.name, alias: relationIdOptions.alias, queryBuilderFactory: relationIdOptions.queryBuilderFactory, } metadataArgsStorage.relationIds.push(relationId) }) } // add index metadata args from the schema if (options.indices) { options.indices.forEach((index) => { const indexArgs: IndexMetadataArgs = { target: options.target || options.name, name: index.name, unique: index.unique === true ? true : false, spatial: index.spatial === true ? true : false, fulltext: index.fulltext === true ? true : false, nullFiltered: index.nullFiltered === true ? true : false, parser: index.parser, synchronize: index.synchronize === false ? false : true, where: index.where, sparse: index.sparse, columns: index.columns, } metadataArgsStorage.indices.push(indexArgs) }) } // add unique metadata args from the schema if (options.uniques) { options.uniques.forEach((unique) => { const uniqueArgs: UniqueMetadataArgs = { target: options.target || options.name, name: unique.name, columns: unique.columns, deferrable: unique.deferrable, } metadataArgsStorage.uniques.push(uniqueArgs) }) } // add check metadata args from the schema if (options.checks) { options.checks.forEach((check) => { const checkArgs: CheckMetadataArgs = { target: options.target || options.name, name: check.name, expression: check.expression, } metadataArgsStorage.checks.push(checkArgs) }) } // add exclusion metadata args from the schema if (options.exclusions) { options.exclusions.forEach((exclusion) => { const exclusionArgs: ExclusionMetadataArgs = { target: options.target || options.name, name: exclusion.name, expression: exclusion.expression, } metadataArgsStorage.exclusions.push(exclusionArgs) }) } if (options.embeddeds) { Object.keys(options.embeddeds).forEach((columnName) => { const embeddedOptions = options.embeddeds![columnName] if (!embeddedOptions.schema) throw EntitySchemaEmbeddedError.createEntitySchemaIsRequiredException( columnName, ) const embeddedSchema = embeddedOptions.schema.options metadataArgsStorage.embeddeds.push({ target: options.target || options.name, propertyName: columnName, isArray: embeddedOptions.array === true, prefix: embeddedOptions.prefix !== undefined ? embeddedOptions.prefix : undefined, type: () => embeddedSchema?.target || embeddedSchema.name, }) this.transformColumnsRecursive( embeddedSchema, metadataArgsStorage, ) }) } } }