splight-lib 5.10.4__tar.gz → 5.11.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 (108) hide show
  1. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/PKG-INFO +1 -1
  2. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/pyproject.toml +1 -1
  3. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/database/classmap.py +5 -1
  4. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/database/remote_client.py +6 -2
  5. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/logging/_internal.py +1 -0
  6. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/__init__.py +4 -0
  7. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/component.py +1 -16
  8. splight_lib-5.11.1.dev0/splight_lib/models/dashboard.py +98 -0
  9. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/exceptions.py +9 -0
  10. splight_lib-5.11.1.dev0/splight_lib/models/hub_server.py +120 -0
  11. splight_lib-5.11.1.dev0/splight_lib/models/server.py +195 -0
  12. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/solution.py +2 -2
  13. splight_lib-5.11.1.dev0/splight_lib/models/variable_types.py +18 -0
  14. splight_lib-5.11.1.dev0/splight_lib/server/__init__.py +5 -0
  15. splight_lib-5.11.1.dev0/splight_lib/server/exceptions.py +3 -0
  16. splight_lib-5.11.1.dev0/splight_lib/server/server.py +15 -0
  17. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/solution/solution.py +1 -1
  18. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/utils/hub.py +1 -0
  19. splight_lib-5.10.4/splight_lib/models/dashboard.py +0 -93
  20. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/LICENSE.txt +0 -0
  21. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/README.md +0 -0
  22. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/__init__.py +0 -0
  23. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/abstract/__init__.py +0 -0
  24. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/abstract/client.py +0 -0
  25. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/auth/__init__.py +0 -0
  26. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/auth/exceptions.py +0 -0
  27. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/auth/mac_auth.py +0 -0
  28. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/auth/token.py +0 -0
  29. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/__init__.py +0 -0
  30. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/database/__init__.py +0 -0
  31. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/database/abstract.py +0 -0
  32. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/database/builder.py +0 -0
  33. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/__init__.py +0 -0
  34. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/abstract.py +0 -0
  35. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/buffer.py +0 -0
  36. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/builder.py +0 -0
  37. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/constants.py +0 -0
  38. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/exceptions.py +0 -0
  39. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/datalake/remote_client.py +0 -0
  40. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/exceptions.py +0 -0
  41. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/file_handler.py +0 -0
  42. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/filter.py +0 -0
  43. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/hub/__init__.py +0 -0
  44. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/hub/abstract.py +0 -0
  45. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/hub/client.py +0 -0
  46. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/tests/test_database.py +0 -0
  47. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/client/tests/test_datalake.py +0 -0
  48. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/component/__init__.py +0 -0
  49. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/component/abstract.py +0 -0
  50. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/component/exceptions.py +0 -0
  51. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/component/spec.py +0 -0
  52. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/component/tests/test_abstract.py +0 -0
  53. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/component/tests/test_spec.py +0 -0
  54. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/conftest.py +0 -0
  55. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/constants.py +0 -0
  56. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/encryption.py +0 -0
  57. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/__init__.py +0 -0
  58. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/engine.py +0 -0
  59. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/exceptions.py +0 -0
  60. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/scheduling.py +0 -0
  61. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/task.py +0 -0
  62. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/tests/test_execution.py +0 -0
  63. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/tests/test_scheduling.py +0 -0
  64. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/execution/trigger.py +0 -0
  65. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/logging/__init__.py +0 -0
  66. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/logging/component.py +0 -0
  67. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/logging/constants.py +0 -0
  68. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/logging/logging.py +0 -0
  69. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/logging/tests/test_logging.py +0 -0
  70. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/actions.py +0 -0
  71. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/alert.py +0 -0
  72. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/asset.py +0 -0
  73. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/attribute.py +0 -0
  74. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/data_address.py +0 -0
  75. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/database_base.py +0 -0
  76. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/datalake.py +0 -0
  77. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/datalake_base.py +0 -0
  78. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/file.py +0 -0
  79. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/function.py +0 -0
  80. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/generic.py +0 -0
  81. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/hub.py +0 -0
  82. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/hub_solution.py +0 -0
  83. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/metadata.py +0 -0
  84. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/native.py +0 -0
  85. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/secret.py +0 -0
  86. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/tag.py +0 -0
  87. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/tests/models.json +0 -0
  88. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/tests/test_component_object_instance.py +0 -0
  89. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/tests/test_database_model.py +0 -0
  90. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/tests/test_metadata.py +0 -0
  91. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/models/tests/test_models.py +0 -0
  92. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/restclient/__init__.py +0 -0
  93. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/restclient/client.py +0 -0
  94. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/restclient/exceptions.py +0 -0
  95. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/restclient/tests/test_restclient.py +0 -0
  96. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/restclient/types.py +0 -0
  97. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/settings.py +0 -0
  98. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/solution/__init__.py +0 -0
  99. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/solution/exceptions.py +0 -0
  100. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/stringcase.py +0 -0
  101. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/testing/__init__.py +0 -0
  102. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/tests/FakeProc.py +0 -0
  103. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/tests/asset_geometries.json +0 -0
  104. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/tests/test_api_contracts.py +0 -0
  105. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/tests/test_encryption.py +0 -0
  106. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/utils/__init__.py +0 -0
  107. {splight_lib-5.10.4 → splight_lib-5.11.1.dev0}/splight_lib/utils/custom_model.py +0 -0
  108. {splight_lib-5.10.4 → splight_lib-5.11.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.10.4
3
+ Version: 5.11.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.10.4"
3
+ version = "5.11.1dev0"
4
4
  description = "Splight Library"
5
5
  authors = ["Splight Dev <dev@splight-ae.com>"]
6
6
  readme = "README.md"
@@ -12,7 +12,7 @@ MODEL_NAME_MAP = {
12
12
  "attribute": f"{ENGINE_PREFIX}/attributes/",
13
13
  "metadata": f"{ENGINE_PREFIX}/metadata/",
14
14
  "chart": f"{ENGINE_PREFIX}/dashboard/charts/",
15
- "chartitem": f"{ENGINE_PREFIX}/dashboard/chartitems/",
15
+ "chartitem": f"{ENGINE_PREFIX}/dashboard/items/",
16
16
  "component": f"{ENGINE_PREFIX}/component/components/",
17
17
  "componentobject": f"{ENGINE_PREFIX}/component/objects/",
18
18
  "dashboard": f"{ENGINE_PREFIX}/dashboard/dashboards/",
@@ -24,9 +24,12 @@ MODEL_NAME_MAP = {
24
24
  "secret": f"{ENGINE_PREFIX}/secrets/",
25
25
  "setpoint": f"{ENGINE_PREFIX}/setpoints/",
26
26
  "solution": f"{ENGINE_PREFIX}/solution/solutions/",
27
+ "server": f"{ENGINE_PREFIX}/server/servers/",
27
28
  "tab": f"{ENGINE_PREFIX}/dashboard/tabs/",
28
29
  "hubsolution": f"{HUB_PREFIX}/solution/solutions/",
29
30
  "hubsolutionversion": f"{HUB_PREFIX}/solution/versions/",
31
+ "hubserver": f"{HUB_PREFIX}/server/servers/",
32
+ "hubserverversion": f"{HUB_PREFIX}/server/versions/",
30
33
  }
31
34
 
32
35
  CUSTOM_PATHS_MAP = {
@@ -34,4 +37,5 @@ CUSTOM_PATHS_MAP = {
34
37
  "get-asset-attribute": "{prefix}/assets/{asset}/get-attribute/",
35
38
  "decrypt-secret": "{prefix}/secrets/decrypt/",
36
39
  "routine-status": "{prefix}/component/routines/{routine}/update_status/",
40
+ "server-status": "{prefix}/server/servers/{server}/update-status/",
37
41
  }
@@ -211,9 +211,13 @@ class RemoteDatabaseClient(AbstractDatabaseClient, AbstractRemoteClient):
211
211
  ------
212
212
  InvalidModelName thrown when the model name is not correct.
213
213
  """
214
- if resource_name.lower() not in ["file", "hubsolutionversion"]:
214
+ if resource_name.lower() not in [
215
+ "file",
216
+ "hubsolutionversion",
217
+ "hubserverversion",
218
+ ]:
215
219
  raise InvalidModel(
216
- "Only files and hub solution can be downloaded."
220
+ "Only files, hub solution and hub servers can be downloaded."
217
221
  )
218
222
  api_path = self._get_api_path(resource_name)
219
223
  resource_id = instance.get("id")
@@ -24,6 +24,7 @@ class LogTags(UppercaseStrEnum):
24
24
  DATABASE = auto()
25
25
  DATALAKE = auto()
26
26
  CACHE = auto()
27
+ SERVER = auto()
27
28
 
28
29
 
29
30
  # don't used now
@@ -22,10 +22,12 @@ from splight_lib.models.datalake import DataRequest, PipelineStep, Trace
22
22
  from splight_lib.models.file import File
23
23
  from splight_lib.models.function import Function, FunctionItem, QueryFilter
24
24
  from splight_lib.models.hub import HubComponent
25
+ from splight_lib.models.hub_server import HubServer
25
26
  from splight_lib.models.hub_solution import HubSolution
26
27
  from splight_lib.models.metadata import Metadata
27
28
  from splight_lib.models.native import Boolean, Number, String
28
29
  from splight_lib.models.secret import Secret
30
+ from splight_lib.models.server import Server
29
31
  from splight_lib.models.solution import Solution
30
32
  from splight_lib.models.tag import Tag
31
33
 
@@ -53,12 +55,14 @@ __all__ = [
53
55
  FunctionItem,
54
56
  HubSolution,
55
57
  HubComponent,
58
+ HubServer,
56
59
  QueryFilter,
57
60
  Metadata,
58
61
  Number,
59
62
  PipelineStep,
60
63
  RoutineEvaluation,
61
64
  Secret,
65
+ Server,
62
66
  SetPoint,
63
67
  Solution,
64
68
  String,
@@ -1,12 +1,10 @@
1
1
  import re
2
2
  import warnings
3
3
  from abc import ABC, abstractmethod
4
- from datetime import datetime
5
4
  from enum import auto
6
5
  from typing import Any, ClassVar, Dict, List, Literal, Optional, Type, Union
7
6
 
8
7
  from pydantic import (
9
- AnyUrl,
10
8
  BaseModel,
11
9
  Field,
12
10
  PrivateAttr,
@@ -17,7 +15,6 @@ from pydantic import (
17
15
  from strenum import LowercaseStrEnum, PascalCaseStrEnum
18
16
 
19
17
  from splight_lib.constants import DESCRIPTION_MAX_LENGTH
20
- from splight_lib.execution.scheduling import Crontab
21
18
  from splight_lib.models.asset import Asset
22
19
  from splight_lib.models.attribute import Attribute
23
20
  from splight_lib.models.data_address import DataAddresses as DLDataAddress
@@ -26,6 +23,7 @@ from splight_lib.models.datalake_base import SplightDatalakeBaseModel
26
23
  from splight_lib.models.exceptions import InvalidObjectInstance
27
24
  from splight_lib.models.file import File
28
25
  from splight_lib.models.secret import Secret
26
+ from splight_lib.models.variable_types import CUSTOM_TYPES, NATIVE_TYPES
29
27
 
30
28
  warnings.filterwarnings("ignore", category=RuntimeWarning)
31
29
 
@@ -211,19 +209,6 @@ class Component(SplightDatabaseBaseModel):
211
209
  routines: List[Routine] = []
212
210
 
213
211
 
214
- NATIVE_TYPES = {
215
- "int": int,
216
- "bool": bool,
217
- "str": str,
218
- "float": float,
219
- "datetime": datetime,
220
- "url": AnyUrl,
221
- }
222
-
223
- CUSTOM_TYPES = {
224
- "crontab": Crontab,
225
- }
226
-
227
212
  DATABASE_TYPES = {
228
213
  "Component": Component,
229
214
  "Asset": Asset,
@@ -0,0 +1,98 @@
1
+ from enum import auto
2
+ from typing import Annotated, Any
3
+
4
+ from pydantic import BaseModel, Field
5
+ from strenum import UppercaseStrEnum
6
+
7
+ from splight_lib.constants import DESCRIPTION_MAX_LENGTH
8
+ from splight_lib.models.database_base import (
9
+ ResourceSummary,
10
+ SplightDatabaseBaseModel,
11
+ )
12
+
13
+
14
+ class ChartItemType(UppercaseStrEnum):
15
+ QUERY = auto()
16
+ EXPRESSION = auto()
17
+
18
+
19
+ class Filter(SplightDatabaseBaseModel):
20
+ id: str | None = None
21
+ chart_item: str
22
+ operator: str | None = None
23
+ key: str | None = None
24
+ value: str | None = None
25
+ label: str | None = None
26
+
27
+
28
+ class AdvancedFilter(SplightDatabaseBaseModel):
29
+ id: str | None = None
30
+ chart_item: str
31
+ operator: str | None = None
32
+ key: str | None = None
33
+ field: str | None = None
34
+ value: str | None = None
35
+
36
+
37
+ class ChartItem(BaseModel):
38
+ id: str | None = None
39
+ type: ChartItemType = ChartItemType.QUERY
40
+ ref_id: str
41
+ # chart: str | None = None
42
+ label: str
43
+ order: int | None = None
44
+ color: str | None = None
45
+ hidden: bool = False
46
+ query_filter_asset: ResourceSummary | None = None
47
+ query_filter_attribute: ResourceSummary | None = None
48
+ query_group_unit: str | None = None
49
+ query_group_function: str | None = None
50
+ query_sort_field: str | None = None
51
+ query_sort_direction: int | None = -1
52
+ query_limit: int = 10000
53
+ query_plain: str = ""
54
+ position_x: int | None = None
55
+ position_y: int | None = None
56
+ expression: str | None = None
57
+ expression_plain: str | None = None
58
+
59
+
60
+ class Chart(SplightDatabaseBaseModel):
61
+ id: str | None = None
62
+ name: str
63
+ description: Annotated[
64
+ str | None, Field(max_length=DESCRIPTION_MAX_LENGTH)
65
+ ] = None
66
+ items: list[ChartItem] | None = None
67
+ tab: str
68
+ position_x: int | None = None
69
+ position_y: int | None = None
70
+ height: int | None = 28
71
+ width: int | None = 80
72
+ min_height: int | None = 14
73
+ min_width: int | None = 14
74
+ type: str | None = None
75
+ timestamp_gte: str | None = None
76
+ timestamp_lte: str | None = None
77
+ refresh_interval: str | None = None
78
+ relative_window_time: str | None = None
79
+ y_axis_max_limit: int | None = None
80
+ y_axis_min_limit: int | None = None
81
+ config: dict[str, Any] | None = None
82
+ chart_items: list[ChartItem] = []
83
+
84
+
85
+ class Tab(SplightDatabaseBaseModel):
86
+ id: str | None = None
87
+ name: str
88
+ charts: list[Chart] | None = None
89
+ order: int | None = None
90
+ dashboard: str
91
+
92
+
93
+ class Dashboard(SplightDatabaseBaseModel):
94
+ id: str | None = None
95
+ name: str
96
+ description: str | None = Field(
97
+ default=None, max_length=DESCRIPTION_MAX_LENGTH
98
+ )
@@ -66,6 +66,15 @@ class InvalidArgument(Exception):
66
66
  pass
67
67
 
68
68
 
69
+ class InvalidServerConfigType(Exception):
70
+ def __init__(self, name: str, type_: str):
71
+ msg = (
72
+ f"Config parameter {name} has an invalid type {type_}. The only "
73
+ "valid types are native and 'File'"
74
+ )
75
+ super().__init__(msg)
76
+
77
+
69
78
  class TraceAlreadyExistsError(Exception):
70
79
  def __init__(self, ref_id: str):
71
80
  msg = f"Trace with ref_id {ref_id} already exists"
@@ -0,0 +1,120 @@
1
+ import os
2
+ from glob import glob
3
+ from tempfile import NamedTemporaryFile
4
+ from typing import Annotated, Self
5
+
6
+ import py7zr
7
+ from pydantic import BaseModel, Field
8
+
9
+ from splight_lib.constants import DESCRIPTION_MAX_LENGTH
10
+ from splight_lib.models.component import InputParameter
11
+ from splight_lib.models.database_base import (
12
+ FilePath,
13
+ PrivacyPolicy,
14
+ SplightDatabaseBaseModel,
15
+ )
16
+ from splight_lib.utils.hub import (
17
+ COMPRESSION_TYPE,
18
+ README_FILE_1,
19
+ RUN_FILE,
20
+ SPEC_FILE,
21
+ get_ignore_pathspec,
22
+ get_spec,
23
+ )
24
+
25
+
26
+ class Port(BaseModel):
27
+ name: str | None = None
28
+ protocol: str = "tcp"
29
+ internal_port: int
30
+ exposed_port: int
31
+
32
+
33
+ class HubServer(SplightDatabaseBaseModel):
34
+ id: str | None = None
35
+ name: str
36
+ version: str
37
+ description: Annotated[
38
+ str | None, Field(max_length=DESCRIPTION_MAX_LENGTH)
39
+ ] = None
40
+ tags: Annotated[list[str] | None, Field()] = []
41
+ privacy_policy: PrivacyPolicy = PrivacyPolicy.PUBLIC
42
+
43
+ config: list[InputParameter] = []
44
+ ports: list[Port] = []
45
+ environment: list[dict[str, str]] = []
46
+
47
+ @classmethod
48
+ def upload(cls, path: FilePath, image_file: str) -> Self:
49
+ db_client = cls._SplightDatabaseBaseModel__get_database_client()
50
+ image_path = os.path.join(path, image_file)
51
+ spec = get_spec(path)
52
+ name = spec.get("name")
53
+ version = spec.get("version")
54
+
55
+ raw_hub_server = db_client.get(
56
+ resource_name=cls.__name__,
57
+ name=name,
58
+ version=version,
59
+ )
60
+ server = cls.model_validate(spec)
61
+ if raw_hub_server:
62
+ old_hub_server = cls.model_validate(raw_hub_server[0])
63
+ server.id = old_hub_server.id
64
+
65
+ server.save()
66
+
67
+ file_name = f"{name}-{version}.{COMPRESSION_TYPE}"
68
+ ignore_pathspec = get_ignore_pathspec(path)
69
+ versioned_path = f"{name}-{version}"
70
+ readme_path = os.path.join(path, README_FILE_1)
71
+ for req_file in [SPEC_FILE, RUN_FILE, README_FILE_1]:
72
+ if not os.path.exists(os.path.join(path, req_file)):
73
+ raise FileNotFoundError(f"Required file not found: {req_file}")
74
+ with py7zr.SevenZipFile(file_name, "w") as archive:
75
+ all_files = glob(f"{path}/**", recursive=True)
76
+ for filepath in all_files:
77
+ if ignore_pathspec and ignore_pathspec.match_file(filepath):
78
+ continue
79
+ if os.path.isdir(filepath):
80
+ continue
81
+ if image_file in filepath:
82
+ continue
83
+ rel_filename = os.path.relpath(filepath, path)
84
+ new_filepath = os.path.join(versioned_path, rel_filename)
85
+ archive.write(filepath, new_filepath)
86
+
87
+ data = server.model_dump()
88
+ try:
89
+ db_client.upload(
90
+ "hubserverversion",
91
+ instance=data,
92
+ file_path=file_name,
93
+ type_="source",
94
+ )
95
+ db_client.upload(
96
+ "hubserverversion",
97
+ instance=data,
98
+ file_path=readme_path,
99
+ type_="readme",
100
+ )
101
+ db_client.upload(
102
+ "hubserverversion",
103
+ instance=data,
104
+ file_path=image_path,
105
+ type_="image",
106
+ )
107
+ except Exception as exc:
108
+ raise exc
109
+ finally:
110
+ if os.path.exists(file_name):
111
+ os.remove(file_name)
112
+ return server
113
+
114
+ def download(self) -> NamedTemporaryFile:
115
+ db_client = self._SplightDatabaseBaseModel__get_database_client()
116
+ return db_client.download(
117
+ resource_name="hubserverversion",
118
+ instance=self.model_dump(),
119
+ type_="source",
120
+ )
@@ -0,0 +1,195 @@
1
+ import re
2
+ from collections import namedtuple
3
+ from enum import auto
4
+ from typing import Any, NamedTuple
5
+
6
+ from pydantic import BaseModel, Field, computed_field
7
+ from strenum import LowercaseStrEnum, PascalCaseStrEnum
8
+
9
+ from splight_lib.models.database_base import SplightDatabaseBaseModel
10
+ from splight_lib.models.exceptions import (
11
+ InvalidArgument,
12
+ InvalidServerConfigType,
13
+ )
14
+ from splight_lib.models.file import File
15
+ from splight_lib.models.hub_server import HubServer
16
+ from splight_lib.models.secret import Secret
17
+ from splight_lib.models.variable_types import CUSTOM_TYPES, NATIVE_TYPES
18
+
19
+ # warnings.filterwarnings("ignore", category=RuntimeWarning)
20
+
21
+ DATABASE_TYPES = {
22
+ "File": File,
23
+ }
24
+
25
+ DB_MODEL_TYPE_MAPPING = {
26
+ **CUSTOM_TYPES,
27
+ **NATIVE_TYPES,
28
+ **DATABASE_TYPES,
29
+ }
30
+
31
+
32
+ def get_field_value(field: dict):
33
+ field_multiple = field.get("multiple", False)
34
+ field_type = field.get("type")
35
+ field_value = field.get("value")
36
+
37
+ if not field_value:
38
+ return [] if field_multiple else None
39
+
40
+ if field_type in NATIVE_TYPES:
41
+ value = (
42
+ field_value
43
+ if not isinstance(field_value, str)
44
+ else parse_variable_string(field_value)
45
+ )
46
+ elif field.type in CUSTOM_TYPES:
47
+ value = CUSTOM_TYPES.get(field.type).from_string(field.value)
48
+ elif field_type in DATABASE_TYPES:
49
+ model_class = DATABASE_TYPES[field_type]
50
+ value = (
51
+ model_class.retrieve(field_value)
52
+ if not field_multiple
53
+ else [model_class.retrieve(item) for item in field_value]
54
+ )
55
+ return value
56
+
57
+
58
+ def get_model_class(config: BaseModel, name: str) -> NamedTuple:
59
+ config_class = namedtuple(name, [x.name for x in config])
60
+ return config_class
61
+
62
+
63
+ def load_server_config(
64
+ config: list[dict], config_class: NamedTuple
65
+ ) -> NamedTuple:
66
+ config_dict = {}
67
+ for item in config:
68
+ if item["type"] not in DB_MODEL_TYPE_MAPPING:
69
+ raise InvalidServerConfigType(item["name"], item["type"])
70
+ config_dict.update({item["name"]: get_field_value(item)})
71
+ config = config_class(**config_dict)
72
+ return config
73
+
74
+
75
+ def validate_in_range(value: int, min_value: int, max_value: int) -> None:
76
+ if not (min_value <= value <= max_value):
77
+ raise ValueError(
78
+ f"{value} is out of valid range ({min_value}-{max_value})"
79
+ )
80
+
81
+
82
+ def validate_protocol(protocol: str, valid_protocols: list[str]) -> None:
83
+ if protocol not in valid_protocols:
84
+ raise ValueError(
85
+ f"Protocol {protocol} is not valid. Must be 'tcp' or 'udp'"
86
+ )
87
+
88
+
89
+ def load_server_ports(
90
+ ports: list[dict], model_class: NamedTuple
91
+ ) -> namedtuple:
92
+ ports_dict = {}
93
+ for port in ports:
94
+ validate_in_range(int(port["internal_port"]), 0, 65535)
95
+ validate_in_range(int(port["exposed_port"]), 0, 65535)
96
+ validate_protocol(port["protocol"], ["tcp", "udp"])
97
+ ports_dict.update({port["name"]: port})
98
+ resources = model_class(**ports_dict)
99
+ return resources
100
+
101
+
102
+ def load_server_env_vars(env_vars: list[dict]) -> namedtuple:
103
+ ntuple = namedtuple("EnvVars", ["name", "value"])
104
+ return [ntuple(**env_var) for env_var in env_vars]
105
+
106
+
107
+ class ServerStatus(PascalCaseStrEnum):
108
+ RUNNING = auto()
109
+ FAILED = auto()
110
+ SUCCEEDED = auto()
111
+ PENDING = auto()
112
+ START_REQUESTED = auto()
113
+ STOP_REQUESTED = auto()
114
+ STOPPED = auto()
115
+ UNKNOWN = auto()
116
+
117
+
118
+ class PrivacyPolicy(LowercaseStrEnum):
119
+ PUBLIC = auto()
120
+ PRIVATE = auto()
121
+
122
+
123
+ class Server(SplightDatabaseBaseModel):
124
+ id: str | None = None
125
+ name: str | None = None
126
+ version: str
127
+ hub_server: HubServer
128
+ raw_config: list[dict] = Field(alias="config")
129
+ raw_ports: list[dict] = Field(alias="ports")
130
+ raw_env_vars: list[dict] = Field(alias="env_vars")
131
+
132
+ def model_dump(self, *args, **kwargs):
133
+ kwargs.update({"by_alias": True})
134
+ return super().model_dump(*args, **kwargs)
135
+
136
+ def model_dump_json(self, *args, **kwargs):
137
+ kwargs.update({"by_alias": True})
138
+ return super().model_dump_json(*args, **kwargs)
139
+
140
+ @computed_field(alias="parsed_config")
141
+ @property
142
+ def config(self) -> NamedTuple:
143
+ model_class = get_model_class(self.hub_server.config, "Config")
144
+ config = load_server_config(self.raw_config, model_class)
145
+ return config
146
+
147
+ @computed_field(alias="parsed_ports")
148
+ @property
149
+ def ports(self) -> NamedTuple:
150
+ model_class = get_model_class(self.hub_server.ports, "Ports")
151
+ ports = load_server_ports(self.raw_ports, model_class)
152
+ return ports
153
+
154
+ @computed_field(alias="parsed_env_vars")
155
+ @property
156
+ def env_vars(self) -> NamedTuple:
157
+ env_vars = load_server_env_vars(self.raw_env_vars)
158
+ return env_vars
159
+
160
+ def update_config(self, **kwargs: dict) -> None:
161
+ valid_params = [x["name"] for x in self.raw_config]
162
+ for key, value in kwargs.items():
163
+ if key not in valid_params:
164
+ raise InvalidArgument(
165
+ (
166
+ f"Got invalid parameter {key}. Valid config parameters "
167
+ f"are {valid_params}"
168
+ )
169
+ )
170
+ for item in self.raw_config:
171
+ if item["name"] == key:
172
+ item["value"] = value
173
+ break
174
+
175
+ def update_status(self, status: ServerStatus) -> None:
176
+ _ = self._db_client.operate(
177
+ resource_name="server-status",
178
+ instance={
179
+ "server": self.id,
180
+ "deployment_status": status,
181
+ },
182
+ )
183
+
184
+
185
+ def parse_variable_string(raw_value: str | None) -> Any:
186
+ if raw_value is None:
187
+ return ""
188
+ pattern = re.compile(r"^\$\{\{(\w+)\.(\w+)\}\}$")
189
+ match = pattern.search(raw_value)
190
+ if not match:
191
+ return raw_value
192
+ _, secret_name = match.groups()
193
+ # TODO: handle errors (not found or not allowed)
194
+ secret = Secret.decrypt(name=secret_name)
195
+ return secret.value
@@ -1,7 +1,7 @@
1
1
  from collections import namedtuple
2
2
  from typing import NamedTuple, Optional
3
3
 
4
- from pydantic import Field, computed_field
4
+ from pydantic import BaseModel, Field, computed_field
5
5
 
6
6
  from splight_lib.models.asset import Asset
7
7
  from splight_lib.models.database_base import SplightDatabaseBaseModel
@@ -21,7 +21,7 @@ CAST_TO = {
21
21
  }
22
22
 
23
23
 
24
- def get_model_class(config: list[dict], name: str) -> NamedTuple:
24
+ def get_model_class(config: BaseModel, name: str) -> NamedTuple:
25
25
  config_class = namedtuple(name, [x.name for x in config])
26
26
  return config_class
27
27
 
@@ -0,0 +1,18 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import AnyUrl
4
+
5
+ from splight_lib.execution.scheduling import Crontab
6
+
7
+ NATIVE_TYPES = {
8
+ "int": int,
9
+ "bool": bool,
10
+ "str": str,
11
+ "float": float,
12
+ "datetime": datetime,
13
+ "url": AnyUrl,
14
+ }
15
+
16
+ CUSTOM_TYPES = {
17
+ "crontab": Crontab,
18
+ }
@@ -0,0 +1,5 @@
1
+ from splight_lib.server.server import ServerLoader
2
+
3
+ __all__ = [
4
+ ServerLoader,
5
+ ]
@@ -0,0 +1,3 @@
1
+ class MissingInstanceEnvVar(Exception):
2
+ def __init__(self, env_var: str):
3
+ super().__init__(f"Missing environment variable: {env_var}")
@@ -0,0 +1,15 @@
1
+ import os
2
+
3
+ from splight_lib.models import Server
4
+ from splight_lib.server.exceptions import MissingInstanceEnvVar
5
+
6
+ ENV_VAR = "SPLIGHT_SERVER_ID"
7
+
8
+
9
+ class ServerLoader:
10
+ @classmethod
11
+ def from_env(cls) -> Server:
12
+ if not (instance_id := os.environ.get(ENV_VAR)):
13
+ raise MissingInstanceEnvVar(ENV_VAR)
14
+ server = Server.retrieve(instance_id)
15
+ return server
@@ -15,7 +15,7 @@ class SolutionLoader:
15
15
  def from_env(cls) -> Solution:
16
16
  if not (str_instance := os.environ.get(ENV_VAR)):
17
17
  raise MissingInstanceEnvVar(ENV_VAR)
18
- str_instance = os.environ.get("INSTANCE_AS_STR", "")
18
+ str_instance = os.environ.get(ENV_VAR, "")
19
19
  instance = json.loads(str_instance)
20
20
  if not (instance_id := instance.get("id")):
21
21
  raise MissingInstanceId()
@@ -7,6 +7,7 @@ import pathspec
7
7
  SPLIGHT_IGNORE = ".splightignore"
8
8
  COMPRESSION_TYPE = "7z"
9
9
  SPEC_FILE = "spec.json"
10
+ RUN_FILE = "run.sh"
10
11
  README_FILE_1 = "README.md"
11
12
  README_FILE_2 = "README"
12
13
 
@@ -1,93 +0,0 @@
1
- from typing import Any, Dict, List, Optional
2
-
3
- from pydantic import Field
4
-
5
- from splight_lib.constants import DESCRIPTION_MAX_LENGTH
6
- from splight_lib.models.database_base import SplightDatabaseBaseModel
7
-
8
-
9
- class Filter(SplightDatabaseBaseModel):
10
- id: Optional[str] = None
11
- chart_item: str
12
- operator: Optional[str] = None
13
- key: Optional[str] = None
14
- value: Optional[str] = None
15
- label: Optional[str] = None
16
-
17
-
18
- class AdvancedFilter(SplightDatabaseBaseModel):
19
- id: Optional[str] = None
20
- chart_item: str
21
- operator: Optional[str] = None
22
- key: Optional[str] = None
23
- field: Optional[str] = None
24
- value: Optional[str] = None
25
-
26
-
27
- class ChartItem(SplightDatabaseBaseModel):
28
- id: Optional[str] = None
29
- chart: str
30
- label: str
31
- order: Optional[int] = None
32
- color: Optional[str] = None
33
- position_x: Optional[str] = None
34
- position_y: Optional[str] = None
35
- width: Optional[str] = None
36
- height: Optional[str] = None
37
- split_by: Optional[str] = None
38
- aggregate_criteria: Optional[str] = None
39
- aggregate_period: Optional[str] = None
40
- source: Optional[str] = None
41
- source_label: Optional[str] = None
42
- source_type: Optional[str] = None
43
- source_component_label: Optional[str] = None
44
- source_component_id: Optional[str] = None
45
- output_format: Optional[str] = None
46
- target: Optional[str] = None
47
- old_source: Optional[str] = None
48
- old_source_label: Optional[str] = None
49
- filters: Optional[List[Filter]] = None
50
- advanced_filters: Optional[List[AdvancedFilter]] = None
51
- query_params: Optional[str] = None
52
-
53
-
54
- class Chart(SplightDatabaseBaseModel):
55
- id: Optional[str] = None
56
- name: str
57
- description: Optional[str] = Field(
58
- default=None, max_length=DESCRIPTION_MAX_LENGTH
59
- )
60
- items: Optional[List[ChartItem]] = None
61
- tab: str
62
- position_x: Optional[str] = 0
63
- position_y: Optional[str] = 0
64
- height: Optional[str] = 28
65
- width: Optional[str] = 80
66
- min_height: Optional[str] = 14
67
- min_width: Optional[str] = 14
68
- type: Optional[str] = None
69
- timestamp_gte: Optional[str] = None
70
- timestamp_lte: Optional[str] = None
71
- refresh_interval: Optional[str] = None
72
- relative_window_time: Optional[str] = None
73
- external_resource: Optional[str] = None
74
- external_resource_type: Optional[str] = None
75
- y_axis_max_limit: Optional[str] = None
76
- y_axis_min_limit: Optional[str] = None
77
- config: Optional[Dict[str, Any]] = None
78
-
79
-
80
- class Tab(SplightDatabaseBaseModel):
81
- id: Optional[str] = None
82
- name: str
83
- charts: Optional[List[Chart]] = None
84
- order: Optional[int] = None
85
- dashboard: str
86
-
87
-
88
- class Dashboard(SplightDatabaseBaseModel):
89
- id: Optional[str] = None
90
- name: str
91
- description: Optional[str] = Field(
92
- default=None, max_length=DESCRIPTION_MAX_LENGTH
93
- )