Abstract

半年ほど前に OpenAPI Generator を使う機会があったので、思い出しながらまとめておきます。

まず、OpenAPIとは、RESTful APIを記述するためのフォーマットのことです。

スキーマ駆動開発で使われたりします。
スキーマ駆動開発は、「先にスキーマを定義し、フロントエンドの開発着手を早めることで、全体的な開発速度向上の効果が期待できる開発手法」のことです。
OpenAPI Generator はそのスキーマを定義して、ソースを自動生成するための一つのツールです。
OpenAPIと比較されるものとして他には、gRPCGraphQL などがあります。それらについては、エンジニアHubの記事で比較されています。

備考:スキーマ駆動開発(SDD)は、2015年7月に掲載された論文Schema-Driven Development of Semantic MediaWikisが元ネタで、現在では、DeNAやFreeeなどが採用しているようです。

また、Open APISwagger は基本的には同じなのですが、別のプロジェクトとして開発されています。 経緯がややこしいのですが、SpeakerDeckの平静を保ち、コードを生成せよ 〜 OpenAPI Generator誕生の背景と軌跡 〜で、詳しく説明されているので、気になる方はそちらを参考にしてください。

前置きが長くなってしまいましたが、本記事では、Spring Frameworkを使う前提で、Open API ver3.0 について触れます。

備考:OpenAPI Generator で使える言語やフレームワークは、公式サイトに記載されています。

1. What you need to use the Open API

必要なものは以下です。

  • APIを定義するYAMLファイル
  • YAMLファイルからソースコード/ドキュメントを生成するための設定ファイル

1.1. YAML FILE

YAMLファイルに定義するものは、主に以下の3つです。

  • メタ情報
    • info
    • servers
    • security
  • エンドポイント
    • paths
  • コンポーネント
    • schemas
    • responses
    • parameters
    • examples
    • requestBodies
    • headers
    • links
    • callbacks
    • securitySchemes

userstore.yaml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
openapi: 3.0.0
info:
  title: Sample API
  description: A short description of API.
  termsOfService: http://example.com/terms/
  contact:
    name: API support
    url: http://www.example.com/support
    email: support@example.com
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.0
servers:
  - url: http://localhost:8080/v1
    description: Development server
paths:
  /users:
    get:
      tags:
        - users
      summary: Get all users.
      description: Returns an array of User model
      parameters: []
      responses:
        '200':
          description: A JSON array of User model
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
                example:
                  - id: 1
                    name: John Doe
                  - id: 2
                    name: Jane Doe
    post:
      tags:
        - users
      summary: Create a new User
      description: Create a new User
      parameters: []
      requestBody:
        description: user to create
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'
            example:
              id: 3
              name: Richard Roe
      responses:
        '201':
          description: CREATED
  /users/{userId}:
    get:
      tags:
        - users
      summary: Get user by ID.
      description: Returns a single User model
      parameters:
        - name: userId
          in: path
          description: user id
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: A single User model
          content:
            application/json:
              schema:
                type: object
                items:
                  $ref: '#/components/schemas/User'
                example:
                  id: 1
                  name: John Doe
  /products:
    get:
      tags:
        - products
      summary: Get all products.
      description: Returns an array of Product model
      parameters: []
      responses:
        '200':
          description: A JSON array of Product model
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Product'
                example:
                  - id: 1
                    name: Apple
                    price: 200
                  - id: 2
                    name: Orange
                    price: 150
components:
  schemas:
    User:
      type: object
      required:
        - id
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
    Product:
      type: object
      required:
        - id
        - price
      properties:
        id:
          type: integer
          format: int64
          example: 1
        name:
          type: string
          example: Laptop
        price:
          type: integer
          example: 1200
security:
  - api_key: []
tags:
  - name: users
    description: Access to Users
  - name: products
    description: Access to Products

1.2. Gradle Setting File

OpenAPI を Gradle で使うにあたって、Openapi-generator-gradle-plugin の公式サイトが参考になります。

また、Gradleプロパティは、以下の設定にしています。
単に、build.gradle にある設定値を一元管理するためです。

1
2
openApiVersion=v1
sampleStoreService=userstore

1.2.1. Generate Server Side File

ConfigOptions の設定により、生成されるソースが変更されます。
openapi-generator/spring.md にSpringの場合の全オプションについて記載されています。

また、supportingFiles の設定により、出力されるソースを制限することができます。

備考:
後から分かったのですが、.openapi-generator-ignore の設定により、出力対象を制限することができるようです。
参考URL : OpenAPI-Generator Customization

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
task openApiGenerateSampleServer(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask) {
    validateSpec = true
    generatorName = "spring"
    inputSpec = "$projectDir/specs/${sampleStoreService}.yaml"
    outputDir = "$projectDir"
    apiPackage = "openapi.sample.${sampleStoreService}.webapi.${openApiVersion}.api"
    modelPackage = "openapi.sample.${sampleStoreService}.webapi.${openApiVersion}.model"
    generateModelTests = false
    generateApiTests = false
    generateModelDocumentation = false
    generateApiDocumentation = false

    // Spring Option Doc
    // https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/spring.md
    configOptions = [
            title                  : "userstore",
            groupId                : "openapi",
            delegatePattern        : "true",
            dateLibrary            : "java8",
            hideGenerationTimestamp: "true",
            serializableModel      : "true",
            returnSuccessCode      : "true"
    ]
    systemProperties = [
            apis           : "",
            models         : "",
            supportingFiles: "ApiUtil.java"
    ]
}

1.2.2. Generate Client Side File

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
task openApiGenerateSampleClient(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask) {
    validateSpec = true
    generatorName = "java"
    inputSpec = "$projectDir/specs/${sampleStoreService}.yaml"
    outputDir = "$projectDir"
    apiPackage = "openapi.sample.${sampleStoreService}.clientapi.${openApiVersion}.api"
    modelPackage = "openapi.sample.${sampleStoreService}.clientapi.${openApiVersion}.model"
    generateModelTests = false
    generateApiTests = false
    generateModelDocumentation = false
    generateApiDocumentation = false

    // Java Option Doc
    // https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/java.md
    configOptions = [
            title                  : "userstore",
            groupId                : "openapi",
            delegatePattern        : "true",
            dateLibrary            : "java8",
            hideGenerationTimestamp: "true",
            serializableModel      : "true",
            library                : "resttemplate"
    ]
    systemProperties = [
            apis           : "",
            models         : "",
            supportingFiles: "ApiClient.java,Authentication.java,HttpBasicAuth.java,HttpBearerAuth.java,ApiKeyAuth.java,RFC3339DateFormat.java"
    ]
}

1.2.3. Generate API Documents

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
task openApiGenerateHtml(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask) {
    validateSpec = true
    generatorName = "html2"
    inputSpec = "$projectDir/specs/${sampleStoreService}.yaml"
    outputDir = "$projectDir/doc"
    invokerPackage = "openapi.sample.${sampleStoreService}.clientapi.${openApiVersion}"

    // HTML2 Option Doc
    // https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/html2.md

    // 以下のイシューが対応されるまで、Schemaは空で出力されてしまいます。
    // https://github.com/OpenAPITools/openapi-generator/issues/3788
}

2. Note

2.1. 3rd Party Document Generation Tool

Open APIで、ドキュメントを生成する際は、個人的には公式の方法よりもReDocがおすすめです。
リンクを張っておきますが、こんな感じで見れて、めっちゃいい感じです。

公式の方法で、html として出力すると、以下の画像ようになります。 (少し殺風景な感じです。

3. Refference