Remove direct execution feature flag - WIP (#19254)
Bug fixes exposed by always-on direct execution
1. GraphQL spec compliance — data[field] = null on resolver error
direct-execution.service.ts — Changed from Promise.allSettled (which
lost the responseKey on rejection) to Promise.all with per-field
try/catch; errors now set data[responseKey] = null per spec
2. Empty object arguments skipped (extractArgumentsFromAst)
extract-arguments-from-ast.util.ts — Removed isEmptyObject check;
filter: {}, data: {} now correctly passed to resolvers instead of
silently dropped (which caused permissions to never be checked)
3. orderBy: {} factory default treated as "no ordering"
direct-execution.service.ts — Before calling the resolver, strips
orderBy: {} and orderByForRecords: {} (empty-object factory defaults
that mean "no ordering")
assert-find-many-args.util.ts / assert-group-by-args.util.ts — Accept {}
for orderBy without throwing
4. orderBy: { field: '...' } object auto-coerced to [{ field: '...' }]
array
direct-execution.service.ts — Applies GraphQL list coercion: a
non-array, non-empty orderBy object is wrapped in an array before
assertion and resolver call
5. totalCount and aggregate fields returned as strings from PostgreSQL
graphql-format-result-from-selected-fields.util.ts — Added
coerceAggregateValue that parses numeric strings to numbers for
totalCount, sum*, avg*, min*, max*, count*, percentageOf* fields
Test updates
nested-relation-queries.integration-spec.ts — Updated expected error
message from Yoga schema-validation message to direct execution resolver
message
~30 snapshot files — Updated to reflect direct execution's error
messages (different from Yoga schema-validation messages for input type
errors)
This commit is contained in:
@@ -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';
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
+10
-1
@@ -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: {
|
||||
|
||||
+1
-1
@@ -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',
|
||||
|
||||
+38
-1
@@ -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<string, string>,
|
||||
fieldIdByJoinColumnName: Record<string, string>,
|
||||
): Record<string, unknown> {
|
||||
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,
|
||||
|
||||
+10
@@ -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;
|
||||
}
|
||||
|
||||
+24
@@ -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`,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
+27
-18
@@ -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<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<Output> {
|
||||
): Promise<CommonQueryExecutionResult<Output, Args>> {
|
||||
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<Args>;
|
||||
|
||||
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>,
|
||||
_args: CommonExtendedInput<Args>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): number {
|
||||
const simpleFieldsComplexity = 1;
|
||||
@@ -387,7 +396,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
private validateQueryComplexity(
|
||||
selectedFieldsResult: CommonSelectedFieldsResult,
|
||||
args: CommonInput<Args>,
|
||||
args: CommonExtendedInput<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
const maximumComplexity = this.twentyConfigService.get(
|
||||
|
||||
+1
-1
@@ -318,7 +318,7 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
|
||||
protected override computeQueryComplexity(
|
||||
selectedFieldsResult: CommonSelectedFieldsResult,
|
||||
args: CommonInput<FindManyQueryArgs>,
|
||||
args: CommonExtendedInput<FindManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): number {
|
||||
const baseComplexity = super.computeQueryComplexity(
|
||||
|
||||
@@ -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<Args>;
|
||||
};
|
||||
|
||||
+51
-72
@@ -203,74 +203,73 @@ export class DirectExecutionService {
|
||||
const variables = req.body.variables ?? {};
|
||||
const data: Record<string, unknown> = {};
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
-11
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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(
|
||||
|
||||
+1
-1
@@ -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(
|
||||
|
||||
+8
-2
@@ -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,
|
||||
|
||||
+1
-1
@@ -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(
|
||||
|
||||
+11
-3
@@ -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',
|
||||
|
||||
+1
-1
@@ -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(
|
||||
|
||||
+1
-1
@@ -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(
|
||||
|
||||
+4
-3
@@ -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;
|
||||
|
||||
+22
-4
@@ -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<string, unknown>)[key] ?? null;
|
||||
|
||||
formatted[key] =
|
||||
isNonEmptyString(rawAggregateValue) && isFinite(Number(rawAggregateValue))
|
||||
? Number(rawAggregateValue)
|
||||
: rawAggregateValue;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
|
||||
+13
-1
@@ -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<YogaDriverConfig<'express'>>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ export class FindDuplicatesResolverFactory
|
||||
});
|
||||
|
||||
try {
|
||||
const paginatedDuplicates =
|
||||
const { results: paginatedDuplicates } =
|
||||
await this.commonFindDuplicatesQueryRunnerService.execute(
|
||||
{ ...args, selectedFields },
|
||||
resolverContext,
|
||||
|
||||
+9
-6
@@ -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,
|
||||
});
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+1
-1
@@ -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,
|
||||
);
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
+5
-4
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ export class RestApiFindDuplicatesHandler extends RestApiBaseHandler {
|
||||
authContext,
|
||||
});
|
||||
|
||||
const duplicateConnections =
|
||||
const { results: duplicateConnections } =
|
||||
await this.commonFindDuplicatesQueryRunnerService.execute(
|
||||
{ data, ids, selectedFields },
|
||||
{
|
||||
|
||||
+15
-14
@@ -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,
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+11
-10
@@ -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) {
|
||||
|
||||
+8
-7
@@ -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}`,
|
||||
|
||||
+8
-7
@@ -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}`);
|
||||
|
||||
|
||||
+16
-14
@@ -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}`,
|
||||
|
||||
+3
-1
@@ -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,
|
||||
|
||||
+5
-4
@@ -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}`,
|
||||
|
||||
+9
-8
@@ -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}`);
|
||||
|
||||
|
||||
+9
-8
@@ -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}`);
|
||||
|
||||
|
||||
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
-135
@@ -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);
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+2
-2
@@ -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\\""]"`;
|
||||
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+1
-1
@@ -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""`;
|
||||
|
||||
|
||||
+15
-5
@@ -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."]"`;
|
||||
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+4
-4
@@ -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""`;
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+1
-1
@@ -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""`;
|
||||
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+2
-2
@@ -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\\""]"`;
|
||||
|
||||
|
||||
+1
-1
@@ -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"`;
|
||||
|
||||
|
||||
+2
-2
@@ -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\\""]"`;
|
||||
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+1
-1
@@ -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\\""]"`;
|
||||
|
||||
+4
-4
@@ -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`] = `
|
||||
[
|
||||
|
||||
+3
-3
@@ -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`] = `
|
||||
[
|
||||
|
||||
+4
-4
@@ -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`] = `
|
||||
[
|
||||
|
||||
+3
-3
@@ -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",
|
||||
]
|
||||
`;
|
||||
|
||||
|
||||
+3
-3
@@ -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`] = `
|
||||
[
|
||||
|
||||
+4
-4
@@ -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`] = `
|
||||
[
|
||||
|
||||
+5
-5
@@ -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`] = `
|
||||
[
|
||||
|
||||
+4
-4
@@ -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"`;
|
||||
|
||||
+5
-5
@@ -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",
|
||||
]
|
||||
`;
|
||||
|
||||
|
||||
+4
-4
@@ -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`] = `
|
||||
[
|
||||
|
||||
+6
-6
@@ -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`] = `
|
||||
[
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+4
@@ -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 =
|
||||
|
||||
+2
-1
@@ -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,
|
||||
},
|
||||
|
||||
-14
@@ -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",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
+20
-20
@@ -80,6 +80,26 @@ exports[`Page layout widget restore via bulk update should succeed should handle
|
||||
"title": "Test Tab For Widget Restore",
|
||||
"updatedAt": Any<String>,
|
||||
"widgets": [
|
||||
{
|
||||
"configuration": {
|
||||
"configurationType": "IFRAME",
|
||||
"url": "https://updated.example.com",
|
||||
},
|
||||
"createdAt": Any<String>,
|
||||
"deletedAt": null,
|
||||
"gridPosition": {
|
||||
"column": 0,
|
||||
"columnSpan": 2,
|
||||
"row": 0,
|
||||
"rowSpan": 2,
|
||||
},
|
||||
"id": Any<String>,
|
||||
"objectMetadataId": null,
|
||||
"pageLayoutTabId": Any<String>,
|
||||
"title": "Updated Widget Title",
|
||||
"type": "IFRAME",
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
{
|
||||
"configuration": {
|
||||
"aggregateFieldMetadataId": Any<String>,
|
||||
@@ -130,26 +150,6 @@ exports[`Page layout widget restore via bulk update should succeed should handle
|
||||
"type": "IFRAME",
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
{
|
||||
"configuration": {
|
||||
"configurationType": "IFRAME",
|
||||
"url": "https://updated.example.com",
|
||||
},
|
||||
"createdAt": Any<String>,
|
||||
"deletedAt": null,
|
||||
"gridPosition": {
|
||||
"column": 0,
|
||||
"columnSpan": 2,
|
||||
"row": 0,
|
||||
"rowSpan": 2,
|
||||
},
|
||||
"id": Any<String>,
|
||||
"objectMetadataId": null,
|
||||
"pageLayoutTabId": Any<String>,
|
||||
"title": "Updated Widget Title",
|
||||
"type": "IFRAME",
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user