jararaca 0.3.12a2__tar.gz → 0.3.12a4__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.

Files changed (89) hide show
  1. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/PKG-INFO +1 -1
  2. jararaca-0.3.12a4/docs/http-rpc.md +564 -0
  3. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/index.md +1 -0
  4. jararaca-0.3.12a4/docs/interceptors.md +210 -0
  5. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/pyproject.toml +1 -1
  6. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/__init__.py +62 -4
  7. jararaca-0.3.12a4/src/jararaca/rpc/http/__init__.py +97 -0
  8. jararaca-0.3.12a4/src/jararaca/rpc/http/backends/__init__.py +10 -0
  9. jararaca-0.3.12a4/src/jararaca/rpc/http/backends/httpx.py +71 -0
  10. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/rpc/http/decorators.py +302 -6
  11. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/scheduler/beat_worker.py +14 -6
  12. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/tools/typescript/interface_parser.py +1 -1
  13. jararaca-0.3.12a2/src/jararaca/rpc/http/backends/httpx.py +0 -41
  14. jararaca-0.3.12a2/src/jararaca/tools/app_config/__init__.py +0 -0
  15. jararaca-0.3.12a2/src/jararaca/utils/__init__.py +0 -0
  16. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/LICENSE +0 -0
  17. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/README.md +0 -0
  18. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/CNAME +0 -0
  19. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/architecture.md +0 -0
  20. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.jpeg +0 -0
  21. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.webp +0 -0
  22. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/assets/tracing_example.png +0 -0
  23. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/messagebus.md +0 -0
  24. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/retry.md +0 -0
  25. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/scheduler.md +0 -0
  26. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/stylesheets/custom.css +0 -0
  27. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/docs/websocket.md +0 -0
  28. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/__main__.py +0 -0
  29. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/broker_backend/__init__.py +0 -0
  30. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/broker_backend/mapper.py +0 -0
  31. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/broker_backend/redis_broker_backend.py +0 -0
  32. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/cli.py +0 -0
  33. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/common/__init__.py +0 -0
  34. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/core/__init__.py +0 -0
  35. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/core/providers.py +0 -0
  36. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/core/uow.py +0 -0
  37. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/di.py +0 -0
  38. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/files/entity.py.mako +0 -0
  39. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/lifecycle.py +0 -0
  40. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/__init__.py +0 -0
  41. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/bus_message_controller.py +0 -0
  42. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/consumers/__init__.py +0 -0
  43. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/decorators.py +0 -0
  44. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/interceptors/__init__.py +0 -0
  45. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py +0 -0
  46. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/interceptors/publisher_interceptor.py +0 -0
  47. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/message.py +0 -0
  48. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/publisher.py +0 -0
  49. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/messagebus/worker.py +0 -0
  50. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/microservice.py +0 -0
  51. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/observability/decorators.py +0 -0
  52. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/observability/interceptor.py +0 -0
  53. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/observability/providers/__init__.py +0 -0
  54. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/observability/providers/otel.py +0 -0
  55. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/base.py +0 -0
  56. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/exports.py +0 -0
  57. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/interceptors/__init__.py +0 -0
  58. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/interceptors/aiosqa_interceptor.py +0 -0
  59. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/session.py +0 -0
  60. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/sort_filter.py +0 -0
  61. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/persistence/utilities.py +0 -0
  62. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/__init__.py +0 -0
  63. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/decorators.py +0 -0
  64. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/hooks.py +0 -0
  65. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/http_microservice.py +0 -0
  66. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/server.py +0 -0
  67. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/__init__.py +0 -0
  68. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/base_types.py +0 -0
  69. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/context.py +0 -0
  70. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/decorators.py +0 -0
  71. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/redis.py +0 -0
  72. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/types.py +0 -0
  73. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/presentation/websocket/websocket_interceptor.py +0 -0
  74. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/py.typed +0 -0
  75. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/reflect/__init__.py +0 -0
  76. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/reflect/controller_inspect.py +0 -0
  77. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/reflect/metadata.py +0 -0
  78. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/rpc/__init__.py +0 -0
  79. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/rpc/http/backends/otel.py +0 -0
  80. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/rpc/http/httpx.py +0 -0
  81. {jararaca-0.3.12a2/src/jararaca/rpc/http → jararaca-0.3.12a4/src/jararaca/scheduler}/__init__.py +0 -0
  82. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/scheduler/decorators.py +0 -0
  83. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/scheduler/types.py +0 -0
  84. {jararaca-0.3.12a2/src/jararaca/rpc/http/backends → jararaca-0.3.12a4/src/jararaca/tools/app_config}/__init__.py +0 -0
  85. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/tools/app_config/decorators.py +0 -0
  86. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/tools/app_config/interceptor.py +0 -0
  87. {jararaca-0.3.12a2/src/jararaca/scheduler → jararaca-0.3.12a4/src/jararaca/utils}/__init__.py +0 -0
  88. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/utils/rabbitmq_utils.py +0 -0
  89. {jararaca-0.3.12a2 → jararaca-0.3.12a4}/src/jararaca/utils/retry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jararaca
3
- Version: 0.3.12a2
3
+ Version: 0.3.12a4
4
4
  Summary: A simple and fast API framework for Python
5
5
  Home-page: https://github.com/LuscasLeo/jararaca
6
6
  Author: Lucas S
@@ -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.
@@ -5,6 +5,7 @@ Jararaca is a powerful Python microservice framework that provides a comprehensi
5
5
  ## Features
6
6
 
7
7
  - 🚀 **FastAPI Integration**: Built-in support for FastAPI with automatic OpenAPI documentation
8
+ - 🌐 **HTTP RPC Client**: Complete REST client implementation with decorators, authentication, caching, and retry logic
8
9
  - 🔌 **WebSocket Support**: Real-time communication capabilities with Redis-backed WebSocket management
9
10
  - 📦 **Dependency Injection**: Flexible dependency injection system with interceptors
10
11
  - 📊 **Database Integration**: SQLAlchemy integration with async support