Help us understand the problem. What is going on with this article?

OpenAPI Generatorで自分好みのpythonクライアントライブラリを生成する方法

OpenAPI Generatorで自分好みのpythonクライアントライブラリを生成する方法

by yuji38kwmt
1 / 21

自己紹介

  • yuji38kwmt
  • 名古屋のAIの会社でAIやってないエンジニア

icon.png


WebAPIをどう実行していますか?

  • curlコマンドですか?
  • curlコマンドはつらくないですか?
  • pythonで実行したくないですか?

WebAPIをPythonでどう実行していますか?

  • APIドキュメントを見ながら、自分で実装していますか?
  • 似たようなコードがたくさんあって、飽きませんか?面倒でないですか?

あなたが実行したいWebAPIは何で定義されていますか?

  • OpenAPI Specification(OAS)ですか?
  • OASで定義されているなら、あなたは幸せです。なぜならOASからソースを自動生成できるツールがあるからです。

OpenAPI Specification(OAS)とは?

REST APIを記述するための言語です。
以下、「OAS」と略します。

The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for REST APIs,

https://github.com/OAI/OpenAPI-Specification 引用

image.png


Qiita API v2をOAS3で記述する

例として、Qiita APIのタグ一覧を返す APIをOASで表現します。
※QiitaはOASで記載されたファイルを公開していません。以下のファイルは非公式です。

image.png


qiita.yaml
openapi: "3.0.0"
info:
  version: 0.1.0
  title: Qiita API
servers:
  - url: https://qiita.com/api/v2
paths:
  /tags:
    get:
      tags:
      - tags
      summary: タグ一覧を作成日時の降順で返します。
      operationId: getTagList
      parameters:
        - name: page
          in: query
          description: ページ番号 (1から100まで)
          example: "1"
          schema:
            type: string
        - name: per_page
          in: query
          description: 1ページあたりに含まれる要素数 (1から100まで)
          example: "20"
          schema:
            type: string
        - name: sort
          in: query
          description: 並び順 (countで記事数順、nameで名前順)
          example: "count"
          schema:
            type: string
            enum:
            - count
            - name
      responses:
        200:
          description: タグ一覧
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Tag"
components:
  schemas:
    Tag:
      properties:
        followers_count:
          description: このタグをフォローしているユーザの数
          example: 100
          type: integer
        icon_url:
          description: このタグに設定されたアイコン画像のURL
          example: "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/9de6a11d330f5694820082438f88ccf4a1b289b2/medium.jpg"
          type: string
        id:
          description: タグを特定するための一意な名前
          example: "qiita"
          type: string
        items_count:
          description: このタグが付けられた記事の数
          example: 200
          type: integer

OpenAPI Generatorとは?

OASからAPIのクライアントライブラリやサーバのスタブ、ドキュメントなどを生成します。

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (both 2.0 and 3.0 are supported).

https://github.com/OpenAPITools/openapi-generator


OpenAPI Generatorが対応している言語/フレームワーク

Pythonクライアントライブラリや、Flaskのスタブが生成できます。
※マイナーな言語にも対応されています

image.png


とりあえずOpenAPI GeneratorでPythonクライアントライブラリを生成する

以下のコマンドを実行すると、qiita.yamlから生成されたPythonクライアントライブラリが、outディレクトリに出力されます。

$ 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ファイルだけでなく、ドキュメントやテストコード、設定ファイルなど、ライブラリに必要なファイル一式が生成されます。

$ tree -a
.
├── docs
│   ├── Tag.md
│   └── TagsApi.md
├── .gitignore
├── git_push.sh
├── openapi_client
│   ├── api
│   │   ├── __init__.py
│   │   └── tags_api.py
│   ├── api_client.py
│   ├── configuration.py
│   ├── exceptions.py
│   ├── __init__.py
│   ├── models
│   │   ├── __init__.py
│   │   └── tag.py
│   └── rest.py
├── .openapi-generator
│   └── VERSION
├── .openapi-generator-ignore
├── README.md
├── requirements.txt
├── setup.py
├── test
│   ├── __init__.py
│   ├── test_tag.py
│   └── test_tags_api.py
├── test-requirements.txt
├── tox.ini
└── .travis.yml

