Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c62fedf3d |
@@ -1,9 +1,10 @@
|
||||
import dotenv from 'dotenv';
|
||||
import { type JestConfigWithTsJest, pathsToModuleNameMapper } from 'ts-jest';
|
||||
|
||||
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
|
||||
import tsConfig from './tsconfig.json' with { type: 'json' };
|
||||
|
||||
import testTokens from './test/integration/constants/test-tokens.json';
|
||||
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
|
||||
import testTokens from './test/integration/constants/test-tokens.json' with { type: 'json' };
|
||||
|
||||
// Load .env vars at jest boot time
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
@@ -15,8 +16,6 @@ if (process.env.NODE_ENV === 'test') {
|
||||
const isBillingEnabled = process.env.IS_BILLING_ENABLED === 'true';
|
||||
const isClickhouseEnabled = process.env.CLICKHOUSE_URL !== undefined;
|
||||
|
||||
const tsConfig = require('./tsconfig.json');
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
// For more information please have a look to official docs https://jestjs.io/docs/configuration/#prettierpath-string
|
||||
// Prettier v3 should be supported in jest v30 https://github.com/jestjs/jest/releases/tag/v30.0.0-alpha.1
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
import { DataArgProcessor } from 'src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor';
|
||||
import { FilterArgProcessor } from 'src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg.processor';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/common/common-args-processors/query-runner-args.factory';
|
||||
|
||||
export const CommonArgsProcessors = [DataArgProcessor, QueryRunnerArgsFactory]; // TODO: Refacto-common Remove QueryRunnerArgsFactory
|
||||
export const CommonArgsProcessors = [
|
||||
DataArgProcessor,
|
||||
FilterArgProcessor,
|
||||
QueryRunnerArgsFactory,
|
||||
]; // TODO: Refacto-common Remove QueryRunnerArgsFactory
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { type FilterOperator } from 'src/engine/api/common/common-args-processors/filter-arg-processor/types/filter-operator.type';
|
||||
|
||||
export const SUPPORTED_FILTER_OPERATORS = [
|
||||
'eq',
|
||||
'neq',
|
||||
'gt',
|
||||
'gte',
|
||||
'lt',
|
||||
'lte',
|
||||
'in',
|
||||
'is',
|
||||
'like',
|
||||
'ilike',
|
||||
'startsWith',
|
||||
'endsWith',
|
||||
'contains',
|
||||
'notContains',
|
||||
'containsAny',
|
||||
'containsIlike',
|
||||
'search',
|
||||
'isEmptyArray',
|
||||
] as const;
|
||||
|
||||
export const ARRAY_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'in',
|
||||
'contains',
|
||||
'notContains',
|
||||
'containsAny',
|
||||
];
|
||||
|
||||
export const STRING_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'eq',
|
||||
'neq',
|
||||
'in',
|
||||
'is',
|
||||
'like',
|
||||
'ilike',
|
||||
'startsWith',
|
||||
'endsWith',
|
||||
'search',
|
||||
];
|
||||
|
||||
export const NUMBER_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'eq',
|
||||
'neq',
|
||||
'gt',
|
||||
'gte',
|
||||
'lt',
|
||||
'lte',
|
||||
'in',
|
||||
'is',
|
||||
];
|
||||
|
||||
export const BOOLEAN_FILTER_OPERATORS: FilterOperator[] = ['eq', 'is'];
|
||||
|
||||
export const DATE_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'eq',
|
||||
'neq',
|
||||
'gt',
|
||||
'gte',
|
||||
'lt',
|
||||
'lte',
|
||||
'is',
|
||||
];
|
||||
|
||||
export const UUID_FILTER_OPERATORS: FilterOperator[] = ['eq', 'neq', 'in', 'is'];
|
||||
|
||||
export const ARRAY_FIELD_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'contains',
|
||||
'notContains',
|
||||
'containsAny',
|
||||
'containsIlike',
|
||||
'isEmptyArray',
|
||||
'is',
|
||||
];
|
||||
|
||||
export const SELECT_FILTER_OPERATORS: FilterOperator[] = ['eq', 'neq', 'in', 'is'];
|
||||
|
||||
export const JSON_FILTER_OPERATORS: FilterOperator[] = ['is'];
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
compositeTypeDefinitions,
|
||||
FieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import {
|
||||
ARRAY_FIELD_FILTER_OPERATORS,
|
||||
BOOLEAN_FILTER_OPERATORS,
|
||||
DATE_FILTER_OPERATORS,
|
||||
JSON_FILTER_OPERATORS,
|
||||
NUMBER_FILTER_OPERATORS,
|
||||
SELECT_FILTER_OPERATORS,
|
||||
STRING_FILTER_OPERATORS,
|
||||
UUID_FILTER_OPERATORS,
|
||||
} from 'src/engine/api/common/common-args-processors/filter-arg-processor/constants/filter-operators.constant';
|
||||
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';
|
||||
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 { 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 { 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 { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class FilterArgProcessor {
|
||||
process<T extends ObjectRecordFilter | undefined>({
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
filter: T;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): T {
|
||||
if (!isDefined(filter)) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
const { fieldIdByName, fieldIdByJoinColumnName } =
|
||||
buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
this.validateFilterRecursive(
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private validateFilterRecursive(
|
||||
filterObject: ObjectRecordFilter,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
fieldIdByName: Record<string, string>,
|
||||
fieldIdByJoinColumnName: Record<string, string>,
|
||||
): void {
|
||||
for (const [key, value] of Object.entries(filterObject)) {
|
||||
if (key === 'and' || key === 'or') {
|
||||
for (const nestedFilter of value as ObjectRecordFilter[]) {
|
||||
this.validateFilterRecursive(
|
||||
nestedFilter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === 'not') {
|
||||
this.validateFilterRecursive(
|
||||
value as ObjectRecordFilter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.validateFieldFilter(
|
||||
key,
|
||||
value,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private validateFieldFilter(
|
||||
key: string,
|
||||
filterValue: unknown,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
fieldIdByName: Record<string, string>,
|
||||
fieldIdByJoinColumnName: Record<string, string>,
|
||||
): void {
|
||||
const fieldMetadataId = fieldIdByName[key] || fieldIdByJoinColumnName[key];
|
||||
|
||||
if (!isDefined(fieldMetadataId)) {
|
||||
const nameSingular = flatObjectMetadata.nameSingular;
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Object ${flatObjectMetadata.nameSingular} doesn't have any "${key}" field.`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid filter : ${nameSingular} object doesn't have any "${key}" field.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const fieldMetadata = flatFieldMetadataMaps.byId[fieldMetadataId];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Field metadata not found for field ${key}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
this.validateCompositeFieldFilter(
|
||||
fieldMetadata,
|
||||
filterValue as Record<string, unknown>,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.validateOperatorAndValue(
|
||||
key,
|
||||
filterValue as Record<string, unknown>,
|
||||
fieldMetadata,
|
||||
);
|
||||
}
|
||||
|
||||
private validateCompositeFieldFilter(
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
filterValue: Record<string, unknown>,
|
||||
): void {
|
||||
const compositeType = compositeTypeDefinitions.get(
|
||||
fieldMetadata.type as CompositeFieldMetadataType,
|
||||
);
|
||||
|
||||
if (!compositeType) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Composite type definition not found for type: ${fieldMetadata.type}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
for (const [subFieldKey, subFieldFilter] of Object.entries(filterValue)) {
|
||||
const subFieldMetadata = compositeType.properties.find(
|
||||
(property) => property.name === subFieldKey,
|
||||
);
|
||||
|
||||
if (!subFieldMetadata) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Sub field "${subFieldKey}" not found for composite type: ${fieldMetadata.type}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
this.validateOperatorAndValue(
|
||||
`${fieldMetadata.name}.${subFieldKey}`,
|
||||
subFieldFilter as Record<string, unknown>,
|
||||
{ ...fieldMetadata, type: subFieldMetadata.type as FieldMetadataType },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private validateOperatorAndValue(
|
||||
fieldName: string,
|
||||
filterValue: Record<string, unknown>,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
): void {
|
||||
const entries = Object.entries(filterValue);
|
||||
|
||||
if (entries.length === 0) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Filter for field "${fieldName}" must have at least one operator`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: msg`Invalid filter for field "${fieldName}"` },
|
||||
);
|
||||
}
|
||||
|
||||
for (const [operator, value] of entries) {
|
||||
this.validateOperatorForFieldType(
|
||||
operator as FilterOperator,
|
||||
fieldMetadata,
|
||||
fieldName,
|
||||
);
|
||||
|
||||
this.validateValueFormat(operator as FilterOperator, value, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
private validateOperatorForFieldType(
|
||||
operator: FilterOperator,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
fieldName: string,
|
||||
): void {
|
||||
const allowedOperators = this.getOperatorsForFieldType(fieldMetadata.type);
|
||||
|
||||
if (!allowedOperators.includes(operator)) {
|
||||
const fieldType = fieldMetadata.type;
|
||||
const allowedOperatorsString = allowedOperators.join(', ');
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Operator "${operator}" is not valid for field "${fieldName}" of type ${fieldType} - Allowed operators: ${allowedOperatorsString}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid filter : Operator "${operator}" is not valid for this "${fieldName}" ${fieldType} field`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private getOperatorsForFieldType(
|
||||
fieldType: FieldMetadataType,
|
||||
): FilterOperator[] {
|
||||
switch (fieldType) {
|
||||
case FieldMetadataType.TEXT:
|
||||
case FieldMetadataType.RICH_TEXT:
|
||||
return STRING_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.NUMBER:
|
||||
case FieldMetadataType.NUMERIC:
|
||||
case FieldMetadataType.POSITION:
|
||||
return NUMBER_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.BOOLEAN:
|
||||
return BOOLEAN_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.DATE:
|
||||
case FieldMetadataType.DATE_TIME:
|
||||
return DATE_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.UUID:
|
||||
case FieldMetadataType.RELATION:
|
||||
case FieldMetadataType.MORPH_RELATION:
|
||||
return UUID_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.ARRAY:
|
||||
case FieldMetadataType.MULTI_SELECT:
|
||||
return ARRAY_FIELD_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.SELECT:
|
||||
case FieldMetadataType.RATING:
|
||||
return SELECT_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.RAW_JSON:
|
||||
return JSON_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.TS_VECTOR:
|
||||
return ['search', 'is'];
|
||||
|
||||
default:
|
||||
return ['eq', 'neq', 'is'];
|
||||
}
|
||||
}
|
||||
|
||||
private validateValueFormat(
|
||||
operator: FilterOperator,
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): void {
|
||||
if (operator === 'is') {
|
||||
if (value !== 'NULL' && value !== 'NOT_NULL') {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid filter value for "${operator}" operator on field ${fieldName}. Expected "NULL" or "NOT_NULL"`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: msg`Invalid value for "is" operator` },
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { type SUPPORTED_FILTER_OPERATORS } from 'src/engine/api/common/common-args-processors/filter-arg-processor/constants/filter-operators.constant';
|
||||
|
||||
export type FilterOperator = (typeof SUPPORTED_FILTER_OPERATORS)[number];
|
||||
+2
-2
@@ -7,7 +7,7 @@ import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace
|
||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
|
||||
import { DataArgProcessor } from 'src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/common/common-args-processors/query-runner-args.factory';
|
||||
import { FilterArgProcessor } from 'src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg.processor';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
@@ -59,7 +59,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
@Inject()
|
||||
protected readonly workspaceQueryHookService: WorkspaceQueryHookService;
|
||||
@Inject()
|
||||
protected readonly queryRunnerArgsFactory: QueryRunnerArgsFactory;
|
||||
protected readonly filterArgProcessor: FilterArgProcessor;
|
||||
@Inject()
|
||||
protected readonly dataArgProcessor: DataArgProcessor;
|
||||
@Inject()
|
||||
|
||||
+3
-3
@@ -104,11 +104,11 @@ export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -105,11 +105,11 @@ export class CommonDestroyManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import {
|
||||
@@ -11,6 +11,7 @@ import { FindOptionsRelations, In, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/common/common-args-processors/query-runner-args.factory';
|
||||
import { CommonBaseQueryRunnerService } from 'src/engine/api/common/common-query-runners/common-base-query-runner.service';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
@@ -39,6 +40,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
FindDuplicatesQueryArgs,
|
||||
CommonFindDuplicatesOutputItem[]
|
||||
> {
|
||||
@Inject()
|
||||
private readonly queryRunnerArgsFactory: QueryRunnerArgsFactory;
|
||||
|
||||
protected readonly operationName = CommonQueryNames.FIND_DUPLICATES;
|
||||
|
||||
async run(
|
||||
|
||||
+3
-3
@@ -231,11 +231,11 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -120,11 +120,11 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -399,11 +399,11 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -105,11 +105,11 @@ export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -106,11 +106,11 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
filter: this.filterArgProcessor.process({
|
||||
filter: args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
}),
|
||||
data: (
|
||||
await this.dataArgProcessor.process({
|
||||
partialRecordInputs: [args.data],
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ export enum CommonQueryRunnerExceptionCode {
|
||||
INVALID_AUTH_CONTEXT = 'INVALID_AUTH_CONTEXT',
|
||||
ARGS_CONFLICT = 'ARGS_CONFLICT',
|
||||
INVALID_ARGS_DATA = 'INVALID_ARGS_DATA',
|
||||
INVALID_ARGS_FILTER = 'INVALID_ARGS_FILTER',
|
||||
INVALID_ARGS_FIRST = 'INVALID_ARGS_FIRST',
|
||||
INVALID_ARGS_LAST = 'INVALID_ARGS_LAST',
|
||||
UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT = 'UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT',
|
||||
|
||||
+7
-6
@@ -1,14 +1,14 @@
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
type CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
type CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import {
|
||||
AuthenticationError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
AuthenticationError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
export const commonQueryRunnerToGraphqlApiExceptionHandler = (
|
||||
@@ -22,6 +22,7 @@ export const commonQueryRunnerToGraphqlApiExceptionHandler = (
|
||||
case CommonQueryRunnerExceptionCode.INVALID_ARGS_LAST:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER:
|
||||
case CommonQueryRunnerExceptionCode.UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_CURSOR:
|
||||
case CommonQueryRunnerExceptionCode.TOO_MANY_RECORDS_TO_UPDATE:
|
||||
|
||||
+7
-6
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
UnauthorizedException,
|
||||
BadRequestException,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerExceptionCode,
|
||||
type CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
type CommonQueryRunnerException,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const commonQueryRunnerToRestApiExceptionHandler = (
|
||||
@@ -21,6 +21,7 @@ export const commonQueryRunnerToRestApiExceptionHandler = (
|
||||
case CommonQueryRunnerExceptionCode.INVALID_ARGS_LAST:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER:
|
||||
case CommonQueryRunnerExceptionCode.UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT:
|
||||
case CommonQueryRunnerExceptionCode.INVALID_CURSOR:
|
||||
case CommonQueryRunnerExceptionCode.TOO_MANY_RECORDS_TO_UPDATE:
|
||||
|
||||
+12
@@ -195,6 +195,18 @@ export const failingFilterInputByFieldMetadataType: {
|
||||
restFilterInput: 'dateField[eq]:"[]"',
|
||||
restErrorMessage: 'invalid input syntax for type date',
|
||||
},
|
||||
{
|
||||
gqlFilterInput: { dateField: { eq: null } },
|
||||
gqlErrorMessage: 'invalid input syntax for type date: "null"',
|
||||
restFilterInput: 'dateField[eq]:"null"',
|
||||
restErrorMessage: 'invalid input syntax for type date',
|
||||
},
|
||||
{
|
||||
gqlFilterInput: { dateField: { eq: 2 } },
|
||||
gqlErrorMessage: 'invalid input syntax for type date: "2"',
|
||||
restFilterInput: 'dateField[eq]:2',
|
||||
restErrorMessage: 'invalid input syntax for type date',
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.BOOLEAN]: [
|
||||
{
|
||||
|
||||
+15
-33
@@ -614,14 +614,13 @@ export const successfulFilterInputByFieldMetadataType: {
|
||||
// },
|
||||
],
|
||||
[FieldMetadataType.DATE]: [
|
||||
// TODO - fix this ? not working
|
||||
// {
|
||||
// gqlFilterInput: { dateField: { eq: '2025-01-01' } },
|
||||
// restFilterInput: 'dateField[eq]=2025-01-01',
|
||||
// validateFilter: (record: Record<string, any>) => {
|
||||
// return record.dateField === '2025-01-01';
|
||||
// },
|
||||
// },
|
||||
{
|
||||
gqlFilterInput: { dateField: { eq: '2025-01-01' } },
|
||||
restFilterInput: 'dateField[eq]=2025-01-01',
|
||||
validateFilter: (record: Record<string, any>) => {
|
||||
return record.dateField === '2025-01-01';
|
||||
},
|
||||
},
|
||||
{
|
||||
gqlFilterInput: { dateField: { neq: '2005-01-01' } },
|
||||
restFilterInput: 'dateField[neq]:"2005-01-01"',
|
||||
@@ -643,14 +642,13 @@ export const successfulFilterInputByFieldMetadataType: {
|
||||
return record.dateField >= '2005-01-01';
|
||||
},
|
||||
},
|
||||
// TODO - fix this ? not working
|
||||
// {
|
||||
// gqlFilterInput: { dateField: { in: ['2025-01-01'] } },
|
||||
// restFilterInput: 'dateField[in]=2025-01-01',
|
||||
// validateFilter: (record: Record<string, any>) => {
|
||||
// return record.dateField === '2025-01-01';
|
||||
// },
|
||||
// },
|
||||
{
|
||||
gqlFilterInput: { dateField: { in: ['2025-01-01'] } },
|
||||
restFilterInput: 'dateField[in]=2025-01-01',
|
||||
validateFilter: (record: Record<string, any>) => {
|
||||
return record.dateField === '2025-01-01';
|
||||
},
|
||||
},
|
||||
{
|
||||
gqlFilterInput: { dateField: { lt: '2125-01-01' } },
|
||||
restFilterInput: 'dateField[lt]:"2125-01-01"',
|
||||
@@ -667,7 +665,7 @@ export const successfulFilterInputByFieldMetadataType: {
|
||||
},
|
||||
{
|
||||
gqlFilterInput: { dateField: { is: 'NOT_NULL' } },
|
||||
restFilterInput: 'dateField[is]:"NOT_NULL"',
|
||||
restFilterInput: 'dateField[is]:NOT_NULL',
|
||||
validateFilter: (record: Record<string, any>) => {
|
||||
return isDefined(record.dateField);
|
||||
},
|
||||
@@ -679,22 +677,6 @@ export const successfulFilterInputByFieldMetadataType: {
|
||||
return record.dateField === null;
|
||||
},
|
||||
},
|
||||
// TODO - fix this, should be returning or not be allowed
|
||||
// {
|
||||
// gqlFilterInput: { dateField: { eq: null } },
|
||||
// restFilterInput: 'dateField[eq]=null',
|
||||
// validateFilter: (record: Record<string, any>) => {
|
||||
// return record.dateField === null;
|
||||
// },
|
||||
// },
|
||||
// TODO - why float are allowed ?
|
||||
// {
|
||||
// gqlFilterInput: { dateField: { eq: 2 } },
|
||||
// restFilterInput: 'dateField[eq]=2',
|
||||
// validateFilter: (record: Record<string, any>) => {
|
||||
// return record.dateField === 2;
|
||||
// },
|
||||
// },
|
||||
],
|
||||
[FieldMetadataType.BOOLEAN]: [
|
||||
// {
|
||||
|
||||
Reference in New Issue
Block a user