Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 31d4e56d6b UpdateWorkflowVersionStep returns client input without valid field, violating non-nullable GraphQL schema
https://sonarly.com/issue/14051?type=bug

The `updateWorkflowVersionStep` mutation returns the raw client-provided step JSON as the GraphQL response without ensuring all non-nullable fields (specifically `valid`) are populated, causing a GraphQL serialization error.

Fix: After computing `updatedStep` from either the type-change or settings-only path, the code now creates a `normalizedUpdatedStep` that ensures the `valid` field is always populated. If the client-provided step includes `valid`, that value is used; otherwise, it falls back to `existingStep.valid` (the step stored in the database, which always has this field).

The `normalizedUpdatedStep` is used both for persisting to the database (in `updatedSteps`) and as the GraphQL return value, ensuring consistency and preventing the `Cannot return null for non-nullable field WorkflowAction.valid` error.

This follows the exact same normalization pattern previously applied for the `position` field (commit d2746d2778), addressing the same class of bug where the `updateWorkflowVersionStep` mutation returns raw client input without ensuring all non-nullable GraphQL DTO fields are populated.
2026-03-13 03:27:01 +00:00
Sonarly Claude Code 904f17e1e7 Hardcoded join column name derivation breaks for renamed relation fields in nested queries
https://sonarly.com/issue/14017?type=bug

The nested relations processor derives join column names from field names (`${name}Id`) instead of using the authoritative `settings.joinColumnName`, causing TypeORM `EntityPropertyNotFoundError` when these diverge.

Fix: Changed `fieldMetadataTargetRelationColumnName` computation in `process-nested-relations-v2.helper.ts` to use `targetRelation?.settings?.joinColumnName` for ALL relation types (not just MORPH_RELATION), with `${targetRelationName}Id` retained as a fallback.

**Before:** The code only read `settings.joinColumnName` for `MORPH_RELATION` types, and for regular `RELATION` types it derived the column name from the field name (`${targetRelationName}Id`). This assumption breaks when a relation field's `name` diverges from its `joinColumnName` (e.g., after renaming a custom relation field — the `name` changes but the database column name in `settings.joinColumnName` correctly stays the same).

**After:** Uses `targetRelation?.settings?.joinColumnName ?? ${targetRelationName}Id` — the same pattern already used 3 lines above for the source-side relation (`sourceFieldMetadata.settings.joinColumnName ?? ${sourceFieldName}Id`). This ensures the TypeORM select uses the actual database column name registered in the entity schema, not a derived guess from the field name.

The fix also simplifies the code by removing the now-unnecessary MORPH_RELATION type check, since `settings.joinColumnName` is the correct source of truth for both RELATION and MORPH_RELATION types.
2026-03-12 23:37:21 +00:00
@@ -73,9 +73,16 @@ export class WorkflowVersionStepUpdateWorkspaceService {
additionalCreatedSteps: undefined,
};
const normalizedUpdatedStep = {
...updatedStep,
valid: isDefined(updatedStep.valid)
? updatedStep.valid
: existingStep.valid,
};
const updatedSteps = workflowVersion.steps.map((existingStep) => {
if (existingStep.id === step.id) {
return updatedStep;
return normalizedUpdatedStep;
} else {
return existingStep;
}
@@ -93,7 +100,7 @@ export class WorkflowVersionStepUpdateWorkspaceService {
},
);
return updatedStep;
return normalizedUpdatedStep;
}
private async updateWorkflowVersionStepType({