Skip to content

Conversation

@magrinj
Copy link
Contributor

@magrinj magrinj commented Feb 14, 2025

Fix twentyhq/core-team-issues#330 (comment) and twentyhq/core-team-issues#327 (comment)

What this PR does when isNewRelationEnabled is set to true:

  • Drop the creation of the foreign key as a FieldMetadata
  • Stop creating RelationMetadata
  • Properly fill FieldMetadata of type RELATION during the sync command
  • Use new relation settings in TwentyORM
  • Properly create FieldMetadata relations when we create a new object
  • Handle database:reset with new relations

@magrinj magrinj force-pushed the feat/new-relation-drop-foreign-key branch 3 times, most recently from 45c4850 to ed349c4 Compare February 25, 2025 16:42
@magrinj magrinj force-pushed the feat/new-relation-drop-foreign-key branch from 146ae15 to 6f38f6b Compare February 27, 2025 17:04
@magrinj magrinj changed the title Feat/new relation drop foreign key feat: new relation sync-metadata, twenty-orm, create/update Mar 12, 2025
@magrinj magrinj marked this pull request as ready for review March 12, 2025 10:51
@magrinj magrinj requested review from Weiko and charlesBochet March 12, 2025 10:51
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR introduces a significant architectural change in how relations are handled in the Twenty system, controlled by the isNewRelationEnabled feature flag.

Key changes:

  • Drops creation of foreign key fields as FieldMetadata and stops creating RelationMetadata entities
  • Replaces RelationMetadataType with RelationType across all workspace entities
  • Introduces WorkspaceSyncFieldMetadataRelationService for handling field metadata relations
  • Adds feature flag checks in query resolvers and formatters to support both old and new relation systems
  • Implements new utilities like determineSchemaRelationDetails and converRelationTypeToTypeORMRelationType for relation handling

The changes appear well-structured with proper feature flag controls for backward compatibility, though careful testing of database migrations and existing relation data is recommended.

123 file(s) reviewed, 28 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +182 to +186
// Temporary fix as we don't have relationId in the new relation
relationId: createDeterministicUuid([
relation.sourceFieldMetadata.id,
relation.targetFieldMetadata.id,
]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using field IDs for relation UUID could cause issues if fields are recreated with same configuration but different IDs. Consider using more stable identifiers.

relation.sourceFieldMetadata.id,
relation.targetFieldMetadata.id,
]),
direction: relation.type as unknown as RelationDefinitionType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Unsafe type casting from relationType to RelationDefinitionType. Should validate the values match before casting.

Comment on lines +179 to 183
const isNewRelationEnabled =
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IsNewRelationEnabled,
objectMetadataInput.workspaceId,
createdObjectMetadata,
{
primaryKeyFieldMetadataSettings:
objectMetadataInput.primaryKeyFieldMetadataSettings,
primaryKeyColumnType: objectMetadataInput.primaryKeyColumnType,
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Feature flag check should be moved before table creation to ensure consistent state if creation fails

Comment on lines 302 to 310
if (input.update.isActive !== undefined) {
await this.objectMetadataRelationService.updateObjectRelationshipsActivationStatus(
input.id,
input.update.isActive,
);
// For new relation system, the active status is stitched to the field metadata
if (!isNewRelationEnabled) {
await this.objectMetadataRelationService.updateObjectRelationshipsActivationStatus(
input.id,
input.update.isActive,
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider moving isActive handling to a separate method for better maintainability and reusability

Comment on lines +119 to +163
public async updateRelationMigrations(
currentObjectMetadata: ObjectMetadataEntity,
alteredObjectMetadata: ObjectMetadataEntity,
relationMetadataCollection: {
targetObjectMetadata: ObjectMetadataEntity;
targetFieldMetadata: FieldMetadataEntity;
sourceFieldMetadata: FieldMetadataEntity;
}[],
workspaceId: string,
) {
for (const { targetObjectMetadata } of relationMetadataCollection) {
const targetTableName = computeObjectTargetTable(targetObjectMetadata);
const columnName = `${currentObjectMetadata.nameSingular}Id`;

await this.workspaceMigrationService.createCustomMigration(
generateMigrationName(
`rename-${currentObjectMetadata.nameSingular}-to-${alteredObjectMetadata.nameSingular}-in-${targetObjectMetadata.nameSingular}`,
),
workspaceId,
[
{
name: targetTableName,
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.ALTER,
currentColumnDefinition: {
columnName,
columnType: 'uuid',
isNullable: true,
defaultValue: null,
},
alteredColumnDefinition: {
columnName: `${alteredObjectMetadata.nameSingular}Id`,
columnType: 'uuid',
isNullable: true,
defaultValue: null,
},
},
],
},
],
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Duplicates logic from createUpdateForeignKeysMigrations method - consider consolidating these methods to avoid maintenance issues

Comment on lines +60 to +62
Partial<ComputedPartialFieldMetadata<FieldMetadataType.RELATION>> & {
id: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The id field is required in the create result but relations might not have an id at creation time. Consider making id optional for CREATE operations

Comment on lines +33 to +44
private readonly _fieldRelationMetadataCreateCollection: (Partial<
ComputedPartialFieldMetadata<FieldMetadataType.RELATION>
> & {
id: string;
})[] = [];
private readonly _fieldRelationMetadataUpdateCollection: (Partial<
ComputedPartialFieldMetadata<FieldMetadataType.RELATION>
> & {
id: string;
})[] = [];
private readonly _fieldRelationMetadataDeleteCollection: FieldMetadataEntity<FieldMetadataType.RELATION>[] =
[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The field relation metadata collections require an id on creation, which differs from the regular field metadata collection that doesn't require an id. This inconsistency could lead to confusion and potential bugs.

Comment on lines +28 to +29
const isNewRelationEnabled =
context.featureFlags[FeatureFlagKey.IsNewRelationEnabled];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Check that context.featureFlags exists before accessing IsNewRelationEnabled to prevent runtime errors

Suggested change
const isNewRelationEnabled =
context.featureFlags[FeatureFlagKey.IsNewRelationEnabled];
const isNewRelationEnabled =
context.featureFlags?.[FeatureFlagKey.IsNewRelationEnabled] ?? false;

Comment on lines 146 to 149
standardId: FAVORITE_STANDARD_FIELD_IDS.workflowVersion,
type: RelationMetadataType.MANY_TO_ONE,
type: RelationType.MANY_TO_ONE,
label: msg`Workflow`,
description: msg`Favorite workflow version`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Label 'Workflow' is reused for three different relations (workflow, workflowVersion, workflowRun) which could cause confusion in the UI

Comment on lines +155 to +158
const isNewRelationEnabled = await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IsNewRelationEnabled,
objectMetadataItemWithFieldsMaps.workspaceId,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider caching feature flag result at class level to avoid repeated DB calls for the same workspace

@charlesBochet charlesBochet self-assigned this Mar 26, 2025
relationType: RelationType;
onDelete?: RelationOnDeleteAction;
joinColumnName?: string;
joinColumnName?: string | null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we avoid | null here and just rely on undefined?

@charlesBochet charlesBochet changed the base branch from main to 0.50.1 April 22, 2025 05:31
@charlesBochet charlesBochet changed the base branch from 0.50.1 to main April 22, 2025 05:31
@charlesBochet charlesBochet merged commit cc29c25 into main Apr 22, 2025
5 checks passed
@charlesBochet charlesBochet deleted the feat/new-relation-drop-foreign-key branch April 22, 2025 17:01
martmull added a commit that referenced this pull request Apr 24, 2025
Follow up from #10217
Adding onDelete action on Many_to_one sides
ehconitin pushed a commit that referenced this pull request Apr 24, 2025
Follow up from #10217
Adding onDelete action on Many_to_one sides
charlesBochet pushed a commit that referenced this pull request Apr 24, 2025
Follow up from #10217
Adding onDelete action on Many_to_one sides
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Relation-Metadata] - Update WorkspaceSyncMetadata in order to work with new relation

4 participants