apexdevkit 1.3.10__tar.gz → 1.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.
Files changed (32) hide show
  1. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/PKG-INFO +1 -1
  2. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/error.py +9 -0
  3. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/router.py +114 -17
  4. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/pyproject.toml +1 -1
  5. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/LICENSE +0 -0
  6. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/README.md +0 -0
  7. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/__init__.py +0 -0
  8. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/annotation/__init__.py +0 -0
  9. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/annotation/deprecate.py +0 -0
  10. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/__init__.py +0 -0
  11. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/builder.py +0 -0
  12. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/dependable.py +0 -0
  13. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/docs.py +0 -0
  14. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/response.py +0 -0
  15. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/schema.py +0 -0
  16. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/fastapi/service.py +0 -0
  17. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/http/__init__.py +0 -0
  18. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/http/fake.py +0 -0
  19. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/http/fluent.py +0 -0
  20. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/http/httpx.py +0 -0
  21. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/http/json.py +0 -0
  22. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/http/url.py +0 -0
  23. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/py.typed +0 -0
  24. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/repository/__init__.py +0 -0
  25. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/repository/connector.py +0 -0
  26. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/repository/database.py +0 -0
  27. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/repository/in_memory.py +0 -0
  28. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/repository/interface.py +0 -0
  29. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/testing/__init__.py +0 -0
  30. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/testing/database.py +0 -0
  31. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/testing/fake.py +0 -0
  32. {apexdevkit-1.3.10 → apexdevkit-1.3.12}/apexdevkit/testing/rest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.3.10
3
+ Version: 1.3.12
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -38,3 +38,12 @@ class ExistsError(Exception):
38
38
  @dataclass
39
39
  class DoesNotExistError(Exception):
40
40
  id: Any = "unknown"
41
+
42
+
43
+ @dataclass
44
+ class ForbiddenError(Exception):
45
+ item: Any = field(default_factory=UnknownItem)
46
+
47
+ @property
48
+ def id(self) -> Any:
49
+ return self.item.id
@@ -1,11 +1,11 @@
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
7
7
 
8
- from apexdevkit.error import DoesNotExistError, ExistsError
8
+ from apexdevkit.error import DoesNotExistError, ExistsError, ForbiddenError
9
9
  from apexdevkit.fastapi.schema import DataclassFields, RestfulSchema, SchemaFields
10
10
  from apexdevkit.fastapi.service import RawCollection, RawItem, RestfulService
11
11
  from apexdevkit.testing import RestfulName
@@ -55,6 +55,13 @@ class RestfulResponse:
55
55
  error=f"An item<{name}> with the {e} already exists.",
56
56
  )
57
57
 
58
+ def forbidden(self, e: ForbiddenError) -> dict[str, Any]:
59
+ return self._response(
60
+ 403,
61
+ data={"id": str(e.id)},
62
+ error="Forbidden",
63
+ )
64
+
58
65
  def created_one(self, item: Any) -> dict[str, Any]:
59
66
  return self._response(201, item)
60
67
 
@@ -71,14 +78,34 @@ class RestfulResponse:
71
78
  T = TypeVar("T")
72
79
 
73
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
+
74
94
  @dataclass
75
95
  class RestfulRouter:
76
- service: RestfulService
96
+ service: RestfulService | None = None
77
97
 
78
98
  router: APIRouter = field(default_factory=APIRouter)
79
99
 
80
100
  name: RestfulName = field(init=False)
81
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)
82
109
 
83
110
  @cached_property
84
111
  def response(self) -> RestfulResponse:
@@ -92,6 +119,10 @@ class RestfulRouter:
92
119
  def id_alias(self) -> str:
93
120
  return self.name.singular + "_id"
94
121
 
122
+ @property
123
+ def parent_id_alias(self) -> str:
124
+ return self.parent + "_id"
125
+
95
126
  @property
96
127
  def item_path(self) -> str:
97
128
  return "/{" + self.id_alias + "}"
@@ -111,7 +142,27 @@ class RestfulRouter:
111
142
 
112
143
  return self
113
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
+
114
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
+
115
166
  item_type = Annotated[
116
167
  RawItem,
117
168
  Depends(self.schema.for_create_one()),
@@ -124,9 +175,11 @@ class RestfulRouter:
124
175
  response_model=self.schema.for_item(),
125
176
  include_in_schema=is_documented,
126
177
  )
