pyramid-swagger-routerと一緒にtoyboxのswaggerのvalidationを使ってみる
相も変わらずtoyboxというリポジトリで作業していた。昨日試しに作ってみたswaggerのinput,outputのvalidationを行うものを昔作っていた pyramid-swagger-router と一緒に使ってみるようにしてみた。ココまで来るとそろそろswaggerに対するnormalizer + iteratorみたいなものが欲しくなってくる。
動くサンプルは ここ
使い方
だいたい以下のようなMakefileを使うという感じで察して欲しい(yapfのコメントアウトはASTのmergeに使うredbaronというpackageのバグを踏んでしまっていたので。yapfはgofmtのpython版みたいなもの)。
gen: spec router schema # fmt: # yapf -r -i --style='{based_on_style: chromium, indent_width: 4}' app spec: cp ../swagger/swagger.yml . gsed -i 's/operationId: /operationId: views./g' swagger.yml router: pyramid-swagger-router --driver=./driver.py:Driver --logging=DEBUG ./swagger.yml app schema: swagger-marshmallow-codegen --full --logging=DEBUG ./swagger.yml > app/schema.py run: PYTHONPATH=. python app/__init__.py
このようにするとだいたい以下のようなコードが生成される。 使うswagger specは 昨日のものと同じもの
routes.py
def includeme_swagger_router(config): config.add_route('views', '/') config.add_route('views1', '/add') config.add_route('views2', '/dateadd') config.scan('.views') def includeme(config): config.include(includeme_swagger_router)
views.py
from pyramid.view import ( view_config ) from . import ( schema ) from toybox.swagger import ( withswagger ) @view_config(decorator=withswagger(schema.Input, schema.Output), renderer='vjson', request_method='GET', route_name='views') def hello(context, request): """ request.GET: * 'name' - `{"type": "string", "example": "Ada", "default": "Friend"}` """ return {} @view_config(decorator=withswagger(schema.AddInput, schema.AddOutput), renderer='vjson', request_method='POST', route_name='views1') def add(context, request): """ request.json_body: ``` { "type": "object", "properties": { "x": { "type": "integer" }, "y": { "type": "integer" } }, "required": [ "x", "y" ] } ``` """ return {} @view_config(decorator=withswagger(schema.DateaddInput, schema.DateaddOutput), renderer='vjson', request_method='POST', route_name='views2') def dateadd(context, request): """ request.json_body: ``` { "type": "object", "properties": { "value": { "type": "string", "format": "date" }, "addend": { "minimum": 1, "type": "integer" }, "unit": { "type": "string", "default": "days", "enum": [ "days", "minutes" ] } }, "required": [ "addend" ] } ``` """ return {}
init.pyは自分で書かなければダメ。
# -*- coding:utf-8 -*- from toybox.simpleapi import run def includeme(config): config.include("toybox.swagger") config.include("app.routes") config.scan("app.views") if __name__ == "__main__": run.include(includeme) run(port=5001)
以下の事が自動で行われる
- routingの定義
- viewの内部のformat validation
もちろんroutingができるだけなので内部のviewは自分で実装してあげないとダメ。
diff --git a/examples/swagger2/app/views.py b/examples/swagger2/app/views.py index a531b8c..85b5a96 100644 --- a/examples/swagger2/app/views.py +++ b/examples/swagger2/app/views.py @@ -1,3 +1,4 @@ +from datetime import datetime, timedelta from pyramid.view import ( view_config ) @@ -17,7 +18,7 @@ def hello(context, request): * 'name' - `{"type": "string", "example": "Ada", "default": "Friend"}` """ - return {} + return {'message': 'Welcome, {}!'.format(request.GET["name"])} @view_config(decorator=withswagger(schema.AddInput, schema.AddOutput), renderer='vjson', request_method='POST', route_name='views1') @@ -45,7 +46,9 @@ def add(context, request): } ``` """ - return {} + x = request.json["x"] + y = request.json["y"] + return {"result": x + y} @view_config(decorator=withswagger(schema.DateaddInput, schema.DateaddOutput), renderer='vjson', request_method='POST', route_name='views2') @@ -82,4 +85,13 @@ def dateadd(context, request): } ``` """ - return {} \ No newline at end of file + value = request.json["value"] + addend = request.json["addend"] + unit = request.json["unit"] + value = value or datetime.utcnow() + if unit == 'minutes': + delta = timedelta(minutes=addend) + else: + delta = timedelta(days=addend) + result = value + delta + return {'result': result}
pyramid-swagger-routerについて
昔記事書いていた