|
4 | 4 | import logging
|
5 | 5 | import re
|
6 | 6 | import time
|
| 7 | +from collections import defaultdict |
7 | 8 | from datetime import datetime, timezone
|
8 | 9 | from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict, Union
|
9 | 10 | from urllib import parse as urlparse
|
|
43 | 44 | from localstack.utils.aws.arns import parse_arn
|
44 | 45 | from localstack.utils.aws.aws_responses import requests_error_response_json, requests_response
|
45 | 46 | from localstack.utils.aws.request_context import MARKER_APIGW_REQUEST_REGION, THREAD_LOCAL
|
46 |
| -from localstack.utils.strings import long_uid, short_uid |
| 47 | +from localstack.utils.strings import long_uid, short_uid, to_str |
47 | 48 | from localstack.utils.time import TIMESTAMP_FORMAT_TZ, timestamp
|
48 | 49 | from localstack.utils.urls import localstack_host
|
49 | 50 |
|
|
71 | 72 | PATH_REGEX_CLIENT_CERTS = r"/clientcertificates/?([^/]+)?$"
|
72 | 73 | PATH_REGEX_VPC_LINKS = r"/vpclinks/([^/]+)?(.*)"
|
73 | 74 | PATH_REGEX_TEST_INVOKE_API = r"^\/restapis\/([A-Za-z0-9_\-]+)\/resources\/([A-Za-z0-9_\-]+)\/methods\/([A-Za-z0-9_\-]+)/?(\?.*)?"
|
74 |
| - |
| 75 | +INVOKE_TEST_LOG_TEMPLATE = """Execution log for request {request_id} |
| 76 | + {formatted_date} : Starting execution for request: {request_id} |
| 77 | + {formatted_date} : HTTP Method: {http_method}, Resource Path: {resource_path} |
| 78 | + {formatted_date} : Method request path: {request_path} |
| 79 | + {formatted_date} : Method request query string: {query_string} |
| 80 | + {formatted_date} : Method request headers: {request_headers} |
| 81 | + {formatted_date} : Method request body before transformations: {request_body} |
| 82 | + {formatted_date} : Method response body after transformations: {response_body} |
| 83 | + {formatted_date} : Method response headers: {response_headers} |
| 84 | + {formatted_date} : Successfully completed execution |
| 85 | + {formatted_date} : Method completed with status: {status_code} |
| 86 | + """ |
75 | 87 | # template for SQS inbound data
|
76 | 88 | APIGATEWAY_SQS_DATA_INBOUND_TEMPLATE = (
|
77 | 89 | "Action=SendMessage&MessageBody=$util.base64Encode($input.json('$'))"
|
@@ -1332,18 +1344,9 @@ def set_api_id_stage_invocation_path(
|
1332 | 1344 | stage = path.strip("/").split("/")[0]
|
1333 | 1345 | relative_path_w_query_params = "/%s" % path.lstrip("/").partition("/")[2]
|
1334 | 1346 | elif test_invoke_match:
|
1335 |
| - # special case: fetch the resource details for TestInvokeApi invocations |
1336 |
| - stage = None |
1337 |
| - region_name = invocation_context.region_name |
1338 |
| - api_id = test_invoke_match.group(1) |
1339 |
| - resource_id = test_invoke_match.group(2) |
1340 |
| - query_string = test_invoke_match.group(4) or "" |
1341 |
| - apigateway = aws_stack.connect_to_service( |
1342 |
| - service_name="apigateway", region_name=region_name |
1343 |
| - ) |
1344 |
| - resource = apigateway.get_resource(restApiId=api_id, resourceId=resource_id) |
1345 |
| - resource_path = resource.get("path") |
1346 |
| - relative_path_w_query_params = f"{resource_path}{query_string}" |
| 1347 | + stage = invocation_context.stage |
| 1348 | + api_id = invocation_context.api_id |
| 1349 | + relative_path_w_query_params = invocation_context.path_with_query_string |
1347 | 1350 | else:
|
1348 | 1351 | raise Exception(
|
1349 | 1352 | f"Unable to extract API Gateway details from request: {path} {dict(headers)}"
|
@@ -1479,3 +1482,45 @@ def is_greedy_path(path_part: str) -> bool:
|
1479 | 1482 |
|
1480 | 1483 | def is_variable_path(path_part: str) -> bool:
|
1481 | 1484 | return path_part.startswith("{") and path_part.endswith("}")
|
| 1485 | + |
| 1486 | + |
| 1487 | +def multi_value_dict_for_list(elements: Union[List, Dict]) -> Dict: |
| 1488 | + temp_mv_dict = defaultdict(list) |
| 1489 | + for key in elements: |
| 1490 | + if isinstance(key, (list, tuple)): |
| 1491 | + key, value = key |
| 1492 | + else: |
| 1493 | + value = elements[key] |
| 1494 | + |
| 1495 | + key = to_str(key) |
| 1496 | + temp_mv_dict[key].append(value) |
| 1497 | + return {k: tuple(v) for k, v in temp_mv_dict.items()} |
| 1498 | + |
| 1499 | + |
| 1500 | +def log_template( |
| 1501 | + request_id: str, |
| 1502 | + date: datetime, |
| 1503 | + http_method: str, |
| 1504 | + resource_path: str, |
| 1505 | + request_path: str, |
| 1506 | + query_string: str, |
| 1507 | + request_headers: str, |
| 1508 | + request_body: str, |
| 1509 | + response_body: str, |
| 1510 | + response_headers: str, |
| 1511 | + status_code: str, |
| 1512 | +): |
| 1513 | + formatted_date = date.strftime("%a %b %d %H:%M:%S %Z %Y") |
| 1514 | + return INVOKE_TEST_LOG_TEMPLATE.format( |
| 1515 | + request_id=request_id, |
| 1516 | + formatted_date=formatted_date, |
| 1517 | + http_method=http_method, |
| 1518 | + resource_path=resource_path, |
| 1519 | + request_path=request_path, |
| 1520 | + query_string=query_string, |
| 1521 | + request_headers=request_headers, |
| 1522 | + request_body=request_body, |
| 1523 | + response_body=response_body, |
| 1524 | + response_headers=response_headers, |
| 1525 | + status_code=status_code, |
| 1526 | + ) |
0 commit comments