Skip to content

fix: POST /api/accounts cannot document both of its 409 response bodies #443

Description

@karirafn

Summary

POST /api/accounts can answer 409 with two different bodies — the structured NamespaceConflictResponse (namespace conflicts awaiting takeover selection) and, since #438, a bare string (duplicate account). ASP.NET's OpenAPI generator keeps only the last .Produces<T> declared for a status code, so the two cannot both be documented.

During #438 that collision silently deleted the NamespaceConflictResponse and NamespaceConflict component schemas from openapi/v1.json altogether. Because src/foundry-web/package.json:10 regenerates the frontend's schema.ts from that spec and account.model.ts derives its types from those exact keys, the next npm run generate:api would have broken the frontend build. #438 resolved it by dropping the .Produces<string>(409), which restores the schemas but leaves the duplicate-account 409 body undocumented.

Acceptance Criteria

  • Given the generated spec, when POST /api/accounts 409 is inspected, then both possible bodies are documented.
  • Given the spec is regenerated, then NamespaceConflictResponse and NamespaceConflict remain present in components.schemas.
  • Given npm run generate:api followed by an Angular build, then the frontend compiles — ideally with a check that fails CI when the checked-in schema.ts drifts from the spec.

Type

bug

Notes

Needs a short design decision. The likely mechanism is an IOpenApiOperationTransformer emitting oneOf for that response. Program.cs:67 already has an AddOpenApi(options => ...) block (currently only customising schema reference ids) as the extension point; the repo has no operation transformer yet, so this would be the first.

Worth weighing a simpler alternative first: give the duplicate-account rejection a structured body of its own so each status maps to one schema. That trades a new contract type for avoiding a transformer, and the frontend's 409 handler already branches on payload shape (account.service.ts:120), so a second object shape would need that branch tightened.

The latent-drift hazard is the more valuable half of this issue. schema.ts is generated but checked in, so a spec regression stays invisible until someone regenerates — #438's reviewer initially read the dropped schemas as pre-existing for exactly that reason. A CI check comparing a freshly generated schema.ts against the committed one would have caught it immediately.

Surfaced by the code review on #438.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingreview-findingNon-blocking finding surfaced during review-gate

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions