apexdevkit 1.23.4__tar.gz → 1.23.6__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.23.4 → apexdevkit-1.23.6}/PKG-INFO +1 -1
- apexdevkit-1.23.6/apexdevkit/date.py +79 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/resource.py +3 -5
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/router.py +4 -1
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/schema.py +13 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/service.py +2 -2
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/pyproject.toml +1 -1
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/LICENSE +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/README.md +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/annotation/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/annotation/deprecate.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/environment.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/error.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/builder.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/dependable.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/docs.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/name.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/request.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fastapi/response.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/fluent.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/formatter.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/fake.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/fluent.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/httpx/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/httpx/client.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/httpx/hooks.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/json.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/http/url.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/id.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/key_fn.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/py.typed +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/query/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/query/generator.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/query/query.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/base.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/connector.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/database.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/decorator.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/in_memory.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/interface.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/mssql.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/repository.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/sql.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/repository/sqlite.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/server.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/synchronization.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/testing/__init__.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/testing/database.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/testing/fake.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/testing/rest.py +0 -0
- {apexdevkit-1.23.4 → apexdevkit-1.23.6}/apexdevkit/value.py +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
from zoneinfo import ZoneInfo
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class DateTime:
|
|
10
|
+
timestamp_ms: int
|
|
11
|
+
|
|
12
|
+
# Date in format of "yyyy-mm-dd"
|
|
13
|
+
def as_date(self, time_zone: ZoneInfo | None = None) -> str:
|
|
14
|
+
if time_zone is None:
|
|
15
|
+
time_zone = ZoneInfo("UTC")
|
|
16
|
+
|
|
17
|
+
dt = datetime.fromtimestamp(self.as_timestamp(), tz=UTC)
|
|
18
|
+
local_dt = dt.astimezone(time_zone)
|
|
19
|
+
return local_dt.strftime("%Y-%m-%d")
|
|
20
|
+
|
|
21
|
+
# Date in format of "yyyy-mm-ddTHH:MM:SS"
|
|
22
|
+
def as_date_time(self, time_zone: ZoneInfo | None = None) -> str:
|
|
23
|
+
if time_zone is None:
|
|
24
|
+
time_zone = ZoneInfo("UTC")
|
|
25
|
+
|
|
26
|
+
dt = datetime.fromtimestamp(self.as_timestamp(), tz=UTC)
|
|
27
|
+
local_dt = dt.astimezone(time_zone)
|
|
28
|
+
return local_dt.strftime("%Y-%m-%dT%H:%M:%S")
|
|
29
|
+
|
|
30
|
+
def as_timestamp(self) -> float:
|
|
31
|
+
return float(self.timestamp_ms) / 1000.0
|
|
32
|
+
|
|
33
|
+
def as_timestamp_ms(self) -> int:
|
|
34
|
+
return self.timestamp_ms
|
|
35
|
+
|
|
36
|
+
# Date in format of "yyyy-mm-dd"
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_date(cls, date: str, time_zone: ZoneInfo | None = None) -> DateTime:
|
|
39
|
+
if time_zone is None:
|
|
40
|
+
time_zone = ZoneInfo("UTC")
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
dt = datetime.strptime(date, "%Y-%m-%d")
|
|
44
|
+
dt = dt.replace(tzinfo=time_zone)
|
|
45
|
+
timestamp_ms = int(dt.timestamp() * 1000)
|
|
46
|
+
return cls(timestamp_ms=timestamp_ms)
|
|
47
|
+
|
|
48
|
+
except ValueError as e:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"Invalid date format: '{date}'. Expected 'yyyy-mm-dd'."
|
|
51
|
+
) from e
|
|
52
|
+
|
|
53
|
+
# Date in format of "yyyy-mm-ddTHH:MM:SS"
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_date_time(
|
|
56
|
+
cls, date_time: str, time_zone: ZoneInfo | None = None
|
|
57
|
+
) -> DateTime:
|
|
58
|
+
if time_zone is None:
|
|
59
|
+
time_zone = ZoneInfo("UTC")
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
dt = datetime.strptime(date_time, "%Y-%m-%dT%H:%M:%S")
|
|
63
|
+
dt = dt.replace(tzinfo=time_zone)
|
|
64
|
+
timestamp_ms = int(dt.timestamp() * 1000)
|
|
65
|
+
return cls(timestamp_ms=timestamp_ms)
|
|
66
|
+
except ValueError as e:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Invalid datetime format: '{date_time}'."
|
|
69
|
+
f" Expected 'yyyy-mm-ddTHH:MM:SS'."
|
|
70
|
+
) from e
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def from_timestamp(cls, timestamp: float) -> DateTime:
|
|
74
|
+
timestamp_ms = int(timestamp * 1000)
|
|
75
|
+
return cls(timestamp_ms=timestamp_ms)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_timestamp_ms(cls, timestamp_ms: int) -> DateTime:
|
|
79
|
+
return cls(timestamp_ms=timestamp_ms)
|
|
@@ -79,12 +79,10 @@ class RestfulResource:
|
|
|
79
79
|
|
|
80
80
|
return endpoint
|
|
81
81
|
|
|
82
|
-
def filter_with(self, Service) -> _Endpoint: # type: ignore
|
|
83
|
-
def endpoint(service: Service, options:
|
|
82
|
+
def filter_with(self, Service, QueryOptions) -> _Endpoint: # type: ignore
|
|
83
|
+
def endpoint(service: Service, options: QueryOptions) -> _Response:
|
|
84
84
|
try:
|
|
85
|
-
return self.response.found_many(
|
|
86
|
-
list(service.filter_with(options.to_query_options()))
|
|
87
|
-
)
|
|
85
|
+
return self.response.found_many(list(service.filter_with(options)))
|
|
88
86
|
except ForbiddenError as e:
|
|
89
87
|
return JSONResponse(self.response.forbidden(e), 403)
|
|
90
88
|
|
|
@@ -171,7 +171,10 @@ class RestfulRouter:
|
|
|
171
171
|
) -> Self:
|
|
172
172
|
self.router.add_api_route(
|
|
173
173
|
"/filter",
|
|
174
|
-
self.resource.filter_with(
|
|
174
|
+
self.resource.filter_with(
|
|
175
|
+
Service=self._resolve(dependency),
|
|
176
|
+
QueryOptions=Annotated[RawItem, Depends(self.schema.for_filters())],
|
|
177
|
+
),
|
|
175
178
|
methods=["POST"],
|
|
176
179
|
status_code=200,
|
|
177
180
|
responses={},
|
|
@@ -8,6 +8,7 @@ from pydantic import BaseModel, create_model
|
|
|
8
8
|
|
|
9
9
|
from apexdevkit.fastapi.name import RestfulName
|
|
10
10
|
from apexdevkit.fluent import FluentDict
|
|
11
|
+
from apexdevkit.http import JsonDict
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class SchemaFields(ABC):
|
|
@@ -20,6 +21,9 @@ class SchemaFields(ABC):
|
|
|
20
21
|
def editable(self) -> FluentDict[type]:
|
|
21
22
|
return self.readable().drop("id")
|
|
22
23
|
|
|
24
|
+
def filters(self) -> FluentDict[type]:
|
|
25
|
+
return JsonDict()
|
|
26
|
+
|
|
23
27
|
@abstractmethod
|
|
24
28
|
def readable(self) -> FluentDict[type]: # pragma: no cover
|
|
25
29
|
pass
|
|
@@ -38,6 +42,7 @@ class RestfulSchema:
|
|
|
38
42
|
update_many_item = self._schema_for(
|
|
39
43
|
"UpdateManyItem", self.fields.editable().merge(self.fields.id())
|
|
40
44
|
)
|
|
45
|
+
self._schema_for("Filter", self.fields.filters())
|
|
41
46
|
|
|
42
47
|
self._schema_for("Item", {self.name.singular: schema})
|
|
43
48
|
self._schema_for("Collection", {self.name.plural: list[schema], "count": int})
|
|
@@ -130,6 +135,14 @@ class RestfulSchema:
|
|
|
130
135
|
|
|
131
136
|
return _
|
|
132
137
|
|
|
138
|
+
def for_filters(self) -> Callable[[BaseModel], dict[str, Any]]:
|
|
139
|
+
schema = self.schemas["Filter"]
|
|
140
|
+
|
|
141
|
+
def _(request: schema) -> dict[str, Any]:
|
|
142
|
+
return request.model_dump()
|
|
143
|
+
|
|
144
|
+
return _
|
|
145
|
+
|
|
133
146
|
|
|
134
147
|
@dataclass(frozen=True)
|
|
135
148
|
class Schema:
|
|
@@ -6,7 +6,7 @@ from functools import cached_property
|
|
|
6
6
|
from typing import Any, Generic, TypeVar
|
|
7
7
|
|
|
8
8
|
from apexdevkit.formatter import Formatter
|
|
9
|
-
from apexdevkit.query.query import FooterOptions,
|
|
9
|
+
from apexdevkit.query.query import FooterOptions, Summary
|
|
10
10
|
from apexdevkit.repository.decorator import BruteForceBatch
|
|
11
11
|
from apexdevkit.repository.interface import Repository
|
|
12
12
|
|
|
@@ -35,7 +35,7 @@ class RestfulService: # pragma: no cover
|
|
|
35
35
|
def read_many(self, **params: Any) -> RawCollection:
|
|
36
36
|
raise NotImplementedError(self.read_many.__name__)
|
|
37
37
|
|
|
38
|
-
def filter_with(self, options:
|
|
38
|
+
def filter_with(self, options: RawItem) -> RawCollection:
|
|
39
39
|
raise NotImplementedError(self.filter_with.__name__)
|
|
40
40
|
|
|
41
41
|
def read_all(self) -> RawCollection:
|
|
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
|
|
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
|