Refactor new relation sync (#11711)
In this PR: - this should fix the sync metadata for new relation system This goes with the recent PR: https://github.com/twentyhq/twenty/pull/11725 What we want: - ONE_TO_MANY relations should have no joinColumn and no onDelete - MANY_TO_ONE should have both
This commit is contained in:
@ -129,8 +129,14 @@ export class MigrateRelationsToFieldMetadataCommand extends ActiveOrSuspendedWor
|
|||||||
...fieldMetadata,
|
...fieldMetadata,
|
||||||
settings: {
|
settings: {
|
||||||
relationType,
|
relationType,
|
||||||
onDelete: relationMetadata.onDeleteAction,
|
onDelete:
|
||||||
joinColumnName: joinColumnFieldMetadata?.name,
|
relationType === RelationType.MANY_TO_ONE
|
||||||
|
? relationMetadata.onDeleteAction
|
||||||
|
: undefined,
|
||||||
|
joinColumnName:
|
||||||
|
relationType === RelationType.MANY_TO_ONE
|
||||||
|
? joinColumnFieldMetadata?.name
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
relationTargetFieldMetadataId,
|
relationTargetFieldMetadataId,
|
||||||
relationTargetObjectMetadataId,
|
relationTargetObjectMetadataId,
|
||||||
|
|||||||
@ -72,6 +72,9 @@ export class AccessTokenService {
|
|||||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
'workspaceMember',
|
'workspaceMember',
|
||||||
|
{
|
||||||
|
shouldFailIfMetadataNotFound: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspaceMember = await workspaceMemberRepository.findOne({
|
const workspaceMember = await workspaceMemberRepository.findOne({
|
||||||
|
|||||||
@ -355,7 +355,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (fieldMetadata.type === FieldMetadataType.RELATION) {
|
if (fieldMetadata.type === FieldMetadataType.RELATION) {
|
||||||
const isManyToManyRelation =
|
const isManyToOneRelation =
|
||||||
(fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>)
|
(fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>)
|
||||||
.settings?.relationType === RelationType.MANY_TO_ONE;
|
.settings?.relationType === RelationType.MANY_TO_ONE;
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.DROP,
|
action: WorkspaceMigrationColumnActionType.DROP,
|
||||||
columnName: isManyToManyRelation
|
columnName: isManyToOneRelation
|
||||||
? `${(fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>).settings?.joinColumnName}`
|
? `${(fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>).settings?.joinColumnName}`
|
||||||
: `${(targetFieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>).settings?.joinColumnName}`,
|
: `${(targetFieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>).settings?.joinColumnName}`,
|
||||||
} satisfies WorkspaceMigrationColumnDrop,
|
} satisfies WorkspaceMigrationColumnDrop,
|
||||||
|
|||||||
@ -100,7 +100,6 @@ export class ObjectMetadataFieldRelationService {
|
|||||||
...sourceFieldMetadata,
|
...sourceFieldMetadata,
|
||||||
settings: {
|
settings: {
|
||||||
relationType: RelationType.ONE_TO_MANY,
|
relationType: RelationType.ONE_TO_MANY,
|
||||||
onDelete: RelationOnDeleteAction.CASCADE,
|
|
||||||
},
|
},
|
||||||
relationTargetObjectMetadataId: targetObjectMetadata.id,
|
relationTargetObjectMetadataId: targetObjectMetadata.id,
|
||||||
relationTargetFieldMetadataId: targetFieldMetadata.id,
|
relationTargetFieldMetadataId: targetFieldMetadata.id,
|
||||||
|
|||||||
@ -55,6 +55,12 @@ export class RelationColumnActionFactory extends ColumnActionAbstractFactory<Fie
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentFieldMetadata.settings.relationType === RelationType.ONE_TO_MANY
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const currentJoinColumnName = currentFieldMetadata.settings.joinColumnName;
|
const currentJoinColumnName = currentFieldMetadata.settings.joinColumnName;
|
||||||
const alteredJoinColumnName = alteredFieldMetadata.settings.joinColumnName;
|
const alteredJoinColumnName = alteredFieldMetadata.settings.joinColumnName;
|
||||||
|
|
||||||
|
|||||||
@ -1,110 +1,232 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { isDefined } from 'class-validator';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
|
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
|
||||||
import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.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 { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
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 { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||||
|
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.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 { getJoinColumn } from 'src/engine/twenty-orm/utils/get-join-column.util';
|
import { getJoinColumn } from 'src/engine/twenty-orm/utils/get-join-column.util';
|
||||||
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
||||||
import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
|
import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-gate-and-not-enabled.util';
|
||||||
import { assert } from 'src/utils/assert';
|
|
||||||
|
|
||||||
interface CustomRelationFactory {
|
|
||||||
object: ObjectMetadataEntity;
|
|
||||||
metadata: typeof BaseWorkspaceEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StandardFieldRelationFactory {
|
export class StandardFieldRelationFactory {
|
||||||
createFieldRelationForCustomObject(
|
computeRelationFieldsForCustomObject(
|
||||||
customObjectFactories: CustomRelationFactory[],
|
customObjectMetadata: ObjectMetadataEntity,
|
||||||
context: WorkspaceSyncContext,
|
|
||||||
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
||||||
): FieldMetadataEntity<FieldMetadataType.RELATION>[] {
|
): FieldMetadataEntity<FieldMetadataType.RELATION>[] {
|
||||||
return customObjectFactories.flatMap((customObjectFactory) =>
|
|
||||||
this.updateFieldRelationMetadata(
|
|
||||||
customObjectFactory,
|
|
||||||
context,
|
|
||||||
originalObjectMetadataMap,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
createFieldRelationForStandardObject(
|
|
||||||
standardObjectMetadataDefinitions: (typeof BaseWorkspaceEntity)[],
|
|
||||||
context: WorkspaceSyncContext,
|
|
||||||
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
|
||||||
): Map<string, FieldMetadataEntity<FieldMetadataType.RELATION>[]> {
|
|
||||||
return standardObjectMetadataDefinitions.reduce(
|
|
||||||
(acc, standardObjectMetadata) => {
|
|
||||||
const workspaceEntityMetadataArgs = metadataArgsStorage.filterEntities(
|
|
||||||
standardObjectMetadata,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!workspaceEntityMetadataArgs) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
isGatedAndNotEnabled(
|
|
||||||
workspaceEntityMetadataArgs.gate,
|
|
||||||
context.featureFlags,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.set(
|
|
||||||
workspaceEntityMetadataArgs.standardId,
|
|
||||||
this.updateFieldRelationMetadata(
|
|
||||||
standardObjectMetadata,
|
|
||||||
context,
|
|
||||||
originalObjectMetadataMap,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
new Map<string, FieldMetadataEntity<FieldMetadataType.RELATION>[]>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateFieldRelationMetadata(
|
|
||||||
workspaceEntityOrCustomRelationFactory:
|
|
||||||
| typeof BaseWorkspaceEntity
|
|
||||||
| CustomRelationFactory,
|
|
||||||
context: WorkspaceSyncContext,
|
|
||||||
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
|
||||||
): FieldMetadataEntity<FieldMetadataType.RELATION>[] {
|
|
||||||
const target =
|
|
||||||
'metadata' in workspaceEntityOrCustomRelationFactory
|
|
||||||
? workspaceEntityOrCustomRelationFactory.metadata
|
|
||||||
: workspaceEntityOrCustomRelationFactory;
|
|
||||||
const workspaceEntity =
|
|
||||||
'metadata' in workspaceEntityOrCustomRelationFactory
|
|
||||||
? metadataArgsStorage.filterExtendedEntities(target)
|
|
||||||
: metadataArgsStorage.filterEntities(target);
|
|
||||||
const workspaceRelationMetadataArgsCollection =
|
const workspaceRelationMetadataArgsCollection =
|
||||||
metadataArgsStorage.filterRelations(target);
|
metadataArgsStorage.filterRelations(CustomWorkspaceEntity);
|
||||||
|
|
||||||
if (!workspaceEntity) {
|
const joinColumnsMetadataArgsCollection =
|
||||||
|
metadataArgsStorage.filterJoinColumns(CustomWorkspaceEntity);
|
||||||
|
|
||||||
|
const objectNameSingular = customObjectMetadata.nameSingular;
|
||||||
|
|
||||||
|
const sourceObjectMetadata = originalObjectMetadataMap[objectNameSingular];
|
||||||
|
|
||||||
|
if (!isDefined(sourceObjectMetadata)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Object metadata decorator not found, can't parse ${target.name}`,
|
`Source object ${objectNameSingular} not found in database while parsing ${objectNameSingular} relations`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return workspaceRelationMetadataArgsCollection.map(
|
||||||
|
(workspaceRelationMetadataArgs) => {
|
||||||
|
const inverseSideTarget =
|
||||||
|
workspaceRelationMetadataArgs.inverseSideTarget();
|
||||||
|
const targetObjectNameSingular = convertClassNameToObjectMetadataName(
|
||||||
|
inverseSideTarget.name,
|
||||||
|
);
|
||||||
|
const targetObjectMetadata =
|
||||||
|
originalObjectMetadataMap[targetObjectNameSingular];
|
||||||
|
|
||||||
|
if (!isDefined(targetObjectMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Target object ${targetObjectNameSingular} not found in database while parsing ${objectNameSingular} relations`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceFieldMetadataName = workspaceRelationMetadataArgs.name;
|
||||||
|
|
||||||
|
const targetFieldMetadataName =
|
||||||
|
workspaceRelationMetadataArgs.inverseSideFieldKey ??
|
||||||
|
objectNameSingular;
|
||||||
|
|
||||||
|
const targetFieldMetadata = targetObjectMetadata.fields.find(
|
||||||
|
(field) => field.name === targetFieldMetadataName,
|
||||||
|
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
|
||||||
|
if (!isDefined(targetFieldMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Target field ${targetFieldMetadataName} not found in object ${targetObjectNameSingular} for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinColumnName =
|
||||||
|
workspaceRelationMetadataArgs.type === RelationType.MANY_TO_ONE
|
||||||
|
? getJoinColumn(
|
||||||
|
joinColumnsMetadataArgsCollection,
|
||||||
|
workspaceRelationMetadataArgs as WorkspaceRelationMetadataArgs,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const sourceFieldMetadata = sourceObjectMetadata.fields.find(
|
||||||
|
(field) => field.name === sourceFieldMetadataName,
|
||||||
|
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
|
||||||
|
if (!isDefined(sourceFieldMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Source field ${sourceFieldMetadataName} not found in object ${sourceFieldMetadata.name} for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...sourceFieldMetadata,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
settings: {
|
||||||
|
relationType: workspaceRelationMetadataArgs.type,
|
||||||
|
onDelete:
|
||||||
|
workspaceRelationMetadataArgs.type === RelationType.MANY_TO_ONE
|
||||||
|
? workspaceRelationMetadataArgs.onDelete
|
||||||
|
: undefined,
|
||||||
|
joinColumnName,
|
||||||
|
},
|
||||||
|
relationTargetObjectMetadataId: targetObjectMetadata.id,
|
||||||
|
relationTargetFieldMetadataId: targetFieldMetadata.id,
|
||||||
|
isNullable: workspaceRelationMetadataArgs.isNullable,
|
||||||
|
} satisfies FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
computeRelationFieldsForStandardObject(
|
||||||
|
standardObjectMetadataWorkspaceEntity: typeof BaseWorkspaceEntity,
|
||||||
|
context: WorkspaceSyncContext,
|
||||||
|
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
||||||
|
): FieldMetadataEntity<FieldMetadataType.RELATION>[] {
|
||||||
|
const workspaceEntityMetadataArgs = metadataArgsStorage.filterEntities(
|
||||||
|
standardObjectMetadataWorkspaceEntity,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!workspaceEntityMetadataArgs) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!workspaceRelationMetadataArgsCollection ||
|
isGatedAndNotEnabled(
|
||||||
isGatedAndNotEnabled(workspaceEntity?.gate, context.featureFlags)
|
workspaceEntityMetadataArgs.gate,
|
||||||
|
context.featureFlags,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspaceRelationMetadataArgsCollection
|
const sourceObjectNameSingular = workspaceEntityMetadataArgs.nameSingular;
|
||||||
|
|
||||||
|
const workspaceStaticRelationMetadataArgsCollection =
|
||||||
|
metadataArgsStorage.filterRelations(
|
||||||
|
standardObjectMetadataWorkspaceEntity,
|
||||||
|
);
|
||||||
|
|
||||||
|
const workspaceDynamicRelationMetadataArgsCollection =
|
||||||
|
metadataArgsStorage.filterDynamicRelations(
|
||||||
|
standardObjectMetadataWorkspaceEntity,
|
||||||
|
);
|
||||||
|
|
||||||
|
const joinColumnsMetadataArgsCollection =
|
||||||
|
metadataArgsStorage.filterJoinColumns(
|
||||||
|
standardObjectMetadataWorkspaceEntity,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isDefined(workspaceStaticRelationMetadataArgsCollection) &&
|
||||||
|
!isDefined(workspaceDynamicRelationMetadataArgsCollection)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`No relations found for object ${sourceObjectNameSingular}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceObjectMetadata =
|
||||||
|
originalObjectMetadataMap[sourceObjectNameSingular];
|
||||||
|
|
||||||
|
if (!isDefined(sourceObjectMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Source object ${sourceObjectNameSingular} not found in database while parsing relations`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationsFromDynamicRelations =
|
||||||
|
workspaceDynamicRelationMetadataArgsCollection
|
||||||
|
.filter(
|
||||||
|
(workspaceDynamicRelationMetadataArgs) =>
|
||||||
|
!isGatedAndNotEnabled(
|
||||||
|
workspaceDynamicRelationMetadataArgs.gate,
|
||||||
|
context.featureFlags,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.flatMap((workspaceDynamicRelationMetadataArgs) => {
|
||||||
|
const customObjectMetadataItems = Object.values(
|
||||||
|
originalObjectMetadataMap,
|
||||||
|
).filter((objectMetadata) => objectMetadata.isCustom);
|
||||||
|
|
||||||
|
// TODO: this is hacky and needs to be simplified
|
||||||
|
return customObjectMetadataItems.flatMap((targetObjectMetadata) => {
|
||||||
|
const relationMetadataArgs =
|
||||||
|
workspaceDynamicRelationMetadataArgs.argsFactory(
|
||||||
|
targetObjectMetadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceFieldMetadata = sourceObjectMetadata?.fields.find(
|
||||||
|
(field) => field.name === relationMetadataArgs.name,
|
||||||
|
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
|
||||||
|
const targetFieldMetadata = targetObjectMetadata?.fields.find(
|
||||||
|
(field) =>
|
||||||
|
field.name ===
|
||||||
|
workspaceDynamicRelationMetadataArgs.inverseSideFieldKey,
|
||||||
|
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
|
||||||
|
if (!isDefined(sourceFieldMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Source field ${relationMetadataArgs.name} not found in object ${sourceObjectNameSingular} for relation ${relationMetadataArgs.name} of type ${relationMetadataArgs}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDefined(targetFieldMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Target field ${workspaceDynamicRelationMetadataArgs.inverseSideFieldKey} not found in object ${targetObjectMetadata.nameSingular} for relation ${relationMetadataArgs.name} of type ${workspaceDynamicRelationMetadataArgs.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...sourceFieldMetadata,
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
settings: {
|
||||||
|
relationType: workspaceDynamicRelationMetadataArgs.type,
|
||||||
|
onDelete:
|
||||||
|
workspaceDynamicRelationMetadataArgs.type ===
|
||||||
|
RelationType.MANY_TO_ONE
|
||||||
|
? workspaceDynamicRelationMetadataArgs.onDelete
|
||||||
|
: undefined,
|
||||||
|
joinColumnName: relationMetadataArgs.joinColumn,
|
||||||
|
},
|
||||||
|
relationTargetObjectMetadataId: targetObjectMetadata.id,
|
||||||
|
relationTargetFieldMetadataId: targetFieldMetadata.id,
|
||||||
|
isNullable: workspaceDynamicRelationMetadataArgs.isNullable,
|
||||||
|
} satisfies FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const staticRelations = workspaceStaticRelationMetadataArgsCollection
|
||||||
.filter(
|
.filter(
|
||||||
(workspaceRelationMetadataArgs) =>
|
(workspaceRelationMetadataArgs) =>
|
||||||
!isGatedAndNotEnabled(
|
!isGatedAndNotEnabled(
|
||||||
@ -113,13 +235,6 @@ export class StandardFieldRelationFactory {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.map((workspaceRelationMetadataArgs) => {
|
.map((workspaceRelationMetadataArgs) => {
|
||||||
// Compute reflect relation metadata
|
|
||||||
const sourceObjectNameSingular =
|
|
||||||
'object' in workspaceEntityOrCustomRelationFactory
|
|
||||||
? workspaceEntityOrCustomRelationFactory.object.nameSingular
|
|
||||||
: convertClassNameToObjectMetadataName(
|
|
||||||
workspaceRelationMetadataArgs.target.name,
|
|
||||||
);
|
|
||||||
const inverseSideTarget =
|
const inverseSideTarget =
|
||||||
workspaceRelationMetadataArgs.inverseSideTarget();
|
workspaceRelationMetadataArgs.inverseSideTarget();
|
||||||
const targetObjectNameSingular = convertClassNameToObjectMetadataName(
|
const targetObjectNameSingular = convertClassNameToObjectMetadataName(
|
||||||
@ -127,60 +242,62 @@ export class StandardFieldRelationFactory {
|
|||||||
);
|
);
|
||||||
const sourceFieldMetadataName = workspaceRelationMetadataArgs.name;
|
const sourceFieldMetadataName = workspaceRelationMetadataArgs.name;
|
||||||
const targetFieldMetadataName =
|
const targetFieldMetadataName =
|
||||||
(workspaceRelationMetadataArgs.inverseSideFieldKey as
|
workspaceRelationMetadataArgs.inverseSideFieldKey ??
|
||||||
| string
|
sourceObjectNameSingular;
|
||||||
| undefined) ?? sourceObjectNameSingular;
|
|
||||||
const sourceObjectMetadata =
|
|
||||||
originalObjectMetadataMap[sourceObjectNameSingular];
|
|
||||||
const joinColumnsMetadataArgsCollection =
|
|
||||||
metadataArgsStorage.filterJoinColumns(target);
|
|
||||||
const joinColumnName = getJoinColumn(
|
|
||||||
joinColumnsMetadataArgsCollection,
|
|
||||||
workspaceRelationMetadataArgs,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert(
|
const joinColumnName =
|
||||||
sourceObjectMetadata,
|
workspaceRelationMetadataArgs.type === RelationType.MANY_TO_ONE
|
||||||
`Source object ${sourceObjectNameSingular} not found in databse for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
? getJoinColumn(
|
||||||
);
|
joinColumnsMetadataArgsCollection,
|
||||||
|
workspaceRelationMetadataArgs as WorkspaceRelationMetadataArgs,
|
||||||
const targetObjectMetadata =
|
)
|
||||||
originalObjectMetadataMap[targetObjectNameSingular];
|
: undefined;
|
||||||
|
|
||||||
assert(
|
|
||||||
targetObjectMetadata,
|
|
||||||
`Target object ${targetObjectNameSingular} not found in databse for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const sourceFieldMetadata = sourceObjectMetadata?.fields.find(
|
const sourceFieldMetadata = sourceObjectMetadata?.fields.find(
|
||||||
(field) => field.name === sourceFieldMetadataName,
|
(field) => field.name === sourceFieldMetadataName,
|
||||||
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
|
||||||
assert(
|
if (!isDefined(sourceFieldMetadata)) {
|
||||||
sourceFieldMetadata,
|
throw new Error(
|
||||||
`Source field ${sourceFieldMetadataName} not found in object ${sourceObjectNameSingular} for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
`Source field ${sourceFieldMetadataName} not found in object ${sourceObjectNameSingular} for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const targetFieldMetadata = targetObjectMetadata?.fields.find(
|
const targetObjectMetadata =
|
||||||
|
originalObjectMetadataMap[targetObjectNameSingular];
|
||||||
|
|
||||||
|
if (!isDefined(targetObjectMetadata)) {
|
||||||
|
throw new Error(
|
||||||
|
`Target object ${targetObjectNameSingular} not found in database for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetFieldMetadata = targetObjectMetadata.fields.find(
|
||||||
(field) => field.name === targetFieldMetadataName,
|
(field) => field.name === targetFieldMetadataName,
|
||||||
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
) as FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
|
|
||||||
assert(
|
if (!isDefined(targetFieldMetadata)) {
|
||||||
targetFieldMetadata,
|
throw new Error(
|
||||||
`Target field ${targetFieldMetadataName} not found in object ${targetObjectNameSingular} for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
`Target field ${targetFieldMetadataName} not found in object ${targetObjectNameSingular} for relation ${workspaceRelationMetadataArgs.name} of type ${workspaceRelationMetadataArgs.type}`,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...sourceFieldMetadata,
|
...sourceFieldMetadata,
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
settings: {
|
settings: {
|
||||||
relationType: workspaceRelationMetadataArgs.type,
|
relationType: workspaceRelationMetadataArgs.type,
|
||||||
onDelete: workspaceRelationMetadataArgs.onDelete,
|
onDelete:
|
||||||
|
workspaceRelationMetadataArgs.type === RelationType.MANY_TO_ONE
|
||||||
|
? workspaceRelationMetadataArgs.onDelete
|
||||||
|
: undefined,
|
||||||
joinColumnName,
|
joinColumnName,
|
||||||
},
|
},
|
||||||
relationTargetObjectMetadataId: targetObjectMetadata.id,
|
relationTargetObjectMetadataId: targetObjectMetadata.id,
|
||||||
relationTargetFieldMetadataId: targetFieldMetadata.id,
|
relationTargetFieldMetadataId: targetFieldMetadata.id,
|
||||||
} satisfies FieldMetadataEntity<FieldMetadataType.RELATION>;
|
} satisfies FieldMetadataEntity<FieldMetadataType.RELATION>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return [...staticRelations, ...relationsFromDynamicRelations];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-syn
|
|||||||
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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-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 { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||||
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
|
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||||
import { WorkspaceMigrationFieldRelationFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory';
|
import { WorkspaceMigrationFieldRelationFactory } from 'src/engine/workspace-manager/workspace-migration-builder/factories/workspace-migration-field-relation.factory';
|
||||||
import {
|
import {
|
||||||
FieldMetadataUpdate,
|
FieldMetadataUpdate,
|
||||||
@ -150,26 +150,30 @@ export class WorkspaceSyncFieldMetadataRelationService {
|
|||||||
originalObjectMetadataMapByName: Record<string, ObjectMetadataEntity>,
|
originalObjectMetadataMapByName: Record<string, ObjectMetadataEntity>,
|
||||||
storage: WorkspaceSyncStorage,
|
storage: WorkspaceSyncStorage,
|
||||||
): Promise<void> {
|
): 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
|
// Create map of original and standard object metadata by standard ids
|
||||||
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
const originalObjectMetadataMap = mapObjectMetadataByUniqueIdentifier(
|
||||||
originalObjectMetadataCollection,
|
originalObjectMetadataCollection,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Loop over all standard objects and compare them with the objects in DB
|
// Loop over all standard objects and compare them with the objects in DB
|
||||||
for (const [
|
for (const standardObjectMetadataDefinition of standardObjectMetadataDefinitions) {
|
||||||
standardObjectId,
|
const workspaceEntityMetadataArgs = metadataArgsStorage.filterEntities(
|
||||||
standardFieldMetadataCollection,
|
standardObjectMetadataDefinition,
|
||||||
] of standardFieldMetadataRelationCollection) {
|
);
|
||||||
|
|
||||||
|
if (!workspaceEntityMetadataArgs) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const standardFieldMetadataRelationCollection =
|
||||||
|
this.standardFieldRelationFactory.computeRelationFieldsForStandardObject(
|
||||||
|
standardObjectMetadataDefinition,
|
||||||
|
context,
|
||||||
|
originalObjectMetadataMapByName,
|
||||||
|
);
|
||||||
|
|
||||||
const originalObjectMetadata =
|
const originalObjectMetadata =
|
||||||
originalObjectMetadataMap[standardObjectId];
|
originalObjectMetadataMap[workspaceEntityMetadataArgs.standardId];
|
||||||
|
|
||||||
const originalFieldRelationMetadataCollection =
|
const originalFieldRelationMetadataCollection =
|
||||||
(originalObjectMetadata?.fields.filter(
|
(originalObjectMetadata?.fields.filter(
|
||||||
@ -183,7 +187,7 @@ export class WorkspaceSyncFieldMetadataRelationService {
|
|||||||
const fieldComparatorResults =
|
const fieldComparatorResults =
|
||||||
this.workspaceFieldRelationComparator.compare(
|
this.workspaceFieldRelationComparator.compare(
|
||||||
originalFieldRelationMetadataCollection,
|
originalFieldRelationMetadataCollection,
|
||||||
standardFieldMetadataCollection,
|
standardFieldMetadataRelationCollection,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.storeComparatorResults(fieldComparatorResults, storage);
|
this.storeComparatorResults(fieldComparatorResults, storage);
|
||||||
@ -196,25 +200,22 @@ export class WorkspaceSyncFieldMetadataRelationService {
|
|||||||
originalObjectMetadataMapByName: Record<string, ObjectMetadataEntity>,
|
originalObjectMetadataMapByName: Record<string, ObjectMetadataEntity>,
|
||||||
storage: WorkspaceSyncStorage,
|
storage: WorkspaceSyncStorage,
|
||||||
): Promise<void> {
|
): 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
|
// Loop over all custom objects from the DB and compare their fields with standard fields
|
||||||
for (const customObjectMetadata of customObjectMetadataCollection) {
|
for (const customObjectMetadata of customObjectMetadataCollection) {
|
||||||
/**
|
const originalFieldRelationMetadataCollection =
|
||||||
* COMPARE FIELD METADATA
|
(customObjectMetadata.fields.filter(
|
||||||
*/
|
(field) => field.type === FieldMetadataType.RELATION,
|
||||||
|
) ?? []) as FieldMetadataEntity<FieldMetadataType.RELATION>[];
|
||||||
|
|
||||||
|
const customFieldMetadataRelationCollection =
|
||||||
|
this.standardFieldRelationFactory.computeRelationFieldsForCustomObject(
|
||||||
|
customObjectMetadata,
|
||||||
|
originalObjectMetadataMapByName,
|
||||||
|
);
|
||||||
|
|
||||||
const fieldComparatorResults =
|
const fieldComparatorResults =
|
||||||
this.workspaceFieldRelationComparator.compare(
|
this.workspaceFieldRelationComparator.compare(
|
||||||
customObjectMetadata.fields as FieldMetadataEntity<FieldMetadataType.RELATION>[],
|
originalFieldRelationMetadataCollection,
|
||||||
customFieldMetadataRelationCollection,
|
customFieldMetadataRelationCollection,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -233,9 +234,6 @@ export class WorkspaceSyncFieldMetadataRelationService {
|
|||||||
await objectMetadataRepository.find({
|
await objectMetadataRepository.find({
|
||||||
where: {
|
where: {
|
||||||
workspaceId: context.workspaceId,
|
workspaceId: context.workspaceId,
|
||||||
fields: {
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
relations: ['dataSource', 'fields'],
|
relations: ['dataSource', 'fields'],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user