-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
APIGW NG implement response template #11144
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
Conversation
LocalStack Community integration with Pro 2 files ± 0 2 suites ±0 23m 53s ⏱️ - 1h 11m 23s Results for commit 66ce1f4. ± Comparison against base commit acbbd3f. This pull request removes 2517 tests.
♻️ This comment has been updated with latest results. |
4528d3d
to
0e15837
Compare
790884b
to
6515404
Compare
6515404
to
3cd1daf
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.
Nice work! 🚀 lots of validation went into this, it's quite apparent 😄
I mostly have a comment regarding the code paths in the handler if we have a response template or not. Not quite sure what is the right solution, and I'd like your opinion on it too!
Also, great catch for the exception handler!
Thank you so much for adding this, I can't wait to try to run the pipeline with all the open PRs merged!
...ck-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_response.py
Outdated
Show resolved
Hide resolved
...ck-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_response.py
Outdated
Show resolved
Hide resolved
...ck-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_response.py
Outdated
Show resolved
Hide resolved
localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py
Show resolved
Hide resolved
localstack-core/localstack/services/apigateway/next_gen/execute_api/template_mapping.py
Outdated
Show resolved
Hide resolved
tests/unit/services/apigateway/test_handler_integration_response.py
Outdated
Show resolved
Hide resolved
tests/unit/services/apigateway/test_handler_integration_response.py
Outdated
Show resolved
Hide resolved
tests/unit/services/apigateway/test_handler_integration_response.py
Outdated
Show resolved
Hide resolved
3cd1daf
to
f238894
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.
LGTM! Great job! And great investigation, we're pushing far into parity here 🚀 I believe most of the comments could be addressed in follow-ups.
The remapping of headers is super interesting!
I've actually looked the remapped headers now, and they are, for once, documented:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html#api-gateway-known-issues-rest-apis
I found some gems here, some we hit our heads against:
The test invocation of a method uses the default content type of application/json and ignores specifications of any other content types.
So yes, we have a bit of work regarding the remapping, but I'm sure we can nicely generalize it and make it usable both in the integration response here and directly in the _PROXY
integration as well. It might make the integration response handler a bit smaller as well, removing the headers list from it (we might need to declare them somewhere else, TBD 😄) as there are multiple dimensions to it, dropped/remapped/passthrough.
Great job, thanks for pushing through and finding where we were wrong!
if mapped_headers := response_data_mapping.get("header"): | ||
response.headers.update(mapped_headers) | ||
# Those headers are passed through from the response headers, there might be more? | ||
passthrough_headers = ("connection", "content-type", "content-length") |
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.
maybe Content-Type
might not come from the response but is set here, depending on the template? We'll need to try with an integration that returns XML or HTML but the response template is JSON or there is no response template 🤔 or maybe you've done it already 😅
API Gateway includes a Content-Type header for all integration responses. By default, the content type is application/json.
return "" | ||
|
||
# The invocation request header is used to find the right response templated | ||
accept = request["headers"].get("accept") |
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 also found this in the documentation:
When a request contains multiple media types in its Accept header, API Gateway only honors the first Accept media type.
I guess it depends it's a proper header list, in that case get
might get the first, or if we need to parse the value and split on ,
. Maybe we could write a quick test for it?
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.
There will be a follow up pr with it that will include a fix for the headers
# When trying to override certain headers, they will instead be remapped | ||
# There may be other headers, but these have been confirmed on aws | ||
remapped_headers = ( | ||
"connection", | ||
"content-length", | ||
"date", | ||
"x-amzn-requestid", | ||
"content-type", | ||
) | ||
for header in remapped_headers: | ||
if value := headers.get(header): | ||
headers[f"x-amzn-remapped-{header}"] = value | ||
headers.remove(header) |
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 a really great find! It's also very similar to what is done in the HTTP_PROXY
integration.
The big table in the documentation shares more insights, and it seems it can be different from each integration... this is painful.
I think we should tackle this in a follow up, I wonder how we can generalize this. Maybe a util? Because I was thinking of a handler, but in the integration response step, this is very specific, it happens after mapping/overriding but before passing through, unlike the proxy where it just happens on the response directly. This is going to be fun to test 😅 and we also need to do it for requests, it seems a few are dropped, nothing remapped.
Could also be some constants we re-use, with a base list and depending on the integration type. As the way we use it depends a lot, we don't always have the same data types as input/output.
(side-note: the AWS_PROXY
also drops some request headers from the payload sent to lambda, but those are marked passthrough in the documentation, which makes sense in the sense of the table, but not for the context... 😅)
(edit2: looks like the table isn't super precise, as it says User-Agent
is Passthrough
.. would need to check different scenarios but we know it overrides it for HTTP)
integration_response_handler(default_context, response) | ||
assert response.data == b"json" | ||
|
||
def test_remapped_headers( |
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.
nice new test, made it very clear 💯
Motivation
Completes the core of the Response Integration handler with implementation of response templates. The response template is used to modify the body along with allowing to override headers and response status code.
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-override-request-response-parameters.html
Selecting the right template is a bit trickier than for the integration request as aws does not simply select a matching template or passthrough, instead it will select a template if there are templates created. The documentation states that if no templates are matched, it will select the first. But there is no mention as to what this first is. It does seem to favor
application/json
no matter if it was created first or not. Over my tests with the console this is the best I could figure out. There might be some extra logic I missed.Changes
Other bug fixes
requestTemplates=None
to preventNoneType Exception
ContextVarsResponseOverride
path
instead ofpath_parameters
TODO
What's left to do: