Skip to content

Commit 5367f61

Browse files
authored
feat(data-classes): Add connect contact flow event (#304)
1 parent 12c512b commit 5367f61

File tree

5 files changed

+303
-0
lines changed

5 files changed

+303
-0
lines changed

aws_lambda_powertools/utilities/data_classes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .alb_event import ALBEvent
22
from .api_gateway_proxy_event import APIGatewayProxyEvent, APIGatewayProxyEventV2
33
from .cloud_watch_logs_event import CloudWatchLogsEvent
4+
from .connect_contact_flow_event import ConnectContactFlowEvent
45
from .dynamo_db_stream_event import DynamoDBStreamEvent
56
from .event_bridge_event import EventBridgeEvent
67
from .kinesis_stream_event import KinesisStreamEvent
@@ -14,6 +15,7 @@
1415
"APIGatewayProxyEventV2",
1516
"ALBEvent",
1617
"CloudWatchLogsEvent",
18+
"ConnectContactFlowEvent",
1719
"DynamoDBStreamEvent",
1820
"EventBridgeEvent",
1921
"KinesisStreamEvent",
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
from enum import Enum, auto
2+
from typing import Dict, Optional
3+
4+
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
5+
6+
7+
class ConnectContactFlowChannel(Enum):
8+
VOICE = auto()
9+
CHAT = auto()
10+
11+
12+
class ConnectContactFlowEndpointType(Enum):
13+
TELEPHONE_NUMBER = auto()
14+
15+
16+
class ConnectContactFlowInitiationMethod(Enum):
17+
INBOUND = auto()
18+
OUTBOUND = auto()
19+
TRANSFER = auto()
20+
CALLBACK = auto()
21+
API = auto()
22+
23+
24+
class ConnectContactFlowEndpoint(DictWrapper):
25+
@property
26+
def address(self) -> str:
27+
"""The phone number."""
28+
return self["Address"]
29+
30+
@property
31+
def endpoint_type(self) -> ConnectContactFlowEndpointType:
32+
"""The enpoint type."""
33+
return ConnectContactFlowEndpointType[self["Type"]]
34+
35+
36+
class ConnectContactFlowQueue(DictWrapper):
37+
@property
38+
def arn(self) -> str:
39+
"""The unique queue ARN."""
40+
return self["ARN"]
41+
42+
@property
43+
def name(self) -> str:
44+
"""The queue name."""
45+
return self["Name"]
46+
47+
48+
class ConnectContactFlowMediaStreamAudio(DictWrapper):
49+
@property
50+
def start_fragment_number(self) -> Optional[str]:
51+
"""The number that identifies the Kinesis Video Streams fragment, in the stream used for Live media streaming,
52+
in which the customer audio stream started.
53+
"""
54+
return self["StartFragmentNumber"]
55+
56+
@property
57+
def start_timestamp(self) -> Optional[str]:
58+
"""When the customer audio stream started."""
59+
return self["StartTimestamp"]
60+
61+
@property
62+
def stream_arn(self) -> Optional[str]:
63+
"""The ARN of the Kinesis Video stream used for Live media streaming that includes the customer data to
64+
reference.
65+
"""
66+
return self["StreamARN"]
67+
68+
69+
class ConnectContactFlowMediaStreamCustomer(DictWrapper):
70+
@property
71+
def audio(self) -> ConnectContactFlowMediaStreamAudio:
72+
return ConnectContactFlowMediaStreamAudio(self["Audio"])
73+
74+
75+
class ConnectContactFlowMediaStreams(DictWrapper):
76+
@property
77+
def customer(self) -> ConnectContactFlowMediaStreamCustomer:
78+
return ConnectContactFlowMediaStreamCustomer(self["Customer"])
79+
80+
81+
class ConnectContactFlowData(DictWrapper):
82+
@property
83+
def attributes(self) -> Dict[str, str]:
84+
"""These are attributes that have been previously associated with a contact,
85+
such as when using a Set contact attributes block in a contact flow.
86+
This map may be empty if there aren't any saved attributes.
87+
"""
88+
return self["Attributes"]
89+
90+
@property
91+
def channel(self) -> ConnectContactFlowChannel:
92+
"""The method used to contact your contact center."""
93+
return ConnectContactFlowChannel[self["Channel"]]
94+
95+
@property
96+
def contact_id(self) -> str:
97+
"""The unique identifier of the contact."""
98+
return self["ContactId"]
99+
100+
@property
101+
def customer_endpoint(self) -> Optional[ConnectContactFlowEndpoint]:
102+
"""Contains the customer’s address (number) and type of address."""
103+
if self["CustomerEndpoint"] is not None:
104+
return ConnectContactFlowEndpoint(self["CustomerEndpoint"])
105+
return None
106+
107+
@property
108+
def initial_contact_id(self) -> str:
109+
"""The unique identifier for the contact associated with the first interaction between the customer and your
110+
contact center. Use the initial contact ID to track contacts between contact flows.
111+
"""
112+
return self["InitialContactId"]
113+
114+
@property
115+
def initiation_method(self) -> ConnectContactFlowInitiationMethod:
116+
"""How the contact was initiated."""
117+
return ConnectContactFlowInitiationMethod[self["InitiationMethod"]]
118+
119+
@property
120+
def instance_arn(self) -> str:
121+
"""The ARN for your Amazon Connect instance."""
122+
return self["InstanceARN"]
123+
124+
@property
125+
def previous_contact_id(self) -> str:
126+
"""The unique identifier for the contact before it was transferred.
127+
Use the previous contact ID to trace contacts between contact flows.
128+
"""
129+
return self["PreviousContactId"]
130+
131+
@property
132+
def queue(self) -> Optional[ConnectContactFlowQueue]:
133+
"""The current queue."""
134+
if self["Queue"] is not None:
135+
return ConnectContactFlowQueue(self["Queue"])
136+
return None
137+
138+
@property
139+
def system_endpoint(self) -> Optional[ConnectContactFlowEndpoint]:
140+
"""Contains the address (number) the customer dialed to call your contact center and type of address."""
141+
if self["SystemEndpoint"] is not None:
142+
return ConnectContactFlowEndpoint(self["SystemEndpoint"])
143+
return None
144+
145+
@property
146+
def media_streams(self) -> ConnectContactFlowMediaStreams:
147+
return ConnectContactFlowMediaStreams(self["MediaStreams"])
148+
149+
150+
class ConnectContactFlowEvent(DictWrapper):
151+
"""Amazon Connect contact flow event
152+
153+
Documentation:
154+
-------------
155+
- https://docs.aws.amazon.com/connect/latest/adminguide/connect-lambda-functions.html
156+
"""
157+
158+
@property
159+
def contact_data(self) -> ConnectContactFlowData:
160+
"""This is always passed by Amazon Connect for every contact. Some parameters are optional."""
161+
return ConnectContactFlowData(self["Details"]["ContactData"])
162+
163+
@property
164+
def parameters(self) -> Dict[str, str]:
165+
"""These are parameters specific to this call that were defined when you created the Lambda function."""
166+
return self["Details"]["Parameters"]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"Name": "ContactFlowEvent",
3+
"Details": {
4+
"ContactData": {
5+
"Attributes": {
6+
"Language": "en-US"
7+
},
8+
"Channel": "VOICE",
9+
"ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
10+
"CustomerEndpoint": {
11+
"Address": "+11234567890",
12+
"Type": "TELEPHONE_NUMBER"
13+
},
14+
"InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
15+
"InitiationMethod": "API",
16+
"InstanceARN": "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa",
17+
"MediaStreams": {
18+
"Customer": {
19+
"Audio": {
20+
"StartFragmentNumber": "91343852333181432392682062622220590765191907586",
21+
"StartTimestamp": "1565781909613",
22+
"StreamARN": "arn:aws:kinesisvideo:eu-central-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806"
23+
}
24+
}
25+
},
26+
"PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
27+
"Queue": {
28+
"ARN": "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa/queue/5cba7cbf-1ecb-4b6d-b8bd-fe91079b3fc8",
29+
"Name": "QueueOne"
30+
},
31+
"SystemEndpoint": {
32+
"Address": "+11234567890",
33+
"Type": "TELEPHONE_NUMBER"
34+
}
35+
},
36+
"Parameters": {
37+
"ParameterOne": "One",
38+
"ParameterTwo": "Two"
39+
}
40+
}
41+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"Name": "ContactFlowEvent",
3+
"Details": {
4+
"ContactData": {
5+
"Attributes": {},
6+
"Channel": "VOICE",
7+
"ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
8+
"CustomerEndpoint": null,
9+
"InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
10+
"InitiationMethod": "API",
11+
"InstanceARN": "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa",
12+
"MediaStreams": {
13+
"Customer": {
14+
"Audio": {
15+
"StartFragmentNumber": null,
16+
"StartTimestamp": null,
17+
"StreamARN": null
18+
}
19+
}
20+
},
21+
"PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
22+
"Queue": null,
23+
"SystemEndpoint": null
24+
},
25+
"Parameters": {}
26+
}
27+
}

