aamalev / aiohttp_apiset

Package to build routes using swagger specification
http://aiohttp-apiset.readthedocs.io/
Apache License 2.0
41 stars 9 forks source link

Requests not validated with the "foreign specification" strategy #16

Open pbasista opened 6 years ago

pbasista commented 6 years ago

According to the documentation: http://aiohttp-apiset.readthedocs.io/en/latest/ the library is supposed to work with a "foreign specification" strategy where the entire OpenAPI specification is read from an external file. So, when I initialize the SwaggerRouter like this:

router = SwaggerRouter("/path/to/openapi.spec.yaml")  # default_validate=True is implicit

I would suppose that the library should:

  1. provide a web-based documentation at apidoc/ URL, and
  2. provide an input/output validation of the requests and responses.

However, the library only performs the first point (i.e. makes the web-based documentation available). It does not validate requests or responses according to the provided OpenAPI specification. The relevant part of the code: https://github.com/aamalev/aiohttp_apiset/blob/b6d6fa0180eeb256ec95eb6f27aa664328653654/aiohttp_apiset/swagger/route.py#L223 is only able to utilize the OpenAPI specification for requests' validation and only when it is present in the request handler's docstring.

I believe that for this library to be useful with the "foreign specification" strategy, it needs to be able to validate the requests and responses using the OpenAPI specification which comes from an external file.

Moreover, it seems like the foreign specification strategy of this library is poorly documented, because the details of the actual behavior are left out and they need to be looked up manually from the code.

aamalev commented 6 years ago
  1. Don't use path param and read examples/foreign
  2. If you load the specification without binding handlers use

    router.include("spec.json", name='myspec')

And browse /apidoc/?spec=myspec

  1. Example: load spec with mocked handler for bind with operationId

    def mocked_handler(request):
        return dict(request)
    
    # operationId-handler association
    opmap = OperationIdMapping(
        defaultdict(lambda: mocked_handler),
    )
    
    def main():
        router = SwaggerRouter(
            swagger_ui='/swagger/',
            search_dirs=[BASE],
        )
    
        app = web.Application(
            router=router,
            middlewares=[jsonify],
        )
    
        # Include our specifications in a router,
        # is now available in the swagger-ui to the address http://localhost:9090/swagger/?spec=v1
        router.include(
            spec='petstore.json',
            operationId_mapping=opmap,
            name='v1',  # name to access in swagger-ui
        )
    
        web.run_app(app, port=9090)
    
    if __name__ == '__main__':
        main()
  2. Unfortunately, the documentation is not complete, but it is gradually replenished. Join the project improvement (:
pbasista commented 6 years ago

So, the explicit call to the include method is needed, because otherwise the name or operationId_mapping parameters cannot be specified. And also, if I understand correctly, the library is not able to automatically create the mapping between operations and handlers. It needs to be provided manually by the operationId keys in the OpenAPI specification and the operationId_mapping parameter to the include method.

Good to know.

aamalev commented 6 years ago

Automatically bind the handler through the operationId of the library knows how, you just need to define the mapping in which handlers can be found. See https://github.com/aamalev/aiohttp_apiset/issues/2

pbasista commented 6 years ago

Yes, I am aware of that option, thanks. It is usable, but perhaps an automated mapping without the need for the operationId would be better. Also, the response validation is not taking place. I think it might be beneficial in some cases.