Giter Club home page Giter Club logo

aiohttp-apispec's Introduction

aiohttp-apispec

Build and document REST APIs with aiohttp and apispec

Pypi Contributors Downloads

build status [docs] [codcov] Code style: black

aiohttp-apispec key features:

  • docs and request_schema decorators to add swagger spec support out of the box;
  • validation_middleware middleware to enable validating with marshmallow schemas from those decorators;
  • SwaggerUI support.
  • New from version 2.0 - match_info_schema, querystring_schema, form_schema, json_schema, headers_schema and cookies_schema decorators for specific request parts validation. Look here for more info.

aiohttp-apispec api is fully inspired by flask-apispec library

Version 3.0.0b1 with apispec>=5.0 webargs>=8.0 is in beta now (pip install aiohttp-apispec==3.0.0b1).

Contents

Install

pip install aiohttp-apispec

Quickstart

Also you can read blog post about quickstart with aiohttp-apispec

from aiohttp_apispec import (
    docs,
    request_schema,
    setup_aiohttp_apispec,
)
from aiohttp import web
from marshmallow import Schema, fields


class RequestSchema(Schema):
    id = fields.Int()
    name = fields.Str(description="name")

@docs(
    tags=["mytag"],
    summary="Test method summary",
    description="Test method description",
)
@request_schema(RequestSchema(strict=True))
async def index(request):
    return web.json_response({"msg": "done", "data": {}})


app = web.Application()
app.router.add_post("/v1/test", index)

# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(
    app=app, 
    title="My Documentation", 
    version="v1",
    url="/api/docs/swagger.json",
    swagger_path="/api/docs",
)

# Now we can find spec on 'http://localhost:8080/api/docs/swagger.json'
# and docs on 'http://localhost:8080/api/docs'
web.run_app(app)

Class based views are also supported:

class TheView(web.View):
    @docs(
        tags=["mytag"],
        summary="View method summary",
        description="View method description",
    )
    @request_schema(RequestSchema(strict=True))
    @response_schema(ResponseSchema(), 200)
    def delete(self):
        return web.json_response(
            {"msg": "done", "data": {"name": self.request["data"]["name"]}}
        )


app.router.add_view("/v1/view", TheView)

As alternative you can add responses info to docs decorator, which is more compact way. And it allows you not to use schemas for responses documentation:

@docs(
    tags=["mytag"],
    summary="Test method summary",
    description="Test method description",
    responses={
        200: {
            "schema": ResponseSchema,
            "description": "Success response",
        },  # regular response
        404: {"description": "Not found"},  # responses without schema
        422: {"description": "Validation error"},
    },
)
@request_schema(RequestSchema(strict=True))
async def index(request):
    return web.json_response({"msg": "done", "data": {}})

Adding validation middleware

from aiohttp_apispec import validation_middleware

...

app.middlewares.append(validation_middleware)

Now you can access all validated data in route from request['data'] like so:

@docs(
    tags=["mytag"],
    summary="Test method summary",
    description="Test method description",
)
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema, 200)
async def index(request):
    uid = request["data"]["id"]
    name = request["data"]["name"]
    return web.json_response(
        {"msg": "done", "data": {"info": f"name - {name}, id - {uid}"}}
    )

You can change Request's 'data' param to another with request_data_name argument of setup_aiohttp_apispec function:

setup_aiohttp_apispec(
    app=app,
    request_data_name="validated_data",
)

...


@request_schema(RequestSchema(strict=True))
async def index(request):
    uid = request["validated_data"]["id"]
    ...

Also you can do it for specific view using put_into parameter (beginning from version 2.0):

@request_schema(RequestSchema(strict=True), put_into="validated_data")
async def index(request):
    uid = request["validated_data"]["id"]
    ...

More decorators

Starting from version 2.0 you can use shortenings for documenting and validating specific request parts like cookies, headers etc using those decorators:

Decorator name Default put_into param
match_info_schema match_info
querystring_schema querystring
form_schema form
json_schema json
headers_schema headers
cookies_schema cookies

And example:

@docs(
    tags=["users"],
    summary="Create new user",
    description="Add new user to our toy database",
    responses={
        200: {"description": "Ok. User created", "schema": OkResponse},
        401: {"description": "Unauthorized"},
        422: {"description": "Validation error"},
        500: {"description": "Server error"},
    },
)
@headers_schema(AuthHeaders)  # <- schema for headers validation
@json_schema(UserMeta)  # <- schema for json body validation
@querystring_schema(UserParams)  # <- schema for querystring params validation
async def create_user(request: web.Request):
    headers = request["headers"]  # <- validated headers!
    json_data = request["json"]  # <- validated json!
    query_params = request["querystring"]  # <- validated querystring!
    ...

Custom error handling

If you want to catch validation errors by yourself you could use error_callback parameter and create your custom error handler. Note that it can be one of coroutine or callable and it should have interface exactly like in examples below:

from marshmallow import ValidationError, Schema
from aiohttp import web
from typing import Optional, Mapping, NoReturn


def my_error_handler(
    error: ValidationError,
    req: web.Request,
    schema: Schema,
    error_status_code: Optional[int] = None,
    error_headers: Optional[Mapping[str, str]] = None,
) -> NoReturn:
    raise web.HTTPBadRequest(
            body=json.dumps(error.messages),
            headers=error_headers,
            content_type="application/json",
        )

setup_aiohttp_apispec(app, error_callback=my_error_handler)

Also you can create your own exceptions and create regular Request in middleware like so:

class MyException(Exception):
    def __init__(self, message):
        self.message = message

# It can be coroutine as well:
async def my_error_handler(
    error, req, schema, error_status_code, error_headers
):
    await req.app["db"].do_smth()  # So you can use some async stuff
    raise MyException({"errors": error.messages, "text": "Oops"})

# This middleware will handle your own exceptions:
@web.middleware
async def intercept_error(request, handler):
    try:
        return await handler(request)
    except MyException as e:
        return web.json_response(e.message, status=400)


setup_aiohttp_apispec(app, error_callback=my_error_handler)

# Do not forget to add your own middleware before validation_middleware
app.middlewares.extend([intercept_error, validation_middleware])

Build swagger web client

3.X SwaggerUI version

Just add swagger_path parameter to setup_aiohttp_apispec function.

For example:

setup_aiohttp_apispec(app, swagger_path="/docs")

Then go to /docs and see awesome SwaggerUI

2.X SwaggerUI version

If you prefer older version you can use aiohttp_swagger library. aiohttp-apispec adds swagger_dict parameter to aiohttp web application after initialization (with setup_aiohttp_apispec function). So you can use it easily like:

from aiohttp_apispec import setup_aiohttp_apispec
from aiohttp_swagger import setup_swagger


def create_app(app):
    setup_aiohttp_apispec(app)

    async def swagger(app):
        setup_swagger(
            app=app, swagger_url="/api/doc", swagger_info=app["swagger_dict"]
        )

    app.on_startup.append(swagger)
    # now we can access swagger client on '/api/doc' url
    ...
    return app

Versioning

This software follows Semantic Versioning.


Please star this repository if this project helped you!

aiohttp-apispec's People

Contributors

abondar avatar agronholm avatar aivanf avatar alvarolopez avatar alvassin avatar alwxsin avatar bobdevries avatar borisrozumnuk avatar diego-plan9 avatar ellmetha avatar hyzyla avatar igorkud90 avatar khrebtukov avatar kiforchuk avatar kurganskyvladimir avatar mat-work avatar maximdanilchenko avatar mirrorrim avatar niccolum avatar ondrejkajinek avatar pierrecdn avatar pzhang65 avatar selimb avatar shvechikov avatar slymit avatar truepack avatar ttyridal avatar yurkovoznyak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

aiohttp-apispec's Issues

date is not JSON serializable when using

Hi there. Using the example from this repo's README and adding a Date field in the schema with a validator using a date as range min value, I'm getting the following exception when loading the swagger UI page:

Failed to load API definition.
Errors
Fetch error
Internal Server Error /api/docs/swagger.json

From app logs:

# python bug.py 
======== Running on http://0.0.0.0:8081 ========
(Press CTRL+C to quit)
Error handling request
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/usr/local/lib/python3.6/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.6/site-packages/aiohttp_apispec/aiohttp_apispec.py", line 57, in swagger_handler
    return web.json_response(request.app["swagger_dict"])
  File "/usr/local/lib/python3.6/site-packages/aiohttp/web_response.py", line 715, in json_response
    text = dumps(data)
  File "/usr/local/lib/python3.6/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/local/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'date' is not JSON serializable

bug.py source:

from aiohttp_apispec import (
    docs,
    request_schema,
    setup_aiohttp_apispec,
)
from aiohttp import web
from marshmallow import Schema, fields, validate
from datetime import date


class RequestSchema(Schema):
    id = fields.Int()
    name = fields.Str(description="name")
    my_date = fields.Date(validate=validate.Range(min=date.today()))

@docs(
    tags=["mytag"],
    summary="Test method summary",
    description="Test method description",
)
@request_schema(RequestSchema(strict=True))
async def index(request):
    return web.json_response({"msg": "done", "data": {}})


app = web.Application()
app.router.add_post("/v1/test", index)

# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(
    app=app,
    title="My Documentation",
    version="v1",
    url="/api/docs/swagger.json",
    swagger_path="/api/docs",
)

# Now we can find spec on 'http://localhost:8080/api/docs/swagger.json'
# and docs on 'http://localhost:8080/api/docs'
web.run_app(app, port=8081)

If I replace the validation min value with something else other than a date object, The swagger UI page works fine.

BTW your package is awesome, thank you 🙇‍♂️

Missing git tags

Hello, it appears that after 1.5.0 you started not pushing git tags anymore? Could you start doing it again? Thanks.

Clarify the Initialization Before Setting Up aiohttp_swagger

When I read the last section of building a swagger web page, I had completely no idea of what do you mean by initialization and why I got a KeyError on app['swagger_dict']. After spending 20~mins on jumping around and reading the source code of aiohttp_swagger, I finally understood you actually mean setup_aiohttp_apispec.

Please make it clear to noobs😕

aiohttp-apispec adds swagger_dict parameter to aiohttp web application after initialization. So you can use it easily with aiohttp_swagger library:
from aiohttp_swagger import setup_swagger

...

async def swagger(app):
    setup_swagger(
        app=app, swagger_url='/api/doc', swagger_info=app['swagger_dict']
    )
app.on_startup.append(swagger)
setup_aiohttp_apispec(
    app=app, title="My Documentation", version="v1", url="/api/docs/api-docs"
)

Validation lenght

Dumb question but

class ReqRequestSchema(Schema):
    uName = fields.Str(required=True)  #how to validate lenght > 3
    uName = fields.Str(required=True, validate= ??? )

@json_schema is missing in sub-app API spec

Hi,

I have 2 aiohttp sub-apps. Both of them handle same route with @json_schema(SomeSchema) applied. I'd like to have separate Swagger UI + API spec for each of the sub-apps

So I call setup_aiohttp_apispec() for the first sub-app and then for the second one. When I run my app, the first created sub-app contains Some schema in spec definitions. But the second one doesn't. So I get

Could not resolve reference: Could not resolve pointer: /definitions/Some does not exist in document

error in Swagger UI for the endpoint in the second sub-app

It's the only schema missing in the spec. Also I use @docs (responses), @match_info_schema, @querystring_schema. All of them work fine in this scenario

Validation still works fine for missing schema though

Custom fields for marshmallow plugin

Hello!

I've spend some time and cannot figure out, how to register custom fields for apispec through aiohttp-apispec library.

Docs about this at apispec:
https://apispec.readthedocs.io/en/latest/using_plugins.html#custom-fields

But MarshmallowPlugin created inside AiohttpApiSpec and not exposed explicitly.

What do you think about instantiating MarshmallowPlugin at module level or put into global context? It will allows me to import this plugin and decorate my own custom fields.

Thanks for your attention!

Webargs 6.0.0 breaks validation_middleware parsing

