自己紹介
- yuji38kwmt
- 名古屋のAIの会社でAIやってないエンジニア
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 引用
Qiita API v2をOAS3で記述する
例として、Qiita APIのタグ一覧を返す APIをOASで表現します。
※QiitaはOASで記載されたファイルを公開していません。以下のファイルは非公式です。
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).
OpenAPI Generatorが対応している言語/フレームワーク
Pythonクライアントライブラリや、Flaskのスタブが生成できます。
※マイナーな言語にも対応されています
とりあえず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記法で記載されたテンプレートファイルを修正して、カスタマイズできます。
{{#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(口髭)という名前?
横向きの口髭に似ている波括弧をたくさん使うから。
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(access_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になる)
- 諦める