diff --git a/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts index b7ab14508..44e69c6dc 100644 --- a/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts @@ -101,7 +101,7 @@ export class ObjectMetadataService extends TypeOrmQueryService { constructor( @@ -56,7 +55,7 @@ export class RelationMetadataService extends TypeOrmQueryService { const baseColumnName = `${camelCase(name)}Id`; diff --git a/packages/twenty-server/src/metadata/relation-metadata/utils/create-relation-foreign-key-field-metadata-name.util.ts b/packages/twenty-server/src/metadata/relation-metadata/utils/create-relation-foreign-key-field-metadata-name.util.ts new file mode 100644 index 000000000..4ddb07c9d --- /dev/null +++ b/packages/twenty-server/src/metadata/relation-metadata/utils/create-relation-foreign-key-field-metadata-name.util.ts @@ -0,0 +1,5 @@ +import { camelCase } from 'src/utils/camel-case'; + +export const createRelationForeignKeyFieldMetadataName = (name: string) => { + return `${camelCase(name)}Id`; +}; 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 1261338b5..abb325b11 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 @@ -63,7 +63,10 @@ export class DatabaseStructureService { c.table_schema AS "tableSchema", c.table_name AS "tableName", c.column_name AS "columnName", - c.data_type AS "dataType", + CASE + WHEN (c.data_type = 'USER-DEFINED') THEN c.udt_name + ELSE data_type + END AS "dataType", c.is_nullable AS "isNullable", c.column_default AS "columnDefault", CASE @@ -116,10 +119,19 @@ export class DatabaseStructureService { })); } - getPostgresDataType(fieldMetadataType: FieldMetadataType): string { + getPostgresDataType( + fieldMetadataType: FieldMetadataType, + fieldMetadataName: string, + objectMetadataNameSingular: string, + ): string { const typeORMType = fieldMetadataTypeToColumnType(fieldMetadataType); const mainDataSource = this.typeORMService.getMainDataSource(); + // TODO: remove special case for enum type, should we include this to fieldMetadataTypeToColumnType? + if (typeORMType === 'enum') { + return `${objectMetadataNameSingular}_${fieldMetadataName}_enum`; + } + return mainDataSource.driver.normalizeType({ type: typeORMType, }); 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 190ef5354..9fd68d352 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 @@ -117,9 +117,13 @@ export class FieldMetadataHealthService { ): WorkspaceHealthIssue[] { const issues: WorkspaceHealthIssue[] = []; const columnName = fieldMetadata.targetColumnMap.value; + const dataType = this.databaseStructureService.getPostgresDataType( fieldMetadata.type, + fieldMetadata.name, + fieldMetadata.object?.nameSingular, ); + const defaultValue = this.databaseStructureService.getPostgresDefault( fieldMetadata.type, fieldMetadata.defaultValue, @@ -155,7 +159,9 @@ export class FieldMetadataHealthService { type: WorkspaceHealthIssueType.COLUMN_NULLABILITY_CONFLICT, fieldMetadata, columnStructure, - message: `Column ${columnName} is not nullable as expected`, + message: `Column ${columnName} is expected to be ${ + fieldMetadata.isNullable ? 'nullable' : 'not nullable' + } but is ${columnStructure.isNullable ? 'nullable' : 'not nullable'}`, }); } diff --git a/packages/twenty-server/src/workspace/workspace-health/services/relation-metadata.health.service.ts b/packages/twenty-server/src/workspace/workspace-health/services/relation-metadata.health.service.ts index 8946fa949..80e8a2b9f 100644 --- a/packages/twenty-server/src/workspace/workspace-health/services/relation-metadata.health.service.ts +++ b/packages/twenty-server/src/workspace/workspace-health/services/relation-metadata.health.service.ts @@ -18,12 +18,13 @@ import { RelationMetadataEntity, RelationMetadataType, } from 'src/metadata/relation-metadata/relation-metadata.entity'; -import { createRelationMetadataForeignKey } from 'src/metadata/relation-metadata/utils/create-relation-metadata-foreign-key.util'; import { RelationDirection, deduceRelationDirection, } from 'src/workspace/utils/deduce-relation-direction.util'; import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity'; +import { createRelationForeignKeyColumnName } from 'src/metadata/relation-metadata/utils/create-relation-foreign-key-column-name.util'; +import { createRelationForeignKeyFieldMetadataName } from 'src/metadata/relation-metadata/utils/create-relation-foreign-key-field-metadata-name.util'; @Injectable() export class RelationMetadataHealthService { @@ -136,7 +137,7 @@ export class RelationMetadataHealthService { } const isCustom = toFieldMetadata.isCustom ?? false; - const foreignKeyColumnName = createRelationMetadataForeignKey( + const foreignKeyColumnName = createRelationForeignKeyColumnName( toFieldMetadata.name, isCustom, ); @@ -144,16 +145,34 @@ export class RelationMetadataHealthService { (column) => column.columnName === foreignKeyColumnName, ); const relationFieldMetadata = toObjectMetadataFields.find( - (fieldMetadata) => fieldMetadata.name === foreignKeyColumnName, + (fieldMetadata) => + fieldMetadata.name === + createRelationForeignKeyFieldMetadataName(toFieldMetadata.name), ); - if (!relationColumn || !relationFieldMetadata) { + if (!relationFieldMetadata) { issues.push({ type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID, fromFieldMetadata, toFieldMetadata, relationMetadata, - message: `Relation ${relationMetadata.id} doesn't have a valid foreign key`, + message: `Relation ${ + relationMetadata.id + } doesn't have a valid foreign key (expected fieldMetadata.name to be ${createRelationForeignKeyFieldMetadataName( + toFieldMetadata.name, + )}`, + }); + + return issues; + } + + if (!relationColumn) { + issues.push({ + type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID, + fromFieldMetadata, + toFieldMetadata, + relationMetadata, + message: `Relation ${relationMetadata.id} doesn't have a valid foreign key (expected column name to be ${foreignKeyColumnName}`, }); return issues;