Connect/Disconnect in .save (#13444)

This commit is contained in:
Etienne
2025-07-28 20:28:41 +02:00
committed by GitHub
parent 2a82a33fff
commit c186b78f67
11 changed files with 119 additions and 64 deletions
@@ -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();
@@ -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,