生成したクライアントライブラリを実行する

import openapi_client
from pprint import pprint

access_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
api_client = openapi_client.ApiClient(header_name="Authorization", header_value="Bearer " + access_token)

# タグの一覧を取得する
tags_api_instance = openapi_client.TagsApi(api_client)
tag_list = tags_api_instance.get_tag_list(per_page=15)
pprint(tag_list)
# [{'followers_count': 0, 'icon_url': None, 'id': 'Broadcas', #'items_count': 6},
# ...

自分好みにクライアントライブラリをカスタマイズする


カスタマイズしていないクライアントライブラリのイケていない部分

  • 型ヒントが使われていない(Python2, Python3.4以下をサポートしているため)
  • query parameterの数だけメソッドの引数が増える(query_parameterをまとめて渡したい)

テンプレートファイルを修正してカスタマイズする

OpenAPI Generatorは、mustache記法で記載されたテンプレートファイルを修正して、カスタマイズできます。

api.mustache
{{#operations}}
{{#operation}}

    def {{operationId}}(self, {{#sortParamsByRequiredFlag}}{{#pathParams}}{{paramName}}: {{dataType}}, {{/pathParams}}{{/sortParamsByRequiredFlag}}{{#hasQueryParams}}query_params: Optional[Dict[str, Any]] = None, {{/hasQueryParams}}{{#hasBodyParam}}request_body: Optional[Any] = None{{/hasBodyParam}}) -> Any:  # noqa: E501
        """
        {{#summary}}{{{.}}}{{/summary}}{{^summary}}{{operationId}}{{/summary}}  # noqa: E501
        """

        url_path = f'{{{path}}}'
        http_method = '{{httpMethod}}'
        keyword_params: Dict[str, Any] = {
{{#hasQueryParams}}
            'query_params': query_params,
{{/hasQueryParams}}
{{#hasBodyParam}}
            'request_body': request_body,
{{/hasBodyParam}}
        }
        return self._request_wrapper(http_method, url_path, **keyword_params)

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

mustacheとは?

  • mustacheはロジックがないテンプレート(Logic-less)
  • ロジックがないとは、if/elseやloopなどの制御構造がないこと

Mustache is described as a "logic-less" system because it lacks any explicit control flow statements, like if and else conditionals or for loops; however, both looping and conditional evaluation can be achieved using section tags processing lists and lambdas.

https://en.wikipedia.org/wiki/Mustache_(template_system) 引用


なぜmustache(口髭)という名前?

横向きの口髭に似ている波括弧をたくさん使うから。

image.png

It is named "Mustache" because of heavy use of braces, { }, that resemble a sideways moustache.

https://en.wikipedia.org/wiki/Mustache_(template_system) 引用


できあがったライブラリ

手動で生成したファイルと連結したら、sedで置換したりして、自分好みのライブラリを作成できました。

### Qiita APIすべてが定義されたクラスのインスタンスを生成
api_instance = QiitaApi(acess_token)

### タグの一覧を取得する
query_params = {"per_page":15, "sort": "name"}
tag_list = api_instance.get_tag_list(query_params=query_params)

まとめ

  • OpenAPI Generatorを使うと、OpenAPI Specification(OAS)でRESTが表現されたAPIのクライアントライブラリが生成できる
  • カスタマイズする場合は、msutache(口髭)ファイルを修正する

補足:自動生成は結構ハマる

完全に自動生成するのは難しい。
ハマった場合の対応方法は以下の3つかな?

  • 泥臭い手法を取る(sed, awk)
  • 自動生成ツールに手を入れる(contributorになる)
  • 諦める
Why do not you register as a user and use Qiita more conveniently?
  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
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