apexdevkit 1.14.2__tar.gz → 1.14.4__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.14.2 → apexdevkit-1.14.4}/PKG-INFO +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/builder.py +4 -2
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/dependable.py +20 -22
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/resource.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/response.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/schema.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/service.py +18 -14
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fluent.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/http/fake.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/http/httpx.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/connector.py +3 -3
- apexdevkit-1.14.4/apexdevkit/repository/database.py +94 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/mongo.py +1 -1
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/sqlite.py +1 -1
- apexdevkit-1.14.4/apexdevkit/synchronization.py +47 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/testing/database.py +6 -6
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/testing/fake.py +2 -2
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/testing/rest.py +3 -3
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/pyproject.toml +1 -1
- apexdevkit-1.14.2/apexdevkit/repository/database.py +0 -90
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/LICENSE +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/README.md +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/__init__.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/annotation/__init__.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/annotation/deprecate.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/environment.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/error.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/__init__.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/docs.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/fastapi/router.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/formatter.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/http/__init__.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/http/fluent.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/http/json.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/http/url.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/key_fn.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/py.typed +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/__init__.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/base.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/decorator.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/in_memory.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/repository/interface.py +0 -0
- {apexdevkit-1.14.2 → apexdevkit-1.14.4}/apexdevkit/testing/__init__.py +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import ABC, abstractmethod
|
|
2
4
|
from dataclasses import dataclass, field
|
|
3
5
|
from typing import Any, Self
|
|
@@ -53,12 +55,12 @@ class RestfulServiceBuilder(ABC):
|
|
|
53
55
|
parent_id: str = field(init=False)
|
|
54
56
|
user: Any = field(init=False)
|
|
55
57
|
|
|
56
|
-
def with_user(self, user: Any) ->
|
|
58
|
+
def with_user(self, user: Any) -> RestfulServiceBuilder:
|
|
57
59
|
self.user = user
|
|
58
60
|
|
|
59
61
|
return self
|
|
60
62
|
|
|
61
|
-
def with_parent(self, identity: str) ->
|
|
63
|
+
def with_parent(self, identity: str) -> RestfulServiceBuilder:
|
|
62
64
|
self.parent_id = identity
|
|
63
65
|
|
|
64
66
|
return self
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Annotated, Any, Callable, Protocol
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Annotated, Any, Callable, Protocol
|
|
3
3
|
|
|
4
4
|
from fastapi import Depends, Path
|
|
5
5
|
from fastapi.requests import Request
|
|
@@ -23,7 +23,7 @@ class _Dependency(Protocol):
|
|
|
23
23
|
pass
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
@dataclass
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
27
|
class ServiceDependency:
|
|
28
28
|
dependency: _Dependency
|
|
29
29
|
|
|
@@ -36,7 +36,7 @@ class ServiceDependency:
|
|
|
36
36
|
return Annotated[RestfulService, Depends(_)]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
@dataclass
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
40
|
class ParentDependency:
|
|
41
41
|
parent: RestfulName
|
|
42
42
|
dependency: _Dependency
|
|
@@ -56,7 +56,7 @@ class ParentDependency:
|
|
|
56
56
|
return Annotated[RestfulServiceBuilder, Depends(_)]
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
@dataclass
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
60
|
class UserDependency:
|
|
61
61
|
extract_user: Callable[..., Any]
|
|
62
62
|
dependency: _Dependency
|
|
@@ -71,7 +71,7 @@ class UserDependency:
|
|
|
71
71
|
return Annotated[RestfulServiceBuilder, Depends(_)]
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
@dataclass
|
|
74
|
+
@dataclass(frozen=True)
|
|
75
75
|
class InfraDependency:
|
|
76
76
|
infra: RestfulServiceBuilder
|
|
77
77
|
|
|
@@ -82,26 +82,24 @@ class InfraDependency:
|
|
|
82
82
|
return Annotated[RestfulServiceBuilder, Depends(_)]
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
@dataclass
|
|
85
|
+
@dataclass(frozen=True)
|
|
86
86
|
class DependableBuilder:
|
|
87
|
-
dependency: _Dependency =
|
|
87
|
+
dependency: _Dependency | None = None
|
|
88
88
|
|
|
89
|
-
def from_infra(self, value: RestfulServiceBuilder) ->
|
|
90
|
-
|
|
89
|
+
def from_infra(self, value: RestfulServiceBuilder) -> "DependableBuilder":
|
|
90
|
+
return DependableBuilder(InfraDependency(value))
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
def with_parent(self, value: RestfulName) -> "DependableBuilder":
|
|
93
|
+
if self.dependency is None:
|
|
94
|
+
raise RuntimeError("RestfulServiceBuilder type not set")
|
|
95
|
+
return DependableBuilder(ParentDependency(value, self.dependency))
|
|
93
96
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return dependable
|
|
99
|
-
|
|
100
|
-
def with_user(self, extract_user: Callable[..., Any]) -> Self:
|
|
101
|
-
dependable = DependableBuilder()
|
|
102
|
-
dependable.dependency = UserDependency(extract_user, self.dependency)
|
|
103
|
-
|
|
104
|
-
return dependable
|
|
97
|
+
def with_user(self, extract_user: Callable[..., Any]) -> "DependableBuilder":
|
|
98
|
+
if self.dependency is None:
|
|
99
|
+
raise RuntimeError("RestfulServiceBuilder type not set")
|
|
100
|
+
return DependableBuilder(UserDependency(extract_user, self.dependency))
|
|
105
101
|
|
|
106
102
|
def as_dependable(self) -> type[RestfulService]:
|
|
103
|
+
if self.dependency is None:
|
|
104
|
+
raise RuntimeError("RestfulServiceBuilder type not set")
|
|
107
105
|
return ServiceDependency(self.dependency).as_dependable()
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import ABC
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from typing import Any, Dict, Generic, Iterable,
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, Generic, Iterable, TypeVar
|
|
4
6
|
|
|
5
7
|
from apexdevkit.formatter import Formatter
|
|
6
8
|
from apexdevkit.repository.decorator import BatchRepositoryDecorator
|
|
@@ -50,26 +52,28 @@ class RestfulService(ABC): # pragma: no cover
|
|
|
50
52
|
ItemT = TypeVar("ItemT")
|
|
51
53
|
|
|
52
54
|
|
|
53
|
-
@dataclass
|
|
55
|
+
@dataclass(frozen=True)
|
|
54
56
|
class RestfulRepositoryBuilder(Generic[ItemT]):
|
|
55
|
-
formatter: Formatter[dict[str, Any], ItemT] =
|
|
56
|
-
repository: Repository[ItemT] =
|
|
57
|
-
|
|
58
|
-
def with_formatter(self, formatter: Formatter[dict[str, Any], ItemT]) -> Self:
|
|
59
|
-
self.formatter = formatter
|
|
60
|
-
|
|
61
|
-
return self
|
|
57
|
+
formatter: Formatter[dict[str, Any], ItemT] | None = None
|
|
58
|
+
repository: Repository[ItemT] | None = None
|
|
62
59
|
|
|
63
|
-
def
|
|
64
|
-
self
|
|
60
|
+
def with_formatter(
|
|
61
|
+
self, formatter: Formatter[dict[str, Any], ItemT]
|
|
62
|
+
) -> RestfulRepositoryBuilder[ItemT]:
|
|
63
|
+
return RestfulRepositoryBuilder[ItemT](formatter, self.repository)
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
def with_repository(
|
|
66
|
+
self, repository: Repository[ItemT]
|
|
67
|
+
) -> RestfulRepositoryBuilder[ItemT]:
|
|
68
|
+
return RestfulRepositoryBuilder[ItemT](self.formatter, repository)
|
|
67
69
|
|
|
68
70
|
def build(self) -> RestfulService:
|
|
71
|
+
if self.formatter is None or self.repository is None:
|
|
72
|
+
raise RuntimeError("Formatter or repository not provided.")
|
|
69
73
|
return _RestfulRepository(self.formatter, self.repository)
|
|
70
74
|
|
|
71
75
|
|
|
72
|
-
@dataclass
|
|
76
|
+
@dataclass(frozen=True)
|
|
73
77
|
class _RestfulRepository(RestfulService, Generic[ItemT]):
|
|
74
78
|
formatter: Formatter[dict[str, Any], ItemT]
|
|
75
79
|
repository: Repository[ItemT]
|
|
@@ -8,7 +8,7 @@ from pymongo import MongoClient
|
|
|
8
8
|
from apexdevkit.repository import Connection
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
@dataclass
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
12
|
class SqliteFileConnector:
|
|
13
13
|
dsn: str
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ class SqliteFileConnector:
|
|
|
19
19
|
return connection
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
@dataclass
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
23
|
class SqliteInMemoryConnector:
|
|
24
24
|
dsn: str = ":memory:"
|
|
25
25
|
|
|
@@ -34,7 +34,7 @@ class SqliteInMemoryConnector:
|
|
|
34
34
|
return connection
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
@dataclass
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
38
|
class PyMongoConnector:
|
|
39
39
|
dsn: str
|
|
40
40
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, ContextManager, Iterable, Protocol
|
|
6
|
+
|
|
7
|
+
_RawData = dict[str, Any]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class Database:
|
|
12
|
+
connector: Connector
|
|
13
|
+
|
|
14
|
+
def execute(self, command: DatabaseCommand) -> _CommandExecutor:
|
|
15
|
+
return Database._CommandExecutor(self.connector, command)
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class _CommandExecutor:
|
|
19
|
+
connector: Connector
|
|
20
|
+
command: DatabaseCommand
|
|
21
|
+
|
|
22
|
+
def fetch_none(self) -> None:
|
|
23
|
+
with self.connector.connect() as connection:
|
|
24
|
+
cursor: Cursor = connection.cursor()
|
|
25
|
+
cursor.execute(self.command.value, self.command.payload)
|
|
26
|
+
cursor.close()
|
|
27
|
+
|
|
28
|
+
def fetch_one(self) -> _RawData:
|
|
29
|
+
with self.connector.connect() as connection:
|
|
30
|
+
cursor: Cursor = connection.cursor()
|
|
31
|
+
cursor.execute(self.command.value, self.command.payload)
|
|
32
|
+
raw = cursor.fetchone()
|
|
33
|
+
cursor.close()
|
|
34
|
+
|
|
35
|
+
return dict(raw or {})
|
|
36
|
+
|
|
37
|
+
def fetch_all(self) -> Iterable[_RawData]:
|
|
38
|
+
with self.connector.connect() as connection:
|
|
39
|
+
cursor: Cursor = connection.cursor()
|
|
40
|
+
cursor.execute(self.command.value, self.command.payload)
|
|
41
|
+
raw = cursor.fetchall()
|
|
42
|
+
cursor.close()
|
|
43
|
+
|
|
44
|
+
return [dict(raw or {}) for raw in raw]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Connector(Protocol): # pragma: no cover
|
|
48
|
+
def connect(self) -> ContextManager[Connection]:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Connection(Protocol): # pragma: no cover
|
|
53
|
+
def cursor(self) -> Cursor:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Cursor(Protocol): # pragma: no cover
|
|
58
|
+
def execute(self, *args: Any, **kwargs: Any) -> Any:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def executemany(self, *args: Any, **kwargs: Any) -> Any:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
def fetchone(self, *args: Any, **kwargs: Any) -> Any:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
def fetchall(self, *args: Any, **kwargs: Any) -> Any:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def close(self) -> None:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(frozen=True)
|
|
75
|
+
class DatabaseCommand:
|
|
76
|
+
value: str = field(default_factory=str)
|
|
77
|
+
payload: _RawData | list[_RawData] = field(default_factory=dict)
|
|
78
|
+
|
|
79
|
+
def with_data(
|
|
80
|
+
self, value: _RawData | None = None, **fields: Any
|
|
81
|
+
) -> DatabaseCommand:
|
|
82
|
+
assert isinstance(self.payload, dict)
|
|
83
|
+
|
|
84
|
+
payload = deepcopy(self.payload)
|
|
85
|
+
payload.update(value or {})
|
|
86
|
+
payload.update(fields)
|
|
87
|
+
|
|
88
|
+
return DatabaseCommand(self.value, payload)
|
|
89
|
+
|
|
90
|
+
def with_collection(self, value: list[_RawData]) -> DatabaseCommand:
|
|
91
|
+
return DatabaseCommand(self.value, value)
|
|
92
|
+
|
|
93
|
+
def __str__(self) -> str: # pragma: no cover
|
|
94
|
+
return self.value
|
|
@@ -9,7 +9,7 @@ from apexdevkit.repository import Database, DatabaseCommand, RepositoryBase
|
|
|
9
9
|
from apexdevkit.repository.interface import ItemT
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@dataclass
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
13
|
class SqliteRepository(RepositoryBase[ItemT]):
|
|
14
14
|
db: Database
|
|
15
15
|
table: SqlTable[ItemT]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Generic, Iterable, Iterator, Protocol, TypeVar
|
|
5
|
+
|
|
6
|
+
ItemT = TypeVar("ItemT")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class Sync(Generic[ItemT]):
|
|
11
|
+
source: _Source[ItemT]
|
|
12
|
+
target: _Target[ItemT]
|
|
13
|
+
|
|
14
|
+
def sync(self) -> None:
|
|
15
|
+
items = self.source.value_of(list(self.target))
|
|
16
|
+
self.target.update_many(list(items))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _Source(Protocol[ItemT]):
|
|
20
|
+
def value_of(self, items: Iterable[ItemT]) -> Iterable[ItemT]:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _Target(Protocol[ItemT]):
|
|
25
|
+
def __iter__(self) -> Iterator[ItemT]:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def update_many(self, items: Iterable[ItemT]) -> None:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class FullSync(Generic[ItemT]):
|
|
34
|
+
source: Iterable[ItemT]
|
|
35
|
+
target: _FullTarget[ItemT]
|
|
36
|
+
|
|
37
|
+
def sync(self) -> None:
|
|
38
|
+
items = list(self.source)
|
|
39
|
+
self.target.update_many(items)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
ItemR = TypeVar("ItemR", contravariant=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _FullTarget(Protocol[ItemR]):
|
|
46
|
+
def update_many(self, items: Iterable[ItemR]) -> None:
|
|
47
|
+
pass
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from contextlib import nullcontext
|
|
2
4
|
from dataclasses import dataclass, field
|
|
3
5
|
from typing import Any, ContextManager, Self
|
|
@@ -5,15 +7,13 @@ from typing import Any, ContextManager, Self
|
|
|
5
7
|
from apexdevkit.repository import DatabaseCommand
|
|
6
8
|
|
|
7
9
|
|
|
8
|
-
@dataclass
|
|
10
|
+
@dataclass(frozen=True)
|
|
9
11
|
class FakeConnector:
|
|
10
12
|
commands: list[tuple[str, Any]] = field(default_factory=list)
|
|
11
|
-
results: list[Any] = field(
|
|
12
|
-
|
|
13
|
-
def with_result(self, values: Any) -> Self:
|
|
14
|
-
self.results = [values, *self.results]
|
|
13
|
+
results: list[Any] = field(default_factory=list)
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
def with_result(self, values: Any) -> FakeConnector:
|
|
16
|
+
return FakeConnector(self.commands, [values, *self.results])
|
|
17
17
|
|
|
18
18
|
def execute(self, command: str, data: Any) -> None:
|
|
19
19
|
self.commands.append((command, data))
|
|
@@ -9,7 +9,7 @@ from apexdevkit.http import JsonDict
|
|
|
9
9
|
ItemT = TypeVar("ItemT")
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@dataclass
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
13
|
class Fake:
|
|
14
14
|
faker: Faker = field(default_factory=Faker)
|
|
15
15
|
|
|
@@ -50,7 +50,7 @@ class Fake:
|
|
|
50
50
|
return bool(self.faker.boolean())
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
@dataclass
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
54
|
class FakeResource(Generic[ItemT]):
|
|
55
55
|
item_type: Type[ItemT] = field()
|
|
56
56
|
fake: Fake = field(default_factory=Fake)
|
|
@@ -8,7 +8,7 @@ from apexdevkit.http import Http, HttpUrl, JsonDict
|
|
|
8
8
|
from apexdevkit.http.fluent import HttpMethod, HttpResponse
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
@dataclass
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
12
|
class RestResource:
|
|
13
13
|
http: Http
|
|
14
14
|
name: RestfulName
|
|
@@ -95,13 +95,13 @@ class RestResource:
|
|
|
95
95
|
)
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
@dataclass
|
|
98
|
+
@dataclass(frozen=True)
|
|
99
99
|
class RestCollection(RestResource):
|
|
100
100
|
def sub_resource(self, name: str) -> RestItem:
|
|
101
101
|
return RestItem(self.http.with_endpoint(self.name.plural), RestfulName(name))
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
@dataclass
|
|
104
|
+
@dataclass(frozen=True)
|
|
105
105
|
class RestItem(RestResource):
|
|
106
106
|
def sub_resource(self, name: str) -> RestItem:
|
|
107
107
|
return RestItem(self.http.with_endpoint(self.name.singular), RestfulName(name))
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import Any, ContextManager, Iterable, Protocol, Self
|
|
5
|
-
|
|
6
|
-
_RawData = dict[str, Any]
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclass
|
|
10
|
-
class Database:
|
|
11
|
-
connector: Connector
|
|
12
|
-
|
|
13
|
-
command: DatabaseCommand = field(init=False)
|
|
14
|
-
|
|
15
|
-
def execute(self, command: DatabaseCommand) -> Self:
|
|
16
|
-
self.command = command
|
|
17
|
-
|
|
18
|
-
return self
|
|
19
|
-
|
|
20
|
-
def fetch_none(self) -> None:
|
|
21
|
-
with self.connector.connect() as connection:
|
|
22
|
-
cursor: Cursor = connection.cursor()
|
|
23
|
-
cursor.execute(self.command.value, self.command.payload)
|
|
24
|
-
cursor.close()
|
|
25
|
-
|
|
26
|
-
def fetch_one(self) -> _RawData:
|
|
27
|
-
with self.connector.connect() as connection:
|
|
28
|
-
cursor: Cursor = connection.cursor()
|
|
29
|
-
cursor.execute(self.command.value, self.command.payload)
|
|
30
|
-
raw = cursor.fetchone()
|
|
31
|
-
cursor.close()
|
|
32
|
-
|
|
33
|
-
return dict(raw or {})
|
|
34
|
-
|
|
35
|
-
def fetch_all(self) -> Iterable[_RawData]:
|
|
36
|
-
with self.connector.connect() as connection:
|
|
37
|
-
cursor: Cursor = connection.cursor()
|
|
38
|
-
cursor.execute(self.command.value, self.command.payload)
|
|
39
|
-
raw = cursor.fetchall()
|
|
40
|
-
cursor.close()
|
|
41
|
-
|
|
42
|
-
return [dict(raw or {}) for raw in raw]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class Connector(Protocol): # pragma: no cover
|
|
46
|
-
def connect(self) -> ContextManager[Connection]:
|
|
47
|
-
pass
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class Connection(Protocol): # pragma: no cover
|
|
51
|
-
def cursor(self) -> Cursor:
|
|
52
|
-
pass
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class Cursor(Protocol): # pragma: no cover
|
|
56
|
-
def execute(self, *args: Any, **kwargs: Any) -> Any:
|
|
57
|
-
pass
|
|
58
|
-
|
|
59
|
-
def executemany(self, *args: Any, **kwargs: Any) -> Any:
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
def fetchone(self, *args: Any, **kwargs: Any) -> Any:
|
|
63
|
-
pass
|
|
64
|
-
|
|
65
|
-
def fetchall(self, *args: Any, **kwargs: Any) -> Any:
|
|
66
|
-
pass
|
|
67
|
-
|
|
68
|
-
def close(self) -> None:
|
|
69
|
-
pass
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass
|
|
73
|
-
class DatabaseCommand:
|
|
74
|
-
value: str
|
|
75
|
-
payload: _RawData | list[_RawData] = field(init=False, default_factory=dict)
|
|
76
|
-
|
|
77
|
-
def with_data(self, value: _RawData | None = None, **fields: Any) -> Self:
|
|
78
|
-
assert isinstance(self.payload, dict)
|
|
79
|
-
self.payload.update(value or {})
|
|
80
|
-
self.payload.update(fields)
|
|
81
|
-
|
|
82
|
-
return self
|
|
83
|
-
|
|
84
|
-
def with_collection(self, value: list[_RawData]) -> Self:
|
|
85
|
-
self.payload = value
|
|
86
|
-
|
|
87
|
-
return self
|
|
88
|
-
|
|
89
|
-
def __str__(self) -> str: # pragma: no cover
|
|
90
|
-
return self.value
|
|
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
|