feat: add deletion support on sync metadata command (#3826)

* feat: add deletion support on sync metadata command

* fix: remove debug
This commit is contained in:
Jérémy M
2024-02-07 15:38:23 +01:00
committed by GitHub
parent b119dd8e9c
commit a908353955
13 changed files with 421 additions and 311 deletions

View File

@ -1,3 +1,6 @@
export function generateMigrationName(name?: string): string {
return `${new Date().getTime()}${name ? `-${name}` : ''}`;
export function generateMigrationName(
name?: string,
addMilliseconds: number = 0,
): string {
return `${new Date().getTime() + addMilliseconds}${name ? `-${name}` : ''}`;
}

View File

@ -0,0 +1,133 @@
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,11 +1,15 @@
import { FeatureFlagFactory } from './feature-flags.factory';
import { StandardObjectFactory } from './standard-object.factory';
import { StandardRelationFactory } from './standard-relation.factory';
import { WorkspaceSyncFactory } from './workspace-sync.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,
WorkspaceSyncFactory,
ObjectWorkspaceMigrationFactory,
FieldWorkspaceMigrationFactory,
RelationWorkspaceMigrationFactory,
];

View File

@ -0,0 +1,114 @@
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

@ -0,0 +1,115 @@
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;
}
}

View File

@ -6,7 +6,7 @@ import { FeatureFlagMap } from 'src/core/feature-flag/interfaces/feature-flag-ma
import { PartialFieldMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
import { standardObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects';
import { standardObjectMetadataCollection } from 'src/workspace/workspace-sync-metadata/standard-objects';
import { TypedReflect } from 'src/utils/typed-reflect';
import { isGatedAndNotEnabled } from 'src/workspace/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
@ -16,7 +16,7 @@ export class StandardObjectFactory {
context: WorkspaceSyncContext,
workspaceFeatureFlagsMap: FeatureFlagMap,
): PartialObjectMetadata[] {
return standardObjectMetadata
return standardObjectMetadataCollection
.map((metadata) =>
this.createObjectMetadata(metadata, context, workspaceFeatureFlagsMap),
)

View File

@ -4,7 +4,7 @@ import { WorkspaceSyncContext } from 'src/workspace/workspace-sync-metadata/inte
import { FeatureFlagMap } from 'src/core/feature-flag/interfaces/feature-flag-map.interface';
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
import { standardObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects';
import { standardObjectMetadataCollection } from 'src/workspace/workspace-sync-metadata/standard-objects';
import { TypedReflect } from 'src/utils/typed-reflect';
import { isGatedAndNotEnabled } from 'src/workspace/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
import { assert } from 'src/utils/assert';
@ -18,7 +18,7 @@ export class StandardRelationFactory {
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
workspaceFeatureFlagsMap: FeatureFlagMap,
): Partial<RelationMetadataEntity>[] {
return standardObjectMetadata.flatMap((standardObjectMetadata) =>
return standardObjectMetadataCollection.flatMap((standardObjectMetadata) =>
this.createRelationMetadata(
standardObjectMetadata,
context,

View File

@ -1,268 +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,
WorkspaceMigrationColumnRelation,
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 {
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 WorkspaceSyncFactory {
constructor(
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
) {}
async createObjectMigration(
originalObjectMetadataCollection: ObjectMetadataEntity[],
createdObjectMetadataCollection: ObjectMetadataEntity[],
objectMetadataDeleteCollection: ObjectMetadataEntity[],
createdFieldMetadataCollection: FieldMetadataEntity[],
fieldMetadataDeleteCollection: FieldMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
/**
* Create object migrations
*/
if (createdObjectMetadataCollection.length > 0) {
for (const objectMetadata of createdObjectMetadataCollection) {
const migrations = [
{
name: computeObjectTargetTable(objectMetadata),
action: 'create',
} satisfies WorkspaceMigrationTableAction,
...objectMetadata.fields
.filter((field) => field.type !== FieldMetadataType.RELATION)
.map(
(field) =>
({
name: computeObjectTargetTable(objectMetadata),
action: 'alter',
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
field,
),
}) satisfies WorkspaceMigrationTableAction,
),
];
workspaceMigrations.push({
workspaceId: objectMetadata.workspaceId,
name: generateMigrationName(`create-${objectMetadata.nameSingular}`),
isCustom: false,
migrations,
});
}
}
/**
* Delete object migrations
* TODO: handle object delete migrations.
* Note: we need to delete the relation first due to the DB constraint.
*/
// if (objectMetadataDeleteCollection.length > 0) {
// for (const objectMetadata of objectMetadataDeleteCollection) {
// const migrations = [
// {
// name: computeObjectTargetTable(objectMetadata),
// action: 'drop',
// columns: [],
// } satisfies WorkspaceMigrationTableAction,
// ];
// workspaceMigrations.push({
// workspaceId: objectMetadata.workspaceId,
// isCustom: false,
// migrations,
// });
// }
// }
/**
* Create field migrations
*/
const originalObjectMetadataMap = originalObjectMetadataCollection.reduce(
(result, currentObject) => {
result[currentObject.id] = currentObject;
return result;
},
{} as Record<string, ObjectMetadataEntity>,
);
if (createdFieldMetadataCollection.length > 0) {
for (const fieldMetadata of createdFieldMetadataCollection) {
if (fieldMetadata.type === FieldMetadataType.RELATION) {
continue;
}
const migrations = [
{
name: computeObjectTargetTable(
originalObjectMetadataMap[fieldMetadata.objectMetadataId],
),
action: 'alter',
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,
fieldMetadata,
),
} satisfies WorkspaceMigrationTableAction,
];
workspaceMigrations.push({
workspaceId: fieldMetadata.workspaceId,
name: generateMigrationName(`create-${fieldMetadata.name}`),
isCustom: false,
migrations,
});
}
}
/**
* Delete field migrations
*/
if (fieldMetadataDeleteCollection.length > 0) {
for (const fieldMetadata of fieldMetadataDeleteCollection) {
const migrations = [
{
name: computeObjectTargetTable(
originalObjectMetadataMap[fieldMetadata.objectMetadataId],
),
action: 'alter',
columns: [
{
action: WorkspaceMigrationColumnActionType.DROP,
columnName: fieldMetadata.name,
},
],
} satisfies WorkspaceMigrationTableAction,
];
workspaceMigrations.push({
workspaceId: fieldMetadata.workspaceId,
name: generateMigrationName(`delete-${fieldMetadata.name}`),
isCustom: false,
migrations,
});
}
}
return workspaceMigrations;
}
async createRelationMigration(
originalObjectMetadataCollection: ObjectMetadataEntity[],
createdRelationMetadataCollection: RelationMetadataEntity[],
// TODO: handle relation deletion
// eslint-disable-next-line @typescript-eslint/no-unused-vars
relationMetadataDeleteCollection: RelationMetadataEntity[],
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
if (createdRelationMetadataCollection.length > 0) {
for (const relationMetadata of createdRelationMetadataCollection) {
const toObjectMetadata = originalObjectMetadataCollection.find(
(object) => object.id === relationMetadata.toObjectMetadataId,
);
const fromObjectMetadata = originalObjectMetadataCollection.find(
(object) => object.id === 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 = [
{
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,
} satisfies WorkspaceMigrationColumnRelation,
],
} satisfies WorkspaceMigrationTableAction,
];
workspaceMigrations.push({
workspaceId: relationMetadata.workspaceId,
name: generateMigrationName(
`create-relation-from-${fromObjectMetadata.nameSingular}-to-${toObjectMetadata.nameSingular}`,
),
isCustom: false,
migrations,
});
}
}
// if (relationMetadataDeleteCollection.length > 0) {
// for (const relationMetadata of relationMetadataDeleteCollection) {
// const toObjectMetadata = originalObjectMetadataCollection.find(
// (object) => object.id === relationMetadata.toObjectMetadataId,
// );
// if (!toObjectMetadata) {
// throw new Error(
// `ObjectMetadata with id ${relationMetadata.toObjectMetadataId} not found`,
// );
// }
// const migrations = [
// {
// name: computeObjectTargetTable(toObjectMetadata),
// action: 'drop',
// columns: [],
// } satisfies WorkspaceMigrationTableAction,
// ];
// workspaceMigrations.push({
// workspaceId: relationMetadata.workspaceId,
// isCustom: false,
// migrations,
// });
// }
// }
return workspaceMigrations;
}
}

View File

@ -162,6 +162,7 @@ export class WorkspaceMetadataUpdaterService {
const relationMetadataRepository = manager.getRepository(
RelationMetadataEntity,
);
const fieldMetadataRepository = manager.getRepository(FieldMetadataEntity);
/**
* Create relation metadata
@ -182,6 +183,20 @@ export class WorkspaceMetadataUpdaterService {
);
}
/**
* Delete related field metadata
*/
const fieldMetadataDeleteCollectionOnlyRelation =
storage.fieldMetadataDeleteCollection.filter(
(field) => field.type === FieldMetadataType.RELATION,
);
if (fieldMetadataDeleteCollectionOnlyRelation.length > 0) {
await fieldMetadataRepository.delete(
fieldMetadataDeleteCollectionOnlyRelation.map((field) => field.id),
);
}
return {
createdRelationMetadataCollection,
};

View File

@ -13,8 +13,9 @@ import { StandardObjectFactory } from 'src/workspace/workspace-sync-metadata/fac
import { WorkspaceObjectComparator } from 'src/workspace/workspace-sync-metadata/comparators/workspace-object.comparator';
import { WorkspaceFieldComparator } from 'src/workspace/workspace-sync-metadata/comparators/workspace-field.comparator';
import { WorkspaceMetadataUpdaterService } from 'src/workspace/workspace-sync-metadata/services/workspace-metadata-updater.service';
import { WorkspaceSyncFactory } from 'src/workspace/workspace-sync-metadata/factories/workspace-sync.factory';
import { WorkspaceSyncStorage } from 'src/workspace/workspace-sync-metadata/storage/workspace-sync.storage';
import { ObjectWorkspaceMigrationFactory } from 'src/workspace/workspace-sync-metadata/factories/object-workspace-migration.factory';
import { FieldWorkspaceMigrationFactory } from 'src/workspace/workspace-sync-metadata/factories/field-workspace-migration.factory';
@Injectable()
export class WorkspaceSyncObjectMetadataService {
@ -25,7 +26,8 @@ export class WorkspaceSyncObjectMetadataService {
private readonly workspaceObjectComparator: WorkspaceObjectComparator,
private readonly workspaceFieldComparator: WorkspaceFieldComparator,
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
private readonly workspaceSyncFactory: WorkspaceSyncFactory,
private readonly objectWorkspaceMigrationFactory: ObjectWorkspaceMigrationFactory,
private readonly fieldWorkspaceMigrationFactory: FieldWorkspaceMigrationFactory,
) {}
async synchronize(
@ -33,12 +35,9 @@ export class WorkspaceSyncObjectMetadataService {
manager: EntityManager,
storage: WorkspaceSyncStorage,
workspaceFeatureFlagsMap: FeatureFlagMap,
): Promise<WorkspaceMigrationEntity[]> {
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const objectMetadataRepository =
manager.getRepository(ObjectMetadataEntity);
const workspaceMigrationRepository = manager.getRepository(
WorkspaceMigrationEntity,
);
// Retrieve object metadata collection from DB
const originalObjectMetadataCollection =
@ -141,22 +140,21 @@ export class WorkspaceSyncObjectMetadataService {
this.logger.log('Generating migrations');
// Create migrations
const workspaceObjectMigrations =
await this.workspaceSyncFactory.createObjectMigration(
originalObjectMetadataCollection,
const objectWorkspaceMigrations =
await this.objectWorkspaceMigrationFactory.create(
metadataObjectUpdaterResult.createdObjectMetadataCollection,
storage.objectMetadataDeleteCollection,
);
const fieldWorkspaceMigrations =
await this.fieldWorkspaceMigrationFactory.create(
originalObjectMetadataCollection,
metadataFieldUpdaterResult.createdFieldMetadataCollection,
storage.fieldMetadataDeleteCollection,
);
this.logger.log('Saving migrations');
// Save migrations into DB
const workspaceMigrations = await workspaceMigrationRepository.save(
workspaceObjectMigrations,
);
return workspaceMigrations;
return [...objectWorkspaceMigrations, ...fieldWorkspaceMigrations];
}
}

View File

@ -12,9 +12,9 @@ import { mapObjectMetadataByUniqueIdentifier } from 'src/workspace/workspace-syn
import { StandardRelationFactory } from 'src/workspace/workspace-sync-metadata/factories/standard-relation.factory';
import { WorkspaceRelationComparator } from 'src/workspace/workspace-sync-metadata/comparators/workspace-relation.comparator';
import { WorkspaceMetadataUpdaterService } from 'src/workspace/workspace-sync-metadata/services/workspace-metadata-updater.service';
import { WorkspaceSyncFactory } from 'src/workspace/workspace-sync-metadata/factories/workspace-sync.factory';
import { WorkspaceMigrationEntity } from 'src/metadata/workspace-migration/workspace-migration.entity';
import { WorkspaceSyncStorage } from 'src/workspace/workspace-sync-metadata/storage/workspace-sync.storage';
import { RelationWorkspaceMigrationFactory } from 'src/workspace/workspace-sync-metadata/factories/relation-workspace-migration.factory';
@Injectable()
export class WorkspaceSyncRelationMetadataService {
@ -26,7 +26,7 @@ export class WorkspaceSyncRelationMetadataService {
private readonly standardRelationFactory: StandardRelationFactory,
private readonly workspaceRelationComparator: WorkspaceRelationComparator,
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
private readonly workspaceSyncFactory: WorkspaceSyncFactory,
private readonly relationWorkspaceMigrationFactory: RelationWorkspaceMigrationFactory,
) {}
async synchronize(
@ -34,12 +34,9 @@ export class WorkspaceSyncRelationMetadataService {
manager: EntityManager,
storage: WorkspaceSyncStorage,
workspaceFeatureFlagsMap: FeatureFlagMap,
): Promise<WorkspaceMigrationEntity[]> {
): Promise<Partial<WorkspaceMigrationEntity>[]> {
const objectMetadataRepository =
manager.getRepository(ObjectMetadataEntity);
const workspaceMigrationRepository = manager.getRepository(
WorkspaceMigrationEntity,
);
// Retrieve object metadata collection from DB
const originalObjectMetadataCollection =
@ -99,17 +96,11 @@ export class WorkspaceSyncRelationMetadataService {
// Create migrations
const workspaceRelationMigrations =
await this.workspaceSyncFactory.createRelationMigration(
await this.relationWorkspaceMigrationFactory.create(
originalObjectMetadataCollection,
metadataRelationUpdaterResult.createdRelationMetadataCollection,
storage.relationMetadataDeleteCollection,
);
// Save migrations into DB
const workspaceMigrations = await workspaceMigrationRepository.save(
workspaceRelationMigrations,
);
return workspaceMigrations;
return workspaceRelationMigrations;
}
}

View File

@ -21,7 +21,7 @@ import { ViewObjectMetadata } from 'src/workspace/workspace-sync-metadata/standa
import { WebhookObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/webhook.object-metadata';
import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata';
export const standardObjectMetadata = [
export const standardObjectMetadataCollection = [
ActivityTargetObjectMetadata,
ActivityObjectMetadata,
ApiKeyObjectMetadata,

View File

@ -11,6 +11,7 @@ import { WorkspaceSyncObjectMetadataService } from 'src/workspace/workspace-sync
import { WorkspaceSyncRelationMetadataService } from 'src/workspace/workspace-sync-metadata/services/workspace-sync-relation-metadata.service';
import { WorkspaceSyncStorage } from 'src/workspace/workspace-sync-metadata/storage/workspace-sync.storage';
import { WorkspaceLogsService } from 'src/workspace/workspace-sync-metadata/services/workspace-logs.service';
import { WorkspaceMigrationEntity } from 'src/metadata/workspace-migration/workspace-migration.entity';
@Injectable()
export class WorkspaceSyncMetadataService {
@ -48,6 +49,9 @@ export class WorkspaceSyncMetadataService {
try {
const storage = new WorkspaceSyncStorage();
const workspaceMigrationRepository = manager.getRepository(
WorkspaceMigrationEntity,
);
// Retrieve feature flags
const workspaceFeatureFlagsMap =
@ -71,13 +75,14 @@ export class WorkspaceSyncMetadataService {
workspaceFeatureFlagsMap,
);
// Save workspace migrations into the database
const workspaceMigrations = await workspaceMigrationRepository.save([
...workspaceObjectMigrations,
...workspaceRelationMigrations,
]);
// If we're running a dry run, rollback the transaction and do not execute migrations
if (options?.dryRun) {
const workspaceMigrations = [
...workspaceObjectMigrations,
...workspaceRelationMigrations,
];
this.logger.log('Running in dry run mode, rolling back transaction');
await queryRunner.rollbackTransaction();