tests/functional/test_lambda_trigger_events.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
VerifyAuthChallengeResponseTriggerEvent,
3030
)
3131
from aws_lambda_powertools.utilities.data_classes.common import BaseProxyEvent, DictWrapper
32+
from aws_lambda_powertools.utilities.data_classes.connect_contact_flow_event import (
33+
ConnectContactFlowChannel,
34+
ConnectContactFlowEndpointType,
35+
ConnectContactFlowEvent,
36+
ConnectContactFlowInitiationMethod,
37+
)
3238
from aws_lambda_powertools.utilities.data_classes.dynamo_db_stream_event import (
3339
AttributeValue,
3440
DynamoDBRecordEventName,
@@ -318,6 +324,67 @@ def test_verify_auth_challenge_response_trigger_event():
318324
assert event.response.answer_correct is True
319325

320326

327+
def test_connect_contact_flow_event_min():
328+
event = ConnectContactFlowEvent(load_event("connectContactFlowEventMin.json"))
329+
330+
assert event.contact_data.attributes == {}
331+
assert event.contact_data.channel == ConnectContactFlowChannel.VOICE
332+
assert event.contact_data.contact_id == "5ca32fbd-8f92-46af-92a5-6b0f970f0efe"
333+
assert event.contact_data.customer_endpoint is None
334+
assert event.contact_data.initial_contact_id == "5ca32fbd-8f92-46af-92a5-6b0f970f0efe"
335+
assert event.contact_data.initiation_method == ConnectContactFlowInitiationMethod.API
336+
assert (
337+
event.contact_data.instance_arn
338+
== "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa"
339+
)
340+
assert event.contact_data.media_streams.customer.audio.start_fragment_number is None
341+
assert event.contact_data.media_streams.customer.audio.start_timestamp is None
342+
assert event.contact_data.media_streams.customer.audio.stream_arn is None
343+
assert event.contact_data.previous_contact_id == "5ca32fbd-8f92-46af-92a5-6b0f970f0efe"
344+
assert event.contact_data.queue is None
345+
assert event.contact_data.system_endpoint is None
346+
assert event.parameters == {}
347+
348+
349+
def test_connect_contact_flow_event_all():
350+
event = ConnectContactFlowEvent(load_event("connectContactFlowEventAll.json"))
351+
352+
assert event.contact_data.attributes == {"Language": "en-US"}
353+
assert event.contact_data.channel == ConnectContactFlowChannel.VOICE
354+
assert event.contact_data.contact_id == "5ca32fbd-8f92-46af-92a5-6b0f970f0efe"
355+
assert event.contact_data.customer_endpoint is not None
356+
assert event.contact_data.customer_endpoint.address == "+11234567890"
357+
assert event.contact_data.customer_endpoint.endpoint_type == ConnectContactFlowEndpointType.TELEPHONE_NUMBER
358+
assert event.contact_data.initial_contact_id == "5ca32fbd-8f92-46af-92a5-6b0f970f0efe"
359+
assert event.contact_data.initiation_method == ConnectContactFlowInitiationMethod.API
360+
assert (
361+
event.contact_data.instance_arn
362+
== "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa"
363+
)
364+
assert (
365+
event.contact_data.media_streams.customer.audio.start_fragment_number
366+
== "91343852333181432392682062622220590765191907586"
367+
)
368+
assert event.contact_data.media_streams.customer.audio.start_timestamp == "1565781909613"
369+
assert (
370+
event.contact_data.media_streams.customer.audio.stream_arn
371+
== "arn:aws:kinesisvideo:eu-central-1:123456789012:stream/"
372+
+ "connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806"
373+
)
374+
assert event.contact_data.previous_contact_id == "5ca32fbd-8f92-46af-92a5-6b0f970f0efe"
375+
assert event.contact_data.queue is not None
376+
assert (
377+
event.contact_data.queue.arn
378+
== "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa/"
379+
+ "queue/5cba7cbf-1ecb-4b6d-b8bd-fe91079b3fc8"
380+
)
381+
assert event.contact_data.queue.name == "QueueOne"
382+
assert event.contact_data.system_endpoint is not None
383+
assert event.contact_data.system_endpoint.address == "+11234567890"
384+
assert event.contact_data.system_endpoint.endpoint_type == ConnectContactFlowEndpointType.TELEPHONE_NUMBER
385+
assert event.parameters == {"ParameterOne": "One", "ParameterTwo": "Two"}
386+
387+
321388
def test_dynamo_db_stream_trigger_event():
322389
event = DynamoDBStreamEvent(load_event("dynamoStreamEvent.json"))
323390

0 commit comments

Comments
 (0)