Webargs 6.0.0 has changed the parse keyword argument from locations to location_loader causing the following exception on new builds of aiohttp-apispec:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_middlewares.py", line 119, in impl
    return await handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_apispec/middlewares.py", line 34, in validation_middleware
    schema["schema"], request, locations=schema["locations"]
TypeError: parse() got an unexpected keyword argument 'locations'

Upgrade aiohttp dependency

Aiohttp-apispec has a specific upper limit on its aiohttp dependency (<=3.4.4). Why is that? Because of this, I cannot upgrade aiohttp to v3.5. I should say that it is highly unusual for libraries to have such specific dependencies (pinned down to a micro version). Keeping this up to date is a lot of work for the maintainer. On the other hand, does aiohttp-apispec really work on any earlier version of aiohttp??

Body argument is deprecated for http web exceptions

In example for custom validation errors handling you raise HTTPException with body argument:

def my_error_handler(
    error: ValidationError,
    req: web.Request,
    schema: Schema,
    error_status_code: Optional[int] = None,
    error_headers: Optional[Mapping[str, str]] = None,
) -> NoReturn:
    raise web.HTTPBadRequest(
            body=json.dumps(error.messages),
            headers=error_headers,
            content_type="application/json",
        )

I see many DeprecationWarning: body argument is deprecated for http web exceptions warnings.

According to this issue it was deprecated in aiohttp in 2018.

Should/can schema coerce datatype?

Is it possible to use a schema to coerce a datatype with querystring_schema or match_info_schema?

For example:

class MyBool(Schema):
    mybool = fields.Boolean()

@querystring_schema(MyBool)
async def my_func(request: web.Request):
    mybool = request.query.getone('mybool')
    dtype = type(mybool)  # this will be a string not a bool`

example call:
curl http://localhost:8080/my_func_url?mybool=true

Should/can the schema coerce 'mybool' to a type bool? Ideally the schema would coerce the datatype rather than me having to do so within my function.

500 internal error if no data in json

if i send non json it just responds proper with missing fields
if i send legit it works normal

POST http://localhost:8080/reg HTTP/1.1
content-type: application/json

{
    "msg":"hello",
    "data":
    {
        "uName":    "nekinalog",
        "uPass":    "myhardpass",
        "uEmail":   "[email protected]"
    }
}

but if i send json that has no "data" it crashes

POST http://localhost:8080/reg HTTP/1.1
content-type: application/json

{
    "uName":    "nekinalog",
    "uPass":    "myhardpass",
    "uEmail":   "[email protected]"
}
 python3 authserver.py 
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
Error handling request
Traceback (most recent call last):
  File "/home/me/.local/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/home/me/.local/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/home/me/.local/lib/python3.7/site-packages/aiohttp/web_middlewares.py", line 119, in impl
    return await handler(request)
  File "/home/me/.local/lib/python3.7/site-packages/aiohttp_apispec/middlewares.py", line 40, in validation_middleware
    request[request.app["_apispec_request_data_name"]] = kwargs
  File "/home/me/.local/lib/python3.7/site-packages/aiohttp/web_app.py", line 160, in __getitem__
    return self._state[key]
KeyError: '_apispec_request_data_name'

Hmm is this normal behavior or am i doing something wrong?
Here my simple server api

from aiohttp_apispec import (
    docs,
    request_schema,
    setup_aiohttp_apispec,
)
from marshmallow import Schema, fields
from aiohttp_apispec import validation_middleware


class ReqRequestSchema(Schema):
    uName = fields.Str(required=True)
    uPass = fields.Str(required=True)
    uEmail = fields.Str(required=True)

ROUTES = web.RouteTableDef()

@ROUTES.post('/reg')
@request_schema(ReqRequestSchema(strict=True))
async def try_reg(request):
  try:   #probbaly can ignore all stuff inhere cuz it crashes in validation_midleware 
    data = await request.json() #before this even runs, anyway will change this after
    print(data)
    txt = data['uName']
  except json.decoder.JSONDecodeError as json_error:
    return web.json_response({'error':'recived json is bad'})
  return web.Response(text=txt)


APP = web.Application(middlewares=[validation_middleware]) #error_middleware, 
APP.add_routes(ROUTES)
web.run_app(APP)

Flexible settings for aiohttp_apispec_middleware middleware

As for now aiohttp_apispec_middleware - is middleware which makes request json validation with given schema and raises 400 if it fails. We need more flexible settings for it - http code in case of validation error or custom callback function:

from aiohttp_apispec import aiohttp_apispec_middleware

...

app.middlewares.append(aiohttp_apispec_middleware(status=400, text="bad request"))

Or just raise validation error to give oppotinity to write own middlware which catchs this error, like this:

@web.middleware
async def validation_error(request, handler):
    try:
        return await handler(request)
    except SomeValidationError:
        return web.Response(status=400)

Second case is more preferable and more pythonic.

Controlling validation error output

Hi

Is there any way to change default validation error code?
As much as I see from code, right now status code is determined by webargs and it doesn't seem to be allowing any customisation (apart from monkey-patching webargs.core.DEFAULT_VALIDATION_STATUS variable)

Also some way of customising body of validation error would be nice, but I can't come up with any ideas how to make it smoothly.
May be some kind of function that you can pass to setup_aiohttp_apispec that will transform validation error body to format that user needs

@match_info_schema results in swagger parameter entries for both "path" and "match_info"

If you set up a schema that validates a route's match_info, the Swagger output will have a generated parameter description for both the parameters in the path and the ones in the schema.

Example, with a route that must have a customer_id that is a valid UUID:

class CustomerIdSchema(Schema):
    customer_id = fields.UUID(required=True)

@docs(...)
@routes.get("/customers/{customer_id}")
@match_info_schema(CustomerIdSchema)
async def route(request):
    customer_id = request.match_info["customer_id"]
    ...

The resulting Swagger schema will have a parameters property that looks like this:

"parameters": [
          {
            "in": "match_info",
            "name": "customer_id",
            "required": true,
            "type": "string",
            "format": "uuid"
          },
          {
            "in": "path",
            "name": "customer_id",
            "required": true,
            "type": "string"
          }
        ],

It contains both the customer_id from the route path as well as from the schema. In the Swagger page it would ask you to enter both.

I think specifying match_info_schema should replace these parameters, right?

Example in README has outdated code

Hi all,

Thank you for this fantastic library, it will be very useful for me!

I was experimenting with this library for a project of mine and noticed that the README has an out of date usage of marshmallow.Schema where the strict=True argument is passed.

In marshmallow > 3 there was a breaking change introduced as per the changelog: https://marshmallow.readthedocs.io/en/stable/changelog.html?highlight=strict#b7-2018-02-03

I'm happy to put up a PR to update the README if you need!

about the header parameter

hi, thanks for your proj:
how to add a parameter whitch the parameter's type is header,for exmaple
the accept header or some customize headers juest like token ?

charmap error on pip install in Windows

> pip install aiohttp-apispec
Collecting aiohttp-apispec
  Downloading https://files.pythonhosted.org/packages/db/28/07d23c9e6e66292d6ef91856ca2291fd0a5ad94eda40947ebab648befe7c/aiohttp-apispec-1.4.0.tar.gz (2.3MB)
    100% |████████████████████████████████| 2.3MB 3.1MB/s
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\selim\AppData\Local\Temp\pip-install-ddcs9gwn\aiohttp-apispec\setup.py", line 14, in <module>
        long_description=read('README.md'),
      File "C:\Users\selim\AppData\Local\Temp\pip-install-ddcs9gwn\aiohttp-apispec\setup.py", line 6, in read
        content = fp.read()
      File "C:\Python37\lib\encodings\cp1252.py", line 23, in decode
        return codecs.charmap_decode(input,self.errors,decoding_table)[0]
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 8116: character maps to <undefined>

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1

This is a pretty common issue:

request param name

Like this:

...
app.middlewares.append(aiohttp_apispec_middleware(request_param='request'))
...
@use_kwargs(RequestSchema(strict=True))
async def index(request):
    request_params = request['request']  # now it is allways just "data"
...

Incorrect behavior in aiohttp_apispec_middleware

