1
1
import datetime
2
+ import logging
2
3
from urllib .parse import parse_qs
3
4
4
5
from rolo import Request
22
23
ContextVarsResponseOverride ,
23
24
)
24
25
26
+ LOG = logging .getLogger (__name__ )
27
+
28
+
25
29
# TODO: we probably need to write and populate those logs as part of the handler chain itself
26
30
# and store it in the InvocationContext. That way, we could also retrieve in when calling TestInvoke
27
31
45
49
{formatted_date} : Method completed with status: {method_response_status}
46
50
"""
47
51
52
+ TEST_INVOKE_TEMPLATE_MOCK = """Execution log for request {request_id}
53
+ {formatted_date} : Starting execution for request: {request_id}
54
+ {formatted_date} : HTTP Method: {request_method}, Resource Path: {resource_path}
55
+ {formatted_date} : Method request path: {method_request_path_parameters}
56
+ {formatted_date} : Method request query string: {method_request_query_string}
57
+ {formatted_date} : Method request headers: {method_request_headers}
58
+ {formatted_date} : Method request body before transformations: {method_request_body}
59
+ {formatted_date} : Method response body after transformations: {method_response_body}
60
+ {formatted_date} : Method response headers: {method_response_headers}
61
+ {formatted_date} : Successfully completed execution
62
+ {formatted_date} : Method completed with status: {method_response_status}
63
+ """
64
+
48
65
49
66
def _dump_headers (headers : Headers ) -> str :
50
67
if not headers :
@@ -93,6 +110,29 @@ def log_template(invocation_context: RestApiInvocationContext, response_headers:
93
110
)
94
111
95
112
113
+ def log_mock_template (
114
+ invocation_context : RestApiInvocationContext , response_headers : Headers
115
+ ) -> str :
116
+ formatted_date = datetime .datetime .now (tz = datetime .UTC ).strftime ("%a %b %d %H:%M:%S %Z %Y" )
117
+ request = invocation_context .invocation_request
118
+ context_var = invocation_context .context_variables
119
+ method_resp = invocation_context .invocation_response
120
+
121
+ return TEST_INVOKE_TEMPLATE_MOCK .format (
122
+ formatted_date = formatted_date ,
123
+ request_id = context_var ["requestId" ],
124
+ resource_path = request ["path" ],
125
+ request_method = request ["http_method" ],
126
+ method_request_path_parameters = dict_to_string (request ["path_parameters" ]),
127
+ method_request_query_string = dict_to_string (request ["query_string_parameters" ]),
128
+ method_request_headers = _dump_headers (request .get ("headers" )),
129
+ method_request_body = to_str (request .get ("body" , "" )),
130
+ method_response_status = method_resp .get ("status_code" ),
131
+ method_response_body = to_str (method_resp .get ("body" , "" )),
132
+ method_response_headers = _dump_headers (response_headers ),
133
+ )
134
+
135
+
96
136
def create_test_chain () -> HandlerChain [RestApiInvocationContext ]:
97
137
return HandlerChain (
98
138
request_handlers = [
@@ -114,13 +154,16 @@ def create_test_invocation_context(
114
154
) -> RestApiInvocationContext :
115
155
parse_handler = handlers .parse_request
116
156
http_method = test_request ["httpMethod" ]
157
+ resource = deployment .rest_api .resources [test_request ["resourceId" ]]
158
+ resource_path = resource ["path" ]
117
159
118
160
# we do not need a true HTTP request for the context, as we are skipping all the parsing steps and using the
119
161
# provider data
120
162
invocation_context = RestApiInvocationContext (
121
163
request = Request (method = http_method ),
122
164
)
123
- path_query = test_request .get ("pathWithQueryString" , "/" ).split ("?" )
165
+ test_request_path = test_request .get ("pathWithQueryString" ) or resource_path
166
+ path_query = test_request_path .split ("?" )
124
167
path = path_query [0 ]
125
168
multi_query_args : dict [str , list [str ]] = {}
126
169
@@ -140,9 +183,22 @@ def create_test_invocation_context(
140
183
# TODO: handle multiValueHeaders
141
184
body = to_bytes (test_request .get ("body" ) or "" ),
142
185
)
186
+
143
187
invocation_context .invocation_request = invocation_request
188
+ try :
189
+ # this is AWS behavior, it will accept any value for the `pathWithQueryString`, even if it doesn't match
190
+ # the expected format. It will just fall back to no value if it cannot parse the path parameters out of it
191
+ _ , path_parameters = RestAPIResourceRouter (deployment ).match (invocation_context )
192
+ except Exception as e :
193
+ LOG .warning (
194
+ "Error while trying to extract path parameters from user-provided 'pathWithQueryString=%s' "
195
+ "for the following resource path: '%s'. Error: '%s'" ,
196
+ path ,
197
+ resource_path ,
198
+ e ,
199
+ )
200
+ path_parameters = {}
144
201
145
- _ , path_parameters = RestAPIResourceRouter (deployment ).match (invocation_context )
146
202
invocation_request ["path_parameters" ] = path_parameters
147
203
148
204
invocation_context .deployment = deployment
@@ -160,7 +216,6 @@ def create_test_invocation_context(
160
216
responseOverride = ContextVarsResponseOverride (header = {}, status = 0 ),
161
217
)
162
218
invocation_context .trace_id = parse_handler .populate_trace_id ({})
163
- resource = deployment .rest_api .resources [test_request ["resourceId" ]]
164
219
resource_method = resource ["resourceMethods" ][http_method ]
165
220
invocation_context .resource = resource
166
221
invocation_context .resource_method = resource_method
@@ -179,8 +234,10 @@ def run_test_invocation(
179
234
invocation_context = create_test_invocation_context (test_request , deployment )
180
235
181
236
test_chain = create_test_chain ()
237
+ is_mock_integration = invocation_context .integration ["type" ] == "MOCK"
238
+
182
239
# header order is important
183
- if invocation_context . integration [ "type" ] == "MOCK" :
240
+ if is_mock_integration :
184
241
base_headers = {"Content-Type" : APPLICATION_JSON }
185
242
else :
186
243
# we manually add the trace-id, as it is normally added by handlers.response_enricher which adds to much data
@@ -199,7 +256,11 @@ def run_test_invocation(
199
256
# AWS does not return the Content-Length for TestInvokeMethod
200
257
response_headers .remove ("Content-Length" )
201
258
202
- log = log_template (invocation_context , response_headers )
259
+ if is_mock_integration :
260
+ # TODO: revisit how we're building the logs
261
+ log = log_mock_template (invocation_context , response_headers )
262
+ else :
263
+ log = log_template (invocation_context , response_headers )
203
264
204
265
headers = dict (response_headers )
205
266
multi_value_headers = build_multi_value_headers (response_headers )
0 commit comments