apexdevkit 1.5.15__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.15 → apexdevkit-1.5.16}/PKG-INFO +1 -1
  2. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/router.py +101 -14
  3. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/schema.py +20 -10
  4. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/service.py +18 -15
  5. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/testing/rest.py +42 -2
  6. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/pyproject.toml +1 -1
  7. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/LICENSE +0 -0
  8. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/README.md +0 -0
  9. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/__init__.py +0 -0
  10. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/annotation/__init__.py +0 -0
  11. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/annotation/deprecate.py +0 -0
  12. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/error.py +0 -0
  13. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/__init__.py +0 -0
  14. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/builder.py +0 -0
  15. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/dependable.py +0 -0
  16. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/docs.py +0 -0
  17. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/fastapi/response.py +0 -0
  18. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/formatter.py +0 -0
  19. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/http/__init__.py +0 -0
  20. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/http/fake.py +0 -0
  21. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/http/fluent.py +0 -0
  22. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/http/httpx.py +0 -0
  23. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/http/json.py +0 -0
  24. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/http/url.py +0 -0
  25. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/py.typed +0 -0
  26. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/repository/__init__.py +0 -0
  27. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/repository/base.py +0 -0
  28. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/repository/connector.py +0 -0
  29. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/repository/database.py +0 -0
  30. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/repository/in_memory.py +0 -0
  31. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/repository/interface.py +0 -0
  32. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/testing/__init__.py +0 -0
  33. {apexdevkit-1.5.15 → apexdevkit-1.5.16}/apexdevkit/testing/database.py +0 -0
  34. {apexdevkit-1.5.15 → 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.15
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
@@ -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
 
@@ -148,12 +147,6 @@ class RestfulRouter:
148
147
  def item_path(self) -> str:
149
148
  return "/{" + self.id_alias + "}"
150
149
 
151
- @deprecated("Use with_name and with_fields instead")
152
- def with_dataclass(self, value: Any) -> Self: # pragma: no cover
153
- return self.with_name(RestfulName(value.__name__.lower())).with_fields(
154
- DataclassFields(value)
155
- )
156
-
157
150
  def with_name(self, value: RestfulName) -> Self:
158
151
  self.name = value
159
152
 
@@ -169,12 +162,6 @@ class RestfulRouter:
169
162
 
170
163
  return self
171
164
 
172
- @deprecated("Use with_infra instead")
173
- def with_service(self, value: RestfulService) -> Self: # pragma: no cover
174
- self.infra = PreBuiltRestfulService(value)
175
-
176
- return self
177
-
178
165
  def with_infra(self, value: RestfulServiceBuilder) -> Self:
179
166
  self.infra = value
180
167
 
@@ -480,6 +467,106 @@ class RestfulRouter:
480
467
 
481
468
  return endpoint
482
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
+
483
570
  def with_delete_one_endpoint(
484
571
  self,
485
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,8 +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.annotation import deprecated
6
- from apexdevkit.formatter import DataclassFormatter, Formatter
5
+ from apexdevkit.formatter import Formatter
7
6
  from apexdevkit.repository.interface import Repository
8
7
 
9
8
  RawItem = dict[str, Any]
@@ -37,6 +36,12 @@ class RestfulService(ABC): # pragma: no cover
37
36
  def update_many(self, items: RawCollectionWithId) -> RawCollection:
38
37
  raise NotImplementedError(self.update_many.__name__)
39
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
+
40
45
  def delete_one(self, item_id: str) -> None:
41
46
  raise NotImplementedError(self.delete_one.__name__)
42
47
 
@@ -46,16 +51,9 @@ ItemT = TypeVar("ItemT")
46
51
 
47
52
  @dataclass
48
53
  class RestfulRepositoryBuilder(Generic[ItemT]):
49
- resource: type[ItemT] | None = field(init=False, default=None)
50
- formatter: Formatter[ItemT] | None = field(init=False, default=None)
54
+ formatter: Formatter[ItemT] = field(init=False)
51
55
  repository: Repository[Any, ItemT] = field(init=False)
52
56
 
53
- @deprecated("Pass formatter instead")
54
- def with_resource(self, resource: type[ItemT]) -> Self:
55
- self.resource = resource
56
-
57
- return self
58
-
59
57
  def with_formatter(self, formatter: Formatter[ItemT]) -> Self:
60
58
  self.formatter = formatter
61
59
 
@@ -67,11 +65,6 @@ class RestfulRepositoryBuilder(Generic[ItemT]):
67
65
  return self
68
66
 
69
67
  def build(self) -> RestfulService:
70
- if not self.formatter and self.resource:
71
- self.with_formatter(DataclassFormatter(self.resource))
72
-
73
- assert self.formatter, "Must provide either resource or formatter"
74
-
75
68
  return _RestfulNestedRepository(self.formatter, self.repository)
76
69
 
77
70
 
@@ -117,5 +110,15 @@ class _RestfulNestedRepository(RestfulService, Generic[ItemT]):
117
110
 
118
111
  return [self.formatter.dump(item) for item in updates]
119
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
+
120
123
  def delete_one(self, item_id: str) -> None:
121
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.15"
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