Feat/workspace health core fix (#3863)

* feat: add deletion support on sync metadata command

* fix: remove debug

* feat: wip workspace health command add --fix option

fix: remove test

* feat: core of --fix option for workspace-health
This commit is contained in:
Jérémy M
2024-02-07 18:27:35 +01:00
committed by GitHub
parent 850eab8f8f
commit 6e3a8e3461
20 changed files with 380 additions and 103 deletions

View File

@ -1,133 +0,0 @@
import { Injectable } from '@nestjs/common';
import {
FieldMetadataEntity,
FieldMetadataType,
} from 'src/metadata/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
import { generateMigrationName } from 'src/metadata/workspace-migration/utils/generate-migration-name.util';
@Injectable()
export class FieldWorkspaceMigrationFactory {
constructor(
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
) {}
async create(
originalObjectMetadataCollection: ObjectMetadataEntity[],
createFieldMetadataCollection: FieldMetadataEntity[],
deleteFieldMetadataCollection: FieldMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
const originalObjectMetadataMap = originalObjectMetadataCollection.reduce(
(result, currentObject) => {
result[currentObject.id] = currentObject;
return result;
},
{} as Record<string, ObjectMetadataEntity>,
);
/**
* Create field migrations
*/
if (createFieldMetadataCollection.length > 0) {
const createFieldWorkspaceMigrations = await this.createFieldMigration(
originalObjectMetadataMap,
createFieldMetadataCollection,
);
workspaceMigrations.push(...createFieldWorkspaceMigrations);
}
/**
* Delete field migrations
*/
if (deleteFieldMetadataCollection.length > 0) {
const deleteFieldWorkspaceMigrations = await this.deleteFieldMigration(
originalObjectMetadataMap,
deleteFieldMetadataCollection,
);
workspaceMigrations.push(...deleteFieldWorkspaceMigrations);
}
return workspaceMigrations;
}
private async createFieldMigration(
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
fieldMetadataCollection: FieldMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const fieldMetadata of fieldMetadataCollection) {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(
originalObjectMetadataMap[fieldMetadata.objectMetadataId],
),
action: 'alter',
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
fieldMetadata,
),
},
];
workspaceMigrations.push({
workspaceId: fieldMetadata.workspaceId,
name: generateMigrationName(`create-${fieldMetadata.name}`),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
private async deleteFieldMigration(
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
fieldMetadataCollection: FieldMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const fieldMetadata of fieldMetadataCollection) {
// We're skipping relation fields, because they're just representation and not real columns
if (fieldMetadata.type === FieldMetadataType.RELATION) {
continue;
}
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(
originalObjectMetadataMap[fieldMetadata.objectMetadataId],
),
action: 'alter',
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP,
columnName: fieldMetadata.name,
},
],
},
];
workspaceMigrations.push({
workspaceId: fieldMetadata.workspaceId,
name: generateMigrationName(`delete-${fieldMetadata.name}`),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
}

View File

@ -1,15 +1,9 @@
import { FeatureFlagFactory } from './feature-flags.factory';
import { StandardObjectFactory } from './standard-object.factory';
import { StandardRelationFactory } from './standard-relation.factory';
import { ObjectWorkspaceMigrationFactory } from './object-workspace-migration.factory';
import { FieldWorkspaceMigrationFactory } from './field-workspace-migration.factory';
import { RelationWorkspaceMigrationFactory } from './relation-workspace-migration.factory';
export const workspaceSyncMetadataFactories = [
FeatureFlagFactory,
StandardObjectFactory,
StandardRelationFactory,
ObjectWorkspaceMigrationFactory,
FieldWorkspaceMigrationFactory,
RelationWorkspaceMigrationFactory,
];

View File

@ -1,114 +0,0 @@
import { Injectable } from '@nestjs/common';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
import { generateMigrationName } from 'src/metadata/workspace-migration/utils/generate-migration-name.util';
@Injectable()
export class ObjectWorkspaceMigrationFactory {
constructor(
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
) {}
async create(
createObjectMetadataCollection: ObjectMetadataEntity[],
deleteObjectMetadataCollection: ObjectMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
/**
* Create object migrations
*/
if (createObjectMetadataCollection.length > 0) {
const createObjectWorkspaceMigrations = await this.createObjectMigration(
createObjectMetadataCollection,
);
workspaceMigrations.push(...createObjectWorkspaceMigrations);
}
/**
* Delete object migrations
*/
if (deleteObjectMetadataCollection.length > 0) {
const deleteObjectWorkspaceMigrations = await this.deleteObjectMigration(
deleteObjectMetadataCollection,
);
workspaceMigrations.push(...deleteObjectWorkspaceMigrations);
}
return workspaceMigrations;
}
private async createObjectMigration(
objectMetadataCollection: ObjectMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const objectMetadata of objectMetadataCollection) {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(objectMetadata),
action: 'create',
},
];
for (const field of objectMetadata.fields) {
if (field.type === FieldMetadataType.RELATION) {
continue;
}
migrations.push({
name: computeObjectTargetTable(objectMetadata),
action: 'alter',
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
field,
),
});
}
workspaceMigrations.push({
workspaceId: objectMetadata.workspaceId,
name: generateMigrationName(`create-${objectMetadata.nameSingular}`),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
private async deleteObjectMigration(
objectMetadataCollection: ObjectMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const objectMetadata of objectMetadataCollection) {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(objectMetadata),
action: 'drop',
columns: [],
},
];
workspaceMigrations.push({
workspaceId: objectMetadata.workspaceId,
name: generateMigrationName(`delete-${objectMetadata.nameSingular}`),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
}

View File

@ -1,115 +0,0 @@
import { Injectable } from '@nestjs/common';
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
} from 'src/metadata/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
import {
RelationMetadataEntity,
RelationMetadataType,
} from 'src/metadata/relation-metadata/relation-metadata.entity';
import { camelCase } from 'src/utils/camel-case';
import { generateMigrationName } from 'src/metadata/workspace-migration/utils/generate-migration-name.util';
@Injectable()
export class RelationWorkspaceMigrationFactory {
constructor() {}
/**
* Deletion of the relation is handled by field deletion
*/
async create(
originalObjectMetadataCollection: ObjectMetadataEntity[],
createRelationMetadataCollection: RelationMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
const originalObjectMetadataMap = originalObjectMetadataCollection.reduce(
(result, currentObject) => {
result[currentObject.id] = currentObject;
return result;
},
{} as Record<string, ObjectMetadataEntity>,
);
if (createRelationMetadataCollection.length > 0) {
const createRelationWorkspaceMigrations =
await this.createRelationMigration(
originalObjectMetadataMap,
createRelationMetadataCollection,
);
workspaceMigrations.push(...createRelationWorkspaceMigrations);
}
return workspaceMigrations;
}
private async createRelationMigration(
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
relationMetadataCollection: RelationMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const relationMetadata of relationMetadataCollection) {
const toObjectMetadata =
originalObjectMetadataMap[relationMetadata.toObjectMetadataId];
const fromObjectMetadata =
originalObjectMetadataMap[relationMetadata.fromObjectMetadataId];
if (!toObjectMetadata) {
throw new Error(
`ObjectMetadata with id ${relationMetadata.toObjectMetadataId} not found`,
);
}
if (!fromObjectMetadata) {
throw new Error(
`ObjectMetadata with id ${relationMetadata.fromObjectMetadataId} not found`,
);
}
const toFieldMetadata = toObjectMetadata.fields.find(
(field) => field.id === relationMetadata.toFieldMetadataId,
);
if (!toFieldMetadata) {
throw new Error(
`FieldMetadata with id ${relationMetadata.toFieldMetadataId} not found`,
);
}
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(toObjectMetadata),
action: 'alter',
columns: [
{
action: WorkspaceMigrationColumnActionType.RELATION,
columnName: `${camelCase(toFieldMetadata.name)}Id`,
referencedTableName: computeObjectTargetTable(fromObjectMetadata),
referencedTableColumnName: 'id',
isUnique:
relationMetadata.relationType ===
RelationMetadataType.ONE_TO_ONE,
},
],
},
];
workspaceMigrations.push({
workspaceId: relationMetadata.workspaceId,
name: generateMigrationName(
`create-relation-from-${fromObjectMetadata.nameSingular}-to-${toObjectMetadata.nameSingular}`,
),
isCustom: false,
migrations,
});
}
return workspaceMigrations;
}
}