apexdevkit 1.5.24__tar.gz → 1.6.1__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.
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/PKG-INFO +1 -1
- apexdevkit-1.6.1/apexdevkit/fastapi/dependable.py +100 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/router.py +33 -103
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/http/httpx.py +13 -10
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/testing/rest.py +87 -53
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/pyproject.toml +2 -2
- apexdevkit-1.5.24/apexdevkit/fastapi/dependable.py +0 -11
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/LICENSE +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/README.md +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/__init__.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/annotation/__init__.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/annotation/deprecate.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/error.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/__init__.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/builder.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/docs.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/resource.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/response.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/schema.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/fastapi/service.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/formatter.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/http/__init__.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/http/fake.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/http/fluent.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/http/json.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/http/url.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/py.typed +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/repository/__init__.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/repository/base.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/repository/connector.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/repository/database.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/repository/in_memory.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/repository/interface.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/testing/__init__.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/testing/database.py +0 -0
- {apexdevkit-1.5.24 → apexdevkit-1.6.1}/apexdevkit/testing/fake.py +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Annotated, Any, Callable, Protocol, Self
|
|
3
|
+
|
|
4
|
+
from fastapi import Depends, Path
|
|
5
|
+
from fastapi.requests import Request
|
|
6
|
+
|
|
7
|
+
from apexdevkit.fastapi import RestfulServiceBuilder
|
|
8
|
+
from apexdevkit.fastapi.service import RestfulService
|
|
9
|
+
from apexdevkit.testing import RestfulName
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def inject(dependency: str) -> Any: # pragma: no cover
|
|
13
|
+
def get(request: Request) -> Any:
|
|
14
|
+
return getattr(request.app.state, dependency)
|
|
15
|
+
|
|
16
|
+
return Depends(get)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _Dependency(Protocol):
|
|
20
|
+
def as_dependable(self) -> type[RestfulServiceBuilder]:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ServiceDependency:
|
|
26
|
+
dependency: _Dependency
|
|
27
|
+
|
|
28
|
+
def as_dependable(self) -> type[RestfulService]:
|
|
29
|
+
Builder = self.dependency.as_dependable()
|
|
30
|
+
|
|
31
|
+
def _(builder: Builder) -> RestfulService:
|
|
32
|
+
return builder.build()
|
|
33
|
+
|
|
34
|
+
return Annotated[RestfulService, Depends(_)]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class ParentDependency:
|
|
39
|
+
parent: RestfulName
|
|
40
|
+
dependency: _Dependency
|
|
41
|
+
|
|
42
|
+
def as_dependable(self) -> type[RestfulServiceBuilder]:
|
|
43
|
+
Builder = self.dependency.as_dependable()
|
|
44
|
+
ParentId = Annotated[str, Path(alias=self.parent.singular + "_id")]
|
|
45
|
+
|
|
46
|
+
def _(builder: Builder, parent_id: ParentId) -> RestfulServiceBuilder:
|
|
47
|
+
return builder.with_parent(parent_id)
|
|
48
|
+
|
|
49
|
+
return Annotated[RestfulServiceBuilder, Depends(_)]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class UserDependency:
|
|
54
|
+
extract_user: Callable[..., Any]
|
|
55
|
+
dependency: _Dependency
|
|
56
|
+
|
|
57
|
+
def as_dependable(self) -> type[RestfulServiceBuilder]:
|
|
58
|
+
Builder = self.dependency.as_dependable()
|
|
59
|
+
User = Annotated[Any, Depends(self.extract_user)]
|
|
60
|
+
|
|
61
|
+
def _(builder: Builder, user: User) -> RestfulServiceBuilder:
|
|
62
|
+
return builder.with_user(user)
|
|
63
|
+
|
|
64
|
+
return Annotated[RestfulServiceBuilder, Depends(_)]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class InfraDependency:
|
|
69
|
+
infra: RestfulServiceBuilder
|
|
70
|
+
|
|
71
|
+
def as_dependable(self) -> type[RestfulServiceBuilder]:
|
|
72
|
+
def _() -> RestfulServiceBuilder:
|
|
73
|
+
return self.infra
|
|
74
|
+
|
|
75
|
+
return Annotated[RestfulServiceBuilder, Depends(_)]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class DependableBuilder:
|
|
80
|
+
dependency: _Dependency = field(init=False)
|
|
81
|
+
|
|
82
|
+
def from_infra(self, value: RestfulServiceBuilder) -> Self:
|
|
83
|
+
self.dependency = InfraDependency(value)
|
|
84
|
+
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
def with_parent(self, value: RestfulName) -> Self:
|
|
88
|
+
dependable = DependableBuilder()
|
|
89
|
+
dependable.dependency = ParentDependency(value, self.dependency)
|
|
90
|
+
|
|
91
|
+
return dependable
|
|
92
|
+
|
|
93
|
+
def with_user(self, extract_user: Callable[..., Any]) -> Self:
|
|
94
|
+
dependable = DependableBuilder()
|
|
95
|
+
dependable.dependency = UserDependency(extract_user, self.dependency)
|
|
96
|
+
|
|
97
|
+
return dependable
|
|
98
|
+
|
|
99
|
+
def as_dependable(self) -> type[RestfulService]:
|
|
100
|
+
return ServiceDependency(self.dependency).as_dependable()
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from functools import cached_property
|
|
3
|
-
from typing import Annotated, Any,
|
|
3
|
+
from typing import Annotated, Any, Protocol, Self, TypeVar
|
|
4
4
|
|
|
5
|
-
from fastapi import APIRouter, Depends,
|
|
5
|
+
from fastapi import APIRouter, Depends, Path
|
|
6
6
|
from fastapi.responses import JSONResponse
|
|
7
7
|
|
|
8
|
-
from apexdevkit.error import DoesNotExistError
|
|
9
8
|
from apexdevkit.fastapi.builder import RestfulServiceBuilder
|
|
10
9
|
from apexdevkit.fastapi.resource import RestfulResource
|
|
11
|
-
from apexdevkit.fastapi.response import RestfulResponse
|
|
12
10
|
from apexdevkit.fastapi.schema import RestfulSchema, SchemaFields
|
|
13
11
|
from apexdevkit.fastapi.service import RawCollection, RawItem, RestfulService
|
|
14
12
|
from apexdevkit.testing import RestfulName
|
|
@@ -18,6 +16,11 @@ _Response = JSONResponse | dict[str, Any]
|
|
|
18
16
|
T = TypeVar("T")
|
|
19
17
|
|
|
20
18
|
|
|
19
|
+
class Dependable(Protocol):
|
|
20
|
+
def as_dependable(self) -> type[RestfulService]:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
21
24
|
@dataclass
|
|
22
25
|
class PreBuiltRestfulService(RestfulServiceBuilder): # pragma: no cover
|
|
23
26
|
service: RestfulService
|
|
@@ -26,27 +29,13 @@ class PreBuiltRestfulService(RestfulServiceBuilder): # pragma: no cover
|
|
|
26
29
|
return self.service
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
def no_user() -> None:
|
|
30
|
-
pass
|
|
31
|
-
|
|
32
|
-
|
|
33
32
|
@dataclass
|
|
34
33
|
class RestfulRouter:
|
|
35
|
-
service: RestfulService | None = None
|
|
36
|
-
|
|
37
34
|
router: APIRouter = field(default_factory=APIRouter)
|
|
38
35
|
|
|
39
36
|
name: RestfulName = field(init=False)
|
|
40
37
|
fields: SchemaFields = field(init=False)
|
|
41
38
|
|
|
42
|
-
parent: str = field(init=False, default="")
|
|
43
|
-
|
|
44
|
-
infra: RestfulServiceBuilder = field(init=False)
|
|
45
|
-
|
|
46
|
-
def __post_init__(self) -> None: # pragma: no cover
|
|
47
|
-
if self.service:
|
|
48
|
-
self.with_infra(PreBuiltRestfulService(self.service))
|
|
49
|
-
|
|
50
39
|
@cached_property
|
|
51
40
|
def schema(self) -> RestfulSchema:
|
|
52
41
|
return RestfulSchema(name=self.name, fields=self.fields)
|
|
@@ -59,10 +48,6 @@ class RestfulRouter:
|
|
|
59
48
|
def id_alias(self) -> str:
|
|
60
49
|
return self.name.singular + "_id"
|
|
61
50
|
|
|
62
|
-
@property
|
|
63
|
-
def parent_id_alias(self) -> str:
|
|
64
|
-
return self.parent + "_id"
|
|
65
|
-
|
|
66
51
|
@property
|
|
67
52
|
def item_path(self) -> str:
|
|
68
53
|
return "/{" + self.id_alias + "}"
|
|
@@ -77,25 +62,15 @@ class RestfulRouter:
|
|
|
77
62
|
|
|
78
63
|
return self
|
|
79
64
|
|
|
80
|
-
def with_parent(self, name: str) -> Self:
|
|
81
|
-
self.parent = name
|
|
82
|
-
|
|
83
|
-
return self
|
|
84
|
-
|
|
85
|
-
def with_infra(self, value: RestfulServiceBuilder) -> Self:
|
|
86
|
-
self.infra = value
|
|
87
|
-
|
|
88
|
-
return self
|
|
89
|
-
|
|
90
65
|
def with_create_one_endpoint(
|
|
91
66
|
self,
|
|
67
|
+
dependable: Dependable,
|
|
92
68
|
is_documented: bool = True,
|
|
93
|
-
extract_user: Callable[..., Any] = no_user,
|
|
94
69
|
) -> Self:
|
|
95
70
|
self.router.add_api_route(
|
|
96
71
|
"",
|
|
97
72
|
self.resource.create_one(
|
|
98
|
-
Service=
|
|
73
|
+
Service=dependable.as_dependable(),
|
|
99
74
|
Item=Annotated[
|
|
100
75
|
RawItem,
|
|
101
76
|
Depends(self.schema.for_create_one()),
|
|
@@ -112,14 +87,12 @@ class RestfulRouter:
|
|
|
112
87
|
return self
|
|
113
88
|
|
|
114
89
|
def with_create_many_endpoint(
|
|
115
|
-
self,
|
|
116
|
-
is_documented: bool = True,
|
|
117
|
-
extract_user: Callable[..., Any] = no_user,
|
|
90
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
118
91
|
) -> Self:
|
|
119
92
|
self.router.add_api_route(
|
|
120
93
|
"/batch",
|
|
121
94
|
self.resource.create_many(
|
|
122
|
-
Service=
|
|
95
|
+
Service=dependable.as_dependable(),
|
|
123
96
|
Collection=Annotated[
|
|
124
97
|
RawCollection,
|
|
125
98
|
Depends(self.schema.for_create_many()),
|
|
@@ -136,14 +109,12 @@ class RestfulRouter:
|
|
|
136
109
|
return self
|
|
137
110
|
|
|
138
111
|
def with_read_one_endpoint(
|
|
139
|
-
self,
|
|
140
|
-
is_documented: bool = True,
|
|
141
|
-
extract_user: Callable[..., Any] = no_user,
|
|
112
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
142
113
|
) -> Self:
|
|
143
114
|
self.router.add_api_route(
|
|
144
115
|
self.item_path,
|
|
145
116
|
self.resource.read_one(
|
|
146
|
-
Service=
|
|
117
|
+
Service=dependable.as_dependable(),
|
|
147
118
|
ItemId=Annotated[
|
|
148
119
|
str,
|
|
149
120
|
Path(alias=self.id_alias),
|
|
@@ -160,14 +131,12 @@ class RestfulRouter:
|
|
|
160
131
|
return self
|
|
161
132
|
|
|
162
133
|
def with_read_all_endpoint(
|
|
163
|
-
self,
|
|
164
|
-
is_documented: bool = True,
|
|
165
|
-
extract_user: Callable[..., Any] = no_user,
|
|
134
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
166
135
|
) -> Self:
|
|
167
136
|
self.router.add_api_route(
|
|
168
137
|
"",
|
|
169
138
|
self.resource.read_all(
|
|
170
|
-
Service=
|
|
139
|
+
Service=dependable.as_dependable(),
|
|
171
140
|
),
|
|
172
141
|
methods=["GET"],
|
|
173
142
|
status_code=200,
|
|
@@ -180,14 +149,12 @@ class RestfulRouter:
|
|
|
180
149
|
return self
|
|
181
150
|
|
|
182
151
|
def with_update_one_endpoint(
|
|
183
|
-
self,
|
|
184
|
-
is_documented: bool = True,
|
|
185
|
-
extract_user: Callable[..., Any] = no_user,
|
|
152
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
186
153
|
) -> Self:
|
|
187
154
|
self.router.add_api_route(
|
|
188
155
|
self.item_path,
|
|
189
156
|
self.resource.update_one(
|
|
190
|
-
Service=
|
|
157
|
+
Service=dependable.as_dependable(),
|
|
191
158
|
ItemId=Annotated[
|
|
192
159
|
str,
|
|
193
160
|
Path(alias=self.id_alias),
|
|
@@ -208,14 +175,12 @@ class RestfulRouter:
|
|
|
208
175
|
return self
|
|
209
176
|
|
|
210
177
|
def with_update_many_endpoint(
|
|
211
|
-
self,
|
|
212
|
-
is_documented: bool = True,
|
|
213
|
-
extract_user: Callable[..., Any] = no_user,
|
|
178
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
214
179
|
) -> Self:
|
|
215
180
|
self.router.add_api_route(
|
|
216
181
|
"",
|
|
217
182
|
self.resource.update_many(
|
|
218
|
-
Service=
|
|
183
|
+
Service=dependable.as_dependable(),
|
|
219
184
|
Collection=Annotated[
|
|
220
185
|
RawCollection,
|
|
221
186
|
Depends(self.schema.for_update_many()),
|
|
@@ -232,14 +197,12 @@ class RestfulRouter:
|
|
|
232
197
|
return self
|
|
233
198
|
|
|
234
199
|
def with_replace_one_endpoint(
|
|
235
|
-
self,
|
|
236
|
-
is_documented: bool = True,
|
|
237
|
-
extract_user: Callable[..., Any] = no_user,
|
|
200
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
238
201
|
) -> Self:
|
|
239
202
|
self.router.add_api_route(
|
|
240
203
|
"",
|
|
241
204
|
self.resource.replace_one(
|
|
242
|
-
Service=
|
|
205
|
+
Service=dependable.as_dependable(),
|
|
243
206
|
Item=Annotated[
|
|
244
207
|
RawItem,
|
|
245
208
|
Depends(self.schema.for_replace_one()),
|
|
@@ -256,14 +219,12 @@ class RestfulRouter:
|
|
|
256
219
|
return self
|
|
257
220
|
|
|
258
221
|
def with_replace_many_endpoint(
|
|
259
|
-
self,
|
|
260
|
-
is_documented: bool = True,
|
|
261
|
-
extract_user: Callable[..., Any] = no_user,
|
|
222
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
262
223
|
) -> Self:
|
|
263
224
|
self.router.add_api_route(
|
|
264
225
|
"/batch",
|
|
265
226
|
self.resource.replace_many(
|
|
266
|
-
Service=
|
|
227
|
+
Service=dependable.as_dependable(),
|
|
267
228
|
Collection=Annotated[
|
|
268
229
|
RawCollection,
|
|
269
230
|
Depends(self.schema.for_replace_many()),
|
|
@@ -280,14 +241,12 @@ class RestfulRouter:
|
|
|
280
241
|
return self
|
|
281
242
|
|
|
282
243
|
def with_delete_one_endpoint(
|
|
283
|
-
self,
|
|
284
|
-
is_documented: bool = True,
|
|
285
|
-
extract_user: Callable[..., Any] = no_user,
|
|
244
|
+
self, dependable: Dependable, is_documented: bool = True
|
|
286
245
|
) -> Self:
|
|
287
246
|
self.router.add_api_route(
|
|
288
247
|
self.item_path,
|
|
289
248
|
self.resource.delete_one(
|
|
290
|
-
Service=
|
|
249
|
+
Service=dependable.as_dependable(),
|
|
291
250
|
ItemId=Annotated[
|
|
292
251
|
str,
|
|
293
252
|
Path(alias=self.id_alias),
|
|
@@ -309,45 +268,16 @@ class RestfulRouter:
|
|
|
309
268
|
|
|
310
269
|
return self
|
|
311
270
|
|
|
312
|
-
def default(self) -> Self:
|
|
271
|
+
def default(self, dependable: Dependable) -> Self:
|
|
313
272
|
return (
|
|
314
|
-
self.with_create_one_endpoint()
|
|
315
|
-
.with_create_many_endpoint()
|
|
316
|
-
.with_read_one_endpoint()
|
|
317
|
-
.with_read_all_endpoint()
|
|
318
|
-
.with_update_one_endpoint()
|
|
319
|
-
.with_update_many_endpoint()
|
|
320
|
-
.with_delete_one_endpoint()
|
|
273
|
+
self.with_create_one_endpoint(dependable)
|
|
274
|
+
.with_create_many_endpoint(dependable)
|
|
275
|
+
.with_read_one_endpoint(dependable)
|
|
276
|
+
.with_read_all_endpoint(dependable)
|
|
277
|
+
.with_update_one_endpoint(dependable)
|
|
278
|
+
.with_update_many_endpoint(dependable)
|
|
279
|
+
.with_delete_one_endpoint(dependable)
|
|
321
280
|
)
|
|
322
281
|
|
|
323
282
|
def build(self) -> APIRouter:
|
|
324
283
|
return self.router
|
|
325
|
-
|
|
326
|
-
def _service(
|
|
327
|
-
self, extract_user: Callable[..., Any] = no_user
|
|
328
|
-
) -> Type[RestfulService]:
|
|
329
|
-
User = Annotated[Any, Depends(extract_user)]
|
|
330
|
-
ParentId = Annotated[str, Path(alias=self.parent_id_alias)]
|
|
331
|
-
|
|
332
|
-
def srv_child(user: User, parent_id: ParentId) -> RestfulService:
|
|
333
|
-
try:
|
|
334
|
-
return self.infra.with_user(user).with_parent(parent_id).build()
|
|
335
|
-
except DoesNotExistError as e:
|
|
336
|
-
raise HTTPException(
|
|
337
|
-
status_code=404,
|
|
338
|
-
detail=RestfulResponse(RestfulName(self.parent)).not_found(e),
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
def srv_root(user: User) -> RestfulService:
|
|
342
|
-
try:
|
|
343
|
-
return self.infra.with_user(user).build()
|
|
344
|
-
except DoesNotExistError as e:
|
|
345
|
-
raise HTTPException(
|
|
346
|
-
status_code=404,
|
|
347
|
-
detail=RestfulResponse(RestfulName(self.parent)).not_found(e),
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
if self.parent:
|
|
351
|
-
return Annotated[RestfulService, Depends(srv_child)] # type: ignore
|
|
352
|
-
else:
|
|
353
|
-
return Annotated[RestfulService, Depends(srv_root)] # type: ignore
|
|
@@ -4,34 +4,37 @@ from dataclasses import dataclass, field
|
|
|
4
4
|
from typing import Any, Iterator, Mapping, Self
|
|
5
5
|
|
|
6
6
|
import httpx
|
|
7
|
+
from httpx import Client
|
|
7
8
|
|
|
8
9
|
from apexdevkit.http.fluent import HttpMethod, HttpResponse
|
|
9
10
|
from apexdevkit.http.json import JsonDict
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def default_config() -> HttpxConfig:
|
|
14
|
+
return HttpxConfig()
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
@dataclass(frozen=True)
|
|
14
18
|
class Httpx:
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
client: httpx.Client
|
|
20
|
+
|
|
21
|
+
config: HttpxConfig = field(default_factory=default_config)
|
|
17
22
|
|
|
18
23
|
@classmethod
|
|
19
24
|
def create_for(cls, url: str) -> Self:
|
|
20
|
-
return cls(
|
|
25
|
+
return cls(Client(base_url=url), HttpxConfig())
|
|
21
26
|
|
|
22
27
|
def with_header(self, key: str, value: str) -> Httpx:
|
|
23
|
-
return Httpx(self.
|
|
28
|
+
return Httpx(self.client, self.config.with_header(key, value))
|
|
24
29
|
|
|
25
30
|
def with_param(self, key: str, value: str) -> Httpx:
|
|
26
|
-
return Httpx(self.
|
|
31
|
+
return Httpx(self.client, self.config.with_param(key, value))
|
|
27
32
|
|
|
28
33
|
def with_json(self, value: JsonDict) -> Httpx:
|
|
29
|
-
return Httpx(self.
|
|
34
|
+
return Httpx(self.client, self.config.with_json(value))
|
|
30
35
|
|
|
31
36
|
def request(self, method: HttpMethod, endpoint: str) -> HttpResponse:
|
|
32
|
-
return _HttpxResponse(
|
|
33
|
-
httpx.request(method.name, self.url + endpoint, **self.config)
|
|
34
|
-
)
|
|
37
|
+
return _HttpxResponse(self.client.request(method.name, endpoint, **self.config))
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
@dataclass
|
|
@@ -5,67 +5,76 @@ from dataclasses import dataclass, field
|
|
|
5
5
|
from functools import cached_property
|
|
6
6
|
from typing import Any, Iterable, Self
|
|
7
7
|
|
|
8
|
-
import httpx
|
|
9
8
|
from fastapi.testclient import TestClient
|
|
10
9
|
|
|
11
|
-
from apexdevkit.http import HttpUrl, JsonDict
|
|
10
|
+
from apexdevkit.http import Http, HttpUrl, JsonDict
|
|
11
|
+
from apexdevkit.http.fluent import HttpMethod, HttpResponse
|
|
12
|
+
from apexdevkit.http.httpx import Httpx
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@dataclass
|
|
15
16
|
class RestResource:
|
|
16
|
-
http: TestClient
|
|
17
|
+
http: TestClient | Http
|
|
17
18
|
name: RestfulName
|
|
18
19
|
|
|
20
|
+
def __post_init__(self) -> None:
|
|
21
|
+
if isinstance(self.http, TestClient):
|
|
22
|
+
self.http = Httpx(self.http)
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def _http(self) -> Http:
|
|
26
|
+
assert not isinstance(self.http, TestClient)
|
|
27
|
+
|
|
28
|
+
return self.http
|
|
29
|
+
|
|
19
30
|
def create_one(self) -> CreateOne:
|
|
20
|
-
return CreateOne(self.name, self.
|
|
31
|
+
return CreateOne(self.name, self._http)
|
|
21
32
|
|
|
22
33
|
def create_many(self) -> CreateMany:
|
|
23
|
-
return CreateMany(self.name, self.
|
|
34
|
+
return CreateMany(self.name, self._http)
|
|
24
35
|
|
|
25
36
|
def read_one(self) -> ReadOne:
|
|
26
|
-
return ReadOne(self.name, self.
|
|
37
|
+
return ReadOne(self.name, self._http)
|
|
27
38
|
|
|
28
39
|
def read_all(self) -> ReadAll:
|
|
29
|
-
return ReadAll(self.name, self.
|
|
40
|
+
return ReadAll(self.name, self._http)
|
|
30
41
|
|
|
31
42
|
def update_one(self) -> UpdateOne:
|
|
32
|
-
return UpdateOne(self.name, self.
|
|
43
|
+
return UpdateOne(self.name, self._http)
|
|
33
44
|
|
|
34
45
|
def update_many(self) -> UpdateMany:
|
|
35
|
-
return UpdateMany(self.name, self.
|
|
46
|
+
return UpdateMany(self.name, self._http)
|
|
36
47
|
|
|
37
48
|
def replace_one(self) -> ReplaceOne:
|
|
38
|
-
return ReplaceOne(self.name, self.
|
|
49
|
+
return ReplaceOne(self.name, self._http)
|
|
39
50
|
|
|
40
51
|
def replace_many(self) -> ReplaceMany:
|
|
41
|
-
return ReplaceMany(self.name, self.
|
|
52
|
+
return ReplaceMany(self.name, self._http)
|
|
42
53
|
|
|
43
54
|
def delete_one(self) -> DeleteOne:
|
|
44
|
-
return DeleteOne(self.name, self.
|
|
55
|
+
return DeleteOne(self.name, self._http)
|
|
45
56
|
|
|
46
57
|
|
|
47
58
|
@dataclass
|
|
48
59
|
class RestCollection(RestResource):
|
|
49
60
|
def sub_resource(self, name: str) -> RestItem:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
61
|
+
assert isinstance(self.http, Httpx), "sub resource only works with Httpx"
|
|
62
|
+
|
|
63
|
+
client = self.http.client
|
|
64
|
+
client.base_url = client.base_url.join(self.name.plural)
|
|
65
|
+
|
|
66
|
+
return RestItem(self.http, RestfulName(name))
|
|
57
67
|
|
|
58
68
|
|
|
59
69
|
@dataclass
|
|
60
70
|
class RestItem(RestResource):
|
|
61
71
|
def sub_resource(self, name: str) -> RestItem:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
72
|
+
assert isinstance(self.http, Httpx), "sub resource only works with Httpx"
|
|
73
|
+
|
|
74
|
+
client = self.http.client
|
|
75
|
+
client.base_url = client.base_url.join(self.name.plural)
|
|
76
|
+
|
|
77
|
+
return RestItem(self.http, RestfulName(name))
|
|
69
78
|
|
|
70
79
|
|
|
71
80
|
@dataclass
|
|
@@ -103,11 +112,11 @@ def as_plural(singular: str) -> str:
|
|
|
103
112
|
@dataclass
|
|
104
113
|
class RestRequest:
|
|
105
114
|
resource: RestfulName
|
|
106
|
-
http:
|
|
115
|
+
http: Http
|
|
107
116
|
|
|
108
117
|
@abstractmethod
|
|
109
118
|
@cached_property
|
|
110
|
-
def response(self) ->
|
|
119
|
+
def response(self) -> HttpResponse: # pragma: no cover
|
|
111
120
|
pass
|
|
112
121
|
|
|
113
122
|
def unpack(self) -> JsonDict:
|
|
@@ -122,7 +131,7 @@ class RestRequest:
|
|
|
122
131
|
return RestResponse(
|
|
123
132
|
resource=self.resource,
|
|
124
133
|
json=JsonDict(self.response.json()),
|
|
125
|
-
http_code=self.response.
|
|
134
|
+
http_code=self.response.code(),
|
|
126
135
|
)
|
|
127
136
|
|
|
128
137
|
|
|
@@ -136,8 +145,11 @@ class CreateOne(RestRequest):
|
|
|
136
145
|
return self
|
|
137
146
|
|
|
138
147
|
@cached_property
|
|
139
|
-
def response(self) ->
|
|
140
|
-
return self.http.
|
|
148
|
+
def response(self) -> HttpResponse:
|
|
149
|
+
return self.http.with_json(self.data).request(
|
|
150
|
+
method=HttpMethod.post,
|
|
151
|
+
endpoint=self.resource + "",
|
|
152
|
+
)
|
|
141
153
|
|
|
142
154
|
|
|
143
155
|
@dataclass
|
|
@@ -145,8 +157,11 @@ class ReadOne(RestRequest):
|
|
|
145
157
|
item_id: str = field(init=False)
|
|
146
158
|
|
|
147
159
|
@cached_property
|
|
148
|
-
def response(self) ->
|
|
149
|
-
return self.http.
|
|
160
|
+
def response(self) -> HttpResponse:
|
|
161
|
+
return self.http.request(
|
|
162
|
+
method=HttpMethod.get,
|
|
163
|
+
endpoint=self.resource + str(self.item_id),
|
|
164
|
+
)
|
|
150
165
|
|
|
151
166
|
def with_id(self, value: Any) -> Self:
|
|
152
167
|
self.item_id = str(value)
|
|
@@ -159,8 +174,12 @@ class ReadAll(RestRequest):
|
|
|
159
174
|
params: dict[str, Any] = field(init=False, default_factory=dict)
|
|
160
175
|
|
|
161
176
|
@cached_property
|
|
162
|
-
def response(self) ->
|
|
163
|
-
|
|
177
|
+
def response(self) -> HttpResponse:
|
|
178
|
+
http = self.http
|
|
179
|
+
for param, value in self.params.items():
|
|
180
|
+
http = http.with_param(param, value)
|
|
181
|
+
|
|
182
|
+
return http.request(method=HttpMethod.get, endpoint=self.resource + "")
|
|
164
183
|
|
|
165
184
|
def with_params(self, **kwargs: Any) -> Self:
|
|
166
185
|
self.params = {**kwargs}
|
|
@@ -174,8 +193,11 @@ class UpdateOne(RestRequest):
|
|
|
174
193
|
data: JsonDict = field(init=False)
|
|
175
194
|
|
|
176
195
|
@cached_property
|
|
177
|
-
def response(self) ->
|
|
178
|
-
return self.http.
|
|
196
|
+
def response(self) -> HttpResponse:
|
|
197
|
+
return self.http.with_json(self.data).request(
|
|
198
|
+
method=HttpMethod.patch,
|
|
199
|
+
endpoint=self.resource + str(self.item_id),
|
|
200
|
+
)
|
|
179
201
|
|
|
180
202
|
def with_id(self, value: Any) -> Self:
|
|
181
203
|
self.item_id = str(value)
|
|
@@ -193,8 +215,11 @@ class ReplaceOne(RestRequest):
|
|
|
193
215
|
data: JsonDict = field(init=False)
|
|
194
216
|
|
|
195
217
|
@cached_property
|
|
196
|
-
def response(self) ->
|
|
197
|
-
return self.http.
|
|
218
|
+
def response(self) -> HttpResponse:
|
|
219
|
+
return self.http.with_json(self.data).request(
|
|
220
|
+
method=HttpMethod.put,
|
|
221
|
+
endpoint=self.resource + "",
|
|
222
|
+
)
|
|
198
223
|
|
|
199
224
|
def from_data(self, value: JsonDict) -> Self:
|
|
200
225
|
self.data = value
|
|
@@ -207,10 +232,12 @@ class CreateMany(RestRequest):
|
|
|
207
232
|
data: list[JsonDict] = field(default_factory=list)
|
|
208
233
|
|
|
209
234
|
@cached_property
|
|
210
|
-
def response(self) ->
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
235
|
+
def response(self) -> HttpResponse:
|
|
236
|
+
json = JsonDict({self.resource.plural: [dict(data) for data in self.data]})
|
|
237
|
+
|
|
238
|
+
return self.http.with_json(json).request(
|
|
239
|
+
method=HttpMethod.post,
|
|
240
|
+
endpoint=self.resource + "batch",
|
|
214
241
|
)
|
|
215
242
|
|
|
216
243
|
def from_data(self, value: JsonDict) -> Self:
|
|
@@ -227,10 +254,12 @@ class UpdateMany(RestRequest):
|
|
|
227
254
|
data: list[JsonDict] = field(default_factory=list)
|
|
228
255
|
|
|
229
256
|
@cached_property
|
|
230
|
-
def response(self) ->
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
257
|
+
def response(self) -> HttpResponse:
|
|
258
|
+
json = JsonDict({self.resource.plural: [dict(data) for data in self.data]})
|
|
259
|
+
|
|
260
|
+
return self.http.with_json(json).request(
|
|
261
|
+
method=HttpMethod.patch,
|
|
262
|
+
endpoint=self.resource + "",
|
|
234
263
|
)
|
|
235
264
|
|
|
236
265
|
def from_data(self, value: JsonDict) -> Self:
|
|
@@ -247,10 +276,12 @@ class ReplaceMany(RestRequest):
|
|
|
247
276
|
data: list[JsonDict] = field(default_factory=list)
|
|
248
277
|
|
|
249
278
|
@cached_property
|
|
250
|
-
def response(self) ->
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
279
|
+
def response(self) -> HttpResponse:
|
|
280
|
+
json = JsonDict({self.resource.plural: [dict(data) for data in self.data]})
|
|
281
|
+
|
|
282
|
+
return self.http.with_json(json).request(
|
|
283
|
+
method=HttpMethod.put,
|
|
284
|
+
endpoint=self.resource + "batch",
|
|
254
285
|
)
|
|
255
286
|
|
|
256
287
|
def from_data(self, value: JsonDict) -> Self:
|
|
@@ -267,8 +298,11 @@ class DeleteOne(RestRequest):
|
|
|
267
298
|
item_id: str = field(init=False)
|
|
268
299
|
|
|
269
300
|
@cached_property
|
|
270
|
-
def response(self) ->
|
|
271
|
-
return self.http.
|
|
301
|
+
def response(self) -> HttpResponse:
|
|
302
|
+
return self.http.request(
|
|
303
|
+
method=HttpMethod.delete,
|
|
304
|
+
endpoint=self.resource + str(self.item_id),
|
|
305
|
+
)
|
|
272
306
|
|
|
273
307
|
def with_id(self, value: Any) -> Self:
|
|
274
308
|
self.item_id = str(value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "apexdevkit"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.6.1"
|
|
4
4
|
description = "Apex Development Tools for python."
|
|
5
5
|
authors = ["Apex Dev <dev@apex.ge>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -25,7 +25,7 @@ ruff = "*"
|
|
|
25
25
|
python_version = "3.11"
|
|
26
26
|
ignore_missing_imports = true
|
|
27
27
|
strict = true
|
|
28
|
-
exclude = ["apexdevkit/fastapi/schema.py"]
|
|
28
|
+
exclude = ["apexdevkit/fastapi/schema.py", "apexdevkit/fastapi/dependable.py"]
|
|
29
29
|
|
|
30
30
|
[tool.ruff]
|
|
31
31
|
target-version = "py311"
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from fastapi import Depends
|
|
4
|
-
from fastapi.requests import Request
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def inject(dependency: str) -> Any: # pragma: no cover
|
|
8
|
-
def get(request: Request) -> Any:
|
|
9
|
-
return getattr(request.app.state, dependency)
|
|
10
|
-
|
|
11
|
-
return Depends(get)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|