Compare commits

...

1 Commits

Author SHA1 Message Date
Sonarly Claude Code 76ad4101e4 fix(field-metadata): handle missing relationType in relation converter
https://sonarly.com/issue/35153?type=bug

UpdateField mutation fails with "Cannot return null for non-nullable field Relation.type" when querying relation metadata for fields with null or incomplete settings.

Fix: ## Changes Made

Modified the `fromMorphOrRelationFlatFieldMetadataToRelationDto` converter function to safely handle missing `settings.relationType`:

1. **from-morph-or-relation-flat-field-metadata-to-relation-dto.util.ts**:
   - Added optional chaining to safely access `sourceFlatFieldMetadata.settings?.relationType`
   - Return `null` early if relationType is missing, preventing undefined from being assigned to the non-nullable `type` field
   - Changed from inline object return to explicit null check + return block

2. **resolve-morph-relations-from-flat-field-metadata.util.ts**:
   - Updated to handle potential null return from converter
   - Changed from direct return to storing result in variable, then conditionally wrapping in array
   - Uses `flatMap` pattern: returns `[relationDto]` if valid, empty array `[]` if null

## Why This Fix Works

The GraphQL schema defines `Relation.type` as non-nullable (RelationDTO), but the parent field `relation` on FieldMetadata is nullable. When the converter returns `null`, GraphQL correctly resolves the parent `relation` field as `null` instead of throwing "Cannot return null for non-nullable field".

This matches the existing pattern in the codebase where:
- `resolveRelationFromFlatFieldMetadata` already returns `RelationDTO | null`
- The dataloader already expects `RelationDTO | null` (dataloader.service.ts:157)
- 10+ other files in the codebase use `settings?.relationType` with optional chaining

## Root Cause Addressed

Legacy relation fields created before settings validation was enforced (pre-Oct 2025 refactor, pre-Mar 2026 SDK validation) may have null or incomplete settings. The fix gracefully handles these fields by returning null for the entire relation instead of attempting to construct a RelationDTO with undefined type, which violated the GraphQL schema constraint.
2026-05-06 01:09:04 +00:00
2 changed files with 26 additions and 16 deletions
@@ -15,18 +15,26 @@ export const fromMorphOrRelationFlatFieldMetadataToRelationDto = ({
sourceFlatObjectMetadata,
targetFlatFieldMetadata,
targetFlatObjectMetadata,
}: FromMorphOrRelationFlatFieldMetadataToRelationDtoArgs) => ({
type: sourceFlatFieldMetadata.settings.relationType,
sourceObjectMetadata: fromFlatObjectMetadataToObjectMetadataDto(
sourceFlatObjectMetadata,
),
sourceFieldMetadata: fromFlatFieldMetadataToFieldMetadataDto(
sourceFlatFieldMetadata,
),
targetObjectMetadata: fromFlatObjectMetadataToObjectMetadataDto(
targetFlatObjectMetadata,
),
targetFieldMetadata: fromFlatFieldMetadataToFieldMetadataDto(
targetFlatFieldMetadata,
),
});
}: FromMorphOrRelationFlatFieldMetadataToRelationDtoArgs) => {
const relationType = sourceFlatFieldMetadata.settings?.relationType;
if (!relationType) {
return null;
}
return {
type: relationType,
sourceObjectMetadata: fromFlatObjectMetadataToObjectMetadataDto(
sourceFlatObjectMetadata,
),
sourceFieldMetadata: fromFlatFieldMetadataToFieldMetadataDto(
sourceFlatFieldMetadata,
),
targetObjectMetadata: fromFlatObjectMetadataToObjectMetadataDto(
targetFlatObjectMetadata,
),
targetFieldMetadata: fromFlatFieldMetadataToFieldMetadataDto(
targetFlatFieldMetadata,
),
};
};
@@ -60,7 +60,7 @@ export const resolveMorphRelationsFromFlatFieldMetadata = ({
namePlural: targetFlatObjectMetadata.namePlural,
});
return fromMorphOrRelationFlatFieldMetadataToRelationDto({
const relationDto = fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata: {
...sourceFlatFieldMetadata,
name: morphNameFromMorphFieldMetadataName,
@@ -69,5 +69,7 @@ export const resolveMorphRelationsFromFlatFieldMetadata = ({
targetFlatObjectMetadata,
sourceFlatObjectMetadata,
});
return relationDto ? [relationDto] : [];
});
};