From 68abea953184dc984c188f5b5868b40f3ded853c Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Sun, 3 May 2026 17:53:52 -0700 Subject: [PATCH 1/4] Add optional type generation for inline schemas Adds output-options.generate-types-for-anonymous-schemas (default false). When set, every inline schema that would otherwise generate as an anonymous Go struct is instead emitted as a named type with a path- derived name (e.g. GetRolesId200JSONResponseBody_Data). Equivalent to adding x-go-type-name to every inline schema; when both are present at the same site, x-go-type-name wins. I will need to document this, since it's still better, from a usability perspective, to put concrete types of interest in component/schemas or as a second alternative, use x-go-type-name. However, this is requested often enough, that I'd thing it's worth exposing as a feature. Test coverage reorganized: internal/test/anonymous_inner_hoisting/ now has two subdirectories. The existing always-on hoisting test (oneOf, anyOf, additionalProperties) moves to implicit/. The new flag-gated behavior gets its own test in global/, exercising the canonical issue shape (allOf-merged response with a sibling inline `data` object that itself contains a nested `role` reference). Closes: #1139 --- configuration-schema.json | 5 + .../anonymous_inner_hoisting/global/cfg.yaml | 7 + .../global/client.gen.go | 279 ++++++++++++++++++ .../global/client_test.go | 44 +++ .../{ => global}/generate.go | 2 +- .../anonymous_inner_hoisting/global/spec.yaml | 58 ++++ .../{ => implicit}/cfg.yaml | 2 +- .../{ => implicit}/client.gen.go | 4 +- .../{ => implicit}/client_test.go | 2 +- .../implicit/generate.go | 3 + .../{ => implicit}/spec.yaml | 0 pkg/codegen/configuration.go | 9 + pkg/codegen/operations.go | 6 +- pkg/codegen/schema.go | 41 +++ 14 files changed, 455 insertions(+), 7 deletions(-) create mode 100644 internal/test/anonymous_inner_hoisting/global/cfg.yaml create mode 100644 internal/test/anonymous_inner_hoisting/global/client.gen.go create mode 100644 internal/test/anonymous_inner_hoisting/global/client_test.go rename internal/test/anonymous_inner_hoisting/{ => global}/generate.go (76%) create mode 100644 internal/test/anonymous_inner_hoisting/global/spec.yaml rename internal/test/anonymous_inner_hoisting/{ => implicit}/cfg.yaml (64%) rename internal/test/anonymous_inner_hoisting/{ => implicit}/client.gen.go (99%) rename internal/test/anonymous_inner_hoisting/{ => implicit}/client_test.go (99%) create mode 100644 internal/test/anonymous_inner_hoisting/implicit/generate.go rename internal/test/anonymous_inner_hoisting/{ => implicit}/spec.yaml (100%) diff --git a/configuration-schema.json b/configuration-schema.json index 8f902ca708..c471f2d2e9 100644 --- a/configuration-schema.json +++ b/configuration-schema.json @@ -277,6 +277,11 @@ "description": "When set to true, automatically renames types that collide across different OpenAPI component sections (schemas, parameters, requestBodies, responses, headers) by appending a suffix based on the component section. Also detects collisions between component types and client response wrapper types. Without this, the codegen will error on duplicate type names, requiring manual resolution via x-go-name.", "default": false }, + "generate-types-for-anonymous-schemas": { + "type": "boolean", + "description": "When true, every inline schema that would otherwise generate as an anonymous Go struct is instead emitted as a named type with a path-derived name (e.g. `GetRolesIdResponseBody_Data`). Equivalent to adding `x-go-type-name` to every inline schema; when both are present at the same site, `x-go-type-name` wins. Default false. See https://github.com/oapi-codegen/oapi-codegen/issues/1139", + "default": false + }, "type-mapping": { "type": "object", "additionalProperties": false, diff --git a/internal/test/anonymous_inner_hoisting/global/cfg.yaml b/internal/test/anonymous_inner_hoisting/global/cfg.yaml new file mode 100644 index 0000000000..2f59ebd5e5 --- /dev/null +++ b/internal/test/anonymous_inner_hoisting/global/cfg.yaml @@ -0,0 +1,7 @@ +package: global +output: client.gen.go +generate: + models: true + client: true +output-options: + generate-types-for-anonymous-schemas: true diff --git a/internal/test/anonymous_inner_hoisting/global/client.gen.go b/internal/test/anonymous_inner_hoisting/global/client.gen.go new file mode 100644 index 0000000000..8b11b6c1f9 --- /dev/null +++ b/internal/test/anonymous_inner_hoisting/global/client.gen.go @@ -0,0 +1,279 @@ +// Package global provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package global + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" +) + +// Role defines model for Role. +type Role struct { + Id int `json:"id"` + Name string `json:"name"` +} + +// SuccessfulResponse defines model for SuccessfulResponse. +type SuccessfulResponse struct { + // Data If successful, response from api + Data map[string]interface{} `json:"data"` + + // Ok Indicated whether the response is successful. + Ok bool `json:"ok"` +} + +// GetRolesId200JSONResponseBody_Data defines parameters for GetRolesId. +type GetRolesId200JSONResponseBody_Data struct { + Role Role `json:"role"` +} + +// GetRolesId200JSONResponseBody defines parameters for GetRolesId. +type GetRolesId200JSONResponseBody struct { + Data GetRolesId200JSONResponseBody_Data `json:"data"` + + // Ok Indicated whether the response is successful. + Ok bool `json:"ok"` +} + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetRolesId request + GetRolesId(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) GetRolesId(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRolesIdRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewGetRolesIdRequest generates requests for GetRolesId +func NewGetRolesIdRequest(server string, id int) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/roles/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetRolesIdWithResponse request + GetRolesIdWithResponse(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*GetRolesIdResponse, error) +} + +type GetRolesIdResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetRolesId200JSONResponseBody +} + +// Status returns HTTPResponse.Status +func (r GetRolesIdResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetRolesIdResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetRolesIdResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// GetRolesIdWithResponse request returning *GetRolesIdResponse +func (c *ClientWithResponses) GetRolesIdWithResponse(ctx context.Context, id int, reqEditors ...RequestEditorFn) (*GetRolesIdResponse, error) { + rsp, err := c.GetRolesId(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRolesIdResponse(rsp) +} + +// ParseGetRolesIdResponse parses an HTTP response from a GetRolesIdWithResponse call +func ParseGetRolesIdResponse(rsp *http.Response) (*GetRolesIdResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetRolesIdResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetRolesId200JSONResponseBody + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} diff --git a/internal/test/anonymous_inner_hoisting/global/client_test.go b/internal/test/anonymous_inner_hoisting/global/client_test.go new file mode 100644 index 0000000000..b6cf9fb9af --- /dev/null +++ b/internal/test/anonymous_inner_hoisting/global/client_test.go @@ -0,0 +1,44 @@ +package global + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestHoistedTypesExist verifies that with output-options.generate-types-for- +// anonymous-schemas enabled, the inline schemas in spec.yaml become named +// Go types we can reference directly. The spec is the canonical issue #1139 +// shape: a response body using `allOf` to merge a $ref with sibling +// `properties:` containing an inline `data` object. +func TestHoistedTypesExist(t *testing.T) { + // Both the response root and the nested inline `data` schema should be + // emitted as named types — assigning a typed zero value would not + // compile if either were still anonymous structs. + var responseBody GetRolesId200JSONResponseBody + var dataField GetRolesId200JSONResponseBody_Data + + // Field-level type identity: GetRolesId200JSONResponseBody.Data must be + // of the hoisted GetRolesId200JSONResponseBody_Data type. This + // assignment fails to compile if Data is still an anonymous struct. + responseBody.Data = dataField + _ = responseBody +} + +func TestHoistedTypesRoundTrip(t *testing.T) { + body := GetRolesId200JSONResponseBody{ + Data: GetRolesId200JSONResponseBody_Data{ + Role: Role{Id: 7, Name: "admin"}, + }, + Ok: true, + } + + encoded, err := json.Marshal(body) + require.NoError(t, err) + + var decoded GetRolesId200JSONResponseBody + require.NoError(t, json.Unmarshal(encoded, &decoded)) + assert.Equal(t, body, decoded) +} diff --git a/internal/test/anonymous_inner_hoisting/generate.go b/internal/test/anonymous_inner_hoisting/global/generate.go similarity index 76% rename from internal/test/anonymous_inner_hoisting/generate.go rename to internal/test/anonymous_inner_hoisting/global/generate.go index 19ff037202..50562013f5 100644 --- a/internal/test/anonymous_inner_hoisting/generate.go +++ b/internal/test/anonymous_inner_hoisting/global/generate.go @@ -1,3 +1,3 @@ -package anonymous_inner_hoisting +package global //go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=cfg.yaml spec.yaml diff --git a/internal/test/anonymous_inner_hoisting/global/spec.yaml b/internal/test/anonymous_inner_hoisting/global/spec.yaml new file mode 100644 index 0000000000..61796b5170 --- /dev/null +++ b/internal/test/anonymous_inner_hoisting/global/spec.yaml @@ -0,0 +1,58 @@ +openapi: "3.0.0" +info: + title: Test inline struct hoisting + version: "1.0" +paths: + /roles/{id}: + summary: Get role by id + get: + operationId: GetRolesId + tags: [ role ] + parameters: + - name: id + in: path + description: Role ID + required: true + schema: + type: integer + responses: + '200': + description: Role + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/SuccessfulResponse' + - properties: + data: + type: object + properties: + role: + $ref: '#/components/schemas/Role' + required: [ role ] + +components: + schemas: + SuccessfulResponse: + type: object + properties: + ok: + type: boolean + default: true + description: Indicated whether the response is successful. + data: + type: object + description: If successful, response from api + required: + - ok + - data + Role: + type: object + properties: + id: + type: integer + name: + type: string + required: + - id + - name diff --git a/internal/test/anonymous_inner_hoisting/cfg.yaml b/internal/test/anonymous_inner_hoisting/implicit/cfg.yaml similarity index 64% rename from internal/test/anonymous_inner_hoisting/cfg.yaml rename to internal/test/anonymous_inner_hoisting/implicit/cfg.yaml index bbdee926a3..b598a0f7c2 100644 --- a/internal/test/anonymous_inner_hoisting/cfg.yaml +++ b/internal/test/anonymous_inner_hoisting/implicit/cfg.yaml @@ -1,4 +1,4 @@ -package: anonymous_inner_hoisting +package: implicit output: client.gen.go generate: models: true diff --git a/internal/test/anonymous_inner_hoisting/client.gen.go b/internal/test/anonymous_inner_hoisting/implicit/client.gen.go similarity index 99% rename from internal/test/anonymous_inner_hoisting/client.gen.go rename to internal/test/anonymous_inner_hoisting/implicit/client.gen.go index 74fe6907c2..23d2b5fb93 100644 --- a/internal/test/anonymous_inner_hoisting/client.gen.go +++ b/internal/test/anonymous_inner_hoisting/implicit/client.gen.go @@ -1,7 +1,7 @@ -// Package anonymous_inner_hoisting provides primitives to interact with the openapi HTTP API. +// Package implicit provides primitives to interact with the openapi HTTP API. // // Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. -package anonymous_inner_hoisting +package implicit import ( "bytes" diff --git a/internal/test/anonymous_inner_hoisting/client_test.go b/internal/test/anonymous_inner_hoisting/implicit/client_test.go similarity index 99% rename from internal/test/anonymous_inner_hoisting/client_test.go rename to internal/test/anonymous_inner_hoisting/implicit/client_test.go index abdf532440..9090a5880a 100644 --- a/internal/test/anonymous_inner_hoisting/client_test.go +++ b/internal/test/anonymous_inner_hoisting/implicit/client_test.go @@ -1,4 +1,4 @@ -package anonymous_inner_hoisting +package implicit import ( "encoding/json" diff --git a/internal/test/anonymous_inner_hoisting/implicit/generate.go b/internal/test/anonymous_inner_hoisting/implicit/generate.go new file mode 100644 index 0000000000..6dab4e3b38 --- /dev/null +++ b/internal/test/anonymous_inner_hoisting/implicit/generate.go @@ -0,0 +1,3 @@ +package implicit + +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=cfg.yaml spec.yaml diff --git a/internal/test/anonymous_inner_hoisting/spec.yaml b/internal/test/anonymous_inner_hoisting/implicit/spec.yaml similarity index 100% rename from internal/test/anonymous_inner_hoisting/spec.yaml rename to internal/test/anonymous_inner_hoisting/implicit/spec.yaml diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go index f2cc4c0162..108cc2b440 100644 --- a/pkg/codegen/configuration.go +++ b/pkg/codegen/configuration.go @@ -389,6 +389,15 @@ type OutputOptions struct { // via x-go-name. ResolveTypeNameCollisions bool `yaml:"resolve-type-name-collisions,omitempty"` + // GenerateTypesForAnonymousSchemas, when true, causes oapi-codegen to + // emit a named Go type for every inline schema that would otherwise + // generate as an anonymous `struct { ... }`. The type's name is derived + // from the schema path (e.g. `GetRolesIdResponseBody_Data`). Default + // false. Equivalent to adding `x-go-type-name` to every inline schema; + // when both are present at the same site, `x-go-type-name` wins. + // See https://github.com/oapi-codegen/oapi-codegen/issues/1139 + GenerateTypesForAnonymousSchemas bool `yaml:"generate-types-for-anonymous-schemas,omitempty"` + // TypeMapping allows customizing OpenAPI type/format to Go type mappings. // User-specified mappings are merged on top of the defaults. TypeMapping *TypeMapping `yaml:"type-mapping,omitempty"` diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go index 94b6911c71..e3a869d13a 100644 --- a/pkg/codegen/operations.go +++ b/pkg/codegen/operations.go @@ -477,7 +477,8 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini // equivalent block in GenerateResponseDefinitions for // rationale. if !IsGoTypeReference(responseRef.Ref) && responseSchema.RefType == "" && - (len(responseSchema.UnionElements) != 0 || responseSchema.HasAdditionalProperties) { + (len(responseSchema.UnionElements) != 0 || responseSchema.HasAdditionalProperties || + (globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && len(responseSchema.Properties) > 0)) { if externalPkg := externalPackageFor(o.PathItemRef); externalPkg != "" { responseSchema.RefType = fmt.Sprintf("%s.%s", externalPkg, responseBodyTypeName) } else { @@ -1092,7 +1093,8 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena // the imported package generated the same hoisted name, so we // reference it instead of redeclaring locally. if !IsGoTypeReference(responseOrRef.Ref) && contentSchema.RefType == "" && - (len(contentSchema.UnionElements) != 0 || contentSchema.HasAdditionalProperties) { + (len(contentSchema.UnionElements) != 0 || contentSchema.HasAdditionalProperties || + (globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && len(contentSchema.Properties) > 0)) { if externalPkg != "" { contentSchema.RefType = fmt.Sprintf("%s.%s", externalPkg, responseBodyTypeName) } else { diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 2e216537f0..f9c3df3c3d 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -621,6 +621,33 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) { } } + // Auto-hoist anonymous inline schemas to a named type when the + // output-options.generate-types-for-anonymous-schemas option is set. + // Mirrors the x-go-type-name path above but with a path-derived name + // and a distinct named type (DefineViaAlias=false). Only fires for + // schemas nested inside another schema (len(path) > 1) — top-level + // component schemas already have their own names. Skipped if the + // schema is already a named ref or has just been wrapped by + // x-go-type-name (DefineViaAlias=true above). + // See https://github.com/oapi-codegen/oapi-codegen/issues/1139 + if globalState.options.OutputOptions.GenerateTypesForAnonymousSchemas && + len(path) > 1 && + outSchema.RefType == "" && + !outSchema.DefineViaAlias && + hasInlineStructuralContent(&outSchema) { + typeName := PathToTypeName(path) + typeDef := TypeDefinition{ + TypeName: typeName, + JsonName: strings.Join(path, "."), + Schema: outSchema, + } + outSchema = Schema{ + Description: typeDef.Schema.Description, + GoType: typeName, + AdditionalTypes: append(outSchema.AdditionalTypes, typeDef), + } + } + return outSchema, nil } else if len(schema.Enum) > 0 { err := oapiSchemaToGoType(schema, path, &outSchema) @@ -1068,3 +1095,17 @@ func hasStructuralSiblings(s *openapi3.Schema) bool { s.AdditionalProperties.Has != nil || s.AdditionalProperties.Schema != nil } + +// hasInlineStructuralContent reports whether a generated Schema is an +// anonymous inline that the generate-types-for-anonymous-schemas option +// should turn into a named type. Pure scalars and aliases-to-refs are +// excluded; objects with properties, schemas with additional-properties, +// and union schemas all qualify. +func hasInlineStructuralContent(s *Schema) bool { + if s == nil { + return false + } + return len(s.Properties) > 0 || + s.HasAdditionalProperties || + len(s.UnionElements) > 0 +} From f6c3b3111376b38943b3f85e5d1ced0d8f903999 Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Mon, 4 May 2026 09:44:34 -0700 Subject: [PATCH 2/4] Update internal/test/anonymous_inner_hoisting/global/spec.yaml Co-authored-by: Jamie Tanna --- internal/test/anonymous_inner_hoisting/global/spec.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/test/anonymous_inner_hoisting/global/spec.yaml b/internal/test/anonymous_inner_hoisting/global/spec.yaml index 61796b5170..884f9369f3 100644 --- a/internal/test/anonymous_inner_hoisting/global/spec.yaml +++ b/internal/test/anonymous_inner_hoisting/global/spec.yaml @@ -7,7 +7,8 @@ paths: summary: Get role by id get: operationId: GetRolesId - tags: [ role ] + tags: + - role parameters: - name: id in: path From 107232dab898ae2780c67ccf0e751d2c0bdd5d7d Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Mon, 4 May 2026 10:52:54 -0700 Subject: [PATCH 3/4] Update hoisted typedef comment --- pkg/codegen/schema.go | 11 +++++++++++ pkg/codegen/templates/param-types.tmpl | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index f9c3df3c3d..3e4fd3bc3d 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -286,6 +286,17 @@ func (t *TypeDefinition) IsAlias() bool { return !globalState.options.Compatibility.OldAliasing && t.Schema.DefineViaAlias } +// SchemaSourceForComment helps us build comments above each type definition, so +// that we can say where it came from. We could be hoisting an inline schema into +// a top level type, or generating one from a direct schema declaration, and it's +// nicer to keep this logic out of templates. +func (t *TypeDefinition) SchemaSourceForComment(operationID string) string { + if t.JsonName != "" && t.JsonName != t.TypeName { + return t.JsonName + } + return operationID +} + type Discriminator struct { // maps discriminator value to go type Mapping map[string]string diff --git a/pkg/codegen/templates/param-types.tmpl b/pkg/codegen/templates/param-types.tmpl index bb107b8f04..0d9538e6b7 100644 --- a/pkg/codegen/templates/param-types.tmpl +++ b/pkg/codegen/templates/param-types.tmpl @@ -1,6 +1,6 @@ {{range .}}{{$opid := .OperationId}} {{range .TypeDefinitions}} -// {{.TypeName}} defines parameters for {{$opid}}. +// {{.TypeName}} defines parameters for {{.SchemaSourceForComment $opid}}. type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}} {{end}} {{end}} From 476b896d2593d3c19d302222d817c3b2462b56dd Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Mon, 4 May 2026 11:30:34 -0700 Subject: [PATCH 4/4] Revert "Update hoisted typedef comment" This reverts commit 107232dab898ae2780c67ccf0e751d2c0bdd5d7d. --- pkg/codegen/schema.go | 11 ----------- pkg/codegen/templates/param-types.tmpl | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 3e4fd3bc3d..f9c3df3c3d 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -286,17 +286,6 @@ func (t *TypeDefinition) IsAlias() bool { return !globalState.options.Compatibility.OldAliasing && t.Schema.DefineViaAlias } -// SchemaSourceForComment helps us build comments above each type definition, so -// that we can say where it came from. We could be hoisting an inline schema into -// a top level type, or generating one from a direct schema declaration, and it's -// nicer to keep this logic out of templates. -func (t *TypeDefinition) SchemaSourceForComment(operationID string) string { - if t.JsonName != "" && t.JsonName != t.TypeName { - return t.JsonName - } - return operationID -} - type Discriminator struct { // maps discriminator value to go type Mapping map[string]string diff --git a/pkg/codegen/templates/param-types.tmpl b/pkg/codegen/templates/param-types.tmpl index 0d9538e6b7..bb107b8f04 100644 --- a/pkg/codegen/templates/param-types.tmpl +++ b/pkg/codegen/templates/param-types.tmpl @@ -1,6 +1,6 @@ {{range .}}{{$opid := .OperationId}} {{range .TypeDefinitions}} -// {{.TypeName}} defines parameters for {{.SchemaSourceForComment $opid}}. +// {{.TypeName}} defines parameters for {{$opid}}. type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}} {{end}} {{end}}