Closed11

oapi-codegenのx-go-type, x-go-type-name, x-go-nameの意味

podhmopodhmo

(追記: 実行環境は https://zenn.dev/link/comments/ab9079c1882e1e)

oapi-codegen用に使える x-<name> 形式 の属性の説明があるがわかりにくかった。

https://github.com/deepmap/oapi-codegen#extensions

- `x-go-type`: specifies Go type name. It allows you to specify the type name for a schema, and
  will override any default value. This extended property isn't supported in all parts of
  OpenAPI, so please refer to the spec as to where it's allowed. Swagger validation tools will
  flag incorrect usage of this property.
- `x-go-name`: specifies Go field name. It allows you to specify the field name for a schema, and
  will override any default value. This extended property isn't supported in all parts of
  OpenAPI, so please refer to the spec as to where it's allowed. Swagger validation tools will
  flag incorrect usage of this property.
- `x-go-type-name`: This property allows for assigning a Go type name to some part of a schema,
  such as generating a type name for an anonymous object inside another object, or renaming
  an enum. It differs from `x-go-type`, in that it doesn't completely replace some type reference,
  but simply names it.
podhmopodhmo

翻訳

oapi-codegenは次の拡張プロパティをサポートしています。

x-go-type: Go タイプ名を指定します。これにより、スキーマの型名を指定でき、デフォルト値がオーバーライドされます。この拡張プロパティは OpenAPI のすべての部分でサポートされているわけではないため、どこで許可されているかについては仕様を参照してください。Swagger 検証ツールは、このプロパティの不正な使用にフラグを立てます。

x-go-name: Go フィールド名を指定します。これにより、スキーマのフィールド名を指定でき、デフォルト値がオーバーライドされます。この拡張プロパティは OpenAPI のすべての部分でサポートされているわけではないため、どこで許可されているかについては仕様を参照してください。Swagger 検証ツールは、このプロパティの不正な使用にフラグを立てます。

x-go-type-name: このプロパティを使用すると、別のオブジェクト内の匿名オブジェクトの型名を生成したり、列挙型の名前を変更したりするなど、Go 型名をスキーマの一部に割り当てることができます。これはx-go-type、型参照を完全に置き換えるのではなく、単に名前を付けるだけであるという点で とは異なります。

x-go-json-ignore-: json 内のフィールドを完全に無視するには、タグを に設定します。

x-oapi-codegen-extra-tags: 生成された構造体フィールドに追加の Go フィールド タグを追加します。これは、タグベースの ORM または検証ライブラリとのインターフェースに役立ちます。追加される追加のタグは、生成される通常の json タグに追加されます。独自のjsonタグを指定すると、デフォルトのタグがオーバーライドされます。

podhmopodhmo

型名の指定にx-go-type-nameを使うとconflictする

x,y 共通で使う型をoapitypeとして出力して、type aliasで参照するような形で使っていた。ここで型名を指定するのに x-go-type-name を利用していた。

  • x/main.yaml -> oapigen/x/server.go
  • y/main.yaml -> oapigen/y/server.go
  • x/main.yaml, y/main.yaml -> common.yaml -> oapigen/oapitype/types.go

📝 ここで、x,yから適切にoapitypeをimportするために生成される型の名前は明示的に固定したい。

podhmopodhmo

ここで以下のようなcommon.yamlを生成して利用していた。

openapi: 3.0.3
info:
  title: api
  version: 0.0.0
paths:
  /_error:
    get:
      operationId: error
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error'
          description: OK
components:
  schemas:
    error:
      description: error response body
      type: object
      properties:
        code:
          type: string
        message:
          type: string
      x-go-type-name: Error   # 生成される型の名前を Error にしたい

以下のようなエラーが出る。どうやらtype aliasと生成される型名が衝突するらしい。

$ oapi-codegen --config tools/oapi-conf/type.yaml bundle3/common_types.yaml
2023/06/07 15:57:00 ----------------------------------------
2023/06/07 15:57:00 GenerateTypes() mismatch (-prevType +typ):
  codegen.TypeDefinition{
        TypeName: "Error",
-       JsonName: "error",
+       JsonName: "",
        Schema: codegen.Schema{
-               GoType: "Error",
+               GoType: (
+                       """
+                       struct {
+                           Code *string`json:"code,omitempty"`
+                           Message *string`json:"message,omitempty"`
+                       }
+                       """
+               ),
                RefType:    "",
                ArrayType:  nil,
                EnumValues: nil,
-               Properties: nil,
+               Properties: []codegen.Property{
+                       {
+                               JsonFieldName: "code",
+                               Schema:        codegen.Schema{GoType: "string", DefineViaAlias: true, OAPISchema: &openapi3.Schema{...}},
+                               Extensions:    map[string]any{},
+                       },
+                       {
+                               JsonFieldName: "message",
+                               Schema:        codegen.Schema{GoType: "string", DefineViaAlias: true, OAPISchema: &openapi3.Schema{...}},
+                               Extensions:    map[string]any{},
+                       },
+               },
                HasAdditionalProperties:  false,
-               AdditionalPropertiesType: nil,
+               AdditionalPropertiesType: &codegen.Schema{GoType: "interface{}"},
-               AdditionalTypes: []codegen.TypeDefinition{
-                       {
-                               TypeName: "Error",
-                               Schema: codegen.Schema{
-                                       GoType:                   "struct {\n    Code *string`json:\""...,
-                                       Properties:               []codegen.Property{...},
-                                       AdditionalPropertiesType: &codegen.Schema{...},
-                                       Description:              "error response body",
-                                       ...
-                               },
-                       },
-               },
+               AdditionalTypes:     nil,
                SkipOptionalPointer: false,
                Description:         "error response body",
                UnionElements:       nil,
                Discriminator:       nil,
-               DefineViaAlias:      true,
+               DefineViaAlias:      false,
-               OAPISchema:          nil,
+               OAPISchema: &openapi3.Schema{
+                       Extensions:  map[string]any{"x-go-type-name": string("Error")},
+                       Type:        "object",
+                       Description: "error response body",
+                       Properties: openapi3.Schemas{
+                               "code":    &{Value: &openapi3.Schema{...}},
+                               "message": &{Value: &openapi3.Schema{...}},
+                       },
+               },
        },
  }
2023/06/07 15:57:00 ----------------------------------------
error generating code: error generating type definitions: error generating code for type definitions: duplicate typename 'Error' detected, can't auto-rename, please use x-go-name to specify your own name for one of them

error generating code: error generating type definitions: error generating code for type definitions: duplicate typename 'Error' detected, can't auto-rename, please use x-go-name to specify your own name for one of them

これがエラーメッセージ。x-go-nameで指定してくれと言われているが、READMEを読んだときにはフィールド名の指定に利用されるように見えた。

podhmopodhmo

環境

  • go -- go version go1.19.9 darwin/amd64
  • oapi-codegen -- v1.13.0
podhmopodhmo

type.yaml

package: oapitype
generate:
  models: true
compatibility:
  always-prefix-enum-values: true
podhmopodhmo

x-go-type, x-go-type-name, x-go-name の意味

x-go-nameで固定できるように変わったのか。(なんでfield用のものなのに型に効くんだ)

  • x-go-type-nameの指定は生成される型名を固定するために使っていたような記憶(思い出す)

実験してみたところ以下のような形で動いていたようだった。

  • x-go-name 生成されるstructの型名を変更 (フィールドに指定する場合にはフィールド名を変更)
  • x-go-type-name その名前のaliasを設定
  • x-go-type その型を利用する

つまりこういうこと。

openapi: 3.0.3
info:
  title: api
  version: 0.0.0
paths:
  /_error:
    get:
      operationId: error
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error'
          description: OK
components:
  schemas:
    error:
      description: error response body
      type: object
      properties:
        code:
          type: string
          x-go-type: myuuid.UUID
          x-go-type-import:
            name: myuuid # myuuid "github.com/google/uuid" とqualified import
            path: github.com/google/uuid
        message:
          type: string
          x-go-name: MessageString  # field名の変更
          x-oapi-codegen-extra-tags:  # tagを追加
            tag1: value1
            json: message
            yaml: message
      x-go-name: XError  # 生成される型を type XError struct { ... } に
      x-go-type-name: YError  # 別名(type alias)を作成 type YError = XError

生成結果はこの通り

// Package oapitype provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
package oapitype

import (
	myuuid "github.com/google/uuid"
)

// XError error response body
type XError = YError

// YError error response body
type YError struct {
	Code          *myuuid.UUID `json:"code,omitempty"`
	MessageString *string      `json:"message" tag1:"value1" yaml:"message"`
}
podhmopodhmo

そんなに甘くない。 refと一緒に使うと期待しない結果になる

openapi: 3.0.3
info:
  title: api
  version: 0.0.0
paths:
  /_error:
    get:
      operationId: error
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error'
          description: OK
components:
  schemas:
    error:
      description: error response body
      type: object
      properties:
        createdAt:
          $ref: "#/components/schemas/datetime"
        updatedAt:
          $ref: "#/components/schemas/datetime"
    datetime:
      type: string
      format: date-time
      x-go-name: MyDateTime

これが以下の結果を返してしまう

// Package oapitype provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
package oapitype

import (
	"time"
)

// MyDateTime defines model for datetime.
type MyDateTime = time.Time

// Error error response body
type Error struct {
	MyDateTime *MyDateTime `json:"createdAt,omitempty"`
	MyDateTime *MyDateTime `json:"updatedAt,omitempty"`
}
podhmopodhmo

以下のようにyamlを変える。

openapi: 3.0.3
info:
  title: api
  version: 0.0.0
paths:
  /_error:
    get:
      operationId: error
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error'
          description: OK
components:
  schemas:
    error:
      description: error response body
      type: object
      properties:
        createdAt:
          $ref: "#/components/schemas/datetime"
        updatedAt:
          $ref: "#/components/schemas/datetime"
      x-go-type-name: MyError  # 追加
    datetime:
      type: string
      format: date-time
      x-go-name: MyDateTime
      x-go-type-name: DateTimeAlias  # 追加

出力結果

// Package oapitype provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.13.0 DO NOT EDIT.
package oapitype

import (
	"time"
)

// MyDateTime defines model for datetime.
type MyDateTime = time.Time

// Error error response body
type Error = MyError

// MyError error response body
type MyError struct {
	MyDateTime *MyDateTime `json:"createdAt,omitempty"`
	MyDateTime *MyDateTime `json:"updatedAt,omitempty"`
}
podhmopodhmo

結論

common側では生成される型の指定はせずに、利用する側(x,y)のところで直接明示的にimportする型名を指定する。

まとめ

  • 共通で利用したい型で型名の指定のために x-go-type-name を使うとconflictしてエラーになることがある。
    • x-go-name を使ってしまうと ref で参照したときにfield名も変わってしまう
      • x-go-nameは元々field名のためのものなのでそれはそう
    • 自動で生成される型名と同じ名前で指定しまうと死ぬらしい
このスクラップは2023/07/03にクローズされました