Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Integration with traversal #31

Open
silenius opened this issue Aug 6, 2019 · 5 comments
Open

Integration with traversal #31

silenius opened this issue Aug 6, 2019 · 5 comments

Comments

@silenius
Copy link

silenius commented Aug 6, 2019

All examples and documentation focus on URL dispatch, it would be good to support Traversal too or at least provide an example on how integrate pyramid_openapi3 in a Traversal only application

@zupo
Copy link
Collaborator

zupo commented Aug 30, 2019

I've only lived in the URL dispatch world for the last ~5 years, and wouldn't know how to even being. Can you provide a basic example and I can then make it pretty, add tests, etc.

@stevepiercy
Copy link
Member

The pyramid-cookiecutter-starter and related tutorial might be helpful.

@jvitus
Copy link

jvitus commented Feb 1, 2022

Hi there is a code example:

import os
import random
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPNotImplemented
from pyramid.interfaces import ILocation
from pyramid.router import Router
from zope.interface import implementer
from zope.interface import Interface
from wsgiref.simple_server import make_server

class IGETView(Interface):
    pass

class IPOSTView(Interface):
    pass

class GETView:
    def __init__(self, context, request):
        self.context = context
        self.request = request

    def __call__(self):
        return self.context.GET()

class POSTView:
    def __init__(self, context, request):
        self.context = context
        self.request = request

    def __call__(self):
        return self.context.POST()


@implementer(ILocation, IGETView, IPOSTView)
class TraversalRoot:
    def __init__(self, name, parent, request):
        self.__name__ = name
        self.__parent__ = parent
        self.__request__ = request

    def GET(self):
        raise  HTTPNotImplemented()
   
    def POST(self):
        raise  HTTPNotImplemented()
       
class Hello(TraversalRoot):
    def GET(self):
        return {"msg": "hello world"}

class Bad_Hello(TraversalRoot):
    def GET(self):
        return {"bad_field": "hello world"}

class Root(TraversalRoot):
    def __getitem__(self, name):
        if name == "hello":
            return Hello(name=name, parent=self, request=self.__request__)
        if name == "bad_hello":
            return Bad_Hello(name=name, parent=self, request=self.__request__)
        raise KeyError

    def POST(self):
        data = self.__request__.json_body

        content =  data.get('content', None)
        if content is None or content == '':
            response  = self.__request__.response
            response.status_code = 400
            response.content_type = "application/json"
            response.charset = "utf-8"
            response.json_body = {
                "field": "content",
                "message": "content field is required and can't be an empty string",
                "exception": "ValidationError"
            }
            return response

        toRet = {
            "ID": random.randint(1,100),
            "content" : data.get('content'),
            "comments": data.get('comments', 'NO_COMMENT_SEND')
        }
        return toRet


def api_factory(request):
    return Root(name="", parent=None, request=request)

def app() -> Router:
    """Prepare a Pyramid app."""
    with Configurator(root_factory=api_factory) as config:
        config.registry.settings["pyramid_openapi3.enable_endpoint_validation"] = False
        # config.registry.settings["pyramid_openapi3.enable_request_validation"] = False
        # config.registry.settings["pyramid_openapi3.enable_response_validation"] = False
        config.include("pyramid_openapi3")
        config.pyramid_openapi3_spec(
            os.path.join(os.path.dirname(__file__), "openapi.yaml"),
        )
        config.pyramid_openapi3_add_explorer()
        config.add_view(
            GETView,
            context=IGETView,
            request_method="GET",
            permission="read",
            name="",
            attr=None,
            renderer="json",
            openapi=True
        )
        config.add_view(
            POSTView,
            context=IPOSTView,
            request_method="POST",
            permission="create",
            name="",
            attr=None,
            renderer="json",
            openapi=True
        )

        return config.make_wsgi_app()


if __name__ == "__main__":
    """If app.py is called directly, start up the app."""
    print("Swagger UI available at http://127.0.0.1:6543/docs/")  # noqa: T001
    server = make_server("127.0.0.1", 6543, app())
    server.serve_forever()

and the openapi.yaml

openapi: "3.0.0"

info:
  version: "1.0.0"
  title: Example traversal api

paths:
  /:
    post:
      summary: Fake create message
      requestBody:
        required: true
        description: Data for creating a fake message
        content:
          application/json:
            schema:
              type: object
              $ref: '#/components/schemas/CreateMessage'

      responses:
        '200':
          description: Success message.
          content:
            application/json:
              schema:
                type: object
                $ref: "#/components/schemas/Message"

        '400':
          $ref: '#/components/responses/ValidationError'
  /hello:
    get:
      summary: Hello Word route
      responses:
        '200':
          description: Display Hello Word message
          content:
            application/json:
              schema:
                type: object
                $ref: "#/components/schemas/Hello"
  /bad_hello:
    get:
      summary: Hello Word route failed response validation
      responses:
        '200':
          description: failed response validation
          content:
            application/json:
              schema:
                type: object
                $ref: "#/components/schemas/Hello"

components:
  schemas:
    Hello:
      type: object
      required:
        - msg
      properties:
        msg:
          type: string
          maxLength: 40
    CreateMessage:
      type: object
      required:
        - content
      properties:
        content:
          type: string
        comments:
          type: string
    Message:
      type: object
      required:
        - ID
        - content
      properties:
        ID:
          type: integer
        content:
          type: string
        comments:
          type: string

    Error:
      type: object
      required:
        - message
      properties:
        field:
          type: string
        message:
          type: string
        exception:
          type: string

  responses:

    ValidationError:
      description: OpenAPI request/response validation failed
      content:
        application/json:
          schema:
            type: object
            items:
              $ref: "#/components/schemas/Error"

As you can see in the App method
pyramid_openapi3.enable_endpoint_validation= False

If we activate it, pyramid_openapi raise a MissingEndpointsError in check_all_routes method but we have no route declared in pyramid when use traversal right ?

we can activate pyramid_openapi3.enable_request_validation and pyramid_openapi3.enable_response_validation but:
That's will work for GET on 127.0.0.1:6543/bad_hello
Not for POST 127.0.0.1:6543/ (if you post a json not corresponding to the schema CreateMessage) excview_tween try to warn {route.name} but no route again

So is it working for traversal and i missing something in my understanding of how to implements it or it's an issue ?

@zupo
Copy link
Collaborator

zupo commented Feb 1, 2022

Looks like some work is needed before it works with traversal out-of-the-box, but your examples shows that it should be possible.

@jvitus
Copy link

jvitus commented Feb 12, 2022

Ok,
I will keep using it for documentation feature.

Thanks for the answer

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants