Skip to content

Commit 16d8cd1

Browse files
committed
Fix v1 status payload projection
Fetch orchestration payloads in the deprecated v1 status query shims while applying show_input locally. Preserve native durabletask query defaults and cover running, completed, failed, and collection status responses. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ff9faf2d-e79a-4309-8a67-5d455e4dd7bd
1 parent 50f62e2 commit 16d8cd1

4 files changed

Lines changed: 84 additions & 22 deletions

File tree

‎azure-functions-durable/CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ avoiding repeated allocation of unused worker resources.
3232

3333
FIXED
3434

35+
- Fixed deprecated v1 status-query methods omitting orchestration output,
36+
custom status, and failure details when input display was disabled.
3537
- Fixed asynchronous durable-client construction failing after an application
3638
event loop had been closed or cleared.
3739
- Prevented Durable HTTP calls from forwarding managed identity tokens,

‎azure-functions-durable/azure/durable_functions/client.py‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,19 @@ async def get_status(
265265
exist, a falsy status is returned rather than ``None``.
266266
267267
The ``show_history`` and ``show_history_output`` flags have no
268-
equivalent in durabletask and are ignored; ``show_input`` maps to
269-
``fetch_payloads``.
268+
equivalent in durabletask and are ignored. Payloads are fetched to
269+
preserve the v1 output and custom-status fields; ``show_input`` controls
270+
whether the compatibility wrapper exposes the input.
270271
"""
271-
state = await self.get_orchestration_state(instance_id, fetch_payloads=show_input)
272-
return DurableOrchestrationStatus.from_orchestration_state(state)
272+
state = await self.get_orchestration_state(instance_id, fetch_payloads=True)
273+
return DurableOrchestrationStatus.from_orchestration_state(
274+
state, include_input=show_input)
273275

274276
@deprecated("get_status_all is deprecated; use get_all_orchestration_states instead.")
275277
async def get_status_all(self) -> list[DurableOrchestrationStatus]:
276278
"""Deprecated alias for :meth:`get_all_orchestration_states`."""
277-
states = await self.get_all_orchestration_states()
279+
states = await self.get_all_orchestration_states(
280+
OrchestrationQuery(fetch_inputs_and_outputs=True))
278281
return [DurableOrchestrationStatus.from_orchestration_state(state) for state in states]
279282

280283
@deprecated("raise_event is deprecated; use raise_orchestration_event instead.")
@@ -376,7 +379,8 @@ async def get_status_by(
376379
query = OrchestrationQuery(
377380
created_time_from=created_time_from,
378381
created_time_to=created_time_to,
379-
runtime_status=to_durabletask_statuses(runtime_status))
382+
runtime_status=to_durabletask_statuses(runtime_status),
383+
fetch_inputs_and_outputs=True)
380384
states = await self.get_all_orchestration_states(query)
381385
return [DurableOrchestrationStatus.from_orchestration_state(state) for state in states]
382386

‎azure-functions-durable/azure/durable_functions/internal/compat/durable_orchestration_status.py‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,18 @@ class DurableOrchestrationStatus:
2929

3030
def __init__(self, state: Optional[OrchestrationState] = None):
3131
self._state = state
32+
self._include_input = True
3233

3334
@classmethod
3435
def from_orchestration_state(
35-
cls, state: Optional[OrchestrationState]) -> "DurableOrchestrationStatus":
36+
cls,
37+
state: Optional[OrchestrationState],
38+
*,
39+
include_input: bool = True) -> "DurableOrchestrationStatus":
3640
"""Wrap a durabletask ``OrchestrationState`` (or ``None``)."""
37-
return cls(state)
41+
status = cls(state)
42+
status._include_input = include_input
43+
return status
3844

3945
@classmethod
4046
def from_json(cls, json_obj: Any) -> "DurableOrchestrationStatus":
@@ -104,7 +110,7 @@ def last_updated_time(self) -> Optional[datetime]:
104110
@property
105111
def input_(self) -> Any:
106112
"""Get the (deserialized) input of the orchestration instance."""
107-
if self._state is None:
113+
if self._state is None or not self._include_input:
108114
return None
109115
return self._raw_payload(self._state.serialized_input)
110116

@@ -170,8 +176,7 @@ def to_json(self) -> dict[str, Any]:
170176
failure_message = getattr(failure, "message", None) if failure is not None else None
171177
if failure_message:
172178
result["output"] = failure_message
173-
input_ = self._raw_payload(
174-
self._state.serialized_input if self._state is not None else None)
179+
input_ = self.input_
175180
if input_ is not None:
176181
result["input"] = input_
177182
if self.runtime_status is not None:

‎tests/azure-functions-durable/test_client_compat.py‎

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,13 @@ async def test_start_new_delegates_to_schedule_new_orchestration():
179179
await client.close()
180180

181181

182-
async def test_get_status_delegates_to_get_orchestration_state():
182+
async def test_get_status_always_fetches_payloads():
183183
client = _make_client()
184184
try:
185185
with patch.object(client, "get_orchestration_state",
186186
new=AsyncMock(return_value=None)) as mock:
187187
with pytest.warns(DeprecationWarning):
188-
await client.get_status("abc", show_input=True)
188+
await client.get_status("abc")
189189
mock.assert_awaited_once_with("abc", fetch_payloads=True)
190190
finally:
191191
await client.close()
@@ -198,7 +198,8 @@ async def test_get_status_all_delegates():
198198
new=AsyncMock(return_value=[])) as mock:
199199
with pytest.warns(DeprecationWarning):
200200
await client.get_status_all()
201-
mock.assert_awaited_once_with()
201+
query = mock.await_args.args[0]
202+
assert query.fetch_inputs_and_outputs is True
202203
finally:
203204
await client.close()
204205

@@ -317,6 +318,7 @@ async def test_get_status_by_maps_statuses():
317318
runtime_status=[df.OrchestrationRuntimeStatus.Running])
318319
query = mock.await_args.args[0]
319320
assert query.runtime_status == [OrchestrationStatus.RUNNING]
321+
assert query.fetch_inputs_and_outputs is True
320322
finally:
321323
await client.close()
322324

@@ -505,16 +507,20 @@ def test_entity_class_raises_not_implemented():
505507
# Return-type shims: DurableOrchestrationStatus
506508
# ---------------------------------------------------------------------------
507509

508-
def _fake_state():
510+
def _fake_state(
511+
runtime_status=OrchestrationStatus.RUNNING,
512+
serialized_output='{"out": 2}',
513+
failure_details=None):
509514
return SimpleNamespace(
510515
name="orch",
511516
instance_id="abc",
512517
created_at=datetime(2026, 1, 1, tzinfo=timezone.utc),
513518
last_updated_at=datetime(2026, 1, 2, tzinfo=timezone.utc),
514-
runtime_status=OrchestrationStatus.RUNNING,
519+
runtime_status=runtime_status,
515520
serialized_input='{"in": 1}',
516-
serialized_output='{"out": 2}',
521+
serialized_output=serialized_output,
517522
serialized_custom_status='"cs"',
523+
failure_details=failure_details,
518524
get_input=lambda: {"in": 1},
519525
get_output=lambda: {"out": 2},
520526
get_custom_status=lambda: "cs",
@@ -527,21 +533,60 @@ def test_from_durabletask_status_reverse_mapping():
527533
OrchestrationStatus.CONTINUED_AS_NEW) == df.OrchestrationRuntimeStatus.ContinuedAsNew
528534

529535

530-
async def test_get_status_returns_wrapped_status():
536+
async def test_get_status_suppresses_only_input_by_default():
531537
client = _make_client()
532538
try:
539+
state = _fake_state(runtime_status=OrchestrationStatus.COMPLETED)
533540
with patch.object(client, "get_orchestration_state",
534-
new=AsyncMock(return_value=_fake_state())):
541+
new=AsyncMock(return_value=state)):
535542
with pytest.warns(DeprecationWarning):
536543
status = await client.get_status("abc")
537544
assert bool(status) is True
538545
assert status.name == "orch"
539546
assert status.instance_id == "abc"
540-
assert status.runtime_status == df.OrchestrationRuntimeStatus.Running
541-
assert status.input_ == {"in": 1}
547+
assert status.runtime_status == df.OrchestrationRuntimeStatus.Completed
548+
assert status.input_ is None
542549
assert status.output == {"out": 2}
543550
assert status.custom_status == "cs"
544-
assert status.to_json()["runtimeStatus"] == "Running"
551+
status_json = status.to_json()
552+
assert status_json["runtimeStatus"] == "Completed"
553+
assert "input" not in status_json
554+
assert status_json["output"] == {"out": 2}
555+
assert status_json["customStatus"] == "cs"
556+
finally:
557+
await client.close()
558+
559+
560+
async def test_get_status_show_input_includes_running_input():
561+
client = _make_client()
562+
try:
563+
with patch.object(client, "get_orchestration_state",
564+
new=AsyncMock(return_value=_fake_state())):
565+
with pytest.warns(DeprecationWarning):
566+
status = await client.get_status("abc", show_input=True)
567+
assert status.runtime_status == df.OrchestrationRuntimeStatus.Running
568+
assert status.input_ == {"in": 1}
569+
assert status.to_json()["input"] == {"in": 1}
570+
finally:
571+
await client.close()
572+
573+
574+
async def test_get_status_failed_preserves_failure_output():
575+
client = _make_client()
576+
try:
577+
state = _fake_state(
578+
runtime_status=OrchestrationStatus.FAILED,
579+
serialized_output=None,
580+
failure_details=SimpleNamespace(message="boom"))
581+
with patch.object(client, "get_orchestration_state",
582+
new=AsyncMock(return_value=state)):
583+
with pytest.warns(DeprecationWarning):
584+
status = await client.get_status("abc")
585+
status_json = status.to_json()
586+
assert status_json["runtimeStatus"] == "Failed"
587+
assert status_json["output"] == "boom"
588+
assert status_json["customStatus"] == "cs"
589+
assert "input" not in status_json
545590
finally:
546591
await client.close()
547592

@@ -569,6 +614,9 @@ async def test_get_status_all_returns_wrapped_list():
569614
statuses = await client.get_status_all()
570615
assert len(statuses) == 1
571616
assert statuses[0].runtime_status == df.OrchestrationRuntimeStatus.Running
617+
assert statuses[0].input_ == {"in": 1}
618+
assert statuses[0].output == {"out": 2}
619+
assert statuses[0].custom_status == "cs"
572620
finally:
573621
await client.close()
574622

@@ -582,6 +630,9 @@ async def test_get_status_by_returns_wrapped_list():
582630
statuses = await client.get_status_by(
583631
runtime_status=[df.OrchestrationRuntimeStatus.Running])
584632
assert statuses[0].instance_id == "abc"
633+
assert statuses[0].input_ == {"in": 1}
634+
assert statuses[0].output == {"out": 2}
635+
assert statuses[0].custom_status == "cs"
585636
finally:
586637
await client.close()
587638

0 commit comments

Comments
 (0)