My problem is if I add aiohttp_apispec_middleware in my MIDDLEWARES list, exception catching middlewares know nothing about exceptions in apispec

MIDDLEWARES = [
    ...,
    exception_middleware,  # Catch exception, format it and respond to client
    SentryMiddleware(settings.SENTRY),  # Send exception data to sentry
    ...,
    aiohttp_apispec_middleware
]

In my opinion, the problem is in this piece of code:

middlewares.py: 40
except web.HTTPClientError as error:
    return (error_handler or _default_error_handler)(error)

If I change it to:

except web.HTTPClientError as error:
    raise error

everything works correctly

If you agree that this is the problem, I can make PR with fix.

Avoid validating GET requests against schema

My (simplified) code looks something like this:

@docs(summary='Reveal details about a specific user')
@use_kwargs(UserSchema())
@marshal_with(UserSchema())
def get_user(request):
    user_id = request.match_info['id']
    user = User.select().where(User.id == user_id).get()

    return web.json_response(UserSchema().dump(user))

The view is obviously meant to be requested via GET and the library should not validate any kind of data. Nevertheless, the validation_middleware middleware will see that this view has __schemas__ and will apply validations, throwing missing fields errors for all required fields.

What am I doing wrong here?

Custom plugins for APISpec

Hello!
I need pass custom plugins to APISpec, but there no possibility to do this because only self.plugin hardcoded in APISpec plugins param

Example from `More Decorators` is not correctly setting attributes on the request object

Hi there, I have the following code inspired by the documentation

from aiohttp_apispec import (docs,
                             setup_aiohttp_apispec,
                             querystring_schema,
                             headers_schema,
                             json_schema)
from aiohttp import web
from marshmallow import Schema, fields


class AuthHeaders(Schema):
    a = fields.String()


class UserMeta(Schema):
    a = fields.String()


class UserParams(Schema):
    a = fields.String()


class OkResponse(Schema):
    a = fields.String()


@docs(
    tags=["users"],
    summary="Create new user",
    description="Add new user to our toy database",
    responses={
        200: {"description": "Ok. User created", "schema": OkResponse},
        401: {"description": "Unauthorized"},
        422: {"description": "Validation error"},
        500: {"description": "Server error"},
    },
)
@headers_schema(AuthHeaders)
@json_schema(UserMeta)
@querystring_schema(UserParams)
async def create_user(request: web.Request):
    headers = request["headers"]  # <- validated headers!
    json_data = request["json"]  # <- validated json!
    query_params = request["querystring"]  # <- validated querystring!
    return web.json_response({"headers": headers, "json_data": json_data, "query_params": query_params})

app = web.Application()

app.router.add_post('/v1/create_user', create_user)

# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(app=app, title="My Documentation", version="v1", swagger_path="/docs")

# find it on 'http://localhost:8080/api/docs/api-docs'
web.run_app(app)

When sending a the following request:

curl -X POST "http://localhost:8080/v1/create_user" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"a\": \"string\"}"

I get the following exception raised

======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
Error handling request
Traceback (most recent call last):
  File "/Users/chris.herrmann/Code/Sportsflare/real-time/scratch/env/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start                                                                                                               
    resp = await task
  File "/Users/chris.herrmann/Code/Sportsflare/real-time/scratch/env/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle                                                                                                                  
    resp = await handler(request)
  File "api_with_apispec.py", line 41, in create_user
    headers = request["headers"]  # <- validated headers!
  File "/Users/chris.herrmann/Code/Sportsflare/real-time/scratch/env/lib/python3.7/site-packages/aiohttp/web_request.py", line 237, in __getitem__                                                                                                          
    return self._state[key]
KeyError: 'headers'

What am I doing wrong here?

Automatically set the location of an argument if it is a file

If I define the following argument and add it to my spec, the Swagger spec and UI correctly renders it (i.e. I can upload a file from the UI, and the spec contains the correct properties).

fields.Field(
    description="Data file to perform inference.",
    required=True,
    location="form",
    type="file"
)

However, files must be sent as multipart/form-data, therefore adding the location="form" is mandatory in such cases.

