API Gatewayのrequest validationをserverless frameworkでdeployする

## Api Gateway request validation

api gatewayにはrequest validationという検証機能があります。もちろんapi gatewayに付けるlambda等でリクエストの検証をすることも可能ですが、lambdaにはビジネスロジックだけを含めたい、という場合には便利な機能です。

## やり方

オフィシャルのserverless frameworkにはrequest validationのデプロイは含まれていないので、`serverless-reqvalidator-plugin`と`serverless-aws-documentation`を組み合わせてデプロイを実現します

最終的なコードをgitにuploadしています 
https://github.com/five510/serverless-python-sample

a今回使ったplugin
https://github.com/deliveryhero/serverless-aws-documentation
https://www.npmjs.com/package/serverless-reqvalidator-plugin

今回は記事の登録をするapiを題材に、`author`,`contents`,`title`実装をしてみます。

### 1. documentationとmodelを作成
serverless.ymlに以下のように記載

```yml
service: serverless-blog-api 
plugins:
  - serverless-reqvalidator-plugin
  - serverless-aws-documentation

custom:
  documentation:
    api:
      info:
        version: '1.0.0'
        title: Aritcle API
        description: This is article API
      tags:
        -
          name: Article
          description: Article Management
    models:
      - name: articleModel
        contentType: "application/json"
        schema: ${file(model/article.json)}
```
model/article.jsonにjson schema形式で(https://json-schema.org/ )でmodelを記載

```json

{
    "title": "articleModel",
    "type": "object",
    "properties": {
        "author": {
            "type": "string"
        },
        "contents": {
            "type": "string"
        },
        "title": {
            "type": "string"
        }
    },
    "required": ["author", "contents","title"]
}
```

### 2. serverless.yml内のResourceでRequestValidatorを作る
今回はrequest bodyの検証のみ有効にしています

```yml
resources:
  Resources:
    # APIG
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        Name: article-api
    # APIG Request Validation
    onlyBody:  
      Type: "AWS::ApiGateway::RequestValidator"
      Properties:
        Name: 'only-body'
        RestApiId: 
          Ref: ApiGatewayRestApi
        ValidateRequestBody: true
        ValidateRequestParameters: false
```
### 3. functionにrequestModelsとしてRequestValidatorをつける
serverless.ymlに以下のように記載

```yml
functions:
  postArticle:
    handler: handlers/article.put_article_handler
    name: postArticle
    memorySize: 1024
    timeout: 300
    events:
      - http:
          path: /article
          method: post
          integration: lambda-proxy
          reqValidatorName: 'onlyBody'
          cors:
            origin: '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - Pragma
              - Cache-Control
              - If-Modified-Since
            allowCredentials: true
          documentation:
            summary: "Post a article"
            description: "Post a article"
            requestModels:
              "application/json": "articleModel"
```

### 4. 対応するlambdaのhandlerを書く

```py
import json

def put_article_handler(event,context):
  article_info = json.loads(event['body'])
  author = article_info['author']
  contents = article_info['contents']
  title = article_info['title']
  return {
            "isBase64Encoded": True,
            "statusCode": 200,
            "headers": { 
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Credentials': 'true'
            },
            "body": json.dumps({'msg': 'ok'})
        }
```

### 5. デプロイする
おなじみのやつ

```
sls deploy
```

## validationが効いているか確認する

api gateway>リソース>テスト
![スクリーンショット 2019-05-17 0.07.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114135/4c17d069-32a5-7ef1-4380-ec3586b3c6e1.png)

bodyに何も含めずテストをするとエラーが返ってくることが確認できます
![スクリーンショット 2019-05-17 0.10.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114135/68927a0b-52c4-deb0-5049-13efb26a7a44.png)


## テストどうするんだ問題
validationをlambda内部で持っている場合はhandlerに異常系のパラメーターを入れていたが、request validationを使う場合は異常系のテストはe2e testで補う必要がありそう。その場合はローカル開発環境でテストが完結しないのでstaging環境ありきの開発になる。微妙。