feat: refactor folder structure (#4498)
* feat: wip refactor folder structure * Fix * fix position --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,270 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { EntityManager, In } 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';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine-metadata/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine-metadata/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataEntity } from 'src/engine-metadata/relation-metadata/relation-metadata.entity';
|
||||
import { FieldMetadataComplexOption } from 'src/engine-metadata/field-metadata/dtos/options.input';
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMetadataUpdaterService {
|
||||
private readonly logger = new Logger(WorkspaceMetadataUpdaterService.name);
|
||||
|
||||
async updateObjectMetadata(
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<{
|
||||
createdObjectMetadataCollection: ObjectMetadataEntity[];
|
||||
updatedObjectMetadataCollection: ObjectMetadataEntity[];
|
||||
}> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
|
||||
/**
|
||||
* Create object metadata
|
||||
*/
|
||||
const createdPartialObjectMetadataCollection =
|
||||
await objectMetadataRepository.save(
|
||||
storage.objectMetadataCreateCollection.map((objectMetadata) => ({
|
||||
...objectMetadata,
|
||||
isActive: true,
|
||||
fields: objectMetadata.fields.map((field) =>
|
||||
this.prepareFieldMetadataForCreation(field),
|
||||
),
|
||||
})) as DeepPartial<ObjectMetadataEntity>[],
|
||||
);
|
||||
const identifiers = createdPartialObjectMetadataCollection.map(
|
||||
(object) => object.id,
|
||||
);
|
||||
const createdObjectMetadataCollection = await manager.find(
|
||||
ObjectMetadataEntity,
|
||||
{
|
||||
where: { id: In(identifiers) },
|
||||
relations: ['dataSource', 'fields'],
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Update object metadata
|
||||
*/
|
||||
const updatedObjectMetadataCollection = await objectMetadataRepository.save(
|
||||
storage.objectMetadataUpdateCollection.map((objectMetadata) =>
|
||||
omit(objectMetadata, ['fields']),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Delete object metadata
|
||||
*/
|
||||
if (storage.objectMetadataDeleteCollection.length > 0) {
|
||||
await objectMetadataRepository.delete(
|
||||
storage.objectMetadataDeleteCollection.map((object) => object.id),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
createdObjectMetadataCollection,
|
||||
updatedObjectMetadataCollection,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Refactor this
|
||||
*/
|
||||
private prepareFieldMetadataForCreation(field: PartialFieldMetadata) {
|
||||
return {
|
||||
...field,
|
||||
...(field.type === FieldMetadataType.SELECT && field.options
|
||||
? {
|
||||
options: this.generateUUIDForNewSelectFieldOptions(
|
||||
field.options as FieldMetadataComplexOption[],
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
isActive: true,
|
||||
};
|
||||
}
|
||||
|
||||
private generateUUIDForNewSelectFieldOptions(
|
||||
options: FieldMetadataComplexOption[],
|
||||
): FieldMetadataComplexOption[] {
|
||||
return options.map((option) => ({
|
||||
...option,
|
||||
id: uuidV4(),
|
||||
}));
|
||||
}
|
||||
|
||||
async updateFieldMetadata(
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<{
|
||||
createdFieldMetadataCollection: FieldMetadataEntity[];
|
||||
updatedFieldMetadataCollection: {
|
||||
current: FieldMetadataEntity;
|
||||
altered: FieldMetadataEntity;
|
||||
}[];
|
||||
}> {
|
||||
const fieldMetadataRepository = manager.getRepository(FieldMetadataEntity);
|
||||
|
||||
/**
|
||||
* Create field metadata
|
||||
*/
|
||||
const createdFieldMetadataCollection = await fieldMetadataRepository.save(
|
||||
storage.fieldMetadataCreateCollection.map((field) =>
|
||||
this.prepareFieldMetadataForCreation(field),
|
||||
) as DeepPartial<FieldMetadataEntity>[],
|
||||
);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
);
|
||||
|
||||
/**
|
||||
* Delete field metadata
|
||||
*/
|
||||
// TODO: handle relation fields deletion. We need to delete the relation metadata first due to the DB constraint.
|
||||
const fieldMetadataDeleteCollectionWithoutRelationType =
|
||||
storage.fieldMetadataDeleteCollection.filter(
|
||||
(field) => field.type !== FieldMetadataType.RELATION,
|
||||
);
|
||||
|
||||
if (fieldMetadataDeleteCollectionWithoutRelationType.length > 0) {
|
||||
await fieldMetadataRepository.delete(
|
||||
fieldMetadataDeleteCollectionWithoutRelationType.map(
|
||||
(field) => field.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async updateRelationMetadata(
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<{
|
||||
createdRelationMetadataCollection: RelationMetadataEntity[];
|
||||
updatedRelationMetadataCollection: RelationMetadataEntity[];
|
||||
}> {
|
||||
const relationMetadataRepository = manager.getRepository(
|
||||
RelationMetadataEntity,
|
||||
);
|
||||
const fieldMetadataRepository = manager.getRepository(FieldMetadataEntity);
|
||||
|
||||
/**
|
||||
* Create relation metadata
|
||||
*/
|
||||
const createdRelationMetadataCollection =
|
||||
await relationMetadataRepository.save(
|
||||
storage.relationMetadataCreateCollection,
|
||||
);
|
||||
|
||||
/**
|
||||
* Update relation metadata
|
||||
*/
|
||||
|
||||
const updatedRelationMetadataCollection =
|
||||
await relationMetadataRepository.save(
|
||||
storage.relationMetadataUpdateCollection,
|
||||
);
|
||||
|
||||
/**
|
||||
* Delete relation metadata
|
||||
*/
|
||||
if (storage.relationMetadataDeleteCollection.length > 0) {
|
||||
await relationMetadataRepository.delete(
|
||||
storage.relationMetadataDeleteCollection.map(
|
||||
(relationMetadata) => relationMetadata.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
updatedRelationMetadataCollection,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { FeatureFlagMap } from 'src/engine/modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine-metadata/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMigrationEntity } from 'src/engine-metadata/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceFieldComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field.comparator';
|
||||
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
import { WorkspaceMigrationFieldFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field.factory';
|
||||
import { StandardFieldFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory';
|
||||
import { CustomObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata';
|
||||
import { computeStandardObject } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-object.util';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceSyncFieldMetadataService {
|
||||
private readonly logger = new Logger(WorkspaceSyncFieldMetadataService.name);
|
||||
|
||||
constructor(
|
||||
private readonly standardFieldFactory: StandardFieldFactory,
|
||||
private readonly workspaceFieldComparator: WorkspaceFieldComparator,
|
||||
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
|
||||
private readonly workspaceMigrationFieldFactory: WorkspaceMigrationFieldFactory,
|
||||
) {}
|
||||
|
||||
async synchronize(
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
|
||||
// Retrieve object metadata collection from DB
|
||||
const originalObjectMetadataCollection =
|
||||
await objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId: context.workspaceId,
|
||||
// We're only interested in standard fields
|
||||
fields: { isCustom: false },
|
||||
},
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
|
||||
// Filter out custom objects
|
||||
const customObjectMetadataCollection =
|
||||
originalObjectMetadataCollection.filter(
|
||||
(objectMetadata) => objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
// Create standard field metadata collection
|
||||
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
||||
CustomObjectMetadata,
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
// Loop over all standard objects and compare them with the objects in DB
|
||||
for (const customObjectMetadata of customObjectMetadataCollection) {
|
||||
// Also, maybe it's better to refactor a bit and move generation part into a separate module ?
|
||||
const standardObjectMetadata = computeStandardObject(
|
||||
{
|
||||
...customObjectMetadata,
|
||||
fields: standardFieldMetadataCollection,
|
||||
},
|
||||
customObjectMetadata,
|
||||
);
|
||||
|
||||
/**
|
||||
* COMPARE FIELD METADATA
|
||||
*/
|
||||
const fieldComparatorResults = this.workspaceFieldComparator.compare(
|
||||
customObjectMetadata,
|
||||
standardObjectMetadata,
|
||||
);
|
||||
|
||||
for (const fieldComparatorResult of fieldComparatorResults) {
|
||||
switch (fieldComparatorResult.action) {
|
||||
case ComparatorAction.CREATE: {
|
||||
storage.addCreateFieldMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
case ComparatorAction.UPDATE: {
|
||||
storage.addUpdateFieldMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
case ComparatorAction.DELETE: {
|
||||
storage.addDeleteFieldMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log('Updating workspace metadata');
|
||||
|
||||
const metadataFieldUpdaterResult =
|
||||
await this.workspaceMetadataUpdaterService.updateFieldMetadata(
|
||||
manager,
|
||||
storage,
|
||||
);
|
||||
|
||||
this.logger.log('Generating migrations');
|
||||
|
||||
const createFieldWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
metadataFieldUpdaterResult.createdFieldMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.CREATE,
|
||||
);
|
||||
|
||||
const updateFieldWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
metadataFieldUpdaterResult.updatedFieldMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.UPDATE,
|
||||
);
|
||||
|
||||
const deleteFieldWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
storage.fieldMetadataDeleteCollection,
|
||||
WorkspaceMigrationBuilderAction.DELETE,
|
||||
);
|
||||
|
||||
this.logger.log('Saving migrations');
|
||||
|
||||
return [
|
||||
...createFieldWorkspaceMigrations,
|
||||
...updateFieldWorkspaceMigrations,
|
||||
...deleteFieldWorkspaceMigrations,
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { FeatureFlagMap } from 'src/engine/modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine-metadata/object-metadata/object-metadata.entity';
|
||||
import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/sync-metadata.util';
|
||||
import { WorkspaceMigrationEntity } from 'src/engine-metadata/workspace-migration/workspace-migration.entity';
|
||||
import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory';
|
||||
import { WorkspaceObjectComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-object.comparator';
|
||||
import { WorkspaceFieldComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field.comparator';
|
||||
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
import { WorkspaceMigrationObjectFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-object.factory';
|
||||
import { computeStandardObject } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-object.util';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceSyncObjectMetadataService {
|
||||
private readonly logger = new Logger(WorkspaceSyncObjectMetadataService.name);
|
||||
|
||||
constructor(
|
||||
private readonly standardObjectFactory: StandardObjectFactory,
|
||||
private readonly workspaceObjectComparator: WorkspaceObjectComparator,
|
||||
private readonly workspaceFieldComparator: WorkspaceFieldComparator,
|
||||
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
|
||||
private readonly workspaceMigrationObjectFactory: WorkspaceMigrationObjectFactory,
|
||||
) {}
|
||||
|
||||
async synchronize(
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
|
||||
// Retrieve object metadata collection from DB
|
||||
const originalObjectMetadataCollection =
|
||||
await objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId: context.workspaceId,
|
||||
fields: { isCustom: false },
|
||||
},
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
const customObjectMetadataCollection =
|
||||
originalObjectMetadataCollection.filter(
|
||||
(objectMetadata) => objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
// Create standard object metadata collection
|
||||
const standardObjectMetadataCollection = this.standardObjectFactory.create(
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
// Create map of original and standard object metadata by standard ids
|
||||
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
||||
originalObjectMetadataCollection,
|
||||
);
|
||||
const standardObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
||||
standardObjectMetadataCollection,
|
||||
);
|
||||
|
||||
this.logger.log('Comparing standard objects and fields metadata');
|
||||
|
||||
// Store object that need to be deleted
|
||||
for (const originalObjectMetadata of originalObjectMetadataCollection.filter(
|
||||
(object) => !object.isCustom,
|
||||
)) {
|
||||
if (
|
||||
originalObjectMetadata.standardId &&
|
||||
!standardObjectMetadataMap[originalObjectMetadata.standardId]
|
||||
) {
|
||||
storage.addDeleteObjectMetadata(originalObjectMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over all standard objects and compare them with the objects in DB
|
||||
for (const standardObjectId in standardObjectMetadataMap) {
|
||||
const originalObjectMetadata =
|
||||
originalObjectMetadataMap[standardObjectId];
|
||||
const standardObjectMetadata = computeStandardObject(
|
||||
standardObjectMetadataMap[standardObjectId],
|
||||
originalObjectMetadata,
|
||||
customObjectMetadataCollection,
|
||||
);
|
||||
|
||||
/**
|
||||
* COMPARE OBJECT METADATA
|
||||
*/
|
||||
const objectComparatorResult = this.workspaceObjectComparator.compare(
|
||||
originalObjectMetadata,
|
||||
standardObjectMetadata,
|
||||
);
|
||||
|
||||
if (objectComparatorResult.action === ComparatorAction.CREATE) {
|
||||
storage.addCreateObjectMetadata(standardObjectMetadata);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (objectComparatorResult.action === ComparatorAction.UPDATE) {
|
||||
storage.addUpdateObjectMetadata(objectComparatorResult.object);
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPARE FIELD METADATA
|
||||
*/
|
||||
const fieldComparatorResults = this.workspaceFieldComparator.compare(
|
||||
originalObjectMetadata,
|
||||
standardObjectMetadata,
|
||||
);
|
||||
|
||||
for (const fieldComparatorResult of fieldComparatorResults) {
|
||||
switch (fieldComparatorResult.action) {
|
||||
case ComparatorAction.CREATE: {
|
||||
storage.addCreateFieldMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
case ComparatorAction.UPDATE: {
|
||||
storage.addUpdateFieldMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
case ComparatorAction.DELETE: {
|
||||
storage.addDeleteFieldMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log('Updating workspace metadata');
|
||||
|
||||
// Apply changes to DB
|
||||
const metadataObjectUpdaterResult =
|
||||
await this.workspaceMetadataUpdaterService.updateObjectMetadata(
|
||||
manager,
|
||||
storage,
|
||||
);
|
||||
|
||||
this.logger.log('Generating migrations');
|
||||
|
||||
// Create migrations
|
||||
const createObjectWorkspaceMigrations =
|
||||
await this.workspaceMigrationObjectFactory.create(
|
||||
metadataObjectUpdaterResult.createdObjectMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.CREATE,
|
||||
);
|
||||
|
||||
const deleteObjectWorkspaceMigrations =
|
||||
await this.workspaceMigrationObjectFactory.create(
|
||||
storage.objectMetadataDeleteCollection,
|
||||
WorkspaceMigrationBuilderAction.DELETE,
|
||||
);
|
||||
|
||||
this.logger.log('Saving migrations');
|
||||
|
||||
return [
|
||||
...createObjectWorkspaceMigrations,
|
||||
...deleteObjectWorkspaceMigrations,
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
import { FeatureFlagMap } from 'src/engine/modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine-metadata/object-metadata/object-metadata.entity';
|
||||
import { RelationMetadataEntity } from 'src/engine-metadata/relation-metadata/relation-metadata.entity';
|
||||
import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/sync-metadata.util';
|
||||
import { StandardRelationFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-relation.factory';
|
||||
import { WorkspaceRelationComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-relation.comparator';
|
||||
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
|
||||
import { WorkspaceMigrationEntity } from 'src/engine-metadata/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
import { WorkspaceMigrationRelationFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-relation.factory';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
import { CustomObjectMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceSyncRelationMetadataService {
|
||||
private readonly logger = new Logger(
|
||||
WorkspaceSyncRelationMetadataService.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly standardRelationFactory: StandardRelationFactory,
|
||||
private readonly workspaceRelationComparator: WorkspaceRelationComparator,
|
||||
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
|
||||
private readonly workspaceMigrationRelationFactory: WorkspaceMigrationRelationFactory,
|
||||
) {}
|
||||
|
||||
async synchronize(
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
|
||||
// Retrieve object metadata collection from DB
|
||||
const originalObjectMetadataCollection =
|
||||
await objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId: context.workspaceId,
|
||||
fields: { isCustom: false },
|
||||
},
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
const customObjectMetadataCollection =
|
||||
originalObjectMetadataCollection.filter(
|
||||
(objectMetadata) => objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
// Create map of object metadata & field metadata by unique identifier
|
||||
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
||||
originalObjectMetadataCollection,
|
||||
// Relation are based on the singular name
|
||||
(objectMetadata) => objectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
const relationMetadataRepository = manager.getRepository(
|
||||
RelationMetadataEntity,
|
||||
);
|
||||
|
||||
// Retrieve relation metadata collection from DB
|
||||
const originalRelationMetadataCollection =
|
||||
await relationMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId: context.workspaceId,
|
||||
fromFieldMetadata: { isCustom: false },
|
||||
},
|
||||
});
|
||||
|
||||
// Create standard relation metadata collection
|
||||
const standardRelationMetadataCollection =
|
||||
this.standardRelationFactory.create(
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
originalObjectMetadataMap,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
const customRelationMetadataCollection =
|
||||
this.standardRelationFactory.create(
|
||||
customObjectMetadataCollection.map((objectMetadata) => ({
|
||||
object: objectMetadata,
|
||||
metadata: CustomObjectMetadata,
|
||||
})),
|
||||
context,
|
||||
originalObjectMetadataMap,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
const relationComparatorResults = this.workspaceRelationComparator.compare(
|
||||
originalRelationMetadataCollection,
|
||||
[
|
||||
...standardRelationMetadataCollection,
|
||||
...customRelationMetadataCollection,
|
||||
],
|
||||
);
|
||||
|
||||
for (const relationComparatorResult of relationComparatorResults) {
|
||||
if (relationComparatorResult.action === ComparatorAction.CREATE) {
|
||||
storage.addCreateRelationMetadata(relationComparatorResult.object);
|
||||
} else if (relationComparatorResult.action === ComparatorAction.UPDATE) {
|
||||
storage.addUpdateRelationMetadata(relationComparatorResult.object);
|
||||
} else if (relationComparatorResult.action === ComparatorAction.DELETE) {
|
||||
storage.addDeleteRelationMetadata(relationComparatorResult.object);
|
||||
}
|
||||
}
|
||||
|
||||
const metadataRelationUpdaterResult =
|
||||
await this.workspaceMetadataUpdaterService.updateRelationMetadata(
|
||||
manager,
|
||||
storage,
|
||||
);
|
||||
|
||||
// Create migrations
|
||||
const createRelationWorkspaceMigrations =
|
||||
await this.workspaceMigrationRelationFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
metadataRelationUpdaterResult.createdRelationMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.CREATE,
|
||||
);
|
||||
|
||||
const updateRelationWorkspaceMigrations =
|
||||
await this.workspaceMigrationRelationFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
metadataRelationUpdaterResult.updatedRelationMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.UPDATE,
|
||||
);
|
||||
|
||||
return [
|
||||
...createRelationWorkspaceMigrations,
|
||||
...updateRelationWorkspaceMigrations,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user