Skip to content

Commit 891c13b

Browse files
authored
apigw clean up the invalid json parser (#11968)
1 parent 6df4dbc commit 891c13b

File tree

2 files changed

+39
-31
lines changed

2 files changed

+39
-31
lines changed

localstack-core/localstack/services/apigateway/next_gen/execute_api/integrations/mock.py

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,48 +63,44 @@ def parse_invalid_json(self, body: str) -> dict:
6363
CDK creates a MOCK OPTIONS route with in valid json. `{statusCode: 200}`
6464
Aws probably has a custom token parser. We can implement one
6565
at some point if we have user requests for it"""
66+
67+
def convert_null_value(value) -> str:
68+
if (value := value.strip()) in ("null", ""):
69+
return '""'
70+
return value
71+
6672
try:
6773
statuscode = ""
6874
matched = re.match(r"^\s*{(.+)}\s*$", body).group(1)
69-
splits = [m.strip() for m in matched.split(",")]
75+
pairs = [m.strip() for m in matched.split(",")]
7076
# TODO this is not right, but nested object would otherwise break the parsing
71-
kvs = [s.split(":", maxsplit=1) for s in splits]
72-
for kv in kvs:
73-
assert len(kv) == 2
74-
k, v = kv
75-
k = k.strip()
76-
v = v.strip()
77-
78-
assert k
79-
assert v
80-
81-
if k == "statusCode":
82-
statuscode = int(v)
77+
key_values = [s.split(":", maxsplit=1) for s in pairs if s]
78+
for key_value in key_values:
79+
assert len(key_value) == 2
80+
key, value = [convert_null_value(el) for el in key_value]
81+
82+
if key in ("statusCode", "'statusCode'", '"statusCode"'):
83+
statuscode = int(value)
8384
continue
8485

85-
if (first_char := k[0]) in "[{":
86-
raise Exception
87-
if first_char in "'\"":
88-
assert len(k) > 2
89-
assert k[-1] == first_char
90-
k = k[1:-1]
86+
assert (leading_key_char := key[0]) not in "[{"
87+
if leading_key_char in "'\"":
88+
assert len(key) >= 2
89+
assert key[-1] == leading_key_char
9190

92-
if (v_first_char := v[0]) in "[{'\"":
93-
assert len(v) > 2
94-
if v_first_char == "{":
91+
if (leading_value_char := value[0]) in "[{'\"":
92+
assert len(value) >= 2
93+
if leading_value_char == "{":
9594
# TODO reparse objects
96-
assert v[-1] == "}"
97-
elif v_first_char == "[":
95+
assert value[-1] == "}"
96+
elif leading_value_char == "[":
9897
# TODO validate arrays
99-
assert v[-1] == "]"
98+
assert value[-1] == "]"
10099
else:
101-
assert v[-1] == v_first_char
102-
v = v[1:-1]
103-
104-
if k == "statusCode":
105-
statuscode = int(v)
100+
assert value[-1] == leading_value_char
106101

107102
return {"statusCode": statuscode}
103+
108104
except Exception as e:
109105
LOG.debug(
110106
"Error Parsing an invalid json, %s", e, exc_info=LOG.isEnabledFor(logging.DEBUG)

tests/unit/services/apigateway/test_mock_integration.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,25 @@ def test_custom_parser(self, create_default_context):
5656
mock_integration = RestApiMockIntegration()
5757

5858
valid_templates = [
59+
"{ statusCode: 200 }", # this is what the CDK creates when configuring CORS for rest apis
5960
"{statusCode: 200,super{ f}oo: [ba r]}",
6061
"{statusCode: 200, \"value\": 'goog'}",
6162
"{statusCode: 200, foo}: [ba r]}",
6263
"{statusCode: 200, foo'}: [ba r]}",
6364
"{statusCode: 200, }foo: [ba r]}",
65+
"{statusCode: 200, }foo: ''}",
66+
'{statusCode: 200, " ": " "}',
67+
'{statusCode: 200, "": ""}',
68+
"{'statusCode': 200, '': ''}",
69+
'{"statusCode": 200, "": ""}',
70+
'{"statusCode": 200 , }',
71+
'{"statusCode": 200 ,, }', # Because?? :cry-bear:
72+
'{"statusCode": 200 , null: null }',
6473
]
6574
invalid_templates = [
75+
"{\"statusCode': 200 }",
76+
"{'statusCode\": 200 }",
77+
"{'statusCode: 200 }",
6678
"statusCode: 200",
6779
"{statusCode: 200, {foo: [ba r]}",
6880
# This test fails as we do not support nested objects
@@ -72,7 +84,7 @@ def test_custom_parser(self, create_default_context):
7284
for valid_template in valid_templates:
7385
ctx = create_default_context(body=valid_template)
7486
response = mock_integration.invoke(ctx)
75-
assert response["status_code"] == 200
87+
assert response["status_code"] == 200, valid_template
7688

7789
for invalid_template in invalid_templates:
7890
ctx = create_default_context(body=invalid_template)

0 commit comments

Comments
 (0)