diff --git a/packages/twenty-client-sdk/scripts/generate-metadata-client.ts b/packages/twenty-client-sdk/scripts/generate-metadata-client.ts index dd32c773cc..3d0166f645 100644 --- a/packages/twenty-client-sdk/scripts/generate-metadata-client.ts +++ b/packages/twenty-client-sdk/scripts/generate-metadata-client.ts @@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { getIntrospectionQuery, buildClientSchema, printSchema } from 'graphql'; +import { buildClientSchema, getIntrospectionQuery, printSchema } from 'graphql'; import { generateMetadataClient } from '../src/generate/generate-metadata-client'; diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index e48ea18a56..d2e049d16a 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -1731,7 +1731,6 @@ enum FeatureFlagKey { IS_DRAFT_EMAIL_ENABLED IS_USAGE_ANALYTICS_ENABLED IS_RICH_TEXT_V1_MIGRATED - IS_DIRECT_GRAPHQL_EXECUTION_ENABLED IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED IS_CONNECTED_ACCOUNT_MIGRATED IS_RECORD_TABLE_WIDGET_ENABLED diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 9eb41a691f..3d2d71a5b2 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -1424,7 +1424,7 @@ export interface PublicFeatureFlag { __typename: 'PublicFeatureFlag' } -export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_DIRECT_GRAPHQL_EXECUTION_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED' +export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED' export interface ClientConfigMaintenanceMode { startAt: Scalars['DateTime'] @@ -9099,7 +9099,6 @@ export const enumFeatureFlagKey = { IS_DRAFT_EMAIL_ENABLED: 'IS_DRAFT_EMAIL_ENABLED' as const, IS_USAGE_ANALYTICS_ENABLED: 'IS_USAGE_ANALYTICS_ENABLED' as const, IS_RICH_TEXT_V1_MIGRATED: 'IS_RICH_TEXT_V1_MIGRATED' as const, - IS_DIRECT_GRAPHQL_EXECUTION_ENABLED: 'IS_DIRECT_GRAPHQL_EXECUTION_ENABLED' as const, IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED: 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' as const, IS_CONNECTED_ACCOUNT_MIGRATED: 'IS_CONNECTED_ACCOUNT_MIGRATED' as const, IS_RECORD_TABLE_WIDGET_ENABLED: 'IS_RECORD_TABLE_WIDGET_ENABLED' as const, diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 7795191da8..d82b144a65 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1724,7 +1724,6 @@ export enum FeatureFlagKey { IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED', IS_CONNECTED_ACCOUNT_MIGRATED = 'IS_CONNECTED_ACCOUNT_MIGRATED', IS_DATASOURCE_MIGRATED = 'IS_DATASOURCE_MIGRATED', - IS_DIRECT_GRAPHQL_EXECUTION_ENABLED = 'IS_DIRECT_GRAPHQL_EXECUTION_ENABLED', IS_DRAFT_EMAIL_ENABLED = 'IS_DRAFT_EMAIL_ENABLED', IS_EMAILING_DOMAIN_ENABLED = 'IS_EMAILING_DOMAIN_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/__tests__/constants/field-metadata-config-by-field-name.constant.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/__tests__/constants/field-metadata-config-by-field-name.constant.ts index 455a1a82f8..ba5432bfa2 100644 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/__tests__/constants/field-metadata-config-by-field-name.constant.ts +++ b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/__tests__/constants/field-metadata-config-by-field-name.constant.ts @@ -49,8 +49,17 @@ export const fieldMetadataConfigByFieldName: Record< { value: 'OPTION_2' }, ] as FieldMetadataDefaultOption[], }, + manyToOneRelationField: { + name: 'manyToOneRelationField', + type: FieldMetadataType.RELATION, + isNullable: true, + settings: { + relationType: RelationType.MANY_TO_ONE, + joinColumnName: 'manyToOneRelationFieldId', + }, + }, manyToOneRelationFieldId: { - name: 'manyToOneRelationFieldId', + name: 'manyToOneRelationField', type: FieldMetadataType.RELATION, isNullable: true, settings: { diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/__tests__/filter-arg-processor.service.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/__tests__/filter-arg-processor.service.spec.ts index ab82480b90..d83899f487 100644 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/__tests__/filter-arg-processor.service.spec.ts +++ b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/__tests__/filter-arg-processor.service.spec.ts @@ -32,7 +32,7 @@ describe('FilterArgProcessorService', () => { byUniversalIdentifier[universalId] = { id: fieldId, - name: fieldName, + name: config.name, type: config.type ?? FieldMetadataType.TEXT, isNullable: config.isNullable ?? true, objectMetadataId: 'object-id', diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg-processor.service.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg-processor.service.ts index 122952907d..39735e1a8f 100644 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg-processor.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg-processor.service.ts @@ -4,6 +4,7 @@ import { msg } from '@lingui/core/macro'; import { compositeTypeDefinitions, FieldMetadataType, + RelationType, } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; @@ -16,11 +17,13 @@ import { } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/composite-field-metadata-type.type'; +import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util'; +import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; @Injectable() @@ -109,7 +112,9 @@ export class FilterArgProcessorService { fieldIdByName: Record, fieldIdByJoinColumnName: Record, ): Record { - const fieldMetadataId = fieldIdByName[key] || fieldIdByJoinColumnName[key]; + const resolvedByName = fieldIdByName[key]; + const resolvedByJoinColumn = fieldIdByJoinColumnName[key]; + const fieldMetadataId = resolvedByName ?? resolvedByJoinColumn; if (!isDefined(fieldMetadataId)) { const nameSingular = flatObjectMetadata.nameSingular; @@ -138,6 +143,38 @@ export class FilterArgProcessorService { ); } + if ( + isDefined(resolvedByName) && + !isDefined(resolvedByJoinColumn) && + (isFlatFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION) || + isFlatFieldMetadataOfType( + fieldMetadata, + FieldMetadataType.MORPH_RELATION, + )) + ) { + if (fieldMetadata.settings?.relationType === RelationType.MANY_TO_ONE) { + const joinColumnName = computeMorphOrRelationFieldJoinColumnName({ + name: key, + }); + + throw new CommonQueryRunnerException( + `Cannot filter by relation field "${key}": use "${joinColumnName}" instead`, + CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER, + { + userFriendlyMessage: msg`Invalid filter: use "${joinColumnName}" to filter by this relation field`, + }, + ); + } + + throw new CommonQueryRunnerException( + `Cannot filter by relation field "${key}"`, + CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER, + { + userFriendlyMessage: msg`Invalid filter: filtering by relation field "${key}" is not supported`, + }, + ); + } + if (isCompositeFieldMetadataType(fieldMetadata.type)) { return this.validateAndTransformCompositeFieldFilter( fieldMetadata, diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-and-transform-value-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-and-transform-value-or-throw.util.ts index 721ea24b33..07852db9f2 100644 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-and-transform-value-or-throw.util.ts +++ b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-and-transform-value-or-throw.util.ts @@ -6,6 +6,7 @@ import { validateAndTransformValueByFieldType } from './validate-and-transform-v import { validateArrayOperatorValueOrThrow } from './validate-array-operator-value-or-throw.util'; import { validateIsEmptyArrayOperatorValueOrThrow } from './validate-is-empty-array-operator-value-or-throw.util'; import { validateIsOperatorFilterValueOrThrow } from './validate-is-operator-filter-value-or-throw.util'; +import { validateStringOperatorValueOrThrow } from './validate-string-operator-value-or-throw.util'; export const validateAndTransformValueOrThrow = ( operator: FilterOperator, @@ -50,6 +51,15 @@ export const validateAndTransformValueOrThrow = ( fieldName, ); + case 'like': + case 'ilike': + case 'startsWith': + case 'endsWith': + case 'containsIlike': + validateStringOperatorValueOrThrow(value, operator, fieldName); + + return value; + default: return value; } diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-string-operator-value-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-string-operator-value-or-throw.util.ts new file mode 100644 index 0000000000..f6c6c68844 --- /dev/null +++ b/packages/twenty-server/src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-string-operator-value-or-throw.util.ts @@ -0,0 +1,24 @@ +import { msg } from '@lingui/core/macro'; +import { isString } from '@sniptt/guards'; + +import { type FilterOperator } from 'src/engine/api/common/common-args-processors/filter-arg-processor/types/filter-operator.type'; +import { + CommonQueryRunnerException, + CommonQueryRunnerExceptionCode, +} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; + +export const validateStringOperatorValueOrThrow = ( + value: unknown, + operator: FilterOperator, + fieldName: string, +): void => { + if (!isString(value)) { + throw new CommonQueryRunnerException( + `Filter operator "${operator}" requires a string value for field "${fieldName}", got ${typeof value}`, + CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER, + { + userFriendlyMessage: msg`Invalid filter: "${operator}" operator requires a String`, + }, + ); + } +}; diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts index d2cc9d8b29..d17ed520c7 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts @@ -25,7 +25,10 @@ import { 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 { + CommonQueryExecutionResult, + CommonQueryResult, +} from 'src/engine/api/common/types/common-query-result.type'; import { CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-selected-fields-result.type'; 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'; @@ -98,7 +101,7 @@ export abstract class CommonBaseQueryRunnerService< public async execute( args: CommonInput, queryRunnerContext: CommonBaseQueryRunnerContext, - ): Promise { + ): Promise> { const { authContext, flatObjectMetadata, @@ -127,26 +130,32 @@ export abstract class CommonBaseQueryRunnerService< args.selectedFields, ); - this.validateQueryComplexity( - selectedFieldsResult, - args, - queryRunnerContext, - ); - const processedArgs = { ...(await this.processArgs(args, queryRunnerContext, this.operationName)), selectedFieldsResult, } as CommonExtendedInput; - return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - async () => - this.executeQueryAndEnrichResults( - processedArgs, - queryRunnerContext, - commonQueryParser, - ), - authContext, + this.validateQueryComplexity( + selectedFieldsResult, + processedArgs, + queryRunnerContext, ); + + const results = + await this.globalWorkspaceOrmManager.executeInWorkspaceContext( + async () => + this.executeQueryAndEnrichResults( + processedArgs, + queryRunnerContext, + commonQueryParser, + ), + authContext, + ); + + return { + results, + args: processedArgs, + }; } protected abstract run( @@ -174,7 +183,7 @@ export abstract class CommonBaseQueryRunnerService< protected computeQueryComplexity( selectedFieldsResult: CommonSelectedFieldsResult, - _args: CommonInput, + _args: CommonExtendedInput, _queryRunnerContext: CommonBaseQueryRunnerContext, ): number { const simpleFieldsComplexity = 1; @@ -387,7 +396,7 @@ export abstract class CommonBaseQueryRunnerService< private validateQueryComplexity( selectedFieldsResult: CommonSelectedFieldsResult, - args: CommonInput, + args: CommonExtendedInput, queryRunnerContext: CommonBaseQueryRunnerContext, ) { const maximumComplexity = this.twentyConfigService.get( diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-many-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-many-query-runner.service.ts index c16a902622..41f97c6f7d 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-many-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-many-query-runner.service.ts @@ -318,7 +318,7 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi protected override computeQueryComplexity( selectedFieldsResult: CommonSelectedFieldsResult, - args: CommonInput, + args: CommonExtendedInput, queryRunnerContext: CommonBaseQueryRunnerContext, ): number { const baseComplexity = super.computeQueryComplexity( diff --git a/packages/twenty-server/src/engine/api/common/types/common-query-result.type.ts b/packages/twenty-server/src/engine/api/common/types/common-query-result.type.ts index 310add8f9b..53b1997144 100644 --- a/packages/twenty-server/src/engine/api/common/types/common-query-result.type.ts +++ b/packages/twenty-server/src/engine/api/common/types/common-query-result.type.ts @@ -3,6 +3,10 @@ import { type ObjectRecord } from 'twenty-shared/types'; import { type CommonFindDuplicatesOutputItem } from 'src/engine/api/common/types/common-find-duplicates-output-item.type'; 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'; +import { + CommonExtendedInput, + CommonQueryArgs, +} from 'src/engine/api/common/types/common-query-args.type'; export type CommonQueryResult = | ObjectRecord[] @@ -10,3 +14,11 @@ export type CommonQueryResult = | CommonGroupByOutputItem[] | CommonFindManyOutput | CommonFindDuplicatesOutputItem[]; + +export type CommonQueryExecutionResult< + Output extends CommonQueryResult, + Args extends CommonQueryArgs, +> = { + results: Output; + args: CommonExtendedInput; +}; diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/direct-execution.service.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/direct-execution.service.ts index 63e7588277..947cee8b81 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/direct-execution.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/direct-execution.service.ts @@ -203,74 +203,73 @@ export class DirectExecutionService { const variables = req.body.variables ?? {}; const data: Record = {}; - const { graphQLResolverNameMap } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'graphQLResolverNameMap', - ]); - const { + graphQLResolverNameMap, flatObjectMetadataMaps, flatFieldMetadataMaps, - objectIdByNameSingular, - } = await this.loadWorkspaceMetadata(workspaceId); + } = await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'graphQLResolverNameMap', + 'flatObjectMetadataMaps', + 'flatFieldMetadataMaps', + ]); - const results = await Promise.allSettled( + const { idByNameSingular: objectIdByNameSingular } = + buildObjectIdByNameMaps(flatObjectMetadataMaps); + + const errors: GraphQLFormattedError[] = []; + + await Promise.all( topLevelFields.map(async (field) => { const entry = graphQLResolverNameMap[field.name.value]; const responseKey = field.alias?.value ?? field.name.value; - const args = extractArgumentsFromAst(field.arguments, variables); + try { + const args = extractArgumentsFromAst(field.arguments, variables); - const graphqlPartialResolveInfo = graphQLBuildPartialResolveInfo( - field, - fragmentMap, - ); - - const workspaceSchemaBuilderContext = - buildWorkspaceSchemaBuilderContext( - entry, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, + const graphqlPartialResolveInfo = graphQLBuildPartialResolveInfo( + field, + fragmentMap, ); - const result = (await this.executeField({ - entry, - args, - graphqlPartialResolveInfo, - workspaceSchemaBuilderContext, - })) as ResolverOutput; + const workspaceSchemaBuilderContext = + buildWorkspaceSchemaBuilderContext( + entry, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + ); - const formattedResult = graphQLFormatResultFromSelectedFields( - result, - graphqlFields( - graphqlPartialResolveInfo as GraphQLResolveInfo, - {}, - { excludedFields: [] }, - ), - workspaceSchemaBuilderContext.flatObjectMetadata.nameSingular, - { - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - method: entry.method, - }, - ); + const result = (await this.executeField({ + entry, + args, + graphqlPartialResolveInfo, + workspaceSchemaBuilderContext, + })) as ResolverOutput; - return { responseKey, result: formattedResult }; + const formattedResult = graphQLFormatResultFromSelectedFields( + result, + graphqlFields( + graphqlPartialResolveInfo as GraphQLResolveInfo, + {}, + { excludedFields: [] }, + ), + workspaceSchemaBuilderContext.flatObjectMetadata.nameSingular, + { + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + method: entry.method, + }, + ); + + data[responseKey] = formattedResult; + } catch (error) { + data[responseKey] = null; + errors.push(this.formatError(error, req)); + } }), ); - const errors: GraphQLFormattedError[] = []; - - for (const settled of results) { - if (settled.status === 'fulfilled') { - data[settled.value.responseKey] = settled.value.result; - } else { - errors.push(this.formatError(settled.reason, req)); - } - } - if (errors.length > 0) { return { data, errors }; } @@ -440,24 +439,4 @@ export class DirectExecutionService { seen.add(name); } } - - private async loadWorkspaceMetadata(workspaceId: string) { - const { flatObjectMetadataMaps, flatFieldMetadataMaps } = - await this.workspaceFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( - { - workspaceId, - flatMapsKeys: ['flatObjectMetadataMaps', 'flatFieldMetadataMaps'], - }, - ); - - const { idByNameSingular } = buildObjectIdByNameMaps( - flatObjectMetadataMaps, - ); - - return { - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular: idByNameSingular, - }; - } } diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/hooks/use-direct-execution.hook.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/hooks/use-direct-execution.hook.ts index 651b779f3c..68a3437b45 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/hooks/use-direct-execution.hook.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/hooks/use-direct-execution.hook.ts @@ -1,7 +1,6 @@ import { type Request } from 'express'; import { DocumentNode, parse } from 'graphql'; import { type Plugin } from 'graphql-yoga'; -import { FeatureFlagKey } from 'twenty-shared/types'; import { isNull } from '@sniptt/guards'; import { type DirectExecutionService } from 'src/engine/api/graphql/direct-execution/direct-execution.service'; @@ -26,16 +25,6 @@ export function useDirectExecution( return; } - const isDirectExecutionEnabled = - await config.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_DIRECT_GRAPHQL_EXECUTION_ENABLED, - req.workspace.id, - ); - - if (!isDirectExecutionEnabled) { - return; - } - const queryString = req.body.query as string; const operationName = req.body.operationName as string | undefined; diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-delete-many-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-delete-many-args.util.ts index a28a18d51a..8ef9f88e2e 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-delete-many-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-delete-many-args.util.ts @@ -1,10 +1,10 @@ import { isObject } from 'class-validator'; +import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { GraphqlDirectExecutionException, GraphqlDirectExecutionExceptionCode, } from 'src/engine/api/graphql/direct-execution/errors/graphql-direct-execution.exception'; -import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { type DeleteManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; export function assertDeleteManyArgs( diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-destroy-many-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-destroy-many-args.util.ts index 9b0ddf073a..6a3a861923 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-destroy-many-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-destroy-many-args.util.ts @@ -1,10 +1,10 @@ import { isObject } from 'class-validator'; +import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { GraphqlDirectExecutionException, GraphqlDirectExecutionExceptionCode, } from 'src/engine/api/graphql/direct-execution/errors/graphql-direct-execution.exception'; -import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { type DestroyManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; export function assertDestroyManyArgs( diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-many-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-many-args.util.ts index 50beae3ac7..cbdb983862 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-many-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-many-args.util.ts @@ -1,6 +1,6 @@ import { isArray, isNumber, isObject, isString } from 'class-validator'; -import { isDefined } from 'twenty-shared/utils'; +import { isDefined, isEmptyObject } from 'twenty-shared/utils'; import { GraphqlDirectExecutionException, @@ -66,7 +66,13 @@ export function assertFindManyArgs( ); } - if ('orderBy' in args && isDefined(args.orderBy) && !isArray(args.orderBy)) { + if ( + 'orderBy' in args && + isDefined(args.orderBy) && + !isEmptyObject(args.orderBy) && + !isArray(args.orderBy) && + !isObject(args.orderBy) + ) { throw new GraphqlDirectExecutionException( 'Invalid argument: "orderBy" must be an array', GraphqlDirectExecutionExceptionCode.INVALID_QUERY_INPUT, diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-one-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-one-args.util.ts index 24a2ad19f9..475633e029 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-one-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-find-one-args.util.ts @@ -2,11 +2,11 @@ import { isObject } from 'class-validator'; import { isDefined } from 'twenty-shared/utils'; +import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { GraphqlDirectExecutionException, GraphqlDirectExecutionExceptionCode, } from 'src/engine/api/graphql/direct-execution/errors/graphql-direct-execution.exception'; -import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { type FindOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; export function assertFindOneArgs( diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-group-by-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-group-by-args.util.ts index 220f474eb0..a06d502ce8 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-group-by-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-group-by-args.util.ts @@ -6,7 +6,7 @@ import { isString, } from 'class-validator'; -import { isDefined } from 'twenty-shared/utils'; +import { isDefined, isEmptyObject } from 'twenty-shared/utils'; import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { @@ -68,7 +68,13 @@ export function assertGroupByArgs( ); } - if ('orderBy' in args && isDefined(args.orderBy) && !isArray(args.orderBy)) { + if ( + 'orderBy' in args && + isDefined(args.orderBy) && + !isEmptyObject(args.orderBy) && + !isArray(args.orderBy) && + !isObject(args.orderBy) + ) { throw new GraphqlDirectExecutionException( 'Invalid argument: "orderBy" must be an array', GraphqlDirectExecutionExceptionCode.INVALID_QUERY_INPUT, @@ -79,7 +85,9 @@ export function assertGroupByArgs( if ( 'orderByForRecords' in args && isDefined(args.orderByForRecords) && - !isArray(args.orderByForRecords) + !isEmptyObject(args.orderByForRecords) && + !isArray(args.orderByForRecords) && + !isObject(args.orderByForRecords) ) { throw new GraphqlDirectExecutionException( 'Invalid argument: "orderByForRecords" must be an array', diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-restore-many-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-restore-many-args.util.ts index 2836f3b9e5..82adaf4383 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-restore-many-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-restore-many-args.util.ts @@ -1,10 +1,10 @@ import { isObject } from 'class-validator'; +import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { GraphqlDirectExecutionException, GraphqlDirectExecutionExceptionCode, } from 'src/engine/api/graphql/direct-execution/errors/graphql-direct-execution.exception'; -import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { type RestoreManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; export function assertRestoreManyArgs( diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-update-many-args.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-update-many-args.util.ts index 2f0c7395a2..8a8ca9a9ea 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-update-many-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/assert-update-many-args.util.ts @@ -1,10 +1,10 @@ import { isObject } from 'class-validator'; +import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { GraphqlDirectExecutionException, GraphqlDirectExecutionExceptionCode, } from 'src/engine/api/graphql/direct-execution/errors/graphql-direct-execution.exception'; -import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { type UpdateManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; export function assertUpdateManyArgs( diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/extract-arguments-from-ast.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/extract-arguments-from-ast.util.ts index 018f93b499..07bb483b0d 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/extract-arguments-from-ast.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/extract-arguments-from-ast.util.ts @@ -1,5 +1,5 @@ import { type ArgumentNode, valueFromASTUntyped } from 'graphql'; -import { isDefined, isEmptyObject } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; // Converts GraphQL AST argument nodes into a plain JS object, // resolving variable references from the variables map. @@ -15,8 +15,9 @@ export const extractArgumentsFromAst = ( for (const arg of argumentNodes) { const value = valueFromASTUntyped(arg.value, variables); - if (!isDefined(value) || isEmptyObject(value)) continue; - result[arg.name.value] = valueFromASTUntyped(arg.value, variables); + + if (!isDefined(value)) continue; + result[arg.name.value] = value; } return result; diff --git a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/graphql-format-result-from-selected-fields.util.ts b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/graphql-format-result-from-selected-fields.util.ts index 3e66663db2..98ec4c7203 100644 --- a/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/graphql-format-result-from-selected-fields.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/direct-execution/utils/graphql-format-result-from-selected-fields.util.ts @@ -1,5 +1,9 @@ -import { isNull } from '@sniptt/guards'; -import { ObjectRecord, RelationType } from 'twenty-shared/types'; +import { isNonEmptyString, isNull } from '@sniptt/guards'; +import { + FieldMetadataType, + ObjectRecord, + RelationType, +} from 'twenty-shared/types'; import { STANDARD_ERROR_MESSAGE } from 'src/engine/api/common/common-query-runners/errors/standard-error-message.constant'; import { @@ -181,6 +185,16 @@ const backfillNullValuesAndComputeTypeNameForObjectRecord = ( continue; } + if ( + isDefined(fieldMetadata) && + fieldMetadata.type === FieldMetadataType.NUMBER && + isNonEmptyString(value) && + isFinite(Number(value)) + ) { + formatted[key] = Number(value); + continue; + } + formatted[key] = value; } @@ -275,9 +289,13 @@ const backfillNullValuesAndComputeTypeNameForConnection = ( continue; } - //aggregate fields - formatted[key] = + const rawAggregateValue = (connection as unknown as Record)[key] ?? null; + + formatted[key] = + isNonEmptyString(rawAggregateValue) && isFinite(Number(rawAggregateValue)) + ? Number(rawAggregateValue) + : rawAggregateValue; } return formatted; diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts index b54c35a777..1b20a76072 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-config/graphql-config.service.ts @@ -1,4 +1,4 @@ -import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; import { ContextIdFactory, ModuleRef } from '@nestjs/core'; import { type GqlOptionsFactory } from '@nestjs/graphql'; @@ -31,6 +31,7 @@ import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hook import { useValidateGraphqlQueryComplexity } from 'src/engine/core-modules/graphql/hooks/use-validate-graphql-query-complexity.hook'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; +import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type'; import { DataloaderService } from 'src/engine/dataloaders/dataloader.service'; @@ -46,6 +47,8 @@ export interface GraphQLContext extends YogaDriverServerContext<'express'> { export class GraphQLConfigService implements GqlOptionsFactory> { + private readonly logger = new Logger(GraphQLConfigService.name); + constructor( private readonly exceptionHandlerService: ExceptionHandlerService, private readonly twentyConfigService: TwentyConfigService, @@ -102,6 +105,10 @@ export class GraphQLConfigService return new GraphQLSchema({}); } + this.logger.log( + `Creating schema for workspace ${workspace.id} for request ${context?.req?.body?.operationName}`, + ); + return await this.createSchema(context, workspace, application?.id); } catch (error) { if (error instanceof UnauthorizedException) { @@ -188,6 +195,11 @@ export class GraphQLConfigService }, ); + await this.metricsService.incrementCounter({ + key: MetricsKeys.GraphqlSchemaBuild, + shouldStoreInCache: false, + }); + return await workspaceFactory.createGraphQLSchema(workspace, applicationId); } } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts index 61a1f05113..bab8cc1408 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts @@ -8,6 +8,7 @@ import { type ObjectRecordOrderBy, } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; +import { type GroupByField } from 'src/engine/api/common/common-query-runners/types/group-by-field.types'; import { GraphqlQueryFilterConditionParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser'; import { GraphqlQueryOrderGroupByParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/graphql-query-order-group-by.parser'; import { @@ -19,7 +20,6 @@ import { GraphqlQuerySelectedFieldsParser, type GraphqlQuerySelectedFieldsResult, } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields.parser'; -import { type GroupByField } from 'src/engine/api/common/common-query-runners/types/group-by-field.types'; import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts index 4953094cf2..2e686d37f9 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts @@ -39,10 +39,11 @@ export class CreateManyResolverFactory }); try { - const records = await this.commonCreateManyQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: records } = + await this.commonCreateManyQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts index 8f7986a7ac..7c0207a941 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts @@ -38,10 +38,11 @@ export class CreateOneResolverFactory }); try { - const record = await this.commonCreateOneQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonCreateOneQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts index 9328cdbe31..b2ea40b695 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts @@ -39,10 +39,11 @@ export class DeleteManyResolverFactory }); try { - const records = await this.commonDeleteManyQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: records } = + await this.commonDeleteManyQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts index 66fa9ab811..bfbc53cb11 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts @@ -38,10 +38,11 @@ export class DeleteOneResolverFactory }); try { - const record = await this.commonDeleteOneQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonDeleteOneQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts index bb622bd546..800d562c39 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts @@ -39,10 +39,11 @@ export class DestroyManyResolverFactory }); try { - const records = await this.commonDestroyManyQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: records } = + await this.commonDestroyManyQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts index 1d090f0a9a..f3967d7329 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts @@ -38,10 +38,11 @@ export class DestroyOneResolverFactory }); try { - const record = await this.commonDestroyOneQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonDestroyOneQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts index abd41c9a4c..07f99bdceb 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts @@ -39,7 +39,7 @@ export class FindDuplicatesResolverFactory }); try { - const paginatedDuplicates = + const { results: paginatedDuplicates } = await this.commonFindDuplicatesQueryRunnerService.execute( { ...args, selectedFields }, resolverContext, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts index 5e188e1b8f..eaf5fe07bd 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts @@ -40,11 +40,14 @@ export class FindManyResolverFactory try { const { - records, - aggregatedValues, - totalCount, - pageInfo, - selectedFieldsResult, + results: { + records, + aggregatedValues, + totalCount, + pageInfo, + selectedFieldsResult, + }, + args: processedArgs, } = await this.commonFindManyQueryRunnerService.execute( { ...args, selectedFields }, resolverContext, @@ -64,7 +67,7 @@ export class FindManyResolverFactory objectName: resolverContext.flatObjectMetadata.nameSingular, take: args.first ?? args.last ?? QUERY_MAX_RECORDS, totalCount, - order: args.orderBy, + order: processedArgs.orderBy, hasNextPage: pageInfo.hasNextPage, hasPreviousPage: pageInfo.hasPreviousPage, }); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts index 0dd83863bc..37c87e365e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts @@ -38,10 +38,11 @@ export class FindOneResolverFactory workspaceSchemaBuilderContext: internalContext, }); - const record = await this.commonFindOneQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonFindOneQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/group-by-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/group-by-resolver.factory.ts index 4de25758be..87366e68f4 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/group-by-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/group-by-resolver.factory.ts @@ -50,7 +50,7 @@ export class GroupByResolverFactory resolverContext.objectIdByNameSingular, ); - const results = await this.commonGroupByQueryRunnerService.execute( + const { results } = await this.commonGroupByQueryRunnerService.execute( { ...args, selectedFields, includeRecords: shouldIncludeRecords }, resolverContext, ); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/merge-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/merge-many-resolver.factory.ts index 3d22a30b6d..d21f0a649c 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/merge-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/merge-many-resolver.factory.ts @@ -38,10 +38,11 @@ export class MergeManyResolverFactory }); try { - const record = await this.commonMergeManyQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonMergeManyQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts index e1773de6cd..6c1a4c19e7 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts @@ -39,10 +39,11 @@ export class RestoreManyResolverFactory }); try { - const records = await this.commonRestoreManyQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: records } = + await this.commonRestoreManyQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts index 2a9518efd0..080fbfa016 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts @@ -38,10 +38,11 @@ export class RestoreOneResolverFactory }); try { - const record = await this.commonRestoreOneQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonRestoreOneQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts index bb49e72ee4..ab7ee61f50 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts @@ -39,10 +39,11 @@ export class UpdateManyResolverFactory }); try { - const records = await this.commonUpdateManyQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: records } = + await this.commonUpdateManyQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts index 06bb758470..d9060c2a04 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts @@ -38,10 +38,11 @@ export class UpdateOneResolverFactory }); try { - const record = await this.commonUpdateOneQueryRunnerService.execute( - { ...args, selectedFields }, - resolverContext, - ); + const { results: record } = + await this.commonUpdateOneQueryRunnerService.execute( + { ...args, selectedFields }, + resolverContext, + ); const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts index 84a110a4da..540fa1c7d2 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts @@ -9,8 +9,6 @@ import { ScalarsExplorerService } from 'src/engine/api/graphql/services/scalars- import { WorkspaceGraphqlSchemaSDLService } from 'src/engine/api/graphql/workspace-graphql-schema-sdl/workspace-graphql-schema-sdl.service'; import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/factories/factories'; import { WorkspaceResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory'; -import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; -import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type'; import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util'; @@ -20,7 +18,6 @@ export class WorkspaceSchemaFactory { private readonly scalarsExplorerService: ScalarsExplorerService, private readonly workspaceResolverFactory: WorkspaceResolverFactory, private readonly workspaceGraphqlSchemaSDLService: WorkspaceGraphqlSchemaSDLService, - private readonly metricsService: MetricsService, ) {} async createGraphQLSchema( @@ -67,11 +64,6 @@ export class WorkspaceSchemaFactory { }, }); - await this.metricsService.incrementCounter({ - key: MetricsKeys.GraphqlSchemaBuild, - shouldStoreInCache: false, - }); - return executableSchema; } } diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-many.handler.ts index 61b40ae6c4..c07305b3c6 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-many.handler.ts @@ -37,16 +37,17 @@ export class RestApiCreateManyHandler extends RestApiBaseHandler { authContext, }); - const records = await this.commonCreateManyQueryRunnerService.execute( - { data, selectedFields, upsert }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: records } = + await this.commonCreateManyQueryRunnerService.execute( + { data, selectedFields, upsert }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(records, flatObjectMetadata.namePlural); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-one.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-one.handler.ts index 97f4b67de7..8888e9f61d 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-one.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-create-one.handler.ts @@ -38,16 +38,17 @@ export class RestApiCreateOneHandler extends RestApiBaseHandler { authContext, }); - const record = await this.commonCreateOneQueryRunnerService.execute( - { data, selectedFields, upsert }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonCreateOneQueryRunnerService.execute( + { data, selectedFields, upsert }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-many.handler.ts index e7fff3d16a..f0351ef45a 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-many.handler.ts @@ -40,16 +40,17 @@ export class RestApiDeleteManyHandler extends RestApiBaseHandler { } = await this.buildCommonOptions(request); try { - const records = await this.commonDeleteManyQueryRunnerService.execute( - { filter, selectedFields: { id: true } }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: records } = + await this.commonDeleteManyQueryRunnerService.execute( + { filter, selectedFields: { id: true } }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(records, flatObjectMetadata.namePlural); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-one.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-one.handler.ts index 9f85b317e9..7356586fbe 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-one.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-delete-one.handler.ts @@ -29,16 +29,17 @@ export class RestApiDeleteOneHandler extends RestApiBaseHandler { objectIdByNameSingular, } = await this.buildCommonOptions(request); - const record = await this.commonDeleteOneQueryRunnerService.execute( - { id, selectedFields: { id: true } }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonDeleteOneQueryRunnerService.execute( + { id, selectedFields: { id: true } }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-many.handler.ts index 5e67558837..90186b6fad 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-many.handler.ts @@ -40,16 +40,17 @@ export class RestApiDestroyManyHandler extends RestApiBaseHandler { } = await this.buildCommonOptions(request); try { - const records = await this.commonDestroyManyQueryRunnerService.execute( - { filter, selectedFields: { id: true } }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: records } = + await this.commonDestroyManyQueryRunnerService.execute( + { filter, selectedFields: { id: true } }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(records, flatObjectMetadata.namePlural); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-one.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-one.handler.ts index 572b95e65d..8c3f0d576d 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-one.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-destroy-one.handler.ts @@ -29,16 +29,17 @@ export class RestApiDestroyOneHandler extends RestApiBaseHandler { objectIdByNameSingular, } = await this.buildCommonOptions(request); - const record = await this.commonDestroyOneQueryRunnerService.execute( - { id, selectedFields: { id: true } }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonDestroyOneQueryRunnerService.execute( + { id, selectedFields: { id: true } }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-duplicates.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-duplicates.handler.ts index ba1e29a308..e6f3f7607c 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-duplicates.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-duplicates.handler.ts @@ -36,7 +36,7 @@ export class RestApiFindDuplicatesHandler extends RestApiBaseHandler { authContext, }); - const duplicateConnections = + const { results: duplicateConnections } = await this.commonFindDuplicatesQueryRunnerService.execute( { data, ids, selectedFields }, { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-many.handler.ts index 6d6e9909e4..99ed1a25db 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-many.handler.ts @@ -43,20 +43,21 @@ export class RestApiFindManyHandler extends RestApiBaseHandler { authContext, }); - const { records, aggregatedValues, pageInfo } = - await this.commonFindManyQueryRunnerService.execute( - { - ...parsedArgs, - selectedFields: { ...selectedFields, totalCount: true }, - }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { + results: { records, aggregatedValues, pageInfo }, + } = await this.commonFindManyQueryRunnerService.execute( + { + ...parsedArgs, + selectedFields: { ...selectedFields, totalCount: true }, + }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse( records, diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-one.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-one.handler.ts index 6f5cabdfe1..358019a5f3 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-one.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-find-one.handler.ts @@ -36,16 +36,17 @@ export class RestApiFindOneHandler extends RestApiBaseHandler { authContext, }); - const record = await this.commonFindOneQueryRunnerService.execute( - { filter, selectedFields }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonFindOneQueryRunnerService.execute( + { filter, selectedFields }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-group-by.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-group-by.handler.ts index 0848998636..bca845d767 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-group-by.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-group-by.handler.ts @@ -41,7 +41,7 @@ export class RestApiGroupByHandler extends RestApiBaseHandler { limit, } = await this.parseRequestArgs(request); - return await this.commonGroupByQueryRunnerService.execute( + const { results } = await this.commonGroupByQueryRunnerService.execute( { filter, orderBy, @@ -60,6 +60,8 @@ export class RestApiGroupByHandler extends RestApiBaseHandler { objectIdByNameSingular, }, ); + + return results; } catch (error) { return workspaceQueryRunnerRestApiExceptionHandler(error); } diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-merge-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-merge-many.handler.ts index 34689a286b..ea555b85fd 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-merge-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-merge-many.handler.ts @@ -36,16 +36,17 @@ export class RestApiMergeManyHandler extends RestApiBaseHandler { authContext, }); - const record = await this.commonMergeManyQueryRunnerService.execute( - { ...restArgs, selectedFields }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonMergeManyQueryRunnerService.execute( + { ...restArgs, selectedFields }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-many.handler.ts index c9788916c7..fcce729674 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-many.handler.ts @@ -42,16 +42,17 @@ export class RestApiRestoreManyHandler extends RestApiBaseHandler { authContext, }); - const records = await this.commonRestoreManyQueryRunnerService.execute( - { filter, selectedFields }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: records } = + await this.commonRestoreManyQueryRunnerService.execute( + { filter, selectedFields }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(records, flatObjectMetadata.namePlural); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-one.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-one.handler.ts index 575dab82d6..81c4c4bb19 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-one.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-restore-one.handler.ts @@ -38,16 +38,17 @@ export class RestApiRestoreOneHandler extends RestApiBaseHandler { authContext, }); - const record = await this.commonRestoreOneQueryRunnerService.execute( - { id, selectedFields }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonRestoreOneQueryRunnerService.execute( + { id, selectedFields }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-many.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-many.handler.ts index 28f081a386..d9a1b69607 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-many.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-many.handler.ts @@ -38,16 +38,17 @@ export class RestApiUpdateManyHandler extends RestApiBaseHandler { authContext, }); - const records = await this.commonUpdateManyQueryRunnerService.execute( - { data, filter, selectedFields }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: records } = + await this.commonUpdateManyQueryRunnerService.execute( + { data, filter, selectedFields }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(records, flatObjectMetadata.namePlural); } catch (error) { diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-one.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-one.handler.ts index efb138b04c..e616272384 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-one.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-update-one.handler.ts @@ -38,16 +38,17 @@ export class RestApiUpdateOneHandler extends RestApiBaseHandler { authContext, }); - const record = await this.commonUpdateOneQueryRunnerService.execute( - { id, data, selectedFields }, - { - authContext, - flatObjectMetadata, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - objectIdByNameSingular, - }, - ); + const { results: record } = + await this.commonUpdateOneQueryRunnerService.execute( + { id, data, selectedFields }, + { + authContext, + flatObjectMetadata, + flatObjectMetadataMaps, + flatFieldMetadataMaps, + objectIdByNameSingular, + }, + ); return this.formatRestResponse(record, flatObjectMetadata.nameSingular); } catch (error) { diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-many-records.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-many-records.service.ts index 1ba38dadc8..11f695183a 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-many-records.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-many-records.service.ts @@ -59,13 +59,14 @@ export class CreateManyRecordsService { createdBy: actorMetadata, })); - const createdRecords = await this.commonCreateManyRunner.execute( - { - data: cleanedRecords, - selectedFields, - }, - queryRunnerContext, - ); + const { results: createdRecords } = + await this.commonCreateManyRunner.execute( + { + data: cleanedRecords, + selectedFields, + }, + queryRunnerContext, + ); this.logger.log( `Created ${createdRecords.length} records in ${objectName}`, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts index 1cc4ce4d06..e1bbb8995e 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts @@ -61,13 +61,14 @@ export class CreateRecordService { const cleanedRecord = removeUndefinedFromRecord(objectRecord); const dataWithActor = { ...cleanedRecord, createdBy: actorMetadata }; - const createdRecord = await this.commonCreateOneRunner.execute( - { - data: dataWithActor, - selectedFields, - }, - queryRunnerContext, - ); + const { results: createdRecord } = + await this.commonCreateOneRunner.execute( + { + data: dataWithActor, + selectedFields, + }, + queryRunnerContext, + ); this.logger.log(`Record created successfully in ${objectName}`); diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts index d389ee9087..e991be8b35 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/delete-record.service.ts @@ -54,13 +54,14 @@ export class DeleteRecordService { } if (soft) { - const deletedRecord = await this.commonDeleteOneRunner.execute( - { - id: objectRecordId, - selectedFields, - }, - queryRunnerContext, - ); + const { results: deletedRecord } = + await this.commonDeleteOneRunner.execute( + { + id: objectRecordId, + selectedFields, + }, + queryRunnerContext, + ); this.logger.log(`Record soft deleted successfully from ${objectName}`); @@ -70,13 +71,14 @@ export class DeleteRecordService { result: deletedRecord, }; } else { - const destroyedRecord = await this.commonDestroyOneRunner.execute( - { - id: objectRecordId, - selectedFields, - }, - queryRunnerContext, - ); + const { results: destroyedRecord } = + await this.commonDestroyOneRunner.execute( + { + id: objectRecordId, + selectedFields, + }, + queryRunnerContext, + ); this.logger.log( `Record permanently deleted successfully from ${objectName}`, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts index 052d50b840..c2dc283774 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/find-records.service.ts @@ -50,7 +50,9 @@ export class FindRecordsService { { id: OrderByDirection.AscNullsFirst }, ]; - const { records, totalCount } = await this.commonFindManyRunner.execute( + const { + results: { records, totalCount }, + } = await this.commonFindManyRunner.execute( { filter, orderBy: orderByWithIdCondition, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/update-many-records.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/update-many-records.service.ts index b78dbf51d5..572b24346e 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/update-many-records.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/update-many-records.service.ts @@ -50,10 +50,11 @@ export class UpdateManyRecordsService { const cleanedData = removeUndefinedFromRecord(data); - const updatedRecords = await this.commonUpdateManyRunner.execute( - { filter, data: cleanedData, selectedFields }, - queryRunnerContext, - ); + const { results: updatedRecords } = + await this.commonUpdateManyRunner.execute( + { filter, data: cleanedData, selectedFields }, + queryRunnerContext, + ); this.logger.log( `Updated ${updatedRecords.length} records in ${objectName}`, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts index a96d790e62..c0d056d9d3 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/update-record.service.ts @@ -89,14 +89,15 @@ export class UpdateRecordService { // This prevents validation errors for partial composite field inputs const cleanedRecord = removeUndefinedFromRecord(filteredObjectRecord); - const updatedRecord = await this.commonUpdateOneRunner.execute( - { - id: objectRecordId, - data: cleanedRecord, - selectedFields, - }, - queryRunnerContext, - ); + const { results: updatedRecord } = + await this.commonUpdateOneRunner.execute( + { + id: objectRecordId, + data: cleanedRecord, + selectedFields, + }, + queryRunnerContext, + ); this.logger.log(`Record updated successfully in ${objectName}`); diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts index 3f01cdbc33..f4343a12fa 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts @@ -48,14 +48,15 @@ export class UpsertRecordService { const cleanedRecord = removeUndefinedFromRecord(objectRecord); // Use Common API with upsert flag - it handles conflict detection automatically - const upsertedRecord = await this.commonCreateOneRunner.execute( - { - data: cleanedRecord, - selectedFields, - upsert: true, - }, - queryRunnerContext, - ); + const { results: upsertedRecord } = + await this.commonCreateOneRunner.execute( + { + data: cleanedRecord, + selectedFields, + upsert: true, + }, + queryRunnerContext, + ); this.logger.log(`Record upserted successfully in ${objectName}`); diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index d0c4d5c664..aff6950795 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -241,7 +241,6 @@ describe('WorkspaceEntityManager', () => { IS_DRAFT_EMAIL_ENABLED: false, IS_USAGE_ANALYTICS_ENABLED: false, IS_RICH_TEXT_V1_MIGRATED: false, - IS_DIRECT_GRAPHQL_EXECUTION_ENABLED: false, IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED: false, IS_CONNECTED_ACCOUNT_MIGRATED: false, IS_RECORD_TABLE_WIDGET_ENABLED: false, diff --git a/packages/twenty-server/src/modules/dashboard/chart-data/services/chart-data-query.service.ts b/packages/twenty-server/src/modules/dashboard/chart-data/services/chart-data-query.service.ts index 6436b8fd90..573a7fee95 100644 --- a/packages/twenty-server/src/modules/dashboard/chart-data/services/chart-data-query.service.ts +++ b/packages/twenty-server/src/modules/dashboard/chart-data/services/chart-data-query.service.ts @@ -236,7 +236,7 @@ export class ChartDataQueryService { groupByDimensionValues: true, }; - const results = await this.commonGroupByQueryRunnerService.execute( + const { results } = await this.commonGroupByQueryRunnerService.execute( { filter: gqlOperationFilter, orderBy: orderBy.length > 0 ? orderBy : undefined, diff --git a/packages/twenty-server/test/integration/graphql/suites/direct-execution-typename.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/direct-execution-typename.integration-spec.ts deleted file mode 100644 index 07493c0811..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/direct-execution-typename.integration-spec.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { randomUUID } from 'crypto'; - -import request from 'supertest'; -import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; -import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update-feature-flag.util'; -import { FeatureFlagKey } from 'twenty-shared/types'; - -const FIND_MANY_COMPANIES_WITH_TYPENAME = ` - query Companies($filter: CompanyFilterInput, $orderBy: [CompanyOrderByInput]) { - companies(filter: $filter, orderBy: $orderBy) { - __typename - edges { - __typename - node { - __typename - id - name - domainName { - __typename - primaryLinkLabel - primaryLinkUrl - } - } - cursor - } - pageInfo { - __typename - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } -`; - -describe('direct execution __typename filling (integration)', () => { - const testCompanyId1 = randomUUID(); - const testCompanyId2 = randomUUID(); - - beforeAll(async () => { - await updateFeatureFlag({ - featureFlag: FeatureFlagKey.IS_DIRECT_GRAPHQL_EXECUTION_ENABLED, - value: false, - expectToFail: false, - }); - - const gqlFields = 'id name'; - - await makeGraphqlAPIRequest( - createOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields, - data: { - id: testCompanyId1, - name: 'TypeName Test Company A', - }, - }), - ); - - await makeGraphqlAPIRequest( - createOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields, - data: { - id: testCompanyId2, - name: 'TypeName Test Company B', - }, - }), - ); - }); - - afterAll(async () => { - await updateFeatureFlag({ - featureFlag: FeatureFlagKey.IS_DIRECT_GRAPHQL_EXECUTION_ENABLED, - value: false, - expectToFail: false, - }); - - for (const id of [testCompanyId1, testCompanyId2]) { - await makeGraphqlAPIRequest( - destroyOneOperationFactory({ - objectMetadataSingularName: 'company', - gqlFields: 'id', - recordId: id, - }), - ); - } - }); - - it('should produce identical __typename values with and without direct execution', async () => { - const client = request(`http://localhost:${APP_PORT}`); - - const variables = { - filter: { - id: { in: [testCompanyId1, testCompanyId2] }, - }, - orderBy: [{ name: 'AscNullsLast' }], - }; - - // Run through standard GraphQL Yoga schema execution - const yogaResponse = await client - .post('/graphql') - .set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`) - .send({ query: FIND_MANY_COMPANIES_WITH_TYPENAME, variables }) - .expect(200); - - expect(yogaResponse.body.errors).toBeUndefined(); - expect(yogaResponse.body.data).toBeDefined(); - - const yogaResult = yogaResponse.body.data.companies; - - await updateFeatureFlag({ - featureFlag: FeatureFlagKey.IS_DIRECT_GRAPHQL_EXECUTION_ENABLED, - value: true, - expectToFail: false, - }); - - // Run through direct execution path - const directResponse = await client - .post('/graphql') - .set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`) - .send({ query: FIND_MANY_COMPANIES_WITH_TYPENAME, variables }) - .expect(200); - - expect(directResponse.body.errors).toBeUndefined(); - expect(directResponse.body.data).toBeDefined(); - - const directResult = directResponse.body.data.companies; - - expect(directResult).toStrictEqual(yogaResult); - }); -}); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap index d6f6e0f589..93a09b691a 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - ADDRESS Gql create input - failure ADDRESS - should fail with : {"addressField":"not-an-address"} 1`] = `"Expected type "AddressCreateInput" to be an object."`; +exports[`Create input validation - ADDRESS Gql create input - failure ADDRESS - should fail with : {"addressField":"not-an-address"} 1`] = `"Invalid object value 'not-an-address' for field "addressField""`; exports[`Create input validation - ADDRESS Rest create input - failure ADDRESS - should fail with : {"addressField":"not-an-address"} 1`] = `"["Invalid object value 'not-an-address' for field \\"addressField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap index 5b50a513fc..183942f1ee 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - ARRAY Gql create input - failure ARRAY - should fail with : {"arrayField":1} 1`] = `"String cannot represent a non string value: 1"`; +exports[`Create input validation - ARRAY Gql create input - failure ARRAY - should fail with : {"arrayField":1} 1`] = `"Invalid value 1 for field "arrayField - Array values need to be string""`; -exports[`Create input validation - ARRAY Gql create input - failure ARRAY - should fail with : {"arrayField":true} 1`] = `"String cannot represent a non string value: true"`; +exports[`Create input validation - ARRAY Gql create input - failure ARRAY - should fail with : {"arrayField":true} 1`] = `"Invalid value true for field "arrayField - Array values need to be string""`; exports[`Create input validation - ARRAY Rest create input - failure ARRAY - should fail with : {"arrayField":1} 1`] = `"["Invalid value 1 for field \\"arrayField - Array values need to be string\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap index 608dec35b7..47302f5806 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - CURRENCY Gql create input - failure CURRENCY - should fail with : {"currencyField":"not-a-currency"} 1`] = `"Expected type "CurrencyCreateInput" to be an object."`; +exports[`Create input validation - CURRENCY Gql create input - failure CURRENCY - should fail with : {"currencyField":"not-a-currency"} 1`] = `"Invalid object value 'not-a-currency' for field "currencyField""`; exports[`Create input validation - CURRENCY Rest create input - failure CURRENCY - should fail with : {"currencyField":"not-a-currency"} 1`] = `"["Invalid object value 'not-a-currency' for field \\"currencyField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap index 5a82f259d7..9fb092d89d 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - EMAILS Gql create input - failure EMAILS - should fail with : {"emailsField":"not-an-email"} 1`] = `"Expected type "EmailsCreateInput" to be an object."`; +exports[`Create input validation - EMAILS Gql create input - failure EMAILS - should fail with : {"emailsField":"not-an-email"} 1`] = `"Invalid object value 'not-an-email' for field "emailsField""`; exports[`Create input validation - EMAILS Gql create input - failure EMAILS - should fail with : {"emailsField":{"additionalEmails":"not-an-email"}} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/files-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/files-field-create-input-validation.integration-spec.ts.snap index ca85343cfa..499d6e5d15 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/files-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/files-field-create-input-validation.integration-spec.ts.snap @@ -1,14 +1,24 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":"not-an-addFiles-property"} 1`] = `"Expected type "FileItemInput" to be an object."`; +exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":"not-an-addFiles-property"} 1`] = `"Invalid value "'not-an-addFiles-property'" for FILES field "filesField" - It should be an array of objects with "fileId" and "label" properties."`; -exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":[{"addFiles":[{"fileId":"550e8400-e29b-41d4-a716-446655440000","label":12345}]}]} 1`] = `"Field "fileId" of required type "UUID!" was not provided."`; +exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":[{"addFiles":[{"fileId":"550e8400-e29b-41d4-a716-446655440000","label":12345}]}]} 1`] = `"Invalid value "[ { addFiles: [ [Object] ] } ]" for FILES field "filesField" - 0.fileId: Invalid input: expected string, received undefined, 0.label: Invalid input: expected string, received undefined, 0: Unrecognized key: "addFiles""`; -exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":{"addFiles":[{"fileId":"550e8400-e29b-41d4-a716-446655440000","label":"Document.pdf","extension":"not-allowed-in-input"}]}} 1`] = `"Field "fileId" of required type "UUID!" was not provided."`; +exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":{"addFiles":[{"fileId":"550e8400-e29b-41d4-a716-446655440000","label":"Document.pdf","extension":"not-allowed-in-input"}]}} 1`] = ` +"Invalid value "{ + addFiles: [ + { + fileId: '550e8400-e29b-41d4-a716-446655440000', + label: 'Document.pdf', + extension: 'not-allowed-in-input' + } + ] +}" for FILES field "filesField" - : Invalid input: expected array, received object" +`; -exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":{"addFiles":[{"fileId":"not-a-uuid","label":"Document.pdf"}]}} 1`] = `"Field "fileId" of required type "UUID!" was not provided."`; +exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":{"addFiles":[{"fileId":"not-a-uuid","label":"Document.pdf"}]}} 1`] = `"Invalid value "{ addFiles: [ { fileId: 'not-a-uuid', label: 'Document.pdf' } ] }" for FILES field "filesField" - : Invalid input: expected array, received object"`; -exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":{"addFiles":[{"invalidField":"test"}]}} 1`] = `"Field "fileId" of required type "UUID!" was not provided."`; +exports[`Create input validation - FILES Gql create input - failure FILES - should fail with : {"filesField":{"addFiles":[{"invalidField":"test"}]}} 1`] = `"Invalid value "{ addFiles: [ { invalidField: 'test' } ] }" for FILES field "filesField" - : Invalid input: expected array, received object"`; exports[`Create input validation - FILES Rest create input - failure FILES - should fail with : {"filesField":"not-an-addFiles-property"} 1`] = `"["Invalid value \\"'not-an-addFiles-property'\\" for FILES field \\"filesField\\" - It should be an array of objects with \\"fileId\\" and \\"label\\" properties."]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap index 85ae209283..3d99dbeb2d 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - FULL_NAME Gql create input - failure FULL_NAME - should fail with : {"fullNameField":"not-a-full-name"} 1`] = `"Expected type "FullNameCreateInput" to be an object."`; +exports[`Create input validation - FULL_NAME Gql create input - failure FULL_NAME - should fail with : {"fullNameField":"not-a-full-name"} 1`] = `"Invalid object value 'not-a-full-name' for field "fullNameField""`; exports[`Create input validation - FULL_NAME Rest create input - failure FULL_NAME - should fail with : {"fullNameField":"not-a-full-name"} 1`] = `"["Invalid object value 'not-a-full-name' for field \\"fullNameField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap index 42ff9ae7ae..8d52a60f43 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - LINKS Gql create input - failure LINKS - should fail with : {"linksField":"not-a-link"} 1`] = `"Expected type "LinksCreateInput" to be an object."`; +exports[`Create input validation - LINKS Gql create input - failure LINKS - should fail with : {"linksField":"not-a-link"} 1`] = `"Invalid object value 'not-a-link' for field "linksField""`; exports[`Create input validation - LINKS Rest create input - failure LINKS - should fail with : {"linksField":"not-a-link"} 1`] = `"["Invalid object value 'not-a-link' for field \\"linksField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/morph-relation-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/morph-relation-field-create-input-validation.integration-spec.ts.snap index 77b2170495..ef9e4f6758 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/morph-relation-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/morph-relation-field-create-input-validation.integration-spec.ts.snap @@ -2,10 +2,10 @@ exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":"not-a-morph-relation"} 1`] = `"Invalid UUID value 'not-a-morph-relation' for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; -exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":[]} 1`] = `"ID cannot represent value: []"`; +exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":[]} 1`] = `"Invalid UUID value [] for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; -exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{}} 1`] = `"ID cannot represent value: {}"`; +exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{}} 1`] = `"Invalid UUID value {} for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; -exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":1} 1`] = `"Invalid UUID value '1' for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; +exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":1} 1`] = `"Invalid UUID value 1 for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; -exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":true} 1`] = `"ID cannot represent value: true"`; +exports[`Create input validation - MORPH_RELATION Gql create input - failure MORPH_RELATION - should fail with : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":true} 1`] = `"Invalid UUID value true for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap index fca2013fc5..bc10dd2fe1 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - MULTI_SELECT Gql create input - failure MULTI_SELECT - should fail with : {"multiSelectField":"not-a-select-option"} 1`] = `"Value "not-a-select-option" does not exist in "ApiInputValidationTestObjectMultiSelectFieldEnum" enum."`; +exports[`Create input validation - MULTI_SELECT Gql create input - failure MULTI_SELECT - should fail with : {"multiSelectField":"not-a-select-option"} 1`] = `"Invalid value 'not-a-select-option' for multi select field "multiSelectField""`; exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":"not-a-select-option"} 1`] = `"["Invalid value 'not-a-select-option' for multi select field \\"multiSelectField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/number-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/number-field-create-input-validation.integration-spec.ts.snap index cb00cefa69..c5d0f1d180 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/number-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/number-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - NUMBER Gql create input - failure NUMBER - should fail with : {"numberField":"string"} 1`] = `"Float cannot represent non numeric value: "string""`; +exports[`Create input validation - NUMBER Gql create input - failure NUMBER - should fail with : {"numberField":"string"} 1`] = `"Invalid number value 'string' for field "numberField""`; exports[`Create input validation - NUMBER Rest create input - failure NUMBER - should fail with : {"numberField":"string"} 1`] = `"["Invalid number value 'string' for field \\"numberField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap index a08c23235e..2e0ee952e8 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - PHONES Gql create input - failure PHONES - should fail with : {"phonesField":"not-a-phone"} 1`] = `"Expected type "PhonesCreateInput" to be an object."`; +exports[`Create input validation - PHONES Gql create input - failure PHONES - should fail with : {"phonesField":"not-a-phone"} 1`] = `"Invalid object value 'not-a-phone' for field "phonesField""`; exports[`Create input validation - PHONES Rest create input - failure PHONES - should fail with : {"phonesField":"not-a-phone"} 1`] = `"["Invalid object value 'not-a-phone' for field \\"phonesField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/position-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/position-field-create-input-validation.integration-spec.ts.snap index 06183c11ae..4cf705ea1b 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/position-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/position-field-create-input-validation.integration-spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - POSITION Gql create input - failure POSITION - should fail with : {"position":"not-a-position"} 1`] = `"Invalid position value: 'not-a-position'. Position must be 'first', 'last', or a number"`; +exports[`Create input validation - POSITION Gql create input - failure POSITION - should fail with : {"position":"not-a-position"} 1`] = `"Invalid position value 'not-a-position' for field "position""`; exports[`Create input validation - POSITION Gql create input - failure POSITION - should fail with : {"position":null} 1`] = `"Invalid position value null for field "position""`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap index 9a1962d8e0..3116367cb5 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - RATING Gql create input - failure RATING - should fail with : {"ratingField":"not-a-rating"} 1`] = `"Value "not-a-rating" does not exist in "ApiInputValidationTestObjectRatingFieldEnum" enum."`; +exports[`Create input validation - RATING Gql create input - failure RATING - should fail with : {"ratingField":"not-a-rating"} 1`] = `"Invalid value 'not-a-rating' for field "ratingField""`; exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":"not-a-rating"} 1`] = `"["Invalid value 'not-a-rating' for field \\"ratingField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap index fe99310121..f3ed256c85 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap @@ -2,9 +2,9 @@ exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":"non-uuid"} 1`] = `"Invalid UUID value 'non-uuid' for field "manyToOneRelationFieldId""`; -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"oneToManyRelationFieldId":"not-existing-field"} 1`] = `"Field "oneToManyRelationFieldId" is not defined by type "ApiInputValidationTestObjectCreateInput". Did you mean "manyToOneRelationFieldId" or "manyToOneRelationField"?"`; +exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"oneToManyRelationFieldId":"not-existing-field"} 1`] = `"Object apiInputValidationTestObject doesn't have any "oneToManyRelationFieldId" field."`; -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"oneToOneRelationField":"not-existing-field"} 1`] = `"Field "oneToOneRelationField" is not defined by type "ApiInputValidationTestObjectCreateInput". Did you mean "manyToOneRelationField" or "manyToOneRelationFieldId"?"`; +exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"oneToOneRelationField":"not-existing-field"} 1`] = `"Object apiInputValidationTestObject doesn't have any "oneToOneRelationField" field."`; exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":"non-uuid"} 1`] = `"["Invalid UUID value 'non-uuid' for field \\"manyToOneRelationFieldId\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap index 1369a17aaf..b7501a84e6 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":"not-a-rich-text"} 1`] = `"Expected type "RichTextCreateInput" to be an object."`; +exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":"not-a-rich-text"} 1`] = `"Invalid object value 'not-a-rich-text' for field "richTextField""`; exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":{"blocknote":"[{\\"id\\":\\"1\\",\\"type\\":\\"paragraph\\",\\"props\\":{},\\"content\\":[{\\"type\\":\\"text\\",\\"text\\":\\"test\\"},\\"children\\":[]}]"}} 1`] = `"Invalid blocknote value for field "richTextField.blocknote" - must contain valid JSON"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap index 03144467a1..819b388c4d 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - SELECT Gql create input - failure SELECT - should fail with : {"selectField":"not-a-select-option"} 1`] = `"Value "not-a-select-option" does not exist in "ApiInputValidationTestObjectSelectFieldEnum" enum."`; +exports[`Create input validation - SELECT Gql create input - failure SELECT - should fail with : {"selectField":"not-a-select-option"} 1`] = `"Invalid value 'not-a-select-option' for field "selectField""`; -exports[`Create input validation - SELECT Gql create input - failure SELECT - should fail with : {"selectField":1} 1`] = `"Enum "ApiInputValidationTestObjectSelectFieldEnum" cannot represent non-string value: 1."`; +exports[`Create input validation - SELECT Gql create input - failure SELECT - should fail with : {"selectField":1} 1`] = `"Invalid string value 1 for text field "selectField""`; exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":"not-a-select-option"} 1`] = `"["Invalid value 'not-a-select-option' for field \\"selectField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap index 8ea26e1007..794f8648be 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":1} 1`] = `"String cannot represent a non string value: 1"`; +exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":1} 1`] = `"Invalid string value 1 for text field "textField""`; exports[`Create input validation - TEXT Rest create input - failure TEXT - should fail with : {"textField":1} 1`] = `"["Invalid string value 1 for text field \\"textField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap index 4b5d55887a..4a4cf240f4 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Create input validation - UUID Gql create input - failure UUID - should fail with : {"uuidField":"non-uuid"} 1`] = `"Invalid UUID: 'non-uuid'"`; +exports[`Create input validation - UUID Gql create input - failure UUID - should fail with : {"uuidField":"non-uuid"} 1`] = `"Invalid UUID value 'non-uuid' for field "uuidField""`; exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":"non-uuid"} 1`] = `"["Invalid UUID value 'non-uuid' for field \\"uuidField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/array-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/array-field-filter-input-validation.integration-spec.ts.snap index 46e2e93b5c..21fb630316 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/array-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/array-field-filter-input-validation.integration-spec.ts.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":[]}} 1`] = `"String cannot represent a non string value: []"`; +exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":[]}} 1`] = `"Filter operator "containsIlike" requires a string value for field "arrayField", got object"`; -exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":{}}} 1`] = `"String cannot represent a non string value: {}"`; +exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":{}}} 1`] = `"Filter operator "containsIlike" requires a string value for field "arrayField", got object"`; -exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":2}} 1`] = `"String cannot represent a non string value: 2"`; +exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":2}} 1`] = `"Filter operator "containsIlike" requires a string value for field "arrayField", got number"`; -exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":true}} 1`] = `"String cannot represent a non string value: true"`; +exports[`Filter args validation - ARRAY Gql filter input - failure ARRAY field type - should fail with filter : {"arrayField":{"containsIlike":true}} 1`] = `"Filter operator "containsIlike" requires a string value for field "arrayField", got boolean"`; exports[`Filter args validation - ARRAY Rest filter input - failure ARRAY field type - should fail with filter : "arrayField[containsAny]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/boolean-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/boolean-field-filter-input-validation.integration-spec.ts.snap index b4590eba87..82cda5ebcf 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/boolean-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/boolean-field-filter-input-validation.integration-spec.ts.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - BOOLEAN Gql filter input - failure BOOLEAN field type - should fail with filter : {"booleanField":{"eq":"not-a-boolean"}} 1`] = `"Boolean cannot represent a non boolean value: "not-a-boolean""`; +exports[`Filter input validation - BOOLEAN Gql filter input - failure BOOLEAN field type - should fail with filter : {"booleanField":{"eq":"not-a-boolean"}} 1`] = `"Invalid boolean value 'not-a-boolean' for field "booleanField""`; -exports[`Filter input validation - BOOLEAN Gql filter input - failure BOOLEAN field type - should fail with filter : {"booleanField":{"eq":[]}} 1`] = `"Boolean cannot represent a non boolean value: []"`; +exports[`Filter input validation - BOOLEAN Gql filter input - failure BOOLEAN field type - should fail with filter : {"booleanField":{"eq":[]}} 1`] = `"Invalid boolean value [] for field "booleanField""`; -exports[`Filter input validation - BOOLEAN Gql filter input - failure BOOLEAN field type - should fail with filter : {"booleanField":{"eq":2}} 1`] = `"Boolean cannot represent a non boolean value: 2"`; +exports[`Filter input validation - BOOLEAN Gql filter input - failure BOOLEAN field type - should fail with filter : {"booleanField":{"eq":2}} 1`] = `"Invalid boolean value 2 for field "booleanField""`; exports[`Filter input validation - BOOLEAN Rest filter input - failure BOOLEAN field type - should fail with filter : "booleanField[eq]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/files-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/files-field-filter-input-validation.integration-spec.ts.snap index 7266a2237c..46fd2c0a24 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/files-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/files-field-filter-input-validation.integration-spec.ts.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":[]}} 1`] = `"Field "containsIlike" is not defined by type "RawJsonFilter"."`; +exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":[]}} 1`] = `"Operator "containsIlike" is not valid for field "filesField" of type FILES - Allowed operators: is, like"`; -exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":{}}} 1`] = `"Field "containsIlike" is not defined by type "RawJsonFilter"."`; +exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":{}}} 1`] = `"Operator "containsIlike" is not valid for field "filesField" of type FILES - Allowed operators: is, like"`; -exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":2}} 1`] = `"Field "containsIlike" is not defined by type "RawJsonFilter"."`; +exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":2}} 1`] = `"Operator "containsIlike" is not valid for field "filesField" of type FILES - Allowed operators: is, like"`; -exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":true}} 1`] = `"Field "containsIlike" is not defined by type "RawJsonFilter"."`; +exports[`Filter input validation - FILES Gql filter input - failure FILES field type - should fail with filter : {"filesField":{"containsIlike":true}} 1`] = `"Operator "containsIlike" is not valid for field "filesField" of type FILES - Allowed operators: is, like"`; exports[`Filter input validation - FILES Rest filter input - failure FILES field type - should fail with filter : "filesField[containsAny]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/morph-relation-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/morph-relation-field-filter-input-validation.integration-spec.ts.snap index 0abd06c5d1..82a1b00b53 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/morph-relation-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/morph-relation-field-filter-input-validation.integration-spec.ts.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - MORPH_RELATION Gql filter input - failure MORPH_RELATION field type - should fail with filter : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1":{"eq":"6dd71a46-68fe-4420-82b3-0d5b00ad2642"}} 1`] = `"Field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1" is not defined by type "ApiInputValidationTestObjectFilterInput". Did you mean "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id" or "manyToOneMorphRelationFieldApiInputValidationTargetTestObject2Id"?"`; +exports[`Filter input validation - MORPH_RELATION Gql filter input - failure MORPH_RELATION field type - should fail with filter : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1":{"eq":"6dd71a46-68fe-4420-82b3-0d5b00ad2642"}} 1`] = `"Cannot filter by relation field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1": use "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id" instead"`; -exports[`Filter input validation - MORPH_RELATION Gql filter input - failure MORPH_RELATION field type - should fail with filter : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID: 'invalid-uuid'"`; +exports[`Filter input validation - MORPH_RELATION Gql filter input - failure MORPH_RELATION field type - should fail with filter : {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`; exports[`Filter input validation - MORPH_RELATION Rest filter input - failure MORPH_RELATION field type - should fail with filter : "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1[eq]:\\"6dd71a46-68fe-4420-82b3-0d5b00ad2642\\"" 1`] = ` [ - "Data validation error.", + "Cannot filter by relation field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1": use "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id" instead", ] `; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/multi-select-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/multi-select-field-filter-input-validation.integration-spec.ts.snap index 087bd5c47d..f5326c3625 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/multi-select-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/multi-select-field-filter-input-validation.integration-spec.ts.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - MULTI_SELECT Gql filter input - failure MULTI_SELECT field type - should fail with filter : {"multiSelectField":{"eq":"not-a-multi-select"}} 1`] = `"Value "not-a-multi-select" does not exist in "ApiInputValidationTestObjectMultiSelectFieldEnum" enum."`; +exports[`Filter input validation - MULTI_SELECT Gql filter input - failure MULTI_SELECT field type - should fail with filter : {"multiSelectField":{"eq":"not-a-multi-select"}} 1`] = `"Operator "eq" is not valid for field "multiSelectField" of type MULTI_SELECT - Allowed operators: containsAny, is, isEmptyArray"`; -exports[`Filter input validation - MULTI_SELECT Gql filter input - failure MULTI_SELECT field type - should fail with filter : {"multiSelectField":{"in":["test"]}} 1`] = `"Value "test" does not exist in "ApiInputValidationTestObjectMultiSelectFieldEnum" enum."`; +exports[`Filter input validation - MULTI_SELECT Gql filter input - failure MULTI_SELECT field type - should fail with filter : {"multiSelectField":{"in":["test"]}} 1`] = `"Operator "in" is not valid for field "multiSelectField" of type MULTI_SELECT - Allowed operators: containsAny, is, isEmptyArray"`; -exports[`Filter input validation - MULTI_SELECT Gql filter input - failure MULTI_SELECT field type - should fail with filter : {"multiSelectField":{"neq":"test"}} 1`] = `"Value "test" does not exist in "ApiInputValidationTestObjectMultiSelectFieldEnum" enum."`; +exports[`Filter input validation - MULTI_SELECT Gql filter input - failure MULTI_SELECT field type - should fail with filter : {"multiSelectField":{"neq":"test"}} 1`] = `"Operator "neq" is not valid for field "multiSelectField" of type MULTI_SELECT - Allowed operators: containsAny, is, isEmptyArray"`; exports[`Filter input validation - MULTI_SELECT Rest filter input - failure MULTI_SELECT field type - should fail with filter : "multiSelectField[eq]:\\"not-a-multi-select\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/number-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/number-field-filter-input-validation.integration-spec.ts.snap index 172c3202d4..70af4103d6 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/number-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/number-field-filter-input-validation.integration-spec.ts.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":"not-a-number"}} 1`] = `"Float cannot represent non numeric value: "not-a-number""`; +exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":"not-a-number"}} 1`] = `"Invalid number value NaN for field "numberField""`; -exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":[]}} 1`] = `"Float cannot represent non numeric value: []"`; +exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":[]}} 1`] = `"Invalid number value [] for field "numberField""`; -exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":{}}} 1`] = `"Float cannot represent non numeric value: {}"`; +exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":{}}} 1`] = `"Invalid number value {} for field "numberField""`; -exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":true}} 1`] = `"Float cannot represent non numeric value: true"`; +exports[`Filter input validation - NUMBER Gql filter input - failure NUMBER field type - should fail with filter : {"numberField":{"eq":true}} 1`] = `"Invalid number value true for field "numberField""`; exports[`Filter input validation - NUMBER Rest filter input - failure NUMBER field type - should fail with filter : "numberField[eq]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/rating-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/rating-field-filter-input-validation.integration-spec.ts.snap index 560050dfaf..336a3d8db6 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/rating-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/rating-field-filter-input-validation.integration-spec.ts.snap @@ -1,14 +1,14 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":"not-a-rating"}} 1`] = `"Value "not-a-rating" does not exist in "ApiInputValidationTestObjectRatingFieldEnum" enum."`; +exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":"not-a-rating"}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_ratingField_enum": "not-a-rating""`; -exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":[]}} 1`] = `"Enum "ApiInputValidationTestObjectRatingFieldEnum" cannot represent non-string value: []."`; +exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":[]}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_ratingField_enum": "{}""`; -exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":{}}} 1`] = `"Enum "ApiInputValidationTestObjectRatingFieldEnum" cannot represent non-string value: {}."`; +exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":{}}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_ratingField_enum": "{}""`; -exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":2}} 1`] = `"Enum "ApiInputValidationTestObjectRatingFieldEnum" cannot represent non-string value: 2."`; +exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":2}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_ratingField_enum": "2""`; -exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":true}} 1`] = `"Enum "ApiInputValidationTestObjectRatingFieldEnum" cannot represent non-string value: true."`; +exports[`Filter input validation - RATING Gql filter input - failure RATING field type - should fail with filter : {"ratingField":{"eq":true}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_ratingField_enum": "true""`; exports[`Filter input validation - RATING Rest filter input - failure RATING field type - should fail with filter : "ratingField[eq]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/raw-json-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/raw-json-field-filter-input-validation.integration-spec.ts.snap index d44d8ea289..353c42d94c 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/raw-json-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/raw-json-field-filter-input-validation.integration-spec.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":[]}} 1`] = `"String cannot represent a non string value: []"`; +exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":[]}} 1`] = `"Filter operator "like" requires a string value for field "rawJsonField", got object"`; -exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":{}}} 1`] = `"String cannot represent a non string value: {}"`; +exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":{}}} 1`] = `"Filter operator "like" requires a string value for field "rawJsonField", got object"`; -exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":2}} 1`] = `"String cannot represent a non string value: 2"`; +exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":2}} 1`] = `"Filter operator "like" requires a string value for field "rawJsonField", got number"`; -exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":true}} 1`] = `"String cannot represent a non string value: true"`; +exports[`Filter input validation - RAW_JSON Gql filter input - failure RAW_JSON field type - should fail with filter : {"rawJsonField":{"like":true}} 1`] = `"Filter operator "like" requires a string value for field "rawJsonField", got boolean"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/relation-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/relation-field-filter-input-validation.integration-spec.ts.snap index 797bd70950..4e46775230 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/relation-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/relation-field-filter-input-validation.integration-spec.ts.snap @@ -1,16 +1,16 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"manyToOneRelationField":{"eq":"6dd71a46-68fe-4420-82b3-0d5b00ad2642"}} 1`] = `"Field "manyToOneRelationField" is not defined by type "ApiInputValidationTestObjectFilterInput". Did you mean "manyToOneRelationFieldId"?"`; +exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"manyToOneRelationField":{"eq":"6dd71a46-68fe-4420-82b3-0d5b00ad2642"}} 1`] = `"Cannot filter by relation field "manyToOneRelationField": use "manyToOneRelationFieldId" instead"`; -exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"manyToOneRelationFieldId":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID: 'invalid-uuid'"`; +exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"manyToOneRelationFieldId":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "manyToOneRelationFieldId""`; -exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"oneToManyRelationField":{"eq":"6dd71a46-68fe-4420-82b3-0d5b00ad2642"}} 1`] = `"Field "oneToManyRelationField" is not defined by type "ApiInputValidationTestObjectFilterInput". Did you mean "manyToOneRelationFieldId"?"`; +exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"oneToManyRelationField":{"eq":"6dd71a46-68fe-4420-82b3-0d5b00ad2642"}} 1`] = `"Cannot filter by relation field "oneToManyRelationField""`; -exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"oneToManyRelationFieldId":{"eq":"invalid-uuid"}} 1`] = `"Field "oneToManyRelationFieldId" is not defined by type "ApiInputValidationTestObjectFilterInput". Did you mean "manyToOneRelationFieldId"?"`; +exports[`Filter input validation - RELATION Gql filter input - failure RELATION field type - should fail with filter : {"oneToManyRelationFieldId":{"eq":"invalid-uuid"}} 1`] = `"Object apiInputValidationTestObject doesn't have any "oneToManyRelationFieldId" field."`; exports[`Filter input validation - RELATION Rest filter input - failure RELATION field type - should fail with filter : "manyToOneRelationField[eq]:\\"6dd71a46-68fe-4420-82b3-0d5b00ad2642\\"" 1`] = ` [ - "Data validation error.", + "Cannot filter by relation field "manyToOneRelationField": use "manyToOneRelationFieldId" instead", ] `; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/select-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/select-field-filter-input-validation.integration-spec.ts.snap index c312ac28cc..c98f0bf0f0 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/select-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/select-field-filter-input-validation.integration-spec.ts.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":"not-a-select"}} 1`] = `"Value "not-a-select" does not exist in "ApiInputValidationTestObjectSelectFieldEnum" enum."`; +exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":"not-a-select"}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_selectField_enum": "not-a-select""`; -exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":[]}} 1`] = `"Enum "ApiInputValidationTestObjectSelectFieldEnum" cannot represent non-string value: []."`; +exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":[]}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_selectField_enum": "{}""`; -exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":{}}} 1`] = `"Enum "ApiInputValidationTestObjectSelectFieldEnum" cannot represent non-string value: {}."`; +exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":{}}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_selectField_enum": "{}""`; -exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":true}} 1`] = `"Enum "ApiInputValidationTestObjectSelectFieldEnum" cannot represent non-string value: true."`; +exports[`Filter input validation - SELECT Gql filter input - failure SELECT field type - should fail with filter : {"selectField":{"eq":true}} 1`] = `"invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5."_apiInputValidationTestObject_selectField_enum": "true""`; exports[`Filter input validation - SELECT Rest filter input - failure SELECT field type - should fail with filter : "selectField[eq]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/uuid-field-filter-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/uuid-field-filter-input-validation.integration-spec.ts.snap index 38b5c52224..e4ff13996e 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/uuid-field-filter-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/__snapshots__/uuid-field-filter-input-validation.integration-spec.ts.snap @@ -1,16 +1,16 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":"2025-01-01"}} 1`] = `"Invalid UUID: '2025-01-01'"`; +exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":"2025-01-01"}} 1`] = `"Invalid UUID value '2025-01-01' for field "uuidField""`; -exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID: 'invalid-uuid'"`; +exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "uuidField""`; -exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":[]}} 1`] = `"UUID must be a string"`; +exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":[]}} 1`] = `"Invalid UUID value [] for field "uuidField""`; -exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":{}}} 1`] = `"UUID must be a string"`; +exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":{}}} 1`] = `"Invalid UUID value {} for field "uuidField""`; -exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":2}} 1`] = `"UUID must be a string"`; +exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":2}} 1`] = `"Invalid UUID value 2 for field "uuidField""`; -exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":true}} 1`] = `"UUID must be a string"`; +exports[`Filter input validation - UUID Gql filter input - failure UUID field type - should fail with filter : {"uuidField":{"eq":true}} 1`] = `"Invalid UUID value true for field "uuidField""`; exports[`Filter input validation - UUID Rest filter input - failure UUID field type - should fail with filter : "uuidField[eq]:\\"[]\\"" 1`] = ` [ diff --git a/packages/twenty-server/test/integration/graphql/suites/object-generated/nested-relation-queries.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/object-generated/nested-relation-queries.integration-spec.ts index 74237c8817..d8c6370cea 100644 --- a/packages/twenty-server/test/integration/graphql/suites/object-generated/nested-relation-queries.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/object-generated/nested-relation-queries.integration-spec.ts @@ -420,7 +420,7 @@ describe('relation connect in workspace createOne/createMany resolvers (e2e)', expect(response.body.errors).toBeDefined(); expect(response.body.errors[0].message).toBe( - 'Field "name" is not defined by type "CompanyWhereUniqueInput".', + "Missing required fields: at least one unique constraint have to be fully populated for 'company'.", ); expect(response.body.errors[0].extensions.code).toBe( ErrorCode.BAD_USER_INPUT, diff --git a/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/fields-permissions/read-permissions.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/fields-permissions/read-permissions.integration-spec.ts index 342385ac25..1b182ebc24 100644 --- a/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/fields-permissions/read-permissions.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/fields-permissions/read-permissions.integration-spec.ts @@ -321,6 +321,8 @@ describe('Field permissions restrictions', () => { objectMetadataSingularName: 'company', objectMetadataPluralName: 'companies', gqlFields: COMPANY_GQL_FIELDS_WITH_EMPLOYEES, + filter: { id: { eq: companyId } }, + data: { name: 'TestUpdate' }, }); const response = @@ -334,6 +336,7 @@ describe('Field permissions restrictions', () => { objectMetadataSingularName: 'company', gqlFields: COMPANY_GQL_FIELDS_WITH_EMPLOYEES, recordId: companyId, + data: { name: 'TestUpdate' }, }); const response = @@ -377,6 +380,7 @@ describe('Field permissions restrictions', () => { objectMetadataSingularName: 'company', objectMetadataPluralName: 'companies', gqlFields: COMPANY_GQL_FIELDS_WITH_EMPLOYEES, + filter: { id: { eq: companyId } }, }); const response = diff --git a/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts index 7e872123da..b481db79b4 100644 --- a/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/search/search-resolver.integration-spec.ts @@ -173,7 +173,6 @@ describe('SearchResolver', () => { await deleteAllRecords('noteTarget'); await deleteAllRecords('taskTarget'); await deleteAllRecords('dashboard'); - await deleteAllRecords('workflow'); await deleteAllRecords('_pet'); await deleteAllRecords('_surveyResult'); await deleteAllRecords('_rocket'); @@ -221,6 +220,7 @@ describe('SearchResolver', () => { 'workspaceMember', 'employmentHistory', 'petCareAgreement', + 'workflow', ], limit: 50, }, @@ -327,6 +327,7 @@ describe('SearchResolver', () => { 'person', 'employmentHistory', 'petCareAgreement', + 'workflow', ], limit: 50, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap index 090f8ad944..86137f5415 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap @@ -9,7 +9,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided country code and calling code are conflicting", }, "message": "Provided country code and calling code are conflicting", - "name": "UserInputError", }, ] `; @@ -23,7 +22,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided country code and calling code are conflicting", }, "message": "Provided country code and calling code are conflicting", - "name": "UserInputError", }, ] `; @@ -37,7 +35,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided and inferred calling code are conflicting", }, "message": "Provided and inferred calling code are conflicting", - "name": "UserInputError", }, ] `; @@ -51,7 +48,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided and inferred calling code are conflicting", }, "message": "Provided and inferred calling code are conflicting", - "name": "UserInputError", }, ] `; @@ -65,7 +61,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided and inferred country code are conflicting", }, "message": "Provided and inferred country code are conflicting", - "name": "UserInputError", }, ] `; @@ -79,7 +74,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided and inferred country code are conflicting", }, "message": "Provided and inferred country code are conflicting", - "name": "UserInputError", }, ] `; @@ -93,7 +87,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Invalid calling code +999", }, "message": "Invalid calling code +999", - "name": "UserInputError", }, ] `; @@ -107,7 +100,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Invalid calling code +999", }, "message": "Invalid calling code +999", - "name": "UserInputError", }, ] `; @@ -121,7 +113,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Invalid country code XX", }, "message": "Invalid country code XX", - "name": "UserInputError", }, ] `; @@ -135,7 +126,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Invalid country code XX", }, "message": "Invalid country code XX", - "name": "UserInputError", }, ] `; @@ -149,7 +139,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided phone number is invalid not-a-number", }, "message": "Provided phone number is invalid not-a-number", - "name": "UserInputError", }, ] `; @@ -163,7 +152,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided phone number is invalid not-a-number", }, "message": "Provided phone number is invalid not-a-number", - "name": "UserInputError", }, ] `; @@ -177,7 +165,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided phone number is invalid 123456789", }, "message": "Provided phone number is invalid 123456789", - "name": "UserInputError", }, ] `; @@ -191,7 +178,6 @@ exports[`failing create phone field metadata test suite it should fail to create "userFriendlyMessage": "Provided phone number is invalid 123456789", }, "message": "Provided phone number is invalid 123456789", - "name": "UserInputError", }, ] `; diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-widget-restore-via-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-widget-restore-via-update.integration-spec.ts.snap index f53e7e5802..bb251b91d5 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-widget-restore-via-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-widget-restore-via-update.integration-spec.ts.snap @@ -80,6 +80,26 @@ exports[`Page layout widget restore via bulk update should succeed should handle "title": "Test Tab For Widget Restore", "updatedAt": Any, "widgets": [ + { + "configuration": { + "configurationType": "IFRAME", + "url": "https://updated.example.com", + }, + "createdAt": Any, + "deletedAt": null, + "gridPosition": { + "column": 0, + "columnSpan": 2, + "row": 0, + "rowSpan": 2, + }, + "id": Any, + "objectMetadataId": null, + "pageLayoutTabId": Any, + "title": "Updated Widget Title", + "type": "IFRAME", + "updatedAt": Any, + }, { "configuration": { "aggregateFieldMetadataId": Any, @@ -130,26 +150,6 @@ exports[`Page layout widget restore via bulk update should succeed should handle "type": "IFRAME", "updatedAt": Any, }, - { - "configuration": { - "configurationType": "IFRAME", - "url": "https://updated.example.com", - }, - "createdAt": Any, - "deletedAt": null, - "gridPosition": { - "column": 0, - "columnSpan": 2, - "row": 0, - "rowSpan": 2, - }, - "id": Any, - "objectMetadataId": null, - "pageLayoutTabId": Any, - "title": "Updated Widget Title", - "type": "IFRAME", - "updatedAt": Any, - }, ], }, ], diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-widget-restore-via-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-widget-restore-via-update.integration-spec.ts index 36467a0ac4..b2664b3b55 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-widget-restore-via-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-widget-restore-via-update.integration-spec.ts @@ -386,10 +386,22 @@ describe('Page layout widget restore via bulk update should succeed', () => { deletedAt: null, }); - expect(mixedData.updatePageLayoutWithTabsAndWidgets).toMatchSnapshot( - extractRecordIdsAndDatesAsExpectAny({ - ...mixedData.updatePageLayoutWithTabsAndWidgets, - }), + const sortedMixedResult = { + ...mixedData.updatePageLayoutWithTabsAndWidgets, + tabs: (mixedData.updatePageLayoutWithTabsAndWidgets.tabs ?? []).map( + (tab) => ({ + ...tab, + widgets: [...(tab.widgets ?? [])].sort( + (a, b) => + (a.gridPosition?.row ?? 0) - (b.gridPosition?.row ?? 0) || + (a.gridPosition?.column ?? 0) - (b.gridPosition?.column ?? 0), + ), + }), + ), + }; + + expect(sortedMixedResult).toMatchSnapshot( + extractRecordIdsAndDatesAsExpectAny(sortedMixedResult), ); }); }); diff --git a/packages/twenty-shared/src/types/FeatureFlagKey.ts b/packages/twenty-shared/src/types/FeatureFlagKey.ts index a1d9b3286e..88406d6167 100644 --- a/packages/twenty-shared/src/types/FeatureFlagKey.ts +++ b/packages/twenty-shared/src/types/FeatureFlagKey.ts @@ -11,7 +11,6 @@ export enum FeatureFlagKey { IS_DRAFT_EMAIL_ENABLED = 'IS_DRAFT_EMAIL_ENABLED', IS_USAGE_ANALYTICS_ENABLED = 'IS_USAGE_ANALYTICS_ENABLED', IS_RICH_TEXT_V1_MIGRATED = 'IS_RICH_TEXT_V1_MIGRATED', - IS_DIRECT_GRAPHQL_EXECUTION_ENABLED = 'IS_DIRECT_GRAPHQL_EXECUTION_ENABLED', IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED = 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED', IS_CONNECTED_ACCOUNT_MIGRATED = 'IS_CONNECTED_ACCOUNT_MIGRATED', IS_RECORD_TABLE_WIDGET_ENABLED = 'IS_RECORD_TABLE_WIDGET_ENABLED',