Code Monkey home page Code Monkey logo

aiohttp-swagger3's People

Contributors

aquamatthias avatar elverde avatar garyvdm avatar hh-h avatar lerushe avatar mfalesni avatar rodcarroll avatar socketpair 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

Watchers

 avatar  avatar

aiohttp-swagger3's Issues

Add prefix support for nested applications

As per aiohttp, nested applications are used to avoid monolithic files with all your endpoints.

I've been able to generate the swagger for every single sub_app, as well as the route app. The only issue is that the swagger has no idea of the prefix that is set on the aiohttp level

Example

main.py

app = web.Application()
s = SwaggerDocs(
    app,
    swagger_ui_settings=SwaggerUiSettings(path="/docs"),
    info=SwaggerInfo(
        title="Base API",
        version="1.0.0",
    ),
)
s.add_routes([web.get("/admin-health", handle_health, allow_head=False)])
admin = admin_factory.create_app()
app.add_subapp('/admin/', admin)

admin_factory.py

# 
app = web.Application()
s = SwaggerDocs(
    app,
    swagger_ui_settings=SwaggerUiSettings(path="/docs"),
    info=SwaggerInfo(
        title="Admin API",
        version="1.0.0",
    ),
)
s.add_routes([web.get("/admin-health", handle_health, allow_head=False)])
return app

Description

So this works, the only issue is that the admin api swagger has no clue of the prefix. ie
App health is -> <url>/health
Admin health is -> <url>/admin/admin-health

The swagger documentation wrongfully points to <url>/admin-health

Should be a pretty easy fix on SwaggerUiSettings

Allow PyYAML >=5.4 to be installed

GitHub informed us that there is a security vulnerability CVE-2020-14343 discovered in PyYAML package. To address this issue we should upgrade PyYAML to version >=5.4.

But we could not upgrade until aiohttp-swagger3 relax restrictions in its requirements.txt file. See error below.

#11 30.74 ERROR: Cannot install -r /opt/app/requirements.txt (line 2) and PyYAML>=5.4 because these package versions have conflicting dependencies.
#11 30.74 
#11 30.74 The conflict is caused by:
#11 30.74     The user requested PyYAML>=5.4
#11 30.74     aiohttp-swagger3 0.4.4 depends on pyyaml<5.4 and >=5.3
#11 30.74     The user requested PyYAML>=5.4
#11 30.74     aiohttp-swagger3 0.4.3 depends on pyyaml==5.3
#11 30.74 
#11 30.74 To fix this you could try to:
#11 30.74 1. loosen the range of package versions you've specified
#11 30.74 2. remove package versions to allow pip attempt to solve the dependency conflict
#11 30.74 
#11 30.74 ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-conflicting-dependencies

Definitions with requestBody / required: false can not be used

Schema with requestBody where required is set to false can not be used as the library fails to handle them.

The library first raises a 400 with a complaint about the content type application/octet-stream not being supported. I would already expect this to work, as it is the default for an empty body. When explicitly setting application/json the library fails with another 400 Expecting value: line 1 column 1 (char 0).

Below a modification of test_nullable_ref test case from test_docs_request_bodies to reproduce the issue:

async def test_non_required(swagger_docs_with_components, aiohttp_client):

    routes = web.RouteTableDef()

    @routes.post("/r")
    async def handler(request, body: Dict):
        """
        ---
        requestBody:
          required: false
          content:
            application/json:
              schema:
                type: object
                required:
                  - pet
                properties:
                  pet:
                    nullable: true
                    allOf:
                      - $ref: '#/components/schemas/Pet'

        responses:
          '200':
            description: OK.
        """
        return web.json_response(body)

    swagger = swagger_docs_with_components()
    swagger.add_routes(routes)

    client = await aiohttp_client(swagger._app)

    resp = await client.post("/r", headers={"content-type": "application/json"})
    assert resp.status == 200
    assert await resp.text() == ""

    body = {
        "pet": None,
    }
    resp = await client.post("/r", json=body)
    assert resp.status == 200
    assert await resp.json() == body

    body = {
        "pet": {
            "name": "lizzy",
            "age": 12,
        }
    }
    resp = await client.post("/r", json=body)
    assert resp.status == 200
    assert await resp.json() == body

Custom string formats

Hello!

In our project we need to validate some string formats, which are not represented in aiohhtp-swagger3, like a IPv4Network (192.168.100.0/24). Specification says, that custom formats may be used even though undefined by this specification.

Can't define parameters in Link Object

It seems to be impossible to define "parameters" for a Link Object in a handler response. For example, if I duplicate the getUser links example in the swagger links example, I get the following error:
Exception: Invalid schema for handler 'create_user' POST /users - data.201 must be valid exactly by one of oneOf definition

If I remove the "parameters" section then there is no longer an exception. However, in this case the link really isn't valid, or useful, anymore as you need to be able to indicate that the id in the response body should be used as a parameter for the getUser operation.

I believe that lines 1584/1585 of schema.json are the cause of this. These lines are:

"parameters": {
      "type": "object",
      "additionalProperties": {
      }
 },

By having an empty additionalProperties key, its invalid to have any properties inside of parameters.

Raise custom exception when validation fails

Hello,

Would you consider raising a custom exception (that could inherit from badrequest) instead of using built-in aiohttp 400 error ?

That would allow users to implement a custom middleware Implementing specific validation error behavior.

Also you could consider adding a prop containing deserialized errors object.

raise web.HTTPBadRequest(reason=json.dumps(errors))

Regards, Adam

content-type can be optional

