-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Fix apigw input path formatting #12379
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 1h 51m 33s ⏱️ -26s Results for commit 00e191b. ± Comparison against base commit 257af99. This pull request removes 1 and adds 165 tests. Note that renamed tests count towards both.
♻️ This comment has been updated with latest results. |
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.
Wow, impressive PR! Thanks a lot for this deep dive into VTL!
I have a few comments, mostly related to naming, it made things a bit hard to follow with the Formatter
naming that still contains the actual object inside. I see where it comes from, as it's mostly a wrapper to provide a clean str
implementation, but it makes thing a bit more complex to understand. But I'm not hard set on changing it either 😄
I would like to know the implication for the $input.json
util function as well, as it relies on the $input.path
implementation, just to be sure we haven't changed something there too, if that's possible? I would think both are very similar and behaves the same.
Also would want to make sure the change to fixture doesn't break any weird tests upstream 😄
In any case, all the logic is there, and this looks really great, you really went the extra mile here! 🚀 thank you 🙏
return value | ||
|
||
|
||
def get_input_path_formatter(value: any) -> any: |
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.
def get_input_path_formatter(value: any) -> any: | |
def get_input_path_formatter(value: any) -> Any: |
@@ -2175,6 +2175,7 @@ def _echo(request: Request) -> Response: | |||
"headers": dict(request.headers), | |||
"url": request.url, | |||
"method": request.method, | |||
"json": request.json if request.is_json else 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.
question: can't this change break other uses of the fixture maybe upstream? Not sure where it is used 😕 might be worth to trigger a run
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.
Good idea, it didn't to seem impact anything. And since this is the behavior implemented in httpbin, there shouldn't be an issue with it unless there are some weird if not is_aws_cloud()
. But I triggered a run upstream 13842940185.
|
||
|
||
class JavaDictFormatter(dict): | ||
# TODO apply this class more generally through the template mappings |
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! Can't wait to have more parity here, and also introduce the "hidden" dict sometimes used! We're getting real close now 👀
if isinstance(value, dict): | ||
return JavaDictFormatter(value) | ||
if isinstance(value, list): | ||
return [get_java_formatter(item) for item in value] |
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 recursion 😄
return value | ||
|
||
|
||
class JavaDictFormatter(dict): |
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 a bit on the fence about the *Formatter
naming, as it still effectively contains/is the primitive value passed, just extended to support VTL, especially the __str__
internal method.
Same with the Java naming, maybe we could scope it down only to VTL instead? Something like VTLMap
or VTLDictExtended
?
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.
Thanks for the suggestion, I struggled with the naming here. I think I like the VTLMap
better.
return dict_to_string(self) | ||
|
||
|
||
class InputPathListFormatter(list): |
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.
same comment for the naming, I think it could be something related to JSON, list and VTL? Naming is hard 😅
Because the documentation mentions returning a JSON object/string, so I think it'd make sense to make it part of the name instead of directly linking it as a result of path
? it will depends on how the $input.json
also behaves 🤔
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 behavior of $input.json
hasn't changed and is behaving as it should. But I realise that we are recursively casting every object in input just to ignore it all and json dumping it anyway. I will separate those a bit more to not uselessly cast everything.
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.
Reguarding the naming, VTLJsonMap
and VTLJsonList
should improve things a bit?
@@ -142,7 +197,7 @@ def path(self, path): | |||
if not self.value: | |||
return {} | |||
value = self.value if isinstance(self.value, dict) else json.loads(self.value) | |||
return extract_jsonpath(value, path) | |||
return get_input_path_formatter(extract_jsonpath(value, path)) | |||
|
|||
def json(self, 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.
as we are calling self.path
in the json
method, I wonder how those 2 behave together, especially the dumping of the weird map value. I think this might change existing behavior because we would return a Python dictionary before, and now will return a weird VTL dictionary instead?
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.
json.dumps
does return the same as before as it does not call str()
on dict or list
str(input.path("$"))='{a=1, b=[{"c":5}]}'
str(input.json("$"))='{"a": 1, "b": [{"c": 5}]}'
@@ -50,6 +53,58 @@ class MappingTemplateVariables(TypedDict, total=False): | |||
stageVariables: dict[str, str] | |||
|
|||
|
|||
def get_java_formatter(value): |
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.
coming back to the naming, it could be something like get_vtl_object
or something related to "type" or maybe even call it cast_to_vtl_object
as it's what we are doing, type casting? I can't really find any good word to replace "formatter". https://velocity.apache.org/engine/devel/developer-guide.html#how-velocity-works
edited: switched back to snake case because I don't know why I put that way, got confused with all the classes before 😅
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! Thanks for addressing the comments so quickly! And for adding the doc strings 😄 this is a great PR and addition, thanks a lot for really pushing this 💯🥇
Motivation
When using VTL template for apigateway, AWS generally returns a java object string representation
{foo=bar}
. But when accessing a list through$input.path('$')
, it returns a valid json string, even if there are dict inside. A nested list inside a dict will cause the encapsulated object to be json encoded as well{list=[{"foo":"bar"}]}
.This pr implements java representation for result of
$input.path
and proper list formatting. Follow ups will be required to implement the Java formatting over more results in the VTL template.Changes
JavaDictFormatter
to return Java like objectInputPathDictFormatter
andInputPathListFormatter
to manage string formatting of children of$input.path