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:
+7
-2
@@ -1,4 +1,9 @@
|
||||
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';
|
||||
|
||||
export const CommonArgsProcessors = [DataArgProcessor, QueryRunnerArgsFactory]; // TODO: Refacto-common Remove QueryRunnerArgsFactory
|
||||
export const CommonArgsProcessors = [
|
||||
DataArgProcessorService,
|
||||
FilterArgProcessorService,
|
||||
QueryRunnerArgsFactory,
|
||||
]; // TODO: Refacto-common Remove QueryRunnerArgsFactory
|
||||
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation ADDRESS should throw for invalid input #1: "not-an-address" 1`] = `"Invalid object value 'not-an-address' for field "addressField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation ADDRESS should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "addressField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation ADDRESS should throw for invalid input #3: true 1`] = `"Invalid object value true for field "addressField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation ARRAY should throw for invalid input #1: true 1`] = `"Invalid value true for field "arrayField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation ARRAY should throw for invalid input #2: 1 1`] = `"Invalid value 1 for field "arrayField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation BOOLEAN should throw for invalid input #1: {} 1`] = `"Invalid boolean value {} for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation BOOLEAN should throw for invalid input #2: [] 1`] = `"Invalid boolean value [] for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation BOOLEAN should throw for invalid input #3: "string" 1`] = `"Invalid boolean value 'string' for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation BOOLEAN should throw for invalid input #4: 1 1`] = `"Invalid boolean value 1 for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation CURRENCY should throw for invalid input #1: "not-a-currency" 1`] = `"Invalid object value 'not-a-currency' for field "currencyField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation CURRENCY should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "currencyField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation CURRENCY should throw for invalid input #3: true 1`] = `"Invalid object value true for field "currencyField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #1: "malformed-date" 1`] = `"Invalid value 'malformed-date' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #2: {} 1`] = `"Invalid value {} for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #3: [] 1`] = `"Invalid value [] for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #4: true 1`] = `"Invalid value true for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #5: 1 1`] = `"Invalid value 1 for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #6: "2024" 1`] = `"Invalid value '2024' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #7: "2024-01" 1`] = `"Invalid value '2024-01' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #8: "2024-13-01" 1`] = `"Invalid value '2024-13-01' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE should throw for invalid input #9: "2024-02-31" 1`] = `"Invalid value '2024-02-31' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #1: "malformed-date" 1`] = `"Invalid value 'malformed-date' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #2: {} 1`] = `"Invalid value {} for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #3: [] 1`] = `"Invalid value [] for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #4: true 1`] = `"Invalid value true for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #5: 1 1`] = `"Invalid value 1 for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #6: "2024" 1`] = `"Invalid value '2024' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #7: "2024-01" 1`] = `"Invalid value '2024-01' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #8: "2024-13-01T10:30:00Z" 1`] = `"Invalid value '2024-13-01T10:30:00Z' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation DATE_TIME should throw for invalid input #9: "2024-01-15T25:30:00Z" 1`] = `"Invalid value '2024-01-15T25:30:00Z' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #1: "not-an-email" 1`] = `"Invalid object value 'not-an-email' for field "emailsField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #2: {"primaryEmail":"not-an-email"} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.primaryEmail""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #3: {"additionalEmails":"not-an-email"} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #4: {"additionalEmails":["not-an-email"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #5: {"primaryEmail":"email@email.com","additionalEmails":["not-an-email"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #6: {"primaryEmail":"not-an-email","additionalEmails":["additional@email.com"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.primaryEmail""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation EMAILS should throw for invalid input #7: {"additionalEmails":["not-an-email","additional@email.com"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation FILES should throw for invalid input #1: "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[`DataArgProcessorService failing inputs validation FILES should throw for invalid input #2: {"addFiles":[{"invalidField":"test"}]} 1`] = `"Invalid value "{ addFiles: [ { invalidField: 'test' } ] }" for FILES field "filesField" - : Invalid input: expected array, received object"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation FILES should throw for invalid input #3: {"addFiles":[{"fileId":"not-a-uuid","label":"Doc.pdf"}]} 1`] = `"Invalid value "{ addFiles: [ { fileId: 'not-a-uuid', label: 'Doc.pdf' } ] }" for FILES field "filesField" - : Invalid input: expected array, received object"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation FILES should throw for invalid input #4: [{"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[`DataArgProcessorService failing inputs validation FILES should throw for invalid input #5: {"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[`DataArgProcessorService failing inputs validation FULL_NAME should throw for invalid input #1: "not-a-full-name" 1`] = `"Invalid object value 'not-a-full-name' for field "fullNameField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation FULL_NAME should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "fullNameField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation FULL_NAME should throw for invalid input #3: true 1`] = `"Invalid object value true for field "fullNameField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation LINKS should throw for invalid input #1: "not-a-link" 1`] = `"Invalid object value 'not-a-link' for field "linksField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation LINKS should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "linksField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation LINKS should throw for invalid input #3: true 1`] = `"Invalid object value true for field "linksField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MORPH_RELATION should throw for invalid input #1: "not-a-morph-relation" 1`] = `"Invalid UUID value 'not-a-morph-relation' for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MORPH_RELATION should throw for invalid input #2: {} 1`] = `"Invalid UUID value {} for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MORPH_RELATION should throw for invalid input #3: [] 1`] = `"Invalid UUID value [] for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MORPH_RELATION should throw for invalid input #4: true 1`] = `"Invalid UUID value true for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MORPH_RELATION should throw for invalid input #5: 1 1`] = `"Invalid UUID value 1 for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MULTI_SELECT should throw for invalid input #1: "not-a-select-option" 1`] = `"Invalid value 'not-a-select-option' for multi select field "multiSelectField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MULTI_SELECT should throw for invalid input #2: {} 1`] = `"Invalid value {} for field "multiSelectField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MULTI_SELECT should throw for invalid input #3: true 1`] = `"Invalid value true for field "multiSelectField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation MULTI_SELECT should throw for invalid input #4: 1 1`] = `"Invalid value 1 for field "multiSelectField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation NUMBER should throw for invalid input #1: {} 1`] = `"Invalid number value {} for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation NUMBER should throw for invalid input #2: [] 1`] = `"Invalid number value [] for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation NUMBER should throw for invalid input #3: true 1`] = `"Invalid number value true for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation NUMBER should throw for invalid input #4: "string" 1`] = `"Invalid number value 'string' for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation NUMERIC should throw for invalid input #1: {} 1`] = `"Invalid number value NaN for field "numericField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation NUMERIC should throw for invalid input #2: "not-a-number" 1`] = `"Invalid number value NaN for field "numericField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation PHONES should throw for invalid input #1: "not-a-phone" 1`] = `"Invalid object value 'not-a-phone' for field "phonesField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation PHONES should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "phonesField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation PHONES should throw for invalid input #3: true 1`] = `"Invalid object value true for field "phonesField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation POSITION should throw for invalid input #1: "not-a-position" 1`] = `"Invalid position value 'not-a-position' for field "position""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation POSITION should throw for invalid input #2: null 1`] = `"Invalid position value NaN for field "position""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation POSITION should throw for invalid input #3: {} 1`] = `"Invalid position value {} for field "position""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation POSITION should throw for invalid input #4: [] 1`] = `"Invalid position value [] for field "position""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RATING should throw for invalid input #1: "not-a-rating" 1`] = `"Invalid value 'not-a-rating' for field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RATING should throw for invalid input #2: {} 1`] = `"Invalid string value {} for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RATING should throw for invalid input #3: [] 1`] = `"Invalid string value [] for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RATING should throw for invalid input #4: true 1`] = `"Invalid string value true for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RATING should throw for invalid input #5: 1 1`] = `"Invalid string value 1 for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RAW_JSON should throw for invalid input #1: "not-a-json" 1`] = `"Invalid object value 'not-a-json' for field "rawJsonField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #1: {} 1`] = `"Invalid UUID value {} for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #2: [] 1`] = `"Invalid UUID value [] for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #3: true 1`] = `"Invalid UUID value true for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #4: 1 1`] = `"Invalid UUID value 1 for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #5: "non-uuid" 1`] = `"Invalid UUID value 'non-uuid' for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RICH_TEXT should throw for invalid input #1: "test" 1`] = `"richTextField RICH_TEXT-typed field does not support write operations"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RICH_TEXT_V2 should throw for invalid input #1: "not-a-rich-text" 1`] = `"Invalid rich text v2 value 'not-a-rich-text' for field "richTextV2Field" - Should be an object"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RICH_TEXT_V2 should throw for invalid input #2: 1 1`] = `"Invalid rich text v2 value 1 for field "richTextV2Field" - Should be an object"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation RICH_TEXT_V2 should throw for invalid input #3: true 1`] = `"Invalid rich text v2 value true for field "richTextV2Field" - Should be an object"`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #1: "not-a-select-option" 1`] = `"Invalid value 'not-a-select-option' for field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #2: {} 1`] = `"Invalid string value {} for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #3: [] 1`] = `"Invalid string value [] for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #4: true 1`] = `"Invalid string value true for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #5: 1 1`] = `"Invalid string value 1 for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation TEXT should throw for invalid input #1: {} 1`] = `"Invalid string value {} for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation TEXT should throw for invalid input #2: [] 1`] = `"Invalid string value [] for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation TEXT should throw for invalid input #3: true 1`] = `"Invalid string value true for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation TEXT should throw for invalid input #4: 1 1`] = `"Invalid string value 1 for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation UUID should throw for invalid input #1: {} 1`] = `"Invalid UUID value {} for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation UUID should throw for invalid input #2: [] 1`] = `"Invalid UUID value [] for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation UUID should throw for invalid input #3: true 1`] = `"Invalid UUID value true for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation UUID should throw for invalid input #4: 1 1`] = `"Invalid UUID value 1 for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessorService failing inputs validation UUID should throw for invalid input #5: "non-uuid" 1`] = `"Invalid UUID value 'non-uuid' for field "uuidField""`;
|
||||
-209
@@ -1,209 +0,0 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation ADDRESS should throw for invalid input #1: "not-an-address" 1`] = `"Invalid object value 'not-an-address' for field "addressField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation ADDRESS should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "addressField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation ADDRESS should throw for invalid input #3: true 1`] = `"Invalid object value true for field "addressField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation ARRAY should throw for invalid input #1: true 1`] = `"Invalid value true for field "arrayField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation ARRAY should throw for invalid input #2: 1 1`] = `"Invalid value 1 for field "arrayField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation BOOLEAN should throw for invalid input #1: {} 1`] = `"Invalid boolean value {} for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation BOOLEAN should throw for invalid input #2: [] 1`] = `"Invalid boolean value [] for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation BOOLEAN should throw for invalid input #3: "string" 1`] = `"Invalid boolean value 'string' for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation BOOLEAN should throw for invalid input #4: 1 1`] = `"Invalid boolean value 1 for field "booleanField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation CURRENCY should throw for invalid input #1: "not-a-currency" 1`] = `"Invalid object value 'not-a-currency' for field "currencyField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation CURRENCY should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "currencyField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation CURRENCY should throw for invalid input #3: true 1`] = `"Invalid object value true for field "currencyField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #1: "malformed-date" 1`] = `"Invalid value 'malformed-date' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #2: {} 1`] = `"Invalid value {} for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #3: [] 1`] = `"Invalid value [] for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #4: true 1`] = `"Invalid value true for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #5: 1 1`] = `"Invalid value 1 for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #6: "2024" 1`] = `"Invalid value '2024' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #7: "2024-01" 1`] = `"Invalid value '2024-01' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #8: "2024-13-01" 1`] = `"Invalid value '2024-13-01' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE should throw for invalid input #9: "2024-02-31" 1`] = `"Invalid value '2024-02-31' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #1: "malformed-date" 1`] = `"Invalid value 'malformed-date' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #2: {} 1`] = `"Invalid value {} for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #3: [] 1`] = `"Invalid value [] for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #4: true 1`] = `"Invalid value true for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #5: 1 1`] = `"Invalid value 1 for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #6: "2024" 1`] = `"Invalid value '2024' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #7: "2024-01" 1`] = `"Invalid value '2024-01' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #8: "2024-13-01T10:30:00Z" 1`] = `"Invalid value '2024-13-01T10:30:00Z' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation DATE_TIME should throw for invalid input #9: "2024-01-15T25:30:00Z" 1`] = `"Invalid value '2024-01-15T25:30:00Z' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #1: "not-an-email" 1`] = `"Invalid object value 'not-an-email' for field "emailsField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #2: {"primaryEmail":"not-an-email"} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.primaryEmail""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #3: {"additionalEmails":"not-an-email"} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #4: {"additionalEmails":["not-an-email"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #5: {"primaryEmail":"email@email.com","additionalEmails":["not-an-email"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #6: {"primaryEmail":"not-an-email","additionalEmails":["additional@email.com"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.primaryEmail""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation EMAILS should throw for invalid input #7: {"additionalEmails":["not-an-email","additional@email.com"]} 1`] = `"Invalid string value 'not-an-email' for email field "emailsField.additionalEmails""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation FILES should throw for invalid input #1: "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[`DataArgProcessor failing inputs validation FILES should throw for invalid input #2: {"addFiles":[{"invalidField":"test"}]} 1`] = `"Invalid value "{ addFiles: [ { invalidField: 'test' } ] }" for FILES field "filesField" - : Invalid input: expected array, received object"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation FILES should throw for invalid input #3: {"addFiles":[{"fileId":"not-a-uuid","label":"Doc.pdf"}]} 1`] = `"Invalid value "{ addFiles: [ { fileId: 'not-a-uuid', label: 'Doc.pdf' } ] }" for FILES field "filesField" - : Invalid input: expected array, received object"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation FILES should throw for invalid input #4: [{"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[`DataArgProcessor failing inputs validation FILES should throw for invalid input #5: {"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[`DataArgProcessor failing inputs validation FULL_NAME should throw for invalid input #1: "not-a-full-name" 1`] = `"Invalid object value 'not-a-full-name' for field "fullNameField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation FULL_NAME should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "fullNameField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation FULL_NAME should throw for invalid input #3: true 1`] = `"Invalid object value true for field "fullNameField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation LINKS should throw for invalid input #1: "not-a-link" 1`] = `"Invalid object value 'not-a-link' for field "linksField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation LINKS should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "linksField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation LINKS should throw for invalid input #3: true 1`] = `"Invalid object value true for field "linksField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MORPH_RELATION should throw for invalid input #1: "not-a-morph-relation" 1`] = `"Invalid UUID value 'not-a-morph-relation' for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MORPH_RELATION should throw for invalid input #2: {} 1`] = `"Invalid UUID value {} for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MORPH_RELATION should throw for invalid input #3: [] 1`] = `"Invalid UUID value [] for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MORPH_RELATION should throw for invalid input #4: true 1`] = `"Invalid UUID value true for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MORPH_RELATION should throw for invalid input #5: 1 1`] = `"Invalid UUID value 1 for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MULTI_SELECT should throw for invalid input #1: "not-a-select-option" 1`] = `"Invalid value 'not-a-select-option' for multi select field "multiSelectField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MULTI_SELECT should throw for invalid input #2: {} 1`] = `"Invalid value {} for field "multiSelectField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MULTI_SELECT should throw for invalid input #3: true 1`] = `"Invalid value true for field "multiSelectField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation MULTI_SELECT should throw for invalid input #4: 1 1`] = `"Invalid value 1 for field "multiSelectField - Array values need to be string""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation NUMBER should throw for invalid input #1: {} 1`] = `"Invalid number value {} for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation NUMBER should throw for invalid input #2: [] 1`] = `"Invalid number value [] for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation NUMBER should throw for invalid input #3: true 1`] = `"Invalid number value true for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation NUMBER should throw for invalid input #4: "string" 1`] = `"Invalid number value 'string' for field "numberField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation NUMERIC should throw for invalid input #1: {} 1`] = `"Invalid number value NaN for field "numericField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation NUMERIC should throw for invalid input #2: "not-a-number" 1`] = `"Invalid number value NaN for field "numericField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation PHONES should throw for invalid input #1: "not-a-phone" 1`] = `"Invalid object value 'not-a-phone' for field "phonesField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation PHONES should throw for invalid input #2: 1 1`] = `"Invalid object value 1 for field "phonesField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation PHONES should throw for invalid input #3: true 1`] = `"Invalid object value true for field "phonesField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation POSITION should throw for invalid input #1: "not-a-position" 1`] = `"Invalid position value 'not-a-position' for field "position""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation POSITION should throw for invalid input #2: null 1`] = `"Invalid position value NaN for field "position""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation POSITION should throw for invalid input #3: {} 1`] = `"Invalid position value {} for field "position""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation POSITION should throw for invalid input #4: [] 1`] = `"Invalid position value [] for field "position""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RATING should throw for invalid input #1: "not-a-rating" 1`] = `"Invalid value 'not-a-rating' for field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RATING should throw for invalid input #2: {} 1`] = `"Invalid string value {} for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RATING should throw for invalid input #3: [] 1`] = `"Invalid string value [] for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RATING should throw for invalid input #4: true 1`] = `"Invalid string value true for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RATING should throw for invalid input #5: 1 1`] = `"Invalid string value 1 for text field "ratingField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RAW_JSON should throw for invalid input #1: "not-a-json" 1`] = `"Invalid object value 'not-a-json' for field "rawJsonField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RELATION should throw for invalid input #1: {} 1`] = `"Invalid UUID value {} for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RELATION should throw for invalid input #2: [] 1`] = `"Invalid UUID value [] for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RELATION should throw for invalid input #3: true 1`] = `"Invalid UUID value true for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RELATION should throw for invalid input #4: 1 1`] = `"Invalid UUID value 1 for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RELATION should throw for invalid input #5: "non-uuid" 1`] = `"Invalid UUID value 'non-uuid' for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RICH_TEXT should throw for invalid input #1: "test" 1`] = `"richTextField RICH_TEXT-typed field does not support write operations"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RICH_TEXT_V2 should throw for invalid input #1: "not-a-rich-text" 1`] = `"Invalid rich text v2 value 'not-a-rich-text' for field "richTextV2Field" - Should be an object"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RICH_TEXT_V2 should throw for invalid input #2: 1 1`] = `"Invalid rich text v2 value 1 for field "richTextV2Field" - Should be an object"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation RICH_TEXT_V2 should throw for invalid input #3: true 1`] = `"Invalid rich text v2 value true for field "richTextV2Field" - Should be an object"`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation SELECT should throw for invalid input #1: "not-a-select-option" 1`] = `"Invalid value 'not-a-select-option' for field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation SELECT should throw for invalid input #2: {} 1`] = `"Invalid string value {} for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation SELECT should throw for invalid input #3: [] 1`] = `"Invalid string value [] for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation SELECT should throw for invalid input #4: true 1`] = `"Invalid string value true for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation SELECT should throw for invalid input #5: 1 1`] = `"Invalid string value 1 for text field "selectField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation TEXT should throw for invalid input #1: {} 1`] = `"Invalid string value {} for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation TEXT should throw for invalid input #2: [] 1`] = `"Invalid string value [] for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation TEXT should throw for invalid input #3: true 1`] = `"Invalid string value true for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation TEXT should throw for invalid input #4: 1 1`] = `"Invalid string value 1 for text field "textField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation UUID should throw for invalid input #1: {} 1`] = `"Invalid UUID value {} for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation UUID should throw for invalid input #2: [] 1`] = `"Invalid UUID value [] for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation UUID should throw for invalid input #3: true 1`] = `"Invalid UUID value true for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation UUID should throw for invalid input #4: 1 1`] = `"Invalid UUID value 1 for field "uuidField""`;
|
||||
|
||||
exports[`DataArgProcessor failing inputs validation UUID should throw for invalid input #5: "non-uuid" 1`] = `"Invalid UUID value 'non-uuid' for field "uuidField""`;
|
||||
+10
-8
@@ -2,7 +2,7 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
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 { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
@@ -21,8 +21,8 @@ jest.mock(
|
||||
}),
|
||||
);
|
||||
|
||||
describe('DataArgProcessor', () => {
|
||||
let dataArgProcessor: DataArgProcessor;
|
||||
describe('DataArgProcessorService', () => {
|
||||
let dataArgProcessorService: DataArgProcessorService;
|
||||
let recordPositionService: jest.Mocked<RecordPositionService>;
|
||||
|
||||
const mockWorkspaceId = '20202020-1234-1234-1234-123456789012';
|
||||
@@ -96,7 +96,7 @@ describe('DataArgProcessor', () => {
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
DataArgProcessor,
|
||||
DataArgProcessorService,
|
||||
{
|
||||
provide: RecordPositionService,
|
||||
useValue: recordPositionService,
|
||||
@@ -104,7 +104,9 @@ describe('DataArgProcessor', () => {
|
||||
],
|
||||
}).compile();
|
||||
|
||||
dataArgProcessor = module.get<DataArgProcessor>(DataArgProcessor);
|
||||
dataArgProcessorService = module.get<DataArgProcessorService>(
|
||||
DataArgProcessorService,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -112,7 +114,7 @@ describe('DataArgProcessor', () => {
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(dataArgProcessor).toBeDefined();
|
||||
expect(dataArgProcessorService).toBeDefined();
|
||||
});
|
||||
|
||||
describe('failing inputs validation', () => {
|
||||
@@ -140,7 +142,7 @@ describe('DataArgProcessor', () => {
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
await expect(
|
||||
dataArgProcessor.process({
|
||||
dataArgProcessorService.process({
|
||||
partialRecordInputs: [testCase.input],
|
||||
authContext: createMockAuthContext(),
|
||||
flatObjectMetadata,
|
||||
@@ -177,7 +179,7 @@ describe('DataArgProcessor', () => {
|
||||
createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const result = await dataArgProcessor.process({
|
||||
const result = await dataArgProcessorService.process({
|
||||
partialRecordInputs: [testCase.input],
|
||||
authContext: createMockAuthContext(),
|
||||
flatObjectMetadata,
|
||||
+1
-1
@@ -62,7 +62,7 @@ import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-module
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class DataArgProcessor {
|
||||
export class DataArgProcessorService {
|
||||
constructor(private readonly recordPositionService: RecordPositionService) {}
|
||||
|
||||
async process({
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isBoolean, isNull } from '@sniptt/guards';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
@@ -12,7 +12,7 @@ export const validateBooleanFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): boolean | null => {
|
||||
if (typeof value !== 'boolean' && !isNull(value)) {
|
||||
if (!isBoolean(value) && !isNull(value)) {
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isNull, isNumber } from '@sniptt/guards';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
@@ -13,8 +13,8 @@ export const validateNumberFieldOrThrow = (
|
||||
fieldName: string,
|
||||
): number | null => {
|
||||
if (
|
||||
(typeof value !== 'number' && !isNull(value)) ||
|
||||
(typeof value === 'number' &&
|
||||
(!isNumber(value) && !isNull(value)) ||
|
||||
(isNumber(value) &&
|
||||
(isNaN(value) || value === Infinity || value === -Infinity))
|
||||
) {
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isNonEmptyString, isNull } from '@sniptt/guards';
|
||||
import { isValidUuid } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
@@ -13,7 +13,10 @@ export const validateUUIDFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): string | null => {
|
||||
if (!isValidUuid(value as string) && !isNull(value)) {
|
||||
if (
|
||||
(!isNonEmptyString(value) && !isNull(value)) ||
|
||||
(isNonEmptyString(value) && !isValidUuid(value))
|
||||
) {
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ACTOR should throw for invalid filter #1: {"actorField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: ACTOR"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ACTOR should throw for invalid filter #2: {"actorField":{"source":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "actorField.source" of type SELECT - Allowed operators: eq, neq, in, containsAny, is, isEmptyArray"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ACTOR should throw for invalid filter #3: {"actorField":{"workspaceMemberId":{"eq":"invalid-uuid"}}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "actorField.workspaceMemberId""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ADDRESS should throw for invalid filter #1: {"addressField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: ADDRESS"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ADDRESS should throw for invalid filter #2: {"addressField":{"addressCity":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "addressField.addressCity" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ADDRESS should throw for invalid filter #3: {"addressField":{"addressCity":{}}} 1`] = `"Filter for field "addressField.addressCity" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ARRAY should throw for invalid filter #1: {"arrayField":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "arrayField" of type ARRAY - Allowed operators: containsIlike, is, isEmptyArray"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ARRAY should throw for invalid filter #2: {"arrayField":{}} 1`] = `"Filter for field "arrayField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation ARRAY should throw for invalid filter #3: {"arrayField":{"containsIlike":"test","is":"NULL"}} 1`] = `"Filter for field "arrayField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation BOOLEAN should throw for invalid filter #1: {"booleanField":{"eq":{}}} 1`] = `"Invalid boolean value {} for field "booleanField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation BOOLEAN should throw for invalid filter #2: {"booleanField":{"eq":[]}} 1`] = `"Invalid boolean value [] for field "booleanField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation BOOLEAN should throw for invalid filter #3: {"booleanField":{"eq":1}} 1`] = `"Invalid boolean value 1 for field "booleanField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation BOOLEAN should throw for invalid filter #4: {"booleanField":{"invalidOperator":true}} 1`] = `"Operator "invalidOperator" is not valid for field "booleanField" of type BOOLEAN - Allowed operators: eq, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation BOOLEAN should throw for invalid filter #5: {"booleanField":{}} 1`] = `"Filter for field "booleanField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation CURRENCY should throw for invalid filter #1: {"currencyField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: CURRENCY"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation CURRENCY should throw for invalid filter #2: {"currencyField":{"amountMicros":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "currencyField.amountMicros" of type NUMERIC - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation CURRENCY should throw for invalid filter #3: {"currencyField":{"amountMicros":{"eq":"not-a-number"}}} 1`] = `"Invalid number value NaN for field "currencyField.amountMicros""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #1: {"dateField":{"eq":"malformed-date"}} 1`] = `"Invalid value 'malformed-date' for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #2: {"dateField":{"eq":{}}} 1`] = `"Invalid value {} for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #3: {"dateField":{"eq":[]}} 1`] = `"Invalid value [] for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #4: {"dateField":{"eq":true}} 1`] = `"Invalid value true for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #5: {"dateField":{"eq":1}} 1`] = `"Invalid value 1 for date field "dateField". Expected format: 'YYYY-MM-DD'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #6: {"dateField":{"invalidOperator":"2025-01-01"}} 1`] = `"Operator "invalidOperator" is not valid for field "dateField" of type DATE - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE should throw for invalid filter #7: {"dateField":{}} 1`] = `"Filter for field "dateField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #1: {"dateTimeField":{"eq":"malformed-date"}} 1`] = `"Invalid value 'malformed-date' for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #2: {"dateTimeField":{"eq":{}}} 1`] = `"Invalid value {} for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #3: {"dateTimeField":{"eq":[]}} 1`] = `"Invalid value [] for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #4: {"dateTimeField":{"eq":true}} 1`] = `"Invalid value true for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #5: {"dateTimeField":{"eq":1}} 1`] = `"Invalid value 1 for date-time field "dateTimeField". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #6: {"dateTimeField":{"invalidOperator":"2025-01-01T10:00:00Z"}} 1`] = `"Operator "invalidOperator" is not valid for field "dateTimeField" of type DATE_TIME - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation DATE_TIME should throw for invalid filter #7: {"dateTimeField":{}} 1`] = `"Filter for field "dateTimeField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation EMAILS should throw for invalid filter #1: {"emailsField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: EMAILS"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation EMAILS should throw for invalid filter #2: {"emailsField":{"primaryEmail":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "emailsField.primaryEmail" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation FILES should throw for invalid filter #1: {"filesField":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "filesField" of type FILES - Allowed operators: is, like"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation FILES should throw for invalid filter #2: {"filesField":{}} 1`] = `"Filter for field "filesField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation FULL_NAME should throw for invalid filter #1: {"fullNameField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: FULL_NAME"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation FULL_NAME should throw for invalid filter #2: {"fullNameField":{"firstName":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "fullNameField.firstName" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation LINKS should throw for invalid filter #1: {"linksField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: LINKS"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation LINKS should throw for invalid filter #2: {"linksField":{"primaryLinkUrl":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "linksField.primaryLinkUrl" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation MORPH_RELATION should throw for invalid filter #1: {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation MORPH_RELATION should throw for invalid filter #2: {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id" of type MORPH_RELATION - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation MORPH_RELATION should throw for invalid filter #3: {"manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id":{}} 1`] = `"Filter for field "manyToOneMorphRelationFieldApiInputValidationTargetTestObject1Id" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation MULTI_SELECT should throw for invalid filter #1: {"multiSelectField":{"invalidOperator":["OPTION_1"]}} 1`] = `"Operator "invalidOperator" is not valid for field "multiSelectField" of type MULTI_SELECT - Allowed operators: containsAny, is, isEmptyArray"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation MULTI_SELECT should throw for invalid filter #2: {"multiSelectField":{}} 1`] = `"Filter for field "multiSelectField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation MULTI_SELECT should throw for invalid filter #3: {"multiSelectField":{"containsAny":"not-an-array"}} 1`] = `"Filter operator "containsAny" requires an array value for field multiSelectField"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #1: {"numberField":{"eq":"not-a-number"}} 1`] = `"Invalid number value NaN for field "numberField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #2: {"numberField":{"eq":{}}} 1`] = `"Invalid number value {} for field "numberField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #3: {"numberField":{"eq":[]}} 1`] = `"Invalid number value [] for field "numberField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #4: {"numberField":{"eq":true}} 1`] = `"Invalid number value true for field "numberField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #5: {"numberField":{"invalidOperator":1}} 1`] = `"Operator "invalidOperator" is not valid for field "numberField" of type NUMBER - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #6: {"numberField":{"eq":1,"neq":2}} 1`] = `"Filter for field "numberField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMBER should throw for invalid filter #7: {"numberField":{}} 1`] = `"Filter for field "numberField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMERIC should throw for invalid filter #1: {"numericField":{"eq":"not-a-number"}} 1`] = `"Invalid number value NaN for field "numericField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMERIC should throw for invalid filter #2: {"numericField":{"eq":{}}} 1`] = `"Invalid number value {} for field "numericField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMERIC should throw for invalid filter #3: {"numericField":{"invalidOperator":1}} 1`] = `"Operator "invalidOperator" is not valid for field "numericField" of type NUMERIC - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation NUMERIC should throw for invalid filter #4: {"numericField":{}} 1`] = `"Filter for field "numericField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation PHONES should throw for invalid filter #1: {"phonesField":{"invalidSubField":{"eq":"test"}}} 1`] = `"Sub field "invalidSubField" not found for composite type: PHONES"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation PHONES should throw for invalid filter #2: {"phonesField":{"primaryPhoneNumber":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "phonesField.primaryPhoneNumber" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation POSITION should throw for invalid filter #1: {"position":{"eq":"not-a-number"}} 1`] = `"Invalid number value NaN for field "position""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation POSITION should throw for invalid filter #2: {"position":{"eq":{}}} 1`] = `"Invalid number value {} for field "position""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation POSITION should throw for invalid filter #3: {"position":{"invalidOperator":1}} 1`] = `"Operator "invalidOperator" is not valid for field "position" of type POSITION - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation POSITION should throw for invalid filter #4: {"position":{}} 1`] = `"Filter for field "position" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RATING should throw for invalid filter #1: {"ratingField":{"invalidOperator":"RATING_1"}} 1`] = `"Operator "invalidOperator" is not valid for field "ratingField" of type RATING - Allowed operators: eq, neq, in, containsAny, is, isEmptyArray"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RATING should throw for invalid filter #2: {"ratingField":{}} 1`] = `"Filter for field "ratingField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RATING should throw for invalid filter #3: {"ratingField":{"eq":"RATING_1","neq":"RATING_2"}} 1`] = `"Filter for field "ratingField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RAW_JSON should throw for invalid filter #1: {"rawJsonField":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "rawJsonField" of type RAW_JSON - Allowed operators: is, like"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RAW_JSON should throw for invalid filter #2: {"rawJsonField":{}} 1`] = `"Filter for field "rawJsonField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RELATION should throw for invalid filter #1: {"manyToOneRelationFieldId":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "manyToOneRelationFieldId""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RELATION should throw for invalid filter #2: {"manyToOneRelationFieldId":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "manyToOneRelationFieldId" of type RELATION - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RELATION should throw for invalid filter #3: {"manyToOneRelationFieldId":{}} 1`] = `"Filter for field "manyToOneRelationFieldId" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT should throw for invalid filter #1: {"richTextField":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "richTextField" of type RICH_TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT should throw for invalid filter #2: {"richTextField":{}} 1`] = `"Filter for field "richTextField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT_V2 should throw for invalid filter #1: {"richTextV2Field":{"invalidOperator":"test"}} 1`] = `"Sub field "invalidOperator" not found for composite type: RICH_TEXT_V2"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT_V2 should throw for invalid filter #2: {"richTextV2Field":{"markdown":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "richTextV2Field.markdown" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation SELECT should throw for invalid filter #1: {"selectField":{"invalidOperator":"OPTION_1"}} 1`] = `"Operator "invalidOperator" is not valid for field "selectField" of type SELECT - Allowed operators: eq, neq, in, containsAny, is, isEmptyArray"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation SELECT should throw for invalid filter #2: {"selectField":{}} 1`] = `"Filter for field "selectField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation SELECT should throw for invalid filter #3: {"selectField":{"eq":"OPTION_1","neq":"OPTION_2"}} 1`] = `"Filter for field "selectField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation TEXT should throw for invalid filter #1: {"textField":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "textField" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation TEXT should throw for invalid filter #2: {"textField":{"eq":"test","neq":"test"}} 1`] = `"Filter for field "textField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation TEXT should throw for invalid filter #3: {"textField":{}} 1`] = `"Filter for field "textField" must have exactly one operator"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #1: {"uuidField":{"eq":"invalid-uuid"}} 1`] = `"Invalid UUID value 'invalid-uuid' for field "uuidField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #2: {"uuidField":{"eq":2}} 1`] = `"Invalid UUID value 2 for field "uuidField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #3: {"uuidField":{"eq":{}}} 1`] = `"Invalid UUID value {} for field "uuidField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #4: {"uuidField":{"eq":[]}} 1`] = `"Invalid UUID value [] for field "uuidField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #5: {"uuidField":{"eq":true}} 1`] = `"Invalid UUID value true for field "uuidField""`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #6: {"uuidField":{"invalidOperator":"test"}} 1`] = `"Operator "invalidOperator" is not valid for field "uuidField" of type UUID - Allowed operators: eq, neq, gt, gte, lt, lte, in, is"`;
|
||||
|
||||
exports[`FilterArgProcessorService failing filter inputs validation UUID should throw for invalid filter #7: {"uuidField":{}} 1`] = `"Filter for field "uuidField" must have exactly one operator"`;
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
import { joinColumnNameForManyToOneMorphRelationField1 } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
export const failingFilterInputsByFieldMetadataType: {
|
||||
[K in FieldMetadataType]?: {
|
||||
filter: Record<string, unknown>;
|
||||
}[];
|
||||
} = {
|
||||
[FieldMetadataType.TEXT]: [
|
||||
{ filter: { textField: { invalidOperator: 'test' } } },
|
||||
{ filter: { textField: { eq: 'test', neq: 'test' } } },
|
||||
{ filter: { textField: {} } },
|
||||
],
|
||||
[FieldMetadataType.RICH_TEXT]: [
|
||||
{ filter: { richTextField: { invalidOperator: 'test' } } },
|
||||
{ filter: { richTextField: {} } },
|
||||
],
|
||||
[FieldMetadataType.NUMBER]: [
|
||||
{ filter: { numberField: { eq: 'not-a-number' } } },
|
||||
{ filter: { numberField: { eq: {} } } },
|
||||
{ filter: { numberField: { eq: [] } } },
|
||||
{ filter: { numberField: { eq: true } } },
|
||||
{ filter: { numberField: { invalidOperator: 1 } } },
|
||||
{ filter: { numberField: { eq: 1, neq: 2 } } },
|
||||
{ filter: { numberField: {} } },
|
||||
],
|
||||
[FieldMetadataType.NUMERIC]: [
|
||||
{ filter: { numericField: { eq: 'not-a-number' } } },
|
||||
{ filter: { numericField: { eq: {} } } },
|
||||
{ filter: { numericField: { invalidOperator: 1 } } },
|
||||
{ filter: { numericField: {} } },
|
||||
],
|
||||
[FieldMetadataType.UUID]: [
|
||||
{ filter: { uuidField: { eq: 'invalid-uuid' } } },
|
||||
{ filter: { uuidField: { eq: 2 } } },
|
||||
{ filter: { uuidField: { eq: {} } } },
|
||||
{ filter: { uuidField: { eq: [] } } },
|
||||
{ filter: { uuidField: { eq: true } } },
|
||||
{ filter: { uuidField: { invalidOperator: 'test' } } },
|
||||
{ filter: { uuidField: {} } },
|
||||
],
|
||||
[FieldMetadataType.BOOLEAN]: [
|
||||
{ filter: { booleanField: { eq: {} } } },
|
||||
{ filter: { booleanField: { eq: [] } } },
|
||||
{ filter: { booleanField: { eq: 1 } } },
|
||||
{ filter: { booleanField: { invalidOperator: true } } },
|
||||
{ filter: { booleanField: {} } },
|
||||
],
|
||||
[FieldMetadataType.DATE]: [
|
||||
{ filter: { dateField: { eq: 'malformed-date' } } },
|
||||
{ filter: { dateField: { eq: {} } } },
|
||||
{ filter: { dateField: { eq: [] } } },
|
||||
{ filter: { dateField: { eq: true } } },
|
||||
{ filter: { dateField: { eq: 1 } } },
|
||||
{ filter: { dateField: { invalidOperator: '2025-01-01' } } },
|
||||
{ filter: { dateField: {} } },
|
||||
],
|
||||
[FieldMetadataType.DATE_TIME]: [
|
||||
{ filter: { dateTimeField: { eq: 'malformed-date' } } },
|
||||
{ filter: { dateTimeField: { eq: {} } } },
|
||||
{ filter: { dateTimeField: { eq: [] } } },
|
||||
{ filter: { dateTimeField: { eq: true } } },
|
||||
{ filter: { dateTimeField: { eq: 1 } } },
|
||||
{ filter: { dateTimeField: { invalidOperator: '2025-01-01T10:00:00Z' } } },
|
||||
{ filter: { dateTimeField: {} } },
|
||||
],
|
||||
[FieldMetadataType.SELECT]: [
|
||||
{ filter: { selectField: { invalidOperator: 'OPTION_1' } } },
|
||||
{ filter: { selectField: {} } },
|
||||
{ filter: { selectField: { eq: 'OPTION_1', neq: 'OPTION_2' } } },
|
||||
],
|
||||
[FieldMetadataType.RATING]: [
|
||||
{ filter: { ratingField: { invalidOperator: 'RATING_1' } } },
|
||||
{ filter: { ratingField: {} } },
|
||||
{ filter: { ratingField: { eq: 'RATING_1', neq: 'RATING_2' } } },
|
||||
],
|
||||
[FieldMetadataType.MULTI_SELECT]: [
|
||||
{ filter: { multiSelectField: { invalidOperator: ['OPTION_1'] } } },
|
||||
{ filter: { multiSelectField: {} } },
|
||||
{ filter: { multiSelectField: { containsAny: 'not-an-array' } } },
|
||||
],
|
||||
[FieldMetadataType.ARRAY]: [
|
||||
{ filter: { arrayField: { invalidOperator: 'test' } } },
|
||||
{ filter: { arrayField: {} } },
|
||||
{ filter: { arrayField: { containsIlike: 'test', is: 'NULL' } } },
|
||||
],
|
||||
[FieldMetadataType.RAW_JSON]: [
|
||||
{ filter: { rawJsonField: { invalidOperator: 'test' } } },
|
||||
{ filter: { rawJsonField: {} } },
|
||||
],
|
||||
[FieldMetadataType.RELATION]: [
|
||||
{ filter: { manyToOneRelationFieldId: { eq: 'invalid-uuid' } } },
|
||||
{ filter: { manyToOneRelationFieldId: { invalidOperator: 'test' } } },
|
||||
{ filter: { manyToOneRelationFieldId: {} } },
|
||||
],
|
||||
[FieldMetadataType.MORPH_RELATION]: [
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: { eq: 'invalid-uuid' },
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: {
|
||||
invalidOperator: 'test',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: { [joinColumnNameForManyToOneMorphRelationField1]: {} },
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.POSITION]: [
|
||||
{ filter: { position: { eq: 'not-a-number' } } },
|
||||
{ filter: { position: { eq: {} } } },
|
||||
{ filter: { position: { invalidOperator: 1 } } },
|
||||
{ filter: { position: {} } },
|
||||
],
|
||||
[FieldMetadataType.FILES]: [
|
||||
{ filter: { filesField: { invalidOperator: 'test' } } },
|
||||
{ filter: { filesField: {} } },
|
||||
],
|
||||
[FieldMetadataType.RICH_TEXT_V2]: [
|
||||
{ filter: { richTextV2Field: { invalidOperator: 'test' } } },
|
||||
{ filter: { richTextV2Field: { markdown: { invalidOperator: 'test' } } } },
|
||||
],
|
||||
[FieldMetadataType.ADDRESS]: [
|
||||
{ filter: { addressField: { invalidSubField: { eq: 'test' } } } },
|
||||
{ filter: { addressField: { addressCity: { invalidOperator: 'test' } } } },
|
||||
{ filter: { addressField: { addressCity: {} } } },
|
||||
],
|
||||
[FieldMetadataType.CURRENCY]: [
|
||||
{ filter: { currencyField: { invalidSubField: { eq: 'test' } } } },
|
||||
{
|
||||
filter: { currencyField: { amountMicros: { invalidOperator: 'test' } } },
|
||||
},
|
||||
{ filter: { currencyField: { amountMicros: { eq: 'not-a-number' } } } },
|
||||
],
|
||||
[FieldMetadataType.EMAILS]: [
|
||||
{ filter: { emailsField: { invalidSubField: { eq: 'test' } } } },
|
||||
{
|
||||
filter: { emailsField: { primaryEmail: { invalidOperator: 'test' } } },
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.PHONES]: [
|
||||
{ filter: { phonesField: { invalidSubField: { eq: 'test' } } } },
|
||||
{
|
||||
filter: {
|
||||
phonesField: { primaryPhoneNumber: { invalidOperator: 'test' } },
|
||||
},
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.FULL_NAME]: [
|
||||
{ filter: { fullNameField: { invalidSubField: { eq: 'test' } } } },
|
||||
{ filter: { fullNameField: { firstName: { invalidOperator: 'test' } } } },
|
||||
],
|
||||
[FieldMetadataType.LINKS]: [
|
||||
{ filter: { linksField: { invalidSubField: { eq: 'test' } } } },
|
||||
{
|
||||
filter: { linksField: { primaryLinkUrl: { invalidOperator: 'test' } } },
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.ACTOR]: [
|
||||
{ filter: { actorField: { invalidSubField: { eq: 'test' } } } },
|
||||
{ filter: { actorField: { source: { invalidOperator: 'test' } } } },
|
||||
{ filter: { actorField: { workspaceMemberId: { eq: 'invalid-uuid' } } } },
|
||||
],
|
||||
};
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
import { joinColumnNameForManyToOneMorphRelationField1 } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
const TEST_UUID = '20202020-b21e-4ec2-873b-de4264d89021';
|
||||
|
||||
export const successfulFilterInputsByFieldMetadataType: {
|
||||
[K in FieldMetadataType]?: {
|
||||
filter: Record<string, unknown>;
|
||||
expected?: Record<string, unknown>;
|
||||
}[];
|
||||
} = {
|
||||
[FieldMetadataType.TEXT]: [
|
||||
{ filter: { textField: { eq: 'test' } } },
|
||||
{ filter: { textField: { neq: 'test' } } },
|
||||
{ filter: { textField: { like: '%test%' } } },
|
||||
{ filter: { textField: { ilike: '%test%' } } },
|
||||
{ filter: { textField: { startsWith: 'test' } } },
|
||||
{ filter: { textField: { endsWith: 'test' } } },
|
||||
{ filter: { textField: { in: ['test1', 'test2'] } } },
|
||||
{ filter: { textField: { is: 'NULL' } } },
|
||||
{ filter: { textField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { textField: { eq: null } } },
|
||||
],
|
||||
[FieldMetadataType.RICH_TEXT]: [
|
||||
{ filter: { richTextField: { eq: 'test' } } },
|
||||
{ filter: { richTextField: { like: '%test%' } } },
|
||||
{ filter: { richTextField: { ilike: '%test%' } } },
|
||||
{ filter: { richTextField: { is: 'NULL' } } },
|
||||
{ filter: { richTextField: { is: 'NOT_NULL' } } },
|
||||
],
|
||||
[FieldMetadataType.NUMBER]: [
|
||||
{ filter: { numberField: { eq: 1 } } },
|
||||
{ filter: { numberField: { neq: 1 } } },
|
||||
{ filter: { numberField: { gt: 0 } } },
|
||||
{ filter: { numberField: { gte: 0 } } },
|
||||
{ filter: { numberField: { lt: 10 } } },
|
||||
{ filter: { numberField: { lte: 10 } } },
|
||||
{ filter: { numberField: { in: [1, 2, 3] } } },
|
||||
{ filter: { numberField: { is: 'NULL' } } },
|
||||
{ filter: { numberField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { numberField: { eq: null } } },
|
||||
{
|
||||
filter: { numberField: { eq: '1' } },
|
||||
expected: { numberField: { eq: 1 } },
|
||||
},
|
||||
{ filter: { numberField: { eq: 0 } } },
|
||||
{ filter: { numberField: { eq: -1.5 } } },
|
||||
],
|
||||
[FieldMetadataType.NUMERIC]: [
|
||||
{ filter: { numericField: { eq: 1 } } },
|
||||
{ filter: { numericField: { neq: 1 } } },
|
||||
{ filter: { numericField: { gt: 0 } } },
|
||||
{ filter: { numericField: { gte: 0 } } },
|
||||
{ filter: { numericField: { lt: 10 } } },
|
||||
{ filter: { numericField: { lte: 10 } } },
|
||||
{ filter: { numericField: { in: [1, 2, 3] } } },
|
||||
{ filter: { numericField: { is: 'NULL' } } },
|
||||
{ filter: { numericField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { numericField: { eq: null } } },
|
||||
],
|
||||
[FieldMetadataType.UUID]: [
|
||||
{ filter: { uuidField: { eq: TEST_UUID } } },
|
||||
{ filter: { uuidField: { neq: TEST_UUID } } },
|
||||
{ filter: { uuidField: { in: [TEST_UUID] } } },
|
||||
{ filter: { uuidField: { is: 'NULL' } } },
|
||||
{ filter: { uuidField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { uuidField: { eq: null } } },
|
||||
],
|
||||
[FieldMetadataType.BOOLEAN]: [
|
||||
{ filter: { booleanField: { eq: true } } },
|
||||
{ filter: { booleanField: { eq: false } } },
|
||||
{ filter: { booleanField: { is: 'NULL' } } },
|
||||
{ filter: { booleanField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { booleanField: { eq: null } } },
|
||||
{
|
||||
filter: { booleanField: { eq: 'true' } },
|
||||
expected: { booleanField: { eq: true } },
|
||||
},
|
||||
{
|
||||
filter: { booleanField: { eq: 'false' } },
|
||||
expected: { booleanField: { eq: false } },
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.DATE]: [
|
||||
{ filter: { dateField: { eq: '2025-01-13' } } },
|
||||
{ filter: { dateField: { neq: '2025-01-13' } } },
|
||||
{ filter: { dateField: { gt: '2025-01-01' } } },
|
||||
{ filter: { dateField: { gte: '2025-01-01' } } },
|
||||
{ filter: { dateField: { lt: '2025-12-31' } } },
|
||||
{ filter: { dateField: { lte: '2025-12-31' } } },
|
||||
{ filter: { dateField: { in: ['2025-01-01', '2025-01-02'] } } },
|
||||
{ filter: { dateField: { is: 'NULL' } } },
|
||||
{ filter: { dateField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { dateField: { eq: null } } },
|
||||
],
|
||||
[FieldMetadataType.DATE_TIME]: [
|
||||
{ filter: { dateTimeField: { eq: '2025-01-13T10:30:00Z' } } },
|
||||
{ filter: { dateTimeField: { neq: '2025-01-13T10:30:00Z' } } },
|
||||
{ filter: { dateTimeField: { gt: '2025-01-01T00:00:00Z' } } },
|
||||
{ filter: { dateTimeField: { gte: '2025-01-01T00:00:00Z' } } },
|
||||
{ filter: { dateTimeField: { lt: '2025-12-31T23:59:59Z' } } },
|
||||
{ filter: { dateTimeField: { lte: '2025-12-31T23:59:59Z' } } },
|
||||
{
|
||||
filter: {
|
||||
dateTimeField: {
|
||||
in: ['2025-01-01T00:00:00Z', '2025-01-02T00:00:00Z'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{ filter: { dateTimeField: { is: 'NULL' } } },
|
||||
{ filter: { dateTimeField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { dateTimeField: { eq: null } } },
|
||||
],
|
||||
[FieldMetadataType.SELECT]: [
|
||||
{ filter: { selectField: { eq: 'OPTION_1' } } },
|
||||
{ filter: { selectField: { neq: 'OPTION_1' } } },
|
||||
{ filter: { selectField: { in: ['OPTION_1', 'OPTION_2'] } } },
|
||||
{ filter: { selectField: { is: 'NULL' } } },
|
||||
{ filter: { selectField: { is: 'NOT_NULL' } } },
|
||||
],
|
||||
[FieldMetadataType.RATING]: [
|
||||
{ filter: { ratingField: { eq: 'RATING_1' } } },
|
||||
{ filter: { ratingField: { neq: 'RATING_1' } } },
|
||||
{ filter: { ratingField: { in: ['RATING_1', 'RATING_2'] } } },
|
||||
{ filter: { ratingField: { is: 'NULL' } } },
|
||||
{ filter: { ratingField: { is: 'NOT_NULL' } } },
|
||||
],
|
||||
[FieldMetadataType.MULTI_SELECT]: [
|
||||
{ filter: { multiSelectField: { containsAny: ['OPTION_1'] } } },
|
||||
{ filter: { multiSelectField: { is: 'NULL' } } },
|
||||
{ filter: { multiSelectField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { multiSelectField: { isEmptyArray: true } } },
|
||||
],
|
||||
[FieldMetadataType.ARRAY]: [
|
||||
{ filter: { arrayField: { containsIlike: '%test%' } } },
|
||||
{ filter: { arrayField: { is: 'NULL' } } },
|
||||
{ filter: { arrayField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { arrayField: { isEmptyArray: true } } },
|
||||
],
|
||||
[FieldMetadataType.RAW_JSON]: [
|
||||
{ filter: { rawJsonField: { is: 'NULL' } } },
|
||||
{ filter: { rawJsonField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { rawJsonField: { like: '%test%' } } },
|
||||
],
|
||||
[FieldMetadataType.RELATION]: [
|
||||
{ filter: { manyToOneRelationFieldId: { eq: TEST_UUID } } },
|
||||
{ filter: { manyToOneRelationFieldId: { neq: TEST_UUID } } },
|
||||
{ filter: { manyToOneRelationFieldId: { in: [TEST_UUID] } } },
|
||||
{ filter: { manyToOneRelationFieldId: { is: 'NULL' } } },
|
||||
{ filter: { manyToOneRelationFieldId: { is: 'NOT_NULL' } } },
|
||||
{ filter: { manyToOneRelationFieldId: { eq: null } } },
|
||||
],
|
||||
[FieldMetadataType.MORPH_RELATION]: [
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: { eq: TEST_UUID },
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: { neq: TEST_UUID },
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: { in: [TEST_UUID] },
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: { is: 'NULL' },
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: { is: 'NOT_NULL' },
|
||||
},
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.POSITION]: [
|
||||
{ filter: { position: { eq: 1 } } },
|
||||
{ filter: { position: { neq: 1 } } },
|
||||
{ filter: { position: { gt: 0 } } },
|
||||
{ filter: { position: { gte: 0 } } },
|
||||
{ filter: { position: { lt: 10 } } },
|
||||
{ filter: { position: { lte: 10 } } },
|
||||
{ filter: { position: { in: [1, 2, 3] } } },
|
||||
{ filter: { position: { is: 'NULL' } } },
|
||||
{ filter: { position: { is: 'NOT_NULL' } } },
|
||||
{
|
||||
filter: { position: { eq: '1' } },
|
||||
expected: { position: { eq: 1 } },
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.FILES]: [
|
||||
{ filter: { filesField: { is: 'NULL' } } },
|
||||
{ filter: { filesField: { is: 'NOT_NULL' } } },
|
||||
{ filter: { filesField: { like: '%test%' } } },
|
||||
],
|
||||
[FieldMetadataType.RICH_TEXT_V2]: [
|
||||
{ filter: { richTextV2Field: { markdown: { ilike: '%test%' } } } },
|
||||
],
|
||||
[FieldMetadataType.ADDRESS]: [
|
||||
{ filter: { addressField: { addressCity: { eq: 'Paris' } } } },
|
||||
{ filter: { addressField: { addressCity: { like: '%Paris%' } } } },
|
||||
{ filter: { addressField: { addressLat: { gt: 48.0 } } } },
|
||||
{ filter: { addressField: { addressStreet1: { is: 'NULL' } } } },
|
||||
],
|
||||
[FieldMetadataType.CURRENCY]: [
|
||||
{ filter: { currencyField: { amountMicros: { eq: 1000000 } } } },
|
||||
{ filter: { currencyField: { amountMicros: { gt: 0 } } } },
|
||||
{ filter: { currencyField: { currencyCode: { eq: 'USD' } } } },
|
||||
{ filter: { currencyField: { currencyCode: { is: 'NULL' } } } },
|
||||
],
|
||||
[FieldMetadataType.EMAILS]: [
|
||||
{ filter: { emailsField: { primaryEmail: { eq: 'test@test.com' } } } },
|
||||
{ filter: { emailsField: { primaryEmail: { like: '%@test.com' } } } },
|
||||
{ filter: { emailsField: { primaryEmail: { is: 'NULL' } } } },
|
||||
],
|
||||
[FieldMetadataType.PHONES]: [
|
||||
{ filter: { phonesField: { primaryPhoneNumber: { eq: '1234567890' } } } },
|
||||
{
|
||||
filter: { phonesField: { primaryPhoneNumber: { like: '%123%' } } },
|
||||
},
|
||||
{ filter: { phonesField: { primaryPhoneCountryCode: { eq: 'FR' } } } },
|
||||
{ filter: { phonesField: { primaryPhoneNumber: { is: 'NULL' } } } },
|
||||
],
|
||||
[FieldMetadataType.FULL_NAME]: [
|
||||
{ filter: { fullNameField: { firstName: { eq: 'John' } } } },
|
||||
{ filter: { fullNameField: { lastName: { like: '%Doe%' } } } },
|
||||
{ filter: { fullNameField: { firstName: { is: 'NULL' } } } },
|
||||
],
|
||||
[FieldMetadataType.LINKS]: [
|
||||
{ filter: { linksField: { primaryLinkUrl: { eq: 'https://test.com' } } } },
|
||||
{ filter: { linksField: { primaryLinkLabel: { like: '%test%' } } } },
|
||||
{ filter: { linksField: { primaryLinkUrl: { is: 'NULL' } } } },
|
||||
],
|
||||
[FieldMetadataType.ACTOR]: [
|
||||
{ filter: { actorField: { source: { eq: 'MANUAL' } } } },
|
||||
{ filter: { actorField: { name: { like: '%John%' } } } },
|
||||
{ filter: { actorField: { workspaceMemberId: { eq: TEST_UUID } } } },
|
||||
{ filter: { actorField: { workspaceMemberId: { is: 'NULL' } } } },
|
||||
],
|
||||
};
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { fieldMetadataConfigByFieldName } from 'src/engine/api/common/common-args-processors/data-arg-processor/__tests__/constants/field-metadata-config-by-field-name.constant';
|
||||
import { FilterArgProcessorService } from 'src/engine/api/common/common-args-processors/filter-arg-processor/filter-arg-processor.service';
|
||||
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';
|
||||
|
||||
import { failingFilterInputsByFieldMetadataType } from './constants/failing-filter-inputs-by-field-metadata-type.constant';
|
||||
import { successfulFilterInputsByFieldMetadataType } from './constants/successful-filter-inputs-by-field-metadata-type.constant';
|
||||
|
||||
describe('FilterArgProcessorService', () => {
|
||||
let filterArgProcessorService: FilterArgProcessorService;
|
||||
|
||||
const createFlatFieldMetadataMaps = (
|
||||
fieldNames: string[],
|
||||
): FlatEntityMaps<FlatFieldMetadata> => {
|
||||
const byUniversalIdentifier: Record<string, FlatFieldMetadata> = {};
|
||||
const universalIdentifierById: Record<string, string> = {};
|
||||
|
||||
for (const fieldName of fieldNames) {
|
||||
const config = fieldMetadataConfigByFieldName[fieldName];
|
||||
|
||||
if (!config) {
|
||||
throw new Error(`No config found for field: ${fieldName}`);
|
||||
}
|
||||
|
||||
const fieldId = `${fieldName}-id`;
|
||||
const universalId = `${fieldName}-universal-id`;
|
||||
|
||||
byUniversalIdentifier[universalId] = {
|
||||
id: fieldId,
|
||||
name: fieldName,
|
||||
type: config.type ?? FieldMetadataType.TEXT,
|
||||
isNullable: config.isNullable ?? true,
|
||||
objectMetadataId: 'object-id',
|
||||
universalIdentifier: universalId,
|
||||
options: config.options,
|
||||
settings: config.settings,
|
||||
defaultValue: config.defaultValue,
|
||||
} as FlatFieldMetadata;
|
||||
|
||||
universalIdentifierById[fieldId] = universalId;
|
||||
}
|
||||
|
||||
return {
|
||||
byUniversalIdentifier,
|
||||
universalIdentifierById,
|
||||
universalIdentifiersByApplicationId: {},
|
||||
} as unknown as FlatEntityMaps<FlatFieldMetadata>;
|
||||
};
|
||||
|
||||
const createFlatObjectMetadata = (fieldNames: string[]): FlatObjectMetadata =>
|
||||
({
|
||||
id: 'object-id',
|
||||
nameSingular: 'testObject',
|
||||
namePlural: 'testObjects',
|
||||
isCustom: false,
|
||||
fieldIds: fieldNames.map((name) => `${name}-id`),
|
||||
universalIdentifier: 'test-object-universal-id',
|
||||
labelIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
imageIdentifierFieldMetadataUniversalIdentifier: null,
|
||||
}) as unknown as FlatObjectMetadata;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [FilterArgProcessorService],
|
||||
}).compile();
|
||||
|
||||
filterArgProcessorService = module.get<FilterArgProcessorService>(
|
||||
FilterArgProcessorService,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('failing filter inputs validation', () => {
|
||||
const fieldMetadataTypesToTest = Object.keys(
|
||||
failingFilterInputsByFieldMetadataType,
|
||||
) as FieldMetadataType[];
|
||||
|
||||
for (const fieldMetadataType of fieldMetadataTypesToTest) {
|
||||
const testCases =
|
||||
failingFilterInputsByFieldMetadataType[fieldMetadataType];
|
||||
|
||||
if (!testCases) {
|
||||
continue;
|
||||
}
|
||||
|
||||
describe(`${fieldMetadataType}`, () => {
|
||||
for (const [index, testCase] of testCases.entries()) {
|
||||
const fieldName = Object.keys(testCase.filter)[0];
|
||||
|
||||
it(`should throw for invalid filter #${index + 1}: ${JSON.stringify(testCase.filter)}`, () => {
|
||||
const fieldNames = [fieldName];
|
||||
|
||||
const flatFieldMetadataMaps =
|
||||
createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
expect(() =>
|
||||
filterArgProcessorService.process({
|
||||
filter: testCase.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('successful filter inputs validation', () => {
|
||||
const fieldMetadataTypesToTest = Object.keys(
|
||||
successfulFilterInputsByFieldMetadataType,
|
||||
) as FieldMetadataType[];
|
||||
|
||||
for (const fieldMetadataType of fieldMetadataTypesToTest) {
|
||||
const testCases =
|
||||
successfulFilterInputsByFieldMetadataType[fieldMetadataType];
|
||||
|
||||
if (!testCases) {
|
||||
continue;
|
||||
}
|
||||
|
||||
describe(`${fieldMetadataType}`, () => {
|
||||
for (const [index, testCase] of testCases.entries()) {
|
||||
const fieldName = Object.keys(testCase.filter)[0];
|
||||
|
||||
it(`should process valid filter #${index + 1}: ${JSON.stringify(testCase.filter)}`, () => {
|
||||
const fieldNames = [fieldName];
|
||||
|
||||
const flatFieldMetadataMaps =
|
||||
createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter: testCase.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const expectedResult = testCase.expected ?? testCase.filter;
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('logical operators', () => {
|
||||
it('should process filter with "and" operator', () => {
|
||||
const fieldNames = ['textField', 'numberField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const filter = {
|
||||
and: [{ textField: { eq: 'test' } }, { numberField: { gt: 0 } }],
|
||||
};
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual(filter);
|
||||
});
|
||||
|
||||
it('should process filter with "or" operator', () => {
|
||||
const fieldNames = ['textField', 'numberField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const filter = {
|
||||
or: [{ textField: { eq: 'test' } }, { numberField: { gt: 0 } }],
|
||||
};
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual(filter);
|
||||
});
|
||||
|
||||
it('should process filter with "not" operator', () => {
|
||||
const fieldNames = ['textField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const filter = {
|
||||
not: { textField: { eq: 'test' } },
|
||||
};
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual(filter);
|
||||
});
|
||||
|
||||
it('should process nested logical operators', () => {
|
||||
const fieldNames = ['textField', 'numberField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const filter = {
|
||||
and: [
|
||||
{
|
||||
or: [
|
||||
{ textField: { eq: 'test1' } },
|
||||
{ textField: { eq: 'test2' } },
|
||||
],
|
||||
},
|
||||
{ numberField: { gt: 0 } },
|
||||
],
|
||||
};
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual(filter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('value transformation', () => {
|
||||
it('should coerce string to number for NUMBER field', () => {
|
||||
const fieldNames = ['numberField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter: { numberField: { eq: '42' } },
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ numberField: { eq: 42 } });
|
||||
});
|
||||
|
||||
it('should coerce string to boolean for BOOLEAN field', () => {
|
||||
const fieldNames = ['booleanField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter: { booleanField: { eq: 'true' } },
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ booleanField: { eq: true } });
|
||||
});
|
||||
|
||||
it('should preserve null values in "in" operator', () => {
|
||||
const fieldNames = ['numberField'];
|
||||
const flatFieldMetadataMaps = createFlatFieldMetadataMaps(fieldNames);
|
||||
const flatObjectMetadata = createFlatObjectMetadata(fieldNames);
|
||||
|
||||
const result = filterArgProcessorService.process({
|
||||
filter: { numberField: { in: [1, null, 3] } },
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ numberField: { in: [1, null, 3] } });
|
||||
});
|
||||
});
|
||||
});
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import { type FilterOperator } from 'src/engine/api/common/common-args-processors/filter-arg-processor/types/filter-operator.type';
|
||||
|
||||
export const STRING_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'eq',
|
||||
'neq',
|
||||
'gt',
|
||||
'gte',
|
||||
'lt',
|
||||
'lte',
|
||||
'in',
|
||||
'is',
|
||||
'like',
|
||||
'ilike',
|
||||
'startsWith',
|
||||
'endsWith',
|
||||
];
|
||||
|
||||
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',
|
||||
'in',
|
||||
'is',
|
||||
];
|
||||
|
||||
export const UUID_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'eq',
|
||||
'neq',
|
||||
'gt',
|
||||
'gte',
|
||||
'lt',
|
||||
'lte',
|
||||
'in',
|
||||
'is',
|
||||
];
|
||||
|
||||
export const ARRAY_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'containsIlike',
|
||||
'is',
|
||||
'isEmptyArray',
|
||||
];
|
||||
|
||||
export const MULTI_SELECT_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'containsAny',
|
||||
'is',
|
||||
'isEmptyArray',
|
||||
];
|
||||
|
||||
export const ENUM_FILTER_OPERATORS: FilterOperator[] = [
|
||||
'eq',
|
||||
'neq',
|
||||
'in',
|
||||
'containsAny',
|
||||
'is',
|
||||
'isEmptyArray',
|
||||
];
|
||||
|
||||
export const RAW_JSON_FILTER_OPERATORS: FilterOperator[] = ['is', 'like'];
|
||||
|
||||
export const RICH_TEXT_V2_FILTER_OPERATORS: FilterOperator[] = ['ilike'];
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
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 { validateAndTransformOperatorAndValue } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-and-transform-operator-and-value.util';
|
||||
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 { 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 { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class FilterArgProcessorService {
|
||||
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,
|
||||
);
|
||||
|
||||
return this.validateAndTransformFilter(
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
) as T;
|
||||
}
|
||||
|
||||
private validateAndTransformFilter(
|
||||
filterObject: ObjectRecordFilter,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
fieldIdByName: Record<string, string>,
|
||||
fieldIdByJoinColumnName: Record<string, string>,
|
||||
): ObjectRecordFilter {
|
||||
const transformedFilter: ObjectRecordFilter = {};
|
||||
|
||||
for (const [key, value] of Object.entries(filterObject)) {
|
||||
if (key === 'and' || key === 'or') {
|
||||
transformedFilter[key] = (value as ObjectRecordFilter[]).map(
|
||||
(nestedFilter) =>
|
||||
this.validateAndTransformFilter(
|
||||
nestedFilter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === 'not') {
|
||||
transformedFilter[key] = this.validateAndTransformFilter(
|
||||
value as ObjectRecordFilter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
transformedFilter[key] = this.validateAndTransformFieldFilter(
|
||||
key,
|
||||
value,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
);
|
||||
}
|
||||
|
||||
return transformedFilter;
|
||||
}
|
||||
|
||||
private validateAndTransformFieldFilter(
|
||||
key: string,
|
||||
filterValue: Record<string, unknown>,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
fieldIdByName: Record<string, string>,
|
||||
fieldIdByJoinColumnName: Record<string, string>,
|
||||
): Record<string, unknown> {
|
||||
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 = findFlatEntityByIdInFlatEntityMaps<FlatFieldMetadata>(
|
||||
{
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (!fieldMetadata) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Field metadata not found for field ${key}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
return this.validateAndTransformCompositeFieldFilter(
|
||||
fieldMetadata,
|
||||
filterValue,
|
||||
);
|
||||
}
|
||||
|
||||
return validateAndTransformOperatorAndValue(
|
||||
key,
|
||||
filterValue,
|
||||
fieldMetadata,
|
||||
);
|
||||
}
|
||||
|
||||
private validateAndTransformCompositeFieldFilter(
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
filterValue: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
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 },
|
||||
);
|
||||
}
|
||||
|
||||
const transformedFilter: Record<string, unknown> = {};
|
||||
|
||||
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 },
|
||||
);
|
||||
}
|
||||
|
||||
transformedFilter[subFieldKey] = validateAndTransformOperatorAndValue(
|
||||
`${fieldMetadata.name}.${subFieldKey}`,
|
||||
subFieldFilter as Record<string, unknown>,
|
||||
{
|
||||
...fieldMetadata,
|
||||
type: subFieldMetadata.type as FieldMetadataType,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return transformedFilter;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
export type FilterOperator =
|
||||
| 'eq'
|
||||
| 'neq'
|
||||
| 'gt'
|
||||
| 'gte'
|
||||
| 'lt'
|
||||
| 'lte'
|
||||
| 'in'
|
||||
| 'is'
|
||||
| 'like'
|
||||
| 'ilike'
|
||||
| 'startsWith'
|
||||
| 'endsWith'
|
||||
| 'containsAny'
|
||||
| 'containsIlike'
|
||||
| 'isEmptyArray'
|
||||
| 'search';
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { getOperatorsForFieldType } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/get-operators-for-field-type.util';
|
||||
|
||||
describe('getOperatorsForFieldType', () => {
|
||||
it('should return STRING_FILTER_OPERATORS for TEXT', () => {
|
||||
const result = getOperatorsForFieldType(FieldMetadataType.TEXT);
|
||||
|
||||
expect(result).toContain('eq');
|
||||
expect(result).toContain('like');
|
||||
expect(result).toContain('startsWith');
|
||||
});
|
||||
|
||||
it('should return NUMBER_FILTER_OPERATORS for NUMBER', () => {
|
||||
const result = getOperatorsForFieldType(FieldMetadataType.NUMBER);
|
||||
|
||||
expect(result).toContain('eq');
|
||||
expect(result).toContain('gt');
|
||||
expect(result).toContain('in');
|
||||
});
|
||||
|
||||
it('should return BOOLEAN_FILTER_OPERATORS for BOOLEAN', () => {
|
||||
const result = getOperatorsForFieldType(FieldMetadataType.BOOLEAN);
|
||||
|
||||
expect(result).toEqual(['eq', 'is']);
|
||||
});
|
||||
|
||||
it('should return ARRAY_FILTER_OPERATORS for ARRAY', () => {
|
||||
const result = getOperatorsForFieldType(FieldMetadataType.ARRAY);
|
||||
|
||||
expect(result).toContain('containsIlike');
|
||||
expect(result).toContain('isEmptyArray');
|
||||
});
|
||||
});
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { parseNumberValue } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/parse-number-value.util';
|
||||
|
||||
describe('parseNumberValue', () => {
|
||||
it('should return value unchanged when not a string', () => {
|
||||
expect(parseNumberValue(42, FieldMetadataType.NUMBER)).toBe(42);
|
||||
});
|
||||
|
||||
it('should parse string to number for NUMBER field', () => {
|
||||
expect(parseNumberValue('42', FieldMetadataType.NUMBER)).toBe(42);
|
||||
expect(parseNumberValue('-3.14', FieldMetadataType.NUMERIC)).toBe(-3.14);
|
||||
});
|
||||
|
||||
it('should parse string to number for POSITION field', () => {
|
||||
expect(parseNumberValue('1.5', FieldMetadataType.POSITION)).toBe(1.5);
|
||||
});
|
||||
|
||||
it('should throw an error for unknown field type', () => {
|
||||
expect(parseNumberValue('not-a-number', FieldMetadataType.POSITION)).toBe(
|
||||
NaN,
|
||||
);
|
||||
});
|
||||
});
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { validateAndTransformOperatorAndValue } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-and-transform-operator-and-value.util';
|
||||
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
const createFieldMetadata = (type: FieldMetadataType): FlatFieldMetadata =>
|
||||
({
|
||||
type,
|
||||
name: 'testField',
|
||||
}) as FlatFieldMetadata;
|
||||
|
||||
describe('validateAndTransformOperatorAndValue', () => {
|
||||
it('should validate and transform single operator filter', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.TEXT);
|
||||
const result = validateAndTransformOperatorAndValue(
|
||||
'testField',
|
||||
{ eq: 'hello' },
|
||||
fieldMetadata,
|
||||
);
|
||||
|
||||
expect(result).toEqual({ eq: 'hello' });
|
||||
});
|
||||
|
||||
it('should throw when filter value is null', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.TEXT);
|
||||
|
||||
expect(() =>
|
||||
validateAndTransformOperatorAndValue(
|
||||
'testField',
|
||||
null as unknown as Record<string, unknown>,
|
||||
fieldMetadata,
|
||||
),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
|
||||
it('should throw when filter has no operators', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.TEXT);
|
||||
|
||||
expect(() =>
|
||||
validateAndTransformOperatorAndValue('testField', {}, fieldMetadata),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { validateArrayOperatorValueOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-array-operator-value-or-throw.util';
|
||||
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
describe('validateArrayOperatorValueOrThrow', () => {
|
||||
it('should not throw when value is an array', () => {
|
||||
expect(() =>
|
||||
validateArrayOperatorValueOrThrow([1, 2], 'in', 'fieldName'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw when value is not an array', () => {
|
||||
expect(() =>
|
||||
validateArrayOperatorValueOrThrow('not-array', 'in', 'fieldName'),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
|
||||
it('should throw when value is object', () => {
|
||||
expect(() =>
|
||||
validateArrayOperatorValueOrThrow({}, 'containsAny', 'fieldName'),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
});
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { validateIsEmptyArrayOperatorValueOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-is-empty-array-operator-value-or-throw.util';
|
||||
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
describe('validateIsEmptyArrayOperatorValueOrThrow', () => {
|
||||
it('should not throw when value is true', () => {
|
||||
expect(() =>
|
||||
validateIsEmptyArrayOperatorValueOrThrow(true, 'fieldName'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw when value is false', () => {
|
||||
expect(() =>
|
||||
validateIsEmptyArrayOperatorValueOrThrow(false, 'fieldName'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw when value is not a boolean', () => {
|
||||
expect(() =>
|
||||
validateIsEmptyArrayOperatorValueOrThrow('true', 'fieldName'),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
|
||||
it('should throw when value is an array', () => {
|
||||
expect(() =>
|
||||
validateIsEmptyArrayOperatorValueOrThrow([], 'fieldName'),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
});
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { validateIsOperatorFilterValueOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-is-operator-filter-value-or-throw.util';
|
||||
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
describe('validateIsOperatorFilterValueOrThrow', () => {
|
||||
it('should not throw when value is NULL', () => {
|
||||
expect(() => validateIsOperatorFilterValueOrThrow('NULL')).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw when value is NOT_NULL', () => {
|
||||
expect(() =>
|
||||
validateIsOperatorFilterValueOrThrow('NOT_NULL'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw when value is invalid', () => {
|
||||
expect(() => validateIsOperatorFilterValueOrThrow('invalid')).toThrow(
|
||||
CommonQueryRunnerException,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when value is null', () => {
|
||||
expect(() => validateIsOperatorFilterValueOrThrow(null)).toThrow(
|
||||
CommonQueryRunnerException,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when value is empty string', () => {
|
||||
expect(() => validateIsOperatorFilterValueOrThrow('')).toThrow(
|
||||
CommonQueryRunnerException,
|
||||
);
|
||||
});
|
||||
});
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { validateOperatorForFieldTypeOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/validate-operator-for-field-type-or-throw.util';
|
||||
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
const createFieldMetadata = (type: FieldMetadataType): FlatFieldMetadata =>
|
||||
({
|
||||
type,
|
||||
name: 'testField',
|
||||
}) as FlatFieldMetadata;
|
||||
|
||||
describe('validateOperatorForFieldTypeOrThrow', () => {
|
||||
it('should not throw when operator is valid for TEXT field', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.TEXT);
|
||||
|
||||
expect(() =>
|
||||
validateOperatorForFieldTypeOrThrow('eq', fieldMetadata, 'testField'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw when operator is invalid for TEXT field', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.TEXT);
|
||||
|
||||
expect(() =>
|
||||
validateOperatorForFieldTypeOrThrow(
|
||||
'invalidOperator' as 'eq',
|
||||
fieldMetadata,
|
||||
'testField',
|
||||
),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
|
||||
it('should not throw when eq is valid for BOOLEAN field', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.BOOLEAN);
|
||||
|
||||
expect(() =>
|
||||
validateOperatorForFieldTypeOrThrow('eq', fieldMetadata, 'testField'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw when like is invalid for BOOLEAN field', () => {
|
||||
const fieldMetadata = createFieldMetadata(FieldMetadataType.BOOLEAN);
|
||||
|
||||
expect(() =>
|
||||
validateOperatorForFieldTypeOrThrow('like', fieldMetadata, 'testField'),
|
||||
).toThrow(CommonQueryRunnerException);
|
||||
});
|
||||
});
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
ARRAY_FILTER_OPERATORS,
|
||||
BOOLEAN_FILTER_OPERATORS,
|
||||
DATE_FILTER_OPERATORS,
|
||||
ENUM_FILTER_OPERATORS,
|
||||
MULTI_SELECT_FILTER_OPERATORS,
|
||||
NUMBER_FILTER_OPERATORS,
|
||||
RAW_JSON_FILTER_OPERATORS,
|
||||
RICH_TEXT_V2_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';
|
||||
|
||||
export const 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:
|
||||
return ARRAY_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.MULTI_SELECT:
|
||||
return MULTI_SELECT_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.SELECT:
|
||||
case FieldMetadataType.RATING:
|
||||
return ENUM_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.RAW_JSON:
|
||||
case FieldMetadataType.FILES:
|
||||
return RAW_JSON_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.RICH_TEXT_V2:
|
||||
return RICH_TEXT_V2_FILTER_OPERATORS;
|
||||
|
||||
case FieldMetadataType.TS_VECTOR:
|
||||
case FieldMetadataType.ACTOR:
|
||||
case FieldMetadataType.ADDRESS:
|
||||
case FieldMetadataType.CURRENCY:
|
||||
case FieldMetadataType.EMAILS:
|
||||
case FieldMetadataType.FULL_NAME:
|
||||
case FieldMetadataType.LINKS:
|
||||
case FieldMetadataType.PHONES:
|
||||
return ['eq', 'neq', 'is'];
|
||||
|
||||
default:
|
||||
assertUnreachable(fieldType);
|
||||
}
|
||||
};
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
export const parseNumberValue = (
|
||||
value: unknown,
|
||||
fieldType:
|
||||
| FieldMetadataType.NUMBER
|
||||
| FieldMetadataType.NUMERIC
|
||||
| FieldMetadataType.POSITION,
|
||||
): unknown => {
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (fieldType) {
|
||||
case FieldMetadataType.NUMBER:
|
||||
case FieldMetadataType.NUMERIC:
|
||||
case FieldMetadataType.POSITION:
|
||||
return parseFloat(value);
|
||||
|
||||
default:
|
||||
assertUnreachable(fieldType);
|
||||
}
|
||||
};
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import type { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
import { validateAndTransformValueByFieldType } from './validate-and-transform-value-by-field-type.util';
|
||||
|
||||
export const validateAndTransformArrayItems = (
|
||||
values: unknown[],
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
fieldName: string,
|
||||
): unknown[] => {
|
||||
return values.map((item) => {
|
||||
if (item === null) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return validateAndTransformValueByFieldType(item, fieldMetadata, fieldName);
|
||||
});
|
||||
};
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
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 type { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
import { validateAndTransformValueOrThrow } from './validate-and-transform-value-or-throw.util';
|
||||
import { validateOperatorForFieldTypeOrThrow } from './validate-operator-for-field-type-or-throw.util';
|
||||
|
||||
export const validateAndTransformOperatorAndValue = (
|
||||
fieldName: string,
|
||||
filterValue: Record<string, unknown>,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
): Record<string, unknown> => {
|
||||
if (filterValue === null || typeof filterValue !== 'object') {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Filter value for field "${fieldName}" must be an object`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid filter: filter value must be an object`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const entries = Object.entries(filterValue);
|
||||
|
||||
if (entries.length !== 1) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Filter for field "${fieldName}" must have exactly one operator`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid filter: exactly one operator per field is required`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const [[operator, value]] = entries;
|
||||
|
||||
validateOperatorForFieldTypeOrThrow(
|
||||
operator as FilterOperator,
|
||||
fieldMetadata,
|
||||
fieldName,
|
||||
);
|
||||
|
||||
const transformedValue = validateAndTransformValueOrThrow(
|
||||
operator as FilterOperator,
|
||||
value,
|
||||
fieldMetadata,
|
||||
fieldName,
|
||||
);
|
||||
|
||||
return { [operator]: transformedValue };
|
||||
};
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { parseBooleanFromStringValue } from 'twenty-shared/workflow';
|
||||
|
||||
import { validateBooleanFieldOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/validator-utils/validate-boolean-field-or-throw.util';
|
||||
import { validateDateFieldOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/validator-utils/validate-date-field-or-throw.util';
|
||||
import { validateDateTimeFieldOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/validator-utils/validate-date-time-field-or-throw.util';
|
||||
import { validateNumberFieldOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/validator-utils/validate-number-field-or-throw.util';
|
||||
import { validateUUIDFieldOrThrow } from 'src/engine/api/common/common-args-processors/filter-arg-processor/validator-utils/validate-uuid-field-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
import { parseNumberValue } from './parse-number-value.util';
|
||||
|
||||
export const validateAndTransformValueByFieldType = (
|
||||
value: unknown,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
fieldName: string,
|
||||
): unknown => {
|
||||
const fieldType = fieldMetadata.type;
|
||||
|
||||
switch (fieldType) {
|
||||
case FieldMetadataType.NUMBER:
|
||||
case FieldMetadataType.NUMERIC:
|
||||
case FieldMetadataType.POSITION: {
|
||||
const coercedNumber = parseNumberValue(value, fieldType);
|
||||
|
||||
validateNumberFieldOrThrow(coercedNumber, fieldName);
|
||||
|
||||
return coercedNumber;
|
||||
}
|
||||
|
||||
case FieldMetadataType.BOOLEAN: {
|
||||
const coercedBoolean =
|
||||
typeof value === 'string'
|
||||
? parseBooleanFromStringValue(value.toString())
|
||||
: value;
|
||||
|
||||
validateBooleanFieldOrThrow(coercedBoolean, fieldName);
|
||||
|
||||
return coercedBoolean;
|
||||
}
|
||||
|
||||
case FieldMetadataType.UUID:
|
||||
case FieldMetadataType.RELATION:
|
||||
case FieldMetadataType.MORPH_RELATION:
|
||||
validateUUIDFieldOrThrow(value, fieldName);
|
||||
|
||||
return value;
|
||||
|
||||
case FieldMetadataType.DATE:
|
||||
validateDateFieldOrThrow(value, fieldName);
|
||||
|
||||
return value;
|
||||
|
||||
case FieldMetadataType.DATE_TIME:
|
||||
validateDateTimeFieldOrThrow(value, fieldName);
|
||||
|
||||
return value;
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { type FilterOperator } from 'src/engine/api/common/common-args-processors/filter-arg-processor/types/filter-operator.type';
|
||||
import type { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
import { validateAndTransformArrayItems } from './validate-and-transform-array-items.util';
|
||||
import { validateAndTransformValueByFieldType } from './validate-and-transform-value-by-field-type.util';
|
||||
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';
|
||||
|
||||
export const validateAndTransformValueOrThrow = (
|
||||
operator: FilterOperator,
|
||||
value: unknown,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
fieldName: string,
|
||||
): unknown => {
|
||||
switch (operator) {
|
||||
case 'is':
|
||||
validateIsOperatorFilterValueOrThrow(value);
|
||||
|
||||
return value;
|
||||
|
||||
case 'isEmptyArray':
|
||||
validateIsEmptyArrayOperatorValueOrThrow(value, fieldName);
|
||||
|
||||
return value;
|
||||
|
||||
case 'in':
|
||||
validateArrayOperatorValueOrThrow(value, operator, fieldName);
|
||||
|
||||
return validateAndTransformArrayItems(
|
||||
value as unknown[],
|
||||
fieldMetadata,
|
||||
fieldName,
|
||||
);
|
||||
|
||||
case 'containsAny':
|
||||
validateArrayOperatorValueOrThrow(value, operator, fieldName);
|
||||
|
||||
return value;
|
||||
|
||||
case 'eq':
|
||||
case 'neq':
|
||||
case 'gt':
|
||||
case 'gte':
|
||||
case 'lt':
|
||||
case 'lte':
|
||||
return validateAndTransformValueByFieldType(
|
||||
value,
|
||||
fieldMetadata,
|
||||
fieldName,
|
||||
);
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
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 validateArrayOperatorValueOrThrow = (
|
||||
value: unknown,
|
||||
operator: FilterOperator,
|
||||
fieldName: string,
|
||||
): void => {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Filter operator "${operator}" requires an array value for field ${fieldName}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid filter: "${operator}" operator requires an array`,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isBoolean } from 'class-validator';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const validateIsEmptyArrayOperatorValueOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): void => {
|
||||
if (!isBoolean(value)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Filter operator "isEmptyArray" requires a boolean value for field ${fieldName}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid filter: "isEmptyArray" operator requires a boolean`,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const validateIsOperatorFilterValueOrThrow = (value: unknown): void => {
|
||||
if (value !== 'NULL' && value !== 'NOT_NULL') {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid filter value for "is" operator. Expected "NULL" or "NOT_NULL"`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: msg`Invalid value for "is" operator` },
|
||||
);
|
||||
}
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
import { type FilterOperator } from 'src/engine/api/common/common-args-processors/filter-arg-processor/types/filter-operator.type';
|
||||
import { getOperatorsForFieldType } from 'src/engine/api/common/common-args-processors/filter-arg-processor/utils/get-operators-for-field-type.util';
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import type { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
export const validateOperatorForFieldTypeOrThrow = (
|
||||
operator: FilterOperator,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
fieldName: string,
|
||||
): void => {
|
||||
const allowedOperators = 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`,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isBoolean, isNull } from '@sniptt/guards';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const validateBooleanFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): boolean | null => {
|
||||
if (!isBoolean(value) && !isNull(value)) {
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid boolean value ${inspect(value)} for field "${fieldName}"`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: msg`Invalid value: "${inspectedValue}"` },
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDate, isNull, isString } from '@sniptt/guards';
|
||||
import { isValid, parse } from 'date-fns';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
const ACCEPTED_DATE_FORMATS = [
|
||||
'yyyy-MM-dd',
|
||||
'yyyyMMdd',
|
||||
'yyyy.MM.dd',
|
||||
'yyyy/MM/dd',
|
||||
'MM-dd-yyyy',
|
||||
'MM/dd/yyyy',
|
||||
'MM.dd.yyyy',
|
||||
'MMMM d, yyyy',
|
||||
'MMM d, yyyy',
|
||||
'd MMMM yyyy',
|
||||
'd MMM yyyy',
|
||||
'dd-MMM-yyyy',
|
||||
'yyyy-MMM-dd',
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSX",
|
||||
"yyyy-MM-dd'T'HH:mm:ssX",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS",
|
||||
"yyyy-MM-dd'T'HH:mm:ss",
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
'yyyy-MM-dd HH:mm:ss.SSS',
|
||||
];
|
||||
|
||||
const isValidDateFormat = (value: string): boolean => {
|
||||
for (const format of ACCEPTED_DATE_FORMATS) {
|
||||
const parsed = parse(value, format, new Date());
|
||||
|
||||
if (isValid(parsed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const validateDateFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): unknown => {
|
||||
if (isNull(value)) return null;
|
||||
|
||||
if (isDate(value) && isValid(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (isString(value) && isValidDateFormat(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid value ${inspectedValue} for date field "${fieldName}". Expected format: 'YYYY-MM-DD'`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid value for date: "${inspectedValue}". Expected format: 'YYYY-MM-DD'`,
|
||||
},
|
||||
);
|
||||
};
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDate, isNull, isString } from '@sniptt/guards';
|
||||
import { isValid, parse } from 'date-fns';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
const ACCEPTED_DATE_TIME_FORMATS = [
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSX",
|
||||
"yyyy-MM-dd'T'HH:mm:ssX",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSxxx",
|
||||
"yyyy-MM-dd'T'HH:mm:ssxxx",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS",
|
||||
"yyyy-MM-dd'T'HH:mm:ss",
|
||||
'yyyy-MM-dd HH:mm:ss.SSS',
|
||||
'yyyy-MM-dd HH:mm:ss',
|
||||
'yyyy-MM-dd HH:mm',
|
||||
'yyyy-MM-dd',
|
||||
'yyyyMMdd',
|
||||
'yyyy.MM.dd',
|
||||
'yyyy/MM/dd',
|
||||
'MM-dd-yyyy',
|
||||
'MM/dd/yyyy',
|
||||
'MM.dd.yyyy',
|
||||
'MMMM d, yyyy',
|
||||
'MMM d, yyyy',
|
||||
'd MMMM yyyy',
|
||||
'd MMM yyyy',
|
||||
'dd-MMM-yyyy',
|
||||
'yyyy-MMM-dd',
|
||||
];
|
||||
|
||||
const isValidDateTimeFormat = (value: string): boolean => {
|
||||
for (const format of ACCEPTED_DATE_TIME_FORMATS) {
|
||||
const parsed = parse(value, format, new Date());
|
||||
|
||||
if (isValid(parsed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const validateDateTimeFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): unknown => {
|
||||
if (isNull(value)) return null;
|
||||
|
||||
if (isDate(value) && isValid(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (isString(value) && isValidDateTimeFormat(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid value ${inspectedValue} for date-time field "${fieldName}". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid value for date-time: "${inspectedValue}". Expected format: 'YYYY-MM-DDTHH:mm:ssZ'`,
|
||||
},
|
||||
);
|
||||
};
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNull, isNumber } from '@sniptt/guards';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const validateNumberFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): number | null => {
|
||||
if (
|
||||
(!isNumber(value) && !isNull(value)) ||
|
||||
(isNumber(value) &&
|
||||
(isNaN(value) || value === Infinity || value === -Infinity))
|
||||
) {
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid number value ${inspectedValue} for field "${fieldName}"`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{
|
||||
userFriendlyMessage: msg`Invalid value for number: "${inspectedValue}"`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNonEmptyString, isNull } from '@sniptt/guards';
|
||||
import { isValidUuid } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const validateUUIDFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): string | null => {
|
||||
if (
|
||||
(!isNonEmptyString(value) && !isNull(value)) ||
|
||||
(isNonEmptyString(value) && !isValidUuid(value))
|
||||
) {
|
||||
const inspectedValue = inspect(value);
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid UUID value ${inspectedValue} for field "${fieldName}"`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_FILTER,
|
||||
{ userFriendlyMessage: msg`Invalid value for UUID: "${inspectedValue}"` },
|
||||
);
|
||||
}
|
||||
|
||||
return value as string;
|
||||
};
|
||||
-87
@@ -1,15 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { 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 { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
|
||||
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 { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
@@ -18,87 +12,6 @@ export class QueryRunnerArgsFactory {
|
||||
private readonly recordInputTransformerService: RecordInputTransformerService,
|
||||
) {}
|
||||
|
||||
public overrideFilterByFieldMetadata<
|
||||
T extends ObjectRecordFilter | undefined,
|
||||
>(
|
||||
filter: T,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): T {
|
||||
if (!isDefined(filter)) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const overrideFilter = (filterObject: ObjectRecordFilter) => {
|
||||
return Object.entries(filterObject).reduce((acc, [key, value]) => {
|
||||
if (key === 'and' || key === 'or') {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
acc[key] = value.map((nestedFilter: ObjectRecordFilter) =>
|
||||
overrideFilter(nestedFilter),
|
||||
);
|
||||
} else if (key === 'not') {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
acc[key] = overrideFilter(value);
|
||||
} else {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
acc[key] = this.transformFilterValueByType(
|
||||
key,
|
||||
value,
|
||||
fieldIdByName,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
return overrideFilter(filter) as T;
|
||||
}
|
||||
|
||||
private transformFilterValueByType(
|
||||
key: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any,
|
||||
fieldIdByName: Record<string, string>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
const fieldMetadataId = fieldIdByName[key];
|
||||
const fieldMetadata = fieldMetadataId
|
||||
? findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
if (!fieldMetadata) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Special handling for filter values, which have a specific structure
|
||||
switch (fieldMetadata.type) {
|
||||
case FieldMetadataType.NUMBER: {
|
||||
if (value?.is === 'NULL') {
|
||||
return value;
|
||||
} else {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([filterKey, filterValue]) => [
|
||||
filterKey,
|
||||
Number(filterValue),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
async overrideValueByFieldMetadata(
|
||||
key: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
+5
-2
@@ -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()
|
||||
|
||||
+3
-3
@@ -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,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+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
@@ -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,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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,
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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],
|
||||
|
||||
+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',
|
||||
|
||||
+1
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user