Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit dcab235

Browse files
committedOct 29, 2021
Merge remote-tracking branch 'michaelbrewer/docs/router' into docs/router
* michaelbrewer/docs/router: feat(data-classes): ActiveMQ and RabbitMQ support (aws-powertools#770) feat(appsync): add Router to allow large resolver composition (aws-powertools#776) chore(deps-dev): bump mkdocs-material from 7.3.3 to 7.3.5 (aws-powertools#781) chore(deps-dev): bump flake8-isort from 4.0.0 to 4.1.1 (aws-powertools#785) chore(deps): bump urllib3 from 1.26.4 to 1.26.5 (aws-powertools#787) chore(deps-dev): bump flake8-eradicate from 1.1.0 to 1.2.0 (aws-powertools#784) chore(deps): bump boto3 from 1.18.61 to 1.19.6 (aws-powertools#783) chore(deps-dev): bump pytest-asyncio from 0.15.1 to 0.16.0 (aws-powertools#782) docs: fix indentation of SAM snippets in install section (aws-powertools#778) Fix middleware sample (aws-powertools#772) Removed unused import, added typing imports, fixed typo in example. (aws-powertools#774) Fix middleware sample (aws-powertools#772) Removed unused import, added typing imports, fixed typo in example. (aws-powertools#774) Update docs/core/event_handler/api_gateway.md # Conflicts: # docs/core/event_handler/api_gateway.md
2 parents 3632d18 + 94493dc commit dcab235

File tree

13 files changed

+608
-79
lines changed

13 files changed

+608
-79
lines changed
 

‎aws_lambda_powertools/event_handler/appsync.py

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from abc import ABC
23
from typing import Any, Callable, Optional, Type, TypeVar
34

45
from aws_lambda_powertools.utilities.data_classes import AppSyncResolverEvent
@@ -9,7 +10,33 @@
910
AppSyncResolverEventT = TypeVar("AppSyncResolverEventT", bound=AppSyncResolverEvent)
1011

1112

12-
class AppSyncResolver:
13+
class BaseRouter(ABC):
14+
current_event: AppSyncResolverEventT # type: ignore[valid-type]
15+
lambda_context: LambdaContext
16+
17+
def __init__(self):
18+
self._resolvers: dict = {}
19+
20+
def resolver(self, type_name: str = "*", field_name: Optional[str] = None):
21+
"""Registers the resolver for field_name
22+
23+
Parameters
24+
----------
25+
type_name : str
26+
Type name
27+
field_name : str
28+
Field name
29+
"""
30+
31+
def register_resolver(func):
32+
logger.debug(f"Adding resolver `{func.__name__}` for field `{type_name}.{field_name}`")
33+
self._resolvers[f"{type_name}.{field_name}"] = {"func": func}
34+
return func
35+
36+
return register_resolver
37+
38+
39+
class AppSyncResolver(BaseRouter):
1340
"""
1441
AppSync resolver decorator
1542
@@ -40,29 +67,8 @@ def common_field() -> str:
4067
return str(uuid.uuid4())
4168
"""
4269

43-
current_event: AppSyncResolverEventT # type: ignore[valid-type]
44-
lambda_context: LambdaContext
45-
4670
def __init__(self):
47-
self._resolvers: dict = {}
48-
49-
def resolver(self, type_name: str = "*", field_name: Optional[str] = None):
50-
"""Registers the resolver for field_name
51-
52-
Parameters
53-
----------
54-
type_name : str
55-
Type name
56-
field_name : str
57-
Field name
58-
"""
59-
60-
def register_resolver(func):
61-
logger.debug(f"Adding resolver `{func.__name__}` for field `{type_name}.{field_name}`")
62-
self._resolvers[f"{type_name}.{field_name}"] = {"func": func}
63-
return func
64-
65-
return register_resolver
71+
super().__init__()
6672

6773
def resolve(
6874
self, event: dict, context: LambdaContext, data_model: Type[AppSyncResolverEvent] = AppSyncResolverEvent
@@ -136,10 +142,10 @@ def lambda_handler(event, context):
136142
ValueError
137143
If we could not find a field resolver
138144
"""
139-
self.current_event = data_model(event)
140-
self.lambda_context = context
141-
resolver = self._get_resolver(self.current_event.type_name, self.current_event.field_name)
142-
return resolver(**self.current_event.arguments)
145+
BaseRouter.current_event = data_model(event)
146+
BaseRouter.lambda_context = context
147+
resolver = self._get_resolver(BaseRouter.current_event.type_name, BaseRouter.current_event.field_name)
148+
return resolver(**BaseRouter.current_event.arguments)
143149

144150
def _get_resolver(self, type_name: str, field_name: str) -> Callable:
145151
"""Get resolver for field_name
@@ -167,3 +173,18 @@ def __call__(
167173
) -> Any:
168174
"""Implicit lambda handler which internally calls `resolve`"""
169175
return self.resolve(event, context, data_model)
176+
177+
def include_router(self, router: "Router") -> None:
178+
"""Adds all resolvers defined in a router
179+
180+
Parameters
181+
----------
182+
router : Router
183+
A router containing a dict of field resolvers
184+
"""
185+
self._resolvers.update(router._resolvers)
186+
187+
188+
class Router(BaseRouter):
189+
def __init__(self):
190+
super().__init__()
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import base64
2+
import json
3+
from typing import Any, Iterator, Optional
4+
5+
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
6+
7+
8+
class ActiveMQMessage(DictWrapper):
9+
@property
10+
def message_id(self) -> str:
11+
"""Unique identifier for the message"""
12+
return self["messageID"]
13+
14+
@property
15+
def message_type(self) -> str:
16+
return self["messageType"]
17+
18+
@property
19+
def data(self) -> str:
20+
return self["data"]
21+
22+
@property
23+
def decoded_data(self) -> str:
24+
"""Decodes the data as a str"""
25+
return base64.b64decode(self.data.encode()).decode()
26+
27+
@property
28+
def json_data(self) -> Any:
29+
"""Parses the data as json"""
30+
return json.loads(self.decoded_data)
31+
32+
@property
33+
def connection_id(self) -> str:
34+
return self["connectionId"]
35+
36+
@property
37+
def redelivered(self) -> bool:
38+
"""true if the message is being resent to the consumer"""
39+
return self["redelivered"]
40+
41+
@property
42+
def timestamp(self) -> int:
43+
"""Time in milliseconds."""
44+
return self["timestamp"]
45+
46+
@property
47+
def broker_in_time(self) -> int:
48+
"""Time stamp (in milliseconds) for when the message arrived at the broker."""
49+
return self["brokerInTime"]
50+
51+
@property
52+
def broker_out_time(self) -> int:
53+
"""Time stamp (in milliseconds) for when the message left the broker."""
54+
return self["brokerOutTime"]
55+
56+
@property
57+
def destination_physicalname(self) -> str:
58+
return self["destination"]["physicalname"]
59+
60+
@property
61+
def delivery_mode(self) -> Optional[int]:
62+
"""persistent or non-persistent delivery"""
63+
return self.get("deliveryMode")
64+
65+
@property
66+
def correlation_id(self) -> Optional[str]:
67+
"""User defined correlation id"""
68+
return self.get("correlationID")
69+
70+
@property
71+
def reply_to(self) -> Optional[str]:
72+
"""User defined reply to"""
73+
return self.get("replyTo")
74+
75+
@property
76+
def get_type(self) -> Optional[str]:
77+
"""User defined message type"""
78+
return self.get("type")
79+
80+
@property
81+
def expiration(self) -> Optional[int]:
82+
"""Expiration attribute whose value is given in milliseconds"""
83+
return self.get("expiration")
84+
85+
@property
86+
def priority(self) -> Optional[int]:
87+
"""
88+
JMS defines a ten-level priority value, with 0 as the lowest priority and 9
89+
as the highest. In addition, clients should consider priorities 0-4 as
90+
gradations of normal priority and priorities 5-9 as gradations of expedited
91+
priority.
92+
93+
JMS does not require that a provider strictly implement priority ordering
94+
of messages; however, it should do its best to deliver expedited messages
95+
ahead of normal messages.
96+
"""
97+
return self.get("priority")
98+
99+
100+
class ActiveMQEvent(DictWrapper):
101+
"""Represents an Active MQ event sent to Lambda
102+
103+
Documentation:
104+
--------------
105+
- https://docs.aws.amazon.com/lambda/latest/dg/with-mq.html
106+
- https://aws.amazon.com/blogs/compute/using-amazon-mq-as-an-event-source-for-aws-lambda/
107+
"""
108+
109+
@property
110+
def event_source(self) -> str:
111+
return self["eventSource"]
112+
113+
@property
114+
def event_source_arn(self) -> str:
115+
"""The Amazon Resource Name (ARN) of the event source"""
116+
return self["eventSourceArn"]
117+
118+
@property
119+
def messages(self) -> Iterator[ActiveMQMessage]:
120+
for record in self["messages"]:
121+
yield ActiveMQMessage(record)
122+
123+
@property
124+
def message(self) -> ActiveMQMessage:
125+
return next(self.messages)
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import base64
2+
import json
3+
from typing import Any, Dict, List
4+
5+
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
6+
7+
8+
class BasicProperties(DictWrapper):
9+
@property
10+
def content_type(self) -> str:
11+
return self["contentType"]
12+
13+
@property
14+
def content_encoding(self) -> str:
15+
return self["contentEncoding"]
16+
17+
@property
18+
def headers(self) -> Dict[str, Any]:
19+
return self["headers"]
20+
21+
@property
22+
def delivery_mode(self) -> int:
23+
return self["deliveryMode"]
24+
25+
@property
26+
def priority(self) -> int:
27+
return self["priority"]
28+
29+
@property
30+
def correlation_id(self) -> str:
31+
return self["correlationId"]
32+
33+
@property
34+
def reply_to(self) -> str:
35+
return self["replyTo"]
36+
37+
@property
38+
def expiration(self) -> str:
39+
return self["expiration"]
40+
41+
@property
42+
def message_id(self) -> str:
43+
return self["messageId"]
44+
45+
@property
46+
def timestamp(self) -> str:
47+
return self["timestamp"]
48+
49+
@property
50+
def get_type(self) -> str:
51+
return self["type"]
52+
53+
@property
54+
def user_id(self) -> str:
55+
return self["userId"]
56+
57+
@property
58+
def app_id(self) -> str:
59+
return self["appId"]
60+
61+
@property
62+
def cluster_id(self) -> str:
63+
return self["clusterId"]
64+
65+
@property
66+
def body_size(self) -> int:
67+
return self["bodySize"]
68+
69+
70+
class RabbitMessage(DictWrapper):
71+
@property
72+
def basic_properties(self) -> BasicProperties:
73+
return BasicProperties(self["basicProperties"])
74+
75+
@property
76+
def redelivered(self) -> bool:
77+
return self["redelivered"]
78+
79+
@property
80+
def data(self) -> str:
81+
return self["data"]
82+
83+
@property
84+
def decoded_data(self) -> str:
85+
"""Decodes the data as a str"""
86+
return base64.b64decode(self.data.encode()).decode()
87+
88+
@property
89+
def json_data(self) -> Any:
90+
"""Parses the data as json"""
91+
return json.loads(self.decoded_data)
92+
93+
94+
class RabbitMQEvent(DictWrapper):
95+
"""Represents a Rabbit MQ event sent to Lambda
96+
97+
Documentation:
98+
--------------
99+
- https://docs.aws.amazon.com/lambda/latest/dg/with-mq.html
100+
- https://aws.amazon.com/blogs/compute/using-amazon-mq-for-rabbitmq-as-an-event-source-for-lambda/
101+
"""
102+
103+
def __init__(self, data: Dict[str, Any]):
104+
super().__init__(data)
105+
self._rmq_messages_by_queue = {
106+
key: [RabbitMessage(message) for message in messages]
107+
for key, messages in self["rmqMessagesByQueue"].items()
108+
}
109+
110+
@property
111+
def event_source(self) -> str:
112+
return self["eventSource"]
113+
114+
@property
115+
def event_source_arn(self) -> str:
116+
"""The Amazon Resource Name (ARN) of the event source"""
117+
return self["eventSourceArn"]
118+
119+
@property
120+
def rmq_messages_by_queue(self) -> Dict[str, List[RabbitMessage]]:
121+
return self._rmq_messages_by_queue

‎docs/index.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ Include Lambda Powertools in your function using the [AWS Lambda Console](https:
4343
MyLambdaFunction:
4444
Type: AWS::Serverless::Function
4545
Properties:
46-
Layers:
47-
- !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:3
46+
Layers:
47+
- !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:3
4848
```
4949

5050
=== "Serverless framework"
@@ -196,16 +196,16 @@ If using SAM, you can include this SAR App as part of your shared Layers stack,
196196
AwsLambdaPowertoolsPythonLayer:
197197
Type: AWS::Serverless::Application
198198
Properties:
199-
Location:
200-
ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer
201-
SemanticVersion: 1.21.1 # change to latest semantic version available in SAR
199+
Location:
200+
ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer
201+
SemanticVersion: 1.21.1 # change to latest semantic version available in SAR
202202

203203
MyLambdaFunction:
204204
Type: AWS::Serverless::Function
205205
Properties:
206-
Layers:
207-
# fetch Layer ARN from SAR App stack output
208-
- !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn
206+
Layers:
207+
# fetch Layer ARN from SAR App stack output
208+
- !GetAtt AwsLambdaPowertoolsPythonLayer.Outputs.LayerVersionArn
209209
```
210210

211211
=== "Serverless framework"
@@ -223,10 +223,10 @@ If using SAM, you can include this SAR App as part of your shared Layers stack,
223223
AwsLambdaPowertoolsPythonLayer:
224224
Type: AWS::Serverless::Application
225225
Properties:
226-
Location:
227-
ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer
228-
# Find latest from github.com/awslabs/aws-lambda-powertools-python/releases
229-
SemanticVersion: 1.21.1
226+
Location:
227+
ApplicationId: arn:aws:serverlessrepo:eu-west-1:057560766410:applications/aws-lambda-powertools-python-layer
228+
# Find latest from github.com/awslabs/aws-lambda-powertools-python/releases
229+
SemanticVersion: 1.21.1
230230
```
231231

232232
=== "CDK"

‎docs/utilities/data_classes.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Same example as above, but using the `event_source` decorator
5858

5959
Event Source | Data_class
6060
------------------------------------------------- | ---------------------------------------------------------------------------------
61+
[Active MQ](#active-mq) | `ActiveMQEvent`
6162
[API Gateway Authorizer](#api-gateway-authorizer) | `APIGatewayAuthorizerRequestEvent`
6263
[API Gateway Authorizer V2](#api-gateway-authorizer-v2) | `APIGatewayAuthorizerEventV2`
6364
[API Gateway Proxy](#api-gateway-proxy) | `APIGatewayProxyEvent`
@@ -72,6 +73,7 @@ Event Source | Data_class
7273
[DynamoDB streams](#dynamodb-streams) | `DynamoDBStreamEvent`, `DynamoDBRecordEventName`
7374
[EventBridge](#eventbridge) | `EventBridgeEvent`
7475
[Kinesis Data Stream](#kinesis-streams) | `KinesisStreamEvent`
76+
[Rabbit MQ](#rabbit-mq) | `RabbitMQEvent`
7577
[S3](#s3) | `S3Event`
7678
[S3 Object Lambda](#s3-object-lambda) | `S3ObjectLambdaEvent`
7779
[SES](#ses) | `SESEvent`
@@ -82,6 +84,31 @@ Event Source | Data_class
8284
The examples provided below are far from exhaustive - the data classes themselves are designed to provide a form of
8385
documentation inherently (via autocompletion, types and docstrings).
8486

87+
### Active MQ
88+
89+
It is used for [Active MQ payloads](https://docs.aws.amazon.com/lambda/latest/dg/with-mq.html){target="_blank"}, also see
90+
the [AWS blog post](https://aws.amazon.com/blogs/compute/using-amazon-mq-as-an-event-source-for-aws-lambda/){target="_blank"}
91+
for more details.
92+
93+
=== "app.py"
94+
95+
```python hl_lines="4-5 9-10"
96+
from typing import Dict
97+
98+
from aws_lambda_powertools import Logger
99+
from aws_lambda_powertools.utilities.data_classes import event_source
100+
from aws_lambda_powertools.utilities.data_classes.active_mq_event import ActiveMQEvent
101+
102+
logger = Logger()
103+
104+
@event_source(data_class=ActiveMQEvent)
105+
def lambda_handler(event: ActiveMQEvent, context):
106+
for message in event.messages:
107+
logger.debug(f"MessageID: {message.message_id}")
108+
data: Dict = message.json_data
109+
logger.debug("Process json in base64 encoded data str", data)
110+
```
111+
85112
### API Gateway Authorizer
86113

87114
> New in 1.20.0
@@ -810,6 +837,33 @@ or plain text, depending on the original payload.
810837
do_something_with(data)
811838
```
812839

840+
### Rabbit MQ
841+
842+
It is used for [Rabbit MQ payloads](https://docs.aws.amazon.com/lambda/latest/dg/with-mq.html){target="_blank"}, also see
843+
the [blog post](https://aws.amazon.com/blogs/compute/using-amazon-mq-for-rabbitmq-as-an-event-source-for-lambda/){target="_blank"}
844+
for more details.
845+
846+
=== "app.py"
847+
848+
```python hl_lines="4-5 9-10"
849+
from typing import Dict
850+
851+
from aws_lambda_powertools import Logger
852+
from aws_lambda_powertools.utilities.data_classes import event_source
853+
from aws_lambda_powertools.utilities.data_classes.rabbit_mq_event import RabbitMQEvent
854+
855+
logger = Logger()
856+
857+
@event_source(data_class=RabbitMQEvent)
858+
def lambda_handler(event: RabbitMQEvent, context):
859+
for queue_name, messages in event.rmq_messages_by_queue.items():
860+
logger.debug(f"Messages for queue: {queue_name}")
861+
for message in messages:
862+
logger.debug(f"MessageID: {message.basic_properties.message_id}")
863+
data: Dict = message.json_data
864+
logger.debug("Process json in base64 encoded data str", data)
865+
```
866+
813867
### S3
814868

815869
=== "app.py"

‎docs/utilities/middleware_factory.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ You can also have your own keyword arguments after the mandatory arguments.
4747
# Obfuscate email before calling Lambda handler
4848
if fields:
4949
for field in fields:
50-
field = event.get(field, "")
5150
if field in event:
52-
event[field] = obfuscate(field)
51+
event[field] = obfuscate(event[field])
5352

5453
return handler(event, context)
5554

‎docs/utilities/parser.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ Use the decorator for fail fast scenarios where you want your Lambda function to
5757
=== "event_parser_decorator.py"
5858

5959
```python hl_lines="18"
60-
from aws_lambda_powertools.utilities.parser import event_parser, BaseModel, ValidationError
60+
from aws_lambda_powertools.utilities.parser import event_parser, BaseModel
6161
from aws_lambda_powertools.utilities.typing import LambdaContext
62+
from typing import List, Optional
6263

6364
import json
6465

@@ -80,7 +81,7 @@ Use the decorator for fail fast scenarios where you want your Lambda function to
8081
print(event.description)
8182
print(event.items)
8283

83-
order_items = [items for item in event.items]
84+
order_items = [item for item in event.items]
8485
...
8586

8687
payload = {
@@ -107,6 +108,7 @@ Use this standalone function when you want more control over the data validation
107108

108109
```python hl_lines="21 30"
109110
from aws_lambda_powertools.utilities.parser import parse, BaseModel, ValidationError
111+
from typing import List, Optional
110112

111113
class OrderItem(BaseModel):
112114
id: int

‎poetry.lock

Lines changed: 47 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@ flake8-builtins = "^1.5.3"
3838
flake8-comprehensions = "^3.7.0"
3939
flake8-debugger = "^4.0.0"
4040
flake8-fixme = "^1.1.1"
41-
flake8-isort = "^4.0.0"
41+
flake8-isort = "^4.1.1"
4242
flake8-variables-names = "^0.0.4"
4343
isort = "^5.9.3"
4444
pytest-cov = "^3.0.0"
4545
pytest-mock = "^3.5.1"
4646
pdoc3 = "^0.10.0"
47-
pytest-asyncio = "^0.15.1"
47+
pytest-asyncio = "^0.16.0"
4848
bandit = "^1.7.0"
4949
radon = "^5.1.0"
5050
xenon = "^0.8.0"
51-
flake8-eradicate = "^1.1.0"
51+
flake8-eradicate = "^1.2.0"
5252
flake8-bugbear = "^21.9.2"
53-
mkdocs-material = "^7.3.3"
53+
mkdocs-material = "^7.3.5"
5454
mkdocs-git-revision-date-plugin = "^0.3.1"
5555
mike = "^0.6.0"
5656
mypy = "^0.910"

‎tests/events/activeMQEvent.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"eventSource": "aws:amq",
3+
"eventSourceArn": "arn:aws:mq:us-west-2:112556298976:broker:test:b-9bcfa592-423a-4942-879d-eb284b418fc8",
4+
"messages": [
5+
{
6+
"messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1",
7+
"messageType": "jms/text-message",
8+
"data": "QUJDOkFBQUE=",
9+
"connectionId": "myJMSCoID",
10+
"redelivered": false,
11+
"destination": {
12+
"physicalname": "testQueue"
13+
},
14+
"timestamp": 1598827811958,
15+
"brokerInTime": 1598827811958,
16+
"brokerOutTime": 1598827811959
17+
},
18+
{
19+
"messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1",
20+
"messageType": "jms/text-message",
21+
"data": "eyJ0aW1lb3V0IjowLCJkYXRhIjoiQ1pybWYwR3c4T3Y0YnFMUXhENEUifQ==",
22+
"connectionId": "myJMSCoID2",
23+
"redelivered": false,
24+
"destination": {
25+
"physicalname": "testQueue"
26+
},
27+
"timestamp": 1598827811958,
28+
"brokerInTime": 1598827811958,
29+
"brokerOutTime": 1598827811959
30+
},
31+
{
32+
"messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1",
33+
"messageType": "jms/bytes-message",
34+
"data": "3DTOOW7crj51prgVLQaGQ82S48k=",
35+
"connectionId": "myJMSCoID1",
36+
"persistent": false,
37+
"destination": {
38+
"physicalname": "testQueue"
39+
},
40+
"timestamp": 1598827811958,
41+
"brokerInTime": 1598827811958,
42+
"brokerOutTime": 1598827811959
43+
}
44+
]
45+
}

‎tests/events/rabbitMQEvent.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"eventSource": "aws:rmq",
3+
"eventSourceArn": "arn:aws:mq:us-west-2:112556298976:broker:pizzaBroker:b-9bcfa592-423a-4942-879d-eb284b418fc8",
4+
"rmqMessagesByQueue": {
5+
"pizzaQueue::/": [
6+
{
7+
"basicProperties": {
8+
"contentType": "text/plain",
9+
"contentEncoding": null,
10+
"headers": {
11+
"header1": {
12+
"bytes": [
13+
118,
14+
97,
15+
108,
16+
117,
17+
101,
18+
49
19+
]
20+
},
21+
"header2": {
22+
"bytes": [
23+
118,
24+
97,
25+
108,
26+
117,
27+
101,
28+
50
29+
]
30+
},
31+
"numberInHeader": 10
32+
},
33+
"deliveryMode": 1,
34+
"priority": 34,
35+
"correlationId": null,
36+
"replyTo": null,
37+
"expiration": "60000",
38+
"messageId": null,
39+
"timestamp": "Jan 1, 1970, 12:33:41 AM",
40+
"type": null,
41+
"userId": "AIDACKCEVSQ6C2EXAMPLE",
42+
"appId": null,
43+
"clusterId": null,
44+
"bodySize": 80
45+
},
46+
"redelivered": false,
47+
"data": "eyJ0aW1lb3V0IjowLCJkYXRhIjoiQ1pybWYwR3c4T3Y0YnFMUXhENEUifQ=="
48+
}
49+
]
50+
}
51+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from typing import Dict
2+
3+
from aws_lambda_powertools.utilities.data_classes.active_mq_event import ActiveMQEvent, ActiveMQMessage
4+
from aws_lambda_powertools.utilities.data_classes.rabbit_mq_event import BasicProperties, RabbitMessage, RabbitMQEvent
5+
from tests.functional.utils import load_event
6+
7+
8+
def test_active_mq_event():
9+
event = ActiveMQEvent(load_event("activeMQEvent.json"))
10+
11+
assert event.event_source == "aws:amq"
12+
assert event.event_source_arn is not None
13+
assert len(list(event.messages)) == 3
14+
15+
message = event.message
16+
assert isinstance(message, ActiveMQMessage)
17+
assert message.message_id is not None
18+
assert message.message_type is not None
19+
assert message.data is not None
20+
assert message.decoded_data is not None
21+
assert message.connection_id is not None
22+
assert message.redelivered is False
23+
assert message.timestamp is not None
24+
assert message.broker_in_time is not None
25+
assert message.broker_out_time is not None
26+
assert message.destination_physicalname is not None
27+
assert message.delivery_mode is None
28+
assert message.correlation_id is None
29+
assert message.reply_to is None
30+
assert message.get_type is None
31+
assert message.expiration is None
32+
assert message.priority is None
33+
34+
messages = list(event.messages)
35+
message = messages[1]
36+
assert message.json_data["timeout"] == 0
37+
38+
39+
def test_rabbit_mq_event():
40+
event = RabbitMQEvent(load_event("rabbitMQEvent.json"))
41+
42+
assert event.event_source == "aws:rmq"
43+
assert event.event_source_arn is not None
44+
45+
message = event.rmq_messages_by_queue["pizzaQueue::/"][0]
46+
assert message.redelivered is False
47+
assert message.data is not None
48+
assert message.decoded_data is not None
49+
assert message.json_data["timeout"] == 0
50+
51+
assert isinstance(message, RabbitMessage)
52+
properties = message.basic_properties
53+
assert isinstance(properties, BasicProperties)
54+
assert properties.content_type == "text/plain"
55+
assert properties.content_encoding is None
56+
assert isinstance(properties.headers, Dict)
57+
assert properties.headers["header1"] is not None
58+
assert properties.delivery_mode == 1
59+
assert properties.priority == 34
60+
assert properties.correlation_id is None
61+
assert properties.reply_to is None
62+
assert properties.expiration == "60000"
63+
assert properties.message_id is None
64+
assert properties.timestamp is not None
65+
assert properties.get_type is None
66+
assert properties.user_id is not None
67+
assert properties.app_id is None
68+
assert properties.cluster_id is None
69+
assert properties.body_size == 80

‎tests/functional/event_handler/test_appsync.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
from aws_lambda_powertools.event_handler import AppSyncResolver
7+
from aws_lambda_powertools.event_handler.appsync import Router
78
from aws_lambda_powertools.utilities.data_classes import AppSyncResolverEvent
89
from aws_lambda_powertools.utilities.typing import LambdaContext
910
from tests.functional.utils import load_event
@@ -161,3 +162,29 @@ def create_something(id: str): # noqa AA03 VNE003
161162
assert result == "my identifier"
162163

163164
assert app.current_event.country_viewer == "US"
165+
166+
167+
def test_resolver_include_resolver():
168+
# GIVEN
169+
app = AppSyncResolver()
170+
router = Router()
171+
172+
@router.resolver(type_name="Query", field_name="listLocations")
173+
def get_locations(name: str):
174+
return "get_locations#" + name
175+
176+
@app.resolver(field_name="listLocations2")
177+
def get_locations2(name: str):
178+
return "get_locations2#" + name
179+
180+
app.include_router(router)
181+
182+
# WHEN
183+
mock_event1 = {"typeName": "Query", "fieldName": "listLocations", "arguments": {"name": "value"}}
184+
mock_event2 = {"typeName": "Query", "fieldName": "listLocations2", "arguments": {"name": "value"}}
185+
result1 = app.resolve(mock_event1, LambdaContext())
186+
result2 = app.resolve(mock_event2, LambdaContext())
187+
188+
# THEN
189+
assert result1 == "get_locations#value"
190+
assert result2 == "get_locations2#value"

0 commit comments

Comments
 (0)
Please sign in to comment.