diff --git a/src/decorator/options/RelationOptions.ts b/src/decorator/options/RelationOptions.ts index a24d42c77..7c5b2d1d9 100644 --- a/src/decorator/options/RelationOptions.ts +++ b/src/decorator/options/RelationOptions.ts @@ -11,7 +11,7 @@ export interface RelationOptions { * If set to true then it means that related object can be allowed to be inserted / updated / removed to the db. * This is option a shortcut if you would like to set cascadeInsert, cascadeUpdate and cascadeRemove to true. */ - cascadeAll?: boolean; + cascadeAll?: boolean; // todo: replace with cascade: boolean|("insert"|"update")[] /** * If set to true then it means that related object can be allowed to be inserted to the db. diff --git a/src/driver/postgres/PostgresDriver.ts b/src/driver/postgres/PostgresDriver.ts index 995fdb8eb..acdd6beab 100644 --- a/src/driver/postgres/PostgresDriver.ts +++ b/src/driver/postgres/PostgresDriver.ts @@ -17,6 +17,7 @@ import {DataTypeDefaults} from "../types/DataTypeDefaults"; import {TableColumn} from "../../schema-builder/schema/TableColumn"; import {PostgresConnectionCredentialsOptions} from "./PostgresConnectionCredentialsOptions"; import {EntityMetadata} from "../../metadata/EntityMetadata"; +import {OrmUtils} from "../../util/OrmUtils"; /** * Organizes communication with PostgreSQL DBMS. @@ -560,7 +561,17 @@ export class PostgresDriver implements Driver { * Creates generated map of values generated or returned by database after INSERT query. */ createGeneratedMap(metadata: EntityMetadata, insertResult: any) { - return insertResult[0]; + if (!insertResult || !insertResult[0]) + return undefined; + + const result: ObjectLiteral = insertResult[0]; + return Object.keys(result).reduce((map, key) => { + const column = metadata.findColumnWithDatabaseName(key); + if (column) { + OrmUtils.mergeDeep(map, column.createValueMap(result[key])); + } + return map; + }, {} as ObjectLiteral); } // ------------------------------------------------------------------------- diff --git a/src/driver/sqlserver/SqlServerDriver.ts b/src/driver/sqlserver/SqlServerDriver.ts index 597253431..359dfd17e 100644 --- a/src/driver/sqlserver/SqlServerDriver.ts +++ b/src/driver/sqlserver/SqlServerDriver.ts @@ -17,6 +17,7 @@ import {MssqlParameter} from "./MssqlParameter"; import {TableColumn} from "../../schema-builder/schema/TableColumn"; import {SqlServerConnectionCredentialsOptions} from "./SqlServerConnectionCredentialsOptions"; import {EntityMetadata} from "../../metadata/EntityMetadata"; +import {OrmUtils} from "../../util/OrmUtils"; /** * Organizes communication with SQL Server DBMS. @@ -468,8 +469,18 @@ export class SqlServerDriver implements Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertionResult: any) { - return insertionResult[0]; + createGeneratedMap(metadata: EntityMetadata, insertResult: any) { + if (!insertResult || !insertResult[0]) + return undefined; + + const result: ObjectLiteral = insertResult[0]; + return Object.keys(result).reduce((map, key) => { + const column = metadata.findColumnWithDatabaseName(key); + if (column) { + OrmUtils.mergeDeep(map, column.createValueMap(result[key])); + } + return map; + }, {} as ObjectLiteral); } // ------------------------------------------------------------------------- diff --git a/src/metadata-builder/EntityMetadataBuilder.ts b/src/metadata-builder/EntityMetadataBuilder.ts index 49c9a9aef..d62c3f0d4 100644 --- a/src/metadata-builder/EntityMetadataBuilder.ts +++ b/src/metadata-builder/EntityMetadataBuilder.ts @@ -326,6 +326,7 @@ export class EntityMetadataBuilder { }); embeddedMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(targets)); embeddedMetadata.embeddeds.forEach(subEmbedded => subEmbedded.parentEmbeddedMetadata = embeddedMetadata); + entityMetadata.allEmbeddeds.push(embeddedMetadata); return embeddedMetadata; }); } diff --git a/src/metadata/ColumnMetadata.ts b/src/metadata/ColumnMetadata.ts index 5c88a7eb5..3525e029b 100644 --- a/src/metadata/ColumnMetadata.ts +++ b/src/metadata/ColumnMetadata.ts @@ -364,7 +364,7 @@ export class ColumnMetadata { * Examples what this method can return depend if this column is in embeds. * { id: 1 } or { title: "hello" }, { counters: { code: 1 } }, { data: { information: { counters: { code: 1 } } } } */ - getEntityValueMap(entity: ObjectLiteral, returnIfEmpty: boolean = false): ObjectLiteral { + getEntityValueMap(entity: ObjectLiteral): ObjectLiteral|undefined { // extract column value from embeds of entity if column is in embedded if (this.embeddedMetadata) { @@ -393,16 +393,21 @@ export class ColumnMetadata { }; const map: ObjectLiteral = {}; extractEmbeddedColumnValue(propertyNames, entity, map); - return map; + return Object.keys(map).length > 0 ? map : undefined; } else { // no embeds - no problems. Simply return column property name and its value of the entity if (this.relationMetadata && entity[this.propertyName] && entity[this.propertyName] instanceof Object) { const map = this.relationMetadata.joinColumns.reduce((map, joinColumn) => { - return OrmUtils.mergeDeep(map, joinColumn.referencedColumn!.getEntityValueMap(entity[this.propertyName])); + const value = joinColumn.referencedColumn!.getEntityValueMap(entity[this.propertyName]); + if (!value) return map; + return OrmUtils.mergeDeep(map, value); }, {}); - return { [this.propertyName]: map }; + return { [this.propertyName]: Object.keys(map).length > 0 ? map : undefined }; } else { - return { [this.propertyName]: entity[this.propertyName] }; + if (entity[this.propertyName] !== undefined) + return { [this.propertyName]: entity[this.propertyName] }; + + return undefined; } } } @@ -443,7 +448,7 @@ export class ColumnMetadata { return undefined; } else { // no embeds - no problems. Simply return column name by property name of the entity - if (this.relationMetadata && this.referencedColumn && this.isVirtual) { // todo: do we really need isVirtual? + if (this.relationMetadata && this.referencedColumn/* && this.isVirtual*/) { // todo: do we really need isVirtual? const relatedEntity = this.relationMetadata.getEntityValue(entity); if (relatedEntity && relatedEntity instanceof Object) return this.referencedColumn.getEntityValue(relatedEntity); diff --git a/src/metadata/EmbeddedMetadata.ts b/src/metadata/EmbeddedMetadata.ts index 18a7fc4dc..7fb3ab324 100644 --- a/src/metadata/EmbeddedMetadata.ts +++ b/src/metadata/EmbeddedMetadata.ts @@ -159,7 +159,7 @@ export class EmbeddedMetadata { this.prefix = this.buildPrefix(connection); this.parentPropertyNames = this.buildParentPropertyNames(); this.parentPrefixes = this.buildParentPrefixes(); - this.propertyPath = this.parentPrefixes.join("."); + this.propertyPath = this.parentPropertyNames.join("."); this.embeddedMetadataTree = this.buildEmbeddedMetadataTree(); this.columnsFromTree = this.buildColumnsFromTree(); this.relationsFromTree = this.buildRelationsFromTree(); diff --git a/src/metadata/EntityMetadata.ts b/src/metadata/EntityMetadata.ts index 0d024d081..e9ffca16f 100644 --- a/src/metadata/EntityMetadata.ts +++ b/src/metadata/EntityMetadata.ts @@ -210,6 +210,11 @@ export class EntityMetadata { */ embeddeds: EmbeddedMetadata[] = []; + /** + * All embeddeds - embeddeds from this entity metadata and from all child embeddeds, etc. + */ + allEmbeddeds: EmbeddedMetadata[] = []; + /** * Entity listener metadatas. */ @@ -495,7 +500,7 @@ export class EntityMetadata { * Finds embedded with a given property path. */ findEmbeddedWithPropertyPath(propertyPath: string): EmbeddedMetadata|undefined { - return this.embeddeds.find(embedded => { + return this.allEmbeddeds.find(embedded => { return embedded.propertyPath === propertyPath; }); } diff --git a/src/metadata/EntityMetadataUtils.ts b/src/metadata/EntityMetadataUtils.ts index c65164016..5d4ab8923 100644 --- a/src/metadata/EntityMetadataUtils.ts +++ b/src/metadata/EntityMetadataUtils.ts @@ -18,7 +18,7 @@ export class EntityMetadataUtils { // example: .update().set({ name: () => `SUBSTR('', 1, 2)` }) const parentPath = prefix ? prefix + "." + key : key; if (metadata.hasEmbeddedWithPropertyPath(parentPath)) { - const subPaths = this.createPropertyPath(metadata, entity[key], key); + const subPaths = this.createPropertyPath(metadata, entity[key], parentPath); paths.push(...subPaths); } else { const path = prefix ? prefix + "." + key : key; diff --git a/src/metadata/RelationMetadata.ts b/src/metadata/RelationMetadata.ts index bd42bf871..06cdf2e25 100644 --- a/src/metadata/RelationMetadata.ts +++ b/src/metadata/RelationMetadata.ts @@ -289,6 +289,8 @@ export class RelationMetadata { getRelationIdMap(entity: ObjectLiteral): ObjectLiteral|undefined { const joinColumns = this.isOwning ? this.joinColumns : this.inverseRelation!.joinColumns; const referencedColumns = joinColumns.map(joinColumn => joinColumn.referencedColumn!); + // console.log("entity", entity); + // console.log("referencedColumns", referencedColumns); return this.inverseEntityMetadata.getValueMap(entity, referencedColumns); } diff --git a/src/persistence/Subject.ts b/src/persistence/Subject.ts index f7ea8c080..bcd065f93 100644 --- a/src/persistence/Subject.ts +++ b/src/persistence/Subject.ts @@ -129,16 +129,23 @@ export class Subject { } } - if (changeMap.column) { + // value = changeMap.valueFactory ? changeMap.valueFactory(value) : changeMap.column.createValueMap(value); + + if (this.metadata.isJunction && changeMap.column) { + OrmUtils.mergeDeep(updateMap, changeMap.column.createValueMap(changeMap.column.referencedColumn!.getEntityValue(value))); + + } else if (changeMap.column) { OrmUtils.mergeDeep(updateMap, changeMap.column.createValueMap(value)); } else if (changeMap.relation) { - changeMap.relation!.joinColumns.forEach(column => { - OrmUtils.mergeDeep(updateMap, column.createValueMap(value)); - }); + OrmUtils.mergeDeep(updateMap, changeMap.relation!.createValueMap(value)); + // changeMap.relation!.joinColumns.forEach(column => { + // OrmUtils.mergeDeep(updateMap, column.createValueMap(value)); + // }); } return updateMap; }, {} as ObjectLiteral); + // console.log(changeSet); this.changeMaps = changeMapsWithoutValues; return changeSet; } @@ -146,7 +153,7 @@ export class Subject { buildIdentifier() { return this.metadata.primaryColumns.reduce((identifier, column) => { if (column.isGenerated && this.generatedMap) { - return OrmUtils.mergeDeep(identifier, column.createValueMap(this.generatedMap[column.databaseName])); + return OrmUtils.mergeDeep(identifier, column.getEntityValueMap(this.generatedMap)); } else { return OrmUtils.mergeDeep(identifier, column.getEntityValueMap(this.entity!)); } diff --git a/src/persistence/SubjectChangeMap.ts b/src/persistence/SubjectChangeMap.ts index 93b4a7ba7..8874ea489 100644 --- a/src/persistence/SubjectChangeMap.ts +++ b/src/persistence/SubjectChangeMap.ts @@ -25,4 +25,9 @@ export interface SubjectChangeMap { */ value: Subject|any; + /** + * Callback used to produce a final value. + */ + valueFactory?: (value: any) => any; + } \ No newline at end of file diff --git a/src/persistence/SubjectExecutor.ts b/src/persistence/SubjectExecutor.ts index 558af0b60..91b2c170b 100644 --- a/src/persistence/SubjectExecutor.ts +++ b/src/persistence/SubjectExecutor.ts @@ -113,10 +113,26 @@ export class SubjectExecutor { newInsertedSubjects.push(...entityTargetSubjects); entityTargetSubjects.forEach(entityTargetSubject => this.insertSubjects.splice(this.insertSubjects.indexOf(entityTargetSubject), 1)); }); + + const dependencies2: string[][] = []; + metadatas.forEach(metadata => { + metadata.relationsWithJoinColumns.forEach(relation => { + dependencies2.push([metadata.targetName, relation.inverseEntityMetadata.targetName]); + }); + }); + + const sortedEntityTargets2 = OrmUtils.toposort(dependencies2).reverse(); + + const newInsertedSubjects2: Subject[] = []; + sortedEntityTargets2.forEach(sortedEntityTarget => { + const entityTargetSubjects = this.insertSubjects.filter(subject => subject.metadata.targetName === sortedEntityTarget); + newInsertedSubjects2.push(...entityTargetSubjects); + entityTargetSubjects.forEach(entityTargetSubject => this.insertSubjects.splice(this.insertSubjects.indexOf(entityTargetSubject), 1)); + }); + + newInsertedSubjects.push(...newInsertedSubjects2); newInsertedSubjects.push(...this.insertSubjects); this.insertSubjects = newInsertedSubjects; - // console.log("dependencies", dependencies); - // console.log("toposort", ); } await this.executeInsertOperations(); @@ -203,6 +219,10 @@ export class SubjectExecutor { return false; } + // if (column.referencedColumn) { + // + // } + return true; }); diffColumns.forEach(column => { @@ -281,6 +301,8 @@ export class SubjectExecutor { */ protected async executeInsertOperations(): Promise { + // console.log(this.insertSubjects.map(subject => subject.entity)); + // then we run insertion in the sequential order which is important since we have an ordered subjects await PromiseUtils.runInSequence(this.insertSubjects, async subject => { @@ -308,7 +330,6 @@ export class SubjectExecutor { // const valueSets = this.getValueSets(); // if (valueSets.length > 1) // throw Error(`Returning / output can be used only when a single value / entity is inserted.`); - // const alias = this.expressionMap.mainAlias!.name; // const returningResult = await this.createQueryBuilder() // .select(this.expressionMap.returning as string[]) @@ -323,12 +344,12 @@ export class SubjectExecutor { if (subject.entity) { subject.identifier = subject.buildIdentifier(); + // console.log(subject.identifier); } // if there are changes left mark it for updation if (subject.hasChanges()) { subject.canBeUpdated = true; - // console.log("can be updated!", subject.mustBeUpdated); } }); } diff --git a/src/persistence/subject-builder/ManyToManySubjectBuilder.ts b/src/persistence/subject-builder/ManyToManySubjectBuilder.ts index 61ed3e76a..8a94fb431 100644 --- a/src/persistence/subject-builder/ManyToManySubjectBuilder.ts +++ b/src/persistence/subject-builder/ManyToManySubjectBuilder.ts @@ -116,9 +116,7 @@ export class ManyToManySubjectBuilder { // extract only relation id from the related entities, since we only need it for comparision // by example: extract from category only relation id (category id, or let's say category title, depend on join column options) - const relatedEntityRelationIdMap = relation.getRelationIdMap(relatedEntity); - - console.log("relatedEntityRelationIdMap", relatedEntityRelationIdMap); + const relatedEntityRelationIdMap = relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity); // try to find a subject of this related entity, maybe it was loaded or was marked for persistence const relatedEntitySubject = this.subjects.find(subject => { @@ -131,10 +129,10 @@ export class ManyToManySubjectBuilder { // if related entity does not have a subject then it means user tries to bind entity which wasn't saved // in this persistence because he didn't pass this entity for save or he did not set cascades // but without entity being inserted we cannot bind it in the relation operation, so we throw an exception here - if (!relatedEntitySubject || true === true) - throw new Error(`Many-to-many relation ${relation.entityMetadata.name}.${relation.propertyPath} contains ` + + if (!relatedEntitySubject) + throw new Error(`Many-to-many relation "${relation.entityMetadata.name}.${relation.propertyPath}" contains ` + `entities which do not exist in the database yet, thus they cannot be bind in the database. ` + - `Please setup cascade insertion or save entity before binding it.`); + `Please setup cascade insertion or save entities before binding it.`); } // try to find related entity in the database @@ -147,8 +145,8 @@ export class ManyToManySubjectBuilder { if (relatedEntityExistInDatabase) return; - const ownerEntityMap = relation.isOwning ? subject.entity! : relatedEntity; // by example: ownerEntityMap is post from subject here - const inverseEntityMap = relation.isOwning ? relatedEntity : subject.entity!; // by example: inverseEntityMap is category from categories array here + const ownerValue = relation.isOwning ? subject : (relatedEntitySubject || relatedEntity); // by example: ownerEntityMap is post from subject here + const inverseValue = relation.isOwning ? (relatedEntitySubject || relatedEntity) : subject; // by example: inverseEntityMap is category from categories array here // create a new subject for insert operation of junction rows const junctionSubject = new Subject(relation.junctionEntityMetadata!); @@ -158,21 +156,23 @@ export class ManyToManySubjectBuilder { relation.junctionEntityMetadata!.ownerColumns.forEach(column => { junctionSubject.changeMaps.push({ column: column, - value: column.referencedColumn!.getEntityValue(ownerEntityMap), + value: ownerValue, + // valueFactory: (value) => column.referencedColumn!.getEntityValue(value) // column.referencedColumn!.getEntityValue(ownerEntityMap), }); }); relation.junctionEntityMetadata!.inverseColumns.forEach(column => { junctionSubject.changeMaps.push({ column: column, - value: column.referencedColumn!.getEntityValue(inverseEntityMap), + value: inverseValue, + // valueFactory: (value) => column.referencedColumn!.getEntityValue(value) // column.referencedColumn!.getEntityValue(inverseEntityMap), }); }); }); // get all inverse entities relation ids that are "bind" to the currently persisted entity const changedInverseEntityRelationIds = relatedEntities - .map(relatedEntity => relation.getRelationIdMap(relatedEntity)) + .map(relatedEntity => relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity)) .filter(relatedEntityRelationIdMap => relatedEntityRelationIdMap !== undefined && relatedEntityRelationIdMap !== null); // now from all entities in the persisted entity find only those which aren't found in the db diff --git a/src/persistence/subject-builder/OneToManySubjectBuilder.ts b/src/persistence/subject-builder/OneToManySubjectBuilder.ts index 5fef8b899..103ca3b34 100644 --- a/src/persistence/subject-builder/OneToManySubjectBuilder.ts +++ b/src/persistence/subject-builder/OneToManySubjectBuilder.ts @@ -76,7 +76,7 @@ export class OneToManySubjectBuilder { // by example: extract from categories only relation ids (category id, or let's say category title, depend on join column options) const relatedPersistedEntityRelationIds: ObjectLiteral[] = []; relatedEntities.forEach(relatedEntity => { // by example: relatedEntity is a category here - const relationIdMap = relation.getRelationIdMap(relatedEntity); // by example: relationIdMap is category.id map here, e.g. { id: ... } + const relationIdMap = relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity); // by example: relationIdMap is category.id map here, e.g. { id: ... } // try to find a subject of this related entity, maybe it was loaded or was marked for persistence let relatedEntitySubject = this.subjects.find(subject => { @@ -93,9 +93,9 @@ export class OneToManySubjectBuilder { // in this persistence because he didn't pass this entity for save or he did not set cascades // but without entity being inserted we cannot bind it in the relation operation, so we throw an exception here if (!relatedEntitySubject) - throw new Error(`One-to-many relation ${relation.entityMetadata.name}.${relation.propertyPath} contains ` + + throw new Error(`One-to-many relation "${relation.entityMetadata.name}.${relation.propertyPath}" contains ` + `entities which do not exist in the database yet, thus they cannot be bind in the database. ` + - `Please setup cascade insertion or save entity before binding it.`); + `Please setup cascade insertion or save entities before binding it.`); // okay, so related subject exist and its marked for insertion, then add a new change map // by example: this will tell category to insert into its post relation our post we are working with diff --git a/src/persistence/subject-builder/OneToOneInverseSideSubjectBuilder.ts b/src/persistence/subject-builder/OneToOneInverseSideSubjectBuilder.ts index 1c0d09ee2..8c728751a 100644 --- a/src/persistence/subject-builder/OneToOneInverseSideSubjectBuilder.ts +++ b/src/persistence/subject-builder/OneToOneInverseSideSubjectBuilder.ts @@ -94,7 +94,7 @@ export class OneToOneInverseSideSubjectBuilder { // extract only relation id from the related entities, since we only need it for comparision // by example: extract from category only relation id (category id, or let's say category title, depend on join column options) - const relationIdMap = relation.getRelationIdMap(relatedEntity); // by example: relationIdMap is category.id map here, e.g. { id: ... } + const relationIdMap = relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity); // by example: relationIdMap is category.id map here, e.g. { id: ... } // try to find a subject of this related entity, maybe it was loaded or was marked for persistence let relatedEntitySubject = this.subjects.find(operateSubject => { @@ -111,7 +111,7 @@ export class OneToOneInverseSideSubjectBuilder { // in this persistence because he didn't pass this entity for save or he did not set cascades // but without entity being inserted we cannot bind it in the relation operation, so we throw an exception here if (!relatedEntitySubject) - throw new Error(`One-to-one inverse relation ${relation.entityMetadata.name}.${relation.propertyPath} contains ` + + throw new Error(`One-to-one inverse relation "${relation.entityMetadata.name}.${relation.propertyPath}" contains ` + `entity which does not exist in the database yet, thus cannot be bind in the database. ` + `Please setup cascade insertion or save entity before binding it.`); diff --git a/src/query-builder/InsertQueryBuilder.ts b/src/query-builder/InsertQueryBuilder.ts index f392cad7d..098887093 100644 --- a/src/query-builder/InsertQueryBuilder.ts +++ b/src/query-builder/InsertQueryBuilder.ts @@ -131,7 +131,12 @@ export class InsertQueryBuilder extends QueryBuilder { values = valueSets.map((valueSet, insertionIndex) => { const columnValues = columns.map(column => { const paramName = "_inserted_" + insertionIndex + "_" + column.databaseName; - const value = this.connection.driver.preparePersistentValue(column.getEntityValue(valueSet), column); + + let value = column.getEntityValue(valueSet); + if (column.referencedColumn && value instanceof Object) { + value = column.referencedColumn.getEntityValue(value); + } + value = this.connection.driver.preparePersistentValue(value, column); if (value instanceof Function) { // support for SQL expressions in update query return value(); diff --git a/src/query-builder/QueryBuilder.ts b/src/query-builder/QueryBuilder.ts index 4d9330639..e6b207e47 100644 --- a/src/query-builder/QueryBuilder.ts +++ b/src/query-builder/QueryBuilder.ts @@ -563,7 +563,14 @@ export abstract class QueryBuilder { protected createReturningExpression(): string { const columns = this.getReturningColumns(); if (columns.length) { - return columns.map(column => "INSERTED." + this.escape(column.databaseName)).join(", "); + return columns.map(column => { + const name = this.escape(column.databaseName); + if (this.connection.driver instanceof SqlServerDriver) { + return "INSERTED." + name; + } else { + return name; + } + }).join(", "); } else if (typeof this.expressionMap.returning === "string") { return this.expressionMap.returning; diff --git a/src/query-builder/UpdateQueryBuilder.ts b/src/query-builder/UpdateQueryBuilder.ts index 4e326694f..35f9ad462 100644 --- a/src/query-builder/UpdateQueryBuilder.ts +++ b/src/query-builder/UpdateQueryBuilder.ts @@ -147,11 +147,17 @@ export class UpdateQueryBuilder extends QueryBuilder implements // todo: make this and other query builder to work with properly with tables without metadata const column = metadata.findColumnWithPropertyPath(propertyPath); - // we update an entity and entity can contain property which aren't columns, so we just skip them + // we update an entity and entity can contain properties which aren't columns, so we just skip them if (!column) return; const paramName = "_updated_" + column.databaseName; - const value = this.connection.driver.preparePersistentValue(column.getEntityValue(valuesSet), column); + + // + let value = column.getEntityValue(valuesSet); + if (column.referencedColumn && value instanceof Object) { + value = column.referencedColumn.getEntityValue(value); + } + value = this.connection.driver.preparePersistentValue(value, column); // todo: duplication zone if (value instanceof Function) { // support for SQL expressions in update query diff --git a/src/util/OrmUtils.ts b/src/util/OrmUtils.ts index 5ad7259ea..7476148be 100644 --- a/src/util/OrmUtils.ts +++ b/src/util/OrmUtils.ts @@ -60,11 +60,19 @@ export class OrmUtils { if (this.isObject(target) && this.isObject(source)) { for (const key in source) { - if (this.isObject(source[key])) { + let propertyKey = key; + if (source[key] instanceof Promise) + continue; + + // if (source[key] instanceof Promise) { + // propertyKey = "__" + key + "__"; + // } + + if (this.isObject(source[propertyKey]) && !(source[propertyKey] instanceof Date)) { if (!target[key]) Object.assign(target, { [key]: {} }); - this.mergeDeep(target[key], source[key]); + this.mergeDeep(target[key], source[propertyKey]); } else { - Object.assign(target, { [key]: source[key] }); + Object.assign(target, { [key]: source[propertyKey] }); } } } diff --git a/test/github-issues/144/issue-144.ts b/test/github-issues/144/issue-144.ts index 6d2b34200..14239396e 100644 --- a/test/github-issues/144/issue-144.ts +++ b/test/github-issues/144/issue-144.ts @@ -3,7 +3,8 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase import {Connection} from "../../../src/connection/Connection"; import {Student} from "./entity/Student"; -describe("github issues > #144 Class Table Inheritance doesn't seem to work", () => { +// todo fix this test once class table inheritance support is back +describe.skip("github issues > #144 Class Table Inheritance doesn't seem to work", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/github-issues/904/issue-904.ts b/test/github-issues/904/issue-904.ts index f6e085886..d95173f6f 100644 --- a/test/github-issues/904/issue-904.ts +++ b/test/github-issues/904/issue-904.ts @@ -3,7 +3,8 @@ import {createTestingConnections, closeTestingConnections, reloadTestingDatabase import {Connection} from "../../../src/connection/Connection"; import {Category} from "./entity/Category"; -describe("github issues > #904 Using closure tables without @TreeLevelColumn will always fail on insert", () => { +// todo: uncomment test once closure tables functionality is back +describe.skip("github issues > #904 Using closure tables without @TreeLevelColumn will always fail on insert", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/integration/sample4-many-to-many.ts b/test/integration/sample4-many-to-many.ts index a7aeac10f..3f53d6b53 100644 --- a/test/integration/sample4-many-to-many.ts +++ b/test/integration/sample4-many-to-many.ts @@ -48,7 +48,7 @@ describe("many-to-many", function() { // Specifications // ------------------------------------------------------------------------- - describe.only("insert post and details (has inverse relation + full cascade options)", function() { + describe("insert post and details (has inverse relation + full cascade options)", function() { let newPost: Post, details: PostDetails, savedPost: Post; before(reloadDatabase);