1
1
# pylint: disable=W0707
2
2
import inspect
3
3
import json
4
- from functools import partial
5
4
from typing import Any , Mapping , Type , cast
6
5
7
6
import httpx
20
19
21
20
22
21
class Resource :
22
+ """
23
+ A python class used to define a RESTful resource.
24
+
25
+ Usage:
26
+
27
+ ```python
28
+ >>> from arrest import Resource
29
+
30
+ >>> user_resource = Resource(name="user", route="/users", handlers=[("GET", "/")])
31
+ ```
32
+
33
+ **Parameters:**
34
+
35
+ * **name** - *(optional)* A unique name for the resource. If not present, will be picked
36
+ from `route`
37
+ * **route** - A unique route for the resource. Must begin with a slash.
38
+ * **headers** - *(optional)* A dictionary of header values to be used for all handlers
39
+ under this resource. Defaults to `{"Content-Type": "application/json}`.
40
+ * **timeout** - *(optional)* Timeout (in seconds) for all HTTP requests from this resource.
41
+ Defaults to 60.
42
+ * **response_model** - *(optional)* Pydantic response model to parse all responses under
43
+ the resource.
44
+ * **handlers** - *(optional)* - a list of handlers for the resource.
45
+
46
+ Handlers can be provided as a list of tuple, dictionary or instances of `ResourceHandler`
47
+
48
+ If provided as a dict or `ResourceHandler`, the keys / fields have to be set according to
49
+ the `ResourceHandler` definiion.
50
+
51
+ If provided as a tuple, at minimum 2 entries `(method, route)` or a maximum of 5 entries
52
+ (method, route, request, response, callback) can be defined.
53
+ """
54
+
23
55
def __init__ (
24
56
self ,
25
57
name : str | None = None ,
@@ -30,7 +62,7 @@ def __init__(
30
62
response_model : Type [BaseModel ] | None = None ,
31
63
handlers : list [ResourceHandler ]
32
64
| list [Mapping [str , Any ]]
33
- | list [tuple [Any , ...]] = [] ,
65
+ | list [tuple [Any , ...]] = None ,
34
66
) -> None :
35
67
self .base_url = "/" # will be filled once bound to a service
36
68
self .route = route
@@ -48,12 +80,6 @@ def __init__(
48
80
49
81
self .routes : dict [HandlerKey , ResourceHandler ] = {}
50
82
51
- self .get = partial (self .request , method = Methods .GET )
52
- self .post = partial (self .request , method = Methods .POST )
53
- self .put = partial (self .request , method = Methods .PUT )
54
- self .patch = partial (self .request , method = Methods .PATCH )
55
- self .delete = partial (self .request , method = Methods .DELETE )
56
-
57
83
self .initialize_handlers (handlers = handlers )
58
84
59
85
def initialize_handlers (
@@ -136,14 +162,21 @@ def _bind_handler(
136
162
137
163
async def request (
138
164
self ,
139
- url : str ,
140
165
method : Methods ,
166
+ url : str ,
141
167
request : BaseModel | None = None ,
142
168
** kwargs ,
143
169
) -> Any | None :
144
170
"""
145
- Parameters
146
- ----------
171
+ Makes an HTTP request against a handler route
172
+
173
+ Usage:
174
+
175
+ ```python
176
+ >>> user_resource.user.request(method="GET")
177
+ ```
178
+
179
+ **Parameters:**
147
180
148
181
request : BaseModel
149
182
pydantic object containing the necessary fields to make an http
@@ -187,6 +220,111 @@ async def request(
187
220
188
221
return response
189
222
223
+ async def get (
224
+ self ,
225
+ url : str ,
226
+ request : BaseModel | None = None ,
227
+ ** kwargs ,
228
+ ):
229
+ """
230
+ Makes a `HTTP GET` request
231
+
232
+ see `request()`
233
+ """
234
+ return await self .request (
235
+ method = Methods .GET , url = url , request = request , ** kwargs
236
+ )
237
+
238
+ async def post (
239
+ self ,
240
+ url : str ,
241
+ request : BaseModel | None = None ,
242
+ ** kwargs ,
243
+ ):
244
+ """
245
+ Makes a `HTTP POST` request
246
+
247
+ see `request()`
248
+ """
249
+ return await self .request (
250
+ method = Methods .POST , url = url , request = request , ** kwargs
251
+ )
252
+
253
+ async def put (
254
+ self ,
255
+ url : str ,
256
+ request : BaseModel | None = None ,
257
+ ** kwargs ,
258
+ ):
259
+ """
260
+ Makes a `HTTP PUT` request
261
+
262
+ see `request()`
263
+ """
264
+ return await self .request (
265
+ method = Methods .PUT , url = url , request = request , ** kwargs
266
+ )
267
+
268
+ async def patch (
269
+ self ,
270
+ url : str ,
271
+ request : BaseModel | None = None ,
272
+ ** kwargs ,
273
+ ):
274
+ """
275
+ Makes a `HTTP PATCH` request
276
+
277
+ see `request()`
278
+ """
279
+ return await self .request (
280
+ method = Methods .PATCH , url = url , request = request , ** kwargs
281
+ )
282
+
283
+ async def delete (
284
+ self ,
285
+ url : str ,
286
+ request : BaseModel | None = None ,
287
+ ** kwargs ,
288
+ ):
289
+ """
290
+ Makes a `HTTP GET` request
291
+
292
+ see `request()`
293
+ """
294
+ return await self .request (
295
+ method = Methods .DELETE , url = url , request = request , ** kwargs
296
+ )
297
+
298
+ async def head (
299
+ self ,
300
+ url : str ,
301
+ request : BaseModel | None = None ,
302
+ ** kwargs ,
303
+ ):
304
+ """
305
+ Makes a `HTTP HEAD` request
306
+
307
+ see `request()`
308
+ """
309
+ return await self .request (
310
+ method = Methods .HEAD , url = url , request = request , ** kwargs
311
+ )
312
+
313
+ async def options (
314
+ self ,
315
+ url : str ,
316
+ request : BaseModel | None = None ,
317
+ ** kwargs ,
318
+ ):
319
+ """
320
+ Makes a `HTTP OPTIONS` request
321
+
322
+ see `request()`
323
+ """
324
+ return await self .request (
325
+ method = Methods .OPTIONS , url = url , request = request , ** kwargs
326
+ )
327
+
190
328
def extract_request_params (
191
329
self ,
192
330
request_type : Type [BaseModel ] | None ,
@@ -311,6 +449,9 @@ async def __make_request(
311
449
case Methods .HEAD :
312
450
response = await client .head (url = url , params = query_params )
313
451
452
+ case Methods .OPTIONS :
453
+ response = await client .options (url = url , params = query_params )
454
+
314
455
status_code = response .status_code
315
456
logger .debug (
316
457
f"{ method !s} { url } returned with status code { status_code !s} "
0 commit comments