Skip to content

Commit dc33a6c

Browse files
authored
Merge branch 'main' into emmett.butler/nine-more-kilobytes
2 parents 4f1e25b + 12a2a5a commit dc33a6c

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

datadog_lambda/durable.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,17 @@ def extract_durable_function_tags(event):
4747
"durable_function_execution_name": execution_name,
4848
"durable_function_execution_id": execution_id,
4949
}
50+
51+
52+
VALID_DURABLE_STATUSES = {"SUCCEEDED", "FAILED", "STOPPED", "TIMED_OUT"}
53+
54+
55+
def extract_durable_execution_status(response, event):
56+
if not isinstance(event, dict) or "DurableExecutionArn" not in event:
57+
return None
58+
if not isinstance(response, dict):
59+
return None
60+
status = response.get("Status")
61+
if status not in VALID_DURABLE_STATUSES:
62+
return None
63+
return status

datadog_lambda/wrapper.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
tracer,
4343
propagator,
4444
)
45-
from datadog_lambda.durable import extract_durable_function_tags
45+
from datadog_lambda.durable import (
46+
extract_durable_function_tags,
47+
extract_durable_execution_status,
48+
)
4649
from datadog_lambda.trigger import (
4750
extract_trigger_tags,
4851
extract_http_status_code_tag,
@@ -153,7 +156,7 @@ def __init__(self, func):
153156
if config.trace_extractor:
154157
extractor_parts = config.trace_extractor.rsplit(".", 1)
155158
if len(extractor_parts) == 2:
156-
(mod_name, extractor_name) = extractor_parts
159+
mod_name, extractor_name = extractor_parts
157160
modified_extractor_name = modify_module_name(mod_name)
158161
extractor_module = import_module(modified_extractor_name)
159162
self.trace_extractor = getattr(extractor_module, extractor_name)
@@ -340,6 +343,13 @@ def _after(self, event, context):
340343
if status_code:
341344
self.span.set_tag("http.status_code", status_code)
342345

346+
durable_status = extract_durable_execution_status(self.response, event)
347+
if durable_status:
348+
self.span.set_tag(
349+
"aws_lambda.durable_function.execution_status",
350+
durable_status,
351+
)
352+
343353
self.span.finish()
344354

345355
if status_code:

tests/test_durable.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from datadog_lambda.durable import (
88
_parse_durable_execution_arn,
99
extract_durable_function_tags,
10+
extract_durable_execution_status,
1011
)
1112

1213

@@ -89,3 +90,72 @@ def test_returns_empty_dict_when_durable_execution_arn_cannot_be_parsed(self):
8990
def test_returns_empty_dict_when_event_is_empty(self):
9091
result = extract_durable_function_tags({})
9192
self.assertEqual(result, {})
93+
94+
95+
class TestExtractDurableExecutionStatus(unittest.TestCase):
96+
def test_returns_succeeded(self):
97+
event = {
98+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
99+
}
100+
response = {"Status": "SUCCEEDED", "Result": "some-result"}
101+
self.assertEqual(extract_durable_execution_status(response, event), "SUCCEEDED")
102+
103+
def test_returns_failed(self):
104+
event = {
105+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
106+
}
107+
response = {"Status": "FAILED", "Error": "some-error"}
108+
self.assertEqual(extract_durable_execution_status(response, event), "FAILED")
109+
110+
def test_returns_stopped(self):
111+
event = {
112+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
113+
}
114+
response = {"Status": "STOPPED"}
115+
self.assertEqual(extract_durable_execution_status(response, event), "STOPPED")
116+
117+
def test_returns_timed_out(self):
118+
event = {
119+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
120+
}
121+
response = {"Status": "TIMED_OUT"}
122+
self.assertEqual(extract_durable_execution_status(response, event), "TIMED_OUT")
123+
124+
def test_returns_none_for_non_durable_event(self):
125+
event = {"key": "value"}
126+
response = {"Status": "SUCCEEDED"}
127+
self.assertIsNone(extract_durable_execution_status(response, event))
128+
129+
def test_returns_none_for_non_dict_response(self):
130+
event = {
131+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
132+
}
133+
self.assertIsNone(extract_durable_execution_status("string", event))
134+
135+
def test_returns_none_for_missing_status(self):
136+
event = {
137+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
138+
}
139+
response = {"Result": "some-result"}
140+
self.assertIsNone(extract_durable_execution_status(response, event))
141+
142+
def test_returns_none_for_invalid_status(self):
143+
event = {
144+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
145+
}
146+
response = {"Status": "INVALID"}
147+
self.assertIsNone(extract_durable_execution_status(response, event))
148+
149+
def test_returns_none_for_non_dict_event(self):
150+
response = {"Status": "SUCCEEDED"}
151+
self.assertIsNone(extract_durable_execution_status(response, "not-a-dict"))
152+
153+
def test_returns_none_for_none_event(self):
154+
response = {"Status": "SUCCEEDED"}
155+
self.assertIsNone(extract_durable_execution_status(response, None))
156+
157+
def test_returns_none_for_none_response(self):
158+
event = {
159+
"DurableExecutionArn": "arn:aws:lambda:us-east-1:123:function:f:1/durable-execution/n/id"
160+
}
161+
self.assertIsNone(extract_durable_execution_status(None, event))

0 commit comments

Comments
 (0)