OpenAPI と Swagger

間違いや不備ございましたらご指摘いただけますと幸いです。

#OpenAPIとは

>OpenAPI仕様は、RESTful Webサービスを記述、生成、消費、および視覚化するための機械可読インターフェースファイルの仕様です
参考:Wikipedia

つまり、REST APIの仕様を記述できるもの

JSONやYAMLで記述できます。

#Swaggerとは

OpenAPIを設計、構築、文書化、使用するためのツールらのこと

ツールらということで、様々ある。

 - Swagger Editor(API仕様を記述するエディタ)
 - Swagger UI(記述したAPI仕様からHTML生成するツール)
 - Swagger Codegen(API仕様からスタブを自動生成するツール)が提供されている。

サードパーティツールも多くある。

[参考](https://future-architect.github.io/articles/20191008/)

OpenAPIの仕様については、下記を参照する。(`/versions`から必要なバージョンのドキュメントを閲覧する。)
https://github.com/OAI/OpenAPI-Specification

#Swaggerを使ってみる

とりあえず書いてみる
今回はSwaggerEditorを使いyamlでかきます
(VSCode拡張のSwaggerViewerでも、サードパーティのでも良い)

##OpenAPIのフォーマット
まずは下記を記述

```yaml:openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /hello:
    get:
      responses:
        '200':
          description: 挨拶の返却
          content:
            application/json:
              schema:
               type: string
               example: Hello World
```

`openapi`でバージョンを指定
`info`にメタ情報を指定します。`title`と`version`は必須です。
`paths`にリクエストするパスを指定し、どんなメソッドのときに、どういうレスポンスを返すか指定します。

![chap](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174757/285bb235-2dd4-4bb3-631d-e3c6eba25b65.png)


プレビューすると上記の画像のようになります。

一つ一つ詳細を見ていきます。

### メタ情報 - info
`info`オブジェクトはAPIのメタ情報を定義します。

```yaml
title: Sample Pet Store App
summary: A pet store manager.
description: This is a sample server for a pet store.
termsOfService: https://example.com/terms/
contact:
  name: API Support
  url: https://www.example.com/support
  email: support@example.com
license:
  name: Apache 2.0
  url: https://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.1
```
Field Name | Type | Description
---|:---:|---
title | `string` | 必須. APIのタイトル。
summary | `string` | APIの簡単な要約。
description | `string` | APIの詳細な説明。
termsOfService | `string` | APIの利用規約へのURL。URLの形式である必要があります。
contact | Contact Object | 公開されたAPIの連絡先情報。
license | License Object | 公開されたAPIのライセンス情報。
version | `string` | 必須。このOpenAPIドキュメントのバージョン

上記の情報を設定できます。

[詳細はこちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#info-object)

### パス - paths
`paths`オブジェクトは、パス、メソッド、操作が指定できます。

```yaml:openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /hello:
    get:
      responses:
        '200':
          description: 挨拶の返却
          content:
            application/json:
              schema:
               type: string
               example: Hello World
```

`/hello`がパスで、`get`がメソッド、それ以下が操作オブジェクトになります。

操作オブジェクトには、メタデータ、リクエストパラメータ、リクエストボディ、レスポンス、セキュリティ等を記述することができます。

#### メタデータ
まずはメタデータについて見ていきます。

```yaml:openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /hello:
    get:
      summary: ハローワールド
      description: ハローワールドを取得できます。
      tags: 
        - hello
      deprecated: false
      responses:
        '200':
          description: 挨拶の返却
          content:
            application/json:
              schema:
               type: string
               example: Hello World
```

`summary`、`description`、`tags`、`deprecated`等を記述できます。

Field Name | Type | Description
---|:---:|---
summary | `string` | 操作の実行内容の簡単な要約。
description | `string` | 操作動作の詳細な説明。
tags | [string] | APIドキュメント制御用のタグのリスト。タグは、リソースまたはその他の修飾子による操作の論理グループ化に使用できます。
deprecated | boolean | 非推奨になることを宣言します。宣言された操作の使用を控えるべきです。

詳細は[こちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#operation-object)。

#### リクエストパラメータ
リクエストパラメータについて見ていきます

リクエストパラメータには下記4つのようなものが定義できます。

 - パスパラメータ(`/users/{id}`)
 - クエリパラメータ(`/users?role=admin`)
 - ヘッダーパラメータ(`X-MyHeader: Value`)
 - Cookieヘッダーで渡されるCookieパラメータ(`Cookie: debug=0;``csrftoken=BUSe35dohU3O1MZvDCU`)

クエリパラメータをつけると、言語を切り替えられるパスを例に見ていきます

```yaml:openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /hello:
    get:
      summary: ハローワールド
      description: 言語ごとのハローワールドを取得できます。
      tags: 
        - hello
      deprecated: false

      parameters:
        - name: lang
          description: 言語。en/jaが指定できます。
          in: query
          required: false
          schema:
            type: string
            example: ja
      
      responses:
        '200':
          description: 挨拶の返却
          content:
            application/json:
              schema:
               type: string
               example: Hello World
```

`parameters`に配列(`-`)で定義します。
`name`でパラメータ名を指定し、`in`でどこのパラメータか、必須か、スキーマを定義しています。

上記で定義しているものは下記になります。

Field Name | Type | Description
---|:---:|---
name | `string` | 必須。リクエストパラメータ名を指定します。
in | `string` | 必須。`path`、`query`、`header`、`cookie`のいずれかを指定する。
required | boolean | このパラメーターが必須かどうかを判別します。パラメータの場所が`path`の場合、このプロパティは必須であり、その値は`true`である必要があります。それ以外の場合、プロパティを含めることができ、デフォルト値はfalseです。
schema | schema | パラメータに使用されるタイプを定義するスキーマ。


![スクリーンショット 2020-11-15 17.01.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174757/26adeed8-6146-9f45-1138-580235573f8e.png)


その他の詳細は[こちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#fixed-fields-10)

#### リクエストボディ

新しい言語でハローワールドを作成できるAPIを例に見ていきましょう

```yaml:openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /hello:
    post:
      requestBody:
        description: ハローワールドの新規作成
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lang: 
                  type: string
                  example: fr
                hello:
                  type: string
                  example: Bonjour le monde
          
      responses:
        '201':
          description: 作成成功
```

requestBody内に定義します。
説明を記述し、必須か、`content`にリクエストボディの詳細を記述していきます。
`application/json`でコンテントタイプを指定し、スキーマを定義しています。

objectを指定し、そのオブジェクトの詳細を`properties`で定義しています。

![スクリーンショット 2020-11-15 16.59.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174757/451daf3b-5cdb-fb94-c15d-b77db6355c85.png)


詳細は[こちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#request-body-examples)

#### レスポンス

どのようなレスポンスが帰ってくるか記述します。

```yaml:openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /hello:
    post:
      requestBody:
        description: ハローワールドの新規作成
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lang: 
                  type: string
                  example: fr
                hello:
                  type: string
                  example: Bonjour le monde
          
      responses:
        '201':
          description: 作成成功
        '400':
          description: Client Error
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: string
                    example: 400
                  message:
                    type: string
                    example: Client Error
                  errors: 
                    type: array
                    items:
                      type: object
                      properties:
                        field:
                          type: string
                          example: lang
                        message:
                          type: string
                          example: Unknown Country
```

`responses`内に定義します。
ステータスコードを記述し、その中に詳細を記述していきます。
`4XX`などの複数を想定した指定も可能です。

ステータスコード内に説明や、返却されるもののスキーマを定義しています。

![スクリーンショット 2020-11-15 16.57.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174757/17a477d6-46fd-e571-8332-b9cf5ddafc25.png)

詳細は[こちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object)

#### セキュリティ

OpenAPIで定義可能なセキュリティは下記です。

- http: Basic、Bearer(JWT)
- apiKey: header(APIキーの利用)、cookie(ログインセッション)
- oauth2
- openIdConnect

```openapi.yaml
openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0

components:
  securitySchemes:
    sampleJwt:
      description: JWT Auth
      type: http
      scheme: bearer
      bearerFormat: JWT
    
    sampleBasic:
      description: Basic Auth
      type: http
      scheme: basic
      
    sampleApiKey:
      description: API Key Auth
      type: apiKey
      in: header
      name: X-API-KEY
      
    sampleCookie:
      description: Session Auth
      type: apiKey
      in: cookie
      name: SESSION_ID
      
    
paths:
  /hello:
    get:
      summary: ハローワールド
      description: 言語ごとのハローワールドを取得できます。
      tags: 
        - hello
      deprecated: false

      parameters:
        - name: lang
          description: 言語。en/jaが指定できます。
          in: query
          required: false
          schema:
            type: string
            example: ja
      
      responses:
        '200':
          description: 挨拶の返却
          content:
            application/json:
              schema:
               type: string
               example: Hello World
      security: 
        - sampleJwt: []
          sampleBasic: []
          sampleApiKey: []
          sampleCookie: []
```

セキュリティはコンポーネントというものを用いて定義します

>コンポーネントオブジェクト
さまざまな側面で再利用可能なオブジェクトのセットを保持します。コンポーネントオブジェクト内で定義されたすべてのオブジェクトは、コンポーネントオブジェクト外のプロパティから明示的に参照されない限り、APIに影響を与えません。
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#components-object

`securitySchemes`内に使用したいセキュリティを作成し、`security`オブジェクト内に定義します。
`sampleJwt: []`のように空の配列の場合、スコープがないことを示しています。

----

ここまでて基本的なOpenAPIの仕様について記述したので、後はドキュメントでどうにかできるかなという感じに思いました。
後は実際に使ってみたいですね