adsk-platform-httpclient 0.2.9__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.
Files changed (20) hide show
  1. adsk_platform_httpclient-0.2.9/PKG-INFO +140 -0
  2. adsk_platform_httpclient-0.2.9/README.md +113 -0
  3. adsk_platform_httpclient-0.2.9/pyproject.toml +47 -0
  4. adsk_platform_httpclient-0.2.9/setup.cfg +4 -0
  5. adsk_platform_httpclient-0.2.9/src/adsk_platform_httpclient.egg-info/PKG-INFO +140 -0
  6. adsk_platform_httpclient-0.2.9/src/adsk_platform_httpclient.egg-info/SOURCES.txt +18 -0
  7. adsk_platform_httpclient-0.2.9/src/adsk_platform_httpclient.egg-info/dependency_links.txt +1 -0
  8. adsk_platform_httpclient-0.2.9/src/adsk_platform_httpclient.egg-info/requires.txt +3 -0
  9. adsk_platform_httpclient-0.2.9/src/adsk_platform_httpclient.egg-info/top_level.txt +1 -0
  10. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/__init__.py +28 -0
  11. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/access_token_provider.py +49 -0
  12. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/http_client_factory.py +170 -0
  13. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/__init__.py +7 -0
  14. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/error_handler.py +89 -0
  15. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/options/__init__.py +11 -0
  16. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/options/error_handler_option.py +19 -0
  17. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/options/query_parameter_handler_option.py +20 -0
  18. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/options/rate_limiting_handler_option.py +55 -0
  19. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/query_parameter_handler.py +64 -0
  20. adsk_platform_httpclient-0.2.9/src/autodesk_common_httpclient/middleware/rate_limiting_handler.py +89 -0
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: adsk-platform-httpclient
3
+ Version: 0.2.9
4
+ Summary: Autodesk Platform Services: shared HTTP client and authentication utilities for Kiota-based SDKs
5
+ Author: duszykf
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Autodesk/APS-SDK-Kiota
8
+ Project-URL: Repository, https://github.com/Autodesk/APS-SDK-Kiota
9
+ Project-URL: Issues, https://github.com/Autodesk/APS-SDK-Kiota/issues
10
+ Keywords: autodesk,aps,kiota,sdk,httpclient
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: microsoft-kiota-abstractions<2.0.0,>=1.9.0
25
+ Requires-Dist: microsoft-kiota-http<2.0.0,>=1.9.0
26
+ Requires-Dist: httpx<1.0.0,>=0.27.0
27
+
28
+ # adsk-platform-httpclient
29
+
30
+ Shared HTTP client, authentication, and middleware utilities for [Autodesk Platform Services](https://aps.autodesk.com/) Python SDKs.
31
+
32
+ This package provides the foundational components used by all `adsk-platform-*` SDK packages.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install adsk-platform-httpclient
38
+ ```
39
+
40
+ > **Note:** You typically don't need to install this package directly. It's automatically included as a dependency of the SDK packages (e.g., `adsk-platform-acc`, `adsk-platform-data-management`).
41
+
42
+ ## Components
43
+
44
+ ### HttpClientFactory
45
+
46
+ Creates pre-configured `httpx.AsyncClient` instances and Kiota request adapters with bearer-token authentication and the custom middleware pipeline.
47
+
48
+ ```python
49
+ from autodesk_common_httpclient import HttpClientFactory
50
+
51
+ async def get_access_token() -> str:
52
+ return "YOUR_ACCESS_TOKEN"
53
+
54
+ # Create a Kiota request adapter with authentication + middleware
55
+ adapter = HttpClientFactory.create_adapter(get_access_token)
56
+
57
+ # Or provide your own httpx client
58
+ import httpx
59
+ custom_client = httpx.AsyncClient(timeout=60.0)
60
+ adapter = HttpClientFactory.create_adapter(get_access_token, http_client=custom_client)
61
+ ```
62
+
63
+ ### AccessTokenProviderCallback
64
+
65
+ Wraps an async callback into a Kiota-compatible `AccessTokenProvider`.
66
+
67
+ ```python
68
+ from autodesk_common_httpclient import AccessTokenProviderCallback
69
+
70
+ provider = AccessTokenProviderCallback(get_access_token)
71
+ ```
72
+
73
+ ## Custom Middleware
74
+
75
+ The package includes three custom middleware handlers that are automatically added to the HTTP pipeline, matching the C# `Autodesk.Common.HttpClientLibrary.Middleware`:
76
+
77
+ ### ErrorHandler
78
+
79
+ Raises an `httpx.HTTPStatusError` when the HTTP response has a non-success status code (4xx/5xx). Enabled by default.
80
+
81
+ ```python
82
+ from autodesk_common_httpclient import ErrorHandlerOption
83
+
84
+ # Disable for a specific request
85
+ error_option = ErrorHandlerOption(enabled=False)
86
+ ```
87
+
88
+ ### QueryParameterHandler
89
+
90
+ Appends or overwrites query parameters on every outgoing request. Useful for injecting API versions, region codes, or tracking identifiers.
91
+
92
+ ```python
93
+ from autodesk_common_httpclient import QueryParameterHandlerOption
94
+
95
+ query_option = QueryParameterHandlerOption(query_parameters={"region": "US"})
96
+ ```
97
+
98
+ ### RateLimitingHandler
99
+
100
+ Limits the number of concurrent requests per endpoint within a sliding time window. Disabled by default.
101
+
102
+ ```python
103
+ from autodesk_common_httpclient import RateLimitingHandlerOption
104
+
105
+ # Allow max 10 requests per endpoint per 60 seconds
106
+ rate_option = RateLimitingHandlerOption()
107
+ rate_option.set_rate_limit(max_requests=10, time_window_seconds=60.0)
108
+ ```
109
+
110
+ ### Creating a client with rate limiting
111
+
112
+ ```python
113
+ from autodesk_common_httpclient import HttpClientFactory
114
+
115
+ # Create an HTTP client with rate limiting enabled
116
+ client = HttpClientFactory.create_with_rate_limit(
117
+ max_requests=10,
118
+ time_window_seconds=60.0,
119
+ )
120
+
121
+ # Use it with any SDK client
122
+ adapter = HttpClientFactory.create_adapter(get_access_token, http_client=client)
123
+ ```
124
+
125
+ ### Middleware Pipeline Order
126
+
127
+ The middleware is executed in this order (matching the C# implementation):
128
+
129
+ 1. **Kiota defaults** — Redirect, Retry, Parameters Name Decoding, URL Replace, User Agent, Headers Inspection
130
+ 2. **RateLimitingHandler** — Per-endpoint rate limiting
131
+ 3. **QueryParameterHandler** — Query parameter injection
132
+ 4. **ErrorHandler** — Error response detection
133
+
134
+ ## Requirements
135
+
136
+ - Python 3.10 or later
137
+
138
+ ## License
139
+
140
+ This project is licensed under the MIT License.
@@ -0,0 +1,113 @@
1
+ # adsk-platform-httpclient
2
+
3
+ Shared HTTP client, authentication, and middleware utilities for [Autodesk Platform Services](https://aps.autodesk.com/) Python SDKs.
4
+
5
+ This package provides the foundational components used by all `adsk-platform-*` SDK packages.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install adsk-platform-httpclient
11
+ ```
12
+
13
+ > **Note:** You typically don't need to install this package directly. It's automatically included as a dependency of the SDK packages (e.g., `adsk-platform-acc`, `adsk-platform-data-management`).
14
+
15
+ ## Components
16
+
17
+ ### HttpClientFactory
18
+
19
+ Creates pre-configured `httpx.AsyncClient` instances and Kiota request adapters with bearer-token authentication and the custom middleware pipeline.
20
+
21
+ ```python
22
+ from autodesk_common_httpclient import HttpClientFactory
23
+
24
+ async def get_access_token() -> str:
25
+ return "YOUR_ACCESS_TOKEN"
26
+
27
+ # Create a Kiota request adapter with authentication + middleware
28
+ adapter = HttpClientFactory.create_adapter(get_access_token)
29
+
30
+ # Or provide your own httpx client
31
+ import httpx
32
+ custom_client = httpx.AsyncClient(timeout=60.0)
33
+ adapter = HttpClientFactory.create_adapter(get_access_token, http_client=custom_client)
34
+ ```
35
+
36
+ ### AccessTokenProviderCallback
37
+
38
+ Wraps an async callback into a Kiota-compatible `AccessTokenProvider`.
39
+
40
+ ```python
41
+ from autodesk_common_httpclient import AccessTokenProviderCallback
42
+
43
+ provider = AccessTokenProviderCallback(get_access_token)
44
+ ```
45
+
46
+ ## Custom Middleware
47
+
48
+ The package includes three custom middleware handlers that are automatically added to the HTTP pipeline, matching the C# `Autodesk.Common.HttpClientLibrary.Middleware`:
49
+
50
+ ### ErrorHandler
51
+
52
+ Raises an `httpx.HTTPStatusError` when the HTTP response has a non-success status code (4xx/5xx). Enabled by default.
53
+
54
+ ```python
55
+ from autodesk_common_httpclient import ErrorHandlerOption
56
+
57
+ # Disable for a specific request
58
+ error_option = ErrorHandlerOption(enabled=False)
59
+ ```
60
+
61
+ ### QueryParameterHandler
62
+
63
+ Appends or overwrites query parameters on every outgoing request. Useful for injecting API versions, region codes, or tracking identifiers.
64
+
65
+ ```python
66
+ from autodesk_common_httpclient import QueryParameterHandlerOption
67
+
68
+ query_option = QueryParameterHandlerOption(query_parameters={"region": "US"})
69
+ ```
70
+
71
+ ### RateLimitingHandler
72
+
73
+ Limits the number of concurrent requests per endpoint within a sliding time window. Disabled by default.
74
+
75
+ ```python
76
+ from autodesk_common_httpclient import RateLimitingHandlerOption
77
+
78
+ # Allow max 10 requests per endpoint per 60 seconds
79
+ rate_option = RateLimitingHandlerOption()
80
+ rate_option.set_rate_limit(max_requests=10, time_window_seconds=60.0)
81
+ ```
82
+
83
+ ### Creating a client with rate limiting
84
+
85
+ ```python
86
+ from autodesk_common_httpclient import HttpClientFactory
87
+
88
+ # Create an HTTP client with rate limiting enabled
89
+ client = HttpClientFactory.create_with_rate_limit(
90
+ max_requests=10,
91
+ time_window_seconds=60.0,
92
+ )
93
+
94
+ # Use it with any SDK client
95
+ adapter = HttpClientFactory.create_adapter(get_access_token, http_client=client)
96
+ ```
97
+
98
+ ### Middleware Pipeline Order
99
+
100
+ The middleware is executed in this order (matching the C# implementation):
101
+
102
+ 1. **Kiota defaults** — Redirect, Retry, Parameters Name Decoding, URL Replace, User Agent, Headers Inspection
103
+ 2. **RateLimitingHandler** — Per-endpoint rate limiting
104
+ 3. **QueryParameterHandler** — Query parameter injection
105
+ 4. **ErrorHandler** — Error response detection
106
+
107
+ ## Requirements
108
+
109
+ - Python 3.10 or later
110
+
111
+ ## License
112
+
113
+ This project is licensed under the MIT License.
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "adsk-platform-httpclient"
7
+ version = "0.2.9"
8
+ description = "Autodesk Platform Services: shared HTTP client and authentication utilities for Kiota-based SDKs"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "duszykf" },
14
+ ]
15
+ keywords = [
16
+ "autodesk",
17
+ "aps",
18
+ "kiota",
19
+ "sdk",
20
+ "httpclient",
21
+ ]
22
+ classifiers = [
23
+ "Development Status :: 3 - Alpha",
24
+ "Intended Audience :: Developers",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
32
+ "Topic :: Software Development :: Libraries :: Python Modules",
33
+ "Typing :: Typed",
34
+ ]
35
+ dependencies = [
36
+ "microsoft-kiota-abstractions>=1.9.0,<2.0.0",
37
+ "microsoft-kiota-http>=1.9.0,<2.0.0",
38
+ "httpx>=0.27.0,<1.0.0",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/Autodesk/APS-SDK-Kiota"
43
+ Repository = "https://github.com/Autodesk/APS-SDK-Kiota"
44
+ Issues = "https://github.com/Autodesk/APS-SDK-Kiota/issues"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: adsk-platform-httpclient
3
+ Version: 0.2.9
4
+ Summary: Autodesk Platform Services: shared HTTP client and authentication utilities for Kiota-based SDKs
5
+ Author: duszykf
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Autodesk/APS-SDK-Kiota
8
+ Project-URL: Repository, https://github.com/Autodesk/APS-SDK-Kiota
9
+ Project-URL: Issues, https://github.com/Autodesk/APS-SDK-Kiota/issues
10
+ Keywords: autodesk,aps,kiota,sdk,httpclient
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: microsoft-kiota-abstractions<2.0.0,>=1.9.0
25
+ Requires-Dist: microsoft-kiota-http<2.0.0,>=1.9.0
26
+ Requires-Dist: httpx<1.0.0,>=0.27.0
27
+
28
+ # adsk-platform-httpclient
29
+
30
+ Shared HTTP client, authentication, and middleware utilities for [Autodesk Platform Services](https://aps.autodesk.com/) Python SDKs.
31
+
32
+ This package provides the foundational components used by all `adsk-platform-*` SDK packages.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install adsk-platform-httpclient
38
+ ```
39
+
40
+ > **Note:** You typically don't need to install this package directly. It's automatically included as a dependency of the SDK packages (e.g., `adsk-platform-acc`, `adsk-platform-data-management`).
41
+
42
+ ## Components
43
+
44
+ ### HttpClientFactory
45
+
46
+ Creates pre-configured `httpx.AsyncClient` instances and Kiota request adapters with bearer-token authentication and the custom middleware pipeline.
47
+
48
+ ```python
49
+ from autodesk_common_httpclient import HttpClientFactory
50
+
51
+ async def get_access_token() -> str:
52
+ return "YOUR_ACCESS_TOKEN"
53
+
54
+ # Create a Kiota request adapter with authentication + middleware
55
+ adapter = HttpClientFactory.create_adapter(get_access_token)
56
+
57
+ # Or provide your own httpx client
58
+ import httpx
59
+ custom_client = httpx.AsyncClient(timeout=60.0)
60
+ adapter = HttpClientFactory.create_adapter(get_access_token, http_client=custom_client)
61
+ ```
62
+
63
+ ### AccessTokenProviderCallback
64
+
65
+ Wraps an async callback into a Kiota-compatible `AccessTokenProvider`.
66
+
67
+ ```python
68
+ from autodesk_common_httpclient import AccessTokenProviderCallback
69
+
70
+ provider = AccessTokenProviderCallback(get_access_token)
71
+ ```
72
+
73
+ ## Custom Middleware
74
+
75
+ The package includes three custom middleware handlers that are automatically added to the HTTP pipeline, matching the C# `Autodesk.Common.HttpClientLibrary.Middleware`:
76
+
77
+ ### ErrorHandler
78
+
79
+ Raises an `httpx.HTTPStatusError` when the HTTP response has a non-success status code (4xx/5xx). Enabled by default.
80
+
81
+ ```python
82
+ from autodesk_common_httpclient import ErrorHandlerOption
83
+
84
+ # Disable for a specific request
85
+ error_option = ErrorHandlerOption(enabled=False)
86
+ ```
87
+
88
+ ### QueryParameterHandler
89
+
90
+ Appends or overwrites query parameters on every outgoing request. Useful for injecting API versions, region codes, or tracking identifiers.
91
+
92
+ ```python
93
+ from autodesk_common_httpclient import QueryParameterHandlerOption
94
+
95
+ query_option = QueryParameterHandlerOption(query_parameters={"region": "US"})
96
+ ```
97
+
98
+ ### RateLimitingHandler
99
+
100
+ Limits the number of concurrent requests per endpoint within a sliding time window. Disabled by default.
101
+
102
+ ```python
103
+ from autodesk_common_httpclient import RateLimitingHandlerOption
104
+
105
+ # Allow max 10 requests per endpoint per 60 seconds
106
+ rate_option = RateLimitingHandlerOption()
107
+ rate_option.set_rate_limit(max_requests=10, time_window_seconds=60.0)
108
+ ```
109
+
110
+ ### Creating a client with rate limiting
111
+
112
+ ```python
113
+ from autodesk_common_httpclient import HttpClientFactory
114
+
115
+ # Create an HTTP client with rate limiting enabled
116
+ client = HttpClientFactory.create_with_rate_limit(
117
+ max_requests=10,
118
+ time_window_seconds=60.0,
119
+ )
120
+
121
+ # Use it with any SDK client
122
+ adapter = HttpClientFactory.create_adapter(get_access_token, http_client=client)
123
+ ```
124
+
125
+ ### Middleware Pipeline Order
126
+
127
+ The middleware is executed in this order (matching the C# implementation):
128
+
129
+ 1. **Kiota defaults** — Redirect, Retry, Parameters Name Decoding, URL Replace, User Agent, Headers Inspection
130
+ 2. **RateLimitingHandler** — Per-endpoint rate limiting
131
+ 3. **QueryParameterHandler** — Query parameter injection
132
+ 4. **ErrorHandler** — Error response detection
133
+
134
+ ## Requirements
135
+
136
+ - Python 3.10 or later
137
+
138
+ ## License
139
+
140
+ This project is licensed under the MIT License.
@@ -0,0 +1,18 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/adsk_platform_httpclient.egg-info/PKG-INFO
4
+ src/adsk_platform_httpclient.egg-info/SOURCES.txt
5
+ src/adsk_platform_httpclient.egg-info/dependency_links.txt
6
+ src/adsk_platform_httpclient.egg-info/requires.txt
7
+ src/adsk_platform_httpclient.egg-info/top_level.txt
8
+ src/autodesk_common_httpclient/__init__.py
9
+ src/autodesk_common_httpclient/access_token_provider.py
10
+ src/autodesk_common_httpclient/http_client_factory.py
11
+ src/autodesk_common_httpclient/middleware/__init__.py
12
+ src/autodesk_common_httpclient/middleware/error_handler.py
13
+ src/autodesk_common_httpclient/middleware/query_parameter_handler.py
14
+ src/autodesk_common_httpclient/middleware/rate_limiting_handler.py
15
+ src/autodesk_common_httpclient/middleware/options/__init__.py
16
+ src/autodesk_common_httpclient/middleware/options/error_handler_option.py
17
+ src/autodesk_common_httpclient/middleware/options/query_parameter_handler_option.py
18
+ src/autodesk_common_httpclient/middleware/options/rate_limiting_handler_option.py
@@ -0,0 +1,3 @@
1
+ microsoft-kiota-abstractions<2.0.0,>=1.9.0
2
+ microsoft-kiota-http<2.0.0,>=1.9.0
3
+ httpx<1.0.0,>=0.27.0
@@ -0,0 +1 @@
1
+ autodesk_common_httpclient
@@ -0,0 +1,28 @@
1
+ """Autodesk Platform Services: shared HTTP client utilities for Kiota-based SDKs."""
2
+
3
+ from autodesk_common_httpclient.access_token_provider import AccessTokenProviderCallback
4
+ from autodesk_common_httpclient.http_client_factory import HttpClientFactory
5
+ from autodesk_common_httpclient.middleware import (
6
+ ErrorContext,
7
+ ErrorHandler,
8
+ QueryParameterHandler,
9
+ RateLimitingHandler,
10
+ )
11
+ from autodesk_common_httpclient.middleware.options import (
12
+ ErrorHandlerOption,
13
+ QueryParameterHandlerOption,
14
+ RateLimitingHandlerOption,
15
+ )
16
+
17
+ __all__ = [
18
+ "AccessTokenProviderCallback",
19
+ "ErrorContext",
20
+ "ErrorHandler",
21
+ "ErrorHandlerOption",
22
+ "HttpClientFactory",
23
+ "QueryParameterHandler",
24
+ "QueryParameterHandlerOption",
25
+ "RateLimitingHandler",
26
+ "RateLimitingHandlerOption",
27
+ ]
28
+ __version__ = "0.1.0"
@@ -0,0 +1,49 @@
1
+ """Access token provider that wraps an async callback for Kiota authentication."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Awaitable, Callable
5
+ from typing import Any
6
+
7
+ from kiota_abstractions.authentication import (
8
+ AccessTokenProvider,
9
+ AllowedHostsValidator,
10
+ )
11
+
12
+
13
+ class AccessTokenProviderCallback(AccessTokenProvider):
14
+ """Wraps an async callback into a Kiota-compatible :class:`AccessTokenProvider`.
15
+
16
+ This is the Python equivalent of the C# ``AccessTokenProvider`` class that wraps
17
+ a ``Func<Task<string>>`` delegate.
18
+
19
+ Args:
20
+ get_access_token: An async callable that returns a valid access token string.
21
+
22
+ Example::
23
+
24
+ async def my_token_provider() -> str:
25
+ return "my-access-token"
26
+
27
+ provider = AccessTokenProviderCallback(my_token_provider)
28
+ """
29
+
30
+ def __init__(self, get_access_token: Callable[[], Awaitable[str]]) -> None:
31
+ self._get_access_token = get_access_token
32
+
33
+ async def get_authorization_token(
34
+ self, uri: str, additional_authentication_context: dict[str, Any] = {}
35
+ ) -> str:
36
+ """Return the access token by invoking the wrapped callback.
37
+
38
+ Args:
39
+ uri: The target URI (ignored — the same token is used for all hosts).
40
+ additional_authentication_context: Additional context (ignored).
41
+
42
+ Returns:
43
+ The access token string.
44
+ """
45
+ return await self._get_access_token()
46
+
47
+ def get_allowed_hosts_validator(self) -> AllowedHostsValidator:
48
+ """Return an :class:`AllowedHostsValidator` that allows all hosts."""
49
+ return AllowedHostsValidator([])
@@ -0,0 +1,170 @@
1
+ """Factory for creating Kiota request adapters with Autodesk authentication and middleware.
2
+
3
+ This is the Python equivalent of the C# ``Autodesk.Common.HttpClientLibrary.HttpClientFactory``.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from collections.abc import Awaitable, Callable
8
+ from typing import Optional
9
+
10
+ import httpx
11
+ from kiota_abstractions.authentication import BaseBearerTokenAuthenticationProvider
12
+ from kiota_abstractions.request_option import RequestOption
13
+ from kiota_http.httpx_request_adapter import HttpxRequestAdapter
14
+ from kiota_http.kiota_client_factory import KiotaClientFactory
15
+ from kiota_http.middleware.middleware import BaseMiddleware
16
+
17
+ from autodesk_common_httpclient.access_token_provider import AccessTokenProviderCallback
18
+ from autodesk_common_httpclient.middleware.error_handler import ErrorHandler
19
+ from autodesk_common_httpclient.middleware.options.error_handler_option import ErrorHandlerOption
20
+ from autodesk_common_httpclient.middleware.options.query_parameter_handler_option import (
21
+ QueryParameterHandlerOption,
22
+ )
23
+ from autodesk_common_httpclient.middleware.options.rate_limiting_handler_option import (
24
+ RateLimitingHandlerOption,
25
+ )
26
+ from autodesk_common_httpclient.middleware.query_parameter_handler import QueryParameterHandler
27
+ from autodesk_common_httpclient.middleware.rate_limiting_handler import RateLimitingHandler
28
+
29
+
30
+ class HttpClientFactory:
31
+ """Creates pre-configured HTTP clients and Kiota request adapters.
32
+
33
+ This mirrors the C# ``HttpClientFactory`` from ``Autodesk.Common.HttpClientLibrary``,
34
+ including the custom middleware pipeline:
35
+
36
+ 1. Kiota default handlers (redirect, retry, parameters name decoding, etc.)
37
+ 2. **RateLimitingHandler** — per-endpoint sliding-window rate limiter
38
+ 3. **QueryParameterHandler** — injects additional query parameters
39
+ 4. **ErrorHandler** — raises on non-success HTTP responses
40
+ """
41
+
42
+ @staticmethod
43
+ def create(
44
+ http_client: Optional[httpx.AsyncClient] = None,
45
+ options: Optional[dict[str, RequestOption]] = None,
46
+ ) -> httpx.AsyncClient:
47
+ """Create an ``httpx.AsyncClient`` with Kiota default + Autodesk custom middleware.
48
+
49
+ Args:
50
+ http_client: An existing client to wrap. If ``None``, a new client
51
+ is created.
52
+ options: Optional dict of :class:`RequestOption` keyed by option key.
53
+ Recognised keys: ``RateLimitingHandlerOption``,
54
+ ``QueryParameterHandlerOption``, ``ErrorHandlerOption``.
55
+
56
+ Returns:
57
+ A configured :class:`httpx.AsyncClient` with the full middleware pipeline.
58
+ """
59
+ # Resolve options (fall back to defaults)
60
+ rate_limit_option = RateLimitingHandlerOption()
61
+ query_param_option = QueryParameterHandlerOption()
62
+ error_option = ErrorHandlerOption()
63
+
64
+ if options:
65
+ opt = options.get(RateLimitingHandlerOption.get_key())
66
+ if isinstance(opt, RateLimitingHandlerOption):
67
+ rate_limit_option = opt
68
+
69
+ opt = options.get(QueryParameterHandlerOption.get_key())
70
+ if isinstance(opt, QueryParameterHandlerOption):
71
+ query_param_option = opt
72
+
73
+ opt = options.get(ErrorHandlerOption.get_key())
74
+ if isinstance(opt, ErrorHandlerOption):
75
+ error_option = opt
76
+
77
+ # Build the middleware list: Kiota defaults + custom handlers
78
+ middleware = KiotaClientFactory.get_default_middleware(options)
79
+ middleware.extend([
80
+ RateLimitingHandler(rate_limit_option),
81
+ QueryParameterHandler(query_param_option),
82
+ ErrorHandler(error_option),
83
+ ])
84
+
85
+ client = http_client or KiotaClientFactory.get_default_client()
86
+ return KiotaClientFactory.create_with_custom_middleware(middleware, client)
87
+
88
+ @staticmethod
89
+ def create_with_rate_limit(
90
+ max_requests: int,
91
+ time_window_seconds: float,
92
+ http_client: Optional[httpx.AsyncClient] = None,
93
+ ) -> httpx.AsyncClient:
94
+ """Create an ``httpx.AsyncClient`` with rate limiting enabled.
95
+
96
+ Args:
97
+ max_requests: Maximum requests per endpoint within the time window.
98
+ time_window_seconds: Duration of the sliding window in seconds.
99
+ http_client: Optional existing client to wrap.
100
+
101
+ Returns:
102
+ A configured :class:`httpx.AsyncClient`.
103
+ """
104
+ rate_option = RateLimitingHandlerOption()
105
+ rate_option.set_rate_limit(max_requests, time_window_seconds)
106
+ return HttpClientFactory.create(
107
+ http_client,
108
+ options={RateLimitingHandlerOption.get_key(): rate_option},
109
+ )
110
+
111
+ @staticmethod
112
+ def create_adapter(
113
+ get_access_token: Callable[[], Awaitable[str]],
114
+ http_client: Optional[httpx.AsyncClient] = None,
115
+ ) -> HttpxRequestAdapter:
116
+ """Create a :class:`HttpxRequestAdapter` with bearer-token auth and custom middleware.
117
+
118
+ This is the Python equivalent of the C#
119
+ ``HttpClientFactory.CreateAdapter(Func<Task<string>>, HttpClient?)``.
120
+
121
+ Args:
122
+ get_access_token: An async callable returning an access token string.
123
+ http_client: Optional :class:`httpx.AsyncClient`. If ``None``, a new
124
+ client with the full middleware pipeline is created.
125
+
126
+ Returns:
127
+ A ready-to-use :class:`HttpxRequestAdapter`.
128
+ """
129
+ auth = BaseBearerTokenAuthenticationProvider(
130
+ AccessTokenProviderCallback(get_access_token)
131
+ )
132
+ client = HttpClientFactory.create(http_client)
133
+ return HttpxRequestAdapter(auth, http_client=client)
134
+
135
+ @staticmethod
136
+ def get_default_middleware(
137
+ options: Optional[dict[str, RequestOption]] = None,
138
+ ) -> list[BaseMiddleware]:
139
+ """Return the full middleware list (Kiota defaults + Autodesk custom).
140
+
141
+ Args:
142
+ options: Optional dict of request options.
143
+
144
+ Returns:
145
+ Ordered list of middleware handlers.
146
+ """
147
+ rate_limit_option = RateLimitingHandlerOption()
148
+ query_param_option = QueryParameterHandlerOption()
149
+ error_option = ErrorHandlerOption()
150
+
151
+ if options:
152
+ opt = options.get(RateLimitingHandlerOption.get_key())
153
+ if isinstance(opt, RateLimitingHandlerOption):
154
+ rate_limit_option = opt
155
+
156
+ opt = options.get(QueryParameterHandlerOption.get_key())
157
+ if isinstance(opt, QueryParameterHandlerOption):
158
+ query_param_option = opt
159
+
160
+ opt = options.get(ErrorHandlerOption.get_key())
161
+ if isinstance(opt, ErrorHandlerOption):
162
+ error_option = opt
163
+
164
+ middleware = KiotaClientFactory.get_default_middleware(options)
165
+ middleware.extend([
166
+ RateLimitingHandler(rate_limit_option),
167
+ QueryParameterHandler(query_param_option),
168
+ ErrorHandler(error_option),
169
+ ])
170
+ return middleware
@@ -0,0 +1,7 @@
1
+ """Custom Kiota middleware for Autodesk Platform Services."""
2
+
3
+ from autodesk_common_httpclient.middleware.error_handler import ErrorContext, ErrorHandler
4
+ from autodesk_common_httpclient.middleware.query_parameter_handler import QueryParameterHandler
5
+ from autodesk_common_httpclient.middleware.rate_limiting_handler import RateLimitingHandler
6
+
7
+ __all__ = ["ErrorContext", "ErrorHandler", "QueryParameterHandler", "RateLimitingHandler"]
@@ -0,0 +1,89 @@
1
+ """Middleware that raises on non-success HTTP responses.
2
+
3
+ Python equivalent of the C# ``Autodesk.Common.HttpClientLibrary.Middleware.ErrorHandler``.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional
9
+
10
+ import httpx
11
+ from kiota_http.middleware.middleware import BaseMiddleware
12
+
13
+ from autodesk_common_httpclient.middleware.options.error_handler_option import ErrorHandlerOption
14
+
15
+
16
+ @dataclass
17
+ class ErrorContext:
18
+ """Contains context information about a failed HTTP request."""
19
+
20
+ request_url: Optional[str] = None
21
+ request_method: Optional[str] = None
22
+ request_headers: dict[str, str] = field(default_factory=dict)
23
+ request_content: Optional[bytes] = None
24
+ response_headers: dict[str, str] = field(default_factory=dict)
25
+ response_content: Optional[bytes] = None
26
+
27
+
28
+ class ErrorHandler(BaseMiddleware):
29
+ """Raises an :class:`httpx.HTTPStatusError` when the response status is not successful.
30
+
31
+ The error includes an :class:`ErrorContext` attached via the ``context`` attribute
32
+ on the exception, containing the full request/response details for debugging.
33
+ This mirrors the C# ``ErrorHandler`` which throws ``HttpRequestException``
34
+ with the response attached in ``Data["context"]``.
35
+
36
+ Args:
37
+ options: Configuration controlling whether the handler is active.
38
+ """
39
+
40
+ def __init__(self, options: ErrorHandlerOption | None = None) -> None:
41
+ super().__init__()
42
+ self.options = options or ErrorHandlerOption(enabled=True)
43
+
44
+ async def send(
45
+ self, request: httpx.Request, transport: httpx.AsyncBaseTransport
46
+ ) -> httpx.Response:
47
+ """Send the request, raising on non-2xx responses if enabled."""
48
+ # Buffer the request content before sending so it's available in the
49
+ # error context even after the request completes
50
+ request_content = request.content if request.content else None
51
+
52
+ response = await super().send(request, transport)
53
+
54
+ current_options = self._get_current_options(request)
55
+
56
+ if current_options.enabled and response.status_code >= 400:
57
+ # Read/buffer the response body so it remains readable after the
58
+ # exception propagates
59
+ response_body = response.text
60
+ response_content = response.content
61
+
62
+ error_context = ErrorContext(
63
+ request_url=str(request.url),
64
+ request_method=request.method,
65
+ request_headers=dict(request.headers),
66
+ request_content=request_content,
67
+ response_headers=dict(response.headers),
68
+ response_content=response_content,
69
+ )
70
+
71
+ error = httpx.HTTPStatusError(
72
+ message=(
73
+ f"Request to '{request.url}' failed with status code "
74
+ f"'{response.status_code}'. Response body: {response_body}"
75
+ ),
76
+ request=request,
77
+ response=response,
78
+ )
79
+ error.context = error_context # type: ignore[attr-defined]
80
+ raise error
81
+
82
+ return response
83
+
84
+ def _get_current_options(self, request: httpx.Request) -> ErrorHandlerOption:
85
+ """Return per-request options if set, otherwise fall back to defaults."""
86
+ request_options = getattr(request, "options", None)
87
+ if request_options:
88
+ return request_options.get(ErrorHandlerOption.get_key(), self.options)
89
+ return self.options
@@ -0,0 +1,11 @@
1
+ """Middleware option classes for custom Autodesk handlers."""
2
+
3
+ from autodesk_common_httpclient.middleware.options.error_handler_option import ErrorHandlerOption
4
+ from autodesk_common_httpclient.middleware.options.query_parameter_handler_option import (
5
+ QueryParameterHandlerOption,
6
+ )
7
+ from autodesk_common_httpclient.middleware.options.rate_limiting_handler_option import (
8
+ RateLimitingHandlerOption,
9
+ )
10
+
11
+ __all__ = ["ErrorHandlerOption", "QueryParameterHandlerOption", "RateLimitingHandlerOption"]
@@ -0,0 +1,19 @@
1
+ """Option class for the ErrorHandler middleware."""
2
+ from kiota_abstractions.request_option import RequestOption
3
+
4
+
5
+ class ErrorHandlerOption(RequestOption):
6
+ """Configuration for the :class:`~autodesk_common_httpclient.middleware.ErrorHandler`.
7
+
8
+ Attributes:
9
+ enabled: If ``True``, the handler raises on non-success HTTP responses.
10
+ """
11
+
12
+ ERROR_HANDLER_OPTION_KEY = "ErrorHandlerOption"
13
+
14
+ def __init__(self, enabled: bool = True) -> None:
15
+ self.enabled = enabled
16
+
17
+ @staticmethod
18
+ def get_key() -> str:
19
+ return ErrorHandlerOption.ERROR_HANDLER_OPTION_KEY
@@ -0,0 +1,20 @@
1
+ """Option class for the QueryParameterHandler middleware."""
2
+ from kiota_abstractions.request_option import RequestOption
3
+
4
+
5
+ class QueryParameterHandlerOption(RequestOption):
6
+ """Configuration for the :class:`~autodesk_common_httpclient.middleware.QueryParameterHandler`.
7
+
8
+ Attributes:
9
+ query_parameters: A dict of query parameter key-value pairs to append
10
+ or overwrite on every outgoing request.
11
+ """
12
+
13
+ QUERY_PARAMETER_HANDLER_OPTION_KEY = "QueryParameterHandlerOption"
14
+
15
+ def __init__(self, query_parameters: dict[str, str] | None = None) -> None:
16
+ self.query_parameters: dict[str, str] = query_parameters or {}
17
+
18
+ @staticmethod
19
+ def get_key() -> str:
20
+ return QueryParameterHandlerOption.QUERY_PARAMETER_HANDLER_OPTION_KEY
@@ -0,0 +1,55 @@
1
+ """Option class for the RateLimitingHandler middleware."""
2
+ from __future__ import annotations
3
+
4
+ from kiota_abstractions.request_option import RequestOption
5
+
6
+
7
+ class RateLimitingHandlerOption(RequestOption):
8
+ """Configuration for the :class:`~autodesk_common_httpclient.middleware.RateLimitingHandler`.
9
+
10
+ Disabled by default. Call :meth:`set_rate_limit` to enable.
11
+ """
12
+
13
+ RATE_LIMITING_HANDLER_OPTION_KEY = "RateLimitingHandlerOption"
14
+
15
+ def __init__(self) -> None:
16
+ self._max_requests: int = 0
17
+ self._time_window_seconds: float = 0.0
18
+
19
+ def set_rate_limit(self, max_requests: int, time_window_seconds: float) -> None:
20
+ """Enable rate limiting.
21
+
22
+ Args:
23
+ max_requests: Maximum number of requests allowed within the time window.
24
+ Must be greater than zero.
25
+ time_window_seconds: Duration of the sliding window in seconds.
26
+ Must be greater than zero.
27
+
28
+ Raises:
29
+ ValueError: If either argument is not positive.
30
+ """
31
+ if max_requests <= 0:
32
+ raise ValueError("max_requests must be greater than zero.")
33
+ if time_window_seconds <= 0:
34
+ raise ValueError("time_window_seconds must be greater than zero.")
35
+ self._max_requests = max_requests
36
+ self._time_window_seconds = time_window_seconds
37
+
38
+ def get_rate_limit(self) -> tuple[int, float] | None:
39
+ """Return the current rate limit, or ``None`` if disabled.
40
+
41
+ Returns:
42
+ A ``(max_requests, time_window_seconds)`` tuple, or ``None``.
43
+ """
44
+ if self._max_requests == 0 or self._time_window_seconds == 0.0:
45
+ return None
46
+ return (self._max_requests, self._time_window_seconds)
47
+
48
+ def disable(self) -> None:
49
+ """Disable rate limiting."""
50
+ self._max_requests = 0
51
+ self._time_window_seconds = 0.0
52
+
53
+ @staticmethod
54
+ def get_key() -> str:
55
+ return RateLimitingHandlerOption.RATE_LIMITING_HANDLER_OPTION_KEY
@@ -0,0 +1,64 @@
1
+ """Middleware that appends or overwrites query parameters on outgoing requests.
2
+
3
+ Python equivalent of the C# ``Autodesk.Common.HttpClientLibrary.Middleware.QueryParameterHandler``.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
8
+
9
+ import httpx
10
+ from kiota_http.middleware.middleware import BaseMiddleware
11
+
12
+ from autodesk_common_httpclient.middleware.options.query_parameter_handler_option import (
13
+ QueryParameterHandlerOption,
14
+ )
15
+
16
+
17
+ class QueryParameterHandler(BaseMiddleware):
18
+ """Injects additional query parameters into every outgoing request URL.
19
+
20
+ Useful for injecting common parameters like API versions, region codes,
21
+ or tracking identifiers across all API calls.
22
+
23
+ Args:
24
+ options: Configuration containing the query parameters to inject.
25
+ """
26
+
27
+ def __init__(self, options: QueryParameterHandlerOption | None = None) -> None:
28
+ super().__init__()
29
+ self.options = options or QueryParameterHandlerOption()
30
+
31
+ async def send(
32
+ self, request: httpx.Request, transport: httpx.AsyncBaseTransport
33
+ ) -> httpx.Response:
34
+ """Merge configured query parameters into the request URL, then forward."""
35
+ current_options = self._get_current_options(request)
36
+
37
+ if current_options.query_parameters:
38
+ parsed = urlparse(str(request.url))
39
+ # Parse existing query string (keep values as flat strings)
40
+ existing_params = parse_qs(parsed.query, keep_blank_values=True)
41
+ # Overwrite / add new parameters
42
+ for key, value in current_options.query_parameters.items():
43
+ existing_params[key] = [value]
44
+ new_query = urlencode(existing_params, doseq=True)
45
+ new_url = urlunparse(parsed._replace(query=new_query))
46
+ request = httpx.Request(
47
+ method=request.method,
48
+ url=new_url,
49
+ headers=request.headers,
50
+ content=request.content,
51
+ extensions=request.extensions,
52
+ )
53
+ # Preserve options attribute if present
54
+ if hasattr(request, "options"):
55
+ pass # httpx.Request is immutable; options are already on the original
56
+
57
+ return await super().send(request, transport)
58
+
59
+ def _get_current_options(self, request: httpx.Request) -> QueryParameterHandlerOption:
60
+ """Return per-request options if set, otherwise fall back to defaults."""
61
+ request_options = getattr(request, "options", None)
62
+ if request_options:
63
+ return request_options.get(QueryParameterHandlerOption.get_key(), self.options)
64
+ return self.options
@@ -0,0 +1,89 @@
1
+ """Middleware that limits concurrent requests per endpoint using a sliding window.
2
+
3
+ Python equivalent of the C# ``Autodesk.Common.HttpClientLibrary.Middleware.RateLimitingHandler``.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ from datetime import datetime, timezone
9
+
10
+ import httpx
11
+ from kiota_http.middleware.middleware import BaseMiddleware
12
+
13
+ from autodesk_common_httpclient.middleware.options.rate_limiting_handler_option import (
14
+ RateLimitingHandlerOption,
15
+ )
16
+
17
+
18
+ class _RateLimiter:
19
+ """Per-endpoint sliding-window rate limiter (async-safe)."""
20
+
21
+ def __init__(self, max_requests: int, time_window_seconds: float) -> None:
22
+ self._max_requests = max_requests
23
+ self._time_window_seconds = time_window_seconds
24
+ self._request_count = 0
25
+ self._reset_time = datetime.now(timezone.utc).timestamp() + time_window_seconds
26
+ self._lock = asyncio.Lock()
27
+
28
+ async def wait_for_availability(self) -> None:
29
+ """Block until a request slot becomes available in the current window."""
30
+ async with self._lock:
31
+ now = datetime.now(timezone.utc).timestamp()
32
+
33
+ if now >= self._reset_time:
34
+ self._request_count = 0
35
+ self._reset_time = now + self._time_window_seconds
36
+
37
+ if self._request_count >= self._max_requests:
38
+ delay = self._reset_time - now
39
+ if delay > 0:
40
+ await asyncio.sleep(delay)
41
+ self._request_count = 0
42
+ self._reset_time = datetime.now(timezone.utc).timestamp() + self._time_window_seconds
43
+
44
+ self._request_count += 1
45
+
46
+
47
+ class RateLimitingHandler(BaseMiddleware):
48
+ """Limits the number of concurrent requests per endpoint within a time window.
49
+
50
+ Endpoints are identified by ``METHOD|path`` (query string excluded) to avoid
51
+ counting different query parameter combinations as separate endpoints.
52
+
53
+ Args:
54
+ options: Rate limiting configuration. Disabled by default.
55
+ """
56
+
57
+ def __init__(self, options: RateLimitingHandlerOption | None = None) -> None:
58
+ super().__init__()
59
+ self.options = options or RateLimitingHandlerOption()
60
+ self._rate_limiters: dict[str, _RateLimiter] = {}
61
+
62
+ async def send(
63
+ self, request: httpx.Request, transport: httpx.AsyncBaseTransport
64
+ ) -> httpx.Response:
65
+ """Wait for an available rate-limit slot, then forward the request."""
66
+ current_options = self._get_current_options(request)
67
+ rate_limit = current_options.get_rate_limit()
68
+
69
+ if rate_limit is not None:
70
+ max_requests, time_window_seconds = rate_limit
71
+ endpoint = self._get_endpoint(request)
72
+ if endpoint not in self._rate_limiters:
73
+ self._rate_limiters[endpoint] = _RateLimiter(max_requests, time_window_seconds)
74
+ await self._rate_limiters[endpoint].wait_for_availability()
75
+
76
+ return await super().send(request, transport)
77
+
78
+ @staticmethod
79
+ def _get_endpoint(request: httpx.Request) -> str:
80
+ """Derive a stable endpoint key from the request (method + path, no query)."""
81
+ url = request.url
82
+ return f"{request.method}|{url.scheme}://{url.host}{url.path}"
83
+
84
+ def _get_current_options(self, request: httpx.Request) -> RateLimitingHandlerOption:
85
+ """Return per-request options if set, otherwise fall back to defaults."""
86
+ request_options = getattr(request, "options", None)
87
+ if request_options:
88
+ return request_options.get(RateLimitingHandlerOption.get_key(), self.options)
89
+ return self.options