The names were free-standing strings, so renaming a field on the response
schema would leave this list stale and quietly downgrade the error message to
the generic "unrecognized key". `satisfies` makes that a build failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same change as the create body: the server-managed fields are no longer
advertised as writable in the generated spec, and the error map keeps the
specific message when one is sent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The generated request spec listed `id`, `active`, `createdAt` and the other
server-managed fields as ordinary properties with empty schemas, because the
generator cannot express a "must be absent" check. Alongside
`additionalProperties: false`, the published contract positively invited callers
to send fields the server always rejects, and an SDK generated from it would
expose them as settable. The hand-written spec marked them `readOnly`.
They are now left out of the request shape, so the spec describes only what a
caller may send. The strict object rejects them as unrecognised keys, and an
error map keeps the specific message rather than letting a read-only field look
like a typo:
{ "id": "abc" } -> 'id' is a read-only field and cannot be set
{ "nam": "oops" } -> Unrecognized key(s) in object: 'nam'
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments described what the code does, which the code already showed. They
now record the reason a reviewer cannot see: Zod strips unknown keys where the
hand-written spec rejected them, and `/discover` can only read request bodies
out of that spec, which a decorator route has no entry in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`workflowNodeGroup.yml` specified the structure: `id`, `name` and `nodeIds`
required, an optional `description` capped at 155 characters, and no other keys.
The DTO only checked that `nodeGroups` was an array, so four things the old API
rejected were being accepted:
nodeGroups: [{}] missing required fields
nodeGroups: [1, 2, 3] not objects
nodeGroups: [{ ...valid, bogus: 1 }] unknown key
nodeGroups: [{ ...valid, description: 156 }] over the length cap
Declared in the public DTO rather than reusing the internal
`workflowNodeGroupsSchema`, so a change to the internal request shape cannot
silently move the public contract.
The cap stays a literal 155 rather than `GROUP_DESCRIPTION_MAX_LENGTH` (145):
the engine truncates to that value instead of rejecting, so borrowing it would
start rejecting requests the old API accepted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves `PUT /workflows/{id}` and `DELETE /workflows/{id}` onto
`@PublicApiController` and removes the legacy handlers, their hand-written spec
file and the now-unused request types. `workflows.id.yml` held only these two
operations, so the file and its `openapi.yml` entry go with them.
One behaviour change, deliberate: the update response now includes `shared`.
`WorkflowService.update` re-fetches with only the `tags` and `activeVersion`
relations, so `shared` came back undefined and the response mapper threw on
`workflow.shared.map(...)`. The update now re-fetches through
`findWorkflowForUser` for its response, as `getWorkflow` does and as
`createWorkflow` already does internally. The response gains a field; nothing is
removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`UpdateWorkflowPublicDto` reuses the shared write shape from the create DTO, so
both bodies accept and reject the same fields; `projectId` is create-only and is
rejected here, matching the old `workflow.yml`.
`UpdateWorkflowQueryDto` carries `publishIfActive`, a documented query parameter
added in #35813. `WorkflowPublishBlockedErrorPublicDto` describes the 409 body.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves workflow creation onto `@PublicApiController` and removes the legacy
handler, its hand-written spec files, and the now-unused request type. Behaviour
is unchanged: the same `WorkflowCreationService` call, the same redaction check,
the same derived settings stripped before saving.
`workflows.yml` held only this operation once the list route moved in #36015, so
the file and its `openapi.yml` entry go with it.
`workflowPublicDto.generated.yml` appears because a second route now references
the response DTO, so the generator hoists it into a shared component. The 237
lines it gains are the 237 `getWorkflow.generated.yml` loses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the hand-written `workflowCreate.yml` and `workflowSettings.yml` that
express-openapi-validator enforced, so the migrated endpoint accepts and rejects
exactly what it did before:
- unknown top-level keys are rejected, via a new optional `{ strict: true }` on
`Z.class` (default behaviour is unchanged for every other DTO)
- all 17 documented settings are typed, with their enums, and unknown settings
are rejected
- server-managed fields such as `id` and `active` are rejected with a read-only
error rather than silently ignored
- `binaryMode` and `credentialResolverId` are accepted, because the old spec
listed them; the controller strips them afterwards
`nodes`, `connections`, `staticData` and `pinData` stay shape-only checks. Their
contents are node-type specific and only the workflow engine can validate them,
which is also what the old spec did.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`GET /discover?includeSchemas=true` describes each endpoint's request body, but
only ever looked in the hand-written spec files. An endpoint described by a Zod
DTO had nothing to find, so it reported no schema at all.
Three endpoints are affected today: `PUT /workflows/{id}/tags`, `POST /roles`
and `POST /role-mapping-rules`. Nothing caught it because no test asserted a
schema for those.
The description now comes from the route's own `@Body` DTO, converted by the
same generator that writes the spec files, so the two cannot disagree.
Also repoints the reference-resolution test. It used `createWorkflow`, whose
generated schema holds no references, so it had started passing without
exercising anything. It now uses `createDestination`, still described by
hand-written files, and scans the whole resolved schema instead of one field.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>