Skip to content

Commit

Permalink
Update routing documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbeSneyders committed Oct 10, 2023
1 parent 649c7ec commit b64a7ae
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 304 deletions.
4 changes: 2 additions & 2 deletions connexion/apps/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def index():
:param endpoint: the name of the endpoint for the registered URL rule, which is used for
reverse lookup. Flask defaults to the name of the view function.
:param view_func: the function to call when serving a request to the provided endpoint.
:param options: the options to be forwarded to the underlying `werkzeug.routing.Rule`
:param options: the options to be forwarded to the underlying ``werkzeug.routing.Rule``
object. A change to Werkzeug is handling of method options. methods is a list of
methods this rule should be limited to (`GET`, `POST` etc.). By default a rule just
listens for `GET` (and implicitly `HEAD`).
Expand All @@ -231,7 +231,7 @@ def index():
return 'Hello World'
:param rule: the URL rule as string
:param options: the options to be forwarded to the underlying `werkzeug.routing.Rule`
:param options: the options to be forwarded to the underlying ``werkzeug.routing.Rule``
object. A change to Werkzeug is handling of method options. methods is a
list of methods this rule should be limited to (`GET`, `POST` etc.).
By default a rule just listens for `GET` (and implicitly `HEAD`).
Expand Down
28 changes: 26 additions & 2 deletions connexion/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,41 @@ def context(self) -> t.Dict[str, t.Any]:

@property
def content_type(self) -> str:
"""The content type included in the request headers."""
raise NotImplementedError

@property
def mimetype(self) -> str:
"""The content type included in the request headers stripped from any optional character
set encoding"""
raise NotImplementedError

@property
def path_params(self) -> t.Dict[str, t.Any]:
"""Path parameters exposed as a dictionary"""
raise NotImplementedError

@property
def query_params(self) -> t.Dict[str, t.Any]:
"""Query parameters exposed as a dictionary"""
raise NotImplementedError

def form(self) -> t.Union[t.Dict[str, t.Any], t.Awaitable[t.Dict[str, t.Any]]]:
"""Form data, including files."""
raise NotImplementedError

def files(self) -> t.Dict[str, t.Any]:
"""Files included in the request."""
raise NotImplementedError

def json(self) -> dict:
"""Json data included in the request."""
raise NotImplementedError

def get_body(self) -> t.Any:
"""Get body based on the content type. This returns json data for json content types,
form data for form content types, and bytes for all others. If the bytes data is emtpy,
:code:`None` is returned instead."""
raise NotImplementedError


Expand Down Expand Up @@ -98,8 +112,10 @@ def form(self):
def files(self):
return self._werkzeug_request.files.to_dict(flat=False)

def json(self):
return self.get_json(silent=True)

def get_body(self):
"""Get body based on content type"""
if self._body is None:
if is_json_mimetype(self.content_type):
self._body = self.get_json(silent=True)
Expand All @@ -115,7 +131,15 @@ def __getattr__(self, item):


class ASGIRequest(_RequestInterface):
"""Wraps starlette Request so it can easily be extended."""
"""
Implementation of the Connexion :code:`_RequestInterface` representing an ASGI request.
.. attribute:: _starlette_request
This class wraps a Starlette `Request <https://www.starlette.io/requests/#request>`_,
and provides access to its attributes by proxy.
"""

def __init__(self, *args, uri_parser=None, **kwargs):
self._starlette_request = StarletteRequest(*args, **kwargs)
Expand Down
63 changes: 37 additions & 26 deletions connexion/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,12 @@ class RestyResolver(Resolver):
Resolves endpoint functions using REST semantics (unless overridden by specifying operationId)
"""

def __init__(self, default_module_name, collection_endpoint_name="search"):
def __init__(
self, default_module_name: str, *, collection_endpoint_name: str = "search"
):
"""
:param default_module_name: Default module name for operations
:type default_module_name: str
:param collection_endpoint_name: Name of function to resolve collection endpoints to
"""
super().__init__()
self.default_module_name = default_module_name
Expand Down Expand Up @@ -185,35 +187,44 @@ def get_function_name():

class MethodResolverBase(RestyResolver):
"""
Resolves endpoint functions based on Flask's MethodView semantics, e.g. ::
paths:
/foo_bar:
get:
# Implied function call: api.FooBarView().get
class FooBarView(MethodView):
def get(self):
return ...
def post(self):
return ...
Resolves endpoint functions based on Flask's MethodView semantics, e.g.
.. code-block:: yaml
paths:
/foo_bar:
get:
# Implied function call: api.FooBarView().get
.. code-block:: python
class FooBarView(MethodView):
def get(self):
return ...
def post(self):
return ...
"""

_class_arguments_type = t.Dict[
str, t.Dict[str, t.Union[t.Iterable, t.Dict[str, t.Any]]]
]

def __init__(self, *args, class_arguments: _class_arguments_type = None, **kwargs):
def __init__(self, class_arguments: _class_arguments_type = None, **kwargs):
"""
:param class_arguments: Arguments to instantiate the View Class in the format # noqa
{
"ViewName": {
"args": (positional arguments,)
"kwargs": {
"keyword": "argument"
}
}
}
:param class_arguments: Arguments to instantiate the View Class in the format below
:param kwargs: Keywords arguments passed to :class:`~RestyResolver`
.. code-block:: python
{
"ViewName": {
"args": (positional arguments,)
"kwargs": {
"keyword": "argument"
}
}
}
"""
self.class_arguments = class_arguments or {}
if "collection_endpoint_name" in kwargs:
Expand All @@ -223,7 +234,7 @@ def __init__(self, *args, class_arguments: _class_arguments_type = None, **kwarg
"collection_endpoint_name is ignored by the MethodViewResolver. "
"Requests to a collection endpoint will be routed to .get()"
)
super(MethodResolverBase, self).__init__(*args, **kwargs)
super(MethodResolverBase, self).__init__(**kwargs)
self.initialized_views: list = []

def resolve_operation_id(self, operation):
Expand Down Expand Up @@ -280,7 +291,7 @@ def resolve_method_from_class(self, view_name, meth_name, view_cls):

class MethodResolver(MethodResolverBase):
"""
A generic method resolver that instantiates a class a extracts the method
A generic method resolver that instantiates a class and extracts the method
from it, based on the operation id.
"""

Expand Down
6 changes: 5 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,11 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
html_static_path = ['_static']

html_css_files = [
'css/default.css',
]

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Welcome to Connexion's documentation!
=====================================

Connexion is a Python web framework that makes spec-first and api-first development easy. You
Connexion is a modern Python web framework that makes spec-first and api-first development easy. You
describe your API in an `OpenAPI`_ (or swagger) specification with as much detail as you want and
Connexion will guarantee that it works as you specified.

Expand Down
Loading

0 comments on commit b64a7ae

Please sign in to comment.