As a wishlist, this could be avoided if we automatically set location="form" if a defined argument is of type file.

Allow more clean naming for request/response decorators (optionally)

From this:

@docs(
    tags=["mytag"], 
    summary="Method summary", 
    description="Method description",
)
@use_kwargs(SomeSchema(strict=True))
@marshal_with(AnotherSchema(), 200)
async def index(request):
    return web.json_response({"msg": "hello"})

To this:

@docs(
    tags=["mytag"], 
    summary="Method summary", 
    description="Method description",
)
@request(SomeSchema(strict=True))
@response(AnotherSchema(), 200)
async def index(request):
    return web.json_response({"msg": "hello"})

Unable to get the latest (0.9.0) version

Hi! I'm not able to get the latest version. Whether the uploaded to pypi files are correct?

pip install aiohttp-apispec==0.9.0                                                                                                                                
Collecting aiohttp-apispec==0.9.0
  Could not find a version that satisfies the requirement aiohttp-apispec==0.9.0 (from versions: 0.9.0rc1.macosx-10.9-x86_64, 0.9.0.macosx-10.9-x86_64, 0.1.0, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.2.0, 0.3.0, 0.3.1, 0.5.0, 0.5.1, 0.5.2, 0.5.3, 0.5.4, 0.5.5, 0.6.0, 0.6.1, 0.7.0, 0.7.1, 0.7.2, 0.7.3, 0.7.4, 0.7.5, 0.7.6, 0.7.7, 0.8.0)
No matching distribution found for aiohttp-apispec==0.9.0

Fails with apispec 3.0.0

MarshmallowPlugin.openapi has been renamed to MarshmallowPlugin.converter in apispec 3.0.0 and thus aiohttp-apispec fails with:

