apexdevkit 1.5.14__tar.gz → 1.5.16__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 (34) hide show
  1. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/PKG-INFO +1 -1
  2. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/builder.py +0 -3
  3. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/router.py +101 -15
  4. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/schema.py +20 -10
  5. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/service.py +18 -13
  6. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/testing/rest.py +42 -2
  7. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/pyproject.toml +1 -1
  8. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/LICENSE +0 -0
  9. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/README.md +0 -0
  10. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/__init__.py +0 -0
  11. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/annotation/__init__.py +0 -0
  12. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/annotation/deprecate.py +0 -0
  13. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/error.py +0 -0
  14. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/__init__.py +0 -0
  15. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/dependable.py +0 -0
  16. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/docs.py +0 -0
  17. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/fastapi/response.py +0 -0
  18. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/formatter.py +0 -0
  19. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/http/__init__.py +0 -0
  20. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/http/fake.py +0 -0
  21. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/http/fluent.py +0 -0
  22. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/http/httpx.py +0 -0
  23. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/http/json.py +0 -0
  24. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/http/url.py +0 -0
  25. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/py.typed +0 -0
  26. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/repository/__init__.py +0 -0
  27. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/repository/base.py +0 -0
  28. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/repository/connector.py +0 -0
  29. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/repository/database.py +0 -0
  30. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/repository/in_memory.py +0 -0
  31. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/repository/interface.py +0 -0
  32. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/testing/__init__.py +0 -0
  33. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/testing/database.py +0 -0
  34. {apexdevkit-1.5.14 → apexdevkit-1.5.16}/apexdevkit/testing/fake.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.5.14
3
+ Version: 1.5.16
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -3,8 +3,6 @@ from typing import Any, Self
3
3
 
4
4
  from fastapi import APIRouter, FastAPI
5
5
 
6
- from apexdevkit.annotation import deprecated
7
-
8
6
 
9
7
  @dataclass
10
8
  class FastApiBuilder:
@@ -28,7 +26,6 @@ class FastApiBuilder:
28
26
 
29
27
  return self
30
28
 
31
- @deprecated("Pass dependencies to router via with_infra method instead")
32
29
  def with_dependency(self, **values: Any) -> Self: # pragma: no cover
33
30
  for key, value in values.items():
34
31
  setattr(self.app.state, key, value)
@@ -6,9 +6,8 @@ from typing import Annotated, Any, Callable, Iterable, Self, TypeVar
6
6
  from fastapi import APIRouter, Depends, Path
7
7
  from fastapi.responses import JSONResponse
8
8
 
9
- from apexdevkit.annotation import deprecated
10
9
  from apexdevkit.error import DoesNotExistError, ExistsError, ForbiddenError
11
- from apexdevkit.fastapi.schema import DataclassFields, RestfulSchema, SchemaFields
10
+ from apexdevkit.fastapi.schema import RestfulSchema, SchemaFields
12
11
  from apexdevkit.fastapi.service import RawCollection, RawItem, RestfulService
13
12
  from apexdevkit.testing import RestfulName
14
13
 
@@ -100,7 +99,6 @@ class RestfulServiceBuilder(ABC):
100
99
  pass
101
100
 
102
101
 
103
- @deprecated("Use infra instead")
104
102
  @dataclass
105
103
  class PreBuiltRestfulService(RestfulServiceBuilder): # pragma: no cover
106
104
  service: RestfulService
@@ -149,12 +147,6 @@ class RestfulRouter:
149
147
  def item_path(self) -> str:
150
148
  return "/{" + self.id_alias + "}"
151
149
 
152
- @deprecated("Use with_name and with_fields instead")
153
- def with_dataclass(self, value: Any) -> Self: # pragma: no cover
154
- return self.with_name(RestfulName(value.__name__.lower())).with_fields(
155
- DataclassFields(value)
156
- )
157
-
158
150
  def with_name(self, value: RestfulName) -> Self:
159
151
  self.name = value
160
152
 
@@ -170,12 +162,6 @@ class RestfulRouter:
170
162
 
171
163
  return self
172
164
 
