Fix/workspace health type (#4053)

* fix: memory issue with truncate command

* fix: LINK doesn't have any default value

* fix: Cannot convert LINK to column type.

* fix: handle old column type and add a warn to fix them manually
This commit is contained in:
Jérémy M
2024-02-19 17:28:40 +01:00
committed by GitHub
parent 4a95798411
commit e293abe332
5 changed files with 77 additions and 36 deletions

View File

@ -2,31 +2,35 @@ import console from 'console';
import { connectionSource, performQuery } from './utils'; import { connectionSource, performQuery } from './utils';
connectionSource async function dropSchemasSequentially() {
.initialize() try {
.then(async () => { await connectionSource.initialize();
await performQuery(
// Fetch all schemas
const schemas = await performQuery(
` `
CREATE OR REPLACE FUNCTION drop_all() RETURNS VOID AS $$ SELECT n.nspname AS "schema_name"
DECLARE schema_item RECORD; FROM pg_catalog.pg_namespace n
BEGIN WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
FOR schema_item IN `,
SELECT subrequest."name" as schema_name 'Fetching schemas...',
FROM (SELECT n.nspname AS "name"
FROM pg_catalog.pg_namespace n
WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema') as subrequest
LOOP
EXECUTE 'DROP SCHEMA ' || schema_item.schema_name || ' CASCADE';
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
SELECT drop_all ();
`,
'Dropping all schemas...',
); );
})
.catch((err) => { // Iterate over each schema and drop it
console.error('Error during Data Source initialization:', err); // This is to avoid dropping all schemas at once, which would cause an out of shared memory error
}); for (const schema of schemas) {
await performQuery(
`
DROP SCHEMA IF EXISTS "${schema.schema_name}" CASCADE;
`,
`Dropping schema ${schema.schema_name}...`,
);
}
console.log('All schemas dropped successfully.');
} catch (err) {
console.error('Error during schema dropping:', err);
}
}
dropSchemasSequentially();

View File

@ -17,6 +17,11 @@ export function generateDefaultValue(
firstName: '', firstName: '',
lastName: '', lastName: '',
}; };
case FieldMetadataType.LINK:
return {
url: '',
label: '',
};
default: default:
return null; return null;
} }

View File

@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { EntityManager } from 'typeorm'; import { EntityManager } from 'typeorm';
@ -10,13 +10,20 @@ import { WorkspaceMigrationBuilderAction } from 'src/workspace/workspace-migrati
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
import { WorkspaceMigrationEntity } from 'src/metadata/workspace-migration/workspace-migration.entity'; import { WorkspaceMigrationEntity } from 'src/metadata/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationFieldFactory } from 'src/workspace/workspace-migration-builder/factories/workspace-migration-field.factory'; import {
FieldMetadataUpdate,
WorkspaceMigrationFieldFactory,
} from 'src/workspace/workspace-migration-builder/factories/workspace-migration-field.factory';
import { DatabaseStructureService } from 'src/workspace/workspace-health/services/database-structure.service'; import { DatabaseStructureService } from 'src/workspace/workspace-health/services/database-structure.service';
import { AbstractWorkspaceFixer } from './abstract-workspace.fixer'; import { AbstractWorkspaceFixer } from './abstract-workspace.fixer';
const oldDataTypes = ['integer'];
@Injectable() @Injectable()
export class WorkspaceTypeFixer extends AbstractWorkspaceFixer<WorkspaceHealthIssueType.COLUMN_DATA_TYPE_CONFLICT> { export class WorkspaceTypeFixer extends AbstractWorkspaceFixer<WorkspaceHealthIssueType.COLUMN_DATA_TYPE_CONFLICT> {
private readonly logger = new Logger(WorkspaceTypeFixer.name);
constructor( constructor(
private readonly workspaceMigrationFieldFactory: WorkspaceMigrationFieldFactory, private readonly workspaceMigrationFieldFactory: WorkspaceMigrationFieldFactory,
private readonly databaseStructureService: DatabaseStructureService, private readonly databaseStructureService: DatabaseStructureService,
@ -40,28 +47,39 @@ export class WorkspaceTypeFixer extends AbstractWorkspaceFixer<WorkspaceHealthIs
objectMetadataCollection: ObjectMetadataEntity[], objectMetadataCollection: ObjectMetadataEntity[],
issues: WorkspaceHealthColumnIssue<WorkspaceHealthIssueType.COLUMN_DATA_TYPE_CONFLICT>[], issues: WorkspaceHealthColumnIssue<WorkspaceHealthIssueType.COLUMN_DATA_TYPE_CONFLICT>[],
): Promise<Partial<WorkspaceMigrationEntity>[]> { ): Promise<Partial<WorkspaceMigrationEntity>[]> {
const fieldMetadataUpdateCollection = issues.map((issue) => { const fieldMetadataUpdateCollection: FieldMetadataUpdate[] = [];
if (!issue.columnStructure?.dataType) {
for (const issue of issues) {
const dataType = issue.columnStructure?.dataType;
if (!dataType) {
throw new Error('Column structure data type is missing'); throw new Error('Column structure data type is missing');
} }
const type = const type =
this.databaseStructureService.getFieldMetadataTypeFromPostgresDataType( this.databaseStructureService.getFieldMetadataTypeFromPostgresDataType(
issue.columnStructure?.dataType, dataType,
); );
if (oldDataTypes.includes(dataType)) {
this.logger.warn(
`Old data type detected for column ${issue.columnStructure?.columnName} with data type ${dataType}. Please update the column data type manually.`,
);
continue;
}
if (!type) { if (!type) {
throw new Error("Can't find field metadata type from column structure"); throw new Error("Can't find field metadata type from column structure");
} }
return { fieldMetadataUpdateCollection.push({
current: { current: {
...issue.fieldMetadata, ...issue.fieldMetadata,
type, type,
}, },
altered: issue.fieldMetadata, altered: issue.fieldMetadata,
}; });
}); }
return this.workspaceMigrationFieldFactory.create( return this.workspaceMigrationFieldFactory.create(
objectMetadataCollection, objectMetadataCollection,

View File

@ -16,6 +16,8 @@ import {
} from 'src/metadata/field-metadata/field-metadata.entity'; } from 'src/metadata/field-metadata/field-metadata.entity';
import { fieldMetadataTypeToColumnType } from 'src/metadata/workspace-migration/utils/field-metadata-type-to-column-type.util'; 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'; import { serializeTypeDefaultValue } from 'src/metadata/field-metadata/utils/serialize-type-default-value.util';
import { isCompositeFieldMetadataType } from 'src/metadata/field-metadata/utils/is-composite-field-metadata-type.util';
import { isRelationFieldMetadataType } from 'src/workspace/utils/is-relation-field-metadata-type.util';
@Injectable() @Injectable()
export class DatabaseStructureService { export class DatabaseStructureService {
@ -165,8 +167,20 @@ export class DatabaseStructureService {
postgresDataType: string, postgresDataType: string,
): FieldMetadataType | null { ): FieldMetadataType | null {
const mainDataSource = this.typeORMService.getMainDataSource(); const mainDataSource = this.typeORMService.getMainDataSource();
const types = Object.values(FieldMetadataType).filter((type) => {
// We're skipping composite and relation types, as they're not directly mapped to a column type
if (isCompositeFieldMetadataType(type)) {
return false;
}
for (const type in FieldMetadataType) { if (isRelationFieldMetadataType(type)) {
return false;
}
return true;
});
for (const type of types) {
const typeORMType = fieldMetadataTypeToColumnType( const typeORMType = fieldMetadataTypeToColumnType(
FieldMetadataType[type], FieldMetadataType[type],
) as ColumnType; ) as ColumnType;

View File

@ -16,7 +16,7 @@ import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-tar
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory'; import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
import { generateMigrationName } from 'src/metadata/workspace-migration/utils/generate-migration-name.util'; import { generateMigrationName } from 'src/metadata/workspace-migration/utils/generate-migration-name.util';
interface FieldMetadataUpdate { export interface FieldMetadataUpdate {
current: FieldMetadataEntity; current: FieldMetadataEntity;
altered: FieldMetadataEntity; altered: FieldMetadataEntity;
} }