2021年1月24日日曜日

flask + apispec を使って OpenAPI3 の定義ファイルを自動生成する

概要

過去に flasgger を使って Swagger2.0 の定義ファイルを作成する方法を紹介しました
しかし flasgger では OpenAPI3 に完全に対応しておらず components.schame など自動で出力してくれません
今回は apispec を使って OpenAPI3 の定義ファイルを自動生成してみました

環境

  • macOS 11.1
  • Python 3.8.7
    • apispec 4.0.0
    • apispec-webframeworks 0.5.2
    • flask 1.1.2
    • marshmallow 3.10.0

インストール

  • pipenv install apispec apispec-webframeworks flask marshmallow

サンプルコード

  • vim app.py
import uuid

from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
from flask import Flask
from marshmallow import Schema, fields


spec = APISpec(
    title="Swagger Petstore",
    version="1.0.0",
    openapi_version="3.0.2",
    plugins=[FlaskPlugin(), MarshmallowPlugin()],
)

class CategorySchema(Schema):
    id = fields.Int()
    name = fields.Str(required=True)


class PetSchema(Schema):
    categories = fields.List(fields.Nested(CategorySchema))
    name = fields.Str()


api_key_scheme = {"type": "apiKey", "in": "header", "name": "X-API-Key"}
spec.components.security_scheme("ApiKeyAuth", api_key_scheme)


app = Flask(__name__)


@app.route("/random")
def random_pet():
    """A cute furry animal endpoint.
    ---
    get:
      description: Get a random pet
      security:
        - ApiKeyAuth: []
      responses:
        200:
          description: Return a pet
          content:
            application/json:
              schema: PetSchema
    """
    pet_data = {
        "name": "sample_pet_" + str(uuid.uuid1()),
        "categories": [{"id": 1, "name": "sample_category"}],
    }
    return PetSchema().dump(pet_data)


with app.test_request_context():
    spec.path(view=random_pet)


if __name__ == "__main__":
    import json
    print(json.dumps(spec.to_dict(), indent=2))
    print(print(spec.to_yaml()))
    app.run(debug=True)
  • pipenv run python app.py

or

  • FLASK_APP=app.py pipenv run flask run

説明

基本は APISpec を作成してこれに必要な属性を追加していく感じです
今回は Flask + Marshmallow と連携するので plugins でそれぞれのプラグインを追加しています
components.schemas はデフォルトで表示してくれますが components.securitySchemes は表示してくれないので spec.components.security_scheme で追加しています

Flask のルーティングのコメント部分に各パスのパラメータやレスポンスの定義を直接記載します

MethodView を使う

MethodView を使うとルーティングをクラスとして管理することができます
こちらの方が管理しやすくなると思います

  • vim app.py
import uuid

from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
from flask import Flask
from flask.views import MethodView
from marshmallow import Schema, fields


spec = APISpec(
    title="Swagger Petstore",
    version="1.0.0",
    openapi_version="3.0.2",
    plugins=[FlaskPlugin(), MarshmallowPlugin()],
)

class CategorySchema(Schema):
    id = fields.Int()
    name = fields.Str(required=True)


class PetSchema(Schema):
    categories = fields.List(fields.Nested(CategorySchema))
    name = fields.Str()


api_key_scheme = {"type": "apiKey", "in": "header", "name": "X-API-Key"}
spec.components.security_scheme("ApiKeyAuth", api_key_scheme)


app = Flask(__name__)


class RandomPet(MethodView):
    def get(self):
        """A cute furry animal endpoint.
        ---
        description: Get a random pet
        security:
        - ApiKeyAuth: []
        responses:
          200:
            description: Return a pet
            content:
              application/json:
                schema: PetSchema
        """
        pet_data = {
            "name": "sample_pet_" + str(uuid.uuid1()),
            "categories": [{"id": 1, "name": "sample_category"}],
            }
        return PetSchema().dump(pet_data)


random_pet_view = RandomPet.as_view("random")
app.add_url_rule(
    "/random",
    view_func=random_pet_view
)
with app.test_request_context():
    spec.path(view=random_pet_view)


if __name__ == "__main__":
    import json
    print(json.dumps(spec.to_dict(), indent=2))
    print(print(spec.to_yaml()))
    app.run(debug=True)
  • pipenv run python app.py

最後に

apispec と flask を組み合わせて OpenAPI3 のドキュメントを flask 内で定義し自動生成する方法を紹介しました
今回はすべて 1 つのファイルに記載しましたが MVC は分割して管理したほうが良いかなと思います
OpenAPI3 の定義ファイルも今回は標準出力に出しているだけなので、別のメインファイルを作成してそちらで spec の情報をファイルに出力するようにしても良いかなと思います

参考サイト

0 件のコメント:

コメントを投稿