173
- @deprecated("Use with_infra instead")
174
- def with_service(self, value: RestfulService) -> Self: # pragma: no cover
175
- self.infra = PreBuiltRestfulService(value)
176
-
177
- return self
178
-
179
165
  def with_infra(self, value: RestfulServiceBuilder) -> Self:
180
166
  self.infra = value
181
167
 
@@ -481,6 +467,106 @@ class RestfulRouter:
481
467
 
482
468
  return endpoint
483
469
 
470
+ def with_replace_one_endpoint(
471
+ self,
472
+ is_documented: bool = True,
473
+ extract_user: Callable[..., Any] = no_user,
474
+ ) -> Self:
475
+ self.router.add_api_route(
476
+ "",
477
+ self.replace_one(
478
+ User=Annotated[
479
+ Any,
480
+ Depends(extract_user),
481
+ ],
482
+ ParentId=Annotated[
483
+ str,
484
+ Path(alias=self.parent_id_alias, default_factory=str),
485
+ ],
486
+ Item=Annotated[
487
+ RawItem,
488
+ Depends(self.schema.for_replace_one()),
489
+ ],
490
+ ),
491
+ methods=["PUT"],
492
+ status_code=200,
493
+ responses={404: {}},
494
+ response_model=self.schema.for_no_data(),
495
+ include_in_schema=is_documented,
496
+ summary="Replace One",
497
+ )
498
+
499
+ return self
500
+
501
+ def replace_one(self, User, ParentId, Item) -> Callable[..., _Response]: # type: ignore
502
+ def endpoint(user: User, parent_id: ParentId, item: Item) -> _Response:
503
+ try:
504
+ service = self.infra.with_user(user).with_parent(parent_id).build()
505
+ except DoesNotExistError as e:
506
+ return JSONResponse(
507
+ RestfulResponse(RestfulName(self.parent)).not_found(e), 404
508
+ )
509
+ try:
510
+ service.replace_one(item)
511
+ except DoesNotExistError as e:
512
+ return JSONResponse(self.response.not_found(e), 404)
513
+ except ForbiddenError as e:
514
+ return JSONResponse(self.response.forbidden(e), 403)
515
+
516
+ return self.response.ok()
517
+
518
+ return endpoint
519
+
520
+ def with_replace_many_endpoint(
521
+ self,
522
+ is_documented: bool = True,
523
+ extract_user: Callable[..., Any] = no_user,
524
+ ) -> Self:
525
+ self.router.add_api_route(
526
+ "/batch",
527
+ self.replace_many(
528
+ User=Annotated[
529
+ Any,
530
+ Depends(extract_user),
531
+ ],
532
+ ParentId=Annotated[
533
+ str,
534
+ Path(alias=self.parent_id_alias, default_factory=str),
535
+ ],
536
+ Collection=Annotated[
537
+ RawCollection,
538
+ Depends(self.schema.for_replace_many()),
539
+ ],
540
+ ),
541
+ methods=["PUT"],
542
+ status_code=200,
543
+ responses={},
544
+ response_model=self.schema.for_no_data(),
545
+ include_in_schema=is_documented,
546
+ summary="Replace Many",
547
+ )
548
+
549
+ return self
550
+
551
+ def replace_many(self, User, ParentId, Collection) -> Callable[..., _Response]: # type: ignore
552
+ def endpoint(user: User, parent_id: ParentId, items: Collection) -> _Response:
553
+ try:
554
+ service = self.infra.with_user(user).with_parent(parent_id).build()
555
+ except DoesNotExistError as e:
556
+ return JSONResponse(
557
+ RestfulResponse(RestfulName(self.parent)).not_found(e), 404
558
+ )
559
+ try:
560
+ service.replace_many(items)
561
+ except DoesNotExistError as e:
562
+ return JSONResponse(self.response.not_found(e), 404)
563
+ except ForbiddenError as e:
564
+ return JSONResponse(self.response.forbidden(e), 403)
565
+
566
+ return self.response.ok()
567
+
568
+ return endpoint
569
+
484
570
  def with_delete_one_endpoint(
485
571
  self,
486
572
  is_documented: bool = True,
@@ -5,7 +5,6 @@ from typing import Any, Callable, Iterable, List
5
5
 
6
6
  from pydantic import BaseModel, create_model
7
7
 
8
- from apexdevkit.annotation import deprecated
9
8
  from apexdevkit.http import JsonDict
10
9
  from apexdevkit.testing import RestfulName
11
10
 
@@ -25,15 +24,6 @@ class SchemaFields(ABC):
25
24
  pass
26
25
 
27
26
 
28
- @deprecated("Use custom schema fields instead")
29
- @dataclass
30
- class DataclassFields(SchemaFields): # pragma: no cover
31
- source: Any
32
-
33
- def readable(self) -> JsonDict:
34
- return JsonDict(self.source.__annotations__)
35
-
36
-
37
27
  @dataclass
38
28
  class RestfulSchema:
39
29
  name: RestfulName
@@ -43,6 +33,7 @@ class RestfulSchema:
43
33
  schema = self._schema_for("", self.fields.readable())
44
34
  create_schema = self._schema_for("Create", self.fields.writable())
45
35
  self._schema_for("Update", self.fields.editable())
36
+ replace_schema = self._schema_for("Replace", self.fields.readable())
46
37
  update_many_item = self._schema_for(
47
38
  "UpdateManyItem", self.fields.editable().merge(self.fields.id())
48
39
  )
@@ -64,6 +55,9 @@ class RestfulSchema:
64
55
  "UpdateMany",
65
56
  JsonDict({self.name.plural: List[update_many_item]}),
66
57
  )
