Skip to content

Commit 7ff1525

Browse files
committed
Storing changes commit
1 parent eba1e8e commit 7ff1525

File tree

11 files changed

+427
-0
lines changed

11 files changed

+427
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## v0.1.0
9+
10+
- Initial implementation

durabletask-azurefunctions/__init__.py

Whitespace-only changes.

durabletask-azurefunctions/durabletask/azurefunctions/__init__.py

Whitespace-only changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Constants used to determine the local running context."""
2+
# Todo: Remove unused constants after module is complete
3+
DEFAULT_LOCAL_HOST: str = 'localhost:7071'
4+
DEFAULT_LOCAL_ORIGIN: str = f'http://{DEFAULT_LOCAL_HOST}'
5+
DATETIME_STRING_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
6+
HTTP_ACTION_NAME = 'BuiltIn::HttpActivity'
7+
ORCHESTRATION_TRIGGER = "orchestrationTrigger"
8+
ACTIVITY_TRIGGER = "activityTrigger"
9+
ENTITY_TRIGGER = "entityTrigger"
10+
DURABLE_CLIENT = "durableClient"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""Durable Task SDK for Python entities component"""
5+
6+
import durabletask.azurefunctions.decorators.durable_app as durable_app
7+
import durabletask.azurefunctions.decorators.metadata as metadata
8+
9+
__all__ = ["durable_app", "metadata"]
10+
11+
PACKAGE_NAME = "durabletask.entities"
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
from .metadata import OrchestrationTrigger, ActivityTrigger, EntityTrigger, \
4+
DurableClient
5+
from typing import Callable, Optional
6+
from typing import Union
7+
from azure.functions import FunctionRegister, TriggerApi, BindingApi, AuthLevel, OrchestrationContext
8+
9+
10+
class Blueprint(TriggerApi, BindingApi):
11+
"""Durable Functions (DF) Blueprint container.
12+
13+
It allows functions to be declared via trigger and binding decorators,
14+
but does not automatically index/register these functions.
15+
16+
To register these functions, utilize the `register_functions` method from any
17+
:class:`FunctionRegister` subclass, such as `DFApp`.
18+
"""
19+
20+
def __init__(self,
21+
http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION):
22+
"""Instantiate a Durable Functions app with which to register Functions.
23+
24+
Parameters
25+
----------
26+
http_auth_level: Union[AuthLevel, str]
27+
Authorization level required for Function invocation.
28+
Defaults to AuthLevel.Function.
29+
30+
Returns
31+
-------
32+
DFApp
33+
New instance of a Durable Functions app
34+
"""
35+
super().__init__(auth_level=http_auth_level)
36+
37+
def _configure_orchestrator_callable(self, wrap) -> Callable:
38+
"""Obtain decorator to construct an Orchestrator class from a user-defined Function.
39+
40+
In the old programming model, this decorator's logic was unavoidable boilerplate
41+
in user-code. Now, this is handled internally by the framework.
42+
43+
Parameters
44+
----------
45+
wrap: Callable
46+
The next decorator to be applied.
47+
48+
Returns
49+
-------
50+
Callable
51+
The function to construct an Orchestrator class from the user-defined Function,
52+
wrapped by the next decorator in the sequence.
53+
"""
54+
def decorator(orchestrator_func):
55+
# Construct an orchestrator based on the end-user code
56+
57+
# TODO: Extract this logic (?)
58+
def handle(context: OrchestrationContext) -> str:
59+
context_body = getattr(context, "body", None)
60+
if context_body is None:
61+
context_body = context
62+
orchestration_context = context_body
63+
# TODO: Run the orchestration using the context
64+
return ""
65+
66+
handle.orchestrator_function = orchestrator_func
67+
68+
# invoke next decorator, with the Orchestrator as input
69+
handle.__name__ = orchestrator_func.__name__
70+
return wrap(handle)
71+
72+
return decorator
73+
74+
def orchestration_trigger(self, context_name: str,
75+
orchestration: Optional[str] = None):
76+
"""Register an Orchestrator Function.
77+
78+
Parameters
79+
----------
80+
context_name: str
81+
Parameter name of the DurableOrchestrationContext object.
82+
orchestration: Optional[str]
83+
Name of Orchestrator Function.
84+
The value is None by default, in which case the name of the method is used.
85+
"""
86+
@self._configure_orchestrator_callable
87+
@self._configure_function_builder
88+
def wrap(fb):
89+
90+
def decorator():
91+
fb.add_trigger(
92+
trigger=OrchestrationTrigger(name=context_name,
93+
orchestration=orchestration))
94+
return fb
95+
96+
return decorator()
97+
98+
return wrap
99+
100+
def activity_trigger(self, input_name: str,
101+
activity: Optional[str] = None):
102+
"""Register an Activity Function.
103+
104+
Parameters
105+
----------
106+
input_name: str
107+
Parameter name of the Activity input.
108+
activity: Optional[str]
109+
Name of Activity Function.
110+
The value is None by default, in which case the name of the method is used.
111+
"""
112+
@self._configure_function_builder
113+
def wrap(fb):
114+
def decorator():
115+
fb.add_trigger(
116+
trigger=ActivityTrigger(name=input_name,
117+
activity=activity))
118+
return fb
119+
120+
return decorator()
121+
122+
return wrap
123+
124+
def entity_trigger(self, context_name: str,
125+
entity_name: Optional[str] = None):
126+
"""Register an Entity Function.
127+
128+
Parameters
129+
----------
130+
context_name: str
131+
Parameter name of the Entity input.
132+
entity_name: Optional[str]
133+
Name of Entity Function.
134+
The value is None by default, in which case the name of the method is used.
135+
"""
136+
@self._configure_function_builder
137+
def wrap(fb):
138+
def decorator():
139+
fb.add_trigger(
140+
trigger=EntityTrigger(name=context_name,
141+
entity_name=entity_name))
142+
return fb
143+
144+
return decorator()
145+
146+
return wrap
147+
148+
def durable_client_input(self,
149+
client_name: str,
150+
task_hub: Optional[str] = None,
151+
connection_name: Optional[str] = None
152+
):
153+
"""Register a Durable-client Function.
154+
155+
Parameters
156+
----------
157+
client_name: str
158+
Parameter name of durable client.
159+
task_hub: Optional[str]
160+
Used in scenarios where multiple function apps share the same storage account
161+
but need to be isolated from each other. If not specified, the default value
162+
from host.json is used.
163+
This value must match the value used by the target orchestrator functions.
164+
connection_name: Optional[str]
165+
The name of an app setting that contains a storage account connection string.
166+
The storage account represented by this connection string must be the same one
167+
used by the target orchestrator functions. If not specified, the default storage
168+
account connection string for the function app is used.
169+
"""
170+
171+
@self._configure_function_builder
172+
def wrap(fb):
173+
def decorator():
174+
# self._add_rich_client(fb, client_name, DurableOrchestrationClient)
175+
176+
fb.add_binding(
177+
binding=DurableClient(name=client_name,
178+
task_hub=task_hub,
179+
connection_name=connection_name))
180+
return fb
181+
182+
return decorator()
183+
184+
return wrap
185+
186+
187+
class DFApp(Blueprint, FunctionRegister):
188+
"""Durable Functions (DF) app.
189+
190+
Exports the decorators required to declare and index DF Function-types.
191+
"""
192+
193+
pass
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
from typing import Optional
4+
5+
from durabletask.azurefunctions.constants import ORCHESTRATION_TRIGGER, \
6+
ACTIVITY_TRIGGER, ENTITY_TRIGGER, DURABLE_CLIENT
7+
from azure.functions.decorators.core import Trigger, InputBinding
8+
9+
10+
class OrchestrationTrigger(Trigger):
11+
"""OrchestrationTrigger.
12+
13+
Trigger representing an Orchestration Function.
14+
"""
15+
16+
@staticmethod
17+
def get_binding_name() -> str:
18+
"""Get the name of this trigger, as a string.
19+
20+
Returns
21+
-------
22+
str
23+
The string representation of this trigger.
24+
"""
25+
return ORCHESTRATION_TRIGGER
26+
27+
def __init__(self,
28+
name: str,
29+
orchestration: Optional[str] = None,
30+
) -> None:
31+
self.orchestration = orchestration
32+
super().__init__(name=name)
33+
34+
35+
class ActivityTrigger(Trigger):
36+
"""ActivityTrigger.
37+
38+
Trigger representing a Durable Functions Activity.
39+
"""
40+
41+
@staticmethod
42+
def get_binding_name() -> str:
43+
"""Get the name of this trigger, as a string.
44+
45+
Returns
46+
-------
47+
str
48+
The string representation of this trigger.
49+
"""
50+
return ACTIVITY_TRIGGER
51+
52+
def __init__(self,
53+
name: str,
54+
activity: Optional[str] = None,
55+
) -> None:
56+
self.activity = activity
57+
super().__init__(name=name)
58+
59+
60+
class EntityTrigger(Trigger):
61+
"""EntityTrigger.
62+
63+
Trigger representing an Entity Function.
64+
"""
65+
66+
@staticmethod
67+
def get_binding_name() -> str:
68+
"""Get the name of this trigger, as a string.
69+
70+
Returns
71+
-------
72+
str
73+
The string representation of this trigger.
74+
"""
75+
return ENTITY_TRIGGER
76+
77+
def __init__(self,
78+
name: str,
79+
entity_name: Optional[str] = None,
80+
) -> None:
81+
self.entity_name = entity_name
82+
super().__init__(name=name)
83+
84+
85+
class DurableClient(InputBinding):
86+
"""DurableClient.
87+
88+
Binding representing a Durable-client object.
89+
"""
90+
91+
@staticmethod
92+
def get_binding_name() -> str:
93+
"""Get the name of this Binding, as a string.
94+
95+
Returns
96+
-------
97+
str
98+
The string representation of this binding.
99+
"""
100+
return DURABLE_CLIENT
101+
102+
def __init__(self,
103+
name: str,
104+
task_hub: Optional[str] = None,
105+
connection_name: Optional[str] = None
106+
) -> None:
107+
self.task_hub = task_hub
108+
self.connection_name = connection_name
109+
super().__init__(name=name)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import abc
2+
from typing import Any, Optional
3+
4+
from azure.functions import meta
5+
6+
7+
class DurableInConverter(meta._BaseConverter, binding=None):
8+
9+
@classmethod
10+
@abc.abstractmethod
11+
def check_input_type_annotation(cls, pytype: type) -> bool:
12+
pass
13+
14+
@classmethod
15+
@abc.abstractmethod
16+
def decode(cls, data: meta.Datum, *, trigger_metadata) -> Any:
17+
raise NotImplementedError
18+
19+
@classmethod
20+
@abc.abstractmethod
21+
def has_implicit_output(cls) -> bool:
22+
return False
23+
24+
25+
class DurableOutConverter(meta._BaseConverter, binding=None):
26+
27+
@classmethod
28+
@abc.abstractmethod
29+
def check_output_type_annotation(cls, pytype: type) -> bool:
30+
pass
31+
32+
@classmethod
33+
@abc.abstractmethod
34+
def encode(cls, obj: Any, *,
35+
expected_type: Optional[type]) -> Optional[meta.Datum]:
36+
raise NotImplementedError
37+
38+
# Durable Functions Durable Client Bindings
39+
40+
41+
class DurableClientConverter(DurableInConverter,
42+
DurableOutConverter,
43+
binding='durableClient'):
44+
@classmethod
45+
def has_implicit_output(cls) -> bool:
46+
return False
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .DurableClientConverter import DurableClientConverter
2+
3+
__all__ = ["DurableClientConverter"]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class TempClass:
2+
pass

0 commit comments

Comments
 (0)