Deprecate legacy RICH_TEXT field metadata type (#18623)

## Summary

- Removes the deprecated `RICH_TEXT` (V1) field metadata type from the
codebase entirely
- Adds a 1.20 upgrade command that migrates existing `RICH_TEXT` fields
to `TEXT` in `core.fieldMetadata`
- Cleans up ~70 files across `twenty-shared`, `twenty-server`,
`twenty-front`, `twenty-sdk`, and `twenty-zapier`

## Context

`RICH_TEXT` was a legacy field type that stored rich text as a single
`text` column. It was already **read-only** — writes threw errors
directing users to `RICH_TEXT_V2` instead. `RICH_TEXT_V2` is the current
approach: a composite type with `blocknote` (editor JSON) and `markdown`
subfields. Keeping the deprecated type added maintenance burden without
any value.

Since the underlying database column type for `RICH_TEXT` was already
`text` (same as `TEXT`), the migration only needs to update the metadata
— no data migration or column changes required.

## Changes

### Upgrade command (new)
- `1-20-migrate-rich-text-to-text.command.ts` — runs `UPDATE
core."fieldMetadata" SET "type" = 'TEXT' WHERE "type" = 'RICH_TEXT'` per
workspace, with cache invalidation

### Enum & shared types
- Removed `RICH_TEXT` from `FieldMetadataType` enum
- Removed from `FieldMetadataDefaultValueMapping`,
`isFieldMetadataTextKind`

### Server (~30 files)
- Removed from type mapper (scalar, filter, order-by), data processors,
input transformer, filter operators, zod schemas, column type mapping,
searchable fields, RLS matching, OpenAPI schema, fake value generators
- Removed from field creation flow and field metadata type validator
- Updated dev seeder Pet `bio` field to `TEXT`
- Cleaned up mocks, snapshots, integration tests

### Frontend (~25 files)
- Deleted: `RichTextFieldDisplay`, `isFieldRichText`,
`isFieldRichTextValue`, `useRichTextFieldDisplay`
- Removed from `FieldDisplay`, `usePersistField`, `isFieldValueEmpty`,
`isRecordMatchingFilter`, `generateEmptyFieldValue`,
`isFieldCellSupported`, spreadsheet import, workflow fake values
- Removed from settings types, field type configs, and field creation
exclusion list
- Updated tests, mocks, and stories

### SDK & Zapier
- Removed from generated GraphQL schema and TypeScript types
- Removed from Zapier `computeInputFields`
This commit is contained in:
Charles Bochet
2026-03-13 17:25:40 +01:00
committed by GitHub
parent 49bdcd6bd5
commit 46e515436e
71 changed files with 165 additions and 368 deletions
@@ -172,8 +172,6 @@ exports[`DataArgProcessorService failing inputs validation RELATION should throw
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"`;
@@ -96,7 +96,6 @@ export const failingInputsByFieldMetadataType: {
{ input: { booleanField: 'string' } },
{ input: { booleanField: 1 } },
],
[FieldMetadataType.RICH_TEXT]: [{ input: { richTextField: 'test' } }],
[FieldMetadataType.ADDRESS]: [
{ input: { addressField: 'not-an-address' } },
{ input: { addressField: 1 } },
@@ -139,11 +139,6 @@ export const fieldMetadataConfigByFieldName: Record<
type: FieldMetadataType.RICH_TEXT_V2,
isNullable: true,
},
richTextField: {
name: 'richTextField',
type: FieldMetadataType.RICH_TEXT,
isNullable: true,
},
position: {
name: 'position',
type: FieldMetadataType.POSITION,
@@ -312,7 +312,6 @@ export class DataArgProcessorService {
return transformLinksValue(validatedValue);
}
case FieldMetadataType.RICH_TEXT:
case FieldMetadataType.TS_VECTOR:
throw new CommonQueryRunnerException(
`${key} ${fieldMetadata.type}-typed field does not support write operations`,
@@ -140,10 +140,6 @@ exports[`FilterArgProcessorService failing filter inputs validation RELATION sho
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"`;
@@ -11,10 +11,6 @@ export const failingFilterInputsByFieldMetadataType: {
{ 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: {} } } },
@@ -21,13 +21,6 @@ export const successfulFilterInputsByFieldMetadataType: {
{ 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 } } },
@@ -20,7 +20,6 @@ export const getOperatorsForFieldType = (
): FilterOperator[] => {
switch (fieldType) {
case FieldMetadataType.TEXT:
case FieldMetadataType.RICH_TEXT:
return STRING_FILTER_OPERATORS;
case FieldMetadataType.NUMBER: