Add basic UUID support to filters (#12676)

- Only operands IS
- Do not set filter when no valid uuids or variables
- Allow ID field to be filterable despite being system



https://github.com/user-attachments/assets/e1c67103-728f-4798-91c6-4aea162f8698

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Thomas Trompette
2025-06-17 14:58:50 +02:00
committed by GitHub
parent 01a69f6288
commit fb9d5066dc
9 changed files with 59 additions and 2 deletions

View File

@ -51,6 +51,8 @@ export const getFilterTypeFromFieldType = (
return 'BOOLEAN';
case FieldMetadataType.TS_VECTOR:
return 'TS_VECTOR';
case FieldMetadataType.UUID:
return 'UUID';
default:
return 'TEXT';
}

View File

@ -9,6 +9,7 @@ export const getFilterFilterableFieldMetadataItems = ({
return (field: FieldMetadataItem) => {
const isSystemField = field.isSystem;
const isFieldActive = field.isActive;
const isIdField = field.name === 'id';
const isRelationFieldHandled = !(
field.type === FieldMetadataType.RELATION &&
@ -33,11 +34,12 @@ export const getFilterFilterableFieldMetadataItems = ({
FieldMetadataType.ACTOR,
FieldMetadataType.PHONES,
FieldMetadataType.ARRAY,
FieldMetadataType.UUID,
...(isJsonFilterEnabled ? [FieldMetadataType.RAW_JSON] : []),
].includes(field.type);
const isFieldFilterable =
!isSystemField &&
(!isSystemField || isIdField) &&
isFieldActive &&
isRelationFieldHandled &&
isFieldTypeFilterable;

View File

@ -7,4 +7,5 @@ export const TEXT_FILTER_TYPES = [
'LINKS',
'ARRAY',
'RAW_JSON',
'UUID',
];

View File

@ -20,6 +20,7 @@ export const FILTERABLE_FIELD_TYPES = [
'ARRAY',
'RAW_JSON',
'BOOLEAN',
'UUID',
] as const;
type FilterableFieldTypeBaseLiteral = (typeof FILTERABLE_FIELD_TYPES)[number];

View File

@ -17,6 +17,7 @@ import {
SelectFilter,
StringFilter,
TSVectorFilter,
UUIDFilter,
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { Field } from '~/generated/graphql';
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
@ -1176,6 +1177,24 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
} as BooleanFilter,
};
}
case 'UUID': {
const recordIds = arrayOfUuidOrVariableSchema.parse(recordFilter.value);
if (recordIds.length === 0) return;
switch (recordFilter.operand) {
case RecordFilterOperand.Is:
return {
[correspondingFieldMetadataItem.name]: {
in: recordIds,
} as UUIDFilter,
};
default:
throw new Error(
`Unknown operand ${recordFilter.operand} for ${filterType} filter`,
);
}
}
default:
throw new Error('Unknown filter type');
}

View File

@ -128,6 +128,7 @@ export const FILTER_OPERANDS_MAP = {
],
BOOLEAN: [RecordFilterOperand.Is],
TS_VECTOR: [RecordFilterOperand.VectorSearch],
UUID: [RecordFilterOperand.Is],
} as const satisfies FilterOperandMap;
export const COMPOSITE_FIELD_FILTER_OPERANDS_MAP = {
@ -204,6 +205,8 @@ export const getRecordFilterOperands = ({
return FILTER_OPERANDS_MAP.BOOLEAN;
case 'TS_VECTOR':
return FILTER_OPERANDS_MAP.TS_VECTOR;
case 'UUID':
return FILTER_OPERANDS_MAP.UUID;
default:
assertUnreachable(filterType, `Unknown filter type ${filterType}`);
}

View File

@ -497,4 +497,15 @@ describe('buildValueFromFilter', () => {
expect(buildValueFromFilter({ filter })).toBeUndefined();
});
});
describe('UUID field type', () => {
it('should return the value', () => {
const filter = createTestFilter(
ViewFilterOperand.Is,
'test-uuid',
'UUID',
);
expect(buildValueFromFilter({ filter })).toBe('test-uuid');
});
});
});

View File

@ -96,6 +96,11 @@ export const buildValueFromFilter = ({
label,
);
}
case 'UUID':
return computeValueFromFilterUUID(
filter.operand as (typeof FILTER_OPERANDS_MAP)['UUID'][number],
filter.value,
);
default:
assertUnreachable(filter.type);
}
@ -310,3 +315,15 @@ const computeValueFromFilterTSVector = (
assertUnreachable(operand);
}
};
const computeValueFromFilterUUID = (
operand: RecordFilterToRecordInputOperand<'UUID'>,
value: string,
) => {
switch (operand) {
case ViewFilterOperand.Is:
return value;
default:
assertUnreachable(operand);
}
};

View File

@ -34,6 +34,7 @@ export const shouldDisplayFormField = ({
actionType: WorkflowActionType;
}) => {
let isTypeAllowedForAction = false;
const isIdField = fieldMetadataItem.name === 'id';
switch (actionType) {
case 'CREATE_RECORD':
@ -57,7 +58,7 @@ export const shouldDisplayFormField = ({
return (
isTypeAllowedForAction &&
!fieldMetadataItem.isSystem &&
(!fieldMetadataItem.isSystem || isIdField) &&
fieldMetadataItem.isActive
);
};