jararaca 0.3.11a16__tar.gz → 0.3.12__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.
Potentially problematic release.
This version of jararaca might be problematic. Click here for more details.
- {jararaca-0.3.11a16 → jararaca-0.3.12}/PKG-INFO +4 -3
- jararaca-0.3.12/docs/http-rpc.md +564 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/index.md +144 -20
- jararaca-0.3.12/docs/interceptors.md +210 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/messagebus.md +24 -6
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/scheduler.md +44 -24
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/websocket.md +24 -10
- {jararaca-0.3.11a16 → jararaca-0.3.12}/pyproject.toml +2 -1
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/__init__.py +106 -8
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/cli.py +216 -31
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/worker.py +749 -272
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/microservice.py +42 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/interceptors/aiosqa_interceptor.py +82 -73
- jararaca-0.3.12/src/jararaca/persistence/interceptors/constants.py +1 -0
- jararaca-0.3.12/src/jararaca/persistence/interceptors/decorators.py +45 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/server.py +57 -11
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/redis.py +113 -7
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/reflect/metadata.py +1 -1
- jararaca-0.3.12/src/jararaca/rpc/http/__init__.py +97 -0
- jararaca-0.3.12/src/jararaca/rpc/http/backends/__init__.py +10 -0
- jararaca-0.3.12/src/jararaca/rpc/http/backends/httpx.py +71 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/rpc/http/decorators.py +302 -6
- jararaca-0.3.12/src/jararaca/scheduler/beat_worker.py +801 -0
- jararaca-0.3.12/src/jararaca/tools/typescript/decorators.py +95 -0
- jararaca-0.3.12/src/jararaca/tools/typescript/interface_parser.py +1415 -0
- jararaca-0.3.11a16/src/jararaca/rpc/http/backends/httpx.py +0 -41
- jararaca-0.3.11a16/src/jararaca/scheduler/beat_worker.py +0 -342
- jararaca-0.3.11a16/src/jararaca/tools/typescript/interface_parser.py +0 -872
- jararaca-0.3.11a16/src/jararaca/utils/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/LICENSE +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/README.md +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/CNAME +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/architecture.md +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.jpeg +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.webp +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/assets/tracing_example.png +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/retry.md +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/docs/stylesheets/custom.css +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/__main__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/broker_backend/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/broker_backend/mapper.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/broker_backend/redis_broker_backend.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/common/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/core/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/core/providers.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/core/uow.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/di.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/files/entity.py.mako +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/lifecycle.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/bus_message_controller.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/consumers/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/decorators.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/interceptors/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/interceptors/publisher_interceptor.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/message.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/messagebus/publisher.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/observability/decorators.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/observability/interceptor.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/observability/providers/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/observability/providers/otel.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/base.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/exports.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/interceptors/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/session.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/sort_filter.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/persistence/utilities.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/decorators.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/hooks.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/http_microservice.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/base_types.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/context.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/decorators.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/types.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/presentation/websocket/websocket_interceptor.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/py.typed +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/reflect/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/reflect/controller_inspect.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/rpc/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/rpc/http/backends/otel.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/rpc/http/httpx.py +0 -0
- {jararaca-0.3.11a16/src/jararaca/rpc/http → jararaca-0.3.12/src/jararaca/scheduler}/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/scheduler/decorators.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/scheduler/types.py +0 -0
- {jararaca-0.3.11a16/src/jararaca/rpc/http/backends → jararaca-0.3.12/src/jararaca/tools/app_config}/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/tools/app_config/decorators.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/tools/app_config/interceptor.py +0 -0
- {jararaca-0.3.11a16/src/jararaca/scheduler → jararaca-0.3.12/src/jararaca/tools/typescript}/__init__.py +0 -0
- {jararaca-0.3.11a16/src/jararaca/tools/app_config → jararaca-0.3.12/src/jararaca/utils}/__init__.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/utils/rabbitmq_utils.py +0 -0
- {jararaca-0.3.11a16 → jararaca-0.3.12}/src/jararaca/utils/retry.py +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: jararaca
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: A simple and fast API framework for Python
|
|
5
|
+
Home-page: https://github.com/LuscasLeo/jararaca
|
|
5
6
|
Author: Lucas S
|
|
6
7
|
Author-email: me@luscasleo.dev
|
|
7
8
|
Requires-Python: >=3.11,<4.0
|
|
8
9
|
Classifier: Programming Language :: Python :: 3
|
|
9
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
12
12
|
Provides-Extra: docs
|
|
13
13
|
Provides-Extra: http
|
|
14
14
|
Provides-Extra: opentelemetry
|
|
@@ -27,6 +27,7 @@ Requires-Dist: redis (>=5.0.8,<6.0.0)
|
|
|
27
27
|
Requires-Dist: sqlalchemy (>=2.0.34,<3.0.0)
|
|
28
28
|
Requires-Dist: types-croniter (>=3.0.3.20240731,<4.0.0.0)
|
|
29
29
|
Requires-Dist: types-redis (>=4.6.0.20240903,<5.0.0.0)
|
|
30
|
+
Requires-Dist: urllib3 (>=2.3.0,<3.0.0)
|
|
30
31
|
Requires-Dist: uvicorn (>=0.30.6,<0.31.0)
|
|
31
32
|
Requires-Dist: uvloop (>=0.20.0,<0.21.0)
|
|
32
33
|
Requires-Dist: watchdog (>=3.0.0,<4.0.0) ; extra == "watch"
|
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
# HTTP RPC Client
|
|
2
|
+
|
|
3
|
+
The Jararaca HTTP RPC client provides a complete REST client implementation with a decorator-based approach for defining HTTP endpoints. It includes advanced features like authentication, caching, retry logic, form data handling, and file uploads.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from jararaca.rpc.http import (
|
|
9
|
+
BearerTokenAuth,
|
|
10
|
+
Body,
|
|
11
|
+
CacheMiddleware,
|
|
12
|
+
Delete,
|
|
13
|
+
File,
|
|
14
|
+
FormData,
|
|
15
|
+
Get,
|
|
16
|
+
HttpRpcClientBuilder,
|
|
17
|
+
HTTPXHttpRPCAsyncBackend,
|
|
18
|
+
Post,
|
|
19
|
+
Put,
|
|
20
|
+
Query,
|
|
21
|
+
RestClient,
|
|
22
|
+
Retry,
|
|
23
|
+
RetryConfig,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@RestClient("https://api.example.com")
|
|
28
|
+
class ApiClient:
|
|
29
|
+
|
|
30
|
+
@Get("/users")
|
|
31
|
+
@Query("limit")
|
|
32
|
+
async def get_users(self, limit: int) -> dict:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@Post("/users")
|
|
36
|
+
@Body("user_data")
|
|
37
|
+
async def create_user(self, user_data: dict) -> dict:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Create client
|
|
41
|
+
backend = HTTPXHttpRPCAsyncBackend()
|
|
42
|
+
auth = BearerTokenAuth("your-token")
|
|
43
|
+
cache = CacheMiddleware(ttl_seconds=300)
|
|
44
|
+
|
|
45
|
+
builder = HttpRpcClientBuilder(
|
|
46
|
+
backend=backend,
|
|
47
|
+
middlewares=[auth, cache]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
client = builder.build(ApiClient)
|
|
51
|
+
|
|
52
|
+
# Use client
|
|
53
|
+
users = await client.get_users(10)
|
|
54
|
+
new_user = await client.create_user({"name": "John", "email": "john@example.com"})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## HTTP Method Decorators
|
|
58
|
+
|
|
59
|
+
### Basic HTTP Methods
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from jararaca.rpc.http import Delete, Get, Patch, Post, Put
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@RestClient("https://api.example.com")
|
|
66
|
+
class ApiClient:
|
|
67
|
+
|
|
68
|
+
@Get("/users")
|
|
69
|
+
async def get_users(self) -> list[dict]:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
@Post("/users")
|
|
73
|
+
async def create_user(self) -> dict:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
@Put("/users/{user_id}")
|
|
77
|
+
async def update_user(self) -> dict:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
@Patch("/users/{user_id}")
|
|
81
|
+
async def patch_user(self) -> dict:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
@Delete("/users/{user_id}")
|
|
85
|
+
async def delete_user(self) -> bool:
|
|
86
|
+
pass
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Request Parameter Decorators
|
|
90
|
+
|
|
91
|
+
### Query Parameters
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from jararaca.rpc.http import Query
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@Get("/users")
|
|
98
|
+
@Query("limit")
|
|
99
|
+
@Query("offset")
|
|
100
|
+
async def get_users(self, limit: int, offset: int = 0) -> list[dict]:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
# Usage: client.get_users(10, 20) -> GET /users?limit=10&offset=20
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Path Parameters
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from jararaca.rpc.http import PathParam
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@Get("/users/{user_id}")
|
|
113
|
+
@PathParam("user_id")
|
|
114
|
+
async def get_user(self, user_id: int) -> dict:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
# Usage: client.get_user(123) -> GET /users/123
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Headers
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from jararaca.rpc.http import Header
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@Get("/users")
|
|
127
|
+
@Header("X-Client-Version")
|
|
128
|
+
async def get_users(self, x_client_version: str = "1.0") -> list[dict]:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
# Usage: client.get_users("2.0") -> adds X-Client-Version: 2.0 header
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Request Body
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from jararaca.rpc.http import Body
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@Post("/users")
|
|
141
|
+
@Body("user_data")
|
|
142
|
+
async def create_user(self, user_data: dict) -> dict:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
# Usage: client.create_user({"name": "John"}) -> sends JSON body
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Form Data
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from jararaca.rpc.http import FormData
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@Post("/login")
|
|
155
|
+
@FormData("username")
|
|
156
|
+
@FormData("password")
|
|
157
|
+
async def login(self, username: str, password: str) -> dict:
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
# Usage: client.login("user", "pass") -> sends form-encoded data
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### File Uploads
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from jararaca.rpc.http import File, FormData
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@Post("/upload")
|
|
170
|
+
@FormData("name")
|
|
171
|
+
@File("avatar")
|
|
172
|
+
async def upload_avatar(self, name: str, avatar: bytes) -> dict:
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
# Usage:
|
|
176
|
+
# with open("avatar.jpg", "rb") as f:
|
|
177
|
+
# result = await client.upload_avatar("John", f.read())
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Configuration Decorators
|
|
181
|
+
|
|
182
|
+
### Timeout
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from jararaca.rpc.http import Timeout
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@Get("/slow-endpoint")
|
|
189
|
+
@Timeout(30.0) # 30 seconds timeout
|
|
190
|
+
async def slow_request(self) -> dict:
|
|
191
|
+
pass
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Retry Configuration
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from jararaca.rpc.http import Retry, RetryConfig
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@Get("/unreliable-endpoint")
|
|
201
|
+
@Retry(RetryConfig(
|
|
202
|
+
max_attempts=3,
|
|
203
|
+
backoff_factor=2.0,
|
|
204
|
+
retry_on_status_codes=[500, 502, 503, 504]
|
|
205
|
+
))
|
|
206
|
+
async def unreliable_request(self) -> dict:
|
|
207
|
+
pass
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Content Type
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from jararaca.rpc.http import ContentType
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@Post("/xml-endpoint")
|
|
217
|
+
@ContentType("application/xml")
|
|
218
|
+
@Body("xml_data")
|
|
219
|
+
async def send_xml(self, xml_data: str) -> dict:
|
|
220
|
+
pass
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Authentication
|
|
224
|
+
|
|
225
|
+
### Bearer Token Authentication
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from jararaca.rpc.http import BearerTokenAuth
|
|
229
|
+
|
|
230
|
+
auth = BearerTokenAuth("your-access-token")
|
|
231
|
+
builder = HttpRpcClientBuilder(backend=backend, middlewares=[auth])
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Basic Authentication
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from jararaca.rpc.http import BasicAuth
|
|
238
|
+
|
|
239
|
+
auth = BasicAuth("username", "password")
|
|
240
|
+
builder = HttpRpcClientBuilder(backend=backend, middlewares=[auth])
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### API Key Authentication
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from jararaca.rpc.http import ApiKeyAuth
|
|
247
|
+
|
|
248
|
+
auth = ApiKeyAuth("your-api-key", header_name="X-API-Key")
|
|
249
|
+
builder = HttpRpcClientBuilder(backend=backend, middlewares=[auth])
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Middleware
|
|
253
|
+
|
|
254
|
+
### Cache Middleware
|
|
255
|
+
|
|
256
|
+
The cache middleware provides in-memory caching for GET requests:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from jararaca.rpc.http import CacheMiddleware
|
|
260
|
+
|
|
261
|
+
cache = CacheMiddleware(ttl_seconds=300) # Cache for 5 minutes
|
|
262
|
+
builder = HttpRpcClientBuilder(backend=backend, middlewares=[cache])
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Custom Request Middleware
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
from jararaca.rpc.http import HttpRPCRequest, RequestMiddleware
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class LoggingMiddleware(RequestMiddleware):
|
|
272
|
+
def on_request(self, request: HttpRPCRequest) -> HttpRPCRequest:
|
|
273
|
+
print(f"Making request to {request.url}")
|
|
274
|
+
return request
|
|
275
|
+
|
|
276
|
+
logging_middleware = LoggingMiddleware()
|
|
277
|
+
builder = HttpRpcClientBuilder(backend=backend, middlewares=[logging_middleware])
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Response Middleware
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
from jararaca.rpc.http import HttpRPCRequest, HttpRPCResponse, ResponseMiddleware
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class ResponseLoggingMiddleware(ResponseMiddleware):
|
|
287
|
+
def on_response(self, request: HttpRPCRequest, response: HttpRPCResponse) -> HttpRPCResponse:
|
|
288
|
+
print(f"Response from {request.url}: {response.status_code}")
|
|
289
|
+
return response
|
|
290
|
+
|
|
291
|
+
response_middleware = ResponseLoggingMiddleware()
|
|
292
|
+
builder = HttpRpcClientBuilder(
|
|
293
|
+
backend=backend,
|
|
294
|
+
response_middlewares=[response_middleware]
|
|
295
|
+
)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Hooks
|
|
299
|
+
|
|
300
|
+
### Request Hooks
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from jararaca.rpc.http import HttpRPCRequest, RequestHook
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class RequestTimingHook(RequestHook):
|
|
307
|
+
def before_request(self, request: HttpRPCRequest) -> HttpRPCRequest:
|
|
308
|
+
request.start_time = time.time()
|
|
309
|
+
return request
|
|
310
|
+
|
|
311
|
+
timing_hook = RequestTimingHook()
|
|
312
|
+
builder = HttpRpcClientBuilder(
|
|
313
|
+
backend=backend,
|
|
314
|
+
request_hooks=[timing_hook]
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Response Hooks
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from jararaca.rpc.http import HttpRPCRequest, HttpRPCResponse, ResponseHook
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class ResponseTimingHook(ResponseHook):
|
|
325
|
+
def after_response(self, request: HttpRPCRequest, response: HttpRPCResponse) -> HttpRPCResponse:
|
|
326
|
+
if hasattr(request, 'start_time'):
|
|
327
|
+
elapsed = time.time() - request.start_time
|
|
328
|
+
print(f"Request took {elapsed:.2f} seconds")
|
|
329
|
+
return response
|
|
330
|
+
|
|
331
|
+
timing_hook = ResponseTimingHook()
|
|
332
|
+
builder = HttpRpcClientBuilder(
|
|
333
|
+
backend=backend,
|
|
334
|
+
response_hooks=[timing_hook]
|
|
335
|
+
)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Error Handling
|
|
339
|
+
|
|
340
|
+
### Global Error Handlers
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
from jararaca.rpc.http import GlobalHttpErrorHandler
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@GlobalHttpErrorHandler(404)
|
|
347
|
+
def handle_not_found(request, response):
|
|
348
|
+
return {"error": "Resource not found"}
|
|
349
|
+
|
|
350
|
+
@GlobalHttpErrorHandler(500)
|
|
351
|
+
def handle_server_error(request, response):
|
|
352
|
+
return {"error": "Server error occurred"}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Route-Specific Error Handlers
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
from jararaca.rpc.http import RouteHttpErrorHandler
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
@Get("/users/{user_id}")
|
|
362
|
+
@PathParam("user_id")
|
|
363
|
+
@RouteHttpErrorHandler(404)
|
|
364
|
+
def handle_user_not_found(request, response):
|
|
365
|
+
return {"error": f"User not found"}
|
|
366
|
+
async def get_user(self, user_id: int) -> dict:
|
|
367
|
+
pass
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Advanced Features
|
|
371
|
+
|
|
372
|
+
### Complete Example with All Features
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
import asyncio
|
|
376
|
+
|
|
377
|
+
from jararaca.rpc.http import (
|
|
378
|
+
ApiKeyAuth,
|
|
379
|
+
BasicAuth,
|
|
380
|
+
BearerTokenAuth,
|
|
381
|
+
Body,
|
|
382
|
+
CacheMiddleware,
|
|
383
|
+
ContentType,
|
|
384
|
+
Delete,
|
|
385
|
+
File,
|
|
386
|
+
FormData,
|
|
387
|
+
Get,
|
|
388
|
+
GlobalHttpErrorHandler,
|
|
389
|
+
Header,
|
|
390
|
+
HttpRpcClientBuilder,
|
|
391
|
+
HTTPXHttpRPCAsyncBackend,
|
|
392
|
+
PathParam,
|
|
393
|
+
Post,
|
|
394
|
+
Put,
|
|
395
|
+
Query,
|
|
396
|
+
RequestHook,
|
|
397
|
+
ResponseHook,
|
|
398
|
+
ResponseMiddleware,
|
|
399
|
+
RestClient,
|
|
400
|
+
Retry,
|
|
401
|
+
RetryConfig,
|
|
402
|
+
RouteHttpErrorHandler,
|
|
403
|
+
Timeout,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# Custom middleware
|
|
408
|
+
class RequestIdMiddleware(RequestMiddleware):
|
|
409
|
+
def on_request(self, request: HttpRPCRequest) -> HttpRPCRequest:
|
|
410
|
+
import uuid
|
|
411
|
+
request.headers.append(("X-Request-ID", str(uuid.uuid4())))
|
|
412
|
+
return request
|
|
413
|
+
|
|
414
|
+
# Error handlers
|
|
415
|
+
@GlobalHttpErrorHandler(500)
|
|
416
|
+
def handle_server_error(request, response):
|
|
417
|
+
return {"error": "Server error", "status": 500}
|
|
418
|
+
|
|
419
|
+
@RestClient("https://api.example.com/v1")
|
|
420
|
+
class AdvancedApiClient:
|
|
421
|
+
|
|
422
|
+
@Get("/users")
|
|
423
|
+
@Query("limit")
|
|
424
|
+
@Query("search")
|
|
425
|
+
@Header("X-Client-Version")
|
|
426
|
+
@Timeout(10.0)
|
|
427
|
+
@CacheMiddleware(ttl_seconds=60)
|
|
428
|
+
async def search_users(
|
|
429
|
+
self,
|
|
430
|
+
limit: int = 10,
|
|
431
|
+
search: str = "",
|
|
432
|
+
x_client_version: str = "1.0"
|
|
433
|
+
) -> list[dict]:
|
|
434
|
+
pass
|
|
435
|
+
|
|
436
|
+
@Post("/users")
|
|
437
|
+
@Body("user_data")
|
|
438
|
+
@ContentType("application/json")
|
|
439
|
+
@Retry(RetryConfig(max_attempts=3, backoff_factor=1.5))
|
|
440
|
+
@RouteHttpErrorHandler(400)
|
|
441
|
+
def handle_validation_error(request, response):
|
|
442
|
+
return {"error": "Validation failed", "details": response.data}
|
|
443
|
+
async def create_user(self, user_data: dict) -> dict:
|
|
444
|
+
pass
|
|
445
|
+
|
|
446
|
+
@Put("/users/{user_id}/avatar")
|
|
447
|
+
@PathParam("user_id")
|
|
448
|
+
@File("avatar")
|
|
449
|
+
@FormData("description")
|
|
450
|
+
@Timeout(30.0)
|
|
451
|
+
async def upload_user_avatar(
|
|
452
|
+
self,
|
|
453
|
+
user_id: int,
|
|
454
|
+
avatar: bytes,
|
|
455
|
+
description: str = ""
|
|
456
|
+
) -> dict:
|
|
457
|
+
pass
|
|
458
|
+
|
|
459
|
+
@Delete("/users/{user_id}")
|
|
460
|
+
@PathParam("user_id")
|
|
461
|
+
@Retry(RetryConfig(max_attempts=2))
|
|
462
|
+
async def delete_user(self, user_id: int) -> bool:
|
|
463
|
+
pass
|
|
464
|
+
|
|
465
|
+
async def main():
|
|
466
|
+
# Setup backend and middleware
|
|
467
|
+
backend = HTTPXHttpRPCAsyncBackend(default_timeout=15.0)
|
|
468
|
+
auth = BearerTokenAuth("your-access-token")
|
|
469
|
+
cache = CacheMiddleware(ttl_seconds=300)
|
|
470
|
+
request_id = RequestIdMiddleware()
|
|
471
|
+
|
|
472
|
+
# Build client with all features
|
|
473
|
+
builder = HttpRpcClientBuilder(
|
|
474
|
+
backend=backend,
|
|
475
|
+
middlewares=[auth, cache, request_id],
|
|
476
|
+
response_middlewares=[],
|
|
477
|
+
request_hooks=[],
|
|
478
|
+
response_hooks=[]
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
client = builder.build(AdvancedApiClient)
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
# Use the client
|
|
485
|
+
users = await client.search_users(limit=20, search="john")
|
|
486
|
+
new_user = await client.create_user({
|
|
487
|
+
"name": "Jane Doe",
|
|
488
|
+
"email": "jane@example.com"
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
# Upload avatar
|
|
492
|
+
with open("avatar.jpg", "rb") as f:
|
|
493
|
+
avatar_result = await client.upload_user_avatar(
|
|
494
|
+
user_id=new_user["id"],
|
|
495
|
+
avatar=f.read(),
|
|
496
|
+
description="Profile picture"
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
print("✅ All operations completed successfully")
|
|
500
|
+
|
|
501
|
+
except Exception as e:
|
|
502
|
+
print(f"❌ Error: {e}")
|
|
503
|
+
|
|
504
|
+
if __name__ == "__main__":
|
|
505
|
+
asyncio.run(main())
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## Backend Configuration
|
|
509
|
+
|
|
510
|
+
### HTTPX Backend Options
|
|
511
|
+
|
|
512
|
+
```python
|
|
513
|
+
from jararaca.rpc.http import HTTPXHttpRPCAsyncBackend
|
|
514
|
+
|
|
515
|
+
backend = HTTPXHttpRPCAsyncBackend(
|
|
516
|
+
prefix_url="https://api.example.com", # Base URL for all requests
|
|
517
|
+
default_timeout=30.0 # Default timeout in seconds
|
|
518
|
+
)
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Exception Handling
|
|
522
|
+
|
|
523
|
+
The HTTP RPC client provides several exception types:
|
|
524
|
+
|
|
525
|
+
- `TimeoutException`: Raised when a request times out
|
|
526
|
+
- `RPCRequestNetworkError`: Raised for network-related errors
|
|
527
|
+
- `RPCUnhandleError`: Raised when no error handler matches the response status
|
|
528
|
+
|
|
529
|
+
```python
|
|
530
|
+
from jararaca.rpc.http import RPCRequestNetworkError, RPCUnhandleError, TimeoutException
|
|
531
|
+
|
|
532
|
+
try:
|
|
533
|
+
result = await client.get_users()
|
|
534
|
+
except TimeoutException:
|
|
535
|
+
print("Request timed out")
|
|
536
|
+
except RPCRequestNetworkError:
|
|
537
|
+
print("Network error occurred")
|
|
538
|
+
except RPCUnhandleError as e:
|
|
539
|
+
print(f"Unhandled error: {e.response.status_code}")
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## Best Practices
|
|
543
|
+
|
|
544
|
+
1. **Use Type Hints**: Always provide type hints for better IDE support and documentation
|
|
545
|
+
2. **Error Handling**: Implement appropriate error handlers for expected error conditions
|
|
546
|
+
3. **Timeouts**: Set reasonable timeouts for all requests
|
|
547
|
+
4. **Retry Logic**: Use retry configuration for operations that may fail temporarily
|
|
548
|
+
5. **Caching**: Use cache middleware for read-heavy operations
|
|
549
|
+
6. **Authentication**: Store tokens securely and refresh them as needed
|
|
550
|
+
7. **Middleware Order**: Consider the order of middleware execution
|
|
551
|
+
8. **Resource Management**: Use async context managers when appropriate
|
|
552
|
+
|
|
553
|
+
## Migration from Previous Versions
|
|
554
|
+
|
|
555
|
+
If you're upgrading from a previous version of the HTTP RPC client, here are the key changes:
|
|
556
|
+
|
|
557
|
+
1. **New Decorators**: `@FormData`, `@File`, `@Timeout`, `@Retry`, `@ContentType`
|
|
558
|
+
2. **Authentication**: New authentication middleware classes
|
|
559
|
+
3. **Caching**: Built-in cache middleware
|
|
560
|
+
4. **Enhanced Error Handling**: More granular exception types
|
|
561
|
+
5. **Middleware System**: Expanded middleware and hooks system
|
|
562
|
+
6. **Form Data Support**: Native support for form submissions and file uploads
|
|
563
|
+
|
|
564
|
+
All existing functionality remains backward compatible.
|