venv/lib/python3.7/site-packages/aiohttp_apispec/aiohttp_apispec.py", line 138, in _update_paths
raw_parameters = self.plugin.openapi.schema2parameters(
AttributeError: 'MarshmallowPlugin' object has no attribute 'openapi'

Swagger docs only show one parameter schema

Summary

Since __apispec__["parameters"] is no longer used in request_schema, and __apispec__["schema"] is used for generating the swagger.json, the schema of only the first added parameter is shown in the swagger docs.

Example

https://repl.it/repls/UrbanMushyMp3

What Happened

As you can see in the output from the above link, the swagger_dict only contains the headers schema, even though both body and headers schemas were provided.

Expected

The parameters list would contain both the headers and body schemas.

Customize HTTP status on validation error

I offer this idea:

from aiohttp import web
from webargs.aiohttpparser import AIOHTTPParser

from .utils import issubclass_py37fix


def validation_middleware(status: int = AIOHTTPParser.DEFAULT_VALIDATION_STATUS):

    class Parser(AIOHTTPParser):
        DEFAULT_VALIDATION_STATUS = status

    parser = Parser()

    @web.middleware
    async def middleware(request: web.Request, handler) -> web.Response:
        """
        Validation middleware for aiohttp web app

        Usage:

        .. code-block:: python

            app.middlewares.append(validation_middleware)


        """
        orig_handler = request.match_info.handler
        if not hasattr(orig_handler, "__schemas__"):
            if not issubclass_py37fix(orig_handler, web.View):
                return await handler(request)
            sub_handler = getattr(orig_handler, request.method.lower(), None)
            if sub_handler is None:
                return await handler(request)
            if not hasattr(sub_handler, "__schemas__"):
                return await handler(request)
            schemas = sub_handler.__schemas__
        else:
            schemas = orig_handler.__schemas__
        kwargs = {}
        for schema in schemas:
            data = await parser.parse(
                schema["schema"], request, locations=schema["locations"]
            )
            if data:
                kwargs.update(data)
        kwargs.update(request.match_info)
        request[request.app["_apispec_request_data_name"]] = kwargs
        return await handler(request)
    return middleware

and app.middlewares.insert(0, validation_middleware(400))

Does not working class based views

python 3.8.
aiohttp 3.6.2
It does not work class-based views, Swagger shows nothing. When using function style all work correctly.

class SomeView(web.View):
    @docs(
        tags=["mytag"],
        summary="View method summary",
        description="View method description",
    )
    async def post(self) -> web.json_response:
        pass

add_swagger_web_page unable to open file in 3.5

The example on the Quickstart section seems to fail when using Python 3.5, during the setup_aiohttp_apispec() call:

TypeError: invalid file: PosixPath('/tmp/venvs/next/lib/python3.5/site-packages/aiohttp_apispec/static/index.html')

Digging a bit deeper, the cause seems to be that add_swagger_web_page() attempts to call open() with a Path-like object, which seems to be only supported in Python 3.6+:

Changed in version 3.6:

  • Support added to accept objects implementing os.PathLike.

As a new user of the library, I'll attempt a quick fix as I believe it aims to be Python 3.5 compatible - apologies in advance for any potential breaking of the contribution flow or wrong assumptions on my part 😁

Add a way to customize the `SwaggerUIBundle` parameters

SwaggerUIBundle is called in the static/index.html file. Its parameters are hard codded. I would like to set validatorUrl to null. But right now this is not possible.

It would be useful to add a way to customize the arguments to SwaggerUIBundle. Either via a dict of extra arguments or more simply by allowing to pass a string that will be inserted as-is in the html file after the last argument.

Non-valid parameter-specific fields in Path Object

Non-valid parameter-specific fields generated for Path Objects.
It happens because of direct reuse of schema2parameters() function in marshal_with decorator implementation.

Example:

paths: {
    /3/match/: {
        get: {
            parameters: [],
            responses: {
                200: {
                    in: "body",       <-- Non-valid
                    required: false,  <-- Non-valid
                    name: "body",     <-- Non-valid
                                      <-- there is no description (required)
                    schema: {...}
                }
            },
            ...
        }
    },
},

I found it with Swagger Online Validator (http://online.swagger.io/validator/debug?url=<SCHEMA URL>)

Path is no longer available in apispec > 0.39.0

According to changes in apispec here the Path is no longer available, yet aiohttp_apispec.py from latest version still tries to import it resulting in ImportError: cannot import name 'Path'. Last changes here was title as 'drop support for apispec <0.39' so it seems to be related. I dont have enough knowledge about the project to submit some PR for that.
Cheers!

Question about validation_middleware

Currently code of this method looks like:

async def validation_middleware(request: web.Request, handler) -> web.Response:
    ...
    for schema in schemas:
        data = await request.app["_apispec_parser"].parse(
            schema["schema"], request, locations=schema["locations"]
        )
        if data:
            try:
                result.update(data)
            except (ValueError, TypeError):
                result = data
                break
    else:
        result.update(request.match_info)
    request[request.app["_apispec_request_data_name"]] = result
    return await handler(request)

So, if schema successfully parsed a request arguments (basically transform each argument to their relative type), they will be put in result, but eventually (if data a dictionary) arguments in result will be replaced by their string representation.

The question is Why? Why replacing a right type arguments with their string representation?

Wrang property `docked`

I use decorator @docs(summary='Some cool endpoint') and receive spec like this:

/v1/endpoint/:
  post:
    ...
    docked: {}
    ...
    summary: Some cool endpoint

But actually property docked is't defined in OpenAPI 2.0.
And online validator says the same:

Structural error at paths./v1/endpoint/.post
should NOT have additional properties
additionalProperty: docked
Jump to line XX

I think problem in line:

func.__apispec__ = {'parameters': [], 'responses': {}, 'docked': {}}

aiohttp-apispec: v1.0.2

Use definitions instead of schema inlining

Hi, i'm using 0.7.5 version, and it is inlining all schemas in spec (not sure about newer versions).
It would be much better to utilize definitions.

Is it possible considering current architecture?
Would be great at least to have an option to register mappings between definition and schema.

HEAD documentation is not generated

The documentation for HEAD is not generated.

It would be easy to allow the AiohttpApiSpec class and setup_aiohttp_apispec function to take a list of path to generate documentation for without changing the default behaviour.

I already made a patch for this issue, i'll make a pull request.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.