fix: use pickMorphGroupSurvivor in relation loader to match field metadata deduplication (#17988)
## Summary - Fixes "Target field metadata full object not found" error thrown during optimistic effects (e.g., bulk delete) on workspaces with custom objects - The relation loader was using a simple sort-by-ID to pick the representative morph field, while `filterMorphRelationDuplicateFields` uses `pickMorphGroupSurvivor` which prefers active non-system fields. When a custom object's auto-created morph field (`isSystem: true`) happened to have the smallest UUID, the two loaders would disagree — the relation DTO pointed to that system field's ID, but the field metadata loader filtered it out in favor of a standard field, causing the frontend lookup to fail. - Now both code paths use `pickMorphGroupSurvivor` so they always agree on which morph field represents the group. ## Test plan - [ ] Create a custom object on a workspace that already has standard objects with morph relations (e.g., noteTarget, taskTarget) - [ ] Bulk-select and delete records (e.g., People) — should no longer throw "Target field metadata full object not found" Made with [Cursor](https://cursor.com) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Small, localized change to morph-relation selection logic in a dataloader; main risk is altered field choice for edge-case morph groups, but behavior now matches existing deduplication. > > **Overview** > Ensures the relation dataloader picks the representative morph-relation target field using `pickMorphGroupSurvivor` (preferring active non-system fields) instead of the previous sort-by-id approach. > > This aligns `createRelationLoader` with `filterMorphRelationDuplicateFields`, preventing mismatches where relation DTOs could reference a morph field that gets filtered out elsewhere (e.g., triggering “Target field metadata full object not found”). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c3a6d861262cd21c49cb7582767d419e8048980d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { type IndexMetadataInterface } from 'src/engine/metadata-modules/index-m
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
||||
import { filterMorphRelationDuplicateFields } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
|
||||
import { pickMorphGroupSurvivor } from 'src/engine/dataloaders/utils/pick-morph-group-survivor.util';
|
||||
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
|
||||
@@ -188,14 +189,18 @@ export class DataloaderService {
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: targetFlatObjectMetadata,
|
||||
}),
|
||||
].sort((a, b) => (a.id > b.id ? 1 : -1));
|
||||
];
|
||||
|
||||
const survivorMorphField = pickMorphGroupSurvivor(
|
||||
allMorphFlatFieldMetadatas,
|
||||
);
|
||||
|
||||
relationDtos.push(
|
||||
fromMorphOrRelationFlatFieldMetadataToRelationDto({
|
||||
sourceFlatFieldMetadata,
|
||||
sourceFlatObjectMetadata,
|
||||
targetFlatFieldMetadata: {
|
||||
...allMorphFlatFieldMetadatas[0],
|
||||
...survivorMorphField,
|
||||
name: morphNameFromMorphFieldMetadataName,
|
||||
},
|
||||
targetFlatObjectMetadata,
|
||||
|
||||
Reference in New Issue
Block a user