Traceback (most recent call last):
  File "start.py", line 120, in error_middleware
    return await handler(request)
  File "start.py", line 115, in validate_token
    return await handler(request)
  File "/root/.local/share/virtualenvs/-x-v5uFv0/lib/python3.7/site-packages/aiohttp_remotes/x_forwarded.py", line 73, in middleware
    return await handler(request)
  File "/root/.local/share/virtualenvs/-x-v5uFv0/lib/python3.7/site-packages/aiohttp/web_urldispatcher.py", line 158, in handler_wrapper
    result = await result
  File "/root/.local/share/virtualenvs/-x-v5uFv0/lib/python3.7/site-packages/aiohttp_swagger3/swagger.py", line 71, in _handle_swagger_call
    kwargs = await route.parse(request)
  File "/root/.local/share/virtualenvs/-x-v5uFv0/lib/python3.7/site-packages/aiohttp_swagger3/swagger_route.py", line 141, in parse
    raw_media_type = request.headers["Content-Type"]
  File "multidict/_multidict.pyx", line 62, in multidict._multidict._Base.__getitem__
  File "multidict/_multidict.pyx", line 57, in multidict._multidict._Base._getone
  File "multidict/_multidict.pyx", line 52, in multidict._multidict._Base._getone
KeyError: 'Content-Type'

Spec validation exceptions don't provide enough information to be able to fix the erroneous spec

Hello @hh-h ,

Thank you for the great product!

I'm trying to add OpenAPI docs to our project and the spec validation fails with a very cryptic error message:

...
  File "/root/.local/share/virtualenvs/app-storage-service-evEBRps7/lib/python3.7/site-packages/aiohttp/web_urldispatcher.py", line 1135, in add_routes
    route_def.register(self)
  File "/root/.local/share/virtualenvs/app-storage-service-evEBRps7/lib/python3.7/site-packages/aiohttp/web_routedef.py", line 65, in register
    reg(self.path, self.handler, **self.kwargs)
  File "/root/.local/share/virtualenvs/app-storage-service-evEBRps7/lib/python3.7/site-packages/aiohttp_swagger3/swagger.py", line 186, in add_get
    self.add_route(hdrs.METH_HEAD, path, handler, **kwargs)
  File "/root/.local/share/virtualenvs/app-storage-service-evEBRps7/lib/python3.7/site-packages/aiohttp_swagger3/swagger_docs.py", line 144, in add_route
    validate=need_validation,
  File "/root/.local/share/virtualenvs/app-storage-service-evEBRps7/lib/python3.7/site-packages/aiohttp_swagger3/swagger_docs.py", line 84, in _wrap_handler
    self.spec_validate(self.spec)
  File "<string>", line 63, in validate
  File "<string>", line 2232, in validate___definitions_paths
  File "<string>", line 390, in validate___definitions_pathitem
  File "<string>", line 460, in validate___definitions_operation
fastjsonschema.exceptions.JsonSchemaException: data.parameters[0] must be valid exactly by one of oneOf definition

I would expect to actually see which particular data definition in the generated yaml file it does not like to be able to fix it. With the current message it is actually impossible to figure out what is wrong :(

readOnly support ?

Hi,

I see you have readOnly prop support in.yout TODO list. I'd would love to have it ;-)

Thanks

data.parameters[0] must be valid exactly by one of oneOf definition since upgrade from 0.2.5

  File "/usr/lib/python3/dist-packages/aiohttp/web_urldispatcher.py", line 1133, in add_routes
    route_def.register(self)
  File "/usr/lib/python3/dist-packages/aiohttp/web_routedef.py", line 64, in register
    reg(self.path, self.handler, **self.kwargs)
  File "/usr/lib/python3/dist-packages/aiohttp_swagger3/swagger.py", line 179, in add_get
    return self.add_route(hdrs.METH_GET, path, handler, name=name, **kwargs)
  File "/usr/lib/python3/dist-packages/aiohttp_swagger3/swagger_docs.py", line 138, in add_route
    validate=need_validation,
  File "/usr/lib/python3/dist-packages/aiohttp_swagger3/swagger_docs.py", line 78, in _wrap_handler
    self.spec_validate(self.spec)
  File "<string>", line 63, in validate
  File "<string>", line 2232, in validate___definitions_paths
  File "<string>", line 390, in validate___definitions_pathitem
  File "<string>", line 460, in validate___definitions_operation
fastjsonschema.exceptions.JsonSchemaException: data.parameters[0] must be valid exactly by one of oneOf definition

It used to work just fine and I don't see what is wrong in my YAML doc. Any hint would be appreciated !

Regards,

Incomplete discriminator support ?

Hello hh,

When using oneOf + discriminator, the OpenAPI spec is supposed to provide a hint to aiohttp-swagger3 on which schema should be chosen to validate provided payload. If so, I expect to receive a proper validation error related to the chosen models, but all I get is "failed to validate oneOf".

Here is an example script reproducing the issue: when POSTing on type1 or type2 route I get an helpful error message, when using the polymorphic route, I don't get any hint.

import tempfile
from aiohttp import web

from aiohttp_swagger3 import SwaggerDocs, SwaggerUiSettings


# Inspired from https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/

swagger_spec = """
components:
  schemas:
    ObjectType1:
      type: object
      properties:
        type:
          type: string
          enum:
          - type1
        prop1:
          type: boolean
        prop2:
          type: string
      required:
      - prop1
      - prop2
      - type
      example:
        type: type1
        prop1: 4
        prop2: abc
    ObjectType2:
      type: object
      properties:
        type:
          type: string
          enum:
          - type2
        prop3:
          type: boolean
        prop4:
          type: string
      required:
      - type
      - prop3
      - prop4
      example:
        type: type2
        prop3: false
        prop5: abc
    ObjectAnyType:
      oneOf:
      - $ref: '#/components/schemas/ObjectType1'
      - $ref: '#/components/schemas/ObjectType2'
      discriminator:
        propertyName: type
        mapping:
          type1: '#/components/schemas/ObjectType1'
          type2: '#/components/schemas/ObjectType2'
      example:
        type: type2
        prop3: false
        prop5: abc
"""


async def post_type1(request):
    """
    Receive an object of type1
    ---
    summary: Receive an object of type1
    requestBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ObjectType1'
    responses:
      "400":
        description: 'Failing with {"body": {"prop1": "value should be type of bool"}}'
    """

    return web.json_response({"worked": True})


async def post_type2(request):
    """
    Receive an object of type2
    ---
    summary: Receive an object of type2
    requestBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ObjectType2'
    responses:
      "400":
        description: 'Failing with {"body": {"prop4": "required property"}}'
    """

    return web.json_response({"worked": True})


async def post_type_polymorphic(request):
    """
    Receive an object of type1 or type2
    ---
    summary: Receive an object of type1 or type2
    requestBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ObjectAnyType'
    responses:
      "400":
        description: >
          Failing with fail to validate oneOf while expecting proper message
          depending on selected models using discriminator
    """

    return web.json_response({"worked": True})


def main():
    app = web.Application()

    with tempfile.NamedTemporaryFile() as spec_fh:
        spec_fh.write(bytes(swagger_spec.strip(), "utf-8"))
        spec_fh.seek(0)
        swagger = SwaggerDocs(app, components=spec_fh.name, swagger_ui_settings=SwaggerUiSettings(path="/"))

    swagger.add_routes(
        [
            web.post("/post/type1", post_type1),
            web.post("/post/type2", post_type2),
            web.post("/post/polymorphic", post_type_polymorphic),
        ]
    )

    web.run_app(app)


if __name__ == "__main__":
    main()

I am not sure this is actually easy to solve, as if I understood correctly you are relying on fastjsonschema library but that would definitely be a major improvement for API users ;-)

