splight-lib 5.9.6.dev2__tar.gz → 5.10.1.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.
Files changed (112) hide show
  1. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/PKG-INFO +1 -1
  2. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/pyproject.toml +49 -1
  3. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/database/classmap.py +1 -0
  4. splight_lib-5.10.1.dev0/splight_lib/client/datalake/abstract.py +36 -0
  5. splight_lib-5.10.1.dev0/splight_lib/client/datalake/constants.py +31 -0
  6. splight_lib-5.10.1.dev0/splight_lib/client/datalake/exceptions.py +12 -0
  7. splight_lib-5.10.1.dev0/splight_lib/client/datalake/remote_client.py +252 -0
  8. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/exceptions.py +2 -1
  9. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/tests/test_datalake.py +2 -1
  10. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/component/spec.py +1 -1
  11. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/task.py +2 -1
  12. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/__init__.py +4 -0
  13. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/actions.py +4 -1
  14. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/alert.py +1 -1
  15. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/asset.py +4 -1
  16. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/attribute.py +1 -1
  17. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/component.py +2 -4
  18. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/dashboard.py +2 -4
  19. splight_lib-5.10.1.dev0/splight_lib/models/database_base.py +79 -0
  20. splight_lib-5.10.1.dev0/splight_lib/models/datalake.py +155 -0
  21. splight_lib-5.10.1.dev0/splight_lib/models/datalake_base.py +130 -0
  22. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/exceptions.py +8 -1
  23. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/file.py +1 -1
  24. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/function.py +1 -1
  25. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/hub_server.py +2 -0
  26. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/hub_solution.py +2 -2
  27. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/metadata.py +1 -1
  28. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/native.py +19 -13
  29. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/secret.py +1 -1
  30. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/server.py +34 -19
  31. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/solution.py +1 -1
  32. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/tag.py +1 -1
  33. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/tests/test_database_model.py +3 -1
  34. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/server/server.py +1 -3
  35. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/tests/test_api_contracts.py +2 -1
  36. splight_lib-5.9.6.dev2/splight_lib/client/datalake/abstract.py +0 -71
  37. splight_lib-5.9.6.dev2/splight_lib/client/datalake/exceptions.py +0 -6
  38. splight_lib-5.9.6.dev2/splight_lib/client/datalake/remote_client.py +0 -405
  39. splight_lib-5.9.6.dev2/splight_lib/client/datalake/schemas.py +0 -56
  40. splight_lib-5.9.6.dev2/splight_lib/models/base.py +0 -203
  41. splight_lib-5.9.6.dev2/splight_lib/models/pipeline.py +0 -80
  42. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/LICENSE.txt +0 -0
  43. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/README.md +0 -0
  44. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/__init__.py +0 -0
  45. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/abstract/__init__.py +0 -0
  46. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/abstract/client.py +0 -0
  47. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/auth/__init__.py +0 -0
  48. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/auth/exceptions.py +0 -0
  49. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/auth/mac_auth.py +0 -0
  50. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/auth/token.py +0 -0
  51. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/__init__.py +0 -0
  52. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/database/__init__.py +0 -0
  53. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/database/abstract.py +0 -0
  54. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/database/builder.py +0 -0
  55. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/database/remote_client.py +0 -0
  56. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/datalake/__init__.py +0 -0
  57. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/datalake/buffer.py +0 -0
  58. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/datalake/builder.py +0 -0
  59. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/file_handler.py +0 -0
  60. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/filter.py +0 -0
  61. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/hub/__init__.py +0 -0
  62. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/hub/abstract.py +0 -0
  63. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/hub/client.py +0 -0
  64. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/client/tests/test_database.py +0 -0
  65. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/component/__init__.py +0 -0
  66. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/component/abstract.py +0 -0
  67. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/component/exceptions.py +0 -0
  68. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/component/tests/test_abstract.py +0 -0
  69. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/component/tests/test_spec.py +0 -0
  70. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/conftest.py +0 -0
  71. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/constants.py +0 -0
  72. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/encryption.py +0 -0
  73. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/__init__.py +0 -0
  74. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/engine.py +0 -0
  75. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/exceptions.py +0 -0
  76. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/scheduling.py +0 -0
  77. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/tests/test_execution.py +0 -0
  78. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/tests/test_scheduling.py +0 -0
  79. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/execution/trigger.py +0 -0
  80. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/logging/__init__.py +0 -0
  81. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/logging/_internal.py +0 -0
  82. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/logging/component.py +0 -0
  83. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/logging/constants.py +0 -0
  84. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/logging/logging.py +0 -0
  85. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/logging/tests/test_logging.py +0 -0
  86. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/data_address.py +0 -0
  87. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/generic.py +0 -0
  88. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/hub.py +0 -0
  89. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/tests/models.json +0 -0
  90. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/tests/test_component_object_instance.py +0 -0
  91. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/tests/test_metadata.py +0 -0
  92. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/models/tests/test_models.py +0 -0
  93. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/restclient/__init__.py +0 -0
  94. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/restclient/client.py +0 -0
  95. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/restclient/exceptions.py +0 -0
  96. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/restclient/tests/test_restclient.py +0 -0
  97. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/restclient/types.py +0 -0
  98. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/server/__init__.py +0 -0
  99. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/server/exceptions.py +0 -0
  100. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/settings.py +0 -0
  101. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/solution/__init__.py +0 -0
  102. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/solution/exceptions.py +0 -0
  103. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/solution/solution.py +0 -0
  104. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/stringcase.py +0 -0
  105. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/testing/__init__.py +0 -0
  106. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/tests/FakeProc.py +0 -0
  107. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/tests/asset_geometries.json +0 -0
  108. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/tests/test_encryption.py +0 -0
  109. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/utils/__init__.py +0 -0
  110. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/utils/custom_model.py +0 -0
  111. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/utils/hub.py +0 -0
  112. {splight_lib-5.9.6.dev2 → splight_lib-5.10.1.dev0}/splight_lib/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: splight-lib
3
- Version: 5.9.6.dev2
3
+ Version: 5.10.1.dev0
4
4
  Summary: Splight Library
5
5
  Author: Splight Dev
6
6
  Author-email: dev@splight-ae.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "splight-lib"
3
- version = "5.9.6.dev2"
3
+ version = "5.10.1dev0"
4
4
  description = "Splight Library"
5
5
  authors = ["Splight Dev <dev@splight-ae.com>"]
6
6
  readme = "README.md"
@@ -71,4 +71,52 @@ filterwarnings = [
71
71
  ]
72
72
 
73
73
  [tool.ruff]
74
+ # Exclude a variety of commonly ignored directories.
75
+ exclude = [
76
+ ".bzr",
77
+ ".direnv",
78
+ ".eggs",
79
+ ".git",
80
+ ".git-rewrite",
81
+ ".hg",
82
+ ".ipynb_checkpoints",
83
+ ".mypy_cache",
84
+ ".nox",
85
+ ".pants.d",
86
+ ".pyenv",
87
+ ".pytest_cache",
88
+ ".pytype",
89
+ ".ruff_cache",
90
+ ".svn",
91
+ ".tox",
92
+ ".venv",
93
+ ".vscode",
94
+ "__pypackages__",
95
+ "_build",
96
+ "buck-out",
97
+ "build",
98
+ "dist",
99
+ "node_modules",
100
+ "site-packages",
101
+ "venv",
102
+ ]
103
+
104
+ # Same as Black.
74
105
  line-length = 79
106
+ indent-width = 4
107
+
108
+ # Assume Python 3.8
109
+ target-version = "py38"
110
+
111
+ [tool.ruff.format]
112
+ # Like Black, use double quotes for strings.
113
+ quote-style = "double"
114
+
115
+ # Like Black, indent with spaces, rather than tabs.
116
+ indent-style = "space"
117
+
118
+ # Like Black, respect magic trailing commas.
119
+ skip-magic-trailing-comma = false
120
+
121
+ # Like Black, automatically detect the appropriate line ending.
122
+ line-ending = "auto"
@@ -36,4 +36,5 @@ CUSTOM_PATHS_MAP = {
36
36
  "get-asset-attribute": "{prefix}/assets/{asset}/get-attribute/",
37
37
  "decrypt-secret": "{prefix}/secrets/decrypt/",
38
38
  "routine-status": "{prefix}/component/routines/{routine}/update_status/",
39
+ "server-status": "{prefix}/server/servers/{server}/update-status/",
39
40
  }
@@ -0,0 +1,36 @@
1
+ from abc import abstractmethod
2
+ from typing import Any, TypedDict
3
+
4
+ from splight_lib.abstract.client import AbstractRemoteClient, QuerySet
5
+
6
+
7
+ class Records(TypedDict):
8
+ collection: str
9
+ records: list[dict[str, Any]]
10
+
11
+
12
+ class AbstractDatalakeClient(AbstractRemoteClient):
13
+ def get(self, *args, **kwargs) -> QuerySet:
14
+ kwargs["get_func"] = "_get"
15
+ kwargs["count_func"] = "None"
16
+ return QuerySet(self, *args, **kwargs)
17
+
18
+ async def async_get(self, *args, **kwargs):
19
+ # TODO: consider using an async QuerySet
20
+ return await self._async_get(*args, **kwargs)
21
+
22
+ @abstractmethod
23
+ def save(self, records: Records) -> list[dict]:
24
+ pass
25
+
26
+ @abstractmethod
27
+ async def async_save(self, records: Records) -> list[dict]:
28
+ pass
29
+
30
+ @abstractmethod
31
+ def _get(self, request: dict) -> list[dict]:
32
+ pass
33
+
34
+ @abstractmethod
35
+ async def _async_get(self, request: dict) -> list[dict]:
36
+ pass
@@ -0,0 +1,31 @@
1
+ from enum import Enum
2
+
3
+
4
+ class StepName(str, Enum):
5
+ ADD_FIELDS = "addFields"
6
+ ADD_TO_SET = "addToSet"
7
+ BUCKET = "bucket"
8
+ BUCKET_AUTO = "bucketAuto"
9
+ BUCKET_GROUP = "bucketGroup"
10
+ COUNT = "count"
11
+ FACET = "facet"
12
+ GRAPH_LOOKUP = "graphLookup"
13
+ GROUP = "group"
14
+ INDEX_STATS = "indexStats"
15
+ LIMIT = "limit"
16
+ LIST_SESSIONS = "listSessions"
17
+ LOOKUP = "lookup"
18
+ MATCH = "match"
19
+ MERGE = "merge"
20
+ OUT = "out"
21
+ PLAN_CACHE_STATS = "planCacheStats"
22
+ PROJECT = "project"
23
+ REDACT = "redact"
24
+ REPLACE_ROOT = "replaceRoot"
25
+ REPLACE_WITH = "replaceWith"
26
+ SAMPLE = "sample"
27
+ SET = "set"
28
+ SKIP = "skip"
29
+ SORT = "sort"
30
+ UNSET = "unset"
31
+ UNWIND = "unwind"
@@ -0,0 +1,12 @@
1
+ class DatalakeRequestError(Exception):
2
+ def __init__(self, status_code: int, message: str):
3
+ self._msg = f"Request failed with status code {status_code}: {message}"
4
+ super().__init__(self._msg)
5
+
6
+
7
+ class InvalidCollectionName(Exception):
8
+ def __init__(self, collection: str):
9
+ self._msg = f"Collection {collection} is not a valid collection"
10
+
11
+ def __str__(self) -> str:
12
+ return self._msg
@@ -0,0 +1,252 @@
1
+ from threading import Lock, Thread
2
+ from time import sleep
3
+
4
+ from furl import furl
5
+ from retry import retry
6
+
7
+ from splight_lib.auth import SplightAuthToken
8
+ from splight_lib.client.datalake.abstract import (
9
+ AbstractDatalakeClient,
10
+ Records,
11
+ )
12
+ from splight_lib.client.datalake.buffer import DatalakeDocumentBuffer
13
+ from splight_lib.client.datalake.exceptions import DatalakeRequestError
14
+ from splight_lib.client.exceptions import SPLIGHT_REQUEST_EXCEPTIONS
15
+ from splight_lib.constants import DL_BUFFER_SIZE, DL_BUFFER_TIMEOUT
16
+ from splight_lib.logging._internal import LogTags, get_splight_logger
17
+ from splight_lib.restclient import SplightRestClient
18
+
19
+ logger = get_splight_logger()
20
+
21
+ EXCEPTIONS = (*SPLIGHT_REQUEST_EXCEPTIONS, DatalakeRequestError)
22
+
23
+
24
+ class SyncRemoteDatalakeClient(AbstractDatalakeClient):
25
+ _PREFIX = "/data"
26
+
27
+ def __init__(
28
+ self, base_url: str, access_id: str, secret_key: str, *args, **kwargs
29
+ ):
30
+ super().__init__()
31
+ self._base_url = furl(base_url)
32
+ token = SplightAuthToken(
33
+ access_key=access_id,
34
+ secret_key=secret_key,
35
+ )
36
+ self._restclient = SplightRestClient()
37
+ self._restclient.update_headers(token.header)
38
+ logger.debug(
39
+ "Remote datalake client initialized.", tags=LogTags.DATALAKE
40
+ )
41
+
42
+ @retry(EXCEPTIONS, tries=3, delay=2, jitter=1)
43
+ def save(self, records: Records) -> list[dict]:
44
+ url = self._base_url / f"{self._PREFIX}/write"
45
+ response = self._restclient.post(url, json=records)
46
+ if response.is_error:
47
+ raise DatalakeRequestError(response.status_code, response.text)
48
+ return records["records"]
49
+
50
+ @retry(EXCEPTIONS, tries=3, delay=2, jitter=1)
51
+ async def async_save(
52
+ self,
53
+ records: Records,
54
+ ) -> list[dict]:
55
+ # POST /data/write
56
+ url = self._base_url / f"{self._PREFIX}/write"
57
+ response = await self._restclient.async_post(url, json=records)
58
+ if response.is_error:
59
+ raise DatalakeRequestError(response.status_code, response.text)
60
+ return records["records"]
61
+
62
+ @retry(EXCEPTIONS, tries=3, delay=2, jitter=1)
63
+ def _get(self, request: dict) -> list[dict]:
64
+ url = self._base_url / f"{self._PREFIX}/read"
65
+ response = self._restclient.post(url, json=request)
66
+ if response.is_error:
67
+ raise DatalakeRequestError(response.status_code, response.text)
68
+ return response.json()
69
+
70
+ @retry(EXCEPTIONS, tries=3, delay=2, jitter=1)
71
+ async def _async_get(self, request: dict) -> list[dict]:
72
+ url = self._base_url / f"{self._PREFIX}/read"
73
+ response = await self._restclient.async_post(url, json=request)
74
+ if response.is_error:
75
+ raise DatalakeRequestError(response.status_code, response.text)
76
+ return response.json()
77
+
78
+
79
+ class BufferedAsyncRemoteDatalakeClient(SyncRemoteDatalakeClient):
80
+ _PREFIX = "data"
81
+
82
+ def __init__(
83
+ self,
84
+ base_url: str,
85
+ access_id: str,
86
+ secret_key: str,
87
+ buffer_size: int = DL_BUFFER_SIZE,
88
+ buffer_timeout: float = DL_BUFFER_TIMEOUT,
89
+ *args,
90
+ **kwargs,
91
+ ):
92
+ super().__init__(
93
+ base_url,
94
+ access_id,
95
+ secret_key,
96
+ buffer_size,
97
+ buffer_timeout,
98
+ *args,
99
+ **kwargs,
100
+ )
101
+ self._base_url = furl(base_url)
102
+ token = SplightAuthToken(
103
+ access_key=access_id,
104
+ secret_key=secret_key,
105
+ )
106
+ self._restclient = SplightRestClient()
107
+ self._restclient.update_headers(token.header)
108
+
109
+ logger.debug(
110
+ "Initializing buffer with size %s and timeout %s",
111
+ buffer_size,
112
+ buffer_timeout,
113
+ )
114
+ self._data_buffers = {
115
+ "default": DatalakeDocumentBuffer(buffer_size, buffer_timeout),
116
+ "routine_evaluations": DatalakeDocumentBuffer(
117
+ buffer_size, buffer_timeout
118
+ ),
119
+ }
120
+ self._lock = Lock()
121
+ self._flush_thread = Thread(target=self._flusher, daemon=True)
122
+ self._flush_thread.start()
123
+ logger.debug(
124
+ "Buffered Remote datalake client initialized.",
125
+ tags=LogTags.DATALAKE,
126
+ )
127
+
128
+ def save(self, records: Records) -> list[dict]:
129
+ logger.debug("Saving documents in datalake", tags=LogTags.DATALAKE)
130
+ collection = records["collection"]
131
+ instances = records["records"]
132
+ buffer = self._data_buffers[collection]
133
+ with self._lock:
134
+ if buffer.should_flush():
135
+ logger.debug(
136
+ "Flushing datalake buffer with %s elements",
137
+ len(buffer.data),
138
+ )
139
+ self._send_documents(collection, buffer.data)
140
+ buffer.reset()
141
+ buffer.add_documents(instances)
142
+ return instances
143
+
144
+ def _flusher(self):
145
+ while True:
146
+ for collection, buffer in self._data_buffers.items():
147
+ self._flush_buffer(collection, buffer)
148
+ sleep(0.5)
149
+
150
+ def _flush_buffer(
151
+ self, collection: str, buffer: DatalakeDocumentBuffer
152
+ ) -> None:
153
+ with self._lock:
154
+ if buffer.should_flush():
155
+ try:
156
+ logger.debug(
157
+ "Flushing datalake buffer with %s elements",
158
+ len(buffer.data),
159
+ )
160
+ self._send_documents(collection, buffer.data)
161
+ buffer.reset()
162
+ except Exception:
163
+ logger.error("Unable to save documents", exc_info=True)
164
+
165
+ @retry(EXCEPTIONS, tries=3, delay=2, jitter=1)
166
+ def _send_documents(self, collection: str, docs: list[dict]) -> list[dict]:
167
+ url = self._base_url / f"{self._PREFIX}/write"
168
+ data = {
169
+ "collection": collection,
170
+ "records": docs,
171
+ }
172
+ response = self._restclient.post(url, json=data)
173
+ if response.is_error:
174
+ raise DatalakeRequestError(response.status_code, response.text)
175
+ return docs
176
+
177
+
178
+ class BufferedSyncRemoteDataClient(SyncRemoteDatalakeClient):
179
+ _PREFIX = "data"
180
+
181
+ def __init__(
182
+ self,
183
+ base_url: str,
184
+ access_id: str,
185
+ secret_key: str,
186
+ buffer_size: int = DL_BUFFER_SIZE,
187
+ buffer_timeout: float = DL_BUFFER_TIMEOUT,
188
+ *args,
189
+ **kwargs,
190
+ ):
191
+ super().__init__(
192
+ base_url,
193
+ access_id,
194
+ secret_key,
195
+ buffer_size,
196
+ buffer_timeout,
197
+ *args,
198
+ **kwargs,
199
+ )
200
+ self._base_url = furl(base_url)
201
+ token = SplightAuthToken(
202
+ access_key=access_id,
203
+ secret_key=secret_key,
204
+ )
205
+ self._restclient = SplightRestClient()
206
+ self._restclient.update_headers(token.header)
207
+
208
+ logger.debug(
209
+ "Initializing buffer with size %s and timeout %s",
210
+ buffer_size,
211
+ buffer_timeout,
212
+ tags=LogTags.DATALAKE,
213
+ )
214
+ self._data_buffers = {
215
+ "default": DatalakeDocumentBuffer(buffer_size, buffer_timeout),
216
+ "routine_evaluations": DatalakeDocumentBuffer(
217
+ buffer_size, buffer_timeout
218
+ ),
219
+ }
220
+ self._lock = Lock()
221
+ logger.debug(
222
+ "Synchronous Buffered Remote datalake client initialized.",
223
+ tags=LogTags.DATALAKE,
224
+ )
225
+
226
+ def save(self, records: Records) -> list[dict]:
227
+ logger.debug("Saving documents in datalake", tag=LogTags.DATALAKE)
228
+ collection = records["collection"]
229
+ buffer = self._data_buffers[collection]
230
+ with self._lock:
231
+ buffer.add_documents(records["records"])
232
+ if buffer.should_flush():
233
+ logger.debug(
234
+ "Flushing datalake buffer with %s elements",
235
+ len(buffer.data),
236
+ tags=LogTags.DATALAKE,
237
+ )
238
+ self._send_documents(collection, buffer.data)
239
+ buffer.reset()
240
+ return records["records"]
241
+
242
+ @retry(EXCEPTIONS, tries=3, delay=2, jitter=1)
243
+ def _send_documents(self, collection: str, docs: list[dict]) -> list[dict]:
244
+ url = self._base_url / f"{self._PREFIX}/write"
245
+ data = {
246
+ "collection": collection,
247
+ "records": docs,
248
+ }
249
+ response = self._restclient.post(url, json=data)
250
+ if response.is_error:
251
+ raise DatalakeRequestError(response.status_code, response.text)
252
+ return docs
@@ -40,4 +40,5 @@ class InstanceNotFound(Exception):
40
40
  return self._msg
41
41
 
42
42
 
43
- class InvalidModel(Exception): ...
43
+ class InvalidModel(Exception):
44
+ pass
@@ -65,6 +65,7 @@ def test_save(mocker: MockerFixture):
65
65
  )
66
66
  collection = "collection_name"
67
67
  instances = [{"key": "value"}]
68
- result = client.save(collection=collection, instances=instances)
68
+ records = {"collection": collection, "records": instances}
69
+ result = client.save(records)
69
70
  mock_post.assert_called_once()
70
71
  assert result == instances
@@ -9,7 +9,6 @@ from splight_lib.component.exceptions import (
9
9
  ParameterDependencyError,
10
10
  )
11
11
  from splight_lib.constants import DESCRIPTION_MAX_LENGTH
12
- from splight_lib.models.base import SplightDatalakeBaseModel
13
12
  from splight_lib.models.component import (
14
13
  Component,
15
14
  ComponentObjectInstance,
@@ -22,6 +21,7 @@ from splight_lib.models.component import (
22
21
  Routine,
23
22
  get_field_value,
24
23
  )
24
+ from splight_lib.models.datalake_base import SplightDatalakeBaseModel
25
25
  from splight_lib.utils.custom_model import create_custom_model
26
26
 
27
27
  VALID_PARAMETER_VALUES = {
@@ -10,7 +10,8 @@ from splight_lib.execution.trigger import IntervalTrigger
10
10
 
11
11
  class BaseTask(ABC):
12
12
  @abstractmethod
13
- def as_job(self) -> Dict: ...
13
+ def as_job(self) -> Dict:
14
+ pass
14
15
 
15
16
 
16
17
  class PeriodicTask(BaseTask):
@@ -18,6 +18,7 @@ from splight_lib.models.dashboard import (
18
18
  Filter,
19
19
  Tab,
20
20
  )
21
+ from splight_lib.models.datalake import DataRequest, PipelineStep, Trace
21
22
  from splight_lib.models.file import File
22
23
  from splight_lib.models.function import Function, FunctionItem, QueryFilter
23
24
  from splight_lib.models.hub import HubComponent
@@ -46,6 +47,7 @@ __all__ = [
46
47
  ComponentObject,
47
48
  ComponentObjectInstance,
48
49
  Dashboard,
50
+ DataRequest,
49
51
  File,
50
52
  Filter,
51
53
  Function,
@@ -56,6 +58,7 @@ __all__ = [
56
58
  QueryFilter,
57
59
  Metadata,
58
60
  Number,
61
+ PipelineStep,
59
62
  RoutineEvaluation,
60
63
  Secret,
61
64
  Server,
@@ -65,4 +68,5 @@ __all__ = [
65
68
  RoutineObject,
66
69
  RoutineObjectInstance,
67
70
  Tab,
71
+ Trace,
68
72
  ]
@@ -1,6 +1,9 @@
1
1
  from datetime import datetime
2
2
 
3
- from splight_lib.models.base import ResourceSummary, SplightDatabaseBaseModel
3
+ from splight_lib.models.database_base import (
4
+ ResourceSummary,
5
+ SplightDatabaseBaseModel,
6
+ )
4
7
 
5
8
 
6
9
  class SetPoint(SplightDatabaseBaseModel):
@@ -14,7 +14,7 @@ from strenum import LowercaseStrEnum, UppercaseStrEnum
14
14
  from typing_extensions import TypedDict
15
15
 
16
16
  from splight_lib.constants import DESCRIPTION_MAX_LENGTH
17
- from splight_lib.models.base import SplightDatabaseBaseModel
17
+ from splight_lib.models.database_base import SplightDatabaseBaseModel
18
18
  from splight_lib.models.exceptions import (
19
19
  InvalidAlertConfiguration,
20
20
  MissingAlertItemExpression,
@@ -6,7 +6,10 @@ from pydantic import BaseModel, Field
6
6
 
7
7
  from splight_lib.constants import DESCRIPTION_MAX_LENGTH
8
8
  from splight_lib.models.attribute import Attribute
9
- from splight_lib.models.base import ResourceSummary, SplightDatabaseBaseModel
9
+ from splight_lib.models.database_base import (
10
+ ResourceSummary,
11
+ SplightDatabaseBaseModel,
12
+ )
10
13
  from splight_lib.models.exceptions import MethodNotAllowed
11
14
  from splight_lib.models.metadata import Metadata
12
15
  from splight_lib.models.tag import Tag
@@ -3,7 +3,7 @@ from typing import Optional
3
3
 
4
4
  from strenum import PascalCaseStrEnum
5
5
 
6
- from splight_lib.models.base import SplightDatabaseBaseModel
6
+ from splight_lib.models.database_base import SplightDatabaseBaseModel
7
7
 
8
8
 
9
9
  class AttributeType(PascalCaseStrEnum):
@@ -20,11 +20,9 @@ from splight_lib.constants import DESCRIPTION_MAX_LENGTH
20
20
  from splight_lib.execution.scheduling import Crontab
21
21
  from splight_lib.models.asset import Asset
22
22
  from splight_lib.models.attribute import Attribute
23
- from splight_lib.models.base import (
24
- SplightDatabaseBaseModel,
25
- SplightDatalakeBaseModel,
26
- )
27
23
  from splight_lib.models.data_address import DataAddresses as DLDataAddress
24
+ from splight_lib.models.database_base import SplightDatabaseBaseModel
25
+ from splight_lib.models.datalake_base import SplightDatalakeBaseModel
28
26
  from splight_lib.models.exceptions import InvalidObjectInstance
29
27
  from splight_lib.models.file import File
30
28
  from splight_lib.models.secret import Secret
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional
3
3
  from pydantic import Field
4
4
 
5
5
  from splight_lib.constants import DESCRIPTION_MAX_LENGTH
6
- from splight_lib.models.base import SplightDatabaseBaseModel
6
+ from splight_lib.models.database_base import SplightDatabaseBaseModel
7
7
 
8
8
 
9
9
  class Filter(SplightDatabaseBaseModel):
@@ -39,9 +39,7 @@ class ChartItem(SplightDatabaseBaseModel):
39
39
  aggregate_period: Optional[str] = None
40
40
  source: Optional[str] = None
41
41
  source_label: Optional[str] = None
42
- source_type: Optional[str] = (
43
- None # TODO: define options (component, native)
44
- )
42
+ source_type: Optional[str] = None
45
43
  source_component_label: Optional[str] = None
46
44
  source_component_id: Optional[str] = None
47
45
  output_format: Optional[str] = None
@@ -0,0 +1,79 @@
1
+ from enum import auto
2
+ from pathlib import Path
3
+ from typing import Dict, List, Optional, TypeVar
4
+
5
+ from pydantic import BaseModel, PrivateAttr
6
+ from strenum import LowercaseStrEnum
7
+
8
+ from splight_lib.client.database import DatabaseClientBuilder
9
+ from splight_lib.client.database.abstract import AbstractDatabaseClient
10
+ from splight_lib.settings import settings
11
+
12
+ FilePath = TypeVar("FilePath", str, Path)
13
+
14
+
15
+ class PrivacyPolicy(LowercaseStrEnum):
16
+ PUBLIC = auto()
17
+ PRIVATE = auto()
18
+
19
+
20
+ class ResourceSummary(BaseModel):
21
+ id: str | None = None
22
+ name: str
23
+
24
+
25
+ class SplightDatabaseBaseModel(BaseModel):
26
+ _db_client: AbstractDatabaseClient = PrivateAttr()
27
+
28
+ def __init__(self, **data):
29
+ super().__init__(**data)
30
+ self._db_client = self.__get_database_client()
31
+
32
+ @staticmethod
33
+ def get_event_name(type_: str, action: str) -> str:
34
+ return f"{type_.lower()}-{action.lower()}"
35
+
36
+ def save(self):
37
+ files_dict = self._get_model_files_dict()
38
+ saved = self._db_client.save(
39
+ self.__class__.__name__,
40
+ self.model_dump(exclude_none=True, mode="json"),
41
+ files=files_dict,
42
+ )
43
+ instance = self.model_validate(saved)
44
+ for key, field in self.model_fields.items():
45
+ setattr(self, key, getattr(instance, key))
46
+
47
+ def delete(self):
48
+ self._db_client.delete(
49
+ resource_name=self.__class__.__name__, id=self.id
50
+ )
51
+
52
+ @classmethod
53
+ def retrieve(cls, resource_id: str) -> "SplightDatabaseBaseModel":
54
+ db_client = cls.__get_database_client()
55
+ instance = db_client.get(
56
+ resource_name=cls.__name__, id=resource_id, first=True
57
+ )
58
+ return cls.model_validate(instance) if instance else None
59
+
60
+ @classmethod
61
+ def list(cls, **params: Dict) -> List["SplightDatabaseBaseModel"]:
62
+ db_client = cls.__get_database_client()
63
+ instances = db_client.get(resource_name=cls.__name__, **params)
64
+ instances = [cls.model_validate(item) for item in instances]
65
+ return instances
66
+
67
+ @staticmethod
68
+ def __get_database_client() -> AbstractDatabaseClient:
69
+ db_client = DatabaseClientBuilder.build(
70
+ parameters={
71
+ "base_url": settings.SPLIGHT_PLATFORM_API_HOST,
72
+ "access_id": settings.SPLIGHT_ACCESS_ID,
73
+ "secret_key": settings.SPLIGHT_SECRET_KEY,
74
+ },
75
+ )
76
+ return db_client
77
+
78
+ def _get_model_files_dict(self) -> Optional[Dict]:
79
+ return None