SwaggerでREST APIを生成する

# SwaggerでREST APIを生成する

## はじめに

`macOS環境の記事ですが、Windows環境も同じ手順になります。環境依存の部分は読み替えてお試しください。`

### 目的

この記事を最後まで読むと、次のことができるようになります。

| No.  | 概要                              | 備考         |
| :--- | :-------------------------------- | :----------- |
| 1    | Swaggerを理解する                 |              |
| 2    | Swagger EditorでAPIを設計する     |              |
| 3    | Swagger CodegenでAPIを生成する    | python-flask |
| 4    | Swagger UIで仕様/定義を可視化する |              |
| 5    | Generated APIを検証する           |              |

### 実行環境

| 環境           | Ver.     |
| :------------- | :------- |
| macOS Catalina | 10.15.6  |
| Swagger        | 3.0.3    |
| Docker         | 19.03.13 |

### 関連する記事

- [Swagger](https://swagger.io/)

## Swaggerを理解する

### 特徴

- REST APIを構築するためのオープンソースフレームワーク
- YAML/JSONでAPIを設計
- 設計をもとにAPIドキュメントの生成が可能
- 設計をもとに20以上の言語からスタブの生成が可能

### API Tools

| No.             | 概要                                                               | 備考                             |
| :-------------- | :----------------------------------------------------------------- | :------------------------------- |
| Swagger Editor  | APIを設計するためのツール                                          |                                  |
| Swagger Codegen | 設計からサーバ向けスタブ/クライアント向けSDKを生成するためのツール | Generate Server, Generate Client |
| Swagger UI      | 設計から仕様/定義を可視化(ドキュメント化)するためのツール          | Generate Server                  |

## Swagger EditorでAPIを設計する

### Case1: Swagger Editor (Web)

#### 1. Swagger Editor表示

- [https://editor.swagger.io/](https://editor.swagger.io/)

### Case2: Swagger Editor (Docker)

#### 1. Swagger Editor起動

- [https://hub.docker.com/r/swaggerapi/swagger-editor/](https://hub.docker.com/r/swaggerapi/swagger-editor/)

```command.sh
docker stop $(docker ps -q)
docker rm $(docker ps -q -a)
docker rmi $(docker images -q) -f
docker pull swaggerapi/swagger-editor
docker run -d -p 80:8080 swaggerapi/swagger-editor
```

*今回は`PORT:80`に割り当てる

#### 2. Swagger Editor表示

- [http://localhost:80](http://localhost:80)

### YAML - Hello World

```hello_world.yaml
openapi: 3.0.3
info:
  title: Hello World
  description: Sample API for trying out Swagger
  termsOfService: http://localhost/termsOfService
  contact:
    email: na010210dv@gmail.com
  license:
    name: nsuhara
    url: http://localhost/license
  version: 0.0.1
externalDocs:
  description: Find out more about Swagger
  url: http://swagger.io
servers:
- url: http://localhost:8080
- url: http://localhost:80
tags:
- name: hello
  description: Greeting API
  externalDocs:
    description: Find out more about hello
    url: http://localhost/hello
- name: xxx
  description: xxx
  externalDocs:
    description: xxx
    url: http://localhost/xxx
paths:
  /hello:
    get:
      tags:
      - hello
      summary: Return 'Hello, {your_name}'
      description: Return 'Hello, {your_name}'
      operationId: get_hello
      parameters:
      - name: your_name
        in: query
        description: The name displayed with Hello
        required: true
        schema:
          type: string
      responses:
        200:
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Hello'
            application/xml:
              schema:
                $ref: '#/components/schemas/Hello'
        400:
          description: Invalid parameters
          content: {}
        404:
          description: Not found
          content: {}
  /xxx:
    post:
      tags:
      - xxx
      summary: xxx
      operationId: post_xxx
      requestBody:
        description: xxx
        content:
          '*/*':
            schema:
              type: array
              items:
                $ref: '#/components/schemas/Xxx'
        required: true
      responses:
        default:
          description: Successful operation
          content: {}
      x-codegen-request-body-name: body
    get:
      tags:
      - xxx
      summary: xxx
      description: xxx
      operationId: get_xxx
      parameters:
      - name: xxx
        in: query
        description: xxx
        required: true
        schema:
          type: string
      responses:
        200:
          description: Successful operation
          content: {}
        400:
          description: Invalid parameters
          content: {}
        404:
          description: Not found
          content: {}
components:
  schemas:
    Hello:
      type: object
      properties:
        result:
          type: string
          example: Hello, {your_name}
      xml:
        name: Hello
    Xxx:
      type: object
      properties:
        id:
          type: integer
          format: int64
        xxx:
          type: string
      xml:
        name: Xxx
  securitySchemes:
    petstore_auth:
      type: oauth2
      flows:
        implicit:
          authorizationUrl: http://petstore.swagger.io/oauth/dialog
          scopes:
            write:pets: modify pets in your account
            read:pets: read your pets
    api_key:
      type: apiKey
      name: api_key
      in: header
```

## Swagger CodegenでAPIを生成する

Swagger Editor > Generate Server > python-flask

*今回は`python-flask`環境を生成する

## Swagger UIで仕様/定義を可視化する

### 1. zip解凍

- python-flask-server-generated.zip

### 2. requirements編集 (connexion問題)

- python-flask-server-generated/requirements.txt

```requirements.txt
- connexion
+ connexion[swagger-ui]
python_dateutil == 2.6.0
setuptools >= 21.0.0
```

### 3. Swagger UI起動

- python-flask-server-generated/README.md

```command.sh
docker build -t swagger_server .
docker run -p 8080:8080 swagger_server
```

*今回は`PORT:8080`に割り当てる

### 4. Swagger UI表示

- [http://localhost:8080/ui/](http://localhost:8080/ui/)

## Generated APIを検証する

### 1. ビジネスロジック追加

- python-flask-server-generated/swagger_server/controllers/hello_controller.py

```hello_controller.py
+ import json

import connexion
import six

from swagger_server.models.hello import Hello  # noqa: E501
from swagger_server import util


def get_hello(your_name):  # noqa: E501
    """Return &#x27;Hello, {your_name}&#x27;

    Return &#x27;Hello, {your_name}&#x27; # noqa: E501

    :param your_name: The name displayed with Hello
    :type your_name: str

    :rtype: Hello
    """
-    return 'do some magic!'
+    return json.dumps({
+        'result': 'Hello, {}'.format(your_name)
+    })
```

### 2. ビジネスロジック検証 (Web browser)

```command.sh
open "http://localhost:8080/hello?your_name=nsuhara"
```

```result.sh
"{\"result\": \"Hello, nsuhara\"}"
```

### 3. ビジネスロジックを検証 (Curl)

```command.sh
curl -X GET "http://localhost:8080/hello?your_name=nsuhara" -H "accept: application/json"
```

```result.sh
"{\"result\": \"Hello, nsuhara\"}"
```