feat: sync command missing ability to rename standard object (#4819)

We've introduced in PR #4373 standard ids to be able to rename standard
fields and objects.
Fields part was working properly, but objects part was not yet
implemented.
This PR is adding the missing parts to make it work.
This commit is contained in:
Jérémy M
2024-04-09 10:20:34 +02:00
committed by GitHub
parent b1242bb850
commit 35717fce8b
16 changed files with 322 additions and 148 deletions

View File

@ -18,6 +18,7 @@ import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnDrop,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
@ -152,7 +153,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
[
{
name: computeObjectTargetTable(objectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
createdFieldMetadata,
@ -328,7 +329,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
[
{
name: computeObjectTargetTable(objectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.ALTER,
existingFieldMetadata,
@ -399,7 +400,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
[
{
name: computeObjectTargetTable(objectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP,

View File

@ -16,6 +16,7 @@ import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/wo
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnDrop,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import {
FieldMetadataEntity,
@ -177,7 +178,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
relationToDelete.toObjectName,
relationToDelete.toObjectMetadataIsCustom,
),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP,
@ -203,7 +204,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
[
{
name: computeObjectTargetTable(objectMetadata),
action: 'drop',
action: WorkspaceMigrationTableActionType.DROP,
},
],
);

View File

@ -5,6 +5,7 @@ import {
WorkspaceMigrationTableAction,
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnCreate,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
@ -17,12 +18,12 @@ export const buildWorkspaceMigrationsForCustomObject = (
): WorkspaceMigrationTableAction[] => [
{
name: computeObjectTargetTable(createdObjectMetadata),
action: 'create',
action: WorkspaceMigrationTableActionType.CREATE,
} satisfies WorkspaceMigrationTableAction,
// Add activity target relation
{
name: computeObjectTargetTable(activityTargetObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -36,7 +37,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
},
{
name: computeObjectTargetTable(activityTargetObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
@ -52,7 +53,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
// Add attachment relation
{
name: computeObjectTargetTable(attachmentObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -66,7 +67,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
},
{
name: computeObjectTargetTable(attachmentObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
@ -82,7 +83,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
// Add event relation
{
name: computeObjectTargetTable(eventObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -96,7 +97,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
},
{
name: computeObjectTargetTable(eventObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
@ -112,7 +113,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
// Add favorite relation
{
name: computeObjectTargetTable(favoriteObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -126,7 +127,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
},
{
name: computeObjectTargetTable(favoriteObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
@ -141,7 +142,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
},
{
name: computeObjectTargetTable(createdObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -154,7 +155,7 @@ export const buildWorkspaceMigrationsForCustomObject = (
// This is temporary until we implement mainIdentifier
{
name: computeObjectTargetTable(createdObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,

View File

@ -6,6 +6,7 @@ import {
WorkspaceMigrationTableAction,
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnCreate,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
@ -61,7 +62,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
return [
{
name: computeObjectTargetTable(activityTargetObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -75,7 +76,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(activityTargetObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -88,7 +89,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(activityTargetObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
@ -104,7 +105,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
// Add attachment relation
{
name: computeObjectTargetTable(attachmentObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -118,7 +119,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(attachmentObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -131,7 +132,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(attachmentObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
@ -147,7 +148,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
// Add event relation
{
name: computeObjectTargetTable(eventObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -161,7 +162,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(eventObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -174,7 +175,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(eventObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
@ -190,7 +191,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
// Add favorite relation
{
name: computeObjectTargetTable(favoriteObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -204,7 +205,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(favoriteObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -217,7 +218,7 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
},
{
name: computeObjectTargetTable(favoriteObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,

View File

@ -17,7 +17,10 @@ import { CreateRelationInput } from 'src/engine/metadata-modules/relation-metada
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { WorkspaceMigrationColumnActionType } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
@ -171,7 +174,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
name: computeObjectTargetTable(
objectMetadataMap[relationMetadataInput.toObjectMetadataId],
),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
@ -186,7 +189,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
name: computeObjectTargetTable(
objectMetadataMap[relationMetadataInput.toObjectMetadataId],
),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,

View File

@ -30,6 +30,7 @@ import { generateMigrationName } from 'src/engine/metadata-modules/workspace-mig
import {
WorkspaceMigrationColumnDefinition,
WorkspaceMigrationForeignTable,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { RemoteTableColumn } from 'src/engine/metadata-modules/remote-server/remote-table/types/remote-table-column';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@ -165,7 +166,7 @@ export class RemoteTableService {
[
{
name: remoteTableName,
action: 'create_foreign_table',
action: WorkspaceMigrationTableActionType.CREATE_FOREIGN_TABLE,
foreignTable: {
columns: remoteTableColumns.map(
(column) =>
@ -252,7 +253,7 @@ export class RemoteTableService {
[
{
name: remoteTableName,
action: 'drop_foreign_table',
action: WorkspaceMigrationTableActionType.DROP_FOREIGN_TABLE,
},
],
);

View File

@ -80,14 +80,21 @@ export type WorkspaceMigrationColumnAction = {
| WorkspaceMigrationCreateComment
);
/**
* Enum values are lowercase to avoid issues with already existing enum values
*/
export enum WorkspaceMigrationTableActionType {
CREATE = 'create',
ALTER = 'alter',
DROP = 'drop',
CREATE_FOREIGN_TABLE = 'create_foreign_table',
DROP_FOREIGN_TABLE = 'drop_foreign_table'
}
export type WorkspaceMigrationTableAction = {
name: string;
action:
| 'create'
| 'alter'
| 'drop'
| 'create_foreign_table'
| 'drop_foreign_table';
newName?: string;
action: WorkspaceMigrationTableActionType;
columns?: WorkspaceMigrationColumnAction[];
foreignTable?: WorkspaceMigrationForeignTable;
};

View File

@ -11,6 +11,7 @@ import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
@ -94,7 +95,7 @@ export class WorkspaceMigrationFieldFactory {
name: computeObjectTargetTable(
originalObjectMetadataMap[fieldMetadata.objectMetadataId],
),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
fieldMetadata,
@ -132,7 +133,7 @@ export class WorkspaceMigrationFieldFactory {
fieldMetadataUpdate.current.objectMetadataId
],
),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.ALTER,
fieldMetadataUpdate.current,
@ -171,7 +172,7 @@ export class WorkspaceMigrationFieldFactory {
name: computeObjectTargetTable(
originalObjectMetadataMap[fieldMetadata.objectMetadataId],
),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP,

View File

@ -8,11 +8,17 @@ import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
export interface ObjectMetadataUpdate {
current: ObjectMetadataEntity;
altered: ObjectMetadataEntity;
}
@Injectable()
export class WorkspaceMigrationObjectFactory {
constructor(
@ -21,13 +27,35 @@ export class WorkspaceMigrationObjectFactory {
async create(
objectMetadataCollection: ObjectMetadataEntity[],
action:
| WorkspaceMigrationBuilderAction.CREATE
| WorkspaceMigrationBuilderAction.DELETE,
): Promise<Partial<WorkspaceMigrationEntity>[]>;
async create(
objectMetadataUpdateCollection: ObjectMetadataUpdate[],
action: WorkspaceMigrationBuilderAction.UPDATE,
): Promise<Partial<WorkspaceMigrationEntity>[]>;
async create(
objectMetadataCollectionOrObjectMetadataUpdateCollection:
| ObjectMetadataEntity[]
| ObjectMetadataUpdate[],
action: WorkspaceMigrationBuilderAction,
): Promise<Partial<WorkspaceMigrationEntity>[]> {
switch (action) {
case WorkspaceMigrationBuilderAction.CREATE:
return this.createObjectMigration(objectMetadataCollection);
return this.createObjectMigration(
objectMetadataCollectionOrObjectMetadataUpdateCollection as ObjectMetadataEntity[],
);
case WorkspaceMigrationBuilderAction.UPDATE:
return this.updateObjectMigration(
objectMetadataCollectionOrObjectMetadataUpdateCollection as ObjectMetadataUpdate[],
);
case WorkspaceMigrationBuilderAction.DELETE:
return this.deleteObjectMigration(objectMetadataCollection);
return this.deleteObjectMigration(
objectMetadataCollectionOrObjectMetadataUpdateCollection as ObjectMetadataEntity[],
);
default:
return [];
}
@ -42,7 +70,7 @@ export class WorkspaceMigrationObjectFactory {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(objectMetadata),
action: 'create',
action: WorkspaceMigrationTableActionType.CREATE,
},
];
@ -53,7 +81,7 @@ export class WorkspaceMigrationObjectFactory {
migrations.push({
name: computeObjectTargetTable(objectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
field,
@ -72,6 +100,40 @@ export class WorkspaceMigrationObjectFactory {
return workspaceMigrations;
}
private async updateObjectMigration(
objectMetadataUpdateCollection: ObjectMetadataUpdate[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
for (const objectMetadataUpdate of objectMetadataUpdateCollection) {
const oldTableName = computeObjectTargetTable(
objectMetadataUpdate.current,
);
const newTableName = computeObjectTargetTable(
objectMetadataUpdate.altered,
);
if (oldTableName !== newTableName) {
workspaceMigrations.push({
workspaceId: objectMetadataUpdate.current.workspaceId,
name: generateMigrationName(
`rename-${objectMetadataUpdate.current.nameSingular}`,
),
isCustom: false,
migrations: [
{
name: oldTableName,
newName: newTableName,
action: WorkspaceMigrationTableActionType.ALTER,
},
],
});
}
}
return workspaceMigrations;
}
private async deleteObjectMigration(
objectMetadataCollection: ObjectMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
@ -81,8 +143,7 @@ export class WorkspaceMigrationObjectFactory {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(objectMetadata),
action: 'drop',
columns: [],
action: WorkspaceMigrationTableActionType.DROP,
},
];

View File

@ -7,6 +7,7 @@ import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationEntity,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import {
@ -90,7 +91,7 @@ export class WorkspaceMigrationRelationFactory {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(toObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP_FOREIGN_KEY,
@ -100,7 +101,7 @@ export class WorkspaceMigrationRelationFactory {
},
{
name: computeObjectTargetTable(toObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
@ -166,7 +167,7 @@ export class WorkspaceMigrationRelationFactory {
const migrations: WorkspaceMigrationTableAction[] = [
{
name: computeObjectTargetTable(toObjectMetadata),
action: 'alter',
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,

View File

@ -18,6 +18,7 @@ import {
WorkspaceMigrationColumnCreateRelation,
WorkspaceMigrationColumnAlter,
WorkspaceMigrationColumnDropRelation,
WorkspaceMigrationTableActionType,
WorkspaceMigrationForeignTable,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
@ -118,18 +119,30 @@ export class WorkspaceMigrationRunnerService {
tableMigration: WorkspaceMigrationTableAction,
) {
switch (tableMigration.action) {
case 'create':
case WorkspaceMigrationTableActionType.CREATE:
await this.createTable(queryRunner, schemaName, tableMigration.name);
break;
case 'alter':
await this.handleColumnChanges(
queryRunner,
schemaName,
tableMigration.name,
tableMigration?.columns,
);
case WorkspaceMigrationTableActionType.ALTER: {
if (tableMigration.newName) {
await this.renameTable(
queryRunner,
schemaName,
tableMigration.name,
tableMigration.newName,
);
}
if (tableMigration.columns && tableMigration.columns.length > 0) {
await this.handleColumnChanges(
queryRunner,
schemaName,
tableMigration.newName ?? tableMigration.name,
tableMigration.columns,
);
}
break;
case 'drop':
}
case WorkspaceMigrationTableActionType.DROP:
await queryRunner.dropTable(`${schemaName}.${tableMigration.name}`);
break;
case 'create_foreign_table':
@ -179,6 +192,25 @@ export class WorkspaceMigrationRunnerService {
`);
}
/**
* Rename a table
* @param queryRunner QueryRunner
* @param schemaName string
* @param oldTableName string
* @param newTableName string
*/
private async renameTable(
queryRunner: QueryRunner,
schemaName: string,
oldTableName: string,
newTableName: string,
) {
await queryRunner.renameTable(
`${schemaName}.${oldTableName}`,
newTableName,
);
}
/**
* Handles column changes for a given migration
*

View File

@ -42,27 +42,39 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner {
: await this.workspaceService.getWorkspaceIds();
for (const workspaceId of workspaceIds) {
const issues = await this.workspaceHealthService.healthCheck(workspaceId);
try {
const issues =
await this.workspaceHealthService.healthCheck(workspaceId);
// Security: abort if there are issues.
if (issues.length > 0) {
// Security: abort if there are issues.
if (issues.length > 0) {
if (!options.force) {
this.logger.error(
`Workspace contains ${issues.length} issues, aborting.`,
);
this.logger.log(
'If you want to force the migration, use --force flag',
);
this.logger.log(
'Please use `workspace:health` command to check issues and fix them before running this command.',
);
return;
}
this.logger.warn(
`Workspace contains ${issues.length} issues, sync has been forced.`,
);
}
} catch (error) {
if (!options.force) {
this.logger.error(
`Workspace contains ${issues.length} issues, aborting.`,
);
this.logger.log(
'If you want to force the migration, use --force flag',
);
this.logger.log(
'Please use `workspace:health` command to check issues and fix them before running this command.',
);
return;
throw error;
}
this.logger.warn(
`Workspace contains ${issues.length} issues, sync has been forced.`,
`Workspace health check failed with error, but sync has been forced.`,
error,
);
}

View File

@ -33,7 +33,9 @@ export interface ComparatorDeleteResult<T> {
export type ObjectComparatorResult =
| ComparatorSkipResult
| ComparatorCreateResult<ComputedPartialObjectMetadata>
| ComparatorUpdateResult<Partial<ComputedPartialObjectMetadata>>;
| ComparatorUpdateResult<
Partial<ComputedPartialObjectMetadata> & { id: string }
>;
export type FieldComparatorResult =
| ComparatorSkipResult

View File

@ -1,8 +1,13 @@
import { Injectable, Logger } from '@nestjs/common';
import { EntityManager, In } from 'typeorm';
import {
EntityManager,
EntityTarget,
FindOptionsWhere,
In,
ObjectLiteral,
} from 'typeorm';
import { v4 as uuidV4 } from 'uuid';
import omit from 'lodash.omit';
import { PartialFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
@ -14,6 +19,8 @@ import {
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
import { FieldMetadataUpdate } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field.factory';
import { ObjectMetadataUpdate } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-object.factory';
@Injectable()
export class WorkspaceMetadataUpdaterService {
@ -24,7 +31,7 @@ export class WorkspaceMetadataUpdaterService {
storage: WorkspaceSyncStorage,
): Promise<{
createdObjectMetadataCollection: ObjectMetadataEntity[];
updatedObjectMetadataCollection: ObjectMetadataEntity[];
updatedObjectMetadataCollection: ObjectMetadataUpdate[];
}> {
const objectMetadataRepository =
manager.getRepository(ObjectMetadataEntity);
@ -56,10 +63,17 @@ export class WorkspaceMetadataUpdaterService {
/**
* Update object metadata
*/
const updatedObjectMetadataCollection = await objectMetadataRepository.save(
storage.objectMetadataUpdateCollection.map((objectMetadata) =>
omit(objectMetadata, ['fields']),
),
const updatedObjectMetadataCollection = await this.updateEntities(
manager,
ObjectMetadataEntity,
storage.objectMetadataUpdateCollection,
[
'fields',
'dataSourceId',
'workspaceId',
'labelIdentifierFieldMetadataId',
'imageIdentifierFieldMetadataId',
],
);
/**
@ -108,10 +122,7 @@ export class WorkspaceMetadataUpdaterService {
storage: WorkspaceSyncStorage,
): Promise<{
createdFieldMetadataCollection: FieldMetadataEntity[];
updatedFieldMetadataCollection: {
current: FieldMetadataEntity;
altered: FieldMetadataEntity;
}[];
updatedFieldMetadataCollection: FieldMetadataUpdate[];
}> {
const fieldMetadataRepository = manager.getRepository(FieldMetadataEntity);
@ -127,41 +138,12 @@ export class WorkspaceMetadataUpdaterService {
/**
* Update field metadata
*/
const oldFieldMetadataCollection = await fieldMetadataRepository.findBy({
id: In(storage.fieldMetadataUpdateCollection.map((field) => field.id)),
});
// Pre-process old collection into a mapping for quick access
const oldFieldMetadataMap = new Map(
oldFieldMetadataCollection.map((field) => [field.id, field]),
);
// Combine old and new field metadata to get whole updated entities
const fieldMetadataUpdateCollection =
storage.fieldMetadataUpdateCollection.map((updateFieldMetadata) => {
const oldFieldMetadata = oldFieldMetadataMap.get(
updateFieldMetadata.id,
);
if (!oldFieldMetadata) {
throw new Error(`
Field ${updateFieldMetadata.id} not found in oldFieldMetadataCollection`);
}
// TypeORM 😢
// If we didn't provide the old value, it will be set to null fields that are not in the updateFieldMetadata
// and override the old value with null in the DB.
// Also save method doesn't return the whole entity if you give a partial one.
// https://github.com/typeorm/typeorm/issues/3490
// To avoid calling update in a for loop, we did this hack.
return {
...omit(oldFieldMetadata, ['objectMetadataId', 'workspaceId']),
...omit(updateFieldMetadata, ['objectMetadataId', 'workspaceId']),
options: updateFieldMetadata.options ?? oldFieldMetadata.options,
};
});
const updatedFieldMetadataCollection = await fieldMetadataRepository.save(
fieldMetadataUpdateCollection,
);
const updatedFieldMetadataCollection = await this.updateEntities<
FieldMetadataEntity<'default'>
>(manager, FieldMetadataEntity, storage.objectMetadataUpdateCollection, [
'objectMetadataId',
'workspaceId',
]);
/**
* Delete field metadata
@ -183,28 +165,7 @@ export class WorkspaceMetadataUpdaterService {
return {
createdFieldMetadataCollection:
createdFieldMetadataCollection as FieldMetadataEntity[],
updatedFieldMetadataCollection: updatedFieldMetadataCollection.map(
(alteredFieldMetadata) => {
const oldFieldMetadata = oldFieldMetadataMap.get(
alteredFieldMetadata.id,
);
if (!oldFieldMetadata) {
throw new Error(`
Field ${alteredFieldMetadata.id} not found in oldFieldMetadataCollection
`);
}
return {
current: oldFieldMetadata as FieldMetadataEntity,
altered: {
...alteredFieldMetadata,
objectMetadataId: oldFieldMetadata.objectMetadataId,
workspaceId: oldFieldMetadata.workspaceId,
} as FieldMetadataEntity,
};
},
),
updatedFieldMetadataCollection,
};
}
@ -267,4 +228,83 @@ export class WorkspaceMetadataUpdaterService {
updatedRelationMetadataCollection,
};
}
/**
* Update entities in the database
* @param manager EntityManager
* @param entityClass Entity class
* @param updateCollection Update collection
* @param keysToOmit keys to omit in the merge process
* @returns Promise<{ current: Entity; altered: Entity }[]>
*/
private async updateEntities<Entity extends ObjectLiteral & { id: string }>(
manager: EntityManager,
entityClass: EntityTarget<Entity>,
updateCollection: Array<
DeepPartial<Omit<Entity, 'fields'>> & { id: string }
>,
keysToOmit: (keyof Entity)[] = [],
): Promise<{ current: Entity; altered: Entity }[]> {
const repository = manager.getRepository(entityClass);
const oldEntities = await repository.findBy({
id: In(updateCollection.map((updateItem) => updateItem.id)),
} as FindOptionsWhere<Entity>);
// Pre-process old collection into a mapping for quick access
const oldEntitiesMap = new Map(
oldEntities.map((oldEntity) => [oldEntity.id, oldEntity]),
);
// Combine old and new field metadata to get whole updated entities
const entityUpdateCollection = updateCollection.map((updateItem) => {
const oldEntity = oldEntitiesMap.get(updateItem.id);
if (!oldEntity) {
throw new Error(`
Entity ${updateItem.id} not found in oldEntities`);
}
// TypeORM 😢
// If we didn't provide the old value, it will be set to null objects that are not in the updateObjectMetadata
// and override the old value with null in the DB.
// Also save method doesn't return the whole entity if you give a partial one.
// https://github.com/typeorm/typeorm/issues/3490
// To avoid calling update in a for loop, we did this hack.
const mergedUpdate = {
...oldEntity,
...updateItem,
};
// Omit keys that we don't want to override
keysToOmit.forEach((key) => {
delete mergedUpdate[key];
});
return mergedUpdate;
});
const updatedEntities = await repository.save(entityUpdateCollection);
return updatedEntities.map((updatedEntity) => {
const oldEntity = oldEntitiesMap.get(updatedEntity.id);
if (!oldEntity) {
throw new Error(`
Entity ${updatedEntity.id} not found in oldEntitiesMap
`);
}
return {
current: oldEntity,
altered: {
...updatedEntity,
...keysToOmit.reduce(
(acc, key) => ({ ...acc, [key]: oldEntity[key] }),
{},
),
},
};
});
}
}

View File

@ -154,6 +154,12 @@ export class WorkspaceSyncObjectMetadataService {
WorkspaceMigrationBuilderAction.CREATE,
);
const updateObjectWorkspaceMigrations =
await this.workspaceMigrationObjectFactory.create(
metadataObjectUpdaterResult.updatedObjectMetadataCollection,
WorkspaceMigrationBuilderAction.UPDATE,
);
const deleteObjectWorkspaceMigrations =
await this.workspaceMigrationObjectFactory.create(
storage.objectMetadataDeleteCollection,
@ -164,6 +170,7 @@ export class WorkspaceSyncObjectMetadataService {
return [
...createObjectWorkspaceMigrations,
...updateObjectWorkspaceMigrations,
...deleteObjectWorkspaceMigrations,
];
}

View File

@ -10,8 +10,9 @@ export class WorkspaceSyncStorage {
// Object metadata
private readonly _objectMetadataCreateCollection: ComputedPartialObjectMetadata[] =
[];
private readonly _objectMetadataUpdateCollection: Partial<ComputedPartialObjectMetadata>[] =
[];
private readonly _objectMetadataUpdateCollection: (Partial<ComputedPartialObjectMetadata> & {
id: string;
})[] = [];
private readonly _objectMetadataDeleteCollection: ObjectMetadataEntity[] = [];
// Field metadata
@ -72,7 +73,9 @@ export class WorkspaceSyncStorage {
this._objectMetadataCreateCollection.push(object);
}
addUpdateObjectMetadata(object: Partial<ComputedPartialObjectMetadata>) {
addUpdateObjectMetadata(
object: Partial<ComputedPartialObjectMetadata> & { id: string },
) {
this._objectMetadataUpdateCollection.push(object);
}