This post is Private. Only a writer or those who know its URL can access this post.

Article information
Show article in Markdown
Report article
Help us understand the problem. What is going on with this article?

OpenAPI Generatorを使ったきっかけ、ハマったことなど

OpenAPI Generatorを使ったきっかけ、ハマったことなど

by yuji38kwmt
1 / 23

自己紹介

  • pythonで雑ツール作っています


参考にしたスライド

https://qiita.com/yuji38kwmt/items/dfb929316a1335a161c0
https://qiita.com/yuji38kwmt/items/ad89fa0b8690627fea0f
https://github.com/kurusugawa-computer/annofab-api-python-client


 話すこと

  • OpenAPI Generatorを使ったきっかけ
  • デフォルトのPythonクライアントコードでは使いづらかった部分
  • OpenAPI Generatorでハマったことなど

OpenAPI Generatorを使ったきっかけ


背景

  • 弊社で提供している「AnnoFab」というWebサービスのWebAPIを、Pythonで利用したい

image.png

https://annofab.com/docs/api/#operation/getInputData


OpenAPI Generatorを使う前の状態

WebAPIを利用しやすくするため、WebAPIごとにメソッドを作成していた。

def get_input_data(project_id: str, input_data_id: str):
    pass

def put_input_data(project_id: str, input_data_id: str, request_body: Dict[str, Any]):
    pass

...

手動でコーディングしていると、WebAPIの変更に追従していけなくなる

  • パスパラメータの増減に対応できない
  • WebAPIの追加/削除に対応できない

自動生成するために、OpenAPI Generatorを使った

  • WebAPIのドキュメントは、OpenAPI Specを使ってReDocで生成されている。
  • すでにOAS3で記載されたファイルがあったので、これを使ってPythonでWebAPIを実行するライブラリを作成した。

とりあえずPythonのクライントコードを出力してみる


生成コマンド

$ docker run --rm -u `id -u`:`id -g` -v ${PWD}:/local openapitools/openapi-generator-cli \
 generate \
 --input-spec /local/qiita.yaml \
 --generator-name python \
 --output /local/out

デフォルトの状態だと使いづらいと感じた部分


型ヒントが使えない

  • 型ヒントとは、Pythonに型情報を与える記法
  • Pythonの古いバージョンにも対応しているため、型ヒントが使えない
    • ライブラリはPython 2.7と3.4以上に対応している。
    • 2020/03時点で最新は3.8。
    • Python2系は2020/04にサポート終了
    • Python3.4は2019/05にサポート終了
# 型ヒントがない状態
def get_input_data(project_id, input_data_id):
    pass
    return {}

# 型ヒントがある状態
def get_input_data(project_id: str, input_data_id: str) -> Dict[str, Any]:
    pass
    return {}

やっぱり型があると嬉しい。


メソッドの引数が多くなってしまう

  • path parameter:1つずつ引数に渡す
  • query parameter:1つずつ引数に渡す
paths:
  /{country_id}/pets/:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: country_id
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            format: int32
        - name: name
          in: query
          required: true
          schema:
            type: string
api_response = api_instance.list_pets(country_id, name, limit=limit)
  • 検索条件をquery parameterで表現するAPIは、メソッド引数が多くなる
  • 感覚的ではあるが、path parameterとquery parameterは意識したい

連番を含むクラスが作成される

request_body, reponseのスキーマを$refを使わずに直接定義すると、InlineObjectXXXInlineResponseXXXのように連番XXXを含むクラスが作成される。

      responses:
        '200':
          description: Expected response to a valid request
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    format: int64
                  name:
                    type: string
                  tag:
                    type: string

連番が含まれるクラス名を使うのはビミョー。
※yaml側を修正すればよいんだけど直接定義している部分がいくつかある


OASのtagsごとにファイルが分けられる

paths:
  /pets:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets

弊社ツールでは、WebAPIのtagsがたまに変更されることがあるので、できるだけtagsに依存したくない。


自分好みのPythonクライアントライブラリを作る

https://github.com/kurusugawa-computer/annofab-api-python-client/blob/master/generate/template/api.mustache

  • APIに対応したメソッドを一つのクラスにまとめる。OASのtagsごとにファイル/クラスを分けない。
    • OASのtagsを意識しなくても、APIを利用できるようにするため
  • メソッド引数のrequest_bodyと、メソッドの戻り値はdictにする。
    • 連番を含むquery_parameterを使わないようにするため
  • query_paramsdictでメソッドに渡す。
    • メソッド引数が増えすぎないようにするため
  • できるだけ型ヒントを使う。

ハマったこと/ツラかったこと


mustacheファイルに記載されている変数の意味が分からない

  • ある程度は推測できるけど確証はないので、デバッグオプションで確認した
{{#operations}}
class {{classname}}(object):

    def __init__(self, api_client=None):
        if api_client is None:
            api_client = ApiClient()
        self.api_client = api_client
{{#operation}}

    def {{operationId}}(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs):  # noqa: E501
        """{{#summary}}{{{.}}}{{/summary}}{{^summary}}{{operationId}}{{/summary}}  # noqa: E501

        ### 処理を省略

{{/operation}}
{{/operations}}

mustacheファイルで記載するのツラい

  • 制御構造がない
  • {{#xxx}}という表記がif分岐なのか、for-eachなのか判断するには、変数名から予測するしかない
  • 見づらい

Handlebarなら書きやすい?


外部ファイルの参照がうまくいかない

petstore-parent.yaml
      responses:
        '200':
          description: Expected response to a valid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pets"
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  $ref: "./petstore-child.yaml"
petstore-child.yaml
schemas:
  Pet:
    required:
      - id
      - name
    properties:
      id:
        type: integer
        format: int64
      name:
        type: string
      tag:
        type: string

皆さんにききたいこと

  • クライアントコードをカスタマイズせずに使っています?
  • mustacheファイルで使える変数はどうやって調べています?
yuji38kwmt
愛知のIT企業で修行しております。2018年4月に転職しました。 基本的に自分用のメモとして、記事を書いております。 所属先の見解とは一切関係ありません。 https://qiita.com/yuji38kwmt/items/a474ad97e0d86f6081a2
kurusugawa
「いいソフトウェアを楽に作る」技術を追求する企業。今は、機械学習、画像認識中心。
http://kurusugawa.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした