csrd-delegate 0.2.0__tar.gz → 0.3.0__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.
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: csrd-delegate
3
+ Version: 0.3.0
4
+ Summary: HTTP delegate base class with retry support
5
+ Project-URL: Repository, https://github.com/csrd-api/fastapi-common
6
+ Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/delegate
7
+ Project-URL: Changelog, https://github.com/csrd-api/fastapi-common/blob/main/CHANGELOG.md
8
+ License: MIT
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: csrd-context
11
+ Requires-Dist: csrd-models
12
+ Requires-Dist: fastapi<1,>=0.115
13
+ Requires-Dist: httpx<1,>=0.28
14
+ Requires-Dist: tenacity<10,>=9.1
15
+ Requires-Dist: yarl<2,>=1.20
16
+ Description-Content-Type: text/markdown
17
+
18
+ # csrd-delegate
19
+
20
+ HTTP client delegate base class with retry support for FastAPI microservices.
21
+
22
+ **Package**: `csrd.delegate` · **Import**: `from csrd.delegate import BaseDelegate`
23
+
24
+ ## What's included
25
+
26
+ - `BaseDelegate` — async HTTP client with header forwarding, retry via tenacity, and lifecycle (`close()` / `async with`)
27
+ - Response parsing via `csrd.models.model_parser`
28
+ - Configurable retry profiles (`conservative`, `aggressive`, `resilient`)
29
+ - httpx-specific response types (`ResponseHandler`, `ResponseHandlerMap`)
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ uv pip install "csrd-delegate @ git+ssh://git@github.com/csrd-api/fastapi-common.git#subdirectory=packages/delegate"
35
+ ```
36
+
37
+ ## Dependencies
38
+
39
+ - `csrd-models`, `csrd-context` (Tier 2)
40
+
41
+ ## Consumer Feature Matrix (`BaseDelegate`)
42
+
43
+ | Capability area | Common expectation | Actual support | How to use / notes |
44
+ |---|---|---|---|
45
+ | HTTP methods | Standard verb helpers exist | ✅ Built-in | `get`, `post`, `put`, `patch`, `delete`, `head`, `options` |
46
+ | Retry support | Automatic retries for transient failures | ✅ Built-in + configurable | Set default in constructor (`retry_profile` / `retry_*`) or per call |
47
+ | Retry presets | Named retry policies | ✅ Built-in | `conservative`, `aggressive`, `resilient` |
48
+ | Auth forwarding | Bearer token from incoming request is forwarded | ✅ Built-in | Uses request context headers; can be filtered explicitly |
49
+ | Header filtering | Hop-by-hop headers are removed safely | ✅ Built-in | RFC-hop headers filtered by default; body requests additionally filter `content-length` |
50
+ | Response parsing | Auto-parse JSON to typed model | ✅ Built-in + configurable | Use `response_model` and/or `model_handler` |
51
+ | Status handling | Non-2xx statuses raise exceptions | ✅ Built-in + overridable | Default raises `HTTPException`; override specific statuses with `response_handlers` |
52
+ | Per-status custom behavior | Custom handling for 404/409/etc | ✅ Built-in | Pass `response_handlers={404: ...}` style map |
53
+ | Client lifecycle | Safe `async with` usage and close management | ✅ Built-in | Delegate-owned client closes automatically; injected client is not closed |
54
+ | Per-call transport overrides | Override timeout/headers/auth per request | ✅ Built-in | `timeout`, `headers`, `auth`, `follow_redirects`, etc. on each call |
55
+ | Circuit breaker | Open/half-open breaker semantics | ❌ Not built-in | Add externally (middleware/proxy/custom wrapper) |
56
+ | Rate limiting | Outbound request throttling | ❌ Not built-in | Add externally (gateway/proxy/custom transport) |
57
+ | Service discovery | Dynamic upstream resolution/registry | ❌ Not built-in | Provide concrete `service_host` at wiring time |
58
+
59
+ ---
60
+
61
+ ## Inter-Service Auth Propagation
62
+
63
+ In multi-service clusters, delegates automatically forward authentication headers from the inbound request to upstream service calls. This enables **transparent claim propagation** through delegate chains.
64
+
65
+ ### How It Works
66
+
67
+ 1. **Client → Service A**: Client sends JWT via `Authorization: Bearer <token>` header
68
+ 2. **Service A** extracts and verifies claims using `VerifiedTokenDep` (or similar)
69
+ 3. **Service A → Service B** (via `BaseDelegate`): Delegate automatically includes `Authorization` header from inbound request
70
+ 4. **Service B** receives and verifies same JWT from Service A's outbound call
71
+ 5. **Service B → Service C** (if needed): Claim propagation continues transitively
72
+
73
+ ### Example: Multi-Hop Orchestration
74
+
75
+ ```python
76
+ # quote-service orchestrates inventory + pricing calls
77
+ class QuoteDelegate(BaseDelegate):
78
+ def __init__(self) -> None:
79
+ super().__init__(settings.quote_service_url, retry_profile="conservative")
80
+
81
+ async def compose_quote(self, item_id: str, quantity: int):
82
+ # All three calls automatically include Authorization header
83
+ inventory_data = await self.get(f"/api/inventory/{item_id}")
84
+ pricing_data = await self.get(f"/api/pricing/{item_id}")
85
+ return {
86
+ "item_id": item_id,
87
+ "quantity": quantity,
88
+ "stock_available": inventory_data["available_qty"],
89
+ "price": pricing_data["base_price"],
90
+ }
91
+
92
+ # On inbound request with JWT:
93
+ @router.post("/api/quotes")
94
+ async def create_quote(
95
+ item_id: str,
96
+ quantity: int,
97
+ verified: VerifiedTokenDep, # JWT verified here
98
+ quote_delegate: QuoteDelegate,
99
+ ) -> dict:
100
+ # QuoteDelegate.compose_quote() calls use verified user's JWT
101
+ return await quote_delegate.compose_quote(item_id, quantity)
102
+ ```
103
+
104
+ ### Header Filtering Behavior
105
+
106
+ - **Hop-by-hop headers** (RFC 7230): Automatically filtered (connection, keep-alive, etc.)
107
+ - **Authorization header**: Always forwarded unless explicitly filtered
108
+ - **Content-Length**: Re-computed by httpx for each request
109
+ - **Custom headers**: Passed through by default; filter via `header_filter_list` if needed
110
+
111
+ ### Pattern Constraints
112
+
113
+ 1. **No auth bypass**: Each hop validates the JWT independently
114
+ 2. **Claim consistency**: All services see the same `sub`, `roles`, etc.
115
+ 3. **No credential injection**: Delegates don't add credentials; they forward inbound claims only
116
+ 4. **No header injection**: Custom headers must be passed explicitly; inbound headers only forwarded if present
117
+
118
+ ### When to Use Manual Header Control
119
+
120
+ Override the automatic forwarding only in specific cases:
121
+
122
+ ```python
123
+ class InternalDelegate(BaseDelegate):
124
+ def __init__(self) -> None:
125
+ super().__init__(
126
+ settings.internal_service_url,
127
+ header_filter_list=["authorization", "x-tenant-id"], # Don't forward these
128
+ ignore_incoming_headers=False, # Still use get_headers() for others
129
+ )
130
+ ```
131
+
132
+ ---
@@ -0,0 +1,115 @@
1
+ # csrd-delegate
2
+
3
+ HTTP client delegate base class with retry support for FastAPI microservices.
4
+
5
+ **Package**: `csrd.delegate` · **Import**: `from csrd.delegate import BaseDelegate`
6
+
7
+ ## What's included
8
+
9
+ - `BaseDelegate` — async HTTP client with header forwarding, retry via tenacity, and lifecycle (`close()` / `async with`)
10
+ - Response parsing via `csrd.models.model_parser`
11
+ - Configurable retry profiles (`conservative`, `aggressive`, `resilient`)
12
+ - httpx-specific response types (`ResponseHandler`, `ResponseHandlerMap`)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ uv pip install "csrd-delegate @ git+ssh://git@github.com/csrd-api/fastapi-common.git#subdirectory=packages/delegate"
18
+ ```
19
+
20
+ ## Dependencies
21
+
22
+ - `csrd-models`, `csrd-context` (Tier 2)
23
+
24
+ ## Consumer Feature Matrix (`BaseDelegate`)
25
+
26
+ | Capability area | Common expectation | Actual support | How to use / notes |
27
+ |---|---|---|---|
28
+ | HTTP methods | Standard verb helpers exist | ✅ Built-in | `get`, `post`, `put`, `patch`, `delete`, `head`, `options` |
29
+ | Retry support | Automatic retries for transient failures | ✅ Built-in + configurable | Set default in constructor (`retry_profile` / `retry_*`) or per call |
30
+ | Retry presets | Named retry policies | ✅ Built-in | `conservative`, `aggressive`, `resilient` |
31
+ | Auth forwarding | Bearer token from incoming request is forwarded | ✅ Built-in | Uses request context headers; can be filtered explicitly |
32
+ | Header filtering | Hop-by-hop headers are removed safely | ✅ Built-in | RFC-hop headers filtered by default; body requests additionally filter `content-length` |
33
+ | Response parsing | Auto-parse JSON to typed model | ✅ Built-in + configurable | Use `response_model` and/or `model_handler` |
34
+ | Status handling | Non-2xx statuses raise exceptions | ✅ Built-in + overridable | Default raises `HTTPException`; override specific statuses with `response_handlers` |
35
+ | Per-status custom behavior | Custom handling for 404/409/etc | ✅ Built-in | Pass `response_handlers={404: ...}` style map |
36
+ | Client lifecycle | Safe `async with` usage and close management | ✅ Built-in | Delegate-owned client closes automatically; injected client is not closed |
37
+ | Per-call transport overrides | Override timeout/headers/auth per request | ✅ Built-in | `timeout`, `headers`, `auth`, `follow_redirects`, etc. on each call |
38
+ | Circuit breaker | Open/half-open breaker semantics | ❌ Not built-in | Add externally (middleware/proxy/custom wrapper) |
39
+ | Rate limiting | Outbound request throttling | ❌ Not built-in | Add externally (gateway/proxy/custom transport) |
40
+ | Service discovery | Dynamic upstream resolution/registry | ❌ Not built-in | Provide concrete `service_host` at wiring time |
41
+
42
+ ---
43
+
44
+ ## Inter-Service Auth Propagation
45
+
46
+ In multi-service clusters, delegates automatically forward authentication headers from the inbound request to upstream service calls. This enables **transparent claim propagation** through delegate chains.
47
+
48
+ ### How It Works
49
+
50
+ 1. **Client → Service A**: Client sends JWT via `Authorization: Bearer <token>` header
51
+ 2. **Service A** extracts and verifies claims using `VerifiedTokenDep` (or similar)
52
+ 3. **Service A → Service B** (via `BaseDelegate`): Delegate automatically includes `Authorization` header from inbound request
53
+ 4. **Service B** receives and verifies same JWT from Service A's outbound call
54
+ 5. **Service B → Service C** (if needed): Claim propagation continues transitively
55
+
56
+ ### Example: Multi-Hop Orchestration
57
+
58
+ ```python
59
+ # quote-service orchestrates inventory + pricing calls
60
+ class QuoteDelegate(BaseDelegate):
61
+ def __init__(self) -> None:
62
+ super().__init__(settings.quote_service_url, retry_profile="conservative")
63
+
64
+ async def compose_quote(self, item_id: str, quantity: int):
65
+ # All three calls automatically include Authorization header
66
+ inventory_data = await self.get(f"/api/inventory/{item_id}")
67
+ pricing_data = await self.get(f"/api/pricing/{item_id}")
68
+ return {
69
+ "item_id": item_id,
70
+ "quantity": quantity,
71
+ "stock_available": inventory_data["available_qty"],
72
+ "price": pricing_data["base_price"],
73
+ }
74
+
75
+ # On inbound request with JWT:
76
+ @router.post("/api/quotes")
77
+ async def create_quote(
78
+ item_id: str,
79
+ quantity: int,
80
+ verified: VerifiedTokenDep, # JWT verified here
81
+ quote_delegate: QuoteDelegate,
82
+ ) -> dict:
83
+ # QuoteDelegate.compose_quote() calls use verified user's JWT
84
+ return await quote_delegate.compose_quote(item_id, quantity)
85
+ ```
86
+
87
+ ### Header Filtering Behavior
88
+
89
+ - **Hop-by-hop headers** (RFC 7230): Automatically filtered (connection, keep-alive, etc.)
90
+ - **Authorization header**: Always forwarded unless explicitly filtered
91
+ - **Content-Length**: Re-computed by httpx for each request
92
+ - **Custom headers**: Passed through by default; filter via `header_filter_list` if needed
93
+
94
+ ### Pattern Constraints
95
+
96
+ 1. **No auth bypass**: Each hop validates the JWT independently
97
+ 2. **Claim consistency**: All services see the same `sub`, `roles`, etc.
98
+ 3. **No credential injection**: Delegates don't add credentials; they forward inbound claims only
99
+ 4. **No header injection**: Custom headers must be passed explicitly; inbound headers only forwarded if present
100
+
101
+ ### When to Use Manual Header Control
102
+
103
+ Override the automatic forwarding only in specific cases:
104
+
105
+ ```python
106
+ class InternalDelegate(BaseDelegate):
107
+ def __init__(self) -> None:
108
+ super().__init__(
109
+ settings.internal_service_url,
110
+ header_filter_list=["authorization", "x-tenant-id"], # Don't forward these
111
+ ignore_incoming_headers=False, # Still use get_headers() for others
112
+ )
113
+ ```
114
+
115
+ ---
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csrd-delegate"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "HTTP delegate base class with retry support"
5
5
  license = { text = "MIT" }
6
6
  requires-python = ">=3.12"
@@ -1,39 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: csrd-delegate
3
- Version: 0.2.0
4
- Summary: HTTP delegate base class with retry support
5
- Project-URL: Repository, https://github.com/csrd-api/fastapi-common
6
- Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/delegate
7
- Project-URL: Changelog, https://github.com/csrd-api/fastapi-common/blob/main/CHANGELOG.md
8
- License: MIT
9
- Requires-Python: >=3.12
10
- Requires-Dist: csrd-context
11
- Requires-Dist: csrd-models
12
- Requires-Dist: fastapi<1,>=0.115
13
- Requires-Dist: httpx<1,>=0.28
14
- Requires-Dist: tenacity<10,>=9.1
15
- Requires-Dist: yarl<2,>=1.20
16
- Description-Content-Type: text/markdown
17
-
18
- # csrd-delegate
19
-
20
- HTTP client delegate base class with retry support for FastAPI microservices.
21
-
22
- **Package**: `csrd.delegate` · **Import**: `from csrd.delegate import BaseDelegate`
23
-
24
- ## What's included
25
-
26
- - `BaseDelegate` — async HTTP client with header forwarding, retry via tenacity, and lifecycle (`close()` / `async with`)
27
- - Response parsing via `csrd.models.model_parser`
28
- - Configurable retry profiles (`conservative`, `aggressive`, `resilient`)
29
- - httpx-specific response types (`ResponseHandler`, `ResponseHandlerMap`)
30
-
31
- ## Installation
32
-
33
- ```bash
34
- uv pip install "csrd-delegate @ git+ssh://git@github.com/csrd-api/fastapi-common.git#subdirectory=packages/delegate"
35
- ```
36
-
37
- ## Dependencies
38
-
39
- - `csrd-models`, `csrd-context` (Tier 2)
@@ -1,22 +0,0 @@
1
- # csrd-delegate
2
-
3
- HTTP client delegate base class with retry support for FastAPI microservices.
4
-
5
- **Package**: `csrd.delegate` · **Import**: `from csrd.delegate import BaseDelegate`
6
-
7
- ## What's included
8
-
9
- - `BaseDelegate` — async HTTP client with header forwarding, retry via tenacity, and lifecycle (`close()` / `async with`)
10
- - Response parsing via `csrd.models.model_parser`
11
- - Configurable retry profiles (`conservative`, `aggressive`, `resilient`)
12
- - httpx-specific response types (`ResponseHandler`, `ResponseHandlerMap`)
13
-
14
- ## Installation
15
-
16
- ```bash
17
- uv pip install "csrd-delegate @ git+ssh://git@github.com/csrd-api/fastapi-common.git#subdirectory=packages/delegate"
18
- ```
19
-
20
- ## Dependencies
21
-
22
- - `csrd-models`, `csrd-context` (Tier 2)
File without changes