Improve health check command (#3553)
* Improve health check command * Fix health check * Fix health check
This commit is contained in:
@ -101,7 +101,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
},
|
},
|
||||||
icon: 'Icon123',
|
icon: 'Icon123',
|
||||||
description: 'Id',
|
description: 'Id',
|
||||||
isNullable: true,
|
isNullable: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
@ -117,7 +117,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
},
|
},
|
||||||
icon: 'IconAbc',
|
icon: 'IconAbc',
|
||||||
description: 'Name',
|
description: 'Name',
|
||||||
isNullable: true,
|
isNullable: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
workspaceId: objectMetadataInput.workspaceId,
|
workspaceId: objectMetadataInput.workspaceId,
|
||||||
@ -132,7 +132,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
},
|
},
|
||||||
icon: 'IconCalendar',
|
icon: 'IconCalendar',
|
||||||
description: 'Creation date',
|
description: 'Creation date',
|
||||||
isNullable: true,
|
isNullable: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
workspaceId: objectMetadataInput.workspaceId,
|
workspaceId: objectMetadataInput.workspaceId,
|
||||||
@ -147,7 +147,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
},
|
},
|
||||||
icon: 'IconCalendar',
|
icon: 'IconCalendar',
|
||||||
description: 'Update date',
|
description: 'Update date',
|
||||||
isNullable: true,
|
isNullable: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
@ -245,7 +245,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||||
columnName: 'name',
|
columnName: 'name',
|
||||||
columnType: 'varchar',
|
columnType: 'text',
|
||||||
defaultValue: "'Untitled'",
|
defaultValue: "'Untitled'",
|
||||||
} satisfies WorkspaceMigrationColumnCreate,
|
} satisfies WorkspaceMigrationColumnCreate,
|
||||||
],
|
],
|
||||||
@ -330,6 +330,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
) {
|
) {
|
||||||
return this.objectMetadataRepository.find({
|
return this.objectMetadataRepository.find({
|
||||||
relations: [
|
relations: [
|
||||||
|
'fields.object',
|
||||||
'fields',
|
'fields',
|
||||||
'fields.fromRelationMetadata',
|
'fields.fromRelationMetadata',
|
||||||
'fields.toRelationMetadata',
|
'fields.toRelationMetadata',
|
||||||
|
|||||||
@ -20,14 +20,13 @@ import { WorkspaceMigrationColumnActionType } from 'src/metadata/workspace-migra
|
|||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
||||||
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
import { createRelationForeignKeyColumnName } from 'src/metadata/relation-metadata/utils/create-relation-foreign-key-column-name.util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RelationMetadataEntity,
|
RelationMetadataEntity,
|
||||||
RelationMetadataType,
|
RelationMetadataType,
|
||||||
} from './relation-metadata.entity';
|
} from './relation-metadata.entity';
|
||||||
|
|
||||||
import { createRelationMetadataForeignKey } from './utils/create-relation-metadata-foreign-key.util';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RelationMetadataService extends TypeOrmQueryService<RelationMetadataEntity> {
|
export class RelationMetadataService extends TypeOrmQueryService<RelationMetadataEntity> {
|
||||||
constructor(
|
constructor(
|
||||||
@ -56,7 +55,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
// NOTE: this logic is called to create relation through metadata graphql endpoint (so only for custom field relations)
|
// NOTE: this logic is called to create relation through metadata graphql endpoint (so only for custom field relations)
|
||||||
const isCustom = true;
|
const isCustom = true;
|
||||||
const baseColumnName = `${camelCase(relationMetadataInput.toName)}Id`;
|
const baseColumnName = `${camelCase(relationMetadataInput.toName)}Id`;
|
||||||
const foreignKeyColumnName = createRelationMetadataForeignKey(
|
const foreignKeyColumnName = createRelationForeignKeyColumnName(
|
||||||
relationMetadataInput.toName,
|
relationMetadataInput.toName,
|
||||||
isCustom,
|
isCustom,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
||||||
import { camelCase } from 'src/utils/camel-case';
|
import { camelCase } from 'src/utils/camel-case';
|
||||||
|
|
||||||
export const createRelationMetadataForeignKey = (
|
export const createRelationForeignKeyColumnName = (
|
||||||
name: string,
|
name: string,
|
||||||
isCustom?: boolean,
|
isCustom: boolean,
|
||||||
) => {
|
) => {
|
||||||
const baseColumnName = `${camelCase(name)}Id`;
|
const baseColumnName = `${camelCase(name)}Id`;
|
||||||
|
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { camelCase } from 'src/utils/camel-case';
|
||||||
|
|
||||||
|
export const createRelationForeignKeyFieldMetadataName = (name: string) => {
|
||||||
|
return `${camelCase(name)}Id`;
|
||||||
|
};
|
||||||
@ -63,7 +63,10 @@ export class DatabaseStructureService {
|
|||||||
c.table_schema AS "tableSchema",
|
c.table_schema AS "tableSchema",
|
||||||
c.table_name AS "tableName",
|
c.table_name AS "tableName",
|
||||||
c.column_name AS "columnName",
|
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.is_nullable AS "isNullable",
|
||||||
c.column_default AS "columnDefault",
|
c.column_default AS "columnDefault",
|
||||||
CASE
|
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 typeORMType = fieldMetadataTypeToColumnType(fieldMetadataType);
|
||||||
const mainDataSource = this.typeORMService.getMainDataSource();
|
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({
|
return mainDataSource.driver.normalizeType({
|
||||||
type: typeORMType,
|
type: typeORMType,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -117,9 +117,13 @@ export class FieldMetadataHealthService {
|
|||||||
): WorkspaceHealthIssue[] {
|
): WorkspaceHealthIssue[] {
|
||||||
const issues: WorkspaceHealthIssue[] = [];
|
const issues: WorkspaceHealthIssue[] = [];
|
||||||
const columnName = fieldMetadata.targetColumnMap.value;
|
const columnName = fieldMetadata.targetColumnMap.value;
|
||||||
|
|
||||||
const dataType = this.databaseStructureService.getPostgresDataType(
|
const dataType = this.databaseStructureService.getPostgresDataType(
|
||||||
fieldMetadata.type,
|
fieldMetadata.type,
|
||||||
|
fieldMetadata.name,
|
||||||
|
fieldMetadata.object?.nameSingular,
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultValue = this.databaseStructureService.getPostgresDefault(
|
const defaultValue = this.databaseStructureService.getPostgresDefault(
|
||||||
fieldMetadata.type,
|
fieldMetadata.type,
|
||||||
fieldMetadata.defaultValue,
|
fieldMetadata.defaultValue,
|
||||||
@ -155,7 +159,9 @@ export class FieldMetadataHealthService {
|
|||||||
type: WorkspaceHealthIssueType.COLUMN_NULLABILITY_CONFLICT,
|
type: WorkspaceHealthIssueType.COLUMN_NULLABILITY_CONFLICT,
|
||||||
fieldMetadata,
|
fieldMetadata,
|
||||||
columnStructure,
|
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'}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,12 +18,13 @@ import {
|
|||||||
RelationMetadataEntity,
|
RelationMetadataEntity,
|
||||||
RelationMetadataType,
|
RelationMetadataType,
|
||||||
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
import { createRelationMetadataForeignKey } from 'src/metadata/relation-metadata/utils/create-relation-metadata-foreign-key.util';
|
|
||||||
import {
|
import {
|
||||||
RelationDirection,
|
RelationDirection,
|
||||||
deduceRelationDirection,
|
deduceRelationDirection,
|
||||||
} from 'src/workspace/utils/deduce-relation-direction.util';
|
} from 'src/workspace/utils/deduce-relation-direction.util';
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
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()
|
@Injectable()
|
||||||
export class RelationMetadataHealthService {
|
export class RelationMetadataHealthService {
|
||||||
@ -136,7 +137,7 @@ export class RelationMetadataHealthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isCustom = toFieldMetadata.isCustom ?? false;
|
const isCustom = toFieldMetadata.isCustom ?? false;
|
||||||
const foreignKeyColumnName = createRelationMetadataForeignKey(
|
const foreignKeyColumnName = createRelationForeignKeyColumnName(
|
||||||
toFieldMetadata.name,
|
toFieldMetadata.name,
|
||||||
isCustom,
|
isCustom,
|
||||||
);
|
);
|
||||||
@ -144,16 +145,34 @@ export class RelationMetadataHealthService {
|
|||||||
(column) => column.columnName === foreignKeyColumnName,
|
(column) => column.columnName === foreignKeyColumnName,
|
||||||
);
|
);
|
||||||
const relationFieldMetadata = toObjectMetadataFields.find(
|
const relationFieldMetadata = toObjectMetadataFields.find(
|
||||||
(fieldMetadata) => fieldMetadata.name === foreignKeyColumnName,
|
(fieldMetadata) =>
|
||||||
|
fieldMetadata.name ===
|
||||||
|
createRelationForeignKeyFieldMetadataName(toFieldMetadata.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!relationColumn || !relationFieldMetadata) {
|
if (!relationFieldMetadata) {
|
||||||
issues.push({
|
issues.push({
|
||||||
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID,
|
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID,
|
||||||
fromFieldMetadata,
|
fromFieldMetadata,
|
||||||
toFieldMetadata,
|
toFieldMetadata,
|
||||||
relationMetadata,
|
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;
|
return issues;
|
||||||
|
|||||||
Reference in New Issue
Block a user