Common api - Destroy and delete queries (#15177)
Done ⬇️ Gql : move delete and destroy logic to common Rest : - rename 'delete' handler to 'destroy' - create soft delete handlers (named 'delete') : one and many - create destroy many handler - update doc --> Rest api gains NEW soft delete one/many + destroy many capabilities closes : https://github.com/twentyhq/core-team-issues/issues/1579
This commit is contained in:
+159
-44
@@ -1,17 +1,33 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Omit } from 'zod/v4/core/util.cjs';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
|
||||
import { CommonSelectedFieldsHandler } from 'src/engine/api/common/common-args-handlers/common-query-selected-fields/common-selected-fields.handler';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonResultGettersService } from 'src/engine/api/common/common-result-getters/common-result-getters.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
CommonQueryArgs,
|
||||
CommonQueryNames,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { CommonQueryResult } from 'src/engine/api/common/types/common-query-result.type';
|
||||
import { isWorkspaceAuthContext } from 'src/engine/api/common/utils/is-workspace-auth-context.util';
|
||||
import { OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS } from 'src/engine/api/graphql/graphql-query-runner/constants/objects-with-settings-permissions-requirements';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper';
|
||||
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||
import { WorkspacePreQueryHookPayload } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service';
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role.service';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
@@ -29,7 +45,10 @@ import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/wo
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export abstract class CommonBaseQueryRunnerService {
|
||||
export abstract class CommonBaseQueryRunnerService<
|
||||
Args extends CommonQueryArgs,
|
||||
Output extends CommonQueryResult,
|
||||
> {
|
||||
@Inject()
|
||||
protected readonly workspaceQueryHookService: WorkspaceQueryHookService;
|
||||
@Inject()
|
||||
@@ -53,82 +72,147 @@ export abstract class CommonBaseQueryRunnerService {
|
||||
@Inject()
|
||||
protected readonly commonResultGettersService: CommonResultGettersService;
|
||||
|
||||
public async prepareQueryRunnerContext({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
}) {
|
||||
if (objectMetadataItemWithFieldMaps.isSystem === true) {
|
||||
await this.validateSettingsPermissionsOnObjectOrThrow(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
public async execute(
|
||||
args: CommonInput<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
operationName: CommonQueryNames,
|
||||
): Promise<Output> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
|
||||
if (!isWorkspaceAuthContext(authContext)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
await this.validate(args, queryRunnerContext);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
if (objectMetadataItemWithFieldMaps.isSystem === true) {
|
||||
await this.validateSettingsPermissionsOnObjectOrThrow(
|
||||
authContext,
|
||||
queryRunnerContext,
|
||||
);
|
||||
}
|
||||
|
||||
const { roleId } = await this.getRoleIdAndObjectsPermissions(
|
||||
authContext,
|
||||
workspace.id,
|
||||
const commonQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const rolePermissionConfig = { unionOf: [roleId] };
|
||||
|
||||
const repository = workspaceDataSource.getRepository(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
rolePermissionConfig,
|
||||
authContext,
|
||||
const processedArgs = await this.processArgs(
|
||||
args,
|
||||
queryRunnerContext,
|
||||
operationName,
|
||||
commonQueryParser,
|
||||
);
|
||||
|
||||
const extendedQueryRunnerContext =
|
||||
await this.prepareExtendedQueryRunnerContext(
|
||||
authContext,
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
const results = await this.run(processedArgs, {
|
||||
...extendedQueryRunnerContext,
|
||||
commonQueryParser,
|
||||
});
|
||||
|
||||
return this.enrichResultsWithGettersAndHooks({
|
||||
results,
|
||||
operationName,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract run(
|
||||
args: CommonExtendedInput<Args>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<Output>;
|
||||
|
||||
protected abstract validate(
|
||||
args: CommonInput<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void>;
|
||||
|
||||
protected abstract computeArgs(
|
||||
args: CommonInput<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<Args>>;
|
||||
|
||||
protected abstract processQueryResult(
|
||||
queryResult: Output,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<Output>;
|
||||
|
||||
private async processArgs(
|
||||
args: CommonInput<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
operationName: CommonQueryNames,
|
||||
commonQueryParser: GraphqlQueryParser,
|
||||
): Promise<CommonExtendedInput<Args>> {
|
||||
const selectedFieldsResult = commonQueryParser.parseSelectedFields(
|
||||
args.selectedFields,
|
||||
);
|
||||
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const hookedArgs =
|
||||
(await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
operationName,
|
||||
args as WorkspacePreQueryHookPayload<CommonQueryNames>,
|
||||
)) as CommonInput<Args>;
|
||||
|
||||
const computedArgs = await this.computeArgs(hookedArgs, queryRunnerContext);
|
||||
|
||||
return {
|
||||
workspaceDataSource,
|
||||
repository,
|
||||
rolePermissionConfig,
|
||||
...computedArgs,
|
||||
selectedFieldsResult,
|
||||
};
|
||||
}
|
||||
|
||||
public async enrichResultsWithGettersAndHooks({
|
||||
private async enrichResultsWithGettersAndHooks({
|
||||
results,
|
||||
operationName,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}: {
|
||||
results: ObjectRecord[];
|
||||
results: Output;
|
||||
operationName: CommonQueryNames;
|
||||
authContext: WorkspaceAuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
}): Promise<ObjectRecord[]> {
|
||||
const resultWithGetters =
|
||||
await this.commonResultGettersService.processQueryResult(
|
||||
results,
|
||||
objectMetadataItemWithFieldMaps.id,
|
||||
objectMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}): Promise<Output> {
|
||||
const resultWithGetters = await this.processQueryResult(
|
||||
results,
|
||||
objectMetadataItemWithFieldMaps.id,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
);
|
||||
|
||||
await this.workspaceQueryHookService.executePostQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
operationName,
|
||||
resultWithGetters,
|
||||
resultWithGetters as QueryResultFieldValue,
|
||||
);
|
||||
|
||||
return resultWithGetters;
|
||||
return resultWithGetters as Output;
|
||||
}
|
||||
|
||||
private async validateSettingsPermissionsOnObjectOrThrow(
|
||||
authContext: WorkspaceAuthContext,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
if (
|
||||
@@ -206,4 +290,35 @@ export abstract class CommonBaseQueryRunnerService {
|
||||
|
||||
return { roleId, objectsPermissions: objectMetadataPermissions[roleId] };
|
||||
}
|
||||
|
||||
private async prepareExtendedQueryRunnerContext(
|
||||
authContext: WorkspaceAuthContext,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<Omit<CommonExtendedQueryRunnerContext, 'commonQueryParser'>> {
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: authContext.workspace.id,
|
||||
});
|
||||
|
||||
const { roleId } = await this.getRoleIdAndObjectsPermissions(
|
||||
authContext,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
const rolePermissionConfig = { unionOf: [roleId] };
|
||||
|
||||
const repository = workspaceDataSource.getRepository(
|
||||
queryRunnerContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
rolePermissionConfig,
|
||||
authContext,
|
||||
);
|
||||
|
||||
return {
|
||||
...queryRunnerContext,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
repository,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+92
-110
@@ -16,13 +16,14 @@ import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonQueryNames,
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
CreateManyQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-selected-fields-result.type';
|
||||
import { isWorkspaceAuthContext } from 'src/engine/api/common/utils/is-workspace-auth-context.util';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
@@ -36,63 +37,28 @@ import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
@Injectable()
|
||||
export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerService {
|
||||
async run({
|
||||
args,
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}: {
|
||||
args: CreateManyQueryArgs;
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
}): Promise<ObjectRecord[]> {
|
||||
if (!isWorkspaceAuthContext(authContext)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
);
|
||||
}
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
|
||||
// TODO : Refacto-common - Remove this validation once https://github.com/twentyhq/core-team-issues/issues/1622 done
|
||||
args.data.forEach((record) => {
|
||||
if (record?.id) {
|
||||
assertIsValidUuid(record.id);
|
||||
}
|
||||
});
|
||||
|
||||
//TODO : Refacto-common - QueryParser should be common branded service
|
||||
const commonQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const selectedFieldsResult = commonQueryParser.parseSelectedFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args.selectedFields,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const { workspaceDataSource, repository, rolePermissionConfig } =
|
||||
await this.prepareQueryRunnerContext({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
|
||||
const processedArgs = await this.processQueryArgs({
|
||||
export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
CreateManyQueryArgs,
|
||||
ObjectRecord[]
|
||||
> {
|
||||
async run(
|
||||
args: CommonExtendedInput<CreateManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
const {
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
});
|
||||
objectMetadataMaps,
|
||||
workspaceDataSource,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const objectRecords = await this.insertOrUpsertRecords({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
args: processedArgs,
|
||||
selectedFieldsResult,
|
||||
args,
|
||||
});
|
||||
|
||||
const upsertedRecords = await this.fetchUpsertedRecords({
|
||||
@@ -100,63 +66,103 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
repository,
|
||||
selectedFieldsResult,
|
||||
selectedFieldsResult: args.selectedFieldsResult,
|
||||
});
|
||||
|
||||
await this.processNestedRelationsIfNeeded({
|
||||
args,
|
||||
records: upsertedRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
rolePermissionConfig,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
selectedFieldsResult,
|
||||
rolePermissionConfig,
|
||||
});
|
||||
|
||||
return upsertedRecords;
|
||||
}
|
||||
|
||||
async processQueryArgs({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
private async processNestedRelationsIfNeeded({
|
||||
args,
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
args: CommonExtendedInput<CreateManyQueryArgs>;
|
||||
records: ObjectRecord[];
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
args: CreateManyQueryArgs;
|
||||
}): Promise<CreateManyQueryArgs> {
|
||||
const hookedArgs =
|
||||
(await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
CommonQueryNames.CREATE_MANY,
|
||||
args,
|
||||
//TODO : Refacto-common - To fix when updating workspaceQueryHookService, removing gql typing dependency
|
||||
)) as CreateManyQueryArgs;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
authContext: AuthContext;
|
||||
workspaceDataSource: WorkspaceDataSource;
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
}): Promise<void> {
|
||||
if (!args.selectedFieldsResult.relations) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: records,
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
>,
|
||||
limit: QUERY_MAX_RECORDS,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
selectedFields: args.selectedFieldsResult.select,
|
||||
});
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<CreateManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<CreateManyQueryArgs>> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...hookedArgs,
|
||||
...args,
|
||||
data: await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({
|
||||
partialRecordInputs: hookedArgs.data,
|
||||
partialRecordInputs: args.data,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<CreateManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
|
||||
args.data.forEach((record) => {
|
||||
if (record?.id) {
|
||||
assertIsValidUuid(record.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async insertOrUpsertRecords({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
args,
|
||||
selectedFieldsResult,
|
||||
}: {
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
args: CreateManyQueryArgs;
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
args: CommonExtendedInput<CreateManyQueryArgs>;
|
||||
}): Promise<InsertResult> {
|
||||
const { selectedFieldsResult } = args;
|
||||
|
||||
if (!args.upsert) {
|
||||
const selectedColumns = buildColumnsToReturn({
|
||||
select: selectedFieldsResult.select,
|
||||
@@ -380,42 +386,18 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
return upsertedRecords as ObjectRecord[];
|
||||
}
|
||||
|
||||
private async processNestedRelationsIfNeeded({
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
rolePermissionConfig,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
selectedFieldsResult,
|
||||
}: {
|
||||
records: ObjectRecord[];
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
authContext: AuthContext;
|
||||
workspaceDataSource: WorkspaceDataSource;
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
}): Promise<void> {
|
||||
if (!selectedFieldsResult.relations) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return await this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: records,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
>,
|
||||
limit: QUERY_MAX_RECORDS,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
selectedFields: selectedFieldsResult.select,
|
||||
});
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
private getRecordWithoutCreatedBy(
|
||||
|
||||
+53
-21
@@ -2,42 +2,74 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import { CommonCreateManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service';
|
||||
import { CreateOneQueryArgs } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
CreateManyQueryArgs,
|
||||
CreateOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerService {
|
||||
export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
CreateOneQueryArgs,
|
||||
ObjectRecord
|
||||
> {
|
||||
constructor(
|
||||
private readonly commonCreateManyQueryRunnerService: CommonCreateManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run({
|
||||
args,
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}: {
|
||||
args: CreateOneQueryArgs;
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
}): Promise<ObjectRecord> {
|
||||
const result = await this.commonCreateManyQueryRunnerService.run({
|
||||
args: {
|
||||
async run(
|
||||
args: CommonExtendedInput<CreateManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord> {
|
||||
const result = await this.commonCreateManyQueryRunnerService.run(
|
||||
{
|
||||
...args,
|
||||
data: [args.data],
|
||||
},
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<CreateOneQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<CreateOneQueryArgs>> {
|
||||
return args;
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
_objectMetadataItemId: string,
|
||||
_objectMetadataMaps: ObjectMetadataMaps,
|
||||
_authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<CreateOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
|
||||
if (args.data?.id) {
|
||||
assertIsValidUuid(args.data.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { FindOptionsRelations, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
DeleteManyQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
DeleteManyQueryArgs,
|
||||
ObjectRecord[]
|
||||
> {
|
||||
async run(
|
||||
args: CommonExtendedInput<DeleteManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
const {
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
|
||||
const tableName = computeTableName(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectMetadataItemWithFieldMaps.isCustom,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
tableName,
|
||||
args.filter,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
.softDelete()
|
||||
.returning(columnsToReturn)
|
||||
.execute();
|
||||
|
||||
const deletedRecords = deletedObjectRecords.generatedMaps as ObjectRecord[];
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: deletedRecords,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
>,
|
||||
limit: QUERY_MAX_RECORDS,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
selectedFields: args.selectedFieldsResult.select,
|
||||
});
|
||||
}
|
||||
|
||||
return deletedRecords;
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<DeleteManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<DeleteManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: DeleteManyQueryArgs,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
if (!args.filter) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Filter is required',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
args.filter.id?.in?.forEach((id: string) => assertIsValidUuid(id));
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import { CommonDeleteManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-many-query-runner.service';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
DeleteOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDeleteOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
DeleteOneQueryArgs,
|
||||
ObjectRecord
|
||||
> {
|
||||
constructor(
|
||||
private readonly commonDeleteManyQueryRunnerService: CommonDeleteManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(
|
||||
args: CommonExtendedInput<DeleteOneQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord> {
|
||||
const result = await this.commonDeleteManyQueryRunnerService.run(
|
||||
{
|
||||
...args,
|
||||
filter: { id: { eq: args.id } },
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
if (!isDefined(result) || result.length === 0) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Record not found',
|
||||
CommonQueryRunnerExceptionCode.RECORD_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<DeleteOneQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<DeleteOneQueryArgs>> {
|
||||
return args;
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
_objectMetadataItemId: string,
|
||||
_objectMetadataMaps: ObjectMetadataMaps,
|
||||
_authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<DeleteOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertIsValidUuid(args.id);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FindOptionsRelations, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
DestroyManyQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDestroyManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
DestroyManyQueryArgs,
|
||||
ObjectRecord[]
|
||||
> {
|
||||
async run(
|
||||
args: CommonExtendedInput<DestroyManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
const {
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
args.filter,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
.delete()
|
||||
.returning(columnsToReturn)
|
||||
.execute();
|
||||
|
||||
const deletedRecords = deletedObjectRecords.generatedMaps as ObjectRecord[];
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: deletedRecords,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
>,
|
||||
limit: QUERY_MAX_RECORDS,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
selectedFields: args.selectedFieldsResult.select,
|
||||
});
|
||||
}
|
||||
|
||||
return deletedRecords;
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<DestroyManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<DestroyManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<DestroyManyQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
if (!isDefined(args.filter)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Filter is required',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import { CommonDestroyManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-many-query-runner.service';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
DestroyOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDestroyOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
DestroyOneQueryArgs,
|
||||
ObjectRecord
|
||||
> {
|
||||
constructor(
|
||||
private readonly commonDestroyManyQueryRunnerService: CommonDestroyManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(
|
||||
args: CommonExtendedInput<DestroyOneQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord> {
|
||||
const result = await this.commonDestroyManyQueryRunnerService.run(
|
||||
{
|
||||
...args,
|
||||
filter: { id: { eq: args.id } },
|
||||
},
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
if (!isDefined(result) || result.length === 0) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Record not found',
|
||||
CommonQueryRunnerExceptionCode.RECORD_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<DestroyOneQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<DestroyOneQueryArgs>> {
|
||||
return args;
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
_objectMetadataItemId: string,
|
||||
_objectMetadataMaps: ObjectMetadataMaps,
|
||||
_authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<DestroyOneQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
if (!isDefined(args.id)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Missing id',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
-107
@@ -16,73 +16,39 @@ import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonPageInfo } from 'src/engine/api/common/types/common-page-info.type';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import { CommonFindManyOutput } from 'src/engine/api/common/types/common-find-many-output.type';
|
||||
import {
|
||||
CommonQueryNames,
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
FindManyQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-selected-fields-result.type';
|
||||
import { getPageInfo } from 'src/engine/api/common/utils/get-page-info.util';
|
||||
import { isWorkspaceAuthContext } from 'src/engine/api/common/utils/is-workspace-auth-context.util';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { getCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
|
||||
import { computeCursorArgFilter } from 'src/engine/api/utils/compute-cursor-arg-filter.utils';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerService {
|
||||
async run({
|
||||
args,
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}: {
|
||||
args: FindManyQueryArgs;
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
}): Promise<{
|
||||
records: ObjectRecord[];
|
||||
aggregatedValues: Record<string, number>;
|
||||
totalCount: number;
|
||||
pageInfo: CommonPageInfo;
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
}> {
|
||||
this.validate(args);
|
||||
|
||||
if (!isWorkspaceAuthContext(authContext)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceDataSource, repository, rolePermissionConfig } =
|
||||
await this.prepareQueryRunnerContext({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
|
||||
const commonQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const selectedFieldsResult = commonQueryParser.parseSelectedFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args.selectedFields,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const processedArgs = await this.processQueryArgs({
|
||||
export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
FindManyQueryArgs,
|
||||
CommonFindManyOutput
|
||||
> {
|
||||
async run(
|
||||
args: CommonExtendedInput<FindManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<CommonFindManyOutput> {
|
||||
const {
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
});
|
||||
objectMetadataMaps,
|
||||
workspaceDataSource,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
@@ -90,7 +56,7 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
|
||||
const aggregateQueryBuilder = queryBuilder.clone();
|
||||
|
||||
let appliedFilters = processedArgs.filter ?? ({} as ObjectRecordFilter);
|
||||
let appliedFilters = args.filter ?? ({} as ObjectRecordFilter);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
aggregateQueryBuilder,
|
||||
@@ -104,13 +70,13 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
);
|
||||
|
||||
const orderByWithIdCondition = [
|
||||
...(processedArgs.orderBy ?? []),
|
||||
...(args.orderBy ?? []),
|
||||
{ id: OrderByDirection.AscNullsFirst },
|
||||
] as ObjectRecordOrderBy;
|
||||
|
||||
const isForwardPagination = !isDefined(processedArgs.before);
|
||||
const isForwardPagination = !isDefined(args.before);
|
||||
|
||||
const cursor = getCursor(processedArgs);
|
||||
const cursor = getCursor(args);
|
||||
|
||||
if (cursor) {
|
||||
const cursorArgFilter = computeCursorArgFilter(
|
||||
@@ -120,9 +86,9 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
isForwardPagination,
|
||||
);
|
||||
|
||||
appliedFilters = (processedArgs.filter
|
||||
appliedFilters = (args.filter
|
||||
? {
|
||||
and: [processedArgs.filter, { or: cursorArgFilter }],
|
||||
and: [args.filter, { or: cursorArgFilter }],
|
||||
}
|
||||
: { or: cursorArgFilter }) as unknown as ObjectRecordFilter;
|
||||
}
|
||||
@@ -143,17 +109,16 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
commonQueryParser.applyDeletedAtToBuilder(queryBuilder, appliedFilters);
|
||||
|
||||
ProcessAggregateHelper.addSelectedAggregatedFieldsQueriesToQueryBuilder({
|
||||
selectedAggregatedFields: selectedFieldsResult.aggregate,
|
||||
selectedAggregatedFields: args.selectedFieldsResult.aggregate,
|
||||
queryBuilder: aggregateQueryBuilder,
|
||||
objectMetadataNameSingular: objectMetadataItemWithFieldMaps.nameSingular,
|
||||
});
|
||||
|
||||
const limit =
|
||||
processedArgs.first ?? processedArgs.last ?? QUERY_MAX_RECORDS;
|
||||
const limit = args.first ?? args.last ?? QUERY_MAX_RECORDS;
|
||||
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: selectedFieldsResult.select,
|
||||
relations: selectedFieldsResult.relations,
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
@@ -183,44 +148,74 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
const parentObjectRecordsAggregatedValues =
|
||||
await aggregateQueryBuilder.getRawOne();
|
||||
|
||||
if (selectedFieldsResult.relations) {
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: objectRecords,
|
||||
parentObjectRecordsAggregatedValues,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: selectedFieldsResult.relations as Record<
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
>,
|
||||
aggregate: selectedFieldsResult.aggregate,
|
||||
aggregate: args.selectedFieldsResult.aggregate,
|
||||
limit: QUERY_MAX_RECORDS,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
selectedFields: selectedFieldsResult.select,
|
||||
selectedFields: args.selectedFieldsResult.select,
|
||||
});
|
||||
}
|
||||
|
||||
const enrichedRecords = await this.enrichResultsWithGettersAndHooks({
|
||||
results: objectRecords,
|
||||
operationName: CommonQueryNames.FIND_MANY,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
records: enrichedRecords,
|
||||
records: objectRecords,
|
||||
aggregatedValues: parentObjectRecordsAggregatedValues,
|
||||
totalCount: parentObjectRecordsAggregatedValues?.totalCount,
|
||||
pageInfo,
|
||||
selectedFieldsResult,
|
||||
selectedFieldsResult: args.selectedFieldsResult,
|
||||
};
|
||||
}
|
||||
|
||||
validate(args: FindManyQueryArgs) {
|
||||
async computeArgs(
|
||||
args: CommonInput<FindManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<FindManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: CommonFindManyOutput,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<CommonFindManyOutput> {
|
||||
const processedRecords =
|
||||
await this.commonResultGettersService.processRecordArray(
|
||||
queryResult.records,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
return {
|
||||
...queryResult,
|
||||
records: processedRecords,
|
||||
};
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<FindManyQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
if (args.first && args.last) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Cannot provide both first and last',
|
||||
@@ -258,31 +253,4 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async processQueryArgs({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
args: FindManyQueryArgs;
|
||||
}): Promise<FindManyQueryArgs> {
|
||||
const hookedArgs =
|
||||
(await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
CommonQueryNames.FIND_MANY,
|
||||
args,
|
||||
//TODO : Refacto-common - To fix when updating workspaceQueryHookService, removing gql typing dependency
|
||||
)) as FindManyQueryArgs;
|
||||
|
||||
return {
|
||||
...hookedArgs,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
hookedArgs.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+60
-90
@@ -13,70 +13,34 @@ import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import {
|
||||
CommonQueryNames,
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
FindOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { isWorkspaceAuthContext } from 'src/engine/api/common/utils/is-workspace-auth-context.util';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerService {
|
||||
async run({
|
||||
args,
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}: {
|
||||
args: FindOneQueryArgs;
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
}): Promise<ObjectRecord> {
|
||||
if (!isWorkspaceAuthContext(authContext)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceDataSource, repository, rolePermissionConfig } =
|
||||
await this.prepareQueryRunnerContext({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
|
||||
//TODO : Refacto-common - QueryParser should be common branded service
|
||||
const commonQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const selectedFieldsResult = commonQueryParser.parseSelectedFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args.selectedFields,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const processedArgs = await this.processQueryArgs({
|
||||
export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
FindOneQueryArgs,
|
||||
ObjectRecord
|
||||
> {
|
||||
async run(
|
||||
args: CommonExtendedInput<FindOneQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord> {
|
||||
const {
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
});
|
||||
|
||||
if (
|
||||
!processedArgs.filter ||
|
||||
Object.keys(processedArgs.filter).length === 0
|
||||
) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Missing filter argument',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
@@ -85,17 +49,17 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
processedArgs.filter ?? ({} as ObjectRecordFilter),
|
||||
args.filter ?? ({} as ObjectRecordFilter),
|
||||
);
|
||||
|
||||
commonQueryParser.applyDeletedAtToBuilder(
|
||||
queryBuilder,
|
||||
processedArgs.filter ?? ({} as ObjectRecordFilter),
|
||||
args.filter ?? ({} as ObjectRecordFilter),
|
||||
);
|
||||
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: selectedFieldsResult.select,
|
||||
relations: selectedFieldsResult.relations,
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
@@ -115,13 +79,13 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
const objectRecords = [objectRecord] as ObjectRecord[];
|
||||
|
||||
if (isDefined(selectedFieldsResult.relations)) {
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: objectRecords,
|
||||
//TODO : Refacto-common - To fix when switching processNestedRelationsHelper to Common
|
||||
relations: selectedFieldsResult.relations as Record<
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
>,
|
||||
@@ -129,45 +93,51 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
selectedFields: selectedFieldsResult.select,
|
||||
selectedFields: args.selectedFieldsResult.select,
|
||||
});
|
||||
}
|
||||
|
||||
const enrichedResults = await this.enrichResultsWithGettersAndHooks({
|
||||
results: objectRecords,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
operationName: CommonQueryNames.FIND_ONE,
|
||||
});
|
||||
|
||||
return enrichedResults[0];
|
||||
return objectRecords[0];
|
||||
}
|
||||
|
||||
async processQueryArgs({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
args: FindOneQueryArgs;
|
||||
}): Promise<FindOneQueryArgs> {
|
||||
const hookedArgs =
|
||||
(await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
CommonQueryNames.FIND_ONE,
|
||||
args,
|
||||
//TODO : Refacto-common - To fix when updating workspaceQueryHookService, removing gql typing dependency
|
||||
)) as FindOneQueryArgs;
|
||||
async computeArgs(
|
||||
args: CommonInput<FindOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<FindOneQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...hookedArgs,
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
hookedArgs.filter,
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<FindOneQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
if (!args.filter || Object.keys(args.filter).length === 0) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Missing filter argument',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-73
@@ -11,6 +11,7 @@ import {
|
||||
computeRecordGqlOperationFilter,
|
||||
convertViewFilterValueToString,
|
||||
getFilterTypeFromFieldType,
|
||||
isDefined,
|
||||
turnAnyFieldFilterIntoRecordGqlFilter,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
@@ -18,24 +19,20 @@ import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace
|
||||
import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { CommonExtendedQueryRunnerContext } from 'src/engine/api/common/types/common-extended-query-runner-context.type';
|
||||
import { CommonGroupByOutputItem } from 'src/engine/api/common/types/common-group-by-output-item.type';
|
||||
import {
|
||||
CommonQueryNames,
|
||||
CommonExtendedInput,
|
||||
CommonInput,
|
||||
GroupByQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { isWorkspaceAuthContext } from 'src/engine/api/common/utils/is-workspace-auth-context.util';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { formatResultWithGroupByDimensionValues } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/format-result-with-group-by-dimension-values.util';
|
||||
import { getGroupByExpression } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/get-group-by-expression.util';
|
||||
import { isGroupByDateField } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/is-group-by-date-field.util';
|
||||
import { parseGroupByArgs } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/parse-group-by-args.util';
|
||||
import { removeQuotes } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/remove-quote.util';
|
||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
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 { ViewFilterGroupService } from 'src/engine/metadata-modules/view-filter-group/services/view-filter-group.service';
|
||||
@@ -45,7 +42,10 @@ import { ViewService } from 'src/engine/metadata-modules/view/services/view.serv
|
||||
import { formatColumnNamesFromCompositeFieldAndSubfields } from 'src/engine/twenty-orm/utils/format-column-names-from-composite-field-and-subfield.util';
|
||||
|
||||
@Injectable()
|
||||
export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerService {
|
||||
export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
GroupByQueryArgs,
|
||||
CommonGroupByOutputItem[]
|
||||
> {
|
||||
constructor(
|
||||
private readonly viewFilterService: ViewFilterService,
|
||||
private readonly viewFilterGroupService: ViewFilterGroupService,
|
||||
@@ -54,46 +54,16 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
super();
|
||||
}
|
||||
|
||||
async run({
|
||||
args,
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}: {
|
||||
args: GroupByQueryArgs;
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
}): Promise<CommonGroupByOutputItem[]> {
|
||||
if (!isWorkspaceAuthContext(authContext)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
);
|
||||
}
|
||||
|
||||
const { repository } = await this.prepareQueryRunnerContext({
|
||||
async run(
|
||||
args: CommonExtendedInput<GroupByQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<CommonGroupByOutputItem[]> {
|
||||
const {
|
||||
repository,
|
||||
commonQueryParser,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
|
||||
//TODO : Refacto-common - QueryParser should be common branded service
|
||||
const commonQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const selectedFieldsResult = commonQueryParser.parseSelectedFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args.selectedFields,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const processedArgs = await this.processQueryArgs({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
});
|
||||
} = queryRunnerContext;
|
||||
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadataItemWithFieldMaps.nameSingular;
|
||||
@@ -104,9 +74,9 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
let appliedFilters = args.filter ?? ({} as ObjectRecordFilter);
|
||||
|
||||
if (args.viewId) {
|
||||
if (isDefined(args.viewId)) {
|
||||
appliedFilters = await this.addFiltersFromView({
|
||||
args: processedArgs,
|
||||
args,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
appliedFilters,
|
||||
@@ -122,13 +92,13 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
commonQueryParser.applyDeletedAtToBuilder(queryBuilder, appliedFilters);
|
||||
|
||||
ProcessAggregateHelper.addSelectedAggregatedFieldsQueriesToQueryBuilder({
|
||||
selectedAggregatedFields: selectedFieldsResult.aggregate,
|
||||
selectedAggregatedFields: args.selectedFieldsResult.aggregate,
|
||||
queryBuilder,
|
||||
objectMetadataNameSingular,
|
||||
});
|
||||
|
||||
const groupByFields = parseGroupByArgs(
|
||||
processedArgs,
|
||||
args,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
);
|
||||
|
||||
@@ -170,7 +140,7 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
commonQueryParser.applyGroupByOrderToBuilder(
|
||||
queryBuilder,
|
||||
processedArgs.orderBy ?? [],
|
||||
args.orderBy ?? [],
|
||||
groupByFields,
|
||||
);
|
||||
|
||||
@@ -179,10 +149,19 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
return formatResultWithGroupByDimensionValues(
|
||||
result,
|
||||
groupByDefinitions,
|
||||
Object.keys(selectedFieldsResult.aggregate),
|
||||
Object.keys(args.selectedFieldsResult.aggregate),
|
||||
);
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: CommonGroupByOutputItem[],
|
||||
_objectMetadataItemId: string,
|
||||
_objectMetadataMaps: ObjectMetadataMaps,
|
||||
_authContext: WorkspaceAuthContext,
|
||||
): Promise<CommonGroupByOutputItem[]> {
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
private async addFiltersFromView({
|
||||
args,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
@@ -271,28 +250,21 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
return appliedFilters;
|
||||
}
|
||||
|
||||
async processQueryArgs({
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
args,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
args: GroupByQueryArgs;
|
||||
}): Promise<GroupByQueryArgs> {
|
||||
const hookedArgs =
|
||||
(await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
CommonQueryNames.GROUP_BY,
|
||||
args,
|
||||
//TODO : Refacto-common - To fix when updating workspaceQueryHookService, removing gql typing dependency
|
||||
)) as GroupByQueryArgs;
|
||||
async validate(
|
||||
_args: CommonInput<GroupByQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<GroupByQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<GroupByQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...hookedArgs,
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
hookedArgs.filter,
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
|
||||
+8
@@ -1,5 +1,9 @@
|
||||
import { CommonCreateManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service';
|
||||
import { CommonCreateOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-one-query-runner.service';
|
||||
import { CommonDeleteManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-many-query-runner.service';
|
||||
import { CommonDeleteOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-one-query-runner.service';
|
||||
import { CommonDestroyManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-many-query-runner.service';
|
||||
import { CommonDestroyOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-one-query-runner.service';
|
||||
import { CommonFindManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-many-query-runner.service';
|
||||
import { CommonFindOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-one-query-runner.service';
|
||||
import { CommonGroupByQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-group-by-query-runner.service';
|
||||
@@ -10,4 +14,8 @@ export const CommonQueryRunners = [
|
||||
CommonCreateOneQueryRunnerService,
|
||||
CommonCreateManyQueryRunnerService,
|
||||
CommonGroupByQueryRunnerService,
|
||||
CommonDestroyOneQueryRunnerService,
|
||||
CommonDestroyManyQueryRunnerService,
|
||||
CommonDeleteOneQueryRunnerService,
|
||||
CommonDeleteManyQueryRunnerService,
|
||||
];
|
||||
|
||||
+2
-16
@@ -43,7 +43,7 @@ export class CommonResultGettersService {
|
||||
]);
|
||||
}
|
||||
|
||||
private async processRecordArray(
|
||||
public async processRecordArray(
|
||||
recordArray: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
@@ -62,7 +62,7 @@ export class CommonResultGettersService {
|
||||
);
|
||||
}
|
||||
|
||||
private async processRecord(
|
||||
public async processRecord(
|
||||
record: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
@@ -133,20 +133,6 @@ export class CommonResultGettersService {
|
||||
return processedRecord;
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResultField: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
workspaceId: string,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return await this.processRecordArray(
|
||||
queryResultField,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private getHandler(objectType: string): QueryResultGetterHandlerInterface {
|
||||
return (
|
||||
this.handlers.get(objectType) || {
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
export type CommonBaseQueryRunnerContext = {
|
||||
authContext: AuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { type CommonBaseQueryRunnerContext } from 'src/engine/api/common/types/common-base-query-runner-context.type';
|
||||
import { type GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
export type CommonExtendedQueryRunnerContext = Omit<
|
||||
CommonBaseQueryRunnerContext,
|
||||
'authContext'
|
||||
> & {
|
||||
authContext: WorkspaceAuthContext;
|
||||
rolePermissionConfig: RolePermissionConfig;
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
commonQueryParser: GraphqlQueryParser;
|
||||
workspaceDataSource: WorkspaceDataSource;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type CommonPageInfo } from 'src/engine/api/common/types/common-page-info.type';
|
||||
import { type CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-selected-fields-result.type';
|
||||
|
||||
export type CommonFindManyOutput = {
|
||||
records: ObjectRecord[];
|
||||
aggregatedValues: Record<string, number>;
|
||||
totalCount: number;
|
||||
pageInfo: CommonPageInfo;
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
};
|
||||
@@ -9,22 +9,35 @@ import {
|
||||
type ObjectRecordOrderBy,
|
||||
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { type CommonSelectedFields } from 'src/engine/api/common/types/common-selected-fields-result.type';
|
||||
import {
|
||||
type CommonSelectedFields,
|
||||
type CommonSelectedFieldsResult,
|
||||
} from 'src/engine/api/common/types/common-selected-fields-result.type';
|
||||
|
||||
export enum CommonQueryNames {
|
||||
FIND_ONE = 'findOne',
|
||||
FIND_MANY = 'findMany',
|
||||
CREATE_ONE = 'createOne',
|
||||
CREATE_MANY = 'createMany',
|
||||
GROUP_BY = 'groupBy',
|
||||
DESTROY_MANY = 'destroyMany',
|
||||
DESTROY_ONE = 'destroyOne',
|
||||
DELETE_MANY = 'deleteMany',
|
||||
DELETE_ONE = 'deleteOne',
|
||||
}
|
||||
|
||||
export interface FindOneQueryArgs {
|
||||
interface BaseQueryArgs {
|
||||
selectedFields: CommonSelectedFields;
|
||||
}
|
||||
|
||||
interface ExtendedBaseQueryArgs {
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
}
|
||||
export interface FindOneQueryArgs extends BaseQueryArgs {
|
||||
filter?: ObjectRecordFilter;
|
||||
}
|
||||
|
||||
export interface FindManyQueryArgs {
|
||||
selectedFields: CommonSelectedFields;
|
||||
filter?: ObjectRecordFilter;
|
||||
orderBy?: ObjectRecordOrderBy;
|
||||
first?: number;
|
||||
@@ -34,25 +47,48 @@ export interface FindManyQueryArgs {
|
||||
}
|
||||
|
||||
export interface CreateManyQueryArgs {
|
||||
selectedFields: CommonSelectedFields;
|
||||
data: Partial<ObjectRecord>[];
|
||||
upsert?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateOneQueryArgs {
|
||||
selectedFields: CommonSelectedFields;
|
||||
data: Partial<ObjectRecord>;
|
||||
upsert?: boolean;
|
||||
}
|
||||
export interface GroupByQueryArgs {
|
||||
selectedFields: CommonSelectedFields;
|
||||
filter?: ObjectRecordFilter;
|
||||
orderBy?: OrderByWithGroupBy;
|
||||
groupBy: ObjectRecordGroupBy;
|
||||
viewId?: string;
|
||||
}
|
||||
export interface DestroyOneQueryArgs {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface DestroyManyQueryArgs {
|
||||
filter: ObjectRecordFilter;
|
||||
}
|
||||
|
||||
export interface DeleteOneQueryArgs {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface DeleteManyQueryArgs {
|
||||
filter: ObjectRecordFilter;
|
||||
}
|
||||
|
||||
export type CommonQueryArgs =
|
||||
| FindOneQueryArgs
|
||||
| FindManyQueryArgs
|
||||
| GroupByQueryArgs;
|
||||
| CreateManyQueryArgs
|
||||
| CreateOneQueryArgs
|
||||
| GroupByQueryArgs
|
||||
| DestroyOneQueryArgs
|
||||
| DestroyManyQueryArgs
|
||||
| DeleteOneQueryArgs
|
||||
| DeleteManyQueryArgs;
|
||||
|
||||
export type CommonInput<T extends CommonQueryArgs> = T & BaseQueryArgs;
|
||||
|
||||
export type CommonExtendedInput<T extends CommonQueryArgs> = T &
|
||||
ExtendedBaseQueryArgs;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type CommonFindManyOutput } from 'src/engine/api/common/types/common-find-many-output.type';
|
||||
import { type CommonGroupByOutputItem } from 'src/engine/api/common/types/common-group-by-output-item.type';
|
||||
|
||||
export type CommonQueryResult =
|
||||
| ObjectRecord[]
|
||||
| ObjectRecord
|
||||
| CommonGroupByOutputItem[]
|
||||
| CommonFindManyOutput;
|
||||
+4
-6
@@ -139,19 +139,17 @@ export class GraphqlQueryParser {
|
||||
}
|
||||
|
||||
public parseSelectedFields(
|
||||
parentObjectMetadata: ObjectMetadataItemWithFieldMaps,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): GraphqlQuerySelectedFieldsResult {
|
||||
const objectMetadataMapItem = getObjectMetadataMapItemByNameSingular(
|
||||
this.objectMetadataMaps,
|
||||
parentObjectMetadata.nameSingular,
|
||||
this.objectMetadataMapItem.nameSingular,
|
||||
);
|
||||
|
||||
if (!objectMetadataMapItem) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Could not find object metadata for ${parentObjectMetadata.nameSingular}`,
|
||||
`Could not find object metadata for ${this.objectMetadataMapItem.nameSingular}`,
|
||||
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
@@ -162,8 +160,8 @@ export class GraphqlQueryParser {
|
||||
|
||||
return selectedFieldsParser.parse(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
objectMetadataMaps,
|
||||
this.objectMetadataMapItem,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-contex
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
@Injectable()
|
||||
export class ProcessNestedRelationsHelper {
|
||||
|
||||
+2
-6
@@ -35,7 +35,7 @@ import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role
|
||||
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
export type GraphqlQueryResolverExecutionArgs<Input extends ResolverArgs> = {
|
||||
args: Input;
|
||||
@@ -162,11 +162,7 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
const selectedFields = graphqlFields(options.info);
|
||||
|
||||
const graphqlQuerySelectedFieldsResult =
|
||||
graphqlQueryParser.parseSelectedFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
selectedFields,
|
||||
options.objectMetadataMaps,
|
||||
);
|
||||
graphqlQueryParser.parseSelectedFields(selectedFields);
|
||||
|
||||
const graphqlQueryResolverExecutionArgs = {
|
||||
args: computedArgs,
|
||||
|
||||
+3
-1
@@ -2,8 +2,10 @@ import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
|
||||
import { type CommonQueryResult } from 'src/engine/api/common/types/common-query-result.type';
|
||||
|
||||
export const isQueryResultFieldValueARecordArray = (
|
||||
result: QueryResultFieldValue,
|
||||
result: QueryResultFieldValue | CommonQueryResult,
|
||||
): result is ObjectRecord[] => {
|
||||
return Array.isArray(result);
|
||||
};
|
||||
|
||||
+3
-1
@@ -2,8 +2,10 @@ import { type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
|
||||
import { type CommonQueryResult } from 'src/engine/api/common/types/common-query-result.type';
|
||||
|
||||
export const isQueryResultFieldValueARecord = (
|
||||
result: QueryResultFieldValue,
|
||||
result: QueryResultFieldValue | CommonQueryResult,
|
||||
): result is ObjectRecord => {
|
||||
return 'id' in result;
|
||||
};
|
||||
|
||||
+8
-6
@@ -233,12 +233,14 @@ export class QueryRunnerArgsFactory {
|
||||
return allOverriddenRecords;
|
||||
}
|
||||
|
||||
public overrideFilterByFieldMetadata(
|
||||
filter: ObjectRecordFilter | undefined,
|
||||
public overrideFilterByFieldMetadata<
|
||||
T extends ObjectRecordFilter | undefined,
|
||||
>(
|
||||
filter: T,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
) {
|
||||
if (!filter) {
|
||||
return;
|
||||
): T {
|
||||
if (!isDefined(filter)) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
const overrideFilter = (filterObject: ObjectRecordFilter) => {
|
||||
@@ -264,7 +266,7 @@ export class QueryRunnerArgsFactory {
|
||||
}, {});
|
||||
};
|
||||
|
||||
return overrideFilter(filter);
|
||||
return overrideFilter(filter) as T;
|
||||
}
|
||||
|
||||
private transformFilterValueByType(
|
||||
|
||||
+6
-7
@@ -12,6 +12,7 @@ import {
|
||||
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonCreateManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryCreateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
@@ -48,13 +49,11 @@ export class CreateManyResolverFactory
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
const records = await this.commonCreateManyQueryRunnerService.run({
|
||||
args: { ...args, selectedFields },
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataMaps: internalContext.objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps:
|
||||
internalContext.objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
const records = await this.commonCreateManyQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.CREATE_MANY,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
|
||||
+6
-7
@@ -11,6 +11,7 @@ import {
|
||||
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonCreateOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryCreateOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
@@ -45,13 +46,11 @@ export class CreateOneResolverFactory
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
const record = await this.commonCreateOneQueryRunnerService.run({
|
||||
args: { ...args, selectedFields },
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataMaps: internalContext.objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps:
|
||||
internalContext.objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
const record = await this.commonCreateOneQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.CREATE_ONE,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
|
||||
+47
@@ -1,5 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import { type WorkspaceResolverBuilderFactoryInterface } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface';
|
||||
import {
|
||||
@@ -8,8 +11,14 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonDeleteManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryDeleteManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteManyResolverFactory
|
||||
@@ -19,6 +28,8 @@ export class DeleteManyResolverFactory
|
||||
|
||||
constructor(
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryDeleteManyResolverService,
|
||||
private readonly commonDeleteManyQueryRunnerService: CommonDeleteManyQueryRunnerService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@@ -27,6 +38,42 @@ export class DeleteManyResolverFactory
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, _context, info) => {
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: internalContext.authContext.workspace?.id as string,
|
||||
});
|
||||
|
||||
const featureFlagsMap = workspaceDataSource.featureFlagMap;
|
||||
|
||||
if (featureFlagsMap[FeatureFlagKey.IS_COMMON_API_ENABLED]) {
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
const records = await this.commonDeleteManyQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.DELETE_MANY,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
const options: WorkspaceQueryRunnerOptions = {
|
||||
authContext: internalContext.authContext,
|
||||
info,
|
||||
|
||||
+45
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import graphqlFields from 'graphql-fields';
|
||||
|
||||
import { type WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import { type WorkspaceResolverBuilderFactoryInterface } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface';
|
||||
import {
|
||||
@@ -8,16 +10,25 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonDeleteOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryDeleteOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteOneResolverFactory
|
||||
implements WorkspaceResolverBuilderFactoryInterface
|
||||
{
|
||||
public static methodName = RESOLVER_METHOD_NAMES.DELETE_ONE;
|
||||
|
||||
constructor(
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryDeleteOneResolverService,
|
||||
private readonly commonDeleteOneQueryRunnerService: CommonDeleteOneQueryRunnerService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@@ -26,6 +37,40 @@ export class DeleteOneResolverFactory
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, _context, info) => {
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: internalContext.authContext.workspace?.id as string,
|
||||
});
|
||||
|
||||
const featureFlagsMap = workspaceDataSource.featureFlagMap;
|
||||
|
||||
if (featureFlagsMap[FeatureFlagKey.IS_COMMON_API_ENABLED]) {
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
const record = await this.commonDeleteOneQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.DELETE_ONE,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
const options: WorkspaceQueryRunnerOptions = {
|
||||
authContext: internalContext.authContext,
|
||||
info,
|
||||
|
||||
+48
@@ -1,5 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import { type WorkspaceResolverBuilderFactoryInterface } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface';
|
||||
import {
|
||||
@@ -8,8 +11,14 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonDestroyManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryDestroyManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyManyResolverFactory
|
||||
@@ -19,6 +28,8 @@ export class DestroyManyResolverFactory
|
||||
|
||||
constructor(
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryDestroyManyResolverService,
|
||||
private readonly commonDestroyManyQueryRunnerService: CommonDestroyManyQueryRunnerService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@@ -27,6 +38,43 @@ export class DestroyManyResolverFactory
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, _context, info) => {
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: internalContext.authContext.workspace?.id as string,
|
||||
});
|
||||
|
||||
const featureFlagsMap = workspaceDataSource.featureFlagMap;
|
||||
|
||||
if (featureFlagsMap[FeatureFlagKey.IS_COMMON_API_ENABLED]) {
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
const records =
|
||||
await this.commonDestroyManyQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.DESTROY_MANY,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
const options: WorkspaceQueryRunnerOptions = {
|
||||
authContext: internalContext.authContext,
|
||||
info,
|
||||
|
||||
+44
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import graphqlFields from 'graphql-fields';
|
||||
|
||||
import { type WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import { type WorkspaceResolverBuilderFactoryInterface } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface';
|
||||
import {
|
||||
@@ -8,8 +10,14 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { type WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonDestroyOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryDestroyOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyOneResolverFactory
|
||||
@@ -19,6 +27,8 @@ export class DestroyOneResolverFactory
|
||||
|
||||
constructor(
|
||||
private readonly graphQLQueryRunnerService: GraphqlQueryDestroyOneResolverService,
|
||||
private readonly commonDestroyOneQueryRunnerService: CommonDestroyOneQueryRunnerService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@@ -27,6 +37,40 @@ export class DestroyOneResolverFactory
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, _context, info) => {
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: internalContext.authContext.workspace?.id as string,
|
||||
});
|
||||
|
||||
const featureFlagsMap = workspaceDataSource.featureFlagMap;
|
||||
|
||||
if (featureFlagsMap[FeatureFlagKey.IS_COMMON_API_ENABLED]) {
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
const record = await this.commonDestroyOneQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.DESTROY_ONE,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
const options: WorkspaceQueryRunnerOptions = {
|
||||
authContext: internalContext.authContext,
|
||||
info,
|
||||
|
||||
+6
-7
@@ -11,6 +11,7 @@ import {
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonFindManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryFindManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
@@ -51,13 +52,11 @@ export class FindManyResolverFactory
|
||||
totalCount,
|
||||
pageInfo,
|
||||
selectedFieldsResult,
|
||||
} = await this.commonFindManyQueryRunnerService.run({
|
||||
args: { ...args, selectedFields },
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataMaps: internalContext.objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps:
|
||||
internalContext.objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
} = await this.commonFindManyQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.FIND_MANY,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
|
||||
+6
-7
@@ -10,6 +10,7 @@ import {
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonFindOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { GraphqlQueryFindOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
@@ -44,13 +45,11 @@ export class FindOneResolverFactory
|
||||
try {
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
const record = await this.commonFindOneQueryRunnerService.run({
|
||||
args: { ...args, selectedFields },
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataMaps: internalContext.objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps:
|
||||
internalContext.objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
const record = await this.commonFindOneQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.FIND_ONE,
|
||||
);
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
|
||||
+6
-7
@@ -11,6 +11,7 @@ import {
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { CommonGroupByQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-group-by-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { GraphqlQueryGroupByResolverService } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/graphql-query-group-by-resolver.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
|
||||
@@ -44,13 +45,11 @@ export class GroupByResolverFactory
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
try {
|
||||
return await this.commonGroupByQueryRunnerService.run({
|
||||
args: { ...args, selectedFields },
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataMaps: internalContext.objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps:
|
||||
internalContext.objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
return await this.commonGroupByQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
internalContext,
|
||||
CommonQueryNames.GROUP_BY,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
|
||||
+12
-8
@@ -6,11 +6,12 @@ import {
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonCreateManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseUpsertRestRequest } from 'src/engine/api/rest/input-request-parsers/upsert-parser-utils/parse-upsert-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
@@ -41,12 +42,15 @@ export class RestApiCreateManyHandler extends RestApiBaseHandler {
|
||||
authContext,
|
||||
});
|
||||
|
||||
const records = await this.commonCreateManyQueryRunnerService.run({
|
||||
args: { data, selectedFields, upsert },
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
const records = await this.commonCreateManyQueryRunnerService.execute(
|
||||
{ data, selectedFields, upsert },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.CREATE_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
records,
|
||||
@@ -61,7 +65,7 @@ export class RestApiCreateManyHandler extends RestApiBaseHandler {
|
||||
records: ObjectRecord[],
|
||||
objectNamePlural: string,
|
||||
) {
|
||||
return { data: { [objectNamePlural]: records } };
|
||||
return { data: { [`create${capitalize(objectNamePlural)}`]: records } };
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
|
||||
+12
-8
@@ -6,11 +6,12 @@ import {
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonCreateOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-create-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseUpsertRestRequest } from 'src/engine/api/rest/input-request-parsers/upsert-parser-utils/parse-upsert-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
@@ -42,12 +43,15 @@ export class RestApiCreateOneHandler extends RestApiBaseHandler {
|
||||
authContext,
|
||||
});
|
||||
|
||||
const record = await this.commonCreateOneQueryRunnerService.run({
|
||||
args: { data, selectedFields, upsert },
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
const record = await this.commonCreateOneQueryRunnerService.execute(
|
||||
{ data, selectedFields, upsert },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.CREATE_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
@@ -59,7 +63,7 @@ export class RestApiCreateOneHandler extends RestApiBaseHandler {
|
||||
}
|
||||
|
||||
private formatRestResponse(record: ObjectRecord, objectNameSingular: string) {
|
||||
return { data: { [objectNameSingular]: record } };
|
||||
return { data: { [`create${capitalize(objectNameSingular)}`]: record } };
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDeleteManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiDeleteManyHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonDeleteManyQueryRunnerService: CommonDeleteManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest): Promise<{
|
||||
data: {
|
||||
[x: string]: ObjectRecord[];
|
||||
};
|
||||
}> {
|
||||
const { filter } = this.parseRequestArgs(request);
|
||||
const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
await this.buildCommonOptions(request);
|
||||
|
||||
try {
|
||||
const records = await this.commonDeleteManyQueryRunnerService.execute(
|
||||
{ filter, selectedFields: { id: true } },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DELETE_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps.namePlural,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(
|
||||
records: ObjectRecord[],
|
||||
objectNamePlural: string,
|
||||
) {
|
||||
return {
|
||||
data: {
|
||||
[`delete${capitalize(objectNamePlural)}`]: records,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const filter = parseFilterRestRequest(request);
|
||||
|
||||
return {
|
||||
filter,
|
||||
};
|
||||
}
|
||||
}
|
||||
+55
-28
@@ -1,43 +1,70 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDeleteOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-delete-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiDeleteOneHandler extends RestApiBaseHandler {
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
constructor(
|
||||
private readonly commonDeleteOneQueryRunnerService: CommonDeleteOneQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
if (!recordId) {
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
const { id } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const record = await this.commonDeleteOneQueryRunnerService.execute(
|
||||
{ id, selectedFields: { id: true } },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DELETE_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(record: ObjectRecord, objectNameSingular: string) {
|
||||
return {
|
||||
data: { [`delete${capitalize(objectNameSingular)}`]: record },
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const { id } = parseCorePath(request);
|
||||
|
||||
if (!isDefined(id)) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
const { objectMetadata, repository, restrictedFields } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
|
||||
const selectOptions = getAllSelectableFields({
|
||||
restrictedFields,
|
||||
objectMetadata,
|
||||
});
|
||||
|
||||
const recordToDelete = await repository.findOneOrFail({
|
||||
where: { id: recordId },
|
||||
select: selectOptions,
|
||||
});
|
||||
|
||||
const columnsToReturnForDelete: string[] = [];
|
||||
|
||||
await repository.delete(recordId, undefined, columnsToReturnForDelete);
|
||||
|
||||
return this.formatResult({
|
||||
operation: 'delete',
|
||||
objectNameSingular: objectMetadata.objectMetadataMapItem.nameSingular,
|
||||
data: {
|
||||
id: recordToDelete.id,
|
||||
},
|
||||
});
|
||||
return {
|
||||
id,
|
||||
depth: parseDepthRestRequest(request),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDestroyManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiDestroyManyHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonDestroyManyQueryRunnerService: CommonDestroyManyQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest): Promise<{
|
||||
data: {
|
||||
[x: string]: ObjectRecord[];
|
||||
};
|
||||
}> {
|
||||
const { filter } = this.parseRequestArgs(request);
|
||||
const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
await this.buildCommonOptions(request);
|
||||
|
||||
try {
|
||||
const records = await this.commonDestroyManyQueryRunnerService.execute(
|
||||
{ filter, selectedFields: { id: true } },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DESTROY_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps.namePlural,
|
||||
);
|
||||
} catch (error) {
|
||||
return workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(
|
||||
records: ObjectRecord[],
|
||||
objectNamePlural: string,
|
||||
) {
|
||||
return {
|
||||
data: {
|
||||
[`delete${capitalize(objectNamePlural)}`]: records,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const filter = parseFilterRestRequest(request);
|
||||
|
||||
return {
|
||||
filter,
|
||||
};
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonDestroyOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-destroy-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { workspaceQueryRunnerRestApiExceptionHandler } from 'src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util';
|
||||
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiDestroyOneHandler extends RestApiBaseHandler {
|
||||
constructor(
|
||||
private readonly commonDestroyOneQueryRunnerService: CommonDestroyOneQueryRunnerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async commonHandle(request: AuthenticatedRequest) {
|
||||
try {
|
||||
const { id } = this.parseRequestArgs(request);
|
||||
|
||||
const {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
} = await this.buildCommonOptions(request);
|
||||
|
||||
const record = await this.commonDestroyOneQueryRunnerService.execute(
|
||||
{ id, selectedFields: { id: true } },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.DESTROY_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
private formatRestResponse(record: ObjectRecord, objectNameSingular: string) {
|
||||
return { data: { [`delete${capitalize(objectNameSingular)}`]: record } };
|
||||
}
|
||||
|
||||
private parseRequestArgs(request: AuthenticatedRequest) {
|
||||
const { id } = parseCorePath(request);
|
||||
|
||||
if (!isDefined(id)) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
async handle(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
|
||||
if (!recordId) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
const { objectMetadata, repository, restrictedFields } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
|
||||
const selectOptions = getAllSelectableFields({
|
||||
restrictedFields,
|
||||
objectMetadata,
|
||||
});
|
||||
|
||||
const recordToDelete = await repository.findOneOrFail({
|
||||
where: { id: recordId },
|
||||
select: selectOptions,
|
||||
});
|
||||
|
||||
const columnsToReturnForDelete: string[] = [];
|
||||
|
||||
await repository.delete(recordId, undefined, columnsToReturnForDelete);
|
||||
|
||||
return this.formatResult({
|
||||
operation: 'delete',
|
||||
objectNameSingular: objectMetadata.objectMetadataMapItem.nameSingular,
|
||||
data: {
|
||||
id: recordToDelete.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -8,6 +8,7 @@ import {
|
||||
} from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonFindManyQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-many-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { parseEndingBeforeRestRequest } from 'src/engine/api/rest/input-request-parsers/ending-before-parser-utils/parse-ending-before-rest-request.util';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
@@ -110,12 +111,15 @@ export class RestApiFindManyHandler extends RestApiBaseHandler {
|
||||
});
|
||||
|
||||
const { records, aggregatedValues, pageInfo } =
|
||||
await this.commonFindManyQueryRunnerService.run({
|
||||
args: { ...parsedArgs, selectedFields },
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
await this.commonFindManyQueryRunnerService.execute(
|
||||
{ ...parsedArgs, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.FIND_MANY,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
records,
|
||||
|
||||
+10
-6
@@ -6,6 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonFindOneQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-find-one-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseDepthRestRequest } from 'src/engine/api/rest/input-request-parsers/depth-parser-utils/parse-depth-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
@@ -73,12 +74,15 @@ export class RestApiFindOneHandler extends RestApiBaseHandler {
|
||||
authContext,
|
||||
});
|
||||
|
||||
const record = await this.commonFindOneQueryRunnerService.run({
|
||||
args: { filter, selectedFields },
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
const record = await this.commonFindOneQueryRunnerService.execute(
|
||||
{ filter, selectedFields },
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.FIND_ONE,
|
||||
);
|
||||
|
||||
return this.formatRestResponse(
|
||||
record,
|
||||
|
||||
+10
-6
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { RestApiBaseHandler } from 'src/engine/api/rest/core/interfaces/rest-api-base.handler';
|
||||
|
||||
import { CommonGroupByQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-group-by-query-runner.service';
|
||||
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { parseAggregateFieldsRestRequest } from 'src/engine/api/rest/input-request-parsers/aggregate-fields-parser-utils/parse-aggregate-fields-rest-request.util';
|
||||
import { parseFilterRestRequest } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util';
|
||||
import { parseGroupByRestRequest } from 'src/engine/api/rest/input-request-parsers/group-by-parser-utils/parse-group-by-rest-request.util';
|
||||
@@ -30,18 +31,21 @@ export class RestApiGroupByHandler extends RestApiBaseHandler {
|
||||
const { filter, orderBy, viewId, groupBy, selectedFields } =
|
||||
this.parseRequestArgs(request);
|
||||
|
||||
return await this.commonGroupByQueryRunnerService.run({
|
||||
args: {
|
||||
return await this.commonGroupByQueryRunnerService.execute(
|
||||
{
|
||||
filter,
|
||||
orderBy,
|
||||
viewId,
|
||||
groupBy,
|
||||
selectedFields,
|
||||
},
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
{
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
},
|
||||
CommonQueryNames.GROUP_BY,
|
||||
);
|
||||
} catch (error) {
|
||||
throw workspaceQueryRunnerRestApiExceptionHandler(error);
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ export abstract class RestApiBaseHandler {
|
||||
objectMetadataMaps,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
depth: Depth;
|
||||
depth?: Depth | undefined;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
}) {
|
||||
|
||||
@@ -5,7 +5,10 @@ import { CoreCommonApiModule } from 'src/engine/api/common/core-common-api.modul
|
||||
import { RestApiCoreController } from 'src/engine/api/rest/core/controllers/rest-api-core.controller';
|
||||
import { RestApiCreateManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-create-many.handler';
|
||||
import { RestApiCreateOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-create-one.handler';
|
||||
import { RestApiDeleteManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-delete-many.handler';
|
||||
import { RestApiDeleteOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-delete-one.handler';
|
||||
import { RestApiDestroyManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-destroy-many.handler';
|
||||
import { RestApiDestroyOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-destroy-one.handler';
|
||||
import { RestApiFindDuplicatesHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-duplicates.handler';
|
||||
import { RestApiFindManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-many.handler';
|
||||
import { RestApiFindOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-one.handler';
|
||||
@@ -28,14 +31,17 @@ import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
const restApiCoreResolvers = [
|
||||
RestApiDeleteOneHandler,
|
||||
RestApiCreateOneHandler,
|
||||
RestApiCreateManyHandler,
|
||||
RestApiUpdateOneHandler,
|
||||
RestApiFindOneHandler,
|
||||
RestApiFindManyHandler,
|
||||
RestApiFindDuplicatesHandler,
|
||||
RestApiGroupByHandler,
|
||||
RestApiUpdateOneHandler,
|
||||
RestApiDestroyOneHandler,
|
||||
RestApiDestroyManyHandler,
|
||||
RestApiDeleteOneHandler,
|
||||
RestApiDeleteManyHandler,
|
||||
];
|
||||
|
||||
@Module({
|
||||
|
||||
@@ -4,13 +4,17 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RestApiCreateManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-create-many.handler';
|
||||
import { RestApiCreateOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-create-one.handler';
|
||||
import { RestApiDeleteManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-delete-many.handler';
|
||||
import { RestApiDeleteOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-delete-one.handler';
|
||||
import { RestApiDestroyManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-destroy-many.handler';
|
||||
import { RestApiDestroyOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-destroy-one.handler';
|
||||
import { RestApiFindDuplicatesHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-duplicates.handler';
|
||||
import { RestApiFindManyHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-many.handler';
|
||||
import { RestApiFindOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-find-one.handler';
|
||||
import { RestApiGroupByHandler } from 'src/engine/api/rest/core/handlers/rest-api-group-by.handler';
|
||||
import { RestApiUpdateOneHandler } from 'src/engine/api/rest/core/handlers/rest-api-update-one.handler';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { parseSoftDeleteRestRequest } from 'src/engine/api/rest/input-request-parsers/soft-delete-parser-utils/parse-soft-delete-rest-request.util';
|
||||
import { AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
@@ -18,14 +22,17 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service
|
||||
@Injectable()
|
||||
export class RestApiCoreService {
|
||||
constructor(
|
||||
private readonly restApiDeleteOneHandler: RestApiDeleteOneHandler,
|
||||
private readonly restApiCreateOneHandler: RestApiCreateOneHandler,
|
||||
private readonly restApiCreateManyHandler: RestApiCreateManyHandler,
|
||||
private readonly restApiUpdateOneHandler: RestApiUpdateOneHandler,
|
||||
private readonly restApiFindOneHandler: RestApiFindOneHandler,
|
||||
private readonly restApiFindManyHandler: RestApiFindManyHandler,
|
||||
private readonly restApiFindDuplicatesHandler: RestApiFindDuplicatesHandler,
|
||||
private readonly restApiGroupByHandler: RestApiGroupByHandler,
|
||||
private readonly restApiUpdateOneHandler: RestApiUpdateOneHandler,
|
||||
private readonly restApiDestroyOneHandler: RestApiDestroyOneHandler,
|
||||
private readonly restApiDestroyManyHandler: RestApiDestroyManyHandler,
|
||||
private readonly restApiDeleteOneHandler: RestApiDeleteOneHandler,
|
||||
private readonly restApiDeleteManyHandler: RestApiDeleteManyHandler,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
@@ -36,10 +43,6 @@ export class RestApiCoreService {
|
||||
);
|
||||
}
|
||||
|
||||
async delete(request: AuthenticatedRequest) {
|
||||
return await this.restApiDeleteOneHandler.handle(request);
|
||||
}
|
||||
|
||||
async createOne(request: AuthenticatedRequest) {
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
|
||||
@@ -60,14 +63,6 @@ export class RestApiCoreService {
|
||||
}
|
||||
}
|
||||
|
||||
async findDuplicates(request: AuthenticatedRequest) {
|
||||
return await this.restApiFindDuplicatesHandler.handle(request);
|
||||
}
|
||||
|
||||
async update(request: AuthenticatedRequest) {
|
||||
return await this.restApiUpdateOneHandler.handle(request);
|
||||
}
|
||||
|
||||
async get(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
@@ -98,4 +93,36 @@ export class RestApiCoreService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async findDuplicates(request: AuthenticatedRequest) {
|
||||
return await this.restApiFindDuplicatesHandler.handle(request);
|
||||
}
|
||||
|
||||
async update(request: AuthenticatedRequest) {
|
||||
return await this.restApiUpdateOneHandler.handle(request);
|
||||
}
|
||||
|
||||
async delete(request: AuthenticatedRequest) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
|
||||
const isCommonApiEnabled = await this.isCommonApiEnabled(request);
|
||||
const isSoftDelete = parseSoftDeleteRestRequest(request);
|
||||
|
||||
if (isCommonApiEnabled && !isSoftDelete && isDefined(recordId))
|
||||
return await this.restApiDestroyOneHandler.commonHandle(request);
|
||||
if (isCommonApiEnabled && !isSoftDelete && !isDefined(recordId))
|
||||
return await this.restApiDestroyManyHandler.handle(request);
|
||||
|
||||
if (isCommonApiEnabled && isSoftDelete && isDefined(recordId))
|
||||
return await this.restApiDeleteOneHandler.handle(request);
|
||||
if (isCommonApiEnabled && isSoftDelete && !isDefined(recordId))
|
||||
return await this.restApiDeleteManyHandler.handle(request);
|
||||
|
||||
if (!isCommonApiEnabled && !isSoftDelete && isDefined(recordId))
|
||||
return await this.restApiDestroyOneHandler.handle(request);
|
||||
|
||||
throw new BadRequestException(
|
||||
'Activate feature flag IS_COMMON_API_ENABLED to use Delete in the REST API',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { parseSoftDeleteRestRequest } from 'src/engine/api/rest/input-request-parsers/soft-delete-parser-utils/parse-soft-delete-rest-request.util';
|
||||
|
||||
describe('parseSoftDeleteRestRequest', () => {
|
||||
it('should return false when soft_delete query parameter is not defined', () => {
|
||||
const request = {
|
||||
query: {},
|
||||
} as any;
|
||||
|
||||
const result = parseSoftDeleteRestRequest(request);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when soft_delete query parameter is "true"', () => {
|
||||
const request = {
|
||||
query: {
|
||||
soft_delete: 'true',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = parseSoftDeleteRestRequest(request);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when soft_delete query parameter is "false"', () => {
|
||||
const request = {
|
||||
query: {
|
||||
soft_delete: 'false',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = parseSoftDeleteRestRequest(request);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when soft_delete query parameter is empty string', () => {
|
||||
const request = {
|
||||
query: {
|
||||
soft_delete: '',
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = parseSoftDeleteRestRequest(request);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when soft_delete query parameter is a boolean true', () => {
|
||||
const request = {
|
||||
query: {
|
||||
soft_delete: true,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = parseSoftDeleteRestRequest(request);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request';
|
||||
|
||||
export const parseSoftDeleteRestRequest = (
|
||||
request: AuthenticatedRequest,
|
||||
): boolean => {
|
||||
if (!isDefined(request.query.soft_delete)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return request.query.soft_delete === 'true';
|
||||
};
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
computeIdPathParameter,
|
||||
computeLimitParameters,
|
||||
computeOrderByParameters,
|
||||
computeSoftDeleteParameters,
|
||||
computeStartingAfterParameters,
|
||||
computeUpsertParameters,
|
||||
} from 'src/engine/core-modules/open-api/utils/parameters.utils';
|
||||
@@ -210,6 +211,7 @@ export const computeParameterComponents = (
|
||||
filter: computeFilterParameters(),
|
||||
depth: computeDepthParameters(),
|
||||
upsert: computeUpsertParameters(),
|
||||
softDelete: computeSoftDeleteParameters(),
|
||||
orderBy: computeOrderByParameters(),
|
||||
limit: computeLimitParameters(fromMetadata),
|
||||
};
|
||||
|
||||
@@ -75,6 +75,20 @@ export const computeUpsertParameters = (): OpenAPIV3_1.ParameterObject => {
|
||||
};
|
||||
};
|
||||
|
||||
export const computeSoftDeleteParameters = (): OpenAPIV3_1.ParameterObject => {
|
||||
return {
|
||||
name: 'soft_delete',
|
||||
in: 'query',
|
||||
description:
|
||||
'If true, soft deletes the objects. If false, objects are permanently deleted.',
|
||||
required: false,
|
||||
schema: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const computeFilterParameters = (): OpenAPIV3_1.ParameterObject => {
|
||||
return {
|
||||
name: 'filter',
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import {
|
||||
getCreateManyResponse201,
|
||||
getCreateOneResponse201,
|
||||
getDeleteManyResponse200,
|
||||
getDeleteResponse200,
|
||||
getFindDuplicatesResponse200,
|
||||
getFindManyResponse200,
|
||||
@@ -79,6 +80,20 @@ export const computeManyResultPath = (
|
||||
'401': { $ref: '#/components/responses/401' },
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
tags: [item.namePlural],
|
||||
summary: `Delete Many ${item.namePlural}`,
|
||||
operationId: `deleteMany${capitalize(item.namePlural)}`,
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/filter' },
|
||||
{ $ref: '#/components/parameters/softDelete' },
|
||||
],
|
||||
responses: {
|
||||
'200': getDeleteManyResponse200(item),
|
||||
'400': { $ref: '#/components/responses/400' },
|
||||
'401': { $ref: '#/components/responses/401' },
|
||||
},
|
||||
},
|
||||
} as OpenAPIV3_1.PathItemObject;
|
||||
};
|
||||
|
||||
@@ -105,7 +120,10 @@ export const computeSingleResultPath = (
|
||||
tags: [item.namePlural],
|
||||
summary: `Delete One ${item.nameSingular}`,
|
||||
operationId: `deleteOne${capitalize(item.nameSingular)}`,
|
||||
parameters: [{ $ref: '#/components/parameters/idPath' }],
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/idPath' },
|
||||
{ $ref: '#/components/parameters/softDelete' },
|
||||
],
|
||||
responses: {
|
||||
'200': getDeleteResponse200(item),
|
||||
'400': { $ref: '#/components/responses/400' },
|
||||
|
||||
@@ -174,6 +174,40 @@ export const getUpdateOneResponse200 = (
|
||||
};
|
||||
};
|
||||
|
||||
export const getDeleteManyResponse200 = (
|
||||
item: Pick<ObjectMetadataEntity, 'namePlural'>,
|
||||
) => {
|
||||
return {
|
||||
description: 'Successful operation',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
[`delete${capitalize(item.namePlural)}`]: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getDeleteResponse200 = (
|
||||
item: Pick<ObjectMetadataEntity, 'nameSingular'>,
|
||||
fromMetadata = false,
|
||||
|
||||
Reference in New Issue
Block a user