Compare commits

...
Author SHA1 Message Date
Etienne 2c62fedf3d wip 2026-02-23 09:46:36 +01:00
19 changed files with 464 additions and 74 deletions
@@ -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
@@ -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
@@ -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'];
@@ -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;
}
}
}
@@ -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];
@@ -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()
@@ -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,
),
}),
};
}
@@ -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,
),
}),
};
}
@@ -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(
@@ -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,
),
}),
};
}
@@ -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,
),
}),
};
}
@@ -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,
),
}),
};
}
@@ -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,
),
}),
};
}
@@ -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],
@@ -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',
@@ -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:
@@ -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:
@@ -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]: [
{
@@ -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]: [
// {