Add TwentyORM query read timeout exception (#13603)
In this PR: - adding a try / catch around all ORM internal methods save, insert, upsert, findOne, ... - leveraging this error to prevent messageChannels to get FAILED - optimizing messaging BATCH_SIZE and THROTTLE threshold according to local tests - <img width="1510" height="851" alt="image" src="https://github.com/user-attachments/assets/802fd933-caac-4291-9cde-34a1ddf59c06" />
This commit is contained in:
+54
-48
@@ -14,6 +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 { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
@@ -62,64 +63,69 @@ export class WorkspaceDeleteQueryBuilder<
|
||||
}
|
||||
|
||||
override async execute(): Promise<DeleteResult & { generatedMaps: T[] }> {
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
try {
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
true,
|
||||
this.authContext,
|
||||
this.featureFlagMap,
|
||||
);
|
||||
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
true,
|
||||
this.authContext,
|
||||
this.featureFlagMap,
|
||||
);
|
||||
|
||||
eventSelectQueryBuilder.expressionMap.wheres = this.expressionMap.wheres;
|
||||
eventSelectQueryBuilder.expressionMap.aliases = this.expressionMap.aliases;
|
||||
eventSelectQueryBuilder.setParameters(this.getParameters());
|
||||
eventSelectQueryBuilder.expressionMap.wheres = this.expressionMap.wheres;
|
||||
eventSelectQueryBuilder.expressionMap.aliases =
|
||||
this.expressionMap.aliases;
|
||||
eventSelectQueryBuilder.setParameters(this.getParameters());
|
||||
|
||||
const before = await eventSelectQueryBuilder.getOne();
|
||||
const before = await eventSelectQueryBuilder.getOne();
|
||||
|
||||
const result = await super.execute();
|
||||
const result = await super.execute();
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result.raw,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result.raw,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
before,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
before,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.DESTROYED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedBefore,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.DESTROYED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedBefore,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
return {
|
||||
raw: result.raw,
|
||||
generatedMaps: formattedResult,
|
||||
affected: result.affected,
|
||||
};
|
||||
return {
|
||||
raw: result.raw,
|
||||
generatedMaps: formattedResult,
|
||||
affected: result.affected,
|
||||
};
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
private getMainAliasTarget(): EntityTarget<T> {
|
||||
|
||||
+90
-85
@@ -17,6 +17,7 @@ import { QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twent
|
||||
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 { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
@@ -100,98 +101,102 @@ export class WorkspaceInsertQueryBuilder<
|
||||
}
|
||||
|
||||
override async execute(): Promise<InsertResult> {
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
try {
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
if (isDefined(this.relationNestedConfig)) {
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
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,
|
||||
});
|
||||
if (isDefined(this.relationNestedConfig)) {
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
this.authContext,
|
||||
);
|
||||
|
||||
this.expressionMap.valuesSet = updatedValues;
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: this.expressionMap.valuesSet as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
queryBuilder: nestedRelationQueryBuilder,
|
||||
});
|
||||
|
||||
this.expressionMap.valuesSet = updatedValues;
|
||||
}
|
||||
|
||||
const result = await super.execute();
|
||||
const eventSelectQueryBuilder = (
|
||||
this.connection.manager as WorkspaceEntityManager
|
||||
).createQueryBuilder(
|
||||
mainAliasTarget,
|
||||
this.expressionMap.mainAlias?.metadata.name ?? '',
|
||||
undefined,
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
) as WorkspaceSelectQueryBuilder<T>;
|
||||
|
||||
eventSelectQueryBuilder.whereInIds(
|
||||
result.identifiers.map((identifier) => identifier.id),
|
||||
);
|
||||
|
||||
const afterResult = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const formattedResultForEvent = formatResult<T[]>(
|
||||
afterResult,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.CREATED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedResultForEvent,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
// TypeORM returns all entity columns for insertions
|
||||
const resultWithoutInsertionExtraColumns = result.raw.map(
|
||||
(rawResult: Record<string, string>) =>
|
||||
Object.keys(rawResult)
|
||||
.filter((key) => this.expressionMap.returning.includes(key))
|
||||
.reduce((filtered: Record<string, string>, key) => {
|
||||
filtered[key] = rawResult[key];
|
||||
|
||||
return filtered;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
resultWithoutInsertionExtraColumns,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: resultWithoutInsertionExtraColumns,
|
||||
generatedMaps: formattedResult,
|
||||
identifiers: result.identifiers,
|
||||
};
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
|
||||
const result = await super.execute();
|
||||
const eventSelectQueryBuilder = (
|
||||
this.connection.manager as WorkspaceEntityManager
|
||||
).createQueryBuilder(
|
||||
mainAliasTarget,
|
||||
this.expressionMap.mainAlias?.metadata.name ?? '',
|
||||
undefined,
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
) as WorkspaceSelectQueryBuilder<T>;
|
||||
|
||||
eventSelectQueryBuilder.whereInIds(
|
||||
result.identifiers.map((identifier) => identifier.id),
|
||||
);
|
||||
|
||||
const afterResult = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const formattedResultForEvent = formatResult<T[]>(
|
||||
afterResult,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.CREATED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedResultForEvent,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
// TypeORM returns all entity columns for insertions
|
||||
const resultWithoutInsertionExtraColumns = result.raw.map(
|
||||
(rawResult: Record<string, string>) =>
|
||||
Object.keys(rawResult)
|
||||
.filter((key) => this.expressionMap.returning.includes(key))
|
||||
.reduce((filtered: Record<string, string>, key) => {
|
||||
filtered[key] = rawResult[key];
|
||||
|
||||
return filtered;
|
||||
}, {}),
|
||||
);
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
resultWithoutInsertionExtraColumns,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: resultWithoutInsertionExtraColumns,
|
||||
generatedMaps: formattedResult,
|
||||
identifiers: result.identifiers,
|
||||
};
|
||||
}
|
||||
|
||||
private getMainAliasTarget(): EntityTarget<T> {
|
||||
|
||||
+108
-75
@@ -11,6 +11,7 @@ import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
@@ -66,111 +67,139 @@ export class WorkspaceSelectQueryBuilder<
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
override async execute(): Promise<any> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const result = await super.execute();
|
||||
const result = await super.execute();
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: result,
|
||||
generatedMaps: formattedResult,
|
||||
identifiers: result.identifiers,
|
||||
};
|
||||
return {
|
||||
raw: result,
|
||||
generatedMaps: formattedResult,
|
||||
identifiers: result.identifiers,
|
||||
};
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override async getMany(): Promise<T[]> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const result = await super.getMany();
|
||||
const result = await super.getMany();
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return formattedResult;
|
||||
return formattedResult;
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
override getRawOne<U = any>(): Promise<U | undefined> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
return super.getRawOne();
|
||||
return super.getRawOne();
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
override getRawMany<U = any>(): Promise<U[]> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
return super.getRawMany();
|
||||
return super.getRawMany();
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override async getOne(): Promise<T | null> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const result = await super.getOne();
|
||||
const result = await super.getOne();
|
||||
|
||||
const formattedResult = formatResult<T>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedResult = formatResult<T>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return formattedResult;
|
||||
return formattedResult;
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override async getOneOrFail(): Promise<T> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const result = await super.getOneOrFail();
|
||||
const result = await super.getOneOrFail();
|
||||
|
||||
const formattedResult = formatResult<T>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedResult = formatResult<T>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return formattedResult[0];
|
||||
return formattedResult[0];
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override getCount(): Promise<number> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
return super.getCount();
|
||||
return super.getCount();
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override getExists(): Promise<boolean> {
|
||||
@@ -181,24 +210,28 @@ export class WorkspaceSelectQueryBuilder<
|
||||
}
|
||||
|
||||
override async getManyAndCount(): Promise<[T[], number]> {
|
||||
this.validatePermissions();
|
||||
try {
|
||||
this.validatePermissions();
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const [result, count] = await super.getManyAndCount();
|
||||
const [result, count] = await super.getManyAndCount();
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return [formattedResult, count];
|
||||
return [formattedResult, count];
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override insert(): WorkspaceInsertQueryBuilder<T> {
|
||||
|
||||
+36
-31
@@ -13,6 +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 { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
@@ -62,43 +63,47 @@ export class WorkspaceSoftDeleteQueryBuilder<
|
||||
}
|
||||
|
||||
override async execute(): Promise<UpdateResult> {
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
try {
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const after = await super.execute();
|
||||
const after = await super.execute();
|
||||
|
||||
const formattedAfter = formatResult<T[]>(
|
||||
after.raw,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const formattedAfter = formatResult<T[]>(
|
||||
after.raw,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.DELETED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedAfter,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.DELETED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedAfter,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
return {
|
||||
raw: after.raw,
|
||||
generatedMaps: formattedAfter,
|
||||
affected: after.affected,
|
||||
};
|
||||
return {
|
||||
raw: after.raw,
|
||||
generatedMaps: formattedAfter,
|
||||
affected: after.affected,
|
||||
};
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override select(): WorkspaceSelectQueryBuilder<T> {
|
||||
|
||||
+174
-163
@@ -17,6 +17,7 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/featu
|
||||
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 { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
@@ -80,156 +81,42 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
}
|
||||
|
||||
override async execute(): Promise<UpdateResult> {
|
||||
if (this.manyInputs) {
|
||||
return this.executeMany();
|
||||
}
|
||||
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
true,
|
||||
this.authContext,
|
||||
this.featureFlagMap,
|
||||
);
|
||||
|
||||
eventSelectQueryBuilder.expressionMap.wheres = this.expressionMap.wheres;
|
||||
eventSelectQueryBuilder.expressionMap.aliases = this.expressionMap.aliases;
|
||||
eventSelectQueryBuilder.setParameters(this.getParameters());
|
||||
|
||||
const before = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
this.authContext,
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
before,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const result = await super.execute();
|
||||
const after = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const formattedAfter = formatResult<T[]>(
|
||||
after,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.UPDATED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedAfter,
|
||||
beforeEntities: formattedBefore,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result.raw,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: result.raw,
|
||||
generatedMaps: formattedResult,
|
||||
affected: result.affected,
|
||||
};
|
||||
}
|
||||
|
||||
public async executeMany(): Promise<UpdateResult> {
|
||||
for (const input of this.manyInputs) {
|
||||
const fakeExpressionMapToValidatePermissions = Object.assign(
|
||||
{},
|
||||
this.expressionMap,
|
||||
{
|
||||
wheres: input.criteria,
|
||||
valuesSet: input.partialEntity,
|
||||
},
|
||||
);
|
||||
try {
|
||||
if (this.manyInputs) {
|
||||
return this.executeMany();
|
||||
}
|
||||
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: fakeExpressionMapToValidatePermissions,
|
||||
expressionMap: this.expressionMap,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
}
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
true,
|
||||
this.authContext,
|
||||
this.featureFlagMap,
|
||||
);
|
||||
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
true,
|
||||
this.authContext,
|
||||
this.featureFlagMap,
|
||||
);
|
||||
|
||||
eventSelectQueryBuilder.whereInIds(
|
||||
this.manyInputs.map((input) => input.criteria),
|
||||
);
|
||||
eventSelectQueryBuilder.expressionMap.aliases = this.expressionMap.aliases;
|
||||
eventSelectQueryBuilder.setParameters(this.getParameters());
|
||||
eventSelectQueryBuilder.expressionMap.wheres = this.expressionMap.wheres;
|
||||
eventSelectQueryBuilder.expressionMap.aliases =
|
||||
this.expressionMap.aliases;
|
||||
eventSelectQueryBuilder.setParameters(this.getParameters());
|
||||
|
||||
const beforeRecords = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
beforeRecords,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const results: UpdateResult[] = [];
|
||||
|
||||
for (const input of this.manyInputs) {
|
||||
this.expressionMap.valuesSet = input.partialEntity;
|
||||
this.where({ id: input.criteria });
|
||||
const before = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
@@ -242,7 +129,7 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
if (isDefined(this.relationNestedConfig)) {
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: input.partialEntity as
|
||||
entities: this.expressionMap.valuesSet as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
@@ -253,39 +140,163 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
updatedValues.length === 1 ? updatedValues[0] : updatedValues;
|
||||
}
|
||||
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
before,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const result = await super.execute();
|
||||
const after = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
results.push(result);
|
||||
const formattedAfter = formatResult<T[]>(
|
||||
after,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.UPDATED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedAfter,
|
||||
beforeEntities: formattedBefore,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
const formattedResult = formatResult<T[]>(
|
||||
result.raw,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: result.raw,
|
||||
generatedMaps: formattedResult,
|
||||
affected: result.affected,
|
||||
};
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
const afterRecords = await eventSelectQueryBuilder.getMany();
|
||||
public async executeMany(): Promise<UpdateResult> {
|
||||
try {
|
||||
for (const input of this.manyInputs) {
|
||||
const fakeExpressionMapToValidatePermissions = Object.assign(
|
||||
{},
|
||||
this.expressionMap,
|
||||
{
|
||||
wheres: input.criteria,
|
||||
valuesSet: input.partialEntity,
|
||||
},
|
||||
);
|
||||
|
||||
const formattedAfter = formatResult<T[]>(
|
||||
afterRecords,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: fakeExpressionMapToValidatePermissions,
|
||||
objectRecordsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
}
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.UPDATED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedAfter,
|
||||
beforeEntities: formattedBefore,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
const formattedResults = formatResult<T[]>(
|
||||
results.map((result) => result.raw),
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataFromEntityTarget(
|
||||
mainAliasTarget,
|
||||
this.internalContext,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: results.map((result) => result.raw),
|
||||
generatedMaps: formattedResults,
|
||||
affected: results.length,
|
||||
};
|
||||
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
true,
|
||||
this.authContext,
|
||||
this.featureFlagMap,
|
||||
);
|
||||
|
||||
eventSelectQueryBuilder.whereInIds(
|
||||
this.manyInputs.map((input) => input.criteria),
|
||||
);
|
||||
eventSelectQueryBuilder.expressionMap.aliases =
|
||||
this.expressionMap.aliases;
|
||||
eventSelectQueryBuilder.setParameters(this.getParameters());
|
||||
|
||||
const beforeRecords = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const formattedBefore = formatResult<T[]>(
|
||||
beforeRecords,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const results: UpdateResult[] = [];
|
||||
|
||||
for (const input of this.manyInputs) {
|
||||
this.expressionMap.valuesSet = input.partialEntity;
|
||||
this.where({ id: input.criteria });
|
||||
|
||||
const nestedRelationQueryBuilder = new WorkspaceSelectQueryBuilder(
|
||||
this as unknown as WorkspaceSelectQueryBuilder<T>,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
this.authContext,
|
||||
);
|
||||
|
||||
if (isDefined(this.relationNestedConfig)) {
|
||||
const updatedValues =
|
||||
await this.relationNestedQueries.processRelationNestedQueries({
|
||||
entities: input.partialEntity as
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>
|
||||
| QueryDeepPartialEntityWithNestedRelationFields<T>[],
|
||||
relationNestedConfig: this.relationNestedConfig,
|
||||
queryBuilder: nestedRelationQueryBuilder,
|
||||
});
|
||||
|
||||
this.expressionMap.valuesSet =
|
||||
updatedValues.length === 1 ? updatedValues[0] : updatedValues;
|
||||
}
|
||||
|
||||
const result = await super.execute();
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
const afterRecords = await eventSelectQueryBuilder.getMany();
|
||||
|
||||
const formattedAfter = formatResult<T[]>(
|
||||
afterRecords,
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.internalContext.eventEmitterService.emitMutationEvent({
|
||||
action: DatabaseEventAction.UPDATED,
|
||||
objectMetadataItem: objectMetadata,
|
||||
workspaceId: this.internalContext.workspaceId,
|
||||
entities: formattedAfter,
|
||||
beforeEntities: formattedBefore,
|
||||
authContext: this.authContext,
|
||||
});
|
||||
|
||||
const formattedResults = formatResult<T[]>(
|
||||
results.map((result) => result.raw),
|
||||
objectMetadata,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
raw: results.map((result) => result.raw),
|
||||
generatedMaps: formattedResults,
|
||||
affected: results.length,
|
||||
};
|
||||
} catch (error) {
|
||||
throw computeTwentyORMException(error);
|
||||
}
|
||||
}
|
||||
|
||||
override set(
|
||||
|
||||
Reference in New Issue
Block a user