Common API - Filter validation layer (#18187)

Closes https://github.com/twentyhq/core-team-issues/issues/1627

**FilterArgProcessor consolidation:**
Refactored to both validate AND transform filter values in a single pass
Coerced string inputs to native types (e.g., "1" → 1, "true" → true -
useful for Rest input)
Returns transformed filter instead of just validating
Removed overrideFilterByFieldMetadata calls from all computeArgs methods
**QueryRunnerArgsFactory cleanup**
**Testing:**
Add unit testing
uncomment integration tests
This commit is contained in:
Etienne
2026-03-04 13:10:44 +01:00
committed by GitHub
parent 80d054563e
commit 906a0aed38
82 changed files with 3068 additions and 1015 deletions
@@ -5,7 +5,8 @@ import { isDefined } from 'twenty-shared/utils';
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 { DataArgProcessorService } from 'src/engine/api/common/common-args-processors/data-arg-processor/data-arg-processor.service';
import { FilterArgProcessorService } from 'src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg-processor.service';
import { QueryRunnerArgsFactory } from 'src/engine/api/common/common-args-processors/query-runner-args.factory';
import { ProcessNestedRelationsHelper } from 'src/engine/api/common/common-nested-relations-processor/process-nested-relations.helper';
import {
@@ -63,7 +64,9 @@ export abstract class CommonBaseQueryRunnerService<
@Inject()
protected readonly queryRunnerArgsFactory: QueryRunnerArgsFactory;
@Inject()
protected readonly dataArgProcessor: DataArgProcessor;
protected readonly dataArgProcessor: DataArgProcessorService;
@Inject()
protected readonly filterArgProcessor: FilterArgProcessorService;
@Inject()
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager;
@Inject()
@@ -103,11 +103,11 @@ export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerSer
return {
...args,
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
args.filter,
filter: this.filterArgProcessor.process({
filter: args.filter,
flatObjectMetadata,
flatFieldMetadataMaps,
),
}),
};
}
@@ -104,11 +104,11 @@ export class CommonDestroyManyQueryRunnerService extends CommonBaseQueryRunnerSe
return {
...args,
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
args.filter,
filter: this.filterArgProcessor.process({
filter: args.filter,
flatObjectMetadata,
flatFieldMetadataMaps,
),
}),
};
}
@@ -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,
),
}),
};
}
@@ -402,11 +402,11 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
return {
...args,
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
args.filter,
filter: this.filterArgProcessor.process({
filter: args.filter,
flatObjectMetadata,
flatFieldMetadataMaps,
),
}),
};
}
@@ -104,11 +104,11 @@ export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerSe
return {
...args,
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
args.filter,
filter: this.filterArgProcessor.process({
filter: args.filter,
flatObjectMetadata,
flatFieldMetadataMaps,
),
}),
};
}
@@ -105,11 +105,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',
@@ -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:
@@ -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: