-
-
Notifications
You must be signed in to change notification settings - Fork 766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add routing middleware #1497
Add routing middleware #1497
Conversation
f0c4549
to
bbf50f1
Compare
9222986
to
2980818
Compare
bbf50f1
to
42da4ba
Compare
2980818
to
db12393
Compare
42da4ba
to
37ab6ce
Compare
Some more context for possible issues of subclassing Also this comment and the corresponding PR: encode/starlette#1519 (comment) |
c5db83d
to
2de9894
Compare
37ab6ce
to
776ee8c
Compare
Thanks for the additional context @Ruwann, I was indeed already running into some of these issues. Note that there's some quick fixes in this PR that I still want to tackle later. Right now, I'm trying to extract all functionality into middleware with minimal effort via maximal reuse of existing connexion code (like the Api and Operation classes), and maximal usage of Starlette tools. |
f67a8dd
to
bbeb817
Compare
e5fd226
to
f0485fe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is ready for review @Ruwann
@@ -27,7 +27,6 @@ def test_errors(problem_app): | |||
error405 = json.loads(get_greeting.data.decode('utf-8', 'replace')) | |||
assert error405['type'] == 'about:blank' | |||
assert error405['title'] == 'Method Not Allowed' | |||
assert error405['detail'] == 'The method is not allowed for the requested URL.' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is Flask specific and not set by Starlette.
@@ -13,15 +13,3 @@ def fake_json_auth(token, required_scopes=None): | |||
return json.loads(token) | |||
except ValueError: | |||
return None | |||
|
|||
|
|||
async def async_basic_auth(username, password, required_scopes=None, request=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some cleanup, these functions are not actually used in the tests.
f0485fe
to
40c9abe
Compare
Pull Request Test Coverage Report for Build 2163779258
💛 - Coveralls |
connexion/apis/abstract.py
Outdated
:param specification: OpenAPI specification. Can be provided either as dict, or as path | ||
to file. | ||
:param base_path: Base path to host the API. | ||
:param arguments: Jinja arguments to resolve in specification. | ||
:param resolver: Callable that maps operationID to a function | ||
:param resolver_error_handler: Callable that generates an Operation used for handling | ||
ResolveErrors | ||
:param debug: Flag to run in debug mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The argument docstrings are a bit unintuitive to me: (a subset of) the parameters of the superclass are described here and some of this class' arguments are described.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, wanted to describe parameters of both this class and superclass, but seems like I forgot to update some. Will clean it up.
connexion/apps/abstract.py
Outdated
self.app = self.create_app() | ||
self.middleware = self._apply_middleware() | ||
|
||
middlewares = middlewares or ConnexionMiddleware.default_middlewares |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't immediately think of a concrete example/usecase, but if the app should be created without any middlewares, this is now not really possible (unless overriding ConnexionMiddleware.default_middlewares
). Should this be allowed by e.g. making a distinction between None
and []
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't really see a use case for it, but the current code can indeed at least lead to some unexpected behavior. I'll put it as default for the argument or handle the []
explicitly.
connexion/middleware/routing.py
Outdated
api_base_path = scope.get('root_path', '')[len(original_scope.get('root_path', '')):] | ||
|
||
if CONNEXION_CONTEXT not in original_scope: | ||
original_scope[CONNEXION_CONTEXT] = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what the convention is, but should we put it under extensions
in the scope instead of directly at the top level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know about this. Makes sense.
connexion/middleware/routing.py
Outdated
if CONNEXION_CONTEXT not in original_scope: | ||
original_scope[CONNEXION_CONTEXT] = {} | ||
|
||
original_scope[CONNEXION_CONTEXT].update({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, the dict.setdefault()
simplifies this a bit (although perhaps a bit less untuitive as it's less known):
original_scope.setdefault(CONNEXION_CONTEXT, {}).update({'api_base_path': api_base_path})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a fan of the one-liner, but can indeed use setdefault
for the first part.
Factor out starlette BaseHTTPMiddleware Fix exceptions for starlette < 0.19 Fix docstring formatting Rename middleware/base.py to abstract.py Rework routing middleware
0bea279
to
ec954cf
Compare
Is this one good to go @Ruwann? |
response = app_client.post("/v1.0/greeting/robbe") | ||
|
||
assert response.headers.get('operation_id') == 'fakeapi.hello.post_greeting', \ | ||
response.status_code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the additional response.status_code
here? Should this be a separate assert
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just raises the status code as info if the test fails.
Part of #1489
This PR adds a routing middleware, which adds the connexion operation to the scope passed to the next middlewares.
The middleware is implemented with a starlette router on which the api endpoints are registered. For all other routes, the router acts ass a pass-through.