splight-lib 5.11.5__tar.gz → 5.11.5.dev0__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.
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/PKG-INFO +1 -1
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/pyproject.toml +1 -1
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/auth/__init__.py +2 -0
- splight_lib-5.11.5.dev0/splight_lib/auth/exceptions.py +12 -0
- splight_lib-5.11.5.dev0/splight_lib/auth/mac_auth.py +96 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/database/abstract.py +4 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/database/builder.py +2 -2
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/database/remote_client.py +23 -23
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/buffer.py +4 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/builder.py +2 -2
- splight_lib-5.11.5.dev0/splight_lib/client/file_handler.py +34 -0
- splight_lib-5.11.5.dev0/splight_lib/client/filter.py +18 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/hub/abstract.py +4 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/hub/client.py +10 -10
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/component/abstract.py +6 -6
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/component/spec.py +28 -31
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/constants.py +0 -1
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/encryption.py +2 -1
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/engine.py +3 -2
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/scheduling.py +11 -10
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/task.py +6 -6
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/trigger.py +4 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/alert.py +33 -38
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/asset.py +13 -16
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/attribute.py +4 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/component.py +58 -58
- splight_lib-5.11.5.dev0/splight_lib/models/dashboard.py +307 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/data_address.py +4 -2
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/datalake.py +5 -33
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/file.py +9 -12
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/function.py +17 -21
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/hub.py +29 -39
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/hub_server.py +1 -4
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/hub_solution.py +7 -10
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/metadata.py +5 -5
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/secret.py +3 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/server.py +1 -1
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/solution.py +3 -3
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/tag.py +3 -1
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/settings.py +5 -5
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/stringcase.py +7 -8
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/testing/__init__.py +4 -4
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/utils/custom_model.py +7 -7
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/utils/hub.py +3 -3
- splight_lib-5.11.5/splight_lib/models/dashboard.py +0 -126
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/LICENSE.txt +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/README.md +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/abstract/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/abstract/client.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/auth/token.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/database/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/database/classmap.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/abstract.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/constants.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/datalake/remote_client.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/hub/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/tests/test_database.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/client/tests/test_datalake.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/component/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/component/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/component/tests/test_abstract.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/component/tests/test_spec.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/conftest.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/tests/test_execution.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/execution/tests/test_scheduling.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/logging/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/logging/_internal.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/logging/component.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/logging/constants.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/logging/logging.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/logging/tests/test_logging.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/actions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/database_base.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/datalake_base.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/generic.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/native.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/tests/models.json +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/tests/test_component_object_instance.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/tests/test_database_model.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/tests/test_metadata.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/tests/test_models.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/models/variable_types.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/restclient/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/restclient/client.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/restclient/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/restclient/tests/test_restclient.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/restclient/types.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/server/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/server/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/server/server.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/solution/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/solution/exceptions.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/solution/solution.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/tests/FakeProc.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/tests/asset_geometries.json +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/tests/test_api_contracts.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/tests/test_encryption.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/utils/__init__.py +0 -0
- {splight_lib-5.11.5 → splight_lib-5.11.5.dev0}/splight_lib/version.py +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class SignatureVerificationError(Exception):
|
|
2
|
+
def __init__(self, message, sig_header, http_body=None):
|
|
3
|
+
super(SignatureVerificationError, self).__init__(message)
|
|
4
|
+
self.sig_header = sig_header
|
|
5
|
+
self._message = message
|
|
6
|
+
self.http_body = http_body
|
|
7
|
+
|
|
8
|
+
def __repr__(self):
|
|
9
|
+
return f"{self.message} {self.http_body}"
|
|
10
|
+
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return self._message
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import hmac
|
|
2
|
+
import time
|
|
3
|
+
from hashlib import sha256
|
|
4
|
+
|
|
5
|
+
import six
|
|
6
|
+
|
|
7
|
+
from splight_lib.auth.exceptions import SignatureVerificationError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def utf8(value):
|
|
11
|
+
if six.PY2 and isinstance(value, six.text_type):
|
|
12
|
+
return value.encode("utf-8")
|
|
13
|
+
else:
|
|
14
|
+
return value
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HmacSignature(object):
|
|
18
|
+
EXPECTED_SCHEME = "v1"
|
|
19
|
+
DEFAULT_TOLERANCE = 300
|
|
20
|
+
|
|
21
|
+
def __init__(self, secret="", *args, **kwargs):
|
|
22
|
+
self.secret = secret
|
|
23
|
+
super().__init__(*args, **kwargs)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def _compute_signature(payload, secret):
|
|
27
|
+
mac = hmac.new(
|
|
28
|
+
secret.encode("utf-8"),
|
|
29
|
+
msg=payload.encode("utf-8"),
|
|
30
|
+
digestmod=sha256,
|
|
31
|
+
)
|
|
32
|
+
return mac.hexdigest()
|
|
33
|
+
|
|
34
|
+
def compute_header_signature(self, payload: str):
|
|
35
|
+
"""
|
|
36
|
+
Compute headers signature using hmac auth method.
|
|
37
|
+
"""
|
|
38
|
+
timestamp = int(time.time())
|
|
39
|
+
signed_payload = "%d.%s" % (timestamp, payload.decode("ascii"))
|
|
40
|
+
mac_hexdigest = self._compute_signature(signed_payload, self.secret)
|
|
41
|
+
return f"t={timestamp},{self.EXPECTED_SCHEME}={mac_hexdigest}"
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _get_timestamp_and_signatures(header, scheme):
|
|
45
|
+
list_items = [i.split("=", 2) for i in header.split(",")]
|
|
46
|
+
timestamp = int([i[1] for i in list_items if i[0] == "t"][0])
|
|
47
|
+
signatures = [i[1] for i in list_items if i[0] == scheme]
|
|
48
|
+
return timestamp, signatures
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def verify_header(
|
|
52
|
+
cls, payload, header, secret, tolerance=DEFAULT_TOLERANCE
|
|
53
|
+
):
|
|
54
|
+
if hasattr(payload, "decode"):
|
|
55
|
+
payload = payload.decode("utf-8")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
timestamp, signatures = cls._get_timestamp_and_signatures(
|
|
59
|
+
header, cls.EXPECTED_SCHEME
|
|
60
|
+
)
|
|
61
|
+
except Exception:
|
|
62
|
+
raise SignatureVerificationError(
|
|
63
|
+
"Unable to extract timestamp and signatures from header",
|
|
64
|
+
header,
|
|
65
|
+
payload,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if not signatures:
|
|
69
|
+
raise SignatureVerificationError(
|
|
70
|
+
"No signatures found with expected scheme "
|
|
71
|
+
"%s" % cls.EXPECTED_SCHEME,
|
|
72
|
+
header,
|
|
73
|
+
payload,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
signed_payload = "%d.%s" % (timestamp, payload)
|
|
77
|
+
expected_sig = cls._compute_signature(signed_payload, secret)
|
|
78
|
+
if not any(
|
|
79
|
+
hmac.compare_digest(utf8(expected_sig), utf8(s))
|
|
80
|
+
for s in signatures
|
|
81
|
+
):
|
|
82
|
+
raise SignatureVerificationError(
|
|
83
|
+
"No signatures found matching the expected signature for "
|
|
84
|
+
"payload",
|
|
85
|
+
header,
|
|
86
|
+
payload,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if tolerance and timestamp < time.time() - tolerance:
|
|
90
|
+
raise SignatureVerificationError(
|
|
91
|
+
"Timestamp outside the tolerance zone (%d)" % timestamp,
|
|
92
|
+
header,
|
|
93
|
+
payload,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return True
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from tempfile import NamedTemporaryFile
|
|
3
|
+
from typing import Dict, List, Union
|
|
3
4
|
|
|
4
5
|
from splight_lib.abstract.client import AbstractClient, QuerySet
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class AbstractDatabaseClient(AbstractClient):
|
|
8
9
|
@abstractmethod
|
|
9
|
-
def save(self, resource_name: str, instance:
|
|
10
|
+
def save(self, resource_name: str, instance: Dict) -> Dict:
|
|
10
11
|
pass
|
|
11
12
|
|
|
12
13
|
@abstractmethod
|
|
13
14
|
def _get(
|
|
14
15
|
self, resource_name: str, first: bool = False, **kwargs
|
|
15
|
-
) ->
|
|
16
|
+
) -> Union[Dict, List[Dict]]:
|
|
16
17
|
pass
|
|
17
18
|
|
|
18
19
|
def get(self, resource_name: str, *args, **kwargs) -> QuerySet:
|
|
@@ -23,5 +24,5 @@ class AbstractDatabaseClient(AbstractClient):
|
|
|
23
24
|
pass
|
|
24
25
|
|
|
25
26
|
@abstractmethod
|
|
26
|
-
def download(self, instance:
|
|
27
|
+
def download(self, instance: Dict) -> NamedTemporaryFile:
|
|
27
28
|
pass
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any, Dict
|
|
2
2
|
|
|
3
3
|
from splight_lib.client.database.abstract import AbstractDatabaseClient
|
|
4
4
|
from splight_lib.client.database.remote_client import RemoteDatabaseClient
|
|
@@ -6,6 +6,6 @@ from splight_lib.client.database.remote_client import RemoteDatabaseClient
|
|
|
6
6
|
|
|
7
7
|
class DatabaseClientBuilder:
|
|
8
8
|
@staticmethod
|
|
9
|
-
def build(parameters:
|
|
9
|
+
def build(parameters: Dict[str, Any] = {}) -> AbstractDatabaseClient:
|
|
10
10
|
db_client = RemoteDatabaseClient(**parameters)
|
|
11
11
|
return db_client
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from tempfile import NamedTemporaryFile
|
|
2
|
-
from typing import Any, Generator
|
|
2
|
+
from typing import Any, Dict, Generator, List, Optional, Union
|
|
3
3
|
|
|
4
4
|
import progressbar
|
|
5
5
|
import requests
|
|
@@ -31,9 +31,9 @@ logger = get_splight_logger()
|
|
|
31
31
|
|
|
32
32
|
class PaginatedResponse(TypedDict):
|
|
33
33
|
count: int
|
|
34
|
-
next: str
|
|
35
|
-
previous: str
|
|
36
|
-
results:
|
|
34
|
+
next: Optional[str]
|
|
35
|
+
previous: Optional[str]
|
|
36
|
+
results: List[Any]
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
@@ -66,9 +66,9 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
66
66
|
def save(
|
|
67
67
|
self,
|
|
68
68
|
resource_name: str,
|
|
69
|
-
instance:
|
|
70
|
-
files:
|
|
71
|
-
) ->
|
|
69
|
+
instance: Dict,
|
|
70
|
+
files: Optional[Dict[str, str]] = None,
|
|
71
|
+
) -> Dict:
|
|
72
72
|
"""Creates or updates a resource depending on the name if
|
|
73
73
|
it contains the id or not.
|
|
74
74
|
|
|
@@ -126,7 +126,7 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
126
126
|
resource_name: str,
|
|
127
127
|
first: bool = False,
|
|
128
128
|
**kwargs,
|
|
129
|
-
) ->
|
|
129
|
+
) -> Union[Dict, List[Dict]]:
|
|
130
130
|
"""Retrieves one or multiple resources. If the parameter id is passed
|
|
131
131
|
as a kwarg, the instance with that id will be retrieved.
|
|
132
132
|
|
|
@@ -149,7 +149,7 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
149
149
|
)
|
|
150
150
|
return instances
|
|
151
151
|
|
|
152
|
-
def operate(self, resource_name: str, instance:
|
|
152
|
+
def operate(self, resource_name: str, instance: Dict) -> Dict:
|
|
153
153
|
model_name = resource_name.lower()
|
|
154
154
|
api_path = CUSTOM_PATHS_MAP.get(model_name)
|
|
155
155
|
if not api_path:
|
|
@@ -163,7 +163,7 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
163
163
|
|
|
164
164
|
def _retrieve_multiple(
|
|
165
165
|
self, resource_name: str, first: bool = False, **kwargs
|
|
166
|
-
) ->
|
|
166
|
+
) -> List[Dict]:
|
|
167
167
|
logger.debug(f"Retrieving objects {resource_name}")
|
|
168
168
|
api_path = self._get_api_path(resource_name)
|
|
169
169
|
url = self._base_url / api_path
|
|
@@ -176,7 +176,7 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
176
176
|
return instances
|
|
177
177
|
|
|
178
178
|
@retry(SPLIGHT_REQUEST_EXCEPTIONS, tries=3, delay=1)
|
|
179
|
-
def _retrieve_single(self, resource_name: str, id: str) ->
|
|
179
|
+
def _retrieve_single(self, resource_name: str, id: str) -> Dict:
|
|
180
180
|
logger.debug(f"Retrieving object {resource_name} with id {id}")
|
|
181
181
|
api_path = self._get_api_path(resource_name)
|
|
182
182
|
url = self._base_url / api_path / f"{id}/"
|
|
@@ -191,8 +191,8 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
191
191
|
def download(
|
|
192
192
|
self,
|
|
193
193
|
resource_name: str,
|
|
194
|
-
instance:
|
|
195
|
-
type_: str
|
|
194
|
+
instance: Dict,
|
|
195
|
+
type_: Optional[str] = None,
|
|
196
196
|
**kwargs,
|
|
197
197
|
) -> NamedTemporaryFile:
|
|
198
198
|
"""Returns the number of resources in the database for a given model
|
|
@@ -271,9 +271,9 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
271
271
|
def _create(
|
|
272
272
|
self,
|
|
273
273
|
resource_name: str,
|
|
274
|
-
instance:
|
|
275
|
-
files:
|
|
276
|
-
) ->
|
|
274
|
+
instance: Dict,
|
|
275
|
+
files: Optional[Dict[str, str]] = None,
|
|
276
|
+
) -> Dict:
|
|
277
277
|
logger.debug("Saving new instance", tags=LogTags.DATABASE)
|
|
278
278
|
model_name = resource_name.lower()
|
|
279
279
|
api_path = self._get_api_path(resource_name)
|
|
@@ -305,9 +305,9 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
305
305
|
self,
|
|
306
306
|
resource_name: str,
|
|
307
307
|
resource_id: str,
|
|
308
|
-
instance:
|
|
309
|
-
files:
|
|
310
|
-
) ->
|
|
308
|
+
instance: Dict,
|
|
309
|
+
files: Optional[Dict[str, str]] = None,
|
|
310
|
+
) -> Dict:
|
|
311
311
|
logger.debug("Saving instance %s", resource_id, tags=LogTags.DATABASE)
|
|
312
312
|
model_name = resource_name.lower()
|
|
313
313
|
api_path = self._get_api_path(resource_name)
|
|
@@ -323,7 +323,7 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
323
323
|
raise RequestError(response.status_code, response.text)
|
|
324
324
|
return response.json()
|
|
325
325
|
|
|
326
|
-
def _create_file(self, instance:
|
|
326
|
+
def _create_file(self, instance: Dict, url: furl):
|
|
327
327
|
response = self._restclient.post(url, data=instance)
|
|
328
328
|
if response.is_error:
|
|
329
329
|
raise RequestError(response.status_code, response.text)
|
|
@@ -333,7 +333,7 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
333
333
|
self._upload_file(created_instance, file_path=file_path)
|
|
334
334
|
return created_instance
|
|
335
335
|
|
|
336
|
-
def _upload_file(self, instance:
|
|
336
|
+
def _upload_file(self, instance: Dict, file_path: str):
|
|
337
337
|
api_path = self._get_api_path("file")
|
|
338
338
|
resource_id = instance.get("id")
|
|
339
339
|
url = self._base_url / api_path / f"{resource_id}/upload_url/"
|
|
@@ -358,9 +358,9 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
|
|
|
358
358
|
def upload(
|
|
359
359
|
self,
|
|
360
360
|
resource_name: str,
|
|
361
|
-
instance:
|
|
361
|
+
instance: Dict,
|
|
362
362
|
file_path: str,
|
|
363
|
-
type_: str
|
|
363
|
+
type_: Optional[str] = None,
|
|
364
364
|
):
|
|
365
365
|
api_path = self._get_api_path(resource_name)
|
|
366
366
|
resource_id = instance.get("id")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import datetime, timezone
|
|
2
|
+
from typing import Dict, List
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class DatalakeDocumentBuffer:
|
|
@@ -7,13 +8,13 @@ class DatalakeDocumentBuffer:
|
|
|
7
8
|
def __init__(self, buffer_size: int, buffer_timeout: float):
|
|
8
9
|
self._size = buffer_size
|
|
9
10
|
self._timeout = buffer_timeout
|
|
10
|
-
self._buffer:
|
|
11
|
+
self._buffer: List[Dict] = []
|
|
11
12
|
self._last_flush = datetime.now(timezone.utc)
|
|
12
13
|
|
|
13
14
|
self.reset()
|
|
14
15
|
|
|
15
16
|
@property
|
|
16
|
-
def data(self) ->
|
|
17
|
+
def data(self) -> List[Dict]:
|
|
17
18
|
"""Retrieves the buffer data
|
|
18
19
|
|
|
19
20
|
Returns
|
|
@@ -44,7 +45,7 @@ class DatalakeDocumentBuffer:
|
|
|
44
45
|
self._last_flush = datetime.now(timezone.utc)
|
|
45
46
|
self._buffer = []
|
|
46
47
|
|
|
47
|
-
def add_documents(self, documents:
|
|
48
|
+
def add_documents(self, documents: List[Dict]):
|
|
48
49
|
"""Adds new documents to the buffer.
|
|
49
50
|
|
|
50
51
|
Parameters
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any, Dict
|
|
2
2
|
|
|
3
3
|
from splight_lib.client.datalake.abstract import AbstractDatalakeClient
|
|
4
4
|
from splight_lib.client.datalake.remote_client import (
|
|
@@ -19,6 +19,6 @@ class DatalakeClientBuilder:
|
|
|
19
19
|
@staticmethod
|
|
20
20
|
def build(
|
|
21
21
|
dl_client_type: DatalakeClientType = DatalakeClientType.BUFFERED_ASYNC,
|
|
22
|
-
parameters:
|
|
22
|
+
parameters: Dict[str, Any] = {},
|
|
23
23
|
) -> AbstractDatalakeClient:
|
|
24
24
|
return DL_CLIENT_TYPE_MAP[dl_client_type](**parameters)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class FixedLineNumberFileHandler:
|
|
6
|
+
def __init__(self, file_path: str, total_lines: int = 10000):
|
|
7
|
+
self._file_path = file_path
|
|
8
|
+
if not os.path.exists(self._file_path):
|
|
9
|
+
self._write_file([])
|
|
10
|
+
self._total_lines = total_lines
|
|
11
|
+
|
|
12
|
+
def write(self, lines: List[str], override=False):
|
|
13
|
+
all_lines = self._read_file()
|
|
14
|
+
|
|
15
|
+
if override:
|
|
16
|
+
all_lines = lines
|
|
17
|
+
else:
|
|
18
|
+
all_lines.extend(lines)
|
|
19
|
+
|
|
20
|
+
lines = all_lines[-self._total_lines :]
|
|
21
|
+
lines = [f"{x}\n" for x in lines]
|
|
22
|
+
self._write_file(lines)
|
|
23
|
+
|
|
24
|
+
def read(self) -> List[str]:
|
|
25
|
+
return self._read_file()
|
|
26
|
+
|
|
27
|
+
def _write_file(self, lines: List[str]):
|
|
28
|
+
with open(self._file_path, "w") as fid:
|
|
29
|
+
fid.writelines(lines)
|
|
30
|
+
|
|
31
|
+
def _read_file(self) -> List[str]:
|
|
32
|
+
with open(self._file_path, "r") as fid:
|
|
33
|
+
all_lines = [x.strip() for x in fid.readlines()]
|
|
34
|
+
return all_lines
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any, Dict, Tuple
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def value_filter(name: str, value: Any, item: Dict):
|
|
5
|
+
satisfied = False
|
|
6
|
+
if "__icontains" in name:
|
|
7
|
+
variable_name = name.split("__")[0]
|
|
8
|
+
satisfied = value in item.get(variable_name, None)
|
|
9
|
+
elif "__in" in name:
|
|
10
|
+
variable_name = name.split("__in")[0]
|
|
11
|
+
satisfied = item.get(variable_name, None) in value
|
|
12
|
+
else:
|
|
13
|
+
satisfied = item.get(name, None) == value
|
|
14
|
+
return satisfied
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def value_filter_on_tuple(name: str, value: Any, item: Tuple[str, Dict]):
|
|
18
|
+
return value_filter(name=name, value=value, item=item[1])
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
+
from typing import Dict, List, Tuple
|
|
2
3
|
|
|
3
4
|
from pydantic import BaseModel
|
|
4
5
|
|
|
@@ -16,7 +17,7 @@ class AbstractHubClient(AbstractClient):
|
|
|
16
17
|
limit_: int = -1,
|
|
17
18
|
skip_: int = 0,
|
|
18
19
|
**kwargs,
|
|
19
|
-
) ->
|
|
20
|
+
) -> List[BaseModel]:
|
|
20
21
|
pass
|
|
21
22
|
|
|
22
23
|
@abstractmethod
|
|
@@ -24,7 +25,7 @@ class AbstractHubClient(AbstractClient):
|
|
|
24
25
|
pass
|
|
25
26
|
|
|
26
27
|
@abstractmethod
|
|
27
|
-
def download(self, data:
|
|
28
|
+
def download(self, data: Dict) -> Tuple:
|
|
28
29
|
pass
|
|
29
30
|
|
|
30
31
|
@abstractmethod
|
|
@@ -32,5 +33,5 @@ class AbstractHubClient(AbstractClient):
|
|
|
32
33
|
pass
|
|
33
34
|
|
|
34
35
|
@abstractmethod
|
|
35
|
-
def save(self, instance:
|
|
36
|
+
def save(self, instance: Dict) -> Dict:
|
|
36
37
|
pass
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from tempfile import NamedTemporaryFile
|
|
2
|
-
from typing import Any, Generator, TypedDict
|
|
2
|
+
from typing import Any, Dict, Generator, List, Optional, TypedDict
|
|
3
3
|
|
|
4
4
|
import progressbar
|
|
5
5
|
import requests
|
|
@@ -13,9 +13,9 @@ from splight_lib.client.hub.abstract import AbstractHubClient
|
|
|
13
13
|
|
|
14
14
|
class PaginatedResponse(TypedDict):
|
|
15
15
|
count: int
|
|
16
|
-
next: str
|
|
17
|
-
previous: str
|
|
18
|
-
results:
|
|
16
|
+
next: Optional[str]
|
|
17
|
+
previous: Optional[str]
|
|
18
|
+
results: List[Any]
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class SplightHubClient(AbstractHubClient):
|
|
@@ -58,7 +58,7 @@ class SplightHubClient(AbstractHubClient):
|
|
|
58
58
|
limit_: int = -1,
|
|
59
59
|
skip_: int = 0,
|
|
60
60
|
**kwargs,
|
|
61
|
-
) ->
|
|
61
|
+
) -> List[BaseModel]:
|
|
62
62
|
url = self._hub_url / "versions/"
|
|
63
63
|
params = self._get_params(limit_, skip_, **kwargs)
|
|
64
64
|
instances = []
|
|
@@ -71,14 +71,14 @@ class SplightHubClient(AbstractHubClient):
|
|
|
71
71
|
def get_org_id(self):
|
|
72
72
|
return self._org_id
|
|
73
73
|
|
|
74
|
-
def _create(self, instance:
|
|
74
|
+
def _create(self, instance: Dict) -> Dict:
|
|
75
75
|
url = self._hub_url / "components/"
|
|
76
76
|
response = self._session.post(url, json=instance)
|
|
77
77
|
response.raise_for_status()
|
|
78
78
|
instance = response.json()
|
|
79
79
|
return instance
|
|
80
80
|
|
|
81
|
-
def _update(self, instance:
|
|
81
|
+
def _update(self, instance: Dict) -> Dict:
|
|
82
82
|
instance_id = instance.get("id")
|
|
83
83
|
url = self._hub_url / "versions" / f"{instance_id}/"
|
|
84
84
|
response = self._session.put(url, json=instance)
|
|
@@ -86,12 +86,12 @@ class SplightHubClient(AbstractHubClient):
|
|
|
86
86
|
instance = response.json()
|
|
87
87
|
return instance
|
|
88
88
|
|
|
89
|
-
def build(self, id: str)
|
|
89
|
+
def build(self, id: str):
|
|
90
90
|
url = self._hub_url / f"versions/{id}/build/"
|
|
91
91
|
response = self._session.post(url)
|
|
92
92
|
response.raise_for_status()
|
|
93
93
|
|
|
94
|
-
def upload(self, id: str, file_path: str, type_: str)
|
|
94
|
+
def upload(self, id: str, file_path: str, type_: str):
|
|
95
95
|
url = self._hub_url / f"versions/{id}/upload_url/"
|
|
96
96
|
params = {"type": type_}
|
|
97
97
|
response = self._session.get(url, params=params)
|
|
@@ -135,7 +135,7 @@ class SplightHubClient(AbstractHubClient):
|
|
|
135
135
|
response.status_code == 204
|
|
136
136
|
), f"Failed to delete component: {response.json()}"
|
|
137
137
|
|
|
138
|
-
def save(self, instance:
|
|
138
|
+
def save(self, instance: Dict) -> Dict:
|
|
139
139
|
if instance.get("id"):
|
|
140
140
|
return self._update(instance)
|
|
141
141
|
else:
|
|
@@ -5,7 +5,7 @@ from collections import namedtuple
|
|
|
5
5
|
from tempfile import NamedTemporaryFile
|
|
6
6
|
from threading import Thread
|
|
7
7
|
from time import sleep
|
|
8
|
-
from typing import Callable, Type
|
|
8
|
+
from typing import Callable, Dict, Optional, Type
|
|
9
9
|
|
|
10
10
|
from pydantic import BaseModel
|
|
11
11
|
from pydantic_core import ValidationError
|
|
@@ -70,7 +70,7 @@ class HealthCheckProcessor:
|
|
|
70
70
|
class SplightBaseComponent(ABC):
|
|
71
71
|
def __init__(
|
|
72
72
|
self,
|
|
73
|
-
component_id: str
|
|
73
|
+
component_id: Optional[str] = None,
|
|
74
74
|
):
|
|
75
75
|
self._component_id = component_id
|
|
76
76
|
self._execution_engine = ExecutionEngine()
|
|
@@ -185,7 +185,7 @@ class SplightBaseComponent(ABC):
|
|
|
185
185
|
return wrapper
|
|
186
186
|
|
|
187
187
|
def _get_custom_type_model(
|
|
188
|
-
self, component_object:
|
|
188
|
+
self, component_object: Dict[str, Type[ComponentObjectInstance]]
|
|
189
189
|
) -> BaseModel:
|
|
190
190
|
custom_type_model = namedtuple(
|
|
191
191
|
"CustomTypes", [k for k in component_object.keys()]
|
|
@@ -193,7 +193,7 @@ class SplightBaseComponent(ABC):
|
|
|
193
193
|
return custom_type_model(**component_object)
|
|
194
194
|
|
|
195
195
|
def _get_routine_model(
|
|
196
|
-
self, routine_objects:
|
|
196
|
+
self, routine_objects: Dict[str, Type[RoutineObjectInstance]]
|
|
197
197
|
) -> namedtuple:
|
|
198
198
|
routine_model = namedtuple(
|
|
199
199
|
"ComponentRoutine", [k for k in routine_objects.keys()]
|
|
@@ -209,9 +209,9 @@ class SplightBaseComponent(ABC):
|
|
|
209
209
|
return spec
|
|
210
210
|
|
|
211
211
|
@abstractmethod
|
|
212
|
-
def start(self)
|
|
212
|
+
def start(self):
|
|
213
213
|
raise NotImplementedError()
|
|
214
214
|
|
|
215
|
-
def stop(self)
|
|
215
|
+
def stop(self):
|
|
216
216
|
self._execution_engine.stop()
|
|
217
217
|
sys.exit(1)
|