Skip to content

Commit 784ff0c

Browse files
author
Michael Brewer
committed
Merge branch 'develop' into chore-feature-toggle
2 parents 0bbc9c6 + d5c3431 commit 784ff0c

File tree

4 files changed

+168
-11
lines changed

4 files changed

+168
-11
lines changed

aws_lambda_powertools/tracing/tracer.py

+27-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
import numbers
77
import os
8-
from typing import Any, Callable, Dict, Optional, Sequence, Union
8+
from typing import Any, Awaitable, Callable, Dict, Optional, Sequence, TypeVar, Union, cast, overload
99

1010
from ..shared import constants
1111
from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
@@ -18,6 +18,9 @@
1818
aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE)
1919
aws_xray_sdk.core = LazyLoader(constants.XRAY_SDK_CORE_MODULE, globals(), constants.XRAY_SDK_CORE_MODULE)
2020

21+
AnyCallableT = TypeVar("AnyCallableT", bound=Callable[..., Any]) # noqa: VNE001
22+
AnyAwaitableT = TypeVar("AnyAwaitableT", bound=Awaitable)
23+
2124

2225
class Tracer:
2326
"""Tracer using AWS-XRay to provide decorators with known defaults for Lambda functions
@@ -329,12 +332,26 @@ def decorate(event, context, **kwargs):
329332

330333
return decorate
331334

335+
# see #465
336+
@overload
337+
def capture_method(self, method: "AnyCallableT") -> "AnyCallableT":
338+
...
339+
340+
@overload
332341
def capture_method(
333342
self,
334-
method: Optional[Callable] = None,
343+
method: None = None,
335344
capture_response: Optional[bool] = None,
336345
capture_error: Optional[bool] = None,
337-
):
346+
) -> Callable[["AnyCallableT"], "AnyCallableT"]:
347+
...
348+
349+
def capture_method(
350+
self,
351+
method: Optional[AnyCallableT] = None,
352+
capture_response: Optional[bool] = None,
353+
capture_error: Optional[bool] = None,
354+
) -> AnyCallableT:
338355
"""Decorator to create subsegment for arbitrary functions
339356
340357
It also captures both response and exceptions as metadata
@@ -487,8 +504,9 @@ async def async_tasks():
487504
# Return a partial function with args filled
488505
if method is None:
489506
logger.debug("Decorator called with parameters")
490-
return functools.partial(
491-
self.capture_method, capture_response=capture_response, capture_error=capture_error
507+
return cast(
508+
AnyCallableT,
509+
functools.partial(self.capture_method, capture_response=capture_response, capture_error=capture_error),
492510
)
493511

494512
method_name = f"{method.__name__}"
@@ -509,7 +527,7 @@ async def async_tasks():
509527
return self._decorate_generator_function(
510528
method=method, capture_response=capture_response, capture_error=capture_error, method_name=method_name
511529
)
512-
elif hasattr(method, "__wrapped__") and inspect.isgeneratorfunction(method.__wrapped__):
530+
elif hasattr(method, "__wrapped__") and inspect.isgeneratorfunction(method.__wrapped__): # type: ignore
513531
return self._decorate_generator_function_with_context_manager(
514532
method=method, capture_response=capture_response, capture_error=capture_error, method_name=method_name
515533
)
@@ -602,11 +620,11 @@ def decorate(*args, **kwargs):
602620

603621
def _decorate_sync_function(
604622
self,
605-
method: Callable,
623+
method: AnyCallableT,
606624
capture_response: Optional[Union[bool, str]] = None,
607625
capture_error: Optional[Union[bool, str]] = None,
608626
method_name: Optional[str] = None,
609-
):
627+
) -> AnyCallableT:
610628
@functools.wraps(method)
611629
def decorate(*args, **kwargs):
612630
with self.provider.in_subsegment(name=f"## {method_name}") as subsegment:
@@ -628,7 +646,7 @@ def decorate(*args, **kwargs):
628646

629647
return response
630648

631-
return decorate
649+
return cast(AnyCallableT, decorate)
632650

