apytizer 0.0.1a0__py3-none-any.whl → 0.0.1b2__py3-none-any.whl
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.
- apytizer/__init__.py +2 -12
- apytizer/adapters/__init__.py +2 -3
- apytizer/adapters/transport_adapter.py +91 -0
- apytizer/apis/__init__.py +6 -0
- apytizer/apis/abstract_api.py +36 -0
- apytizer/apis/web_api.py +460 -0
- apytizer/connections/__init__.py +6 -0
- apytizer/connections/abstract_connection.py +28 -0
- apytizer/connections/http_connection.py +431 -0
- apytizer/decorators/__init__.py +5 -5
- apytizer/decorators/caching.py +60 -9
- apytizer/decorators/chunking.py +105 -0
- apytizer/decorators/connection.py +55 -20
- apytizer/decorators/json.py +70 -12
- apytizer/decorators/pagination.py +50 -32
- apytizer/endpoints/__init__.py +6 -0
- apytizer/endpoints/abstract_endpoint.py +38 -0
- apytizer/endpoints/web_endpoint.py +519 -0
- apytizer/engines/__init__.py +6 -0
- apytizer/engines/abstract_engine.py +45 -0
- apytizer/engines/http_engine.py +171 -0
- apytizer/errors.py +34 -0
- apytizer/factories/__init__.py +5 -0
- apytizer/factories/abstract_factory.py +17 -0
- apytizer/http_methods.py +34 -0
- apytizer/managers/__init__.py +12 -0
- apytizer/managers/abstract_manager.py +80 -0
- apytizer/managers/base_manager.py +116 -0
- apytizer/mappers/__init__.py +6 -0
- apytizer/mappers/abstract_mapper.py +48 -0
- apytizer/mappers/base_mapper.py +78 -0
- apytizer/media_types.py +118 -0
- apytizer/models/__init__.py +6 -0
- apytizer/models/abstract_model.py +119 -0
- apytizer/models/base_model.py +85 -0
- apytizer/protocols.py +38 -0
- apytizer/repositories/__init__.py +6 -0
- apytizer/repositories/abstract_repository.py +81 -0
- apytizer/repositories/managed_repository.py +92 -0
- apytizer/routes/__init__.py +6 -0
- apytizer/routes/abstract_route.py +32 -0
- apytizer/routes/base_route.py +138 -0
- apytizer/sessions/__init__.py +33 -0
- apytizer/sessions/abstract_session.py +63 -0
- apytizer/sessions/requests_session.py +125 -0
- apytizer/states/__init__.py +6 -0
- apytizer/states/abstract_state.py +71 -0
- apytizer/states/local_state.py +99 -0
- apytizer/utils/__init__.py +9 -4
- apytizer/utils/caching.py +39 -0
- apytizer/utils/dictionaries.py +376 -0
- apytizer/utils/errors.py +104 -0
- apytizer/utils/iterables.py +91 -0
- apytizer/utils/objects.py +145 -0
- apytizer/utils/strings.py +69 -0
- apytizer/utils/typing.py +29 -0
- apytizer-0.0.1b2.dist-info/METADATA +41 -0
- apytizer-0.0.1b2.dist-info/RECORD +60 -0
- {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b2.dist-info}/WHEEL +1 -2
- apytizer/abstracts/__init__.py +0 -8
- apytizer/abstracts/api.py +0 -147
- apytizer/abstracts/endpoint.py +0 -177
- apytizer/abstracts/model.py +0 -50
- apytizer/abstracts/session.py +0 -39
- apytizer/adapters/transport.py +0 -40
- apytizer/base/__init__.py +0 -8
- apytizer/base/api.py +0 -510
- apytizer/base/endpoint.py +0 -443
- apytizer/base/model.py +0 -119
- apytizer/utils/generate_key.py +0 -18
- apytizer/utils/merge.py +0 -19
- apytizer-0.0.1a0.dist-info/METADATA +0 -27
- apytizer-0.0.1a0.dist-info/RECORD +0 -25
- apytizer-0.0.1a0.dist-info/top_level.txt +0 -1
- {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b2.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/engines/http_engine.py
|
|
3
|
+
"""HTTP Engine Class.
|
|
4
|
+
|
|
5
|
+
This module defines the HTTP engine class implementation.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from collections import ChainMap
|
|
11
|
+
from typing import Any
|
|
12
|
+
from typing import MutableMapping
|
|
13
|
+
from typing import Optional
|
|
14
|
+
from typing import Tuple
|
|
15
|
+
from typing import Type
|
|
16
|
+
from typing import TypeVar
|
|
17
|
+
from typing import Union
|
|
18
|
+
|
|
19
|
+
# Third-Party Imports
|
|
20
|
+
from requests.auth import AuthBase
|
|
21
|
+
from requests.adapters import HTTPAdapter
|
|
22
|
+
|
|
23
|
+
# Local Imports
|
|
24
|
+
from .abstract_engine import AbstractEngine
|
|
25
|
+
from ..connections import HttpConnection
|
|
26
|
+
from ..protocols import Protocol
|
|
27
|
+
from ..protocols import get_protocol
|
|
28
|
+
|
|
29
|
+
__all__ = ["HTTPEngine"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Custom types:
|
|
33
|
+
T = TypeVar("T")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class HTTPEngine(AbstractEngine):
|
|
37
|
+
"""Implements an HTTP engine.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
url: Base URL.
|
|
41
|
+
adapters (optional): Connection adapters. Default ``None``.
|
|
42
|
+
auth (optional): Authentication header. default ``None``.
|
|
43
|
+
cert (optional): Client certificate. Default ``None``.
|
|
44
|
+
headers (optional): Headers to set globally. Default ``None``.
|
|
45
|
+
params (optional): Parameters to set globally. Default ``None``.
|
|
46
|
+
proxies (optional): Protocols mapped to proxy URLs. Default ``None``.
|
|
47
|
+
stream (optional): Whether to stream response content. Default ``False``.
|
|
48
|
+
timeout (optional): How long to wait before timing out. Default ``None``.
|
|
49
|
+
verify (optional): Whether to verify certificate. Default ``True``.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
TypeError: when URL of type other than `str`.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
_connection_cls: Type[HttpConnection] = HttpConnection
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
url: str,
|
|
61
|
+
*,
|
|
62
|
+
adapters: Optional[MutableMapping[Protocol, HTTPAdapter]] = None,
|
|
63
|
+
auth: Optional[Union[AuthBase, Tuple[str, str]]] = None,
|
|
64
|
+
cert: Optional[Union[str, Tuple[str, str]]] = None,
|
|
65
|
+
headers: Optional[MutableMapping[str, str]] = None,
|
|
66
|
+
params: Optional[MutableMapping[str, Any]] = None,
|
|
67
|
+
proxies: Optional[MutableMapping[str, str]] = None,
|
|
68
|
+
stream: Optional[bool] = False,
|
|
69
|
+
timeout: Optional[Union[float, Tuple[float, float]]] = None,
|
|
70
|
+
verify: Optional[bool] = True,
|
|
71
|
+
) -> None:
|
|
72
|
+
self._url = standardize_url(url)
|
|
73
|
+
self._adapters = ChainMap(adapters or {})
|
|
74
|
+
self._auth = auth
|
|
75
|
+
self._cert = cert
|
|
76
|
+
self._headers = ChainMap(headers or {})
|
|
77
|
+
self._params = ChainMap(params or {})
|
|
78
|
+
self._proxies = ChainMap(proxies or {})
|
|
79
|
+
self._stream = stream or False
|
|
80
|
+
self._timeout = timeout
|
|
81
|
+
self._verify = verify
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def protocol(self) -> Optional[Protocol]:
|
|
85
|
+
"""Protocol."""
|
|
86
|
+
result = get_protocol(self.url)
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def url(self) -> str:
|
|
91
|
+
"""Base URL."""
|
|
92
|
+
return self._url
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def adapters(self) -> ChainMap[Protocol, HTTPAdapter]:
|
|
96
|
+
"""Connection adapters."""
|
|
97
|
+
return self._adapters
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def auth(self) -> Optional[Union[AuthBase, Tuple[str, str]]]:
|
|
101
|
+
"""Authentication header."""
|
|
102
|
+
return self._auth
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def cert(self) -> Optional[Union[str, Tuple[str, str]]]:
|
|
106
|
+
"""Certificate."""
|
|
107
|
+
return self._cert
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def headers(self) -> ChainMap[str, str]:
|
|
111
|
+
"""Headers."""
|
|
112
|
+
return self._headers
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def params(self) -> ChainMap[str, Any]:
|
|
116
|
+
"""Parameters."""
|
|
117
|
+
return self._params
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def proxies(self) -> ChainMap[str, str]:
|
|
121
|
+
"""Proxies."""
|
|
122
|
+
return self._proxies
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def stream(self) -> bool:
|
|
126
|
+
"""Whether response content will be streamed."""
|
|
127
|
+
return self._stream
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def timeout(self) -> Optional[Union[float, Tuple[float, float]]]:
|
|
131
|
+
"""How long before request times out."""
|
|
132
|
+
return self._timeout
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def verify(self) -> Optional[Union[float, Tuple[float, float]]]:
|
|
136
|
+
"""Whether certificate is verified."""
|
|
137
|
+
return self._verify
|
|
138
|
+
|
|
139
|
+
def __repr__(self) -> str:
|
|
140
|
+
return f"Engine({self.url!r})"
|
|
141
|
+
|
|
142
|
+
def connect(self) -> HttpConnection:
|
|
143
|
+
"""Establish connection.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Connection instance.
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
result = self._connection_cls(self)
|
|
150
|
+
return result
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ----------------------------------------------------------------------------
|
|
154
|
+
# Helpers
|
|
155
|
+
# ----------------------------------------------------------------------------
|
|
156
|
+
def standardize_url(__url: Any, /) -> str:
|
|
157
|
+
"""Standardize URL.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
__url: URL.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
URL.
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
if not isinstance(__url, str):
|
|
167
|
+
message = f"expected type 'str', got {type(__url)} instead"
|
|
168
|
+
raise TypeError(message)
|
|
169
|
+
|
|
170
|
+
result = __url if __url.endswith("/") else __url + "/"
|
|
171
|
+
return result
|
apytizer/errors.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/errors.py
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ConnectionError(Exception):
|
|
6
|
+
"""Base class for connection errors."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ConnectionNotStarted(ConnectionError):
|
|
10
|
+
"""Error raised when connection not started."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EndpointError(Exception):
|
|
14
|
+
"""Base class for endpoint errors."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EndpointNotFound(Exception):
|
|
18
|
+
"""Error raised when endpoint not found."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RequestError(Exception):
|
|
22
|
+
"""Base class for request errors."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MethodNotAllowed(RequestError):
|
|
26
|
+
"""Error raised when request method not allowed."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SessionError(Exception):
|
|
30
|
+
"""Base class for session errors."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SessionNotStarted(SessionError):
|
|
34
|
+
"""Error raised when session not started."""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/factories/abstract_factory.py
|
|
3
|
+
"""Abstract factory class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract factory class which provides an interface for
|
|
6
|
+
subclasses to implement.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
import abc
|
|
12
|
+
|
|
13
|
+
__all__ = ["AbstractFactory"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AbstractFactory(abc.ABC):
|
|
17
|
+
"""Represents an abstract factory."""
|
apytizer/http_methods.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/http_methods.py
|
|
3
|
+
|
|
4
|
+
# Standard Library Imports
|
|
5
|
+
import enum
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@enum.unique
|
|
10
|
+
class HTTPMethod(str, enum.Enum):
|
|
11
|
+
"""Implements standard HTTP methods."""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def _missing_(cls, value: object) -> Optional[enum.Enum]:
|
|
15
|
+
result = (
|
|
16
|
+
HTTPMethod(value.upper())
|
|
17
|
+
if (
|
|
18
|
+
isinstance(value, str)
|
|
19
|
+
and not value.isupper()
|
|
20
|
+
and value.upper() in HTTPMethod
|
|
21
|
+
)
|
|
22
|
+
else None
|
|
23
|
+
)
|
|
24
|
+
return result
|
|
25
|
+
|
|
26
|
+
CONNECT = "CONNECT"
|
|
27
|
+
HEAD = "HEAD"
|
|
28
|
+
GET = "GET"
|
|
29
|
+
POST = "POST"
|
|
30
|
+
PUT = "PUT"
|
|
31
|
+
PATCH = "PATCH"
|
|
32
|
+
DELETE = "DELETE"
|
|
33
|
+
OPTIONS = "OPTIONS"
|
|
34
|
+
TRACE = "TRACE"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/managers/__init__.py
|
|
3
|
+
"""Managers.
|
|
4
|
+
|
|
5
|
+
Managers are abstractions around endpoints. Managers act as a bridge between
|
|
6
|
+
their respective endpoints and the repositories and models that rely on them.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Local Imports
|
|
11
|
+
from .abstract_manager import *
|
|
12
|
+
from .base_manager import *
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/managers/abstract_manager.py
|
|
3
|
+
"""Abstract manager class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract manager class which provides an interface for
|
|
6
|
+
subclasses to implement. Each of the abstract methods represents a standard
|
|
7
|
+
create, read, update or delete (CRUD) operation.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Standard Library Imports
|
|
12
|
+
import abc
|
|
13
|
+
from typing import Any
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..models import AbstractModel
|
|
19
|
+
|
|
20
|
+
__all__ = ["AbstractManager"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AbstractManager(abc.ABC):
|
|
24
|
+
"""Represents an abstract model manager."""
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def create(self, obj: AbstractModel, /) -> None:
|
|
28
|
+
"""Abstract method to create an object.
|
|
29
|
+
|
|
30
|
+
This method must call the `post` method on an associated endpoint to
|
|
31
|
+
create the object.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
obj: Instance of an abstract model subclass.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
|
|
39
|
+
@abc.abstractmethod
|
|
40
|
+
def read(self, ref: Any, /) -> AbstractModel:
|
|
41
|
+
"""Abstract method to 'read' an object.
|
|
42
|
+
|
|
43
|
+
This method must call the `get` method on an associated endpoint to
|
|
44
|
+
retrieve object data. The response is used to instantiate an instance
|
|
45
|
+
of the managed object class.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
ref: Reference to object on endpoint.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Instance of an abstract model subclass.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
@abc.abstractmethod
|
|
57
|
+
def update(self, obj: AbstractModel, /) -> None:
|
|
58
|
+
"""Abstract method to update an object.
|
|
59
|
+
|
|
60
|
+
This method must call the `put` method on an associated endpoint to
|
|
61
|
+
update object data.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
obj: Instance of an abstract model subclass.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
@abc.abstractmethod
|
|
70
|
+
def delete(self, obj: AbstractModel, /) -> None:
|
|
71
|
+
"""Abstract method to delete an object.
|
|
72
|
+
|
|
73
|
+
This method must call the `delete` method on an associated endpoint
|
|
74
|
+
to delete the object.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
obj: Instance of an abstract model subclass.
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/managers/base_manager.py
|
|
3
|
+
"""Base manager class.
|
|
4
|
+
|
|
5
|
+
This module defines a base implementation of a manager class.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import Type
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
from .abstract_manager import AbstractManager
|
|
18
|
+
from ..apis import AbstractAPI
|
|
19
|
+
from ..mappers import AbstractMapper
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ..models import AbstractModel
|
|
23
|
+
|
|
24
|
+
__all__ = ["BaseManager"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Initialize logger.
|
|
28
|
+
log = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BaseManager(AbstractManager):
|
|
32
|
+
"""Base class from which all manager implementations are derived.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
__api: API instance.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
TypeError: when argument is not type 'Endpoint'.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __new__(
|
|
43
|
+
cls: Type[BaseManager],
|
|
44
|
+
__api: AbstractAPI,
|
|
45
|
+
__mapper: AbstractMapper,
|
|
46
|
+
/,
|
|
47
|
+
) -> BaseManager:
|
|
48
|
+
if not isinstance(__api, AbstractAPI): # type: ignore
|
|
49
|
+
message = f"expected type 'WebAPI', got {type(__api)} instead"
|
|
50
|
+
raise TypeError(message)
|
|
51
|
+
|
|
52
|
+
if not isinstance(__mapper, AbstractMapper): # type: ignore
|
|
53
|
+
message = f"expected type 'Mapper', got {type(__mapper)} instead"
|
|
54
|
+
raise TypeError(message)
|
|
55
|
+
|
|
56
|
+
instance = super().__new__(cls) # type: ignore
|
|
57
|
+
return instance
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
__api: AbstractAPI,
|
|
62
|
+
__mapper: AbstractMapper,
|
|
63
|
+
/,
|
|
64
|
+
) -> None:
|
|
65
|
+
self._api = __api
|
|
66
|
+
self._mapper = __mapper
|
|
67
|
+
|
|
68
|
+
def __repr__(self) -> str:
|
|
69
|
+
result = "<{cls!s}>".format(cls=self.__class__.__name__)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
def create(self, obj: "AbstractModel", /) -> None:
|
|
73
|
+
"""Create an object.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
obj: Object to create.
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
def read(self, ref: Any, /) -> "AbstractModel":
|
|
82
|
+
"""Read an object.
|
|
83
|
+
|
|
84
|
+
Calls `get` method on associated endpoint to retrieve object data.
|
|
85
|
+
Uses response to instantiate an instance of the managed object class.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
ref: Reference to object on endpoint.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Object instance.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
raise NotImplementedError
|
|
95
|
+
|
|
96
|
+
def update(self, obj: "AbstractModel", /) -> None:
|
|
97
|
+
"""Update an object.
|
|
98
|
+
|
|
99
|
+
Calls `put` method on associated endpoint to update object data.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
obj: Object to update.
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
raise NotImplementedError
|
|
106
|
+
|
|
107
|
+
def delete(self, obj: "AbstractModel", /) -> None:
|
|
108
|
+
"""Delete an object.
|
|
109
|
+
|
|
110
|
+
Calls `delete` method on associated endpoint to delete the object.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
obj: Object to delete.
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/mappers/abstract_mapper.py
|
|
3
|
+
"""Abstract mapper class interface.
|
|
4
|
+
|
|
5
|
+
This module defines an abstract mapper class which provides an interface for
|
|
6
|
+
subclasses to implement.
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Standard Library Imports
|
|
11
|
+
import abc
|
|
12
|
+
from typing import Mapping
|
|
13
|
+
from typing import Type
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..models import AbstractModel
|
|
19
|
+
from ..routes import AbstractRoute
|
|
20
|
+
|
|
21
|
+
__all__ = ["AbstractMapper"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AbstractMapper(abc.ABC):
|
|
25
|
+
"""Represents an abstract mapper.
|
|
26
|
+
|
|
27
|
+
Each mapper is an association between a Python class and an API endpoint,
|
|
28
|
+
which allows ORM operations against the class.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abc.abstractmethod
|
|
34
|
+
def model(self) -> Type["AbstractModel"]:
|
|
35
|
+
"""Model class."""
|
|
36
|
+
raise NotImplementedError
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
@abc.abstractmethod
|
|
40
|
+
def route(self) -> "AbstractRoute":
|
|
41
|
+
"""Routes."""
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
@abc.abstractmethod
|
|
46
|
+
def properties(self) -> Mapping[str, str]:
|
|
47
|
+
"""Properties."""
|
|
48
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# src/apytizer/mappers/base_mapper.py
|
|
3
|
+
"""Base Mapper Class.
|
|
4
|
+
|
|
5
|
+
This module defines the base mapper class implementation.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard Library Imports
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
from types import MappingProxyType
|
|
12
|
+
from typing import Mapping
|
|
13
|
+
from typing import Type
|
|
14
|
+
|
|
15
|
+
# Local Imports
|
|
16
|
+
from .abstract_mapper import AbstractMapper
|
|
17
|
+
from ..models import AbstractModel
|
|
18
|
+
from ..routes import AbstractRoute
|
|
19
|
+
|
|
20
|
+
__all__ = ["Mapper"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Mapper(AbstractMapper):
|
|
24
|
+
"""Implements a mapper.
|
|
25
|
+
|
|
26
|
+
Each mapper is an association between a Python class and an API endpoint,
|
|
27
|
+
which allows ORM operations against the class.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __new__(
|
|
32
|
+
cls: Type[Mapper],
|
|
33
|
+
__class: Type[AbstractModel],
|
|
34
|
+
__route: AbstractRoute,
|
|
35
|
+
__properties: Mapping[str, str],
|
|
36
|
+
/,
|
|
37
|
+
) -> Mapper:
|
|
38
|
+
if not issubclass(__class, AbstractModel): # type: ignore
|
|
39
|
+
message = f"{__class} is not a subclass of 'AbstractModel'"
|
|
40
|
+
raise TypeError(message)
|
|
41
|
+
|
|
42
|
+
if not isinstance(__route, AbstractRoute): # type: ignore
|
|
43
|
+
message = f"expected type 'Route', got {type(__route)} instead"
|
|
44
|
+
raise TypeError(message)
|
|
45
|
+
|
|
46
|
+
if not isinstance(__properties, Mapping): # type: ignore
|
|
47
|
+
expected = "expected type 'Mapping'"
|
|
48
|
+
actual = f"got {type(__properties)} instead"
|
|
49
|
+
message = ", ".join([expected, actual])
|
|
50
|
+
raise TypeError(message)
|
|
51
|
+
|
|
52
|
+
instance = super().__new__(cls) # type: ignore
|
|
53
|
+
return instance
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
__class: Type[AbstractModel],
|
|
58
|
+
__route: AbstractRoute,
|
|
59
|
+
__properties: Mapping[str, str],
|
|
60
|
+
) -> None:
|
|
61
|
+
self._class = __class
|
|
62
|
+
self._route = __route
|
|
63
|
+
self._properties = MappingProxyType(__properties)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def model(self) -> Type[AbstractModel]:
|
|
67
|
+
"""Model."""
|
|
68
|
+
raise NotImplementedError
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def route(self) -> AbstractRoute:
|
|
72
|
+
"""Route."""
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def properties(self) -> Mapping[str, str]:
|
|
77
|
+
"""Properties."""
|
|
78
|
+
raise NotImplementedError
|