feat: new relation sync-metadata, twenty-orm, create/update (#10217)
Fix https://github.com/twentyhq/core-team-issues/issues/330#issue-2827026606 and https://github.com/twentyhq/core-team-issues/issues/327#issue-2827001814 What this PR does when `isNewRelationEnabled` is set to `true`: - [x] Drop the creation of the foreign key as a `FieldMetadata` - [x] Stop creating `RelationMetadata` - [x] Properly fill `FieldMetadata` of type `RELATION` during the sync command - [x] Use new relation settings in TwentyORM - [x] Properly create `FieldMetadata` relations when we create a new object - [x] Handle `database:reset` with new relations --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
This commit is contained in:
+151
-44
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import {
|
||||
EntityManager,
|
||||
EntityTarget,
|
||||
@@ -10,12 +12,13 @@ import {
|
||||
} from 'typeorm';
|
||||
import { DeepPartial } from 'typeorm/common/DeepPartial';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { PartialFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||
import { PartialIndexMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-index-metadata.interface';
|
||||
import { UpdaterOptions } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/updater-options.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@@ -25,63 +28,77 @@ import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
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';
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMetadataUpdaterService {
|
||||
constructor(private readonly featureFlagService: FeatureFlagService) {}
|
||||
|
||||
async updateObjectMetadata(
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
options?: UpdaterOptions,
|
||||
): Promise<{
|
||||
createdObjectMetadataCollection: ObjectMetadataEntity[];
|
||||
updatedObjectMetadataCollection: ObjectMetadataUpdate[];
|
||||
}> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
let createdObjectMetadataCollection: ObjectMetadataEntity[] = [];
|
||||
let updatedObjectMetadataCollection: ObjectMetadataUpdate[] = [];
|
||||
|
||||
/**
|
||||
* Create object metadata
|
||||
*/
|
||||
const createdPartialObjectMetadataCollection =
|
||||
await objectMetadataRepository.save(
|
||||
storage.objectMetadataCreateCollection.map((objectMetadata) => ({
|
||||
...objectMetadata,
|
||||
isActive: true,
|
||||
})) as DeepPartial<ObjectMetadataEntity>[],
|
||||
if (!options || options.actions.includes('create')) {
|
||||
const createdPartialObjectMetadataCollection =
|
||||
await objectMetadataRepository.save(
|
||||
storage.objectMetadataCreateCollection.map((objectMetadata) => ({
|
||||
...objectMetadata,
|
||||
isActive: true,
|
||||
})) as DeepPartial<ObjectMetadataEntity>[],
|
||||
);
|
||||
const identifiers = createdPartialObjectMetadataCollection.map(
|
||||
(object) => object.id,
|
||||
);
|
||||
const identifiers = createdPartialObjectMetadataCollection.map(
|
||||
(object) => object.id,
|
||||
);
|
||||
const createdObjectMetadataCollection = await manager.find(
|
||||
ObjectMetadataEntity,
|
||||
{
|
||||
where: { id: In(identifiers) },
|
||||
relations: ['dataSource', 'fields'],
|
||||
},
|
||||
);
|
||||
|
||||
createdObjectMetadataCollection = await manager.find(
|
||||
ObjectMetadataEntity,
|
||||
{
|
||||
where: { id: In(identifiers) },
|
||||
relations: ['dataSource', 'fields'],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update object metadata
|
||||
*/
|
||||
const updatedObjectMetadataCollection = await this.updateEntities(
|
||||
manager,
|
||||
ObjectMetadataEntity,
|
||||
storage.objectMetadataUpdateCollection,
|
||||
[
|
||||
'fields',
|
||||
'dataSourceId',
|
||||
'workspaceId',
|
||||
'labelIdentifierFieldMetadataId',
|
||||
'imageIdentifierFieldMetadataId',
|
||||
],
|
||||
);
|
||||
if (!options || options.actions.includes('update')) {
|
||||
updatedObjectMetadataCollection = await this.updateEntities(
|
||||
manager,
|
||||
ObjectMetadataEntity,
|
||||
storage.objectMetadataUpdateCollection,
|
||||
[
|
||||
'fields',
|
||||
'dataSourceId',
|
||||
'workspaceId',
|
||||
'labelIdentifierFieldMetadataId',
|
||||
'imageIdentifierFieldMetadataId',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete object metadata
|
||||
*/
|
||||
if (storage.objectMetadataDeleteCollection.length > 0) {
|
||||
if (
|
||||
storage.objectMetadataDeleteCollection.length > 0 &&
|
||||
(!options || options.actions.includes('delete'))
|
||||
) {
|
||||
await objectMetadataRepository.delete(
|
||||
storage.objectMetadataDeleteCollection.map((object) => object.id),
|
||||
);
|
||||
@@ -121,6 +138,7 @@ export class WorkspaceMetadataUpdaterService {
|
||||
async updateFieldMetadata(
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
options?: UpdaterOptions,
|
||||
): Promise<{
|
||||
createdFieldMetadataCollection: FieldMetadataEntity[];
|
||||
updatedFieldMetadataCollection: FieldMetadataUpdate[];
|
||||
@@ -130,26 +148,32 @@ export class WorkspaceMetadataUpdaterService {
|
||||
IndexFieldMetadataEntity,
|
||||
);
|
||||
const indexMetadataRepository = manager.getRepository(IndexMetadataEntity);
|
||||
let createdFieldMetadataCollection: FieldMetadataEntity[] = [];
|
||||
let updatedFieldMetadataCollection: FieldMetadataUpdate[] = [];
|
||||
|
||||
/**
|
||||
* Update field metadata
|
||||
*/
|
||||
const updatedFieldMetadataCollection =
|
||||
await this.updateEntities<FieldMetadataEntity>(
|
||||
manager,
|
||||
FieldMetadataEntity,
|
||||
storage.fieldMetadataUpdateCollection,
|
||||
['objectMetadataId', 'workspaceId'],
|
||||
);
|
||||
if (!options || options.actions.includes('update')) {
|
||||
updatedFieldMetadataCollection =
|
||||
await this.updateEntities<FieldMetadataEntity>(
|
||||
manager,
|
||||
FieldMetadataEntity,
|
||||
storage.fieldMetadataUpdateCollection,
|
||||
['objectMetadataId', 'workspaceId'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create field metadata
|
||||
*/
|
||||
const createdFieldMetadataCollection = await fieldMetadataRepository.save(
|
||||
storage.fieldMetadataCreateCollection.map((field) =>
|
||||
this.prepareFieldMetadataForCreation(field),
|
||||
) as DeepPartial<FieldMetadataEntity>[],
|
||||
);
|
||||
if (!options || options.actions.includes('create')) {
|
||||
createdFieldMetadataCollection = await fieldMetadataRepository.save(
|
||||
storage.fieldMetadataCreateCollection.map((field) =>
|
||||
this.prepareFieldMetadataForCreation(field),
|
||||
) as DeepPartial<FieldMetadataEntity>[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete field metadata
|
||||
@@ -160,7 +184,10 @@ export class WorkspaceMetadataUpdaterService {
|
||||
(field) => field.type !== FieldMetadataType.RELATION,
|
||||
);
|
||||
|
||||
if (fieldMetadataDeleteCollectionWithoutRelationType.length > 0) {
|
||||
if (
|
||||
fieldMetadataDeleteCollectionWithoutRelationType.length > 0 &&
|
||||
(!options || options.actions.includes('delete'))
|
||||
) {
|
||||
await this.deleteIndexFieldMetadata(
|
||||
fieldMetadataDeleteCollectionWithoutRelationType,
|
||||
indexFieldMetadataRepository,
|
||||
@@ -181,6 +208,68 @@ export class WorkspaceMetadataUpdaterService {
|
||||
};
|
||||
}
|
||||
|
||||
async updateFieldRelationMetadata(
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
options?: UpdaterOptions,
|
||||
): Promise<{
|
||||
createdFieldRelationMetadataCollection: FieldMetadataUpdate<FieldMetadataType.RELATION>[];
|
||||
updatedFieldRelationMetadataCollection: FieldMetadataUpdate<FieldMetadataType.RELATION>[];
|
||||
deletedFieldRelationMetadataCollection: FieldMetadataUpdate<FieldMetadataType.RELATION>[];
|
||||
}> {
|
||||
let createdFieldRelationMetadataCollection: FieldMetadataUpdate<FieldMetadataType.RELATION>[] =
|
||||
[];
|
||||
let updatedFieldRelationMetadataCollection: FieldMetadataUpdate<FieldMetadataType.RELATION>[] =
|
||||
[];
|
||||
let deletedFieldRelationMetadataCollection: FieldMetadataUpdate<FieldMetadataType.RELATION>[] =
|
||||
[];
|
||||
|
||||
/**
|
||||
* Create field relation metadata
|
||||
*/
|
||||
if (!options || options.actions.includes('create')) {
|
||||
createdFieldRelationMetadataCollection = await this.updateEntities<
|
||||
FieldMetadataEntity<FieldMetadataType.RELATION>
|
||||
>(
|
||||
manager,
|
||||
FieldMetadataEntity,
|
||||
storage.fieldRelationMetadataCreateCollection,
|
||||
['objectMetadataId', 'workspaceId'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update field relation metadata
|
||||
*/
|
||||
if (!options || options.actions.includes('update')) {
|
||||
updatedFieldRelationMetadataCollection = await this.updateEntities<
|
||||
FieldMetadataEntity<FieldMetadataType.RELATION>
|
||||
>(
|
||||
manager,
|
||||
FieldMetadataEntity,
|
||||
storage.fieldRelationMetadataUpdateCollection,
|
||||
['objectMetadataId', 'workspaceId'],
|
||||
);
|
||||
}
|
||||
|
||||
if (!options || options.actions.includes('delete')) {
|
||||
deletedFieldRelationMetadataCollection = await this.updateEntities<
|
||||
FieldMetadataEntity<FieldMetadataType.RELATION>
|
||||
>(
|
||||
manager,
|
||||
FieldMetadataEntity,
|
||||
storage.fieldRelationMetadataDeleteCollection,
|
||||
['objectMetadataId', 'workspaceId'],
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
createdFieldRelationMetadataCollection,
|
||||
updatedFieldRelationMetadataCollection,
|
||||
deletedFieldRelationMetadataCollection,
|
||||
};
|
||||
}
|
||||
|
||||
async deleteIndexFieldMetadata(
|
||||
fieldMetadataDeleteCollectionWithoutRelationType: Partial<FieldMetadataEntity>[],
|
||||
indexFieldMetadataRepository: Repository<IndexFieldMetadataEntity>,
|
||||
@@ -276,6 +365,15 @@ export class WorkspaceMetadataUpdaterService {
|
||||
createdIndexMetadataCollection: IndexMetadataEntity[];
|
||||
}> {
|
||||
const indexMetadataRepository = manager.getRepository(IndexMetadataEntity);
|
||||
const workspaceId = originalObjectMetadataCollection?.[0]?.workspaceId;
|
||||
let isNewRelationEnabled = false;
|
||||
|
||||
if (workspaceId) {
|
||||
isNewRelationEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsNewRelationEnabled,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
const convertIndexMetadataForSaving = (
|
||||
indexMetadata: PartialIndexMetadata,
|
||||
@@ -288,6 +386,15 @@ export class WorkspaceMetadataUpdaterService {
|
||||
const fieldMetadata = originalObjectMetadataCollection
|
||||
.find((object) => object.id === indexMetadata.objectMetadataId)
|
||||
?.fields.find((field) => {
|
||||
if (
|
||||
isNewRelationEnabled &&
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION)
|
||||
) {
|
||||
if (field.settings?.joinColumnName === column) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (field.name === column) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceMigrationBuilderAction } from 'src/engine/workspace-manager/workspace-migration-builder/interfaces/workspace-migration-builder-action.interface';
|
||||
import {
|
||||
ComparatorAction,
|
||||
FieldRelationComparatorResult,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
||||
import { WorkspaceMigrationFieldRelationFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory';
|
||||
import {
|
||||
FieldMetadataUpdate,
|
||||
WorkspaceMigrationFieldFactory,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field.factory';
|
||||
import { WorkspaceFieldRelationComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field-relation.comparator';
|
||||
import { StandardFieldRelationFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field-relation.factory';
|
||||
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-sync-metadata/storage/workspace-sync.storage';
|
||||
import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/sync-metadata.util';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceSyncFieldMetadataRelationService {
|
||||
private readonly logger = new Logger(
|
||||
WorkspaceSyncFieldMetadataRelationService.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly standardFieldRelationFactory: StandardFieldRelationFactory,
|
||||
private readonly workspaceFieldRelationComparator: WorkspaceFieldRelationComparator,
|
||||
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
|
||||
private readonly workspaceMigrationFieldFactory: WorkspaceMigrationFieldFactory,
|
||||
private readonly workspaceMigrationFieldRelationFactory: WorkspaceMigrationFieldRelationFactory,
|
||||
) {}
|
||||
|
||||
async synchronize(
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
let originalObjectMetadataCollection =
|
||||
await this.getOriginalObjectMetadataCollection(context, manager);
|
||||
|
||||
const customObjectMetadataCollection =
|
||||
originalObjectMetadataCollection.filter(
|
||||
(objectMetadata) => objectMetadata.isCustom,
|
||||
);
|
||||
|
||||
// Create map of object metadata & field metadata by unique identifier
|
||||
const originalObjectMetadataMapByName = mapObjectMetadataByUniqueIdentifier(
|
||||
originalObjectMetadataCollection,
|
||||
// Relation are based on the singular name
|
||||
(objectMetadata) => objectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
await this.synchronizeStandardObjectRelationFields(
|
||||
context,
|
||||
originalObjectMetadataCollection,
|
||||
originalObjectMetadataMapByName,
|
||||
storage,
|
||||
);
|
||||
|
||||
await this.synchronizeCustomObjectRelationFields(
|
||||
context,
|
||||
customObjectMetadataCollection,
|
||||
originalObjectMetadataMapByName,
|
||||
storage,
|
||||
);
|
||||
|
||||
this.logger.log('Updating workspace metadata');
|
||||
|
||||
// Save field metadata to DB
|
||||
const metadataFieldUpdaterResult =
|
||||
await this.workspaceMetadataUpdaterService.updateFieldRelationMetadata(
|
||||
manager,
|
||||
storage,
|
||||
);
|
||||
|
||||
this.logger.log('Generating migrations');
|
||||
|
||||
const updateFieldWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
[
|
||||
// Both of them are update as field where created in WorkspaceSyncFieldMetadataService
|
||||
...metadataFieldUpdaterResult.createdFieldRelationMetadataCollection,
|
||||
...metadataFieldUpdaterResult.updatedFieldRelationMetadataCollection,
|
||||
...metadataFieldUpdaterResult.deletedFieldRelationMetadataCollection,
|
||||
] as FieldMetadataUpdate[],
|
||||
WorkspaceMigrationBuilderAction.UPDATE,
|
||||
);
|
||||
|
||||
// Resync updated object metadata
|
||||
originalObjectMetadataCollection =
|
||||
await this.getOriginalObjectMetadataCollection(context, manager);
|
||||
|
||||
const deletedFieldRelationMetadataCollection =
|
||||
metadataFieldUpdaterResult.deletedFieldRelationMetadataCollection.map(
|
||||
(field) => field.altered,
|
||||
);
|
||||
|
||||
const deleteFieldRelationWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldRelationFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
deletedFieldRelationMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.DELETE,
|
||||
);
|
||||
|
||||
const createdFieldRelationMetadataCollection =
|
||||
metadataFieldUpdaterResult.createdFieldRelationMetadataCollection.map(
|
||||
(field) => field.altered,
|
||||
);
|
||||
|
||||
const createFieldRelationWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldRelationFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
createdFieldRelationMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.CREATE,
|
||||
);
|
||||
|
||||
const updateFieldRelationWorkspaceMigrations =
|
||||
await this.workspaceMigrationFieldRelationFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
metadataFieldUpdaterResult.updatedFieldRelationMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.UPDATE,
|
||||
);
|
||||
|
||||
// TODO: Should we handle deletion of relation here?
|
||||
|
||||
this.logger.log('Saving migrations');
|
||||
|
||||
return [
|
||||
...updateFieldWorkspaceMigrations,
|
||||
...deleteFieldRelationWorkspaceMigrations,
|
||||
...createFieldRelationWorkspaceMigrations,
|
||||
...updateFieldRelationWorkspaceMigrations,
|
||||
];
|
||||
}
|
||||
|
||||
private async synchronizeStandardObjectRelationFields(
|
||||
context: WorkspaceSyncContext,
|
||||
originalObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
originalObjectMetadataMapByName: Record<string, ObjectMetadataEntity>,
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<void> {
|
||||
// Create standard field metadata map
|
||||
const standardFieldMetadataRelationCollection =
|
||||
this.standardFieldRelationFactory.createFieldRelationForStandardObject(
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
originalObjectMetadataMapByName,
|
||||
);
|
||||
|
||||
// Create map of original and standard object metadata by standard ids
|
||||
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
||||
originalObjectMetadataCollection,
|
||||
);
|
||||
|
||||
// Loop over all standard objects and compare them with the objects in DB
|
||||
for (const [
|
||||
standardObjectId,
|
||||
standardFieldMetadataCollection,
|
||||
] of standardFieldMetadataRelationCollection) {
|
||||
const originalObjectMetadata =
|
||||
originalObjectMetadataMap[standardObjectId];
|
||||
|
||||
const originalFieldRelationMetadataCollection =
|
||||
(originalObjectMetadata?.fields.filter(
|
||||
(field) => field.type === FieldMetadataType.RELATION,
|
||||
) ?? []) as FieldMetadataEntity<FieldMetadataType.RELATION>[];
|
||||
|
||||
if (originalFieldRelationMetadataCollection.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldComparatorResults =
|
||||
this.workspaceFieldRelationComparator.compare(
|
||||
originalFieldRelationMetadataCollection,
|
||||
standardFieldMetadataCollection,
|
||||
);
|
||||
|
||||
this.storeComparatorResults(fieldComparatorResults, storage);
|
||||
}
|
||||
}
|
||||
|
||||
private async synchronizeCustomObjectRelationFields(
|
||||
context: WorkspaceSyncContext,
|
||||
customObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
originalObjectMetadataMapByName: Record<string, ObjectMetadataEntity>,
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<void> {
|
||||
// Create standard field metadata collection
|
||||
const customFieldMetadataRelationCollection =
|
||||
this.standardFieldRelationFactory.createFieldRelationForCustomObject(
|
||||
customObjectMetadataCollection.map((objectMetadata) => ({
|
||||
object: objectMetadata,
|
||||
metadata: CustomWorkspaceEntity,
|
||||
})),
|
||||
context,
|
||||
originalObjectMetadataMapByName,
|
||||
);
|
||||
|
||||
// Loop over all custom objects from the DB and compare their fields with standard fields
|
||||
for (const customObjectMetadata of customObjectMetadataCollection) {
|
||||
/**
|
||||
* COMPARE FIELD METADATA
|
||||
*/
|
||||
const fieldComparatorResults =
|
||||
this.workspaceFieldRelationComparator.compare(
|
||||
customObjectMetadata.fields as FieldMetadataEntity<FieldMetadataType.RELATION>[],
|
||||
customFieldMetadataRelationCollection,
|
||||
);
|
||||
|
||||
this.storeComparatorResults(fieldComparatorResults, storage);
|
||||
}
|
||||
}
|
||||
|
||||
private async getOriginalObjectMetadataCollection(
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
): Promise<ObjectMetadataEntity[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
|
||||
const originalObjectMetadataCollection =
|
||||
await objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId: context.workspaceId,
|
||||
fields: {
|
||||
type: FieldMetadataType.RELATION,
|
||||
},
|
||||
},
|
||||
relations: ['dataSource', 'fields'],
|
||||
});
|
||||
|
||||
return originalObjectMetadataCollection;
|
||||
}
|
||||
|
||||
private storeComparatorResults(
|
||||
fieldComparatorResults: FieldRelationComparatorResult[],
|
||||
storage: WorkspaceSyncStorage,
|
||||
): void {
|
||||
for (const fieldComparatorResult of fieldComparatorResults) {
|
||||
switch (fieldComparatorResult.action) {
|
||||
case ComparatorAction.CREATE: {
|
||||
storage.addCreateFieldRelationMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
case ComparatorAction.UPDATE: {
|
||||
storage.addUpdateFieldRelationMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
case ComparatorAction.DELETE: {
|
||||
storage.addDeleteFieldRelationMetadata(fieldComparatorResult.object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-12
@@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-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 {
|
||||
ComparatorAction,
|
||||
@@ -37,7 +36,6 @@ export class WorkspaceSyncFieldMetadataService {
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
@@ -61,14 +59,12 @@ export class WorkspaceSyncFieldMetadataService {
|
||||
originalObjectMetadataCollection,
|
||||
customObjectMetadataCollection,
|
||||
storage,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
await this.synchronizeCustomObjectFields(
|
||||
context,
|
||||
customObjectMetadataCollection,
|
||||
storage,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
this.logger.log('Updating workspace metadata');
|
||||
@@ -121,14 +117,12 @@ export class WorkspaceSyncFieldMetadataService {
|
||||
originalObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
customObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<void> {
|
||||
// Create standard field metadata map
|
||||
const standardObjectStandardFieldMetadataMap =
|
||||
this.standardFieldFactory.create(
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
// Create map of original and standard object metadata by standard ids
|
||||
@@ -145,6 +139,7 @@ export class WorkspaceSyncFieldMetadataService {
|
||||
originalObjectMetadataMap[standardObjectId];
|
||||
|
||||
const computedStandardFieldMetadataCollection = computeStandardFields(
|
||||
context,
|
||||
standardFieldMetadataCollection,
|
||||
originalObjectMetadata,
|
||||
// We need to provide this for generated relations with custom objects
|
||||
@@ -161,24 +156,46 @@ export class WorkspaceSyncFieldMetadataService {
|
||||
}
|
||||
}
|
||||
|
||||
synchronizeCustomObject(
|
||||
context: WorkspaceSyncContext,
|
||||
customObjectMetadata: ObjectMetadataEntity,
|
||||
): FieldComparatorResult[] {
|
||||
// Create standard field metadata collection
|
||||
const customObjectStandardFieldMetadataCollection =
|
||||
this.standardFieldFactory.create(CustomWorkspaceEntity, context);
|
||||
|
||||
const standardFieldMetadataCollection = computeStandardFields(
|
||||
context,
|
||||
customObjectStandardFieldMetadataCollection,
|
||||
customObjectMetadata,
|
||||
);
|
||||
|
||||
/**
|
||||
* COMPARE FIELD METADATA
|
||||
*/
|
||||
const fieldComparatorResults = this.workspaceFieldComparator.compare(
|
||||
customObjectMetadata.id,
|
||||
customObjectMetadata.fields,
|
||||
standardFieldMetadataCollection,
|
||||
);
|
||||
|
||||
return fieldComparatorResults;
|
||||
}
|
||||
|
||||
private async synchronizeCustomObjectFields(
|
||||
context: WorkspaceSyncContext,
|
||||
customObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<void> {
|
||||
// Create standard field metadata collection
|
||||
const customObjectStandardFieldMetadataCollection =
|
||||
this.standardFieldFactory.create(
|
||||
CustomWorkspaceEntity,
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
this.standardFieldFactory.create(CustomWorkspaceEntity, context);
|
||||
|
||||
// Loop over all custom objects from the DB and compare their fields with standard fields
|
||||
for (const customObjectMetadata of customObjectMetadataCollection) {
|
||||
// Also, maybe it's better to refactor a bit and move generation part into a separate module ?
|
||||
const standardFieldMetadataCollection = computeStandardFields(
|
||||
context,
|
||||
customObjectStandardFieldMetadataCollection,
|
||||
customObjectMetadata,
|
||||
);
|
||||
|
||||
-3
@@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { Any, EntityManager } from 'typeorm';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-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 { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
@@ -33,7 +32,6 @@ export class WorkspaceSyncIndexMetadataService {
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
this.logger.log('Syncing index metadata');
|
||||
|
||||
@@ -89,7 +87,6 @@ export class WorkspaceSyncIndexMetadataService {
|
||||
context,
|
||||
originalStandardObjectMetadataMap,
|
||||
originalCustomObjectMetadataMap,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
const indexComparatorResults = this.workspaceIndexComparator.compare(
|
||||
|
||||
+2
-8
@@ -3,7 +3,6 @@ import { Injectable } from '@nestjs/common';
|
||||
import { EntityManager, Repository } from 'typeorm';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@@ -21,7 +20,6 @@ export class WorkspaceSyncObjectMetadataIdentifiersService {
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
_storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<void> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
@@ -32,10 +30,8 @@ export class WorkspaceSyncObjectMetadataIdentifiersService {
|
||||
objectMetadataRepository,
|
||||
);
|
||||
|
||||
const standardObjectMetadataMap = this.createStandardObjectMetadataMap(
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
const standardObjectMetadataMap =
|
||||
this.createStandardObjectMetadataMap(context);
|
||||
|
||||
await this.processObjectMetadataCollection(
|
||||
originalObjectMetadataCollection,
|
||||
@@ -56,12 +52,10 @@ export class WorkspaceSyncObjectMetadataIdentifiersService {
|
||||
|
||||
private createStandardObjectMetadataMap(
|
||||
context: WorkspaceSyncContext,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Record<string, any> {
|
||||
const standardObjectMetadataCollection = this.standardObjectFactory.create(
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
return mapObjectMetadataByUniqueIdentifier(
|
||||
|
||||
-3
@@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-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 { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
@@ -33,7 +32,6 @@ export class WorkspaceSyncObjectMetadataService {
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
@@ -83,7 +81,6 @@ export class WorkspaceSyncObjectMetadataService {
|
||||
const standardObjectMetadataCollection = this.standardObjectFactory.create(
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
// Create map of original and standard object metadata by standard ids
|
||||
|
||||
-4
@@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-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 { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
@@ -32,7 +31,6 @@ export class WorkspaceSyncRelationMetadataService {
|
||||
context: WorkspaceSyncContext,
|
||||
manager: EntityManager,
|
||||
storage: WorkspaceSyncStorage,
|
||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const objectMetadataRepository =
|
||||
manager.getRepository(ObjectMetadataEntity);
|
||||
@@ -76,7 +74,6 @@ export class WorkspaceSyncRelationMetadataService {
|
||||
standardObjectMetadataDefinitions,
|
||||
context,
|
||||
originalObjectMetadataMap,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
const customRelationMetadataCollection =
|
||||
@@ -87,7 +84,6 @@ export class WorkspaceSyncRelationMetadataService {
|
||||
})),
|
||||
context,
|
||||
originalObjectMetadataMap,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
|
||||
const relationComparatorResults = this.workspaceRelationComparator.compare(
|
||||
|
||||
Reference in New Issue
Block a user