API Gateway の CORS 設定を swaggerで行う。

# 背景
定番の、AWS S3 + APIGateway の組み合わせでプライベートwebアプリ作成中。
API Gateway部分はローカルマシンで構築できないので（AWS Outpostsというサービスもあるようですが）、フロント開発ではローカルから APIGatewayへアクセスしたい。その時に必要になるのがCORS設定。
AWSコンソールから設定する事も出来ます。ただ、API部分はswaggerで設計しているので、swaggerの中で指定したいですね。

# 構成図と、今回関係する部分
![qiita_CORS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530376/b202dd82-4236-e07c-6c40-d3882acd6174.png)
この図の赤部分です。そのままだと、APIGatewayにアクセスできません。詳しくはググって欲しいですが、簡単に言うと勝手に外のサイトから引用されないようにする仕組み（だと思う）です。S3に正式にデプロイされた状態ではAPIもstaticリソースもCloudFrontドメインになるので問題ないですが、ローカル開発ではドメインが異なるのでAPIアクセスできない状態です。
（ちなみに、curlコマンドなどブラウザでない方法ではアクセス出来るので、CORS設定していなければセキュアになってる、とは言えなさそうです）

# 手順
1. AWSコンソールでAPIGatewayサービスへ移動し、対象のAPI選択
1. CORS設定前のAPIGateway情報をエクスポートしておく（バックアップにもなります）。
  - **ステージ** で、対象のステージを選択
  - 右ペインの **エクスポート** タブに移動。 **次の形式でエクスポート** で **swagger** を選択したうえで、**Swagger + API Gateway 拡張の形式でエクスポート** にホバーして、**YAML** を選択し、ファイル保存。
1. 対象エンドポイントの一つに対してAWSコンソールからCORS設定を実行
  - **リソース** で、対象のエンドポイントを一つ選択
  - **アクション** ボタンから **CORS の有効化** を実行
  - 同ボタンから **API のデプロイ** を実行。2で選択したstageを選択して **デプロイ**
1. 2と同様に **エクスポート** し、差分比較（git commitしておくか、保存時にファイル名変えるなど後で差分比較できるようにします）
1. swaggerエディタで他のエンドポイントに4で検出した差分を適用
1. APIGatewayの各エンドポイント-各メソッドの**統合リクエスト**にて**Lambda プロキシ統合の使用**を使用しているメソッドと連携しているlambda関数のレスポンスにヘッダ情報追加 （2019-11-10追記）

# 詳細
### 手順3を実行した時のメッセージ
```
Create OPTIONS method
Add 200 Method Response with Empty Response Model to OPTIONS method
Add Mock Integration to OPTIONS method
Add 200 Integration Response to OPTIONS method
Add Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin Method Response Headers to OPTIONS method
Add Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin Integration Response Header Mappings to OPTIONS method
Add Access-Control-Allow-Origin Method Response Header to GET method
Add Access-Control-Allow-Origin Integration Response Header Mapping to GET method
Add Access-Control-Allow-Origin Method Response Header to POST method
Add Access-Control-Allow-Origin Integration Response Header Mapping to POST method
```
##### 簡単に言うと以下の処理を行ったようです。
 - OPTIONSメソッドを追加して、必要なヘッダ設定を追加
 - そのエンドポイントにある他のGETやPOSTメソッドにヘッダ設定を追加

### 実際の差分
##### OPTION メソッド追加部分

```yaml
    options:
      consumes:
      - "application/json"
      produces:
      - "application/json"
      responses:
        200:
          description: "200 response"
          schema:
            $ref: "#/definitions/Empty"
          headers:
            Access-Control-Allow-Origin:
              type: "string"
            Access-Control-Allow-Methods:
              type: "string"
            Access-Control-Allow-Headers:
              type: "string"
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS,POST'"
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
              method.response.header.Access-Control-Allow-Origin: "'http://localhost:8080'"
        requestTemplates:
          application/json: "{\"statusCode\": 200}"
        passthroughBehavior: "when_no_templates"
        type: "mock"
```

＊Allow-Originなど少々AWSコンソール上で触った部分もあります。

##### GET,POSTなど既存メソッドに追加された部分
```yaml
      responses:
        200:
          description: "200 response"
          schema:
            $ref: "#/definitions/Hogehoge"
          # 以下部分が追加されました。
          headers:
            Access-Control-Allow-Origin:
              type: "string"
            Access-Control-Allow-Methods:
              type: "string"
            Access-Control-Allow-Headers:
              type: "string"
```

・・中略・・

```yaml
      x-amazon-apigateway-integration:
        uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:{ACCOUNT ID}:function:fugafuga-lambda-function/invocations"
        responses:
          default:
            statusCode: "200"
            # 以下部分が追加されました。
            responseParameters:
              method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS,POST'"
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
              method.response.header.Access-Control-Allow-Origin: "'http://localhost:8080'"

```

AWSコンソール上で言うと、各エンドポイント毎、各メソッドの**メソッドレスポンス** の **200 のレスポンスヘッダー** に３つのヘッダー設定が追加され（前者差分）、 **統合レスポンス** の 200レスポンスの設定内 **ヘッダーのマッピング** に３つのヘッダーが追加されている（後者差分）感じですね。

### lambda関数の戻り値にヘッダ情報追加 
APIGatewayの各エンドポイント-各メソッドの**統合リクエスト**にて**Lambda プロキシ統合の使用**を使用しているメソッドでは、**統合レスポンス**が設定できません。そのメソッドでは、lambda側でヘッダを指定する必要があります。

```py
    return {
        'statusCode': 200,
        'body': json.dumps(ret, indent=0),
        # この部分追加
        'headers': {
            'Access-Control-Allow-Headers' : 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
            'Access-Control-Allow-Methods' : 'GET,OPTIONS,POST',
            'Access-Control-Allow-Origin' : 'http://localhost:8080'
        },
        'isBase64Encoded': False
    }
```
(2019-11-10追記)

# 参考にさせてもらったサイト
<a href="https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-cors.html">AWS 公式サイト - API Gateway REST API リソースの CORS を有効にする</a>

# 反省
この記事書いてる最中に、よく探したら、ほぼ同じ事をOpenAPIで説明してる公式サイト発見。しかも上のリンクのすぐ近く。
<a href="https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/enable-cors-for-resource-using-swagger-importer-tool.html">AWS 公式サイト - API Gateway の API のインポートを使用してリソースで CORS を有効にする</a>

swaggerばっかり目が行ってましたが、OpenAPIはswaggerの拡張版（というかバージョンアップして名前変わった？）様ですね。
<a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md">GitHub - OpenAPI Specification</a>

とはいえ、膨大な公式ドキュメントを一度読んで吸収できる人はほんの一握りだと思います。多くの人は少しずつ理解領域を増やしていきその都度、公式ドキュメントを読んでいると想像します。没にする事も考えましたが、自分と同じような人の参考になる可能性も結構あると思い、投稿します。