Skip to content

Commit 0e33ce3

Browse files
committed
Add change supporting unit testing
- Support orchestrators and entities
1 parent efa6321 commit 0e33ce3

File tree

2 files changed

+68
-22
lines changed

2 files changed

+68
-22
lines changed

azure/durable_functions/entity.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,45 @@
33
from datetime import datetime
44
from typing import Callable, Any, List, Dict
55

6+
import azure.functions as func
67

78
class InternalEntityException(Exception):
89
"""Framework-internal Exception class (for internal use only)."""
910

1011
pass
1112

13+
class EntityHandler(Callable):
14+
"""Durable Entity Handler.
15+
A callable class that wraps the user defined entity function for execution by the Python worker
16+
and also allows access to the original method for unit testing
17+
"""
18+
19+
def __init__(self, func: Callable[[DurableEntityContext], None]):
20+
"""
21+
Create a new entity handler for the user defined entity function.
22+
23+
Parameters
24+
----------
25+
func: Callable[[DurableEntityContext], None]
26+
The user defined entity function.
27+
"""
28+
self._func = func
29+
30+
def __call__(self, context: func.EntityContext) -> str:
31+
"""Handle the execution of the user defined entity function.
32+
Parameters
33+
----------
34+
context : func.EntityContext
35+
The DF entity context"""
36+
# It is not clear when the context JSON would be found
37+
# inside a "body"-key, but this pattern matches the
38+
# orchestrator implementation, so we keep it for safety.
39+
context_body = getattr(context, "body", None)
40+
if context_body is None:
41+
context_body = context
42+
ctx, batch = DurableEntityContext.from_json(context_body)
43+
return Entity(self._func).handle(ctx, batch)
44+
1245

1346
class Entity:
1447
"""Durable Entity Class.
@@ -92,19 +125,10 @@ def create(cls, fn: Callable[[DurableEntityContext], None]) -> Callable[[Any], s
92125
93126
Returns
94127
-------
95-
Callable[[Any], str]
96-
Handle function of the newly created entity client
128+
EntityHandler
129+
Entity Handler callable for the newly created entity client
97130
"""
98-
def handle(context) -> str:
99-
# It is not clear when the context JSON would be found
100-
# inside a "body"-key, but this pattern matches the
101-
# orchestrator implementation, so we keep it for safety.
102-
context_body = getattr(context, "body", None)
103-
if context_body is None:
104-
context_body = context
105-
ctx, batch = DurableEntityContext.from_json(context_body)
106-
return Entity(fn).handle(ctx, batch)
107-
return handle
131+
return EntityHandler(fn)
108132

109133
def _elapsed_milliseconds_since(self, start_time: datetime) -> int:
110134
"""Calculate the elapsed time, in milliseconds, from the start_time to the present.

azure/durable_functions/orchestrator.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,35 @@
1111
import azure.functions as func
1212

1313

14+
class OrchestrationHandler(Callable):
15+
"""Durable Orchestration Handler.
16+
A callable class that wraps the user defined generator function for execution by the Python worker
17+
and also allows access to the original method for unit testing
18+
"""
19+
20+
def __init__(self, func: Callable[[DurableOrchestrationContext], Generator[Any, Any, Any]]):
21+
"""
22+
Create a new orchestrator handler for the user defined orchestrator function.
23+
24+
Parameters
25+
----------
26+
func: Callable[[DurableOrchestrationContext], Generator[Any, Any, Any]]
27+
The user defined orchestrator function.
28+
"""
29+
self._func = func
30+
31+
def __call__(self, context: func.OrchestrationContext) -> str:
32+
"""Handle the execution of the user defined orchestrator function.
33+
Parameters
34+
----------
35+
context : func.OrchestrationContext
36+
The DF orchestration context"""
37+
context_body = getattr(context, "body", None)
38+
if context_body is None:
39+
context_body = context
40+
return Orchestrator(self._func).handle(DurableOrchestrationContext.from_json(context_body))
41+
42+
1443
class Orchestrator:
1544
"""Durable Orchestration Class.
1645
@@ -58,14 +87,7 @@ def create(cls, fn: Callable[[DurableOrchestrationContext], Generator[Any, Any,
5887
5988
Returns
6089
-------
61-
Callable[[Any], str]
62-
Handle function of the newly created orchestration client
90+
OrchestrationHandler
91+
Orchestration handler callable class for the newly created orchestration client
6392
"""
64-
65-
def handle(context: func.OrchestrationContext) -> str:
66-
context_body = getattr(context, "body", None)
67-
if context_body is None:
68-
context_body = context
69-
return Orchestrator(fn).handle(DurableOrchestrationContext.from_json(context_body))
70-
71-
return handle
93+
return OrchestrationHandler(fn)

0 commit comments

Comments
 (0)