Initial OpenAPI 3.1 support based on kin-openapi#2336
Conversation
|
@greptileai Please review this large change. It's a back-port from a different repo where this functionality is well tested. It's not ready to merge, due to kin-openapi not having a release ready, but I'd like feedback on the work to date to see if I missed anything major. Pay extra attention to places where OpenAPI 3.0 and OpenAPI 3.1 schemas provide alternative paths in the codegen. |
Greptile SummaryThis PR adds initial OpenAPI 3.1 support to oapi-codegen, ported from an experimental branch. It introduces webhook support (3.1+), callback support (3.0+), the Confidence Score: 5/5Safe to merge; only P2 observations remain — both the non-determinism P1 and the pseudo-version P1 from earlier rounds are resolved. No P0 or P1 issues found in the changed code. The previously flagged CallbackOperationDefinitions non-determinism is fixed with SortedMapKeys. The kin-openapi dependency is now a stable tagged release. Two P2 edge-case suggestions (multi-non-null type array handling and a nil-Responses guard for webhooks) do not block merging. pkg/codegen/schema.go and pkg/codegen/operations.go have the P2 suggestions worth considering before a stable release.
|
| Filename | Overview |
|---|---|
| pkg/codegen/codegen.go | Adds webhook/callback operation gathering to Generate(), wires globalState.is31 from the spec, and correctly gates webhook/callback output sections on their operation slices. |
| pkg/codegen/operations.go | Adds WebhookOperationDefinitions and CallbackOperationDefinitions; both use SortedMapKeys on every map range, fixing the determinism concern from the earlier review. Nil guards are consistent with the existing path-operation code. |
| pkg/codegen/schema.go | Adds schemaIsNullable, schemaPrimaryType, detectEnumViaOneOf, and describeWithExamples helpers; all depend on globalState.is31 being set before any call (precondition documented). schemaPrimaryType has an edge case for multi-non-null type arrays. |
| pkg/codegen/merge_schemas.go | Switches ExclusiveMin/ExclusiveMax comparison to reflect.DeepEqual and nullable comparison to schemaIsNullable(), making merging version-aware without breaking 3.0 behavior. |
| pkg/codegen/configuration.go | Adds SkipEnumViaOneOf opt-out flag; properly reflected in configuration-schema.json and the detectEnumViaOneOf gate. |
| pkg/codegen/templates/webhook-initiator.tmpl | New template mirrors the existing client template; uses a per-call targetURL instead of a stored server URL, correct for the webhook dispatch model. |
| go.mod | Updates kin-openapi to v0.136.0 (now a stable release), bumps Go to 1.26.2; the previously noted pseudo-version concern has been addressed. |
Reviews (2): Last reviewed commit: "Fix lint issues and CI versioning" | Re-trigger Greptile
e4b5380 to
d33792a
Compare
|
@greptileai - all comments addressed, review again. |
|
(note to folks following this - we'll be releasing this as a 2.8.0 release, after 2.7.0) |
|
Do we think this will close any others from https://github.com/oapi-codegen/oapi-codegen/issues?q=is%3Aissue%20state%3Aopen%20openapi%203.1%20label%3A3.1 ? |
|
Worth checking if anything from 620da6b (and the related change to make the |
jamietanna
left a comment
There was a problem hiding this comment.
We'll also need a README change when this is closer to completion - to note that we do support 3.1 (but not 3.2!)
jamietanna
left a comment
There was a problem hiding this comment.
Will have a more in-depth review shortly, but so far this is looking reasonable 🥳
Yes. I will add them to PR description. |
d33792a to
38df03d
Compare
|
I rebased this onto the latest main, and downgraded the Go requirement to 1.25.9 |
kin-openapi's 3.1 work changed Schema.ExclusiveMin/Max from bool to
ExclusiveBound{Bool *bool; Value *float64}. Direct struct equality (!=)
still compiles but compares pointer addresses, not the wrapped values, so
two schemas with semantically identical bounds would incorrectly fail the
merge. Use reflect.DeepEqual for value-aware comparison.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This is Phase 1 of bringing 3.1 feature parity from oapi-codegen-exp into
mainline. It establishes the architectural patterns the rest of the work
will follow.
Version awareness:
* New globalState.is31 flag, populated once at Generate() entry from
swagger.IsOpenAPI31OrLater(). All version-aware logic lives in Go and
branches on this flag; templates remain version-blind and consume
pre-computed derived fields.
* Comment on the field explicitly forbids exposing it to TemplateFunctions
to prevent layering violations.
Nullable handling:
* New schemaIsNullable() helper that branches on globalState.is31:
3.0 reads s.Nullable; 3.1 reads s.Type.Includes("null").
* New schemaPrimaryType() helper strips "null" from the type slice in
3.1 mode so existing dispatch (`*Types.Is("string")` etc.) keeps
working for type:["string","null"].
* Four direct .Nullable read sites switched to the helper:
schema.go (property construction, array nullable, additionalProperties)
and operations.go (response header).
* merge_schemas.go's Nullable comparison routed through the helper; an
explanatory comment notes that type merging itself is NOT version
branched (equalTypes() handles single-element 3.0 and multi-element
3.1 type slices identically), and that result.Type already carries
any "null" entry forward in 3.1 mode.
Other:
* Adds OutputOptions.SkipEnumViaOneOf for Phase 2 (enum-via-oneOf
detection); declared now to keep the config schema stable across
phases.
* Removes the stale "OpenAPI 3.1.x is not yet supported" stderr
warning at cmd/oapi-codegen entry; partial 3.1 support now exists
and the message will become misleading as later phases land.
* Two path parameters in pkg/codegen/test_specs/x-go-type-import-pet.yaml
were missing the OpenAPI-required `required: true`. The omission was
incidental to what the test exercises (x-go-type-import); the spec is
now spec-conformant.
Tests:
* internal/test/openapi31_nullable/ -- two specs (3.0 nullable:true,
3.1 type:["string","null"]) generate into spec_3_0/ and spec_3_1/
subpackages. Instantiation tests (no string-matching) assert both
produce *string and round-trip JSON identically.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 2 of the 3.1 backport. Detects the 3.1 enum-via-oneOf idiom and
routes it through the existing typed-enum codegen path so users see the
same Go shape they'd get from a plain `enum:` array.
The idiom (per OpenAPI 3.1 / JSON Schema 2020-12):
Severity:
type: integer # or "string"
oneOf:
- title: HIGH
const: 2
description: An urgent problem
- title: MEDIUM
const: 1
- title: LOW
const: 0
Generates:
type Severity int
const (
HIGH Severity = 2
LOW Severity = 0
MEDIUM Severity = 1
)
Implementation:
* New detectEnumViaOneOf() helper in pkg/codegen/schema.go. Trigger
conditions: globalState.is31 is true, !SkipEnumViaOneOf, schema's
primary type is "string" or "integer", every oneOf branch carries
non-empty Title AND non-nil Const, and no branch is itself a
composition or has Properties.
* GenerateGoSchema short-circuits between the AllOf branch and the
type dispatch: when the idiom matches, populate Schema.EnumValues
directly and register an AdditionalType for non-toplevel schemas
so the existing EnumDefinition collector picks them up. Negative
matches fall through to the standard oneOf union generator.
* No template changes -- the existing constants.tmpl renders the
typed enum + Valid() method via the standard EnumDefinition path.
* The OutputOptions.SkipEnumViaOneOf flag declared in Phase 1 is
now actually consulted here.
Tests:
* internal/test/enum_via_oneof/ -- ports the exp test fixture
(Severity int, Color string, MixedOneOf negative path). Five
instantiation tests (no string-matching) cover constant values,
JSON round-trips, and the negative path's compile-time alias
shape (a plain string is directly assignable to MixedOneOf,
proving it did NOT become a typed-enum newtype).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…iver
Phase 3 of the 3.1 backport. Generates client-side and server-side
support for `webhooks:` declared in OpenAPI 3.1 specs. The output
mirrors the existing Client / Server interfaces in shape but lives in
its own types, matching the conventions oapi-codegen-exp established.
Data model:
* OperationDefinition gains IsWebhook bool and WebhookName string.
* New WebhookOperationDefinitions() walks swagger.Webhooks and produces
OperationDefinitions in the same shape as path operations, minus the
path-alias and path-parameter logic (webhooks have no path template).
No version gate -- kin-openapi only populates the Webhooks field for
3.1+ documents, so 3.0 specs short-circuit on the empty map.
Generated client (WebhookInitiator):
* New pkg/codegen/templates/webhook-initiator.tmpl, modeled on
client.tmpl. Emits:
type WebhookInitiator struct{ Client; RequestEditors }
NewWebhookInitiator + WebhookInitiatorOption + WithWebhookHTTPClient
+ WithWebhookRequestEditorFn
WebhookInitiatorInterface
per-webhook methods (Op + OpWithBody) and request builders
(NewOpWebhookRequest + NewOpWebhookRequestWithBody)
* No stored Server -- the target URL is supplied per-call by the
caller (typically discovered from a subscription registration).
* Gated by Generate.Client (paired with the path Client).
Generated server (WebhookReceiver, stdhttp):
* New pkg/codegen/templates/stdhttp/std-http-webhook-receiver.tmpl.
Emits:
type WebhookReceiverInterface { HandleOpWebhook(w, r) for each }
type WebhookReceiverMiddlewareFunc func(http.Handler) http.Handler
OpWebhookHandler(si, middlewares...) http.Handler -- per-webhook
factory; caller mounts the returned http.Handler at the URL
path they advertise to subscribers.
* Methods take plain (w, r) -- the user reads/parses the body
themselves. Simpler than the full ServerInterfaceWrapper machinery;
parameter binding can be added later if the demand surfaces.
* Gated by Generate.StdHTTPServer (paired with the path stdhttp
ServerInterface). Other frameworks are left as follow-ups.
Wiring (codegen.go):
* allOps = ops + webhookOps is passed to OperationImports and
GenerateTypeDefinitions so webhook bodies and responses generate
their type definitions and imports normally.
* New GenerateWebhookInitiator and GenerateStdHTTPWebhookReceiver entry
points in operations.go, modeled on GenerateClient and
GenerateStdHTTPServer.
* Output ordering: webhook initiator follows ClientWithResponses;
webhook receiver follows the path StdHTTPServer block.
Tests (internal/test/webhooks/):
* spec.yaml declares a single petStatusChanged webhook with a JSON
PetStatusEvent body and a 204 response.
* TestWebhookRoundTrip mounts the generated factory against an
httptest.Server and fires a webhook via the initiator; asserts the
payload, method, and Content-Type round-trip intact.
* TestWebhookInitiatorRequestEditor verifies WithWebhookRequestEditorFn
applies on every outgoing request (parity with the path Client).
* TestWebhookReceiverMiddleware documents middleware composition order:
the factory wraps in `for _, mw := range middlewares { h = mw(h) }`,
so the LAST middleware passed becomes the outermost wrapper.
Example (examples/webhook/):
* Door badge reader scenario ported from oapi-codegen-exp/examples/webhook.
The server randomly generates enter/exit badge events every second
and fires the appropriate webhook to all registered subscribers; the
client subscribes to both event kinds via the path API, runs a local
HTTP receiver to handle inbound events, and deregisters cleanly on
exit. Demonstrates the full Client/Server + WebhookInitiator/Receiver
pairing in one example.
* Lives as a regular package under examples/go.mod -- no separate
module, no tools.go shim. `make generate` from examples/ regenerates
via the package's go:generate directive.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 4 of the 3.1 backport. Generates client-side and server-side
support for `callbacks:` blocks nested under path operations. The output
mirrors WebhookInitiator / WebhookReceiver in shape -- separate types
named with the `Callback` prefix to match oapi-codegen-exp.
Callbacks have been part of the OpenAPI spec since 3.0, so this is NOT
version-gated: any spec that declares callbacks gets them generated.
Data model:
* OperationDefinition gains IsCallback bool and CallbackName string
(alongside the IsWebhook / WebhookName pair from Phase 3).
* New CallbackOperationDefinitions() walks
spec.Paths.<path>.<method>.Callbacks. Each leaf operation under the
callback's URL-expression key becomes one OperationDefinition with
IsCallback=true and CallbackName set to the outer map key (e.g.
"treePlanted"). The codegen does not interpret the URL expression
itself -- the caller of CallbackInitiator supplies the resolved
target URL at runtime, the same way it does for a webhook.
* Callback's internal map is private; iterate via cb.Keys() / cb.Value()
with sorted keys for deterministic output.
Generated client (CallbackInitiator):
* New pkg/codegen/templates/callback-initiator.tmpl. Same shape as the
Phase 3 webhook-initiator.tmpl with `Webhook` -> `Callback` rename:
type CallbackInitiator struct{ Client; RequestEditors }
NewCallbackInitiator + CallbackInitiatorOption
+ WithCallbackHTTPClient + WithCallbackRequestEditorFn
CallbackInitiatorInterface
per-callback methods (Op + OpWithBody) and request builders
(NewOpCallbackRequest + NewOpCallbackRequestWithBody)
* No stored Server -- targetURL is per-call.
* Gated only by Generate.Client + non-empty callbackOps.
Generated server (CallbackReceiver, stdhttp):
* New pkg/codegen/templates/stdhttp/std-http-callback-receiver.tmpl.
* WebhookReceiverInterface analog: Handle{Op}Callback(w, r) plus a
per-callback factory function {Op}CallbackHandler(si, middlewares...)
http.Handler.
* Gated only by Generate.StdHTTPServer + non-empty callbackOps.
Wiring (codegen.go):
* allOps is now `ops + webhookOps + callbackOps` so type-definition
and import passes see callback bodies / responses.
* Callback initiator output follows webhook initiator (so the order is
Client / ClientWithResponses / WebhookInitiator / CallbackInitiator).
* Callback receiver output follows webhook receiver under the
StdHTTPServer block.
* New GenerateCallbackInitiator and GenerateStdHTTPCallbackReceiver
template entry points in operations.go.
Tests (internal/test/callbacks/):
* spec.yaml deliberately uses `openapi: 3.0.3` to verify the codegen
does NOT gate callback emission on 3.1 (callbacks are 3.0+).
Models a tree-planting flow: parent operation PlantTree, callback
treePlanted with TreePlantingResult body.
* TestCallbackRoundTrip mounts the generated factory against an
httptest.Server and fires a callback via the initiator; asserts the
payload, method, and Content-Type round-trip intact.
* TestCallbackInitiatorRequestEditor verifies WithCallbackRequestEditorFn
applies on every outgoing request (parity with the path Client and
the WebhookInitiator).
Example (examples/callback/):
* Tree farm scenario ported from oapi-codegen-exp/examples/callback.
Server accepts PlantTree, returns 202, sleeps 1-5s, then fires the
treePlanted callback to the caller-supplied URL. Client opens an
ephemeral receiver, fires 10 plantings in sequence, and waits on a
channel until all 10 callbacks have arrived.
* Lives as a regular package under examples/go.mod -- no separate
module, no tools.go shim. Same flat layout as examples/webhook/.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 5 of the 3.1 backport. Two small additive features that round out the 3.1 surface; no template changes. `examples:` (plural array) → Go doc comments: * New describeWithExamples() helper in pkg/codegen/schema.go folds example data into the description string used for generated doc comments. Version-aware: 3.0 reads schema.Example (singular), 3.1 reads schema.Examples (plural array). Cross-version misuse is documented as invalid input and the helper does not look at the off-version field. * Non-string examples are JSON-encoded so structured values render readably; strings round-trip as themselves. * Applied at the three sites where Description flows from the spec schema into the generated Schema/Property: GenerateGoSchema's $ref short-circuit, the main outSchema construction, and per-property population during object walk. `const:` → typed alias + singleton constant: * The existing enum branch in GenerateGoSchema now also fires on `(globalState.is31 && schema.Const != nil)`. A local enumSource abstracts over the two cases so a scalar `const: X` schema emits the same shape as `enum: [X]`: a typed alias plus a sanitized constant derived from the value. * Falls through the standard EnumDefinition pipeline -- existing constants.tmpl renders the typed enum + Valid() method without changes. Paths optional in 3.1: no-op. Every swagger.Paths access site (filter.go, prune.go, gather.go, operations.go, the new WebhookOperationDefinitions and CallbackOperationDefinitions) already nil-guards. Tests (internal/test/openapi31_polish/): * Pet.Name has a description plus two string examples; Pet.Lives has one integer example. Status is a top-level `const: active` schema. * TestStatusConstSchema (instantiation): `var s Status = Active` compiles only because Status is a distinct named type, and asserts the constant's value plus that Valid() returns true. * TestPetExampleComments parses the generated Go source and walks the AST to extract Pet's per-field doc comments, then asserts the expected description and "Examples: ..." fragments. Doc comments aren't runtime-introspectable, so AST inspection of the generated file is the right granularity here -- not a brittle string-match against the file as a whole. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…add param binding
Phase 6.1 of the 3.1 backport (kicking off the per-framework receiver
work). Sets up the prototype shape that the other 7 server-framework
receivers will follow.
Two changes that always have to land together:
1. Unify the two receiver templates into one parameterized template,
matching oapi-codegen-exp's structure:
* Replaces pkg/codegen/templates/stdhttp/std-http-webhook-receiver.tmpl
and pkg/codegen/templates/stdhttp/std-http-callback-receiver.tmpl
(deleted) with a single std-http-receiver.tmpl. The new template
takes a Prefix data field ("Webhook" or "Callback") and emits the
correct interface, middleware type, and per-op handler factory.
* New ReceiverTemplateData struct + NewReceiverTemplateData
constructor in operations.go feed the template.
* Single GenerateStdHTTPReceiver(t, prefix, ops) entry point;
callers in Generate() pass "Webhook" or "Callback".
2. Add query/header parameter binding to the receiver factory,
matching mainline's existing path-server middleware pattern.
The factory now takes an errHandler argument:
func PetStatusChangedWebhookHandler(
si WebhookReceiverInterface,
errHandler func(w http.ResponseWriter, r *http.Request, err error),
middlewares ...WebhookReceiverMiddlewareFunc,
) http.Handler
When operations have query/header params (rare for webhooks but
common for callbacks), the inline closure binds them into a typed
{Op}Params struct via runtime.BindQueryParameterWithOptions /
runtime.BindStyledParameterWithOptions and routes any error through
errHandler. errHandler may be nil; the default returns 400 with the
error message. When operations have no params, errHandler is
unused -- pass nil and the prior shape's behavior is preserved.
This is a breaking change to anyone using the Phase 3/4 generated
*Handler(si, middlewares...) shape. Acceptable since the Phase 3/4
surface is unreleased and only-this-branch.
New helper: OperationDefinition.SourceName() returns WebhookName when
IsWebhook, CallbackName when IsCallback. Templates use it to label
emitted handlers uniformly without branching on the source kind.
Existing call sites updated to pass nil for errHandler:
internal/test/webhooks/webhooks_test.go (3 places)
internal/test/callbacks/callbacks_test.go (2 places)
examples/webhook/client/main.go (2 places)
examples/callback/client/main.go (1 place)
The four affected gen files are regenerated. Other gen files in the
tree have unrelated drift from Phase 5's examples-in-doc-comments
feature (those don't touch the new receiver shape); a follow-up
`make generate` commit picks them up cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…omments Phase 5 (eedf057) added the describeWithExamples() helper that folds schema.Example / schema.Examples into the description string used for generated Go doc comments. Existing test specs that already had `example:` set on their schemas now produce richer doc comments. This commit is purely the result of running `make generate` -- no behavior change, only doc-comment additions like: // Code The underlying http status code +// +// Example: 500 Code int32 `json:"code"` Touched files: examples/minimal-server/stdhttp/api/ping.gen.go internal/test/externalref/petstore/externalref.gen.go internal/test/issues/issue-1087/deps/deps.gen.go internal/test/issues/issue-1168/api.gen.go internal/test/issues/issue1561/issue1561.gen.go Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.2 of the per-framework receiver work. Chi's path-handler signature is identical to stdhttp's (`(w http.ResponseWriter, r *http.Request)`), so the receiver template is structurally identical; only the file path and Go entry point are new. * New pkg/codegen/templates/chi/chi-receiver.tmpl -- a copy of the Phase 6.1 unified stdhttp receiver template. Single template handles both webhook and callback receivers via the Prefix data field. * New GenerateChiReceiver() entry point in operations.go. * In Generate(): two new blocks gated on Generate.ChiServer + non-empty webhook/callback ops, mirroring the stdhttp blocks. Generated output is written inside the existing chi-server WriteString block so all chi-related output stays grouped. * New internal/test/webhooks_chi/ -- compile-time assertion that the chi receiver template produces valid Go. The runtime round-trip behavior is already covered by internal/test/webhooks (stdhttp); since chi shares the signature, that coverage transfers. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.3. Gorilla shares stdhttp's `(w, r)` handler signature, so the receiver template is structurally identical -- same approach as the Phase 6.2 chi addition. * New pkg/codegen/templates/gorilla/gorilla-receiver.tmpl * New GenerateGorillaReceiver() entry point * Wiring in Generate() gated on Generate.GorillaServer; output written inside the existing gorilla-server WriteString block * New internal/test/webhooks_gorilla/ as a compile-time assertion; runtime round-trip behavior is covered by internal/test/webhooks (signature is identical, so coverage transfers) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.4. First framework with a non-stdhttp signature, requiring its
own bespoke template.
Echo's idioms:
* Method signature `Handle{Op}{Prefix}(ctx echo.Context, params...) error`
(echo v4 uses the non-pointer Context).
* Factory returns `echo.HandlerFunc`; middlewares are echo's native
`echo.MiddlewareFunc` (`func(echo.HandlerFunc) echo.HandlerFunc`).
* Parameter-binding errors are returned via echo.NewHTTPError so
echo's framework error chain reports them as 400. There's no
errHandler argument like the stdhttp factory has.
* Query params bind via ctx.QueryParam / ctx.QueryParams() (and
runtime.BindQueryParameterWithOptions for styled). Header params
read ctx.Request().Header.
* New pkg/codegen/templates/echo/echo-receiver.tmpl
* New GenerateEchoReceiver() entry point
* Wiring in Generate() gated on Generate.EchoServer; output written
inside the existing echo-server WriteString block
* New internal/test/webhooks_echo/ -- compile-time assertion that the
echo receiver template produces valid Go
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.5. Echo v5 differs from v4 only in using `*echo.Context` (pointer) -- otherwise the API surface (ctx.QueryParam, ctx.Request(), echo.NewHTTPError, echo.HandlerFunc, echo.MiddlewareFunc) is identical. The template is a `echo.Context` -> `*echo.Context` substitution from the Phase 6.4 v4 template. * New pkg/codegen/templates/echo/v5/echo-receiver.tmpl * New GenerateEcho5Receiver() entry point * Wiring in Generate() gated on Generate.Echo5Server; output written inside the existing echo5-server WriteString block No internal/test/webhooks_echo5/ directory: echo v5 is not in internal/test/go.mod (the existing echov5 fixture lives in its own sub-module under internal/test/parameters/echov5/), so adding a webhook test would require either invasive dep changes or another sub-module. The Phase 6.4 echo v4 test exercises the same template shape end-to-end -- inspection of the v5 codegen output confirms only the Context pointer-ness changes. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.6. Gin's idioms:
* Method signature `Handle{Op}{Prefix}(c *gin.Context, params...)`
with no error return -- gin reports errors via the context.
* Factory returns `gin.HandlerFunc`. Per-handler middleware is NOT
generated (gin's idiom prefers route-group / engine .Use()
composition); users mount middleware at the engine level.
* Parameter-binding errors abort the request with
c.JSON(http.StatusBadRequest, gin.H{"error": "..."}).
* Query params bind via c.Query / c.Request.URL.Query() (and
runtime.BindQueryParameterWithOptions for styled). Header params
read c.Request.Header.
* New pkg/codegen/templates/gin/gin-receiver.tmpl
* New GenerateGinReceiver() entry point
* Wiring in Generate() gated on Generate.GinServer
* New internal/test/webhooks_gin/ -- compile-time assertion
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.7. Fiber's idioms:
* Method signature `Handle{Op}{Prefix}(c *fiber.Ctx, params...) error`
(mainline pins fiber v2 -> *fiber.Ctx pointer; v3 changes to a
non-pointer interface but is not pinned in mainline).
* Factory returns `fiber.Handler` (alias for `func(*fiber.Ctx) error`).
* Parameter-binding errors are returned via `fiber.NewError(
fiber.StatusBadRequest, ...)` so fiber's error chain reports them
as 400.
* Query-param binding via `url.ParseQuery(string(c.Request().URI().
QueryString()))` to get url.Values for runtime.BindQueryParameter
WithOptions, plus `c.Query("name")` for direct passthrough/JSON.
* Header params read `c.GetReqHeaders()` which returns
map[string][]string.
* Per-handler middleware is NOT generated; users compose middleware
via fiber.App.Use().
* New pkg/codegen/templates/fiber/fiber-receiver.tmpl
* New GenerateFiberReceiver() entry point
* Wiring in Generate() gated on Generate.FiberServer
* New internal/test/webhooks_fiber/ -- compile-time assertion
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6.8, completing the per-framework receiver fan-out. Iris's
idioms:
* Method signature `Handle{Op}{Prefix}(ctx iris.Context, params...)`
with no error return -- iris reports errors via the context.
* Factory returns `iris.Handler`. Per-handler middleware is NOT
generated; iris's idiom prefers app.Use() / Party-level composition.
* Parameter-binding errors set ctx.StatusCode(400) plus
ctx.WriteString(reason) and return.
* Query params bind via ctx.URLParam("name") for direct values, and
runtime.BindQueryParameterWithOptions with ctx.Request().URL.Query()
for styled. Header params read ctx.Request().Header.
* New pkg/codegen/templates/iris/iris-receiver.tmpl
* New GenerateIrisReceiver() entry point
* Wiring in Generate() gated on Generate.IrisServer
* New internal/test/webhooks_iris/ -- compile-time assertion
All seven server frameworks (stdhttp/chi/gorilla/echo/echo5/gin/
fiber/iris) now emit framework-native webhook+callback receiver
interfaces alongside their path-server interfaces, gated by the
existing per-framework Generate.* flags.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…/<framework>/ Final Phase 6 cleanup. Reorganizes the seven flat per-framework webhook test directories under a single internal/test/webhooks/ parent so they group together and the layout reflects the framework-fan-out done in Phases 6.1-6.8. Layout change: internal/test/webhooks/ (the original stdhttp tests) internal/test/webhooks_chi/ } internal/test/webhooks_echo/ } internal/test/webhooks_fiber/ } flat per-framework dirs internal/test/webhooks_gin/ } internal/test/webhooks_gorilla/ } internal/test/webhooks_iris/ } becomes: internal/test/webhooks/ ├── spec.yaml (shared by all subdirs) ├── stdhttp/ (original round-trip tests) ├── chi/ ├── echo/ ├── fiber/ ├── gin/ ├── gorilla/ └── iris/ Per-subdir changes: * Package name simplified from `webhooks` / `webhooks_<framework>` to just `<framework>` (matches dir name; package-vs-import name overlap with framework packages like chi/v5 is harmless since Go's local package name is implicit). * Generated file uniformly named `webhooks.gen.go` (was `webhooks_<framework>.gen.go` in flat dirs). * config.yaml `package:` and `output:` directives updated; schema reference path bumped one level deeper. * doc.go `package` and prose updated; `go:generate` now points at the shared `../spec.yaml`. Spec deduplication: * The seven copies of spec.yaml (functionally identical, only title/description varied) collapse to one shared file at internal/test/webhooks/spec.yaml. Each subdir's go:generate references it via `../spec.yaml`. The round-trip test in stdhttp/webhooks_test.go has its package declaration updated to `package stdhttp`. No code or template changes outside the moves; same gen output up to package name. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Two findings from automated review: P1 -- Non-deterministic map iteration in CallbackOperationDefinitions (pkg/codegen/operations.go). The inner loop walked `pathItem.Operations()` directly. When a path declares callbacks on multiple HTTP methods (e.g. both POST and PUT), the generated output would differ between runs because Go map iteration is randomized. Fix: copy keys into a slice and walk via SortedMapKeys, matching the pattern used by every other similar loop in the codebase (including WebhookOperationDefinitions immediately above). P2 -- Hidden global-state dependency in version-aware helpers (pkg/codegen/schema.go). schemaIsNullable, schemaPrimaryType, describeWithExamples, and detectEnumViaOneOf all read globalState.is31 implicitly, with no indication at the function signature that callers must invoke them only after Generate() has initialized the global. Fix: add an explicit "Precondition" line to each helper's doc comment so callers (including future refactors that move schema resolution earlier) are aware of the dependency. The signatures themselves are not changed -- threading the version flag through every call site would be an intrusive refactor for a problem better described in prose. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Pulls in the official kin release which adds OpenAPI 3.1 support, and updates CI to run only on Go 1.26
Two distinct lint issues across four test files.
errcheck (5 sites): defer / direct calls to r.Body.Close() and
resp.Body.Close() ignored their error returns. Standard fix:
* `defer r.Body.Close()` -> `defer func() { _ = r.Body.Close() }()`
* `resp.Body.Close()` (non-deferred) -> `_ = resp.Body.Close()`
internal/test/callbacks/callbacks_test.go (1)
internal/test/webhooks/stdhttp/webhooks_test.go (4)
ST1023 (2 sites): "should omit type X from declaration; it will be
inferred from the right-hand side". Two different stories.
* internal/test/openapi31_polish/openapi31_polish_test.go: `var s
Status = Active` is genuinely redundant -- `Active` is declared
`const Active Status = "active"`, so type inference gives `s` the
type `Status` already. Switched to `s := Active`. Comment updated
to explain the still-meaningful compile-time check (if Active had
been emitted untyped, `s := Active` would not preserve the Status
type).
* internal/test/enum_via_oneof/enum_via_oneof_test.go: `var m
MixedOneOf = s` (where `s` is a plain string) IS the test. The
redundancy is intentional -- if MixedOneOf were a `type MixedOneOf
string` newtype rather than the alias `type MixedOneOf = string`,
this assignment would fail to compile. Letting inference give `m`
the type `string` would lose the property under test. Added
`//nolint:staticcheck` with the rationale inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Now that kin doesn't require Go 1.26, downgrade to Go 1.25 and rebase changes from main.
Add OpenApi 3.1 top level schema sources to the hoisting test.
38df03d to
329414b
Compare
|
(note to me - we should double check everything in https://learn.openapis.org/upgrading/v3.0-to-v3.1.html is covered) |
|
I'm seeing the same Edit: |
|
No, it's not expected. I'll look at it in a little while. Very busy with other things right now. Thanks for letting us know. |
# Conflicts: # Makefile
Types.Is() is too strict for out needs. We want Is("object") to return
true for [object, "null"], but it's an exact match, replace this with
our own alternative that behaves as we want.
Fixes: #373
This is a back-port of my OpenAPI 3.1 work in the oapi-codegen-exp repo.
Since kin-openapi now requires Go 1.26.2, merging this requires some updates to our build, and also increasing our Go version dependency. That will require a bit of cleanup in our sub-modules and lots of them can be simplified. For now, I've done the minimal set of work in this branch to get it to compile.
OpenAPI 3.1 idioms suported: