mirror of
https://github.com/twentyhq/twenty.git
synced 2024-10-05 13:27:12 +03:00
feat: refactor workspace sync fields (#6069)
This PR was first here to fix the issue related to ticket #5004, after some testing it seems that changing the name of a relation is actually properly working, if we rename `ONE-TO-MANY` side, the only things that is going to be updated is the FieldMetadata as the `joinColumn` is stored on the opposite object. For `MANY-TO-ONE` relations, the `joinColumn` migration is properly generated. We need to take care that if we rename a side of a relation, sometimes the opposite side doesn't have `inverseSideFieldKey` implemented and used by default the name of the opposite object, so this is going to throw an error as the field can't be found in the object. --------- Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com>
This commit is contained in:
parent
a163ccced6
commit
5b26452649
6
.vscode/twenty.code-workspace
vendored
6
.vscode/twenty.code-workspace
vendored
@ -43,21 +43,21 @@
|
|||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true,
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.addMissingImports": "always"
|
"source.addMissingImports": "always"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true,
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.addMissingImports": "always"
|
"source.addMissingImports": "always"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true,
|
"source.fixAll.eslint": "explicit",
|
||||||
"source.addMissingImports": "always"
|
"source.addMissingImports": "always"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -122,7 +122,7 @@ export class TimelineCalendarEventService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
totalNumberOfCalendarEvents: total,
|
totalNumberOfCalendarEvents: total,
|
||||||
timelineCalendarEvents,
|
timelineCalendarEvents: timelineCalendarEvents,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
|||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||||
import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory';
|
import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory';
|
||||||
import { computeStandardObject } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-object.util';
|
|
||||||
import { StandardFieldFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory';
|
import { StandardFieldFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory';
|
||||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
||||||
|
import { computeStandardFields } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-fields.util';
|
||||||
|
|
||||||
interface RunCommandOptions {
|
interface RunCommandOptions {
|
||||||
workspaceId?: string;
|
workspaceId?: string;
|
||||||
@ -123,11 +123,8 @@ export class AddStandardIdCommand extends CommandRunner {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const computedStandardObjectMetadata = computeStandardObject(
|
const computedStandardFieldMetadataCollection = computeStandardFields(
|
||||||
standardObjectMetadata ?? {
|
standardFieldMetadataCollection,
|
||||||
...originalObjectMetadata,
|
|
||||||
fields: standardFieldMetadataCollection,
|
|
||||||
},
|
|
||||||
originalObjectMetadata,
|
originalObjectMetadata,
|
||||||
customObjectMetadataCollection,
|
customObjectMetadataCollection,
|
||||||
);
|
);
|
||||||
@ -135,13 +132,13 @@ export class AddStandardIdCommand extends CommandRunner {
|
|||||||
if (!originalObjectMetadata.isCustom) {
|
if (!originalObjectMetadata.isCustom) {
|
||||||
updateObjectMetadataCollection.push({
|
updateObjectMetadataCollection.push({
|
||||||
id: originalObjectMetadata.id,
|
id: originalObjectMetadata.id,
|
||||||
standardId: computedStandardObjectMetadata.standardId,
|
standardId: originalObjectMetadata.standardId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const fieldMetadata of originalObjectMetadata.fields) {
|
for (const fieldMetadata of originalObjectMetadata.fields) {
|
||||||
const standardFieldMetadata =
|
const standardFieldMetadata =
|
||||||
computedStandardObjectMetadata.fields.find(
|
computedStandardFieldMetadataCollection.find(
|
||||||
(field) => field.name === fieldMetadata.name && !field.isCustom,
|
(field) => field.name === fieldMetadata.name && !field.isCustom,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ describe('WorkspaceFieldComparator', () => {
|
|||||||
],
|
],
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const result = comparator.compare(original, standard);
|
const result = comparator.compare('', original.fields, standard.fields);
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ describe('WorkspaceFieldComparator', () => {
|
|||||||
],
|
],
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const result = comparator.compare(original, standard);
|
const result = comparator.compare('', original.fields, standard.fields);
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
{
|
{
|
||||||
@ -88,7 +88,7 @@ describe('WorkspaceFieldComparator', () => {
|
|||||||
} as any;
|
} as any;
|
||||||
const standard = { fields: [] } as any;
|
const standard = { fields: [] } as any;
|
||||||
|
|
||||||
const result = comparator.compare(original, standard);
|
const result = comparator.compare('', original.fields, standard.fields);
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
{
|
{
|
||||||
@ -108,7 +108,7 @@ describe('WorkspaceFieldComparator', () => {
|
|||||||
fields: [createMockFieldMetadata({ standardId: '1' })],
|
fields: [createMockFieldMetadata({ standardId: '1' })],
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const result = comparator.compare(original, standard);
|
const result = comparator.compare('', original.fields, standard.fields);
|
||||||
|
|
||||||
expect(result).toHaveLength(0);
|
expect(result).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
@ -7,9 +7,7 @@ import {
|
|||||||
FieldComparatorResult,
|
FieldComparatorResult,
|
||||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface';
|
||||||
import { ComputedPartialFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
import { ComputedPartialFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||||
import { ComputedPartialWorkspaceEntity } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-object-metadata.interface';
|
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
|
||||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||||
import {
|
import {
|
||||||
FieldMetadataEntity,
|
FieldMetadataEntity,
|
||||||
@ -35,8 +33,9 @@ export class WorkspaceFieldComparator {
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
public compare(
|
public compare(
|
||||||
originalObjectMetadata: ObjectMetadataEntity,
|
originalObjectMetadataId: string,
|
||||||
standardObjectMetadata: ComputedPartialWorkspaceEntity,
|
originalFieldMetadataCollection: FieldMetadataEntity[],
|
||||||
|
standardFieldMetadataCollection: ComputedPartialFieldMetadata[],
|
||||||
): FieldComparatorResult[] {
|
): FieldComparatorResult[] {
|
||||||
const result: FieldComparatorResult[] = [];
|
const result: FieldComparatorResult[] = [];
|
||||||
const fieldPropertiesToUpdateMap: Record<
|
const fieldPropertiesToUpdateMap: Record<
|
||||||
@ -46,7 +45,7 @@ export class WorkspaceFieldComparator {
|
|||||||
|
|
||||||
// Double security to only compare non-custom fields
|
// Double security to only compare non-custom fields
|
||||||
const filteredOriginalFieldCollection =
|
const filteredOriginalFieldCollection =
|
||||||
originalObjectMetadata.fields.filter((field) => !field.isCustom);
|
originalFieldMetadataCollection.filter((field) => !field.isCustom);
|
||||||
const originalFieldMetadataMap = transformMetadataForComparison(
|
const originalFieldMetadataMap = transformMetadataForComparison(
|
||||||
filteredOriginalFieldCollection,
|
filteredOriginalFieldCollection,
|
||||||
{
|
{
|
||||||
@ -73,7 +72,7 @@ export class WorkspaceFieldComparator {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const standardFieldMetadataMap = transformMetadataForComparison(
|
const standardFieldMetadataMap = transformMetadataForComparison(
|
||||||
standardObjectMetadata.fields,
|
standardFieldMetadataCollection,
|
||||||
{
|
{
|
||||||
shouldIgnoreProperty: (property, originalMetadata) => {
|
shouldIgnoreProperty: (property, originalMetadata) => {
|
||||||
if (commonFieldPropertiesToIgnore.includes(property)) {
|
if (commonFieldPropertiesToIgnore.includes(property)) {
|
||||||
@ -117,9 +116,9 @@ export class WorkspaceFieldComparator {
|
|||||||
};
|
};
|
||||||
// Object shouldn't have thousands of fields, so we can use find here
|
// Object shouldn't have thousands of fields, so we can use find here
|
||||||
const standardFieldMetadata =
|
const standardFieldMetadata =
|
||||||
standardObjectMetadata.fields.find(findField);
|
standardFieldMetadataCollection.find(findField);
|
||||||
const originalFieldMetadata =
|
const originalFieldMetadata =
|
||||||
originalObjectMetadata.fields.find(findField);
|
originalFieldMetadataCollection.find(findField);
|
||||||
|
|
||||||
switch (difference.type) {
|
switch (difference.type) {
|
||||||
case 'CREATE': {
|
case 'CREATE': {
|
||||||
@ -133,7 +132,7 @@ export class WorkspaceFieldComparator {
|
|||||||
action: ComparatorAction.CREATE,
|
action: ComparatorAction.CREATE,
|
||||||
object: {
|
object: {
|
||||||
...standardFieldMetadata,
|
...standardFieldMetadata,
|
||||||
objectMetadataId: originalObjectMetadata.id,
|
objectMetadataId: originalObjectMetadataId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -27,8 +27,8 @@ export class WorkspaceObjectComparator {
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
public compare(
|
public compare(
|
||||||
originalObjectMetadata: ObjectMetadataEntity | undefined,
|
originalObjectMetadata: Omit<ObjectMetadataEntity, 'fields'> | undefined,
|
||||||
standardObjectMetadata: ComputedPartialWorkspaceEntity,
|
standardObjectMetadata: Omit<ComputedPartialWorkspaceEntity, 'fields'>,
|
||||||
): ObjectComparatorResult {
|
): ObjectComparatorResult {
|
||||||
// If the object doesn't exist in the original metadata, we need to create it
|
// If the object doesn't exist in the original metadata, we need to create it
|
||||||
if (!originalObjectMetadata) {
|
if (!originalObjectMetadata) {
|
||||||
|
@ -24,10 +24,53 @@ export class StandardFieldFactory {
|
|||||||
target: typeof BaseWorkspaceEntity,
|
target: typeof BaseWorkspaceEntity,
|
||||||
context: WorkspaceSyncContext,
|
context: WorkspaceSyncContext,
|
||||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||||
): Array<PartialFieldMetadata | PartialComputedFieldMetadata> {
|
): (PartialFieldMetadata | PartialComputedFieldMetadata)[];
|
||||||
|
|
||||||
|
create(
|
||||||
|
targets: (typeof BaseWorkspaceEntity)[],
|
||||||
|
context: WorkspaceSyncContext,
|
||||||
|
workspaceFeatureFlagsMap: FeatureFlagMap, // Map of standardId to field metadata
|
||||||
|
): Map<string, (PartialFieldMetadata | PartialComputedFieldMetadata)[]>;
|
||||||
|
|
||||||
|
create(
|
||||||
|
targetOrTargets:
|
||||||
|
| typeof BaseWorkspaceEntity
|
||||||
|
| (typeof BaseWorkspaceEntity)[],
|
||||||
|
context: WorkspaceSyncContext,
|
||||||
|
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||||
|
):
|
||||||
|
| (PartialFieldMetadata | PartialComputedFieldMetadata)[]
|
||||||
|
| Map<string, (PartialFieldMetadata | PartialComputedFieldMetadata)[]> {
|
||||||
|
if (Array.isArray(targetOrTargets)) {
|
||||||
|
return targetOrTargets.reduce((acc, target) => {
|
||||||
|
const workspaceEntityMetadataArgs =
|
||||||
|
metadataArgsStorage.filterEntities(target);
|
||||||
|
|
||||||
|
if (!workspaceEntityMetadataArgs) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isGatedAndNotEnabled(
|
||||||
|
workspaceEntityMetadataArgs.gate,
|
||||||
|
workspaceFeatureFlagsMap,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.set(
|
||||||
|
workspaceEntityMetadataArgs.standardId,
|
||||||
|
this.create(target, context, workspaceFeatureFlagsMap),
|
||||||
|
);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, new Map<string, (PartialFieldMetadata | PartialComputedFieldMetadata)[]>());
|
||||||
|
}
|
||||||
|
|
||||||
const workspaceEntityMetadataArgs =
|
const workspaceEntityMetadataArgs =
|
||||||
metadataArgsStorage.filterEntities(target);
|
metadataArgsStorage.filterEntities(targetOrTargets);
|
||||||
const metadataCollections = this.collectMetadata(target);
|
const metadataCollections = this.collectMetadata(targetOrTargets);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...this.processMetadata(
|
...this.processMetadata(
|
||||||
|
@ -8,17 +8,13 @@ import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-syn
|
|||||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||||
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||||
|
|
||||||
import { StandardFieldFactory } from './standard-field.factory';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StandardObjectFactory {
|
export class StandardObjectFactory {
|
||||||
constructor(private readonly standardFieldFactory: StandardFieldFactory) {}
|
|
||||||
|
|
||||||
create(
|
create(
|
||||||
standardObjectMetadataDefinitions: (typeof BaseWorkspaceEntity)[],
|
standardObjectMetadataDefinitions: (typeof BaseWorkspaceEntity)[],
|
||||||
context: WorkspaceSyncContext,
|
context: WorkspaceSyncContext,
|
||||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||||
): PartialWorkspaceEntity[] {
|
): Omit<PartialWorkspaceEntity, 'fields'>[] {
|
||||||
return standardObjectMetadataDefinitions
|
return standardObjectMetadataDefinitions
|
||||||
.map((metadata) =>
|
.map((metadata) =>
|
||||||
this.createObjectMetadata(metadata, context, workspaceFeatureFlagsMap),
|
this.createObjectMetadata(metadata, context, workspaceFeatureFlagsMap),
|
||||||
@ -30,7 +26,7 @@ export class StandardObjectFactory {
|
|||||||
target: typeof BaseWorkspaceEntity,
|
target: typeof BaseWorkspaceEntity,
|
||||||
context: WorkspaceSyncContext,
|
context: WorkspaceSyncContext,
|
||||||
workspaceFeatureFlagsMap: FeatureFlagMap,
|
workspaceFeatureFlagsMap: FeatureFlagMap,
|
||||||
): PartialWorkspaceEntity | undefined {
|
): Omit<PartialWorkspaceEntity, 'fields'> | undefined {
|
||||||
const workspaceEntityMetadataArgs =
|
const workspaceEntityMetadataArgs =
|
||||||
metadataArgsStorage.filterEntities(target);
|
metadataArgsStorage.filterEntities(target);
|
||||||
|
|
||||||
@ -49,12 +45,6 @@ export class StandardObjectFactory {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = this.standardFieldFactory.create(
|
|
||||||
target,
|
|
||||||
context,
|
|
||||||
workspaceFeatureFlagsMap,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...workspaceEntityMetadataArgs,
|
...workspaceEntityMetadataArgs,
|
||||||
// TODO: Remove targetTableName when we remove the old metadata
|
// TODO: Remove targetTableName when we remove the old metadata
|
||||||
@ -64,7 +54,6 @@ export class StandardObjectFactory {
|
|||||||
isCustom: false,
|
isCustom: false,
|
||||||
isRemote: false,
|
isRemote: false,
|
||||||
isSystem: workspaceEntityMetadataArgs.isSystem ?? false,
|
isSystem: workspaceEntityMetadataArgs.isSystem ?? false,
|
||||||
fields,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,9 @@ export interface ComparatorDeleteResult<T> {
|
|||||||
|
|
||||||
export type ObjectComparatorResult =
|
export type ObjectComparatorResult =
|
||||||
| ComparatorSkipResult
|
| ComparatorSkipResult
|
||||||
| ComparatorCreateResult<ComputedPartialWorkspaceEntity>
|
| ComparatorCreateResult<Omit<ComputedPartialWorkspaceEntity, 'fields'>>
|
||||||
| ComparatorUpdateResult<
|
| ComparatorUpdateResult<
|
||||||
Partial<ComputedPartialWorkspaceEntity> & { id: string }
|
Partial<Omit<ComputedPartialWorkspaceEntity, 'fields'>> & { id: string }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type FieldComparatorResult =
|
export type FieldComparatorResult =
|
||||||
|
@ -47,9 +47,6 @@ export class WorkspaceMetadataUpdaterService {
|
|||||||
storage.objectMetadataCreateCollection.map((objectMetadata) => ({
|
storage.objectMetadataCreateCollection.map((objectMetadata) => ({
|
||||||
...objectMetadata,
|
...objectMetadata,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
fields: objectMetadata.fields.map((field) =>
|
|
||||||
this.prepareFieldMetadataForCreation(field),
|
|
||||||
),
|
|
||||||
})) as DeepPartial<ObjectMetadataEntity>[],
|
})) as DeepPartial<ObjectMetadataEntity>[],
|
||||||
);
|
);
|
||||||
const identifiers = createdPartialObjectMetadataCollection.map(
|
const identifiers = createdPartialObjectMetadataCollection.map(
|
||||||
|
@ -15,7 +15,9 @@ import { WorkspaceSyncStorage } from 'src/engine/workspace-manager/workspace-syn
|
|||||||
import { WorkspaceMigrationFieldFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field.factory';
|
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 { StandardFieldFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory';
|
||||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
||||||
import { computeStandardObject } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-object.util';
|
import { computeStandardFields } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-standard-fields.util';
|
||||||
|
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||||
|
import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/sync-metadata.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceSyncFieldMetadataService {
|
export class WorkspaceSyncFieldMetadataService {
|
||||||
@ -47,56 +49,25 @@ export class WorkspaceSyncFieldMetadataService {
|
|||||||
},
|
},
|
||||||
relations: ['dataSource', 'fields'],
|
relations: ['dataSource', 'fields'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter out non-custom objects
|
|
||||||
const customObjectMetadataCollection =
|
const customObjectMetadataCollection =
|
||||||
originalObjectMetadataCollection.filter(
|
originalObjectMetadataCollection.filter(
|
||||||
(objectMetadata) => objectMetadata.isCustom,
|
(objectMetadata) => objectMetadata.isCustom,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create standard field metadata collection
|
await this.synchronizeStandardObjectFields(
|
||||||
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
|
||||||
CustomWorkspaceEntity,
|
|
||||||
context,
|
context,
|
||||||
|
originalObjectMetadataCollection,
|
||||||
|
customObjectMetadataCollection,
|
||||||
|
storage,
|
||||||
workspaceFeatureFlagsMap,
|
workspaceFeatureFlagsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Loop over all custom objects from the DB and compare their fields with standard fields
|
await this.synchronizeCustomObjectFields(
|
||||||
for (const customObjectMetadata of customObjectMetadataCollection) {
|
context,
|
||||||
// Also, maybe it's better to refactor a bit and move generation part into a separate module ?
|
customObjectMetadataCollection,
|
||||||
const standardObjectMetadata = computeStandardObject(
|
storage,
|
||||||
{
|
workspaceFeatureFlagsMap,
|
||||||
...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');
|
this.logger.log('Updating workspace metadata');
|
||||||
|
|
||||||
@ -137,4 +108,120 @@ export class WorkspaceSyncFieldMetadataService {
|
|||||||
...deleteFieldWorkspaceMigrations,
|
...deleteFieldWorkspaceMigrations,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be optimized to avoid import of standardObjectFactory here.
|
||||||
|
* We should refactor the logic of the factory, so this one only create the objects and not the fields.
|
||||||
|
* Then standardFieldFactory should be used to create the fields of standard objects.
|
||||||
|
*/
|
||||||
|
private async synchronizeStandardObjectFields(
|
||||||
|
context: WorkspaceSyncContext,
|
||||||
|
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
|
||||||
|
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
||||||
|
originalObjectMetadataCollection,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.log('Comparing standard objects and fields metadata');
|
||||||
|
|
||||||
|
// Loop over all standard objects and compare them with the objects in DB
|
||||||
|
for (const [
|
||||||
|
standardObjectId,
|
||||||
|
standardFieldMetadataCollection,
|
||||||
|
] of standardObjectStandardFieldMetadataMap) {
|
||||||
|
const originalObjectMetadata =
|
||||||
|
originalObjectMetadataMap[standardObjectId];
|
||||||
|
const computedStandardFieldMetadataCollection = computeStandardFields(
|
||||||
|
standardFieldMetadataCollection,
|
||||||
|
originalObjectMetadata,
|
||||||
|
// We need to provide this for generated relations with custom objects
|
||||||
|
customObjectMetadataCollection,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fieldComparatorResults = this.workspaceFieldComparator.compare(
|
||||||
|
originalObjectMetadata.id,
|
||||||
|
originalObjectMetadata.fields,
|
||||||
|
computedStandardFieldMetadataCollection,
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
customObjectStandardFieldMetadataCollection,
|
||||||
|
customObjectMetadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMPARE FIELD METADATA
|
||||||
|
*/
|
||||||
|
const fieldComparatorResults = this.workspaceFieldComparator.compare(
|
||||||
|
customObjectMetadata.id,
|
||||||
|
customObjectMetadata.fields,
|
||||||
|
standardFieldMetadataCollection,
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,9 @@ import { mapObjectMetadataByUniqueIdentifier } from 'src/engine/workspace-manage
|
|||||||
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||||
import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory';
|
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 { 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 { 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 { 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 { 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';
|
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -26,7 +24,6 @@ export class WorkspaceSyncObjectMetadataService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly standardObjectFactory: StandardObjectFactory,
|
private readonly standardObjectFactory: StandardObjectFactory,
|
||||||
private readonly workspaceObjectComparator: WorkspaceObjectComparator,
|
private readonly workspaceObjectComparator: WorkspaceObjectComparator,
|
||||||
private readonly workspaceFieldComparator: WorkspaceFieldComparator,
|
|
||||||
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
|
private readonly workspaceMetadataUpdaterService: WorkspaceMetadataUpdaterService,
|
||||||
private readonly workspaceMigrationObjectFactory: WorkspaceMigrationObjectFactory,
|
private readonly workspaceMigrationObjectFactory: WorkspaceMigrationObjectFactory,
|
||||||
) {}
|
) {}
|
||||||
@ -49,10 +46,6 @@ export class WorkspaceSyncObjectMetadataService {
|
|||||||
},
|
},
|
||||||
relations: ['dataSource', 'fields'],
|
relations: ['dataSource', 'fields'],
|
||||||
});
|
});
|
||||||
const customObjectMetadataCollection =
|
|
||||||
originalObjectMetadataCollection.filter(
|
|
||||||
(objectMetadata) => objectMetadata.isCustom,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create standard object metadata collection
|
// Create standard object metadata collection
|
||||||
const standardObjectMetadataCollection = this.standardObjectFactory.create(
|
const standardObjectMetadataCollection = this.standardObjectFactory.create(
|
||||||
@ -87,11 +80,8 @@ export class WorkspaceSyncObjectMetadataService {
|
|||||||
for (const standardObjectId in standardObjectMetadataMap) {
|
for (const standardObjectId in standardObjectMetadataMap) {
|
||||||
const originalObjectMetadata =
|
const originalObjectMetadata =
|
||||||
originalObjectMetadataMap[standardObjectId];
|
originalObjectMetadataMap[standardObjectId];
|
||||||
const standardObjectMetadata = computeStandardObject(
|
const standardObjectMetadata =
|
||||||
standardObjectMetadataMap[standardObjectId],
|
standardObjectMetadataMap[standardObjectId];
|
||||||
originalObjectMetadata,
|
|
||||||
customObjectMetadataCollection,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* COMPARE OBJECT METADATA
|
* COMPARE OBJECT METADATA
|
||||||
@ -109,35 +99,6 @@ export class WorkspaceSyncObjectMetadataService {
|
|||||||
if (objectComparatorResult.action === ComparatorAction.UPDATE) {
|
if (objectComparatorResult.action === ComparatorAction.UPDATE) {
|
||||||
storage.addUpdateObjectMetadata(objectComparatorResult.object);
|
storage.addUpdateObjectMetadata(objectComparatorResult.object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* COMPARE FIELD METADATA
|
|
||||||
* NOTE: This should be moved to WorkspaceSyncFieldMetadataService for more clarity since
|
|
||||||
* this code only adds field metadata to the storage but it's actually used in the other service.
|
|
||||||
* NOTE2: WorkspaceSyncFieldMetadataService has been added for custom fields sync, it should be refactored to handle
|
|
||||||
* both custom and non-custom fields.
|
|
||||||
*/
|
|
||||||
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');
|
this.logger.log('Updating workspace metadata');
|
||||||
|
@ -28,6 +28,7 @@ import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/stan
|
|||||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||||
|
|
||||||
|
// TODO: Maybe we should automate this with the DiscoverService of Nest.JS
|
||||||
export const standardObjectMetadataDefinitions = [
|
export const standardObjectMetadataDefinitions = [
|
||||||
ActivityTargetWorkspaceEntity,
|
ActivityTargetWorkspaceEntity,
|
||||||
ActivityWorkspaceEntity,
|
ActivityWorkspaceEntity,
|
||||||
|
@ -8,9 +8,13 @@ import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/
|
|||||||
|
|
||||||
export class WorkspaceSyncStorage {
|
export class WorkspaceSyncStorage {
|
||||||
// Object metadata
|
// Object metadata
|
||||||
private readonly _objectMetadataCreateCollection: ComputedPartialWorkspaceEntity[] =
|
private readonly _objectMetadataCreateCollection: Omit<
|
||||||
[];
|
ComputedPartialWorkspaceEntity,
|
||||||
private readonly _objectMetadataUpdateCollection: (Partial<ComputedPartialWorkspaceEntity> & {
|
'fields'
|
||||||
|
>[] = [];
|
||||||
|
private readonly _objectMetadataUpdateCollection: (Partial<
|
||||||
|
Omit<ComputedPartialWorkspaceEntity, 'fields'>
|
||||||
|
> & {
|
||||||
id: string;
|
id: string;
|
||||||
})[] = [];
|
})[] = [];
|
||||||
private readonly _objectMetadataDeleteCollection: ObjectMetadataEntity[] = [];
|
private readonly _objectMetadataDeleteCollection: ObjectMetadataEntity[] = [];
|
||||||
@ -88,7 +92,9 @@ export class WorkspaceSyncStorage {
|
|||||||
return this._indexMetadataDeleteCollection;
|
return this._indexMetadataDeleteCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
addCreateObjectMetadata(object: ComputedPartialWorkspaceEntity) {
|
addCreateObjectMetadata(
|
||||||
|
object: Omit<ComputedPartialWorkspaceEntity, 'fields'>,
|
||||||
|
) {
|
||||||
this._objectMetadataCreateCollection.push(object);
|
this._objectMetadataCreateCollection.push(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
ComputedPartialWorkspaceEntity,
|
ComputedPartialFieldMetadata,
|
||||||
PartialWorkspaceEntity,
|
PartialComputedFieldMetadata,
|
||||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-object-metadata.interface';
|
PartialFieldMetadata,
|
||||||
import { ComputedPartialFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
} from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
@ -11,16 +11,18 @@ import {
|
|||||||
createRelationDeterministicUuid,
|
createRelationDeterministicUuid,
|
||||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
||||||
|
|
||||||
export const computeStandardObject = (
|
export const computeStandardFields = (
|
||||||
standardObjectMetadata: Omit<PartialWorkspaceEntity, 'standardId'> & {
|
standardFieldMetadataCollection: (
|
||||||
standardId: string | null;
|
| PartialFieldMetadata
|
||||||
},
|
| PartialComputedFieldMetadata
|
||||||
|
)[],
|
||||||
originalObjectMetadata: ObjectMetadataEntity,
|
originalObjectMetadata: ObjectMetadataEntity,
|
||||||
customObjectMetadataCollection: ObjectMetadataEntity[] = [],
|
customObjectMetadataCollection: ObjectMetadataEntity[] = [],
|
||||||
): ComputedPartialWorkspaceEntity => {
|
): ComputedPartialFieldMetadata[] => {
|
||||||
const fields: ComputedPartialFieldMetadata[] = [];
|
const fields: ComputedPartialFieldMetadata[] = [];
|
||||||
|
|
||||||
for (const partialFieldMetadata of standardObjectMetadata.fields) {
|
for (const partialFieldMetadata of standardFieldMetadataCollection) {
|
||||||
|
// Relation from standard object to custom object
|
||||||
if ('argsFactory' in partialFieldMetadata) {
|
if ('argsFactory' in partialFieldMetadata) {
|
||||||
// Compute standard fields of custom object
|
// Compute standard fields of custom object
|
||||||
for (const customObjectMetadata of customObjectMetadataCollection) {
|
for (const customObjectMetadata of customObjectMetadataCollection) {
|
||||||
@ -63,6 +65,7 @@ export const computeStandardObject = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Relation from standard object to standard object
|
||||||
const labelText =
|
const labelText =
|
||||||
typeof partialFieldMetadata.label === 'function'
|
typeof partialFieldMetadata.label === 'function'
|
||||||
? partialFieldMetadata.label(originalObjectMetadata)
|
? partialFieldMetadata.label(originalObjectMetadata)
|
||||||
@ -80,8 +83,5 @@ export const computeStandardObject = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return fields;
|
||||||
...standardObjectMetadata,
|
|
||||||
fields,
|
|
||||||
};
|
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user