Best regards, Adam.

Multiple Authentication Types

Hi @hh-h ,

Thank you for great lib!

I found and issue with auth types. Currently on some routes we have to use bearer or cookie auth for different types of request. They configured for routes in a following way:

security:
  - bearerAuth: []
  - cookieAuth: []

However, when client sends both header and cookie it receives an error:
https://github.com/hh-h/aiohttp-swagger3/blob/master/aiohttp_swagger3/validators.py#L541

Documentation for swagger states that any of auth mechanisms should be provided.
Is it possible to have them both enabled and set priority by sequence in security list?

Support for route decorators?

Currently, to use aiohttp-swagger3, one needs to use SwaggerDocs.add_routes():

    s = SwaggerDocs(app, '/docs', title="Swagger Petstore", version="1.0.0", components="components.yaml")
    s.add_routes([
        web.get("/pets/{pet_id}", get_one_pet),
    ])

Is it possible to use aiohttp decorator format?

routes = web.RouteTableDef()

@routes.get('/')
async def hello(request):
    return web.Response(text="Hello, world")

app = web.Application()
app.add_routes(routes)
web.run_app(app)

A example in my mind is like what aiohttp-swagger, where you can define the app the way you want, then add setup_swagger(app). That style is more easily to adapt the existing code base. (https://aiohttp-swagger.readthedocs.io/en/latest/quick_start.html):

from aiohttp import web
from aiohttp_swagger import *


async def ping(request):
    """
    ---
    description: This end-point allow to test that service is up.
    tags:
    - Health check
    produces:
    - text/plain
    responses:
        "200":
            description: successful operation. Return "pong" text
        "405":
            description: invalid HTTP Method
    """
    return web.Response(text="pong")


app = web.Application()
app.router.add_route('GET', "/ping", ping)

setup_swagger(app)

web.run_app(app, host="127.0.0.1")

Is this something that can be added easily?

Incorrect validation of mixed type array with int and float

Hello,

Pushing an array with an int inside such payload results in a validation error: {"body": {"1": "fail to validate oneOf"}}

      additionalProperties:
        type: array
        items:
          oneOf:
            - type: string
            - type: integer
            - type: number
              format: float

Removing integer from oneOf makes parsing works, but the resulting payload contains a float instead of expected integer.

I understand this is "shit" use-case, but JSON spec have different types for int and float so it should work. Maybe it's not allowed by OpenAPI, not sure.

Regards, Adam

RegExp in route

If regular expression is situated in route path, swagger don't pass parameter to route.
For example, route path is/pets/{id} without reg.exp. Url of request with id=123 and GET method will be http://127.0.0.1:8080/api/v1/pets/123 and it's all OK, but if route path look like /pets/{id: \d+}, where \d+ is one or more integers then url of request will be looking like http://127.0.0.1:8080/api/v1/pets/{id:\d+} and it return 404 error in my handler.

Combination of $ref with nullable: true: validation fails

Hello hh-h,

Just went into a validation error for some specific case which is supposed to be supported: combinating an existing model and make it nullable by using:

nullable: true
allOf:
  - $ref: '#/components/schemas/SomeObject'

Working with other frameworks and swagger editor says it's valid so I guess it's a bug, despite being an edge case I admit ;)

Here is a complete Python script reproducing the error:

import tempfile
from aiohttp import web

from aiohttp_swagger3 import SwaggerDocs, SwaggerUiSettings


swagger_spec = """
components:
  schemas:
    SomeObject:
      type: object
      properties:
        prop1:
          type: boolean
        prop2:
          type: string
      required:
      - prop1
      - prop2
"""


async def post_nullable_ref(request):
    """
    Example failing when combinating nullable: true and $ref with allOf

    This is supposed to be supported:
      * https://stackoverflow.com/a/48114924
      * https://github.com/OAI/OpenAPI-Specification/issues/1368

    Confirmed as being supported by connexion framewrok by a colleague
    ---
    summary: Combination of ref and nullable
    requestBody:
      required: true
      content:
        application/json:
          schema:
            type: object
            properties:
              someProp:
                nullable: true
                allOf:
                - $ref: '#/components/schemas/SomeObject'
            required:
            - someProp
            example:
              someProp: null
    responses:
      "200":
        description: Worked
    """

    return web.json_response({"worked": True})


def main():
    app = web.Application()

    with tempfile.NamedTemporaryFile() as spec_fh:
        spec_fh.write(bytes(swagger_spec.strip(), "utf-8"))
        spec_fh.seek(0)
        swagger = SwaggerDocs(app, components=spec_fh.name, swagger_ui_settings=SwaggerUiSettings(path="/"))

    swagger.add_routes([web.post("/post_nullable_ref", post_nullable_ref)])

    web.run_app(app)


if __name__ == "__main__":
    main()

Best regards, Adam.

Python 3.11 - Swagger._handle_swagger_method_call() missing 1 required positional argument: 'route'

In python3.11, the following tests fail:

FAILED tests/test_class_based_view.py::test_class_based_view[pyloop] - AssertionError: assert 500 == 200
FAILED tests/test_class_based_view.py::test_decorated_class_based_view[pyloop] - AssertionError: assert 500 == 200
FAILED tests/test_class_based_view.py::test_class_based_spec_file[pyloop] - AssertionError: assert 500 == 200
FAILED tests/test_docs_decorator.py::test_decorated_handlers[pyloop] - AssertionError: assert 500 == 200
FAILED tests/test_methods.py::test_all_methods[pyloop] - AssertionError: assert 500 == 200

For all of them, the exception in the web server is:

ERROR    aiohttp.server:web_protocol.py:403 Error handling request
Traceback (most recent call last):
  File ".../lib64/python3.11/site-packages/aiohttp/web_protocol.py", line 433, in _handle_request
    resp = await request_handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../lib64/python3.11/site-packages/aiohttp/web_app.py", line 504, in _handle
    resp = await handler(request)
           ^^^^^^^^^^^^^^^^^^^^^^
  File ".../lib64/python3.11/site-packages/aiohttp/web_urldispatcher.py", line 956, in _iter
    resp = await method()
                 ^^^^^^^^
TypeError: Swagger._handle_swagger_method_call() missing 1 required positional argument: 'route'

This is happening due to what seems to be a change in behavior of partialmethod used here. I've written up a minimal test case that demonstrates this change here.

I've got 2 ideas how to fix this in. PR's to follow shortly. Only one idea worked. PR submitted.

How to handle route prefix rewrite?

Hello,

this is, probably, a question, but could also be a feature request.

When we publish our API endpoints to Swagger then there is an issue with route prefixes. E.g. our APIs routes in the code look like /api/v1/service/..., but they are exposed to the external world via an API Gateway, which rewrites all paths to the following form /v1/service (the /api part is cut off). How it would be possible to represent that in the resulting Swagger spec, so the public OpenApi sandbox page behind the API Gateway points to the correct paths (starting with /v1/service rather than /api/v1/service)?

Inject custom YAML to route ?

Hello,

I've been struggling with a not so hard but specific need. I want to create multiple routes attaching the same handler, however, I need to inject a bit of logic in the YAML OpenAPI docstring for each route.
Is that possible ? I tried to hack around with a decorator modifying func __doc__ (in the hope you're code will read the function definition after the decorator) but as soon as I add a decorator to the handler, the route is not added to Swagger definition anymore (but still works if called directly).

Any pointer would be appreciated !

Regards, Adam.

pytest DeprecationWarning: Bare functions are deprecated, use async ones

Hi I am just flagging a warning from pytest:

...\Python310\lib\site-packages\aiohttp\web_urldispatcher.py:189: DeprecationWarning: Bare functions are deprecated, use async ones

When I set a breakpoint in the relevant aiohttp library code I find it seems to be this function causing the warning:

functools.partial(<bound method Swagger._handle_swagger_call of <aiohttp_swagger3.swagger_file.SwaggerFile object

Is that a difficult one to fix?

It's not really bothering me but it's nice to stay ontop of these things if possible ๐Ÿ˜„

TypeError: Object of type date is not JSON serializable

When schema has a string field with format type date

                 date:
                     type: string
                     format: date
                     example: 2019-01-01

en exception is raised when rendering SwaggerDocs.

I assume this needs customized JSON serializer instance somewhere to properly turn example date type value into JSON.

ERROR:aiohttp.server:Error handling request
Traceback (most recent call last):
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/site-packages/aiohttp_swagger3/routes.py", line 12, in _swagger_spec
    return web.json_response(request.app[_SWAGGER_SPECIFICATION])
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/site-packages/aiohttp/web_response.py", line 715, in json_response
    text = dumps(data)
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/nix/store/kxj94larps9qil0hbx5ca1wg3kngg36h-python3-3.7.4-env/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type date is not JSON serializable

Create tags

Hello,

Could you please create tags ? It would be useful for packager to get latest "stable" version.
I known it's also on PyPi but it misses examples and tests which are definitely useful.

Regards,

"Bare functions are deprecated" DeprecationWarning when using SwaggerFile router

repro

swagger.yaml file

openapi: 3.0.0
info:
  description: ''
  version: ''
  title: ''
paths:
  '/':
    get:
      responses:
        '200':
          description: ''

Test script

import warnings

from aiohttp import web
from aiohttp_swagger3 import SwaggerFile, SwaggerUiSettings


async def hello(request):
    return web.Response(text="Hello, world")


if __name__ == '__main__':
    warnings.simplefilter('always')

    app = web.Application()
    router = SwaggerFile(
        app=app,
        spec_file="swagger.yaml",
        swagger_ui_settings=SwaggerUiSettings(path="/docs"),
    )

    router.add_routes([web.get('/', hello)])
    # web.run_app(app)

Output

/Users/walkingpendulum/test/venv/lib/python3.7/site-packages/aiohttp/web_urldispatcher.py:152: 
DeprecationWarning: Bare functions are deprecated, use async ones
  "use async ones", DeprecationWarning)

versions

aiohttp-swagger3==0.4.3
aiohttp==3.6.2

Python 3.7

extra

aio-libs/aiohttp#3252 contains some discussion on this topic.

proposed solution

To get rid of partial call here in favor of custom tailored async def wrapper.

Python 3.9 support ?

Hello hh,

It seems one unittest is failing with Python 3.9 (might also be related to a third party dependency, I'm not sure yet:

=================================== FAILURES ===================================
_________________________ test_string_formats[pyloop] __________________________

swagger_docs = <function swagger_docs.<locals>._swagger_docs at 0x7fbd1aa59670>
aiohttp_client = <function aiohttp_client.<locals>.go at 0x7fbd1aa59ca0>

    async def test_string_formats(swagger_docs, aiohttp_client):
        async def handler(
            request,
            date: str,
            datetime: str,
            password: str,
            byte: str,
            binary: str,
            email: str,
            uuid: str,
            hostname: str,
            ipv4: str,
            ipv6: str,
        ):
            """
            ---
            parameters:
    
              - name: date
                in: query
                required: true
                schema:
                  type: string
                  format: date
    
              - name: datetime
                in: query
                required: true
                schema:
                  type: string
                  format: date-time
    
              - name: password
                in: query
                required: true
                schema:
                  type: string
                  format: password
    
              - name: byte
                in: query
                required: true
                schema:
                  type: string
                  format: byte
    
              - name: binary
                in: query
                required: true
                schema:
                  type: string
                  format: binary
    
              - name: email
                in: query
                required: true
                schema:
                  type: string
                  format: email
    
              - name: uuid
                in: query
                required: true
                schema:
                  type: string
                  format: uuid
    
              - name: hostname
                in: query
                required: true
                schema:
                  type: string
                  format: hostname
    
              - name: ipv4
                in: query
                required: true
                schema:
                  type: string
                  format: ipv4
    
              - name: ipv6
                in: query
                required: true
                schema:
                  type: string
                  format: ipv6
    
            responses:
              '200':
                description: OK.
    
            """
            return web.json_response(
                {
                    "date": date,
                    "datetime": datetime,
                    "password": password,
                    "byte": byte,
                    "binary": binary,
                    "email": email,
                    "uuid": uuid,
                    "hostname": hostname,
                    "ipv4": ipv4,
                    "ipv6": ipv6,
                }
            )
    
        swagger = swagger_docs()
        swagger.add_route("GET", "/r", handler)
    
        client = await aiohttp_client(swagger._app)
    
        date = "2017-07-21"
        datetime = "2017-07-21T00:00:00Z"
        password = "password"
        byte = "Oik="
        binary = "รขPNGIHDRdlรบยฑ9bKGDห‡ห‡ห‡โ€ ฮฉรŸรฌpHYs..."
        email = "[email protected]"
        uuid = "550e8400-e29b-41d4-a716-446655440001"
        hostname = "test.example.com"
        ipv4 = "127.0.0.1"
        ipv6 = "2001:db8::ff00:42:8329"
        resp = await client.get(
            "/r",
            params={
                "date": date,
                "datetime": datetime,
                "password": password,
                "byte": byte,
                "binary": binary,
                "email": email,
                "uuid": uuid,
                "hostname": hostname,
                "ipv4": ipv4,
                "ipv6": ipv6,
            },
        )
>       assert resp.status == 200
E       assert 400 == 200
E         +400
E         -200

tests/test_docs_queries.py:411: AssertionError
=========================== short test summary info ============================
FAILED tests/test_docs_queries.py::test_string_formats[pyloop] - assert 400 =...
======================== 1 failed, 179 passed in 13.75s ========================

Let me know if you want some help to find out what's happening!

Regards, Adam.

Asynchronous multipart/form-data loader

Hello,

I wanted to add a route to my API to perform asynchronous upload of large file.
For this, I had to disable validation globally (otherwise request is fully read instead of being passed to my handler as an awaitable) and register multipart/form-data to a passthru customer handler.

It works but that does not look like being a perfect solution. Can I have you input on this ?

"""
Asynchronous multipart form
"""


import os
import logging
from typing import Tuple

import aiohttp
import aiohttp_swagger3
import psutil  # type: ignore


#: Constant pointing to root folder of this project code
PROJECT_ROOT = os.path.abspath(os.path.join(__file__, os.pardir))


class FileUpload:
    """
    View to receive chunked file upload from a multiform
    """

    def __init__(self) -> None:
        self.logger = logging.getLogger(self.__class__.__name__)
        self.process = psutil.Process()

    @property
    def current_mem_consumption(self) -> float:
        """
        Return current process memory consumption in Mb

        :returns: Current process memory consumption in Mb
        :rtype: float
        """

        return self.process.memory_info().rss / 1000 / 1000

    @staticmethod
    def size_mb(size: int) -> float:
        """
        Convert size from bytes to Mb

        :param size: Size in bytes
        :type size: int
        :returns: Size in Mb
        :rtype: float
        """

        return size / 1000 / 1000

    async def post(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
        """
        Method receiving multipart form as POST to upload big file

        :param request: Aiohttp request object
        :type request: aiohttp.web.Request
        :returns: Aiohttp response object
        :type: aiohttp.web.Response
        ---
        summary: Upload file with multiparts form
        tags:
          - upload
        requestBody:
          content:
            multipart/form-data:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    description: Name of the file uploaded
                    example: ISO CentOS 7
                  payload:
                    type: string
                    format: binary
        responses:
          '201':
            description: Deep learning models successfully uploaded
            content:
              application/json:
                schema:
                  type: object
                  properties:
                    name:
                      type: string
                      description: Name of the file uploaded
                      example: ISO CentOS 7
                    filename:
                      type: string
                      description: Filename of the file uploaded
                      example: CentOS-8-x86_64-1905-dvd1.iso
                    size:
                      type: integer
                      description: Size of the file uploaded in Mb
                      example: 7136
        """

        # https://docs.aiohttp.org/en/stable/web_quickstart.html#file-uploads
        reader = await request.multipart()

        field = await reader.next()
        assert field.name == "name", 'First form field must be a "name" field containing string'
        name = str(await field.read(), "utf-8")

        field = await reader.next()
        assert field.name == "payload", 'Second form field must be "payload" field containing file bytes'
        filename = field.filename

        self.logger.info('Starting to receive file "%s"', filename)

        # You cannot rely on Content-Length if transfer is chunked.
        size = 0
        chunk_id = 0
        with open(os.path.join(PROJECT_ROOT, filename), "wb") as dest_fh:
            while True:
                chunk = await field.read_chunk()  # 8192 bytes by default.
                if not chunk:
                    break
                size += len(chunk)
                chunk_id += 1
                dest_fh.write(chunk)
                if chunk_id % 10000 == 0:
                    self.logger.info('File "%s" received %.2f Mb, memory_consumption: %.2f Mb', filename, self.size_mb(size), self.current_mem_consumption)

        self.logger.info('File "%s" received, total size: %.2f Mb', filename, self.size_mb(size))

        return aiohttp.web.json_response({"name": name, "filename": filename, "size": int(round(self.size_mb(size)))})


def redirect_response(dest_url: str) -> None:
    """
    Raise an HTTP/302 Redirect exception to given URL

    :param dest_url: Redirect to this url
    :type dest_url: str
    :raise: aiohttp.web.HTTPFound
    """

    assert isinstance(dest_url, str) and dest_url, "dest_url parameter must be a non-empty string"
    raise aiohttp.web.HTTPFound(dest_url)


def main() -> None:
    """
    Start example server
    """

    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s [%(name)s] %(message)s")

    app = aiohttp.web.Application()
    file_upload_resource = FileUpload()

    app.router.add_route("GET", "/", lambda x: redirect_response("/apidoc"))
    swagger = aiohttp_swagger3.SwaggerDocs(app, "/apidoc", validate=False)

    async def passthru_handler(request: aiohttp.web.Request) -> Tuple[aiohttp.web.Request, bool]:
        return request, True

    swagger.register_media_type_handler("multipart/form-data", passthru_handler)

    swagger.add_routes([aiohttp.web.post("/upload", file_upload_resource.post)])

    aiohttp.web.run_app(app, access_log_format="%s %r [status:%s request:%Tfs bytes:%bb]")


if __name__ == "__main__":

    main()

readOnly: true forbid re-use of ref in valid context

Hello again,

If I define a model for ObjectId which is marked as readOnly: true I can use it properly in GET and POST route (and thanks a lot for that !) sadly I cannot re-use this model in /get/{id} route (validation fails).

Here is a complete example showing the issue (actually most of the code is useless, all you need is to call /get/12345 and see the validation error:

import tempfile
from uuid import uuid4
from aiohttp import web

from aiohttp_swagger3 import SwaggerDocs, SwaggerUiSettings


data = {}

swagger_spec = """
components:
  schemas:
    ObjectId:
      type: string
      example: 123e4567-e89b-12d3-a456-426614174000
      readOnly: true
    SomeObject:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/ObjectId'
        prop1:
          type: boolean
        prop2:
          type: string
      required:
      - id
      - prop1
      - prop2
"""


async def post(request, body):
    """
    Create a new object with readOnly id prop

    ---
    summary: Create a new object with id generated server side
    requestBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SomeObject'
    responses:
      "200":
        description: Worked
    """

    uuid = str(uuid4())
    body["id"] = uuid
    data[uuid] = body
    return web.json_response(body)


async def get(request):
    """
    Get all objects stored in memory

    ---
    summary: Get all objects stored in memory
    responses:
      "200":
        description: Worked
    """

    return web.json_response(list(data.values()))


async def get_one(request, id):
    """
    Get object matching provided id

    ---
    summary: Get object matching provided id
    parameters:
    - name: id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/ObjectId'
    responses:
      "200":
        description: Worked
    """

    return web.json_response(data[id])


def main():
    app = web.Application()

    with tempfile.NamedTemporaryFile() as spec_fh:
        spec_fh.write(bytes(swagger_spec.strip(), "utf-8"))
        spec_fh.seek(0)
        swagger = SwaggerDocs(app, components=spec_fh.name, swagger_ui_settings=SwaggerUiSettings(path="/"))

    swagger.add_routes([web.post("/post", post), web.get("/get", get, allow_head=False), web.get("/get/{id}", get_one, allow_head=False)])

    web.run_app(app)


if __name__ == "__main__":
    main()

Regards, Adam.

Couple of tests fails

Hello,

On my machine thw following tests fails:

tests/test_authentication.py::test_api_key_cookie_auth[pyloop] FAILED    [  5%]
tests/test_authentication.py::test_all_of_auth[pyloop] FAILED            [  7%]
tests/test_authentication.py::test_one_of_auth[pyloop] FAILED            [  8%]
tests/test_authentication.py::test_complex_auth[pyloop] FAILED           [ 13%]
tests/test_docs_cookies.py::test_cookies[pyloop] FAILED                  [ 25%]
tests/test_docs_cookies.py::test_fail_validation[pyloop] FAILED          [ 28%]

Here are the errors:

_______________________ test_api_key_cookie_auth[pyloop] _______________________

aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f006be54268>
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>

    async def test_api_key_cookie_auth(aiohttp_client, loop):
        app = web.Application(loop=loop)
        s = SwaggerDocs(app, "/docs", components="tests/testdata/components.yaml")
    
        async def handler(request):
            """
            ---
            security:
              - apiKeyCookieAuth: []
    
            responses:
              '200':
                description: OK.
    
            """
            assert "C-API-KEY" in request["data"]
            return web.json_response({"api_key": request["data"]["C-API-KEY"]})
    
        s.add_route("GET", "/r", handler)
    
        client = await aiohttp_client(app)
    
        api_key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ4"
        cookies = {"C-API-KEY": api_key}
    
        resp = await client.get("/r", cookies=cookies)
>       assert resp.status == 200
E       assert 400 == 200
E        +  where 400 = <ClientResponse(http://127.0.0.1:56177/r) [400 {"C-API-KEY": "is required"}]>\n<CIMultiDictProxy('Content-Type': 'text/...harset=utf-8', 'Content-Length': '33', 'Date': 'Wed, 27 Nov 2019 13:17:32 GMT', 'Server': 'Python/3.7 aiohttp/3.5.1')>\n.status

tests/test_authentication.py:176: AssertionError
___________________________ test_all_of_auth[pyloop] ___________________________

aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f006bc722f0>
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>

    async def test_all_of_auth(aiohttp_client, loop):
        app = web.Application(loop=loop)
        s = SwaggerDocs(app, "/docs", components="tests/testdata/components.yaml")
    
        async def handler(request):
            """
            ---
            security:
              - basicAuth: []
                apiKeyCookieAuth: []
    
            responses:
              '200':
                description: OK.
    
            """
            assert "C-API-KEY" in request["data"]
            assert "authorization" in request["data"]
            return web.json_response(
                {
                    "api_key": request["data"]["C-API-KEY"],
                    "authorization": request["data"]["authorization"],
                }
            )
    
        s.add_route("GET", "/r", handler)
    
        client = await aiohttp_client(app)
    
        api_key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ4"
        cookies = {"C-API-KEY": api_key}
    
        authorization = "ZGVtbzpwQDU1dzByZA=="
        headers = {"Authorization": f"Basic {authorization}"}
    
        resp = await client.get("/r", headers=headers, cookies=cookies)
>       assert resp.status == 200
E       assert 400 == 200
E        +  where 400 = <ClientResponse(http://127.0.0.1:52737/r) [400 {"C-API-KEY": "is required"}]>\n<CIMultiDictProxy('Content-Type': 'text/...harset=utf-8', 'Content-Length': '33', 'Date': 'Wed, 27 Nov 2019 13:17:32 GMT', 'Server': 'Python/3.7 aiohttp/3.5.1')>\n.status

tests/test_authentication.py:226: AssertionError
___________________________ test_any_of_auth[pyloop] ___________________________

aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f006d56eb70>
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>

    async def test_any_of_auth(aiohttp_client, loop):
        app = web.Application(loop=loop)
        s = SwaggerDocs(app, "/docs", components="tests/testdata/components.yaml")
    
        async def handler(request):
            """
            ---
            security:
              - bearerAuth: []
              - apiKeyCookieAuth: []
    
            responses:
              '200':
                description: OK.
    
            """
            assert not (
                "C-API-KEY" in request["data"] and "authorization" in request["data"]
            )
            r = {}
            if "C-API-KEY" in request["data"]:
                assert "authorization" not in request["data"]
                r["api_key"] = request["data"]["C-API-KEY"]
            else:
                assert "authorization" in request["data"]
                assert "C-API-KEY" not in request["data"]
                r["authorization"] = request["data"]["authorization"]
    
            return web.json_response(r)
    
        s.add_route("GET", "/r", handler)
    
        client = await aiohttp_client(app)
    
        api_key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ4"
        cookies = {"C-API-KEY": api_key}
    
        resp = await client.get("/r", cookies=cookies)
>       assert resp.status == 200
E       assert 400 == 200
E        +  where 400 = <ClientResponse(http://127.0.0.1:54471/r) [400 "no auth has been provided"]>\n<CIMultiDictProxy('Content-Type': 'text/p...harset=utf-8', 'Content-Length': '32', 'Date': 'Wed, 27 Nov 2019 13:17:32 GMT', 'Server': 'Python/3.7 aiohttp/3.5.1')>\n.status

tests/test_authentication.py:283: AssertionError
__________________________ test_complex_auth[pyloop] ___________________________

aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f006be540d0>
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>

    async def test_complex_auth(aiohttp_client, loop):
        app = web.Application(loop=loop)
        s = SwaggerDocs(app, "/docs", components="tests/testdata/components.yaml")
    
        async def handler(request):
            """
            ---
            security:
              - basicAuth: []
                apiKeyCookieAuth: []
              - bearerAuth: []
                apiKeyHeaderAuth: []
    
            responses:
              '200':
                description: OK.
    
            """
            assert "C-API-KEY" in request["data"] or "x-api-key" in request["data"]
            api_key = (
                request["data"]["C-API-KEY"]
                if "C-API-KEY" in request["data"]
                else request["data"]["x-api-key"]
            )
            authorization = request["data"]["authorization"]
            return web.json_response({"http": authorization, "api_key": api_key})
    
        s.add_route("GET", "/r", handler)
    
        client = await aiohttp_client(app)
    
        option1_http = "111ZGVtbzpwQDU1dzByZA=="
        option1_api_key = "111eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ4"
        option1 = {
            "cookies": {"C-API-KEY": option1_api_key},
            "headers": {"Authorization": f"Basic {option1_http}"},
        }
    
        option2_http = "222ZGVtbzpwQDU1dzByZA=="
        option2_api_key = "222eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ4"
        option2 = {
            "headers": {
                "Authorization": f"Bearer {option2_http}",
                "X-API-KEY": option2_api_key,
            }
        }
    
        resp = await client.get("/r", **option1)
>       assert resp.status == 200
E       assert 400 == 200
E        +  where 400 = <ClientResponse(http://127.0.0.1:37611/r) [400 "no auth has been provided"]>\n<CIMultiDictProxy('Content-Type': 'text/p...harset=utf-8', 'Content-Length': '32', 'Date': 'Wed, 27 Nov 2019 13:17:32 GMT', 'Server': 'Python/3.7 aiohttp/3.5.1')>\n.status

tests/test_authentication.py:435: AssertionError
_____________________________ test_cookies[pyloop] _____________________________

aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f006be54510>
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>

    async def test_cookies(aiohttp_client, loop):
        app = web.Application(loop=loop)
        s = SwaggerDocs(app, "/docs")
    
        async def handler(
            request, array: List[int], integer: int, string: str, boolean: bool
        ):
            """
            ---
            parameters:
    
              - name: array
                in: cookie
                required: true
                schema:
                  type: array
                  items:
                    type: integer
    
              - name: integer
                in: cookie
                required: true
                schema:
                  type: integer
    
              - name: string
                in: cookie
                required: true
                schema:
                  type: string
    
              - name: boolean
                in: cookie
                required: true
                schema:
                  type: boolean
    
            responses:
              '200':
                description: OK.
    
            """
            return web.json_response(
                {"array": array, "integer": integer, "string": string, "boolean": boolean}
            )
    
        s.add_route("POST", "/r", handler)
    
        client = await aiohttp_client(app)
    
        c1 = [1, 2, 3, 4, 5]
        c2 = 100
        c3 = "string"
        c4 = False
        cookies = {
            "array": ",".join(str(x) for x in c1),
            "integer": c2,
            "string": c3,
            "boolean": str(c4).lower(),
        }
        resp = await client.post(f"/r", cookies=cookies)
>       assert resp.status == 200
E       assert 400 == 200
E        +  where 400 = <ClientResponse(http://127.0.0.1:50923/r) [400 {"array": "is required", "integer": "is required", "string": "is requir...arset=utf-8', 'Content-Length': '106', 'Date': 'Wed, 27 Nov 2019 13:17:32 GMT', 'Server': 'Python/3.7 aiohttp/3.5.1')>\n.status

tests/test_docs_cookies.py:71: AssertionError
_________________________ test_fail_validation[pyloop] _________________________

aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f006bc9db70>
loop = <_UnixSelectorEventLoop running=False closed=False debug=False>

    async def test_fail_validation(aiohttp_client, loop):
        app = web.Application(loop=loop)
        s = SwaggerDocs(app, "/docs")
    
        async def handler(request, int32: int):
            """
            ---
            parameters:
    
              - name: int32
                in: cookie
                required: true
                schema:
                  type: integer
                  format: int32
    
            responses:
              '200':
                description: OK.
    
            """
            return web.json_response({"int32": int32})
    
        s.add_route("POST", "/r", handler)
    
        client = await aiohttp_client(app)
    
        cookies = {"int32": "abc"}
        resp = await client.post(f"/r", cookies=cookies)
        assert resp.status == 400
        error = error_to_json(await resp.text())
>       assert error == {"int32": "value should be type of int"}
E       AssertionError: assert {'int32': 'is required'} == {'int32': 'value ... be type of int'}
E         Differing items:
E         {'int32': 'is required'} != {'int32': 'value should be type of int'}
E         Full diff:
E         - {'int32': 'is required'}
E         + {'int32': 'value should be type of int'}

tests/test_docs_cookies.py:203: AssertionError

Correct openapi fails validation

I use this library to show swagger-ui from my openapi doc, that is completely maintained in a file.
The file is attached and can be found here: api-doc.yaml.zip.

Integration looks like this:

        SwaggerFile(
            self.app,
            spec_file="./static/api-doc.yaml",
            swagger_ui_settings=SwaggerUiSettings(path="/api-doc", layout="BaseLayout")
        )

When the system is starting up, I get this error:

 File "/core/web/api.py", line 36, in __init__
    SwaggerFile(
  File "/env/lib/python3.9/site-packages/aiohttp_swagger3/swagger_file.py", line 45, in __init__
    super().__init__(
  File "/env/lib/python3.9/site-packages/aiohttp_swagger3/swagger.py", line 81, in __init__
    self.spec_validate(self.spec)
  File "<string>", line 63, in validate
  File "<string>", line 2230, in validate___definitions_paths
  File "<string>", line 390, in validate___definitions_pathitem
  File "<string>", line 480, in validate___definitions_operation
  File "<string>", line 564, in validate___definitions_responses
TypeError: expected string or bytes-like object
(env) 

The openapi doc looks correct to me - editor.swagger.io does not complain.
I also checked the jsonschema via https://www.jsonschemavalidator.net.

I tried to avoid the validation via this PR #90
which does not seem to be the ideal solution.

Env:
Python 3.9.5
aiohttp[speedups]==3.7.4.post0
aiohttp-swagger3==0.6.0

KeyError SWAGGER_SPECIFICATION when no route is defined

Not really important but still. Attaching one route with Swagger docstring and the error is gone

  File "/usr/lib/python3/dist-packages/aiohttp_swagger3/routes.py", line 24, in _swagger_spec
    request.app[_SWAGGER_SPECIFICATION],
  File "/usr/lib/python3/dist-packages/aiohttp/web_app.py", line 137, in __getitem__
    return self._state[key]
KeyError: 'SWAGGER_SPECIFICATION'

IndexError while validating empty hostname

There are raising IndexError for format hostname of string type if you pass an empty string

File "/root/.local/share/virtualenvs/opt-zvmYt2-H/lib/python3.7/site-packages/aiohttp_swagger3/validators.py", line 370, in validate
    val = validator.validate(prop, raw)
File "/root/.local/share/virtualenvs/opt-zvmYt2-H/lib/python3.7/site-packages/aiohttp_swagger3/validators.py", line 239, in validate
    hostname = value[:-1] if value[-1] == "." else value
IndexError: string index out of range

Adding licence info to spec isn't being recognized by the SwaggerDocs class.

Adding contact info by manually editing the swagger spec dictionary after the instantiation of the SwaggerDocs class works fine, but for some reason I can't understand why, the UI frontend does not display the license info at all. If one were to use SwaggerFile class with a separate spec.yaml file instead, the UI frontend will display the license info correctly. This behavior is perplexing for me, since both classes, SwaggerDocs and SwaggerFile, pass the spec dict to the parent Swagger class the same way. Source:

from helpers import build_route_table
from aiohttp_swagger3 import SwaggerDocs, SwaggerUiSettings
from aiohttp import web

app = web.Application()  
swagger = SwaggerDocs(
    app, 
    swagger_ui_settings=SwaggerUiSettings(path="/")
)  
swagger.spec['info'] = {  
    'title': 'API Name',  
    'description': 'API Description',  
    'contact': {  
        'name': 'Creator Name',  
        'url': 'Website Address',  
        'email': 'Email Address'  
    },  
    'licence': {  
        'name': 'MIT License',  
        'url': 'Website Address'  
    },  
    'version': '1.0.0'  
}
routes = build_route_table()
swagger.add_routes(routes)
app['storage'] = dict()
web.run_app(
    app, 
    host='localhost', 
    port='8080'
)

Optional authentication

Hello hh-h,

Sorry for one more request but I ported one big API to your module yesterday and I had an issue.
I have a couple of routes that return minimal output in not JWT auth is provided, and enhanced output if token is provided. I did not manage to do this with your module. It's not a big deal at the moment but I could be a problem at some point and would require me to duplicate the route to workaround the issue.

It seems OpenAPI allow such setup by adding an empty objects in security list definitions for the route but aiohttp_swagger3 refuses that.

See: OAI/OpenAPI-Specification#14

Best regards, Adam.

Don't work when add regexpr to url.

When I use URL like /api/v1.0/orders/{order_id:\d}/ and trying to send request from swagger I get curl -X GET"http://localhost:8090/api/v1.0/orders/{order_id:\d}/" -H "accept: */*" instead of curl -X DELETE "http://localhost:8090/api/v1.0/orders/12/" -H "accept: */*".

Deserialize type: string / format: date-time to datetime.datetime ?

Hello,

I'm pretty surprised the ISO8601 strings are deserialized to string instead of expected datetime.datetime using the following definition:

parameters:
  - name: before_dt
    in: query
    description: Filter latest known position before this date
    required: false
    schema:
      type: string
      format: date-time

Is that normal ? Do you plan to allow such behavior ?

PS: Sorry I did not try the new version yet but it's definitely on my TODO !

Adam.

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.