Connect/Disconnect in .save (#13444)
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
import { DeepPartial } from 'typeorm/common/DeepPartial';
|
||||
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import {
|
||||
ConnectObject,
|
||||
DisconnectObject,
|
||||
EntityRelationFields,
|
||||
} from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
|
||||
export type DeepPartialWithNestedRelationFields<T> = Omit<
|
||||
DeepPartial<T>,
|
||||
EntityRelationFields<T>
|
||||
> & {
|
||||
[K in keyof T]?: T[K] extends BaseWorkspaceEntity | null
|
||||
? DeepPartial<T[K]> | ConnectObject | DisconnectObject
|
||||
: DeepPartial<T[K]>;
|
||||
};
|
||||
+1
-1
@@ -17,7 +17,7 @@ export type DisconnectObject = {
|
||||
[RELATION_NESTED_QUERY_KEYWORDS.DISCONNECT]: true;
|
||||
};
|
||||
|
||||
type EntityRelationFields<T> = {
|
||||
export type EntityRelationFields<T> = {
|
||||
[K in keyof T]: T[K] extends BaseWorkspaceEntity | null ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
ConnectObject,
|
||||
DisconnectObject,
|
||||
} from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
} from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
|
||||
export type RelationConnectQueryFieldsByEntityIndex = {
|
||||
[entityIndex: string]: { [key: string]: ConnectObject };
|
||||
|
||||
+33
-7
@@ -40,7 +40,9 @@ import {
|
||||
PermissionsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
import { DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { RelationNestedQueries } from 'src/engine/twenty-orm/relation-nested-queries/relation-nested-queries';
|
||||
import {
|
||||
OperationType,
|
||||
validateOperationIsPermittedOrThrow,
|
||||
@@ -976,7 +978,7 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
permissionOptions?: PermissionOptions,
|
||||
): Promise<Entity>;
|
||||
|
||||
override save<Entity, T extends DeepPartial<Entity>>(
|
||||
override save<Entity, T extends DeepPartialWithNestedRelationFields<Entity>>(
|
||||
targetOrEntity: EntityTarget<Entity>,
|
||||
entities: T[],
|
||||
options: SaveOptions & {
|
||||
@@ -985,14 +987,14 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
permissionOptions?: PermissionOptions,
|
||||
): Promise<T[]>;
|
||||
|
||||
override save<Entity, T extends DeepPartial<Entity>>(
|
||||
override save<Entity, T extends DeepPartialWithNestedRelationFields<Entity>>(
|
||||
targetOrEntity: EntityTarget<Entity>,
|
||||
entities: T[],
|
||||
options?: SaveOptions,
|
||||
permissionOptions?: PermissionOptions,
|
||||
): Promise<(T & Entity)[]>;
|
||||
|
||||
override save<Entity, T extends DeepPartial<Entity>>(
|
||||
override save<Entity, T extends DeepPartialWithNestedRelationFields<Entity>>(
|
||||
targetOrEntity: EntityTarget<Entity>,
|
||||
entity: T,
|
||||
options: SaveOptions & {
|
||||
@@ -1001,7 +1003,7 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
permissionOptions?: PermissionOptions,
|
||||
): Promise<T>;
|
||||
|
||||
override save<Entity, T extends DeepPartial<Entity>>(
|
||||
override save<Entity, T extends DeepPartialWithNestedRelationFields<Entity>>(
|
||||
targetOrEntity: EntityTarget<Entity>,
|
||||
entity: T,
|
||||
options?: SaveOptions,
|
||||
@@ -1010,7 +1012,7 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
|
||||
override async save<
|
||||
Entity extends ObjectLiteral,
|
||||
T extends DeepPartial<Entity>,
|
||||
T extends DeepPartialWithNestedRelationFields<Entity>,
|
||||
>(
|
||||
targetOrEntity: EntityTarget<Entity> | Entity | Entity[],
|
||||
entityOrMaybeOptions:
|
||||
@@ -1062,6 +1064,30 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
target ?? (isEntityArray ? entity[0]?.constructor : entity.constructor);
|
||||
|
||||
const entityArray = isEntityArray ? entity : [entity];
|
||||
|
||||
const relationNestedQueries = new RelationNestedQueries(
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const relationNestedConfig =
|
||||
relationNestedQueries.prepareNestedRelationQueries(
|
||||
entityArray,
|
||||
entityTarget,
|
||||
);
|
||||
|
||||
const updatedEntities = isDefined(relationNestedConfig)
|
||||
? await relationNestedQueries.processRelationNestedQueries({
|
||||
entities: entityArray,
|
||||
relationNestedConfig,
|
||||
queryBuilder: this.createQueryBuilder(
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
permissionOptions,
|
||||
),
|
||||
})
|
||||
: entityArray;
|
||||
|
||||
const entityIds = entityArray.map((e) => (e as { id: string }).id);
|
||||
const beforeUpdate = await this.find(
|
||||
entityTarget,
|
||||
@@ -1086,7 +1112,7 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
);
|
||||
|
||||
const formattedEntityOrEntities = formatData(
|
||||
entityArray,
|
||||
updatedEntities,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
|
||||
+18
-10
@@ -1,11 +1,11 @@
|
||||
import { isDefined } from 'class-validator';
|
||||
import { RELATION_NESTED_QUERY_KEYWORDS } from 'twenty-shared/constants';
|
||||
import { EntityTarget, ObjectLiteral } from 'typeorm';
|
||||
import { EntityTarget, ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
||||
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type';
|
||||
import {
|
||||
RelationConnectQueryFieldsByEntityIndex,
|
||||
@@ -36,10 +36,9 @@ export class RelationNestedQueries {
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<Entity>[]
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<Entity>,
|
||||
target: EntityTarget<Entity>,
|
||||
): [
|
||||
RelationConnectQueryConfig[],
|
||||
RelationDisconnectQueryFieldsByEntityIndex,
|
||||
] {
|
||||
):
|
||||
| [RelationConnectQueryConfig[], RelationDisconnectQueryFieldsByEntityIndex]
|
||||
| null {
|
||||
const entitiesArray = Array.isArray(entities) ? entities : [entities];
|
||||
|
||||
const {
|
||||
@@ -53,7 +52,10 @@ export class RelationNestedQueries {
|
||||
relationConnectQueryFieldsByEntityIndex,
|
||||
);
|
||||
|
||||
return [connectConfig, relationDisconnectQueryFieldsByEntityIndex];
|
||||
return connectConfig.length > 0 ||
|
||||
Object.keys(relationDisconnectQueryFieldsByEntityIndex).length > 0
|
||||
? [connectConfig, relationDisconnectQueryFieldsByEntityIndex]
|
||||
: null;
|
||||
}
|
||||
|
||||
private prepareRelationConnect<Entity extends ObjectLiteral>(
|
||||
@@ -90,7 +92,9 @@ export class RelationNestedQueries {
|
||||
RelationConnectQueryConfig[],
|
||||
RelationDisconnectQueryFieldsByEntityIndex,
|
||||
];
|
||||
queryBuilder: WorkspaceSelectQueryBuilder<Entity>;
|
||||
queryBuilder:
|
||||
| WorkspaceSelectQueryBuilder<Entity>
|
||||
| SelectQueryBuilder<Entity>;
|
||||
}): Promise<QueryDeepPartialEntity<Entity>[]> {
|
||||
const entitiesArray = Array.isArray(entities) ? entities : [entities];
|
||||
|
||||
@@ -120,7 +124,9 @@ export class RelationNestedQueries {
|
||||
}: {
|
||||
entities: QueryDeepPartialEntityWithNestedRelationFields<Entity>[];
|
||||
relationConnectQueryConfigs: RelationConnectQueryConfig[];
|
||||
queryBuilder: WorkspaceSelectQueryBuilder<Entity>;
|
||||
queryBuilder:
|
||||
| WorkspaceSelectQueryBuilder<Entity>
|
||||
| SelectQueryBuilder<Entity>;
|
||||
}): Promise<QueryDeepPartialEntity<Entity>[]> {
|
||||
if (relationConnectQueryConfigs.length === 0) return entities;
|
||||
|
||||
@@ -139,7 +145,9 @@ export class RelationNestedQueries {
|
||||
|
||||
private async executeConnectQueries<Entity extends ObjectLiteral>(
|
||||
relationConnectQueryConfigs: RelationConnectQueryConfig[],
|
||||
queryBuilder: WorkspaceSelectQueryBuilder<Entity>,
|
||||
queryBuilder:
|
||||
| WorkspaceSelectQueryBuilder<Entity>
|
||||
| SelectQueryBuilder<Entity>,
|
||||
): Promise<[RelationConnectQueryConfig, Record<string, unknown>[]][]> {
|
||||
const allRecordsToConnectWithConfig: [
|
||||
RelationConnectQueryConfig,
|
||||
|
||||
+23
-21
@@ -1,4 +1,5 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
EntityTarget,
|
||||
InsertQueryBuilder,
|
||||
@@ -12,7 +13,7 @@ import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/works
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type';
|
||||
import { RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type';
|
||||
import {
|
||||
@@ -38,10 +39,9 @@ export class WorkspaceInsertQueryBuilder<
|
||||
private authContext?: AuthContext;
|
||||
private featureFlagMap?: FeatureFlagMap;
|
||||
private relationNestedQueries: RelationNestedQueries;
|
||||
private relationNestedConfig: [
|
||||
RelationConnectQueryConfig[],
|
||||
RelationDisconnectQueryFieldsByEntityIndex,
|
||||
];
|
||||
private relationNestedConfig:
|
||||
| [RelationConnectQueryConfig[], RelationDisconnectQueryFieldsByEntityIndex]
|
||||
| null;
|
||||
|
||||
constructor(
|
||||
queryBuilder: InsertQueryBuilder<T>,
|
||||
@@ -115,24 +115,26 @@ export class WorkspaceInsertQueryBuilder<
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
this.authContext,
|
||||
);
|
||||
if (isDefined(this.relationNestedConfig)) {
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
this.authContext,
|
||||
);
|
||||
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: this.expressionMap.valuesSet as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
queryBuilder: nestedRelationQueryBuilder,
|
||||
});
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: this.expressionMap.valuesSet as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
queryBuilder: nestedRelationQueryBuilder,
|
||||
});
|
||||
|
||||
this.expressionMap.valuesSet = updatedValues;
|
||||
this.expressionMap.valuesSet = updatedValues;
|
||||
}
|
||||
|
||||
const result = await super.execute();
|
||||
|
||||
|
||||
+17
-15
@@ -1,4 +1,5 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
EntityTarget,
|
||||
ObjectLiteral,
|
||||
@@ -13,7 +14,7 @@ import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/works
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type';
|
||||
import { RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type';
|
||||
import {
|
||||
@@ -38,10 +39,9 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
private authContext?: AuthContext;
|
||||
private featureFlagMap?: FeatureFlagMap;
|
||||
private relationNestedQueries: RelationNestedQueries;
|
||||
private relationNestedConfig: [
|
||||
RelationConnectQueryConfig[],
|
||||
RelationDisconnectQueryFieldsByEntityIndex,
|
||||
];
|
||||
private relationNestedConfig:
|
||||
| [RelationConnectQueryConfig[], RelationDisconnectQueryFieldsByEntityIndex]
|
||||
| null;
|
||||
|
||||
constructor(
|
||||
queryBuilder: UpdateQueryBuilder<T>,
|
||||
@@ -115,17 +115,19 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
this.authContext,
|
||||
);
|
||||
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: this.expressionMap.valuesSet as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
queryBuilder: nestedRelationQueryBuilder,
|
||||
});
|
||||
if (isDefined(this.relationNestedConfig)) {
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: this.expressionMap.valuesSet as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
queryBuilder: nestedRelationQueryBuilder,
|
||||
});
|
||||
|
||||
this.expressionMap.valuesSet =
|
||||
updatedValues.length === 1 ? updatedValues[0] : updatedValues;
|
||||
this.expressionMap.valuesSet =
|
||||
updatedValues.length === 1 ? updatedValues[0] : updatedValues;
|
||||
}
|
||||
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
before,
|
||||
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
import { DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||
@@ -243,31 +244,31 @@ export class WorkspaceRepository<
|
||||
/**
|
||||
* SAVE METHODS
|
||||
*/
|
||||
override save<U extends DeepPartial<T>>(
|
||||
override save<U extends DeepPartialWithNestedRelationFields<T>>(
|
||||
entities: U[],
|
||||
options: SaveOptions & { reload: false },
|
||||
entityManager?: WorkspaceEntityManager,
|
||||
): Promise<T[]>;
|
||||
|
||||
override save<U extends DeepPartial<T>>(
|
||||
override save<U extends DeepPartialWithNestedRelationFields<T>>(
|
||||
entities: U[],
|
||||
options?: SaveOptions,
|
||||
entityManager?: WorkspaceEntityManager,
|
||||
): Promise<(U & T)[]>;
|
||||
|
||||
override save<U extends DeepPartial<T>>(
|
||||
override save<U extends DeepPartialWithNestedRelationFields<T>>(
|
||||
entity: U,
|
||||
options: SaveOptions & { reload: false },
|
||||
entityManager?: WorkspaceEntityManager,
|
||||
): Promise<T>;
|
||||
|
||||
override save<U extends DeepPartial<T>>(
|
||||
override save<U extends DeepPartialWithNestedRelationFields<T>>(
|
||||
entity: U,
|
||||
options?: SaveOptions,
|
||||
entityManager?: WorkspaceEntityManager,
|
||||
): Promise<U & T>;
|
||||
|
||||
override async save<U extends DeepPartial<T>>(
|
||||
override async save<U extends DeepPartialWithNestedRelationFields<T>>(
|
||||
entityOrEntities: U | U[],
|
||||
options?: SaveOptions | (SaveOptions & { reload: false }),
|
||||
entityManager?: WorkspaceEntityManager,
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { ConnectObject } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
import { ConnectObject } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import {
|
||||
RelationConnectQueryConfig,
|
||||
UniqueConstraintCondition,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { RELATION_NESTED_QUERY_KEYWORDS } from 'twenty-shared/constants';
|
||||
import {
|
||||
ConnectObject,
|
||||
DisconnectObject,
|
||||
} from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-relation-connect.type';
|
||||
} from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import {
|
||||
RelationConnectQueryFieldsByEntityIndex,
|
||||
RelationDisconnectQueryFieldsByEntityIndex,
|
||||
|
||||
-1
@@ -37,7 +37,6 @@ export class CleanerWorkspaceService {
|
||||
private readonly inactiveDaysBeforeDelete: number;
|
||||
private readonly inactiveDaysBeforeWarn: number;
|
||||
private readonly maxNumberOfWorkspacesDeletedPerExecution: number;
|
||||
|
||||
constructor(
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
|
||||
Reference in New Issue
Block a user