twenty icon indicating copy to clipboard operation
twenty copied to clipboard

migration for Custom Objects

Open guillim opened this issue 3 weeks ago • 3 comments

Before working on migration of timeline activites, we need to work on this project first.

There are two main feature to develop

  1. updateOne of a Relation that involves a CustomObject, because the nameSingular needs to be updated in the fieldMetadata
  • nameSingular and namePlural must be provided since they are necessary for morph name computation
  • label sync should be false
  1. updateOne on a morph relation: inside a morph relation, we need to be able to add or remove targets objects. using the updateOne from fieldMetadata sounds a great idea but we want to have a dedicated fieldMetadataupdateMorphInput that will take the morphId instead of the id

We will handle 2. in a second PR for better clarity

Interesting files to look at:

  • packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts

  • UPDATE => packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts ( also update relation indexes ) needs v2 refactor to handle field relation name update

  • CREATE => packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts ( handle morph instead of previous classic relation )

  • DELETE => DONE

guillim avatar Nov 10 '25 17:11 guillim

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:15583

This environment will automatically shut down when the PR is closed or after 5 hours.

github-actions[bot] avatar Nov 10 '25 17:11 github-actions[bot]

Closing for now, let's re-open when ready :)

charlesBochet avatar Nov 11 '25 15:11 charlesBochet

Greptile Overview

Greptile Summary

This PR implements proper handling of field metadata name updates when custom objects are renamed, particularly for morph relations. The changes ensure that when an object's nameSingular or namePlural is updated, all related morph relation field names, join column names, and indexes are correctly recomputed and updated.

Key Changes

  • Created new utility compute-flat-field-to-update-and-related-flat-field-to-update.util.ts to handle propagation of field updates to sibling morph relations and target relation fields
  • Enhanced rename-related-morph-field-on-object-names-update.util.ts to properly handle index updates alongside field name changes
  • Modified update-field-action-handler.service.ts to handle join column renames during workspace migrations
  • Added constants defining which properties are editable on sibling fields during morph relation updates
  • Significantly expanded test coverage for both field updates and object renames with morph relations

Technical Implementation

The PR introduces a sophisticated system for cascading updates:

  1. When a morph relation field is updated, changes propagate to all sibling morph fields and their target relation fields
  2. When an object is renamed, all morph fields pointing to it are recomputed with new names
  3. Join column names are automatically updated to match new field names
  4. Related indexes are found and recomputed to reflect name changes

The implementation properly distinguishes between standard and custom fields, applying different property update rules for each.

Confidence Score: 4/5

  • This PR is safe to merge with comprehensive test coverage, though the complexity of the changes warrants careful monitoring in production
  • Score reflects well-structured implementation with extensive test coverage (245+ lines of new tests), but the high complexity of cascading updates across morph relations, indexes, and views introduces some risk. The refactoring extracts logic into reusable utilities which improves maintainability. Previous style comments have been addressed.
  • Pay close attention to rename-related-morph-field-on-object-names-update.util.ts as it handles critical index recomputation logic

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts 4/5 Enhanced to handle index updates when morph field names change during object rename, now properly recomputes and updates related indexes
packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-to-update-and-related-flat-field-to-update.util.ts 5/5 New utility that computes field updates for morph relations and standard relations, propagating changes to sibling fields with appropriate property filtering
packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata.util.ts 5/5 Refactored to use new compute-flat-field-to-update util, handling field updates with side effects for views, indexes, and related fields
packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/update-field-action-handler.service.ts 4/5 Enhanced to handle join column name updates for morph/relation fields during workspace migrations, properly renaming database columns

Sequence Diagram