58
+ self._schema_for(
59
+ "ReplaceMany", JsonDict({self.name.plural: List[replace_schema]})
60
+ )
67
61
 
68
62
  def _schema_for(self, action: str, fields: dict[str, Any]) -> type[BaseModel]:
69
63
  if action not in self.schemas:
@@ -139,3 +133,19 @@ class RestfulSchema:
139
133
  return [dict(item) for item in request.model_dump()[self.name.plural]]
140
134
 
141
135
  return _
136
+
137
+ def for_replace_one(self) -> Callable[[BaseModel], dict[str, Any]]:
138
+ schema = self.schemas["Replace"]
139
+
140
+ def _(request: schema) -> dict[str, Any]:
141
+ return request.model_dump()
142
+
143
+ return _
144
+
145
+ def for_replace_many(self) -> Callable[[BaseModel], Iterable[dict[str, Any]]]:
146
+ schema = self.schemas["ReplaceMany"]
147
+
148
+ def _(request: schema) -> Iterable[dict[str, Any]]:
149
+ return [dict(item) for item in request.model_dump()[self.name.plural]]
150
+
151
+ return _
@@ -2,7 +2,7 @@ from abc import ABC
2
2
  from dataclasses import dataclass, field
3
3
  from typing import Any, Dict, Generic, Iterable, Self, TypeVar
4
4
 
5
- from apexdevkit.formatter import DataclassFormatter, Formatter
5
+ from apexdevkit.formatter import Formatter
6
6
  from apexdevkit.repository.interface import Repository
7
7
 
8
8
  RawItem = dict[str, Any]
@@ -36,6 +36,12 @@ class RestfulService(ABC): # pragma: no cover
36
36
  def update_many(self, items: RawCollectionWithId) -> RawCollection:
37
37
  raise NotImplementedError(self.update_many.__name__)
38
38
 
39
+ def replace_one(self, item: RawItem) -> RawItem:
40
+ raise NotImplementedError(self.replace_one.__name__)
41
+
42
+ def replace_many(self, items: RawCollection) -> RawCollection:
43
+ raise NotImplementedError(self.replace_many.__name__)
44
+
39
45
  def delete_one(self, item_id: str) -> None:
40
46
  raise NotImplementedError(self.delete_one.__name__)
41
47
 
@@ -45,15 +51,9 @@ ItemT = TypeVar("ItemT")
45
51
 
46
52
  @dataclass
47
53
  class RestfulRepositoryBuilder(Generic[ItemT]):
48
- resource: type[ItemT] | None = field(init=False, default=None)
49
- formatter: Formatter[ItemT] | None = field(init=False, default=None)
54
+ formatter: Formatter[ItemT] = field(init=False)
50
55
  repository: Repository[Any, ItemT] = field(init=False)
