Skip to content

Add [Operation] and align the code-first sample with its contracts - #285

Merged
ipjohnson merged 1 commit into
mainfrom
operation-attribute
Sep 5, 2026
Merged

ipjohnson merged 1 commit into
mainfrom
operation-attribute

Conversation

@ipjohnson

Copy link
Copy Markdown
Owner

What

[Operation("listTodos")] on a code-first handler declares the operationId the document publishes. The generator carries it on the handler model beside Tag, writes it as given, prefixes a derived name that collides with a declared one with its tag (as it already does for two derived names), and reports two handlers declaring one id as HRDOA004, an error. Spec-first handlers carry their description's id verbatim instead of a camel-cased round trip, so the Smithy fixture's exported document moves from getPet to GetPet, which is what Smithy's own conversion writes.

The template's code-first sample declares the contract's four ids and the contract's constraints, [Range(Min = 1)] on the id and [StringLength(1, 64)] on the title. Its document now declares the same 400s with the same RequestValidationError schema, the minimum and minLength/maxLength the contract has, and the same operation ids, so the Refit method names, the three constraint tests and the two malformed-id tests run in every contract mode with no codeFirst or specFirst branch. DocumentStatusTests keeps one expectation set for every mode that declares its statuses; code-first throws mode keeps its own.

The socket test's Kiota and Refit blocks gain the NUnit assert pair #284 put around every other assert. Merged after #284, #283 had left them xUnit-only, so the script's kestrel:code:response:kiota:nunit row does not compile on main.

Why

A code-first handler's id was the method name, so the client generated from a code-first service differed from the one generated from the same service written spec-first, and the template's tests had to know which way the service was written. The id is part of the contract: a Refit interface names its methods after it, and deriving it from the method name made a C# rename a breaking change on the wire. With the ids and the constraints declared, one client comes out whichever way the service was written, which is what lets one test file cover both.

Verified: the CI-parity build, the full suite (7266 tests, the Smithy fixture's export re-approved by the build and the public API re-approved for the attribute), six new generator tests for the id derivation and the diagnostic, and six template rows scaffolded from a pack of this tree with zero warnings and 15 tests passing each: kestrel code-first with Kiota, Kiota under NUnit, Refit, Refit in throws mode and no client, and kestrel openapi with Refit. scripts/verify-templates.sh was running when this opened; its result follows as a comment. The generator source could not be validated against Hardened.Amz locally, as Amz main does not yet build against Framework main.

🤖 Generated with Claude Code

A code-first handler's operationId was the method name in camelCase, so
the Refit client from a code-first template said All() where the
spec-first one said ListTodos(), and the template's Refit tests carried a
codeFirst branch for the names. [Operation("listTodos")] on the handler
declares the id; the document writes it as given, a derived name that
collides with a declared one is prefixed with its tag as two derived
names are, and two handlers declaring one id is HRDOA004. Spec-first
handlers now carry their description's id verbatim rather than a
camel-cased round trip, which is why the Smithy fixture's export moves
to its shape names.

The template's code-first sample declares the contract's four ids, and
the same constraints: [Range(Min = 1)] on the id and [StringLength(1, 64)]
on the title, so the code-first document declares the same 400s with the
same RequestValidationError schema. The Refit method names, the
constraint tests and the malformed-id tests therefore run in every
contract mode with no branch, and DocumentStatusTests has one expectation
set for every mode that declares its statuses.

The socket test's Kiota and Refit blocks gain the NUnit assert pair #284
put around every other assert; merged after it, #283 had left them
xUnit-only, so the script's kiota:nunit row did not compile.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ipjohnson

Copy link
Copy Markdown
Owner Author

scripts/verify-templates.sh finished after this opened, exit 0: "templates verified". 14 hardened-web rows built and tested from the packed template, including the NUnit, Moq and FakeItEasy rows #284 added and the three Refit rows whose tests now call the contract's method names code-first, plus the three Amz rows against published Hardened.Amz. Host independence held for src/Sample, src/Sample.Client and tests/Sample.Tests. The smithy rows were skipped, as the pinned Smithy CLI is not on this machine's PATH.

@ipjohnson
ipjohnson merged commit 1370232 into main Sep 5, 2026
2 checks passed
@ipjohnson
ipjohnson deleted the operation-attribute branch September 6, 2026 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant