Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,63 @@ You can see this in more detail in [the example code](examples/extensions/xorder
</td>
</tr>

<tr>
<td>

`x-oapi-codegen-only-honour-go-name`

</td>
<td>
Only honour the `x-go-name` when generating field names
</td>
<td>
<details>

> [!WARNING]
> Using this option may lead to cases where `oapi-codegen`'s rewriting of field names to prevent clashes with other types, or to prevent including characters that may not be valid Go field names.

In some cases, you may not want use the inbuilt options for converting an OpenAPI field name to a Go field name, such as the `name-normalizer: "ToCamelCaseWithInitialisms"`, and instead trust the name that you've defined for the type better.

In this case, you can use `x-oapi-codegen-only-honour-go-name` to enforce this, alongside specifying the `allow-unexported-struct-field-names` compatibility option.

This allows you to take a spec such as:

```yaml
openapi: "3.0.0"
info:
version: 1.0.0
title: x-oapi-codegen-only-honour-go-name
components:
schemas:
TypeWithUnexportedField:
description: A struct will be output where one of the fields is not exported
properties:
name:
type: string
id:
type: string
# NOTE that there is an explicit usage of a lowercase character
x-go-name: accountIdentifier
x-oapi-codegen-extra-tags:
json: "-"
x-oapi-codegen-only-honour-go-name: true
```

And we'll generate:

```go
// TypeWithUnexportedField A struct will be output where one of the fields is not exported
type TypeWithUnexportedField struct {
accountIdentifier *string `json:"-"`
Name *string `json:"name,omitempty"`
}
```

You can see this in more detail in [the example code](examples/extensions/xoapicodegenonlyhonourgoname).

</details>
</td>
</tr>

</table>

Expand Down
4 changes: 4 additions & 0 deletions configuration-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@
"circular-reference-limit": {
"type": "integer",
"description": "DEPRECATED: No longer used.\nCircularReferenceLimit allows controlling the limit for circular reference checking. In some OpenAPI specifications, we have a higher number of circular references than is allowed out-of-the-box, but can be tuned to allow traversing them."
},
"allow-unexported-struct-field-names": {
"type": "boolean",
"description": "AllowUnexportedStructFieldNames makes it possible to output structs that have fields that are unexported.\nThis is expected to be used in conjunction with an extension such as `x-go-name` to override the output name, and `x-oapi-codegen-extra-tags` to not produce JSON tags for `encoding/json`.\nNOTE that this can be confusing to users of your OpenAPI specification, who may see a field present and therefore be expecting to see it in the response, without understanding the nuance of how `oapi-codegen` generates the code."
}
}
},
Expand Down
18 changes: 18 additions & 0 deletions examples/extensions/xoapicodegenonlyhonourgoname/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: x-oapi-codegen-only-honour-go-name
components:
schemas:
TypeWithUnexportedField:
description: A struct will be output where one of the fields is not exported
properties:
name:
type: string
id:
type: string
# NOTE that there is an explicit usage of a lowercase character
x-go-name: accountIdentifier
x-oapi-codegen-extra-tags:
json: "-"
x-oapi-codegen-only-honour-go-name: true
10 changes: 10 additions & 0 deletions examples/extensions/xoapicodegenonlyhonourgoname/cfg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# yaml-language-server: $schema=../../../configuration-schema.json
package: xoapicodegenonlyhonourgoname
output: gen.go
generate:
models: true
output-options:
# to make sure that all types are generated, even if they're unreferenced
skip-prune: true
compatibility:
allow-unexported-struct-field-names: true
10 changes: 10 additions & 0 deletions examples/extensions/xoapicodegenonlyhonourgoname/gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions examples/extensions/xoapicodegenonlyhonourgoname/gen_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package xoapicodegenonlyhonourgoname

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/require"
)

func TestTypeWithUnexportedField(t *testing.T) {
var v TypeWithUnexportedField

err := json.Unmarshal([]byte(`{"id": "some-id"}`), &v)
require.NoError(t, err)

// this field will never be unmarshaled
require.Nil(t, v.accountIdentifier)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package xoapicodegenonlyhonourgoname

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
15 changes: 15 additions & 0 deletions pkg/codegen/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ type CompatibilityOptions struct {
// traversing them.
// Deprecated: In kin-openapi v0.126.0 (https://github.com/getkin/kin-openapi/tree/v0.126.0?tab=readme-ov-file#v01260) the Circular Reference Counter functionality was removed, instead resolving all references with backtracking, to avoid needing to provide a limit to reference counts.
CircularReferenceLimit int `yaml:"circular-reference-limit"`
// AllowUnexportedStructFieldNames makes it possible to output structs that have fields that are unexported.
//
// This is expected to be used in conjunction with `x-go-name` and `x-oapi-codegen-only-honour-go-name` to override the resulting output field name, and `x-oapi-codegen-extra-tags` to not produce JSON tags for `encoding/json`, such as:
//
// ```yaml
// id:
// type: string
// x-go-name: accountIdentifier
// x-oapi-codegen-extra-tags:
// json: "-"
// x-oapi-codegen-only-honour-go-name: true
// ```
//
// NOTE that this can be confusing to users of your OpenAPI specification, who may see a field present and therefore be expecting to see/use it in the request/response, without understanding the nuance of how `oapi-codegen` generates the code.
AllowUnexportedStructFieldNames bool `yaml:"allow-unexported-struct-field-names"`
}

// OutputOptions are used to modify the output code in some way.
Expand Down
11 changes: 11 additions & 0 deletions pkg/codegen/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const (
extEnumNames = "x-enumNames"
extDeprecationReason = "x-deprecated-reason"
extOrder = "x-order"
// extOapiCodegenOnlyHonourGoName is to be used to explicitly enforce the generation of a field as the `x-go-name` extension has describe it.
// This is intended to be used alongside the `allow-unexported-struct-field-names` Compatibility option
extOapiCodegenOnlyHonourGoName = "x-oapi-codegen-only-honour-go-name"
)

func extString(extPropValue interface{}) (string, error) {
Expand Down Expand Up @@ -100,3 +103,11 @@ func extParseEnumVarNames(extPropValue interface{}) ([]string, error) {
func extParseDeprecationReason(extPropValue interface{}) (string, error) {
return extString(extPropValue)
}

func extParseOapiCodegenOnlyHonourGoName(extPropValue interface{}) (bool, error) {
onlyHonourGoName, ok := extPropValue.(bool)
if !ok {
return false, fmt.Errorf("failed to convert type: %T", extPropValue)
}
return onlyHonourGoName, nil
}
10 changes: 10 additions & 0 deletions pkg/codegen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ func (p Property) GoFieldName() string {
}
}

if globalState.options.Compatibility.AllowUnexportedStructFieldNames {
if extension, ok := p.Extensions[extOapiCodegenOnlyHonourGoName]; ok {
if extOapiCodegenOnlyHonourGoName, err := extParseOapiCodegenOnlyHonourGoName(extension); err == nil {
if extOapiCodegenOnlyHonourGoName {
return goFieldName
}
}
}
}

return SchemaNameToTypeName(goFieldName)
}

Expand Down