sequenceDiagram
    participant Client
    participant UpdateFieldResolver
    participant fromUpdateFieldInput
    participant computeFlatField
    participant sanitizeInput
    participant handleSideEffect
    participant UpdateFieldHandler
    participant Database

    alt Object Name Update
        Client->>UpdateFieldResolver: Update Object (nameSingular/namePlural)
        UpdateFieldResolver->>handleSideEffect: handleFlatObjectMetadataUpdateSideEffect
        handleSideEffect->>handleSideEffect: Check if nameSingular or namePlural changed
        handleSideEffect->>renameRelatedMorph: renameRelatedMorphFieldOnObjectNamesUpdate
        renameRelatedMorph->>renameRelatedMorph: Get all morph relations targeting this object
        renameRelatedMorph->>renameRelatedMorph: For each morph field, compute new name
        renameRelatedMorph->>renameRelatedMorph: Recompute join column names
        renameRelatedMorph->>renameRelatedMorph: Find and update related indexes
        renameRelatedMorph-->>handleSideEffect: morphFlatFieldMetadatasToUpdate, morphRelatedFlatIndexesToUpdate
        handleSideEffect-->>UpdateFieldResolver: Updated fields and indexes
    end

    alt Field Metadata Update
        Client->>UpdateFieldResolver: Update Field (label, description, etc.)
        UpdateFieldResolver->>fromUpdateFieldInput: fromUpdateFieldInputToFlatFieldMetadata
        fromUpdateFieldInput->>sanitizeInput: sanitizeRawUpdateFieldInput
        sanitizeInput->>sanitizeInput: Extract editable properties
        sanitizeInput->>sanitizeInput: Add UUIDs to options if present
        sanitizeInput->>sanitizeInput: Handle standard overrides
        sanitizeInput-->>fromUpdateFieldInput: sanitized input
        
        fromUpdateFieldInput->>computeFlatField: computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate
        
        alt RELATION field
            computeFlatField->>computeFlatField: Find target relation field
            computeFlatField->>computeFlatField: Merge updates using RELATION_EDITABLE_PROPERTIES
            computeFlatField-->>fromUpdateFieldInput: field + 1 related field
        else MORPH_RELATION field
            computeFlatField->>computeFlatField: Find all sibling morph fields
            computeFlatField->>computeFlatField: Find all target relation fields
            computeFlatField->>computeFlatField: Update morph siblings using MORPH_RELATION_EDITABLE_PROPERTIES
            computeFlatField->>computeFlatField: Update relation targets using RELATION_EDITABLE_PROPERTIES
            computeFlatField-->>fromUpdateFieldInput: field + multiple related fields
        else Other field types
            computeFlatField-->>fromUpdateFieldInput: field only
        end
        
        fromUpdateFieldInput->>handleSideEffect: For each field, handleFlatFieldMetadataUpdateSideEffect
        handleSideEffect->>handleSideEffect: Update indexes if name changed
        handleSideEffect->>handleSideEffect: Update view filters/groups/fields
        handleSideEffect-->>fromUpdateFieldInput: Side effects (indexes, views, etc.)
        
        fromUpdateFieldInput-->>UpdateFieldResolver: All fields to update + side effects
        UpdateFieldResolver->>UpdateFieldHandler: Execute migration
        UpdateFieldHandler->>UpdateFieldHandler: optimisticallyApplyActionOnAllFlatEntityMaps
        UpdateFieldHandler->>Database: Update metadata tables
        UpdateFieldHandler->>Database: Rename columns if joinColumnName changed
        UpdateFieldHandler->>Database: Update indexes, views, etc.
        UpdateFieldHandler-->>Client: Success
    end

greptile-apps[bot] avatar Nov 13 '25 18:11 greptile-apps[bot]

I faced a bug while trying to create a morph. I believe it's when you select the object itself as start and end of the morph, looks not related to this PR ==> looks actually legit on BE side (Morph relation creation payloads must not target source object metadata), we should just surface it better on FE image

charlesBochet avatar Nov 18 '25 18:11 charlesBochet

Error when trying to rename an object image

seems related to this one!

charlesBochet avatar Nov 18 '25 18:11 charlesBochet

@greptileai trigger

prastoin avatar Nov 21 '25 15:11 prastoin

@charlesBochet it would be great to have a ~second~ third eye on this PR since @prastoin helped me a lot in the work I started on this topic.

guillim avatar Nov 21 '25 17:11 guillim

cc: @Bonapara @FelixMalfait This PR allows custom relation field name update. We could update the frontend to allow doing so in a separate PR. Would this make sense for the product in advanced mode ? We could also allow label and name synchronization for relations fields too

ps: not join column name yet as unless i' mistaken @guillim is about to implement it ?

prastoin avatar Nov 21 '25 17:11 prastoin

Great! Yes renaming relations is a frequent request and would be great to expose in the frontend (not in Advanced mode imo, it should be seamless ideally). @guillim could you please create a follow up issue if you are handling this to make sure we don't lose track of it? Thanks!

FelixMalfait avatar Nov 22 '25 08:11 FelixMalfait

Great! Yes renaming relations is a frequent request and would be great to expose in the frontend (not in Advanced mode imo, it should be seamless ideally). @guillim could you please create a follow up issue if you are handling this to make sure we don't lose track of it? Thanks!

I will yes. I will include this "FE relation name edition" in the main issue about morph. Even though it is not directly related to morph, I will keep track of it and we will for sure keep this in the roadmap. Consider it done

guillim avatar Nov 22 '25 20:11 guillim

Congrats! Reviewed and tested! Let's merge :)

charlesBochet avatar Nov 23 '25 20:11 charlesBochet