diff --git a/packages/twenty-server/src/metadata/field-metadata/utils/is-enum-field-metadata-type.util.ts b/packages/twenty-server/src/metadata/field-metadata/utils/is-enum-field-metadata-type.util.ts index 2eae3bad1..88dbd6404 100644 --- a/packages/twenty-server/src/metadata/field-metadata/utils/is-enum-field-metadata-type.util.ts +++ b/packages/twenty-server/src/metadata/field-metadata/utils/is-enum-field-metadata-type.util.ts @@ -1,11 +1,13 @@ import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; +export type EnumFieldMetadataUnionType = + | FieldMetadataType.RATING + | FieldMetadataType.SELECT + | FieldMetadataType.MULTI_SELECT; + export const isEnumFieldMetadataType = ( type: FieldMetadataType, -): type is - | FieldMetadataType.RATING - | FieldMetadataType.SELECT - | FieldMetadataType.MULTI_SELECT => { +): type is EnumFieldMetadataUnionType => { return ( type === FieldMetadataType.RATING || type === FieldMetadataType.SELECT || diff --git a/packages/twenty-server/src/workspace/workspace-health/services/database-structure.service.ts b/packages/twenty-server/src/workspace/workspace-health/services/database-structure.service.ts index c5260ee7a..d7ff40adc 100644 --- a/packages/twenty-server/src/workspace/workspace-health/services/database-structure.service.ts +++ b/packages/twenty-server/src/workspace/workspace-health/services/database-structure.service.ts @@ -10,7 +10,10 @@ import { import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface'; import { TypeORMService } from 'src/database/typeorm/typeorm.service'; -import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; +import { + FieldMetadataEntity, + FieldMetadataType, +} from 'src/metadata/field-metadata/field-metadata.entity'; import { fieldMetadataTypeToColumnType } from 'src/metadata/workspace-migration/utils/field-metadata-type-to-column-type.util'; import { serializeTypeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-type-default-value.util'; @@ -122,17 +125,17 @@ export class DatabaseStructureService { })); } - getPostgresDataType( - fieldMetadataType: FieldMetadataType, - fieldMetadataName: string, - objectMetadataNameSingular: string, - ): string { - const typeORMType = fieldMetadataTypeToColumnType(fieldMetadataType); + getPostgresDataType(fieldMetadata: FieldMetadataEntity): string { + const typeORMType = fieldMetadataTypeToColumnType(fieldMetadata.type); const mainDataSource = this.typeORMService.getMainDataSource(); - // TODO: remove special case for enum type, should we include this to fieldMetadataTypeToColumnType? + // Compute enum name to compare data type properly if (typeORMType === 'enum') { - return `${objectMetadataNameSingular}_${fieldMetadataName}_enum`; + const objectName = fieldMetadata.object?.nameSingular; + const prefix = fieldMetadata.isCustom ? '_' : ''; + const fieldName = fieldMetadata.name; + + return `${objectName}_${prefix}${fieldName}_enum`; } return mainDataSource.driver.normalizeType({ diff --git a/packages/twenty-server/src/workspace/workspace-health/services/field-metadata-health.service.ts b/packages/twenty-server/src/workspace/workspace-health/services/field-metadata-health.service.ts index 9fd68d352..6e14db6e9 100644 --- a/packages/twenty-server/src/workspace/workspace-health/services/field-metadata-health.service.ts +++ b/packages/twenty-server/src/workspace/workspace-health/services/field-metadata-health.service.ts @@ -6,6 +6,7 @@ import { } from 'src/workspace/workspace-health/interfaces/workspace-health-issue.interface'; import { WorkspaceTableStructure } from 'src/workspace/workspace-health/interfaces/workspace-table-definition.interface'; import { WorkspaceHealthOptions } from 'src/workspace/workspace-health/interfaces/workspace-health-options.interface'; +import { FieldMetadataDefaultValue } from 'src/metadata/field-metadata/interfaces/field-metadata-default-value.interface'; import { FieldMetadataEntity, @@ -16,8 +17,12 @@ import { DatabaseStructureService } from 'src/workspace/workspace-health/service import { validName } from 'src/workspace/workspace-health/utils/valid-name.util'; import { compositeDefinitions } from 'src/metadata/field-metadata/composite-types'; import { validateDefaultValueForType } from 'src/metadata/field-metadata/utils/validate-default-value-for-type.util'; -import { isEnumFieldMetadataType } from 'src/metadata/field-metadata/utils/is-enum-field-metadata-type.util'; +import { + EnumFieldMetadataUnionType, + isEnumFieldMetadataType, +} from 'src/metadata/field-metadata/utils/is-enum-field-metadata-type.util'; import { validateOptionsForType } from 'src/metadata/field-metadata/utils/validate-options-for-type.util'; +import { serializeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-default-value'; @Injectable() export class FieldMetadataHealthService { @@ -118,11 +123,8 @@ export class FieldMetadataHealthService { const issues: WorkspaceHealthIssue[] = []; const columnName = fieldMetadata.targetColumnMap.value; - const dataType = this.databaseStructureService.getPostgresDataType( - fieldMetadata.type, - fieldMetadata.name, - fieldMetadata.object?.nameSingular, - ); + const dataType = + this.databaseStructureService.getPostgresDataType(fieldMetadata); const defaultValue = this.databaseStructureService.getPostgresDefault( fieldMetadata.type, @@ -144,6 +146,8 @@ export class FieldMetadataHealthService { return issues; } + const columnDefaultValue = columnStructure.columnDefault?.split('::')?.[0]; + // Check if column data type is the same if (columnStructure.dataType !== dataType) { issues.push({ @@ -167,8 +171,27 @@ export class FieldMetadataHealthService { if ( defaultValue && - columnStructure.columnDefault && - !columnStructure.columnDefault.startsWith(defaultValue) + columnDefaultValue && + isEnumFieldMetadataType(fieldMetadata.type) + ) { + const enumValues = fieldMetadata.options?.map((option) => + serializeDefaultValue(option.value), + ); + + if (!enumValues.includes(columnDefaultValue)) { + issues.push({ + type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID, + fieldMetadata, + columnStructure, + message: `Column ${columnName} default value is not in the enum values "${columnDefaultValue}" NOT IN "${enumValues}"`, + }); + } + } + + if ( + defaultValue && + columnDefaultValue && + columnDefaultValue !== defaultValue ) { issues.push({ type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_CONFLICT, @@ -314,6 +337,24 @@ export class FieldMetadataHealthService { }); } + if ( + isEnumFieldMetadataType(fieldMetadata.type) && + fieldMetadata.defaultValue + ) { + const enumValues = fieldMetadata.options?.map((option) => option.value); + const metadataDefaultValue = ( + fieldMetadata.defaultValue as FieldMetadataDefaultValue + )?.value; + + if (metadataDefaultValue && !enumValues.includes(metadataDefaultValue)) { + issues.push({ + type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID, + fieldMetadata, + message: `Column default value is not in the enum values "${metadataDefaultValue}" NOT IN "${enumValues}"`, + }); + } + } + return issues; }