633651
def _add_response_as_metadata(
634652
self,

aws_lambda_powertools/utilities/parser/models/apigw.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class APIGatewayEventAuthorizer(BaseModel):
4646
class APIGatewayEventRequestContext(BaseModel):
4747
accountId: str
4848
apiId: str
49-
authorizer: APIGatewayEventAuthorizer
49+
authorizer: Optional[APIGatewayEventAuthorizer]
5050
stage: str
5151
protocol: str
5252
identity: APIGatewayEventIdentity
@@ -70,7 +70,7 @@ class APIGatewayEventRequestContext(BaseModel):
7070

7171

7272
class APIGatewayProxyEventModel(BaseModel):
73-
version: str
73+
version: Optional[str]
7474
resource: str
7575
path: str
7676
httpMethod: Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"resource": "/my/path",
3+
"path": "/my/path",
4+
"httpMethod": "GET",
5+
"headers": {
6+
"Header1": "value1",
7+
"Header2": "value2"
8+
},
9+
"multiValueHeaders": {
10+
"Header1": [
11+
"value1"
12+
],
13+
"Header2": [
14+
"value1",
15+
"value2"
16+
]
17+
},
18+
"queryStringParameters": {
19+
"parameter1": "value1",
20+
"parameter2": "value"
21+
},
22+
"multiValueQueryStringParameters": {
23+
"parameter1": [
24+
"value1",
25+
"value2"
26+
],
27+
"parameter2": [
28+
"value"
29+
]
30+
},
31+
"requestContext": {
32+
"accountId": "123456789012",
33+
"apiId": "id",
34+
"domainName": "id.execute-api.us-east-1.amazonaws.com",
35+
"domainPrefix": "id",
36+
"extendedRequestId": "request-id",
37+
"httpMethod": "GET",
38+
"identity": {
39+
"accessKey": null,
40+
"accountId": null,
41+
"caller": null,
42+
"cognitoAuthenticationProvider": null,
43+
"cognitoAuthenticationType": null,
44+
"cognitoIdentityId": null,
45+
"cognitoIdentityPoolId": null,
46+
"principalOrgId": null,
47+
"sourceIp": "192.168.0.1/32",
48+
"user": null,
49+
"userAgent": "user-agent",
50+
"userArn": null,
51+
"clientCert": {
52+
"clientCertPem": "CERT_CONTENT",
53+
"subjectDN": "www.example.com",
54+
"issuerDN": "Example issuer",
55+
"serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1",
56+
"validity": {
57+
"notBefore": "May 28 12:30:02 2019 GMT",
58+
"notAfter": "Aug 5 09:36:04 2021 GMT"
59+
}
60+
}
61+
},
62+
"path": "/my/path",
63+
"protocol": "HTTP/1.1",
64+
"requestId": "id=",
65+
"requestTime": "04/Mar/2020:19:15:17 +0000",
66+
"requestTimeEpoch": 1583349317135,
67+
"resourceId": null,
68+
"resourcePath": "/my/path",
69+
"stage": "$default"
70+
},
71+
"pathParameters": null,
72+
"stageVariables": null,
73+
"body": "Hello from Lambda!",
74+
"isBase64Encoded": true
75+
}

tests/functional/test_data_classes.py

+64
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,70 @@ def test_seq_trigger_event():
743743
assert record.aws_region == "us-east-2"
744744

745745

746+
def test_default_api_gateway_proxy_event():
747+
event = APIGatewayProxyEvent(load_event("apiGatewayProxyEvent_noVersionAuth.json"))
748+
749+
assert event.get("version") is None
750+
assert event.resource == event["resource"]
751+
assert event.path == event["path"]
752+
assert event.http_method == event["httpMethod"]
753+
assert event.headers == event["headers"]
754+
assert event.multi_value_headers == event["multiValueHeaders"]
755+
assert event.query_string_parameters == event["queryStringParameters"]
756+
assert event.multi_value_query_string_parameters == event["multiValueQueryStringParameters"]
757+
758+
request_context = event.request_context
759+
assert request_context.account_id == event["requestContext"]["accountId"]
760+
assert request_context.api_id == event["requestContext"]["apiId"]
761+
762+
assert request_context.get("authorizer") is None
763+
764+
assert request_context.domain_name == event["requestContext"]["domainName"]
765+
assert request_context.domain_prefix == event["requestContext"]["domainPrefix"]
766+
assert request_context.extended_request_id == event["requestContext"]["extendedRequestId"]
767+
assert request_context.http_method == event["requestContext"]["httpMethod"]
768+
769+
identity = request_context.identity
770+
assert identity.access_key == event["requestContext"]["identity"]["accessKey"]
771+
assert identity.account_id == event["requestContext"]["identity"]["accountId"]
772+
assert identity.caller == event["requestContext"]["identity"]["caller"]
773+
assert (
774+
identity.cognito_authentication_provider == event["requestContext"]["identity"]["cognitoAuthenticationProvider"]
775+
)
776+
assert identity.cognito_authentication_type == event["requestContext"]["identity"]["cognitoAuthenticationType"]
777+
assert identity.cognito_identity_id == event["requestContext"]["identity"]["cognitoIdentityId"]
778+
assert identity.cognito_identity_pool_id == event["requestContext"]["identity"]["cognitoIdentityPoolId"]
779+
assert identity.principal_org_id == event["requestContext"]["identity"]["principalOrgId"]
780+
assert identity.source_ip == event["requestContext"]["identity"]["sourceIp"]
781+
assert identity.user == event["requestContext"]["identity"]["user"]
782+
assert identity.user_agent == event["requestContext"]["identity"]["userAgent"]
783+
assert identity.user_arn == event["requestContext"]["identity"]["userArn"]
784+
785+
assert request_context.path == event["requestContext"]["path"]
786+
assert request_context.protocol == event["requestContext"]["protocol"]
787+
assert request_context.request_id == event["requestContext"]["requestId"]
788+
assert request_context.request_time == event["requestContext"]["requestTime"]
789+
assert request_context.request_time_epoch == event["requestContext"]["requestTimeEpoch"]
790+
assert request_context.resource_id == event["requestContext"]["resourceId"]
791+
assert request_context.resource_path == event["requestContext"]["resourcePath"]
792+
assert request_context.stage == event["requestContext"]["stage"]
793+
794+
assert event.path_parameters == event["pathParameters"]
795+
assert event.stage_variables == event["stageVariables"]
796+
assert event.body == event["body"]
797+
assert event.is_base64_encoded == event["isBase64Encoded"]
798+
799+
assert request_context.connected_at is None
800+
assert request_context.connection_id is None
801+
assert request_context.event_type is None
802+
assert request_context.message_direction is None
803+
assert request_context.message_id is None
804+
assert request_context.route_key is None
805+
assert request_context.operation_name is None
806+
assert identity.api_key is None
807+
assert identity.api_key_id is None
808+
809+
746810
def test_api_gateway_proxy_event():
747811
event = APIGatewayProxyEvent(load_event("apiGatewayProxyEvent.json"))
748812

0 commit comments

Comments
 (0)