Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code b709da5825 fix: keep RICH_TEXT_V2 DB value in FieldMetadataType enum for v1.19.x compatibility
https://sonarly.com/issue/17827?type=bug

Frontend is completely broken on v1.19.1 because the FieldMetadataType GraphQL enum no longer accepts the RICH_TEXT_V2 value still stored in the database, causing all metadata queries to fail.

Fix: ## Fix: Restore RICH_TEXT_V2 as the enum value for FieldMetadataType.RICH_TEXT

The core issue is that commit `d9eb317bb5` changed `RICH_TEXT = 'RICH_TEXT_V2'` to `RICH_TEXT = 'RICH_TEXT'`, despite claiming to "keep the DB value." This caused the GraphQL enum (built via `registerEnumType`) to only accept `RICH_TEXT`, while the database still stores `RICH_TEXT_V2`.

### Changes:

1. **`packages/twenty-shared/src/types/FieldMetadataType.ts`** — Restored the enum value from `'RICH_TEXT'` to `'RICH_TEXT_V2'`. This is the single-line fix that resolves the GraphQL serialization error. The GraphQL schema will now correctly accept `RICH_TEXT_V2` values from the database.

2. **`packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-migrate-rich-text-to-text.command.ts`** — Removed the `RICH_TEXT_V2 → RICH_TEXT` rename SQL since the DB value should remain `RICH_TEXT_V2`. The command now only handles migrating deprecated V1 `RICH_TEXT` fields to `TEXT`.

3. **`packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/is-text-column-type.util.ts`** — Replaced `(type as string) === 'RICH_TEXT_V2'` with `type === FieldMetadataType.RICH_TEXT` since the enum value now correctly matches the DB value. The raw string fallback for `'RICH_TEXT'` (V1) is kept for pre-upgrade compatibility.

### Follow-up required:
- Run `npx nx run twenty-front:graphql:generate` and `npx nx run twenty-front:graphql:generate --configuration=metadata` to regenerate GraphQL types
- After codegen, update `CompositeFieldType.ts` and `assertFieldMetadata.ts` to use `'RICH_TEXT_V2'` string literals (they currently use `'RICH_TEXT'` to match the generated enum)
- Rebuild `twenty-shared` before other packages
2026-03-24 10:02:48 +00:00
3 changed files with 8 additions and 25 deletions
@@ -20,7 +20,7 @@ import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/wor
@Command({
name: 'upgrade:1-20:migrate-rich-text-to-text',
description:
'Migrate deprecated RICH_TEXT (V1) to TEXT and rename RICH_TEXT_V2 to RICH_TEXT. The underlying column type is already text, so only the metadata needs updating.',
'Migrate deprecated RICH_TEXT (V1) to TEXT. The underlying column type is already text, so only the metadata needs updating.',
})
export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@@ -59,7 +59,7 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
if (dryRun) {
this.logger.log(
`[DRY RUN] Would update RICH_TEXT -> TEXT and RICH_TEXT_V2 -> RICH_TEXT in core.fieldMetadata for workspace ${workspaceId}. Skipping.`,
`[DRY RUN] Would update RICH_TEXT (V1) -> TEXT in core.fieldMetadata for workspace ${workspaceId}. Skipping.`,
);
return;
@@ -84,17 +84,6 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
const v1Count = v1Result.length;
const renameResult = await queryRunner.query(
`UPDATE core."fieldMetadata"
SET "type" = 'RICH_TEXT'
WHERE "workspaceId" = $1
AND "type" = 'RICH_TEXT_V2'
RETURNING "id"`,
[workspaceId],
);
const renameCount = renameResult.length;
await queryRunner.commitTransaction();
if (v1Count > 0) {
@@ -103,22 +92,16 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
);
}
if (renameCount > 0) {
this.logger.log(
`Renamed ${renameCount} RICH_TEXT_V2 field(s) to RICH_TEXT in workspace ${workspaceId}`,
);
}
await this.featureFlagService.enableFeatureFlags(
[FeatureFlagKey.IS_RICH_TEXT_V1_MIGRATED],
workspaceId,
);
if (v1Count > 0 || renameCount > 0) {
if (v1Count > 0) {
await this.invalidateCaches(workspaceId);
} else {
this.logger.log(
`No RICH_TEXT or RICH_TEXT_V2 fields found in workspace ${workspaceId}`,
`No RICH_TEXT (V1) fields found in workspace ${workspaceId}`,
);
}
} catch (error) {
@@ -1,12 +1,12 @@
import { FieldMetadataType } from 'twenty-shared/types';
// Raw strings kept for pre-upgrade workspaces that haven't yet run
// the 1.20 migrate-rich-text-to-text / rename-rich-text-v2 commands.
// Raw string kept for pre-upgrade workspaces that haven't yet run
// the 1.20 migrate-rich-text-to-text command (RICH_TEXT V1 → TEXT).
export const isTextColumnType = (type: FieldMetadataType) => {
return (
type === FieldMetadataType.TEXT ||
type === FieldMetadataType.ARRAY ||
(type as string) === 'RICH_TEXT_V2' ||
type === FieldMetadataType.RICH_TEXT ||
(type as string) === 'RICH_TEXT'
);
};
@@ -19,7 +19,7 @@ export enum FieldMetadataType {
RATING = 'RATING',
RAW_JSON = 'RAW_JSON',
RELATION = 'RELATION',
RICH_TEXT = 'RICH_TEXT',
RICH_TEXT = 'RICH_TEXT_V2',
SELECT = 'SELECT',
TEXT = 'TEXT',
TS_VECTOR = 'TS_VECTOR',