Common - many fixes (#15395)

This commit is contained in:
Etienne
2025-10-28 13:33:41 +01:00
committed by GitHub
parent 99d73d1d4b
commit ee6b86b1a8
30 changed files with 391 additions and 105 deletions
@@ -4,9 +4,9 @@ import { FieldMetadataType, ObjectsPermissions } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-selected-fields-result.type';
import { getAllSelectableFields } from 'src/engine/api/rest/core/rest-to-common-args-handlers/utils/get-all-selectable-fields.util';
import { MAX_DEPTH } from 'src/engine/api/rest/input-request-parsers/constants/max-depth.constant';
import { Depth } from 'src/engine/api/rest/input-request-parsers/types/depth.type';
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
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 { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
@@ -94,6 +94,8 @@ export abstract class CommonBaseQueryRunnerService<
);
}
await this.throttleQueryExecution(authContext.workspace.id);
await this.validate(args, queryRunnerContext);
if (objectMetadataItemWithFieldMaps.isSystem === true) {
@@ -115,8 +117,6 @@ export abstract class CommonBaseQueryRunnerService<
commonQueryParser,
);
await this.throttleQueryExecution(authContext.workspace.id);
const extendedQueryRunnerContext =
await this.prepareExtendedQueryRunnerContext(
authContext,
@@ -28,7 +28,7 @@ import { CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-s
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';
import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable-fields.utils';
import { getAllSelectableColumnNames } from 'src/engine/api/utils/get-all-selectable-column-names.utils';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
@@ -276,7 +276,7 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
repository.objectRecordsPermissions?.[objectMetadataItemWithFieldMaps.id]
?.restrictedFields;
const selectOptions = getAllSelectableFields({
const selectOptions = getAllSelectableColumnNames({
restrictedFields: restrictedFields ?? {},
objectMetadata: {
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
@@ -49,9 +49,20 @@ export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerServ
async computeArgs(
args: CommonInput<CreateOneQueryArgs>,
_queryRunnerContext: CommonBaseQueryRunnerContext,
queryRunnerContext: CommonBaseQueryRunnerContext,
): Promise<CommonInput<CreateOneQueryArgs>> {
return args;
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
return {
...args,
data: (
await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({
partialRecordInputs: [args.data],
authContext,
objectMetadataItemWithFieldMaps,
})
)[0],
};
}
async processQueryResult(
@@ -4,7 +4,7 @@ import isEmpty from 'lodash.isempty';
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
import { ObjectRecord, OrderByDirection } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { In } from 'typeorm';
import { FindOptionsRelations, In, ObjectLiteral } from 'typeorm';
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
@@ -43,6 +43,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
objectMetadataItemWithFieldMaps,
objectMetadataMaps,
commonQueryParser,
authContext,
workspaceDataSource,
rolePermissionConfig,
} = queryRunnerContext;
const existingRecordsQueryBuilder = repository.createQueryBuilder(
@@ -58,7 +61,7 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
objectMetadataMaps,
});
if (isDefined(args.ids)) {
if (isDefined(args.ids) && args.ids.length > 0) {
objectRecords = (await existingRecordsQueryBuilder
.where({ id: In(args.ids) })
.setFindOptions({
@@ -127,6 +130,26 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
}),
);
if (isDefined(args.selectedFieldsResult.relations)) {
await this.processNestedRelationsHelper.processNestedRelations({
objectMetadataMaps,
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
parentObjectRecords: findDuplicatesOutput.flatMap(
(item) => item.records,
),
parentObjectRecordsAggregatedValues: {},
relations: args.selectedFieldsResult.relations as Record<
string,
FindOptionsRelations<ObjectLiteral>
>,
limit: QUERY_MAX_RECORDS,
authContext,
workspaceDataSource,
rolePermissionConfig,
selectedFields: args.selectedFieldsResult.select,
});
}
return findDuplicatesOutput;
}
@@ -140,10 +140,6 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
isForwardPagination,
);
if (objectRecords.length > limit) {
objectRecords.pop();
}
if (!isForwardPagination) {
objectRecords.reverse();
}
@@ -58,9 +58,21 @@ export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerServ
async computeArgs(
args: CommonInput<UpdateOneQueryArgs>,
_queryRunnerContext: CommonBaseQueryRunnerContext,
queryRunnerContext: CommonBaseQueryRunnerContext,
): Promise<CommonInput<UpdateOneQueryArgs>> {
return args;
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
return {
...args,
data: (
await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({
partialRecordInputs: [args.data],
authContext,
objectMetadataItemWithFieldMaps,
shouldBackfillPositionIfUndefined: false,
})
)[0],
};
}
async processQueryResult(
@@ -11,4 +11,5 @@ export enum CommonQueryRunnerExceptionCode {
INVALID_ARGS_LAST = 'INVALID_ARGS_LAST',
UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT = 'UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT',
MISSING_SYSTEM_FIELD = 'MISSING_SYSTEM_FIELD',
INVALID_CURSOR = 'INVALID_CURSOR',
}
@@ -21,6 +21,7 @@ export const commonQueryRunnerToGraphqlApiExceptionHandler = (
case CommonQueryRunnerExceptionCode.INVALID_ARGS_LAST:
case CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT:
case CommonQueryRunnerExceptionCode.UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT:
case CommonQueryRunnerExceptionCode.INVALID_CURSOR:
throw new UserInputError(error);
case CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT:
throw new AuthenticationError(error);
@@ -20,6 +20,7 @@ export const commonQueryRunnerToRestApiExceptionHandler = (
case CommonQueryRunnerExceptionCode.INVALID_ARGS_LAST:
case CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT:
case CommonQueryRunnerExceptionCode.UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT:
case CommonQueryRunnerExceptionCode.INVALID_CURSOR:
throw new BadRequestException(error.message);
case CommonQueryRunnerExceptionCode.RECORD_NOT_FOUND:
throw new NotFoundException('Record not found');
@@ -14,12 +14,16 @@ export const getPageInfo = (
limit: number,
isForwardPagination: boolean,
): CommonPageInfo => {
const { hasNextPage, hasPreviousPage } = getPaginationInfo(
const { hasNextPage, hasPreviousPage, hasMoreRecords } = getPaginationInfo(
records,
limit,
isForwardPagination,
);
if (hasMoreRecords) {
records.pop();
}
const startCursor =
records.length > 0 ? encodeCursor(records[0], orderBy) : null;
const endCursor =