apexdevkit 1.3.11__tar.gz → 1.3.13__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.3.11 → apexdevkit-1.3.13}/PKG-INFO +1 -1
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/router.py +104 -16
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/pyproject.toml +1 -1
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/LICENSE +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/README.md +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/__init__.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/annotation/__init__.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/annotation/deprecate.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/error.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/__init__.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/builder.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/dependable.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/docs.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/response.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/schema.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/fastapi/service.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/http/__init__.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/http/fake.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/http/fluent.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/http/httpx.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/http/json.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/http/url.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/py.typed +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/repository/__init__.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/repository/connector.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/repository/database.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/repository/in_memory.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/repository/interface.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/testing/__init__.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/testing/database.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/testing/fake.py +0 -0
- {apexdevkit-1.3.11 → apexdevkit-1.3.13}/apexdevkit/testing/rest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from functools import cached_property
|
|
3
|
-
from typing import Annotated, Any, Iterable, Self, TypeVar
|
|
3
|
+
from typing import Annotated, Any, Iterable, Protocol, Self, TypeVar
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, Depends, Path
|
|
6
6
|
from fastapi.responses import JSONResponse
|
|
@@ -78,14 +78,34 @@ class RestfulResponse:
|
|
|
78
78
|
T = TypeVar("T")
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
class RestfulServiceInfra(Protocol):
|
|
82
|
+
def service_for(self, parent_id: str) -> RestfulService:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class SingleRestfulServiceInfra:
|
|
88
|
+
service: RestfulService
|
|
89
|
+
|
|
90
|
+
def service_for(self, parent_id: str) -> RestfulService:
|
|
91
|
+
return self.service
|
|
92
|
+
|
|
93
|
+
|
|
81
94
|
@dataclass
|
|
82
95
|
class RestfulRouter:
|
|
83
|
-
service: RestfulService
|
|
96
|
+
service: RestfulService | None = None
|
|
84
97
|
|
|
85
98
|
router: APIRouter = field(default_factory=APIRouter)
|
|
86
99
|
|
|
87
100
|
name: RestfulName = field(init=False)
|
|
88
101
|
fields: SchemaFields = field(init=False)
|
|
102
|
+
infra: RestfulServiceInfra = field(init=False)
|
|
103
|
+
|
|
104
|
+
parent: str = field(init=False, default="")
|
|
105
|
+
|
|
106
|
+
def __post_init__(self) -> None:
|
|
107
|
+
if self.service:
|
|
108
|
+
self.infra = SingleRestfulServiceInfra(self.service)
|
|
89
109
|
|
|
90
110
|
@cached_property
|
|
91
111
|
def response(self) -> RestfulResponse:
|
|
@@ -99,6 +119,10 @@ class RestfulRouter:
|
|
|
99
119
|
def id_alias(self) -> str:
|
|
100
120
|
return self.name.singular + "_id"
|
|
101
121
|
|
|
122
|
+
@property
|
|
123
|
+
def parent_id_alias(self) -> str:
|
|
124
|
+
return self.parent + "_id"
|
|
125
|
+
|
|
102
126
|
@property
|
|
103
127
|
def item_path(self) -> str:
|
|
104
128
|
return "/{" + self.id_alias + "}"
|
|
@@ -118,7 +142,27 @@ class RestfulRouter:
|
|
|
118
142
|
|
|
119
143
|
return self
|
|
120
144
|
|
|
145
|
+
def with_parent(self, name: str) -> Self:
|
|
146
|
+
self.parent = name
|
|
147
|
+
|
|
148
|
+
return self
|
|
149
|
+
|
|
150
|
+
def with_service(self, value: RestfulService) -> Self:
|
|
151
|
+
self.infra = SingleRestfulServiceInfra(value)
|
|
152
|
+
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
def with_infra(self, value: RestfulServiceInfra) -> Self:
|
|
156
|
+
self.infra = value
|
|
157
|
+
|
|
158
|
+
return self
|
|
159
|
+
|
|
121
160
|
def with_create_one_endpoint(self, is_documented: bool = True) -> Self:
|
|
161
|
+
parent_id_type = Annotated[
|
|
162
|
+
str,
|
|
163
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
164
|
+
]
|
|
165
|
+
|
|
122
166
|
item_type = Annotated[
|
|
123
167
|
RawItem,
|
|
124
168
|
Depends(self.schema.for_create_one()),
|
|
@@ -131,9 +175,11 @@ class RestfulRouter:
|
|
|
131
175
|
response_model=self.schema.for_item(),
|
|
132
176
|
include_in_schema=is_documented,
|
|
133
177
|
)
|
|
134
|
-
def create_one(item: item_type) -> _Response:
|
|
178
|
+
def create_one(parent_id: parent_id_type, item: item_type) -> _Response:
|
|
179
|
+
service = self.infra.service_for(parent_id)
|
|
180
|
+
|
|
135
181
|
try:
|
|
136
|
-
item =
|
|
182
|
+
item = service.create_one(item)
|
|
137
183
|
except ExistsError as e:
|
|
138
184
|
return JSONResponse(self.response.exists(e), 409)
|
|
139
185
|
|
|
@@ -142,6 +188,11 @@ class RestfulRouter:
|
|
|
142
188
|
return self
|
|
143
189
|
|
|
144
190
|
def with_create_many_endpoint(self, is_documented: bool = True) -> Self:
|
|
191
|
+
parent_id_type = Annotated[
|
|
192
|
+
str,
|
|
193
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
194
|
+
]
|
|
195
|
+
|
|
145
196
|
collection_type = Annotated[
|
|
146
197
|
RawCollection,
|
|
147
198
|
Depends(self.schema.for_create_many()),
|
|
@@ -154,9 +205,11 @@ class RestfulRouter:
|
|
|
154
205
|
response_model=self.schema.for_collection(),
|
|
155
206
|
include_in_schema=is_documented,
|
|
156
207
|
)
|
|
157
|
-
def create_many(items: collection_type) -> _Response:
|
|
208
|
+
def create_many(parent_id: parent_id_type, items: collection_type) -> _Response:
|
|
209
|
+
service = self.infra.service_for(parent_id)
|
|
210
|
+
|
|
158
211
|
try:
|
|
159
|
-
return self.response.created_many(
|
|
212
|
+
return self.response.created_many(service.create_many(items))
|
|
160
213
|
except ExistsError as e:
|
|
161
214
|
return JSONResponse(self.response.exists(e), 409)
|
|
162
215
|
|
|
@@ -164,6 +217,10 @@ class RestfulRouter:
|
|
|
164
217
|
|
|
165
218
|
def with_read_one_endpoint(self, is_documented: bool = True) -> Self:
|
|
166
219
|
id_type = Annotated[str, Path(alias=self.id_alias)]
|
|
220
|
+
parent_id_type = Annotated[
|
|
221
|
+
str,
|
|
222
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
223
|
+
]
|
|
167
224
|
|
|
168
225
|
@self.router.get(
|
|
169
226
|
self.item_path,
|
|
@@ -172,15 +229,22 @@ class RestfulRouter:
|
|
|
172
229
|
response_model=self.schema.for_item(),
|
|
173
230
|
include_in_schema=is_documented,
|
|
174
231
|
)
|
|
175
|
-
def read_one(item_id: id_type) -> _Response:
|
|
232
|
+
def read_one(parent_id: parent_id_type, item_id: id_type) -> _Response:
|
|
233
|
+
service = self.infra.service_for(parent_id)
|
|
234
|
+
|
|
176
235
|
try:
|
|
177
|
-
return self.response.found_one(
|
|
236
|
+
return self.response.found_one(service.read_one(item_id))
|
|
178
237
|
except DoesNotExistError as e:
|
|
179
238
|
return JSONResponse(self.response.not_found(e), 404)
|
|
180
239
|
|
|
181
240
|
return self
|
|
182
241
|
|
|
183
242
|
def with_read_all_endpoint(self, is_documented: bool = True) -> Self:
|
|
243
|
+
parent_id_type = Annotated[
|
|
244
|
+
str,
|
|
245
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
246
|
+
]
|
|
247
|
+
|
|
184
248
|
@self.router.get(
|
|
185
249
|
"",
|
|
186
250
|
status_code=200,
|
|
@@ -188,12 +252,18 @@ class RestfulRouter:
|
|
|
188
252
|
response_model=self.schema.for_collection(),
|
|
189
253
|
include_in_schema=is_documented,
|
|
190
254
|
)
|
|
191
|
-
def read_all() -> _Response:
|
|
192
|
-
|
|
255
|
+
def read_all(parent_id: parent_id_type) -> _Response:
|
|
256
|
+
service = self.infra.service_for(parent_id)
|
|
257
|
+
|
|
258
|
+
return self.response.found_many(list(service.read_all()))
|
|
193
259
|
|
|
194
260
|
return self
|
|
195
261
|
|
|
196
262
|
def with_update_one_endpoint(self, is_documented: bool = True) -> Self:
|
|
263
|
+
parent_id_type = Annotated[
|
|
264
|
+
str,
|
|
265
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
266
|
+
]
|
|
197
267
|
id_type = Annotated[str, Path(alias=self.id_alias)]
|
|
198
268
|
update_type = Annotated[
|
|
199
269
|
RawItem,
|
|
@@ -207,9 +277,15 @@ class RestfulRouter:
|
|
|
207
277
|
response_model=self.schema.for_no_data(),
|
|
208
278
|
include_in_schema=is_documented,
|
|
209
279
|
)
|
|
210
|
-
def update_one(
|
|
280
|
+
def update_one(
|
|
281
|
+
parent_id: parent_id_type,
|
|
282
|
+
item_id: id_type,
|
|
283
|
+
updates: update_type,
|
|
284
|
+
) -> _Response:
|
|
285
|
+
service = self.infra.service_for(parent_id)
|
|
286
|
+
|
|
211
287
|
try:
|
|
212
|
-
|
|
288
|
+
service.update_one(item_id, **updates)
|
|
213
289
|
except DoesNotExistError as e:
|
|
214
290
|
return JSONResponse(self.response.not_found(e), 404)
|
|
215
291
|
except ForbiddenError as e:
|
|
@@ -220,6 +296,10 @@ class RestfulRouter:
|
|
|
220
296
|
return self
|
|
221
297
|
|
|
222
298
|
def with_update_many_endpoint(self, is_documented: bool = True) -> Self:
|
|
299
|
+
parent_id_type = Annotated[
|
|
300
|
+
str,
|
|
301
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
302
|
+
]
|
|
223
303
|
collection_type = Annotated[
|
|
224
304
|
RawCollection,
|
|
225
305
|
Depends(self.schema.for_update_many()),
|
|
@@ -232,14 +312,20 @@ class RestfulRouter:
|
|
|
232
312
|
response_model=self.schema.for_no_data(),
|
|
233
313
|
include_in_schema=is_documented,
|
|
234
314
|
)
|
|
235
|
-
def update_many(items: collection_type) -> _Response:
|
|
236
|
-
self.
|
|
315
|
+
def update_many(parent_id: parent_id_type, items: collection_type) -> _Response:
|
|
316
|
+
service = self.infra.service_for(parent_id)
|
|
317
|
+
|
|
318
|
+
service.update_many(items)
|
|
237
319
|
|
|
238
320
|
return self.response.ok()
|
|
239
321
|
|
|
240
322
|
return self
|
|
241
323
|
|
|
242
324
|
def with_delete_one_endpoint(self, is_documented: bool = True) -> Self:
|
|
325
|
+
parent_id_type = Annotated[
|
|
326
|
+
str,
|
|
327
|
+
Path(alias=self.parent_id_alias, default_factory=str),
|
|
328
|
+
]
|
|
243
329
|
id_type = Annotated[str, Path(alias=self.id_alias)]
|
|
244
330
|
|
|
245
331
|
@self.router.delete(
|
|
@@ -249,9 +335,11 @@ class RestfulRouter:
|
|
|
249
335
|
response_model=self.schema.for_no_data(),
|
|
250
336
|
include_in_schema=is_documented,
|
|
251
337
|
)
|
|
252
|
-
def delete_one(item_id: id_type) -> _Response:
|
|
338
|
+
def delete_one(parent_id: parent_id_type, item_id: id_type) -> _Response:
|
|
339
|
+
service = self.infra.service_for(parent_id)
|
|
340
|
+
|
|
253
341
|
try:
|
|
254
|
-
|
|
342
|
+
service.delete_one(item_id)
|
|
255
343
|
except DoesNotExistError as e:
|
|
256
344
|
return JSONResponse(self.response.not_found(e), 404)
|
|
257
345
|
|
|
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
|