Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 9ea9ff569f chore: improve monitoring for fix(workflow): allow duplicating workflow versions
Added `WorkflowTriggerGraphqlApiExceptionFilter` to the `@UseFilters` list on `WorkflowVersionResolver`. This filter catches `WorkflowTriggerException` and re-throws it as a `UserInputError` (a standard GraphQL client error), which is not captured by the Sentry exception handler. Without this filter, all `WorkflowTriggerException` variants (`INVALID_WORKFLOW_VERSION`, `INVALID_INPUT`, `INVALID_WORKFLOW_STATUS`, etc.) thrown by the resolver's methods fire as full Sentry exception events, polluting the error dashboard with expected user-input validation errors. The fix mirrors the pattern already used in `workflow-builder.resolver.ts` and `workflow-trigger.resolver.ts`.
2026-05-08 16:46:15 +00:00
Sonarly Claude Code 2df52785ba fix(workflow): allow duplicating workflow versions without a trigger
https://sonarly.com/issue/36114?type=bug

Users who attempt to duplicate an incomplete workflow (one with steps but no trigger configured) receive an unhandled error. The backend strictly requires a trigger to be present in the source version before duplication, but the frontend offers the duplicate action regardless of whether a trigger exists.

Fix: ## Code fix: `workflow-version.workspace-service.ts`

Removed `assertWorkflowVersionTriggerIsDefined(sourceVersion)` and `assertWorkflowVersionHasSteps(sourceVersion)` from the `duplicateWorkflow` method. These guards are appropriate for *activating* a workflow (handled in `workflow-trigger.workspace-service.ts`) but not for duplicating one — a duplication always produces a new DRAFT, so copying an incomplete draft (missing trigger or steps) into another draft is semantically valid and was already handled correctly by the surrounding code:

- `sourceVersion.trigger` null → `remappedTrigger = undefined` (already guarded with `isDefined(newTrigger)`)
- `sourceVersion.steps` null/empty → `for (const step of sourceVersion.steps ?? [])` (already guarded with `?? []`)

The imports for both utilities are preserved because they continue to be used in `createDraftFromWorkflowVersion`.

## Monitoring fix: `workflow-version.resolver.ts`

Added `WorkflowTriggerGraphqlApiExceptionFilter` to the `@UseFilters` decorator on `WorkflowVersionResolver`. This filter converts `WorkflowTriggerException` into a `UserInputError` (a GraphQL-level validation error), which prevents it from being sent to Sentry. This matches the existing pattern in `workflow-builder.resolver.ts` and `workflow-trigger.resolver.ts`, which already apply this filter. Without it, any future `WorkflowTriggerException` thrown from `createDraftFromWorkflowVersion` or `updateWorkflowVersionPositions` would also produce noisy Sentry events.
2026-05-08 16:46:15 +00:00
2 changed files with 2 additions and 3 deletions
@@ -10,6 +10,7 @@ import { CreateDraftFromWorkflowVersionInput } from 'src/engine/core-modules/wor
import { DuplicateWorkflowInput } from 'src/engine/core-modules/workflow/dtos/duplicate-workflow.input';
import { UpdateWorkflowVersionPositionsInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-positions.input';
import { WorkflowVersionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version.dto';
import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
@@ -26,6 +27,7 @@ import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-b
SettingsPermissionGuard(PermissionFlagType.WORKFLOWS),
)
@UseFilters(
WorkflowTriggerGraphqlApiExceptionFilter,
PermissionsGraphqlApiExceptionFilter,
PreventNestToAutoLogGraphqlErrorsFilter,
)
@@ -205,9 +205,6 @@ export class WorkflowVersionWorkspaceService {
);
}
assertWorkflowVersionTriggerIsDefined(sourceVersion);
assertWorkflowVersionHasSteps(sourceVersion);
const workflowPosition =
await this.recordPositionService.buildRecordPosition({
value: 'first',