127
- 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
+
128
181
  try:
129
- item = self.service.create_one(item)
182
+ item = service.create_one(item)
130
183
  except ExistsError as e:
131
184
  return JSONResponse(self.response.exists(e), 409)
132
185
 
@@ -135,6 +188,11 @@ class RestfulRouter:
135
188
  return self
136
189
 
137
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
+
138
196
  collection_type = Annotated[
139
197
  RawCollection,
140
198
  Depends(self.schema.for_create_many()),
@@ -147,9 +205,11 @@ class RestfulRouter:
147
205
  response_model=self.schema.for_collection(),
148
206
  include_in_schema=is_documented,
149
207
  )
150
- 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
+
151
211
  try:
152
- return self.response.created_many(self.service.create_many(items))
212
+ return self.response.created_many(service.create_many(items))
153
213
  except ExistsError as e:
154
214
  return JSONResponse(self.response.exists(e), 409)
155
215
 
@@ -157,6 +217,10 @@ class RestfulRouter:
157
217
 
158
218
  def with_read_one_endpoint(self, is_documented: bool = True) -> Self:
159
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
+ ]
160
224
 
161
225
  @self.router.get(
162
226
  self.item_path,
@@ -165,15 +229,22 @@ class RestfulRouter:
165
229
  response_model=self.schema.for_item(),
166
230
  include_in_schema=is_documented,
167
231
  )
168
- 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
+
169
235
  try:
170
- return self.response.found_one(self.service.read_one(item_id))
236
+ return self.response.found_one(service.read_one(item_id))
171
237
  except DoesNotExistError as e:
172
238
  return JSONResponse(self.response.not_found(e), 404)
173
239
 
174
240
  return self
175
241
 
176
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
+
177
248
  @self.router.get(
178
249
  "",
179
250
  status_code=200,
@@ -181,12 +252,18 @@ class RestfulRouter:
181
252
  response_model=self.schema.for_collection(),
182
253
  include_in_schema=is_documented,
183
254
  )
184
- def read_all() -> _Response:
185
- return self.response.found_many(list(self.service.read_all()))
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()))
186
259
 
187
260
  return self
188
261
 
189
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
+ ]
190
267
  id_type = Annotated[str, Path(alias=self.id_alias)]
191
268
  update_type = Annotated[
192
269
  RawItem,
@@ -200,17 +277,29 @@ class RestfulRouter:
200
277
  response_model=self.schema.for_no_data(),
201
278
  include_in_schema=is_documented,
202
279
  )
203
- def update_one(item_id: id_type, updates: update_type) -> _Response:
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
+
204
287
  try:
205
- self.service.update_one(item_id, **updates)
288
+ service.update_one(item_id, **updates)
206
289
  except DoesNotExistError as e:
207
290
  return JSONResponse(self.response.not_found(e), 404)
291
+ except ForbiddenError as e:
292
+ return JSONResponse(self.response.forbidden(e), 403)
208
293
 
209
294
  return self.response.ok()
210
295
 
211
296
  return self
212
297
 
213
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
+ ]
214
303
  collection_type = Annotated[
215
304
  RawCollection,
216
305
  Depends(self.schema.for_update_many()),
@@ -223,14 +312,20 @@ class RestfulRouter:
223
312
  response_model=self.schema.for_no_data(),
224
313
  include_in_schema=is_documented,
225
314
  )
226
- def update_many(items: collection_type) -> _Response:
227
- self.service.update_many(items)
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)
228
319
 
229
320
  return self.response.ok()
230
321
 
231
322
  return self
232
323
 
233
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
+ ]
234
329
  id_type = Annotated[str, Path(alias=self.id_alias)]
235
330
 
236
331
  @self.router.delete(
@@ -240,9 +335,11 @@ class RestfulRouter:
240
335
  response_model=self.schema.for_no_data(),
241
336
  include_in_schema=is_documented,
242
337
  )
243
- 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
+
244
341
  try:
245
- self.service.delete_one(item_id)
342
+ service.delete_one(item_id)
246
343
  except DoesNotExistError as e:
247
344
  return JSONResponse(self.response.not_found(e), 404)
248
345
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.3.10"
3
+ version = "1.3.12"
4
4
  description = "Apex Development Tools for python."
5
5
  authors = ["Apex Dev <dev@apex.ge>"]
6
6
  readme = "README.md"
File without changes
File without changes