durabletask.azuremanaged 0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- durabletask_azuremanaged-0.1/PKG-INFO +14 -0
- durabletask_azuremanaged-0.1/durabletask/azuremanaged/__init__.py +0 -0
- durabletask_azuremanaged-0.1/durabletask/azuremanaged/client.py +30 -0
- durabletask_azuremanaged-0.1/durabletask/azuremanaged/internal/access_token_manager.py +49 -0
- durabletask_azuremanaged-0.1/durabletask/azuremanaged/internal/durabletask_grpc_interceptor.py +41 -0
- durabletask_azuremanaged-0.1/durabletask/azuremanaged/worker.py +30 -0
- durabletask_azuremanaged-0.1/durabletask.azuremanaged.egg-info/PKG-INFO +14 -0
- durabletask_azuremanaged-0.1/durabletask.azuremanaged.egg-info/SOURCES.txt +11 -0
- durabletask_azuremanaged-0.1/durabletask.azuremanaged.egg-info/dependency_links.txt +1 -0
- durabletask_azuremanaged-0.1/durabletask.azuremanaged.egg-info/requires.txt +2 -0
- durabletask_azuremanaged-0.1/durabletask.azuremanaged.egg-info/top_level.txt +1 -0
- durabletask_azuremanaged-0.1/pyproject.toml +41 -0
- durabletask_azuremanaged-0.1/setup.cfg +4 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: durabletask.azuremanaged
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Extensions for the Durable Task Python SDK for integrating with the Durable Task Scheduler in Azure
|
|
5
|
+
Project-URL: repository, https://github.com/microsoft/durabletask-python
|
|
6
|
+
Project-URL: changelog, https://github.com/microsoft/durabletask-python/blob/main/CHANGELOG.md
|
|
7
|
+
Keywords: durable,task,workflow,azure
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: durabletask>=0.2.0
|
|
14
|
+
Requires-Dist: azure-identity>=1.19.0
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from azure.core.credentials import TokenCredential
|
|
5
|
+
|
|
6
|
+
from durabletask.azuremanaged.internal.durabletask_grpc_interceptor import \
|
|
7
|
+
DTSDefaultClientInterceptorImpl
|
|
8
|
+
from durabletask.client import TaskHubGrpcClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Client class used for Durable Task Scheduler (DTS)
|
|
12
|
+
class DurableTaskSchedulerClient(TaskHubGrpcClient):
|
|
13
|
+
def __init__(self, *,
|
|
14
|
+
host_address: str,
|
|
15
|
+
taskhub: str,
|
|
16
|
+
token_credential: TokenCredential,
|
|
17
|
+
secure_channel: bool = True):
|
|
18
|
+
|
|
19
|
+
if not taskhub:
|
|
20
|
+
raise ValueError("Taskhub value cannot be empty. Please provide a value for your taskhub")
|
|
21
|
+
|
|
22
|
+
interceptors = [DTSDefaultClientInterceptorImpl(token_credential, taskhub)]
|
|
23
|
+
|
|
24
|
+
# We pass in None for the metadata so we don't construct an additional interceptor in the parent class
|
|
25
|
+
# Since the parent class doesn't use anything metadata for anything else, we can set it as None
|
|
26
|
+
super().__init__(
|
|
27
|
+
host_address=host_address,
|
|
28
|
+
secure_channel=secure_channel,
|
|
29
|
+
metadata=None,
|
|
30
|
+
interceptors=interceptors)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from azure.core.credentials import AccessToken, TokenCredential
|
|
7
|
+
|
|
8
|
+
import durabletask.internal.shared as shared
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# By default, when there's 10minutes left before the token expires, refresh the token
|
|
12
|
+
class AccessTokenManager:
|
|
13
|
+
|
|
14
|
+
_token: Optional[AccessToken]
|
|
15
|
+
|
|
16
|
+
def __init__(self, token_credential: Optional[TokenCredential], refresh_interval_seconds: int = 600):
|
|
17
|
+
self._scope = "https://durabletask.io/.default"
|
|
18
|
+
self._refresh_interval_seconds = refresh_interval_seconds
|
|
19
|
+
self._logger = shared.get_logger("token_manager")
|
|
20
|
+
|
|
21
|
+
self._credential = token_credential
|
|
22
|
+
|
|
23
|
+
if self._credential is not None:
|
|
24
|
+
self._token = self._credential.get_token(self._scope)
|
|
25
|
+
self.expiry_time = datetime.fromtimestamp(self._token.expires_on, tz=timezone.utc)
|
|
26
|
+
else:
|
|
27
|
+
self._token = None
|
|
28
|
+
self.expiry_time = None
|
|
29
|
+
|
|
30
|
+
def get_access_token(self) -> Optional[AccessToken]:
|
|
31
|
+
if self._token is None or self.is_token_expired():
|
|
32
|
+
self.refresh_token()
|
|
33
|
+
return self._token
|
|
34
|
+
|
|
35
|
+
# Checks if the token is expired, or if it will expire in the next "refresh_interval_seconds" seconds.
|
|
36
|
+
# For example, if the token is created to have a lifespan of 2 hours, and the refresh buffer is set to 30 minutes,
|
|
37
|
+
# We will grab a new token when there're 30minutes left on the lifespan of the token
|
|
38
|
+
def is_token_expired(self) -> bool:
|
|
39
|
+
if self.expiry_time is None:
|
|
40
|
+
return True
|
|
41
|
+
return datetime.now(timezone.utc) >= (self.expiry_time - timedelta(seconds=self._refresh_interval_seconds))
|
|
42
|
+
|
|
43
|
+
def refresh_token(self):
|
|
44
|
+
if self._credential is not None:
|
|
45
|
+
self._token = self._credential.get_token(self._scope)
|
|
46
|
+
|
|
47
|
+
# Convert UNIX timestamp to timezone-aware datetime
|
|
48
|
+
self.expiry_time = datetime.fromtimestamp(self._token.expires_on, tz=timezone.utc)
|
|
49
|
+
self._logger.debug(f"Token refreshed. Expires at: {self.expiry_time}")
|
durabletask_azuremanaged-0.1/durabletask/azuremanaged/internal/durabletask_grpc_interceptor.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
import grpc
|
|
5
|
+
from azure.core.credentials import TokenCredential
|
|
6
|
+
|
|
7
|
+
from durabletask.azuremanaged.internal.access_token_manager import \
|
|
8
|
+
AccessTokenManager
|
|
9
|
+
from durabletask.internal.grpc_interceptor import (
|
|
10
|
+
DefaultClientInterceptorImpl, _ClientCallDetails)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DTSDefaultClientInterceptorImpl (DefaultClientInterceptorImpl):
|
|
14
|
+
"""The class implements a UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor,
|
|
15
|
+
StreamUnaryClientInterceptor and StreamStreamClientInterceptor from grpc to add an
|
|
16
|
+
interceptor to add additional headers to all calls as needed."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, token_credential: TokenCredential, taskhub_name: str):
|
|
19
|
+
self._metadata = [("taskhub", taskhub_name)]
|
|
20
|
+
super().__init__(self._metadata)
|
|
21
|
+
|
|
22
|
+
if token_credential is not None:
|
|
23
|
+
self._token_credential = token_credential
|
|
24
|
+
self._token_manager = AccessTokenManager(token_credential=self._token_credential)
|
|
25
|
+
access_token = self._token_manager.get_access_token()
|
|
26
|
+
if access_token is not None:
|
|
27
|
+
self._metadata.append(("authorization", f"Bearer {access_token.token}"))
|
|
28
|
+
|
|
29
|
+
def _intercept_call(
|
|
30
|
+
self, client_call_details: _ClientCallDetails) -> grpc.ClientCallDetails:
|
|
31
|
+
"""Internal intercept_call implementation which adds metadata to grpc metadata in the RPC
|
|
32
|
+
call details."""
|
|
33
|
+
# Refresh the auth token if it is present and needed
|
|
34
|
+
if self._metadata is not None:
|
|
35
|
+
for i, (key, _) in enumerate(self._metadata):
|
|
36
|
+
if key.lower() == "authorization": # Ensure case-insensitive comparison
|
|
37
|
+
new_token = self._token_manager.get_access_token() # Get the new token
|
|
38
|
+
if new_token is not None:
|
|
39
|
+
self._metadata[i] = ("authorization", f"Bearer {new_token.token}") # Update the token
|
|
40
|
+
|
|
41
|
+
return super()._intercept_call(client_call_details)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from azure.core.credentials import TokenCredential
|
|
5
|
+
|
|
6
|
+
from durabletask.azuremanaged.internal.durabletask_grpc_interceptor import \
|
|
7
|
+
DTSDefaultClientInterceptorImpl
|
|
8
|
+
from durabletask.worker import TaskHubGrpcWorker
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Worker class used for Durable Task Scheduler (DTS)
|
|
12
|
+
class DurableTaskSchedulerWorker(TaskHubGrpcWorker):
|
|
13
|
+
def __init__(self, *,
|
|
14
|
+
host_address: str,
|
|
15
|
+
taskhub: str,
|
|
16
|
+
token_credential: TokenCredential,
|
|
17
|
+
secure_channel: bool = True):
|
|
18
|
+
|
|
19
|
+
if not taskhub:
|
|
20
|
+
raise ValueError("The taskhub value cannot be empty.")
|
|
21
|
+
|
|
22
|
+
interceptors = [DTSDefaultClientInterceptorImpl(token_credential, taskhub)]
|
|
23
|
+
|
|
24
|
+
# We pass in None for the metadata so we don't construct an additional interceptor in the parent class
|
|
25
|
+
# Since the parent class doesn't use anything metadata for anything else, we can set it as None
|
|
26
|
+
super().__init__(
|
|
27
|
+
host_address=host_address,
|
|
28
|
+
secure_channel=secure_channel,
|
|
29
|
+
metadata=None,
|
|
30
|
+
interceptors=interceptors)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: durabletask.azuremanaged
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Extensions for the Durable Task Python SDK for integrating with the Durable Task Scheduler in Azure
|
|
5
|
+
Project-URL: repository, https://github.com/microsoft/durabletask-python
|
|
6
|
+
Project-URL: changelog, https://github.com/microsoft/durabletask-python/blob/main/CHANGELOG.md
|
|
7
|
+
Keywords: durable,task,workflow,azure
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: durabletask>=0.2.0
|
|
14
|
+
Requires-Dist: azure-identity>=1.19.0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
durabletask.azuremanaged.egg-info/PKG-INFO
|
|
3
|
+
durabletask.azuremanaged.egg-info/SOURCES.txt
|
|
4
|
+
durabletask.azuremanaged.egg-info/dependency_links.txt
|
|
5
|
+
durabletask.azuremanaged.egg-info/requires.txt
|
|
6
|
+
durabletask.azuremanaged.egg-info/top_level.txt
|
|
7
|
+
durabletask/azuremanaged/__init__.py
|
|
8
|
+
durabletask/azuremanaged/client.py
|
|
9
|
+
durabletask/azuremanaged/worker.py
|
|
10
|
+
durabletask/azuremanaged/internal/access_token_manager.py
|
|
11
|
+
durabletask/azuremanaged/internal/durabletask_grpc_interceptor.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
durabletask
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
# For more information on pyproject.toml, see https://peps.python.org/pep-0621/
|
|
5
|
+
|
|
6
|
+
[build-system]
|
|
7
|
+
requires = ["setuptools", "wheel"]
|
|
8
|
+
build-backend = "setuptools.build_meta"
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "durabletask.azuremanaged"
|
|
12
|
+
version = "0.1"
|
|
13
|
+
description = "Extensions for the Durable Task Python SDK for integrating with the Durable Task Scheduler in Azure"
|
|
14
|
+
keywords = [
|
|
15
|
+
"durable",
|
|
16
|
+
"task",
|
|
17
|
+
"workflow",
|
|
18
|
+
"azure"
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.9"
|
|
26
|
+
license = {file = "LICENSE"}
|
|
27
|
+
readme = "README.md"
|
|
28
|
+
dependencies = [
|
|
29
|
+
"durabletask>=0.2.0",
|
|
30
|
+
"azure-identity>=1.19.0"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
repository = "https://github.com/microsoft/durabletask-python"
|
|
35
|
+
changelog = "https://github.com/microsoft/durabletask-python/blob/main/CHANGELOG.md"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
include = ["durabletask.azuremanaged", "durabletask.azuremanaged.*"]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
minversion = "6.0"
|