python用にswagger-codegenしたgrapeのPOSTがうまく動かない時の対処法

# 背景
grapeでpython用にswagger-codegenしたのはいいけれどpostメソッドが上手く動いてくれないため、とても困りました。swagger_doc上では上手く動いてくれてのですが、pythonでリクエストを送っても、`is missing`だとか`body is invalid`とか、そんなエラーばかり返ってきました。同じエラーに引っかかる人はあまりいないと思いますが一応対処法を載せておきます。

# 問題点

まず初めに、swagger-codegenをgrape-railsでする場合は、Entityをちゃんと使ってあげる必要があります。`Entity`を使って、`nickname`でちゃんと名前を指定して、`pram_type`もちゃんと指定した上でおこったエラーなので、そこら辺を指定してない場合は、見直してみるといいかもしれません。またimportErrorの場合は「[pythonでswagger-codegenしてImportError: No module namedが出た時の対処法] (http://qiita.com/gamerinshaft/items/93fe3b31d041cdd4cec7)」の記事が役に立つかとおもいます。

実は、上記で述べた`param_type`っていうのが肝で、
`paramy_type`には、

- `param_type: "query"`
- `param_type: "body"`
- `param_type: "form"`
 
など、いくつかあります。pythonでswagger-codegenした時に、この`param_type`が指定してないパラメーターがある時、POSTメソッドに関してはデフォルトで`pram_type: "form"`が割り当てられてライブラリが生成されます。
しかし、その吐き出されたでライブラリでpython側からPOSTリクエストを送っても、Railsサーバー側はエラーを返すばかりで心を開いてくれませんでした。

仕方なしに、吐き出されたライブラリをいろいろいじってみると、
`form_params["param"] = "paramValue"`を、
`body_params["param"] = "paramValue"`のように書き換えると上手く動いてくれることがわかりました。

この`body_params`というのは、`param_type: "body"`と指定することで吐き出されるらしいので、試しに、requireしているパラメータすべてのparam_typeをbodyにしてみましたが、一番最後のパラメーターがbodyのタイプになるだけで、他のパラメータは`missing`のエラーがでました。
[swagger-grape](https://github.com/ruby-grape/grape-swagger)のドキュメントや、[example](https://github.com/swagger-api/rails-petstore)を読んでみるとわかりますが、どの例にも、複数のパラメーターに対して`param_type: "body"`を指定しているものはないんですよね。

そこで、swagger-codegenのテンプレート部分のコードを読んでみたんですが、原因が何かがわかりました。

```api.mustache

form_params = {}
files = {}

{{#formParams}}
        if '{{paramName}}' in params:
            {{#notFile}}form_params['{{baseName}}'] = params['{{paramName}}']{{/notFile}}{{#isFile}}files['{{baseName}}'] = params['{{paramName}}']{{/isFile}}
{{/formParams}}

body_params = None
{{#bodyParam}}
        if '{{paramName}}' in params:
            body_params = params['{{paramName}}']
{{/bodyParam}}
```

上のコードを読んでわかる通り、`form_params[""]`はハッシュなので複数の値が生成されるが、`base_params`はただ一つの変数を上書きしているだけです。これが最後に指定したひとつしかbody_paramsにならない、`is missing` の原因だったんですね。

# 解決法

body_paramsにパラメーターが一つ(または入れ子で)しか渡せないのは仕様なのかわかりませんが、今のAPIの構造を崩したくなかったし、ここでウダウダ悩んでも仕方ないので、テンプレートを書き換えることにしました。

`swagger-codegen`は実行時にカスタムテンプレートを引数で渡すことができます。

例えば、

`swagger-codegen generate ~ -t ./custom-template`

のように指定してあげれば、custom-templateフォルダ以下のファイルをテンプレートファイルとして読み込んでくれます。

pythonの場合は、[ここ](https://github.com/swagger-api/swagger-codegen/tree/master/modules/swagger-codegen/src/main/resources/python)からファイルをコピーしてcustom-template以下に配置しすればカスタムテンプレートとして利用できます。

今回は、`api.mastache`を以下のように変えてあげました。

```api.mustache
form_params = {}
files = {}
{{#formParams}}
        if '{{paramName}}' in params:
            {{#notFile}}form_params['{{baseName}}'] = params['{{paramName}}']{{/notFile}}{{#isFile}}files['{{baseName}}'] = params['{{paramName}}']{{/isFile}}
{{/formParams}}

body_params = None
{{#bodyParam}}
        if '{{paramName}}' in params:
            body_params = params['{{paramName}}']
{{/bodyParam}}

```

↓

```api.mustache
form_params = {}
files = {}
{{#formParams}}
        if '{{paramName}}' in params:
            # {{#notFile}}form_params['{{baseName}}'] = params['{{paramName}}']{{/notFile}}{{#isFile}}files['{{baseName}}'] = params['{{paramName}}']{{/isFile}}
{{/formParams}}

body_params = {}
{{#bodyParams}}
        if '{{paramName}}' in params:
            # body_params = params['{{paramName}}']
            body_params['{{baseName}}'] = params['{{paramName}}']
{{/bodyParams}}

```

あとは、Grapeの方を

```rb:api.rb
params :create_params do
      requires :auth_token, type: String, desc: 'Auth Token', documentation: { param_type: 'query' }
      requires :serial_number, type: String, desc: 'Serial Number', documentation: { param_type: 'body' }
      requires :ble_address, type: String, desc: 'BLE Address', documentation: { param_type: 'body' }
      requires :hardware_revision, type: String, desc: 'Hardware Revision', documentation: { param_type: 'body' }
      requires :factory_firmware_revision, type: String, desc: 'Factory Firmware Revision', documentation: { param_type: 'body' }
end
```
のように指定してあげれば、複数の値をbody_paramsとして渡すことができるようになります。
ベストではないと思うけれど、これで十分機能しているので、悩んでいる人は是非試してみてください。