[Apps] Fix - app-synced object should be searchable (#19206)

## Summary

- **Make app-synced objects searchable**: `isSearchable` was hardcoded
to `false` and the `searchVector` field was missing the `GENERATED
ALWAYS AS (...)` expression, causing all records to have a `NULL` search
vector and be excluded from search results. Fixed by defaulting
`isSearchable` to `true` (configurable via the object manifest),
computing the `asExpression` from the label identifier field, and
allowing the update-field-action-handler to handle the `null` → defined
`asExpression` transition.
- **Make `isSearchable` updatable on an object**: The property had
`toCompare: false` in the entity properties configuration, so updates
via the API were silently ignored and never persisted. Fixed by setting
`toCompare: true`.
This commit is contained in:
Marie
2026-04-02 19:14:37 +02:00
committed by GitHub
parent 43baf7b91c
commit 2d6c8be7df
34 changed files with 639 additions and 51 deletions
@@ -8,6 +8,7 @@ export type ObjectManifest = SyncableEntityOptions & {
labelPlural: string;
description?: string;
icon?: string;
isSearchable?: boolean;
fields: ObjectFieldManifest[];
labelIdentifierFieldMetadataUniversalIdentifier: string;
};
@@ -204,6 +204,8 @@ export { assertIsDefinedOrThrow } from './validation/assertIsDefinedOrThrow';
export { isDefined } from './validation/isDefined';
export { isEmptyObject } from './validation/isEmptyObject';
export { isLabelIdentifierFieldMetadataTypes } from './validation/isLabelIdentifierFieldMetadataTypes';
export type { SearchableFieldType } from './validation/isSearchableFieldType';
export { isSearchableFieldType } from './validation/isSearchableFieldType';
export { isValidLocale } from './validation/isValidLocale';
export { isValidTwentySubdomain } from './validation/isValidTwentySubdomain';
export { isValidUuid } from './validation/isValidUuid';
@@ -2,5 +2,6 @@ export * from './isDefined';
export * from './assertIsDefinedOrThrow';
export * from './isValidLocale';
export * from './isValidTwentySubdomain';
export * from './isSearchableFieldType';
export * from './isValidUuid';
export * from './normalizeLocale';
@@ -0,0 +1,20 @@
import { FieldMetadataType } from '@/types';
const SEARCHABLE_FIELD_TYPES = [
FieldMetadataType.TEXT,
FieldMetadataType.FULL_NAME,
FieldMetadataType.EMAILS,
FieldMetadataType.ADDRESS,
FieldMetadataType.LINKS,
FieldMetadataType.PHONES,
FieldMetadataType.RICH_TEXT,
FieldMetadataType.UUID,
] as const;
export type SearchableFieldType = (typeof SEARCHABLE_FIELD_TYPES)[number];
export const isSearchableFieldType = (
type: FieldMetadataType,
): type is SearchableFieldType => {
return SEARCHABLE_FIELD_TYPES.includes(type as SearchableFieldType);
};