51
56
 
52
- def with_resource(self, resource: type[ItemT]) -> Self:
53
- self.resource = resource
54
-
55
- return self
56
-
57
57
  def with_formatter(self, formatter: Formatter[ItemT]) -> Self:
58
58
  self.formatter = formatter
59
59
 
@@ -65,11 +65,6 @@ class RestfulRepositoryBuilder(Generic[ItemT]):
65
65
  return self
66
66
 
67
67
  def build(self) -> RestfulService:
68
- if not self.formatter and self.resource:
69
- self.with_formatter(DataclassFormatter(self.resource))
70
-
71
- assert self.formatter, "Must provide either resource or formatter"
72
-
73
68
  return _RestfulNestedRepository(self.formatter, self.repository)
74
69
 
75
70
 
@@ -115,5 +110,15 @@ class _RestfulNestedRepository(RestfulService, Generic[ItemT]):
115
110
 
116
111
  return [self.formatter.dump(item) for item in updates]
117
112
 
113
+ def replace_one(self, item: RawItem) -> RawItem:
114
+ self.repository.update(self.formatter.load(item))
115
+
116
+ return item
117
+
118
+ def replace_many(self, items: RawCollection) -> RawCollection:
119
+ self.repository.update_many([self.formatter.load(item) for item in items])
120
+
121
+ return items
122
+
118
123
  def delete_one(self, item_id: str) -> None:
119
124
  self.repository.delete(item_id)
@@ -19,6 +19,9 @@ class RestResource:
19
19
  def create_one(self) -> CreateOne:
20
20
  return CreateOne(self.name, self.http)
21
21
 
22
+ def create_many(self) -> CreateMany:
23
+ return CreateMany(self.name, self.http)
24
+
22
25
  def read_one(self) -> ReadOne:
23
26
  return ReadOne(self.name, self.http)
24
27
 
@@ -31,8 +34,11 @@ class RestResource:
31
34
  def update_many(self) -> UpdateMany:
32
35
  return UpdateMany(self.name, self.http)
33
36
 
34
- def create_many(self) -> CreateMany:
35
- return CreateMany(self.name, self.http)
37
+ def replace_one(self) -> ReplaceOne:
38
+ return ReplaceOne(self.name, self.http)
39
+
40
+ def replace_many(self) -> ReplaceMany:
41
+ return ReplaceMany(self.name, self.http)
36
42
 
37
43
  def delete_one(self) -> DeleteOne:
38
44
  return DeleteOne(self.name, self.http)
@@ -182,6 +188,20 @@ class UpdateOne(RestRequest):
182
188
  return self
183
189
 
184
190
 
191
+ @dataclass
192
+ class ReplaceOne(RestRequest):
193
+ data: JsonDict = field(init=False)
194
+
195
+ @cached_property
196
+ def response(self) -> httpx.Response:
197
+ return self.http.put(self.resource + "", json=dict(self.data))
198
+
199
+ def from_data(self, value: JsonDict) -> Self:
200
+ self.data = value
201
+
202
+ return self
203
+
204
+
185
205
  @dataclass
186
206
  class CreateMany(RestRequest):
187
207
  data: list[JsonDict] = field(default_factory=list)
@@ -222,6 +242,26 @@ class UpdateMany(RestRequest):
222
242
  return self.from_data(value)
223
243
 
224
244
 
245
+ @dataclass
246
+ class ReplaceMany(RestRequest):
247
+ data: list[JsonDict] = field(default_factory=list)
248
+
249
+ @cached_property
250
+ def response(self) -> httpx.Response:
251
+ return self.http.put(
252
+ self.resource + "batch",
253
+ json={self.resource.plural: [dict(data) for data in self.data]},
254
+ )
255
+
256
+ def from_data(self, value: JsonDict) -> Self:
257
+ self.data.append(value)
258
+
259
+ return self
260
+
261
+ def and_data(self, value: JsonDict) -> Self:
262
+ return self.from_data(value)
263
+
264
+
225
265
  @dataclass
226
266
  class DeleteOne(RestRequest):
227
267
  item_id: str = field(init=False)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.5.14"
3
+ version = "1.5.16"
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