splight-lib 5.9.5__tar.gz → 5.9.6.dev1__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 (105) hide show
  1. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/PKG-INFO +1 -1
  2. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/pyproject.toml +1 -1
  3. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/database/classmap.py +3 -0
  4. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/database/remote_client.py +6 -2
  5. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/exceptions.py +1 -2
  6. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/task.py +1 -2
  7. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/logging/_internal.py +1 -0
  8. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/__init__.py +4 -0
  9. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/dashboard.py +3 -3
  10. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/exceptions.py +8 -0
  11. splight_lib-5.9.6.dev1/splight_lib/models/hub_server.py +116 -0
  12. splight_lib-5.9.6.dev1/splight_lib/models/server.py +196 -0
  13. splight_lib-5.9.6.dev1/splight_lib/server/__init__.py +5 -0
  14. splight_lib-5.9.6.dev1/splight_lib/server/exceptions.py +3 -0
  15. splight_lib-5.9.6.dev1/splight_lib/server/server.py +18 -0
  16. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/solution/solution.py +1 -1
  17. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/tests/test_api_contracts.py +1 -2
  18. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/LICENSE.txt +0 -0
  19. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/README.md +0 -0
  20. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/__init__.py +0 -0
  21. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/abstract/__init__.py +0 -0
  22. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/abstract/client.py +0 -0
  23. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/auth/__init__.py +0 -0
  24. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/auth/exceptions.py +0 -0
  25. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/auth/mac_auth.py +0 -0
  26. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/auth/token.py +0 -0
  27. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/__init__.py +0 -0
  28. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/database/__init__.py +0 -0
  29. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/database/abstract.py +0 -0
  30. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/database/builder.py +0 -0
  31. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/__init__.py +0 -0
  32. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/abstract.py +0 -0
  33. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/buffer.py +0 -0
  34. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/builder.py +0 -0
  35. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/exceptions.py +0 -0
  36. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/remote_client.py +0 -0
  37. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/datalake/schemas.py +0 -0
  38. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/file_handler.py +0 -0
  39. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/filter.py +0 -0
  40. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/hub/__init__.py +0 -0
  41. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/hub/abstract.py +0 -0
  42. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/hub/client.py +0 -0
  43. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/tests/test_database.py +0 -0
  44. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/client/tests/test_datalake.py +0 -0
  45. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/component/__init__.py +0 -0
  46. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/component/abstract.py +0 -0
  47. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/component/exceptions.py +0 -0
  48. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/component/spec.py +0 -0
  49. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/component/tests/test_abstract.py +0 -0
  50. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/component/tests/test_spec.py +0 -0
  51. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/conftest.py +0 -0
  52. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/constants.py +0 -0
  53. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/encryption.py +0 -0
  54. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/__init__.py +0 -0
  55. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/engine.py +0 -0
  56. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/exceptions.py +0 -0
  57. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/scheduling.py +0 -0
  58. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/tests/test_execution.py +0 -0
  59. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/tests/test_scheduling.py +0 -0
  60. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/execution/trigger.py +0 -0
  61. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/logging/__init__.py +0 -0
  62. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/logging/component.py +0 -0
  63. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/logging/constants.py +0 -0
  64. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/logging/logging.py +0 -0
  65. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/logging/tests/test_logging.py +0 -0
  66. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/actions.py +0 -0
  67. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/alert.py +0 -0
  68. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/asset.py +0 -0
  69. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/attribute.py +0 -0
  70. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/base.py +0 -0
  71. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/component.py +0 -0
  72. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/data_address.py +0 -0
  73. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/file.py +0 -0
  74. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/function.py +0 -0
  75. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/generic.py +0 -0
  76. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/hub.py +0 -0
  77. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/hub_solution.py +0 -0
  78. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/metadata.py +0 -0
  79. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/native.py +0 -0
  80. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/pipeline.py +0 -0
  81. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/secret.py +0 -0
  82. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/solution.py +0 -0
  83. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/tag.py +0 -0
  84. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/tests/models.json +0 -0
  85. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/tests/test_component_object_instance.py +0 -0
  86. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/tests/test_database_model.py +0 -0
  87. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/tests/test_metadata.py +0 -0
  88. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/models/tests/test_models.py +0 -0
  89. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/restclient/__init__.py +0 -0
  90. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/restclient/client.py +0 -0
  91. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/restclient/exceptions.py +0 -0
  92. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/restclient/tests/test_restclient.py +0 -0
  93. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/restclient/types.py +0 -0
  94. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/settings.py +0 -0
  95. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/solution/__init__.py +0 -0
  96. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/solution/exceptions.py +0 -0
  97. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/stringcase.py +0 -0
  98. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/testing/__init__.py +0 -0
  99. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/tests/FakeProc.py +0 -0
  100. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/tests/asset_geometries.json +0 -0
  101. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/tests/test_encryption.py +0 -0
  102. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/utils/__init__.py +0 -0
  103. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/utils/custom_model.py +0 -0
  104. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/splight_lib/utils/hub.py +0 -0
  105. {splight_lib-5.9.5 → splight_lib-5.9.6.dev1}/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.5
3
+ Version: 5.9.6.dev1
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.5"
3
+ version = "5.9.6.dev1"
4
4
  description = "Splight Library"
5
5
  authors = ["Splight Dev <dev@splight-ae.com>"]
6
6
  readme = "README.md"
@@ -23,9 +23,12 @@ MODEL_NAME_MAP = {
23
23
  "secret": f"{ENGINE_PREFIX}/secrets/",
24
24
  "setpoint": f"{ENGINE_PREFIX}/setpoints/",
25
25
  "solution": f"{ENGINE_PREFIX}/solution/solutions/",
26
+ "server": f"{ENGINE_PREFIX}/server/servers/",
26
27
  "tab": f"{ENGINE_PREFIX}/dashboard/tabs/",
27
28
  "hubsolution": f"{HUB_PREFIX}/solution/solutions/",
28
29
  "hubsolutionversion": f"{HUB_PREFIX}/solution/versions/",
30
+ "hubserver": f"{HUB_PREFIX}/server/servers/",
31
+ "hubserverversion": f"{HUB_PREFIX}/server/versions/",
29
32
  }
30
33
 
31
34
  CUSTOM_PATHS_MAP = {
@@ -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")
@@ -40,5 +40,4 @@ class InstanceNotFound(Exception):
40
40
  return self._msg
41
41
 
42
42
 
43
- class InvalidModel(Exception):
44
- ...
43
+ class InvalidModel(Exception): ...
@@ -10,8 +10,7 @@ from splight_lib.execution.trigger import IntervalTrigger
10
10
 
11
11
  class BaseTask(ABC):
12
12
  @abstractmethod
13
- def as_job(self) -> Dict:
14
- ...
13
+ def as_job(self) -> Dict: ...
15
14
 
16
15
 
17
16
  class PeriodicTask(BaseTask):
@@ -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
@@ -21,10 +21,12 @@ from splight_lib.models.dashboard import (
21
21
  from splight_lib.models.file import File
22
22
  from splight_lib.models.function import Function, FunctionItem, QueryFilter
23
23
  from splight_lib.models.hub import HubComponent
24
+ from splight_lib.models.hub_server import HubServer
24
25
  from splight_lib.models.hub_solution import HubSolution
25
26
  from splight_lib.models.metadata import Metadata
26
27
  from splight_lib.models.native import Boolean, Number, String
27
28
  from splight_lib.models.secret import Secret
29
+ from splight_lib.models.server import Server
28
30
  from splight_lib.models.solution import Solution
29
31
  from splight_lib.models.tag import Tag
30
32
 
@@ -50,11 +52,13 @@ __all__ = [
50
52
  FunctionItem,
51
53
  HubSolution,
52
54
  HubComponent,
55
+ HubServer,
53
56
  QueryFilter,
54
57
  Metadata,
55
58
  Number,
56
59
  RoutineEvaluation,
57
60
  Secret,
61
+ Server,
58
62
  SetPoint,
59
63
  Solution,
60
64
  String,
@@ -39,9 +39,9 @@ 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[
43
- str
44
- ] = None # TODO: define options (component, native)
42
+ source_type: Optional[str] = (
43
+ None # TODO: define options (component, native)
44
+ )
45
45
  source_component_label: Optional[str] = None
46
46
  source_component_id: Optional[str] = None
47
47
  output_format: Optional[str] = None
@@ -64,3 +64,11 @@ class MissingAsset(Exception):
64
64
 
65
65
  class InvalidArgument(Exception):
66
66
  pass
67
+
68
+ class InvalidServerConfigType(Exception):
69
+ def __init__(self, name: str, type_: str):
70
+ msg = (
71
+ f"Config parameter {name} has an invalid type {type_}. The only "
72
+ "valid types are native and 'File'"
73
+ )
74
+ super().__init__(msg)
@@ -0,0 +1,116 @@
1
+ import os
2
+ from glob import glob
3
+ from tempfile import NamedTemporaryFile
4
+ from typing import Dict, List, Optional
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.base import (
11
+ FilePath,
12
+ PrivacyPolicy,
13
+ SplightDatabaseBaseModel,
14
+ )
15
+ from splight_lib.models.component import InputParameter
16
+ from splight_lib.utils.hub import (
17
+ COMPRESSION_TYPE,
18
+ README_FILE_1,
19
+ get_ignore_pathspec,
20
+ get_spec,
21
+ )
22
+
23
+ class Port(BaseModel):
24
+ name: Optional[str]
25
+ internal_port: int
26
+ exposed_port: int
27
+
28
+
29
+ class HubServer(SplightDatabaseBaseModel):
30
+ id: Optional[str] = None
31
+ name: str
32
+ version: str
33
+ description: Optional[str] = Field(
34
+ default=None, max_length=DESCRIPTION_MAX_LENGTH
35
+ )
36
+ tags: Optional[List[str]] = Field(default=[])
37
+ privacy_policy: PrivacyPolicy = PrivacyPolicy.PUBLIC
38
+
39
+ config: List[InputParameter] = []
40
+ ports: List[Port] = []
41
+ environment: List[Dict[str, str]] = []
42
+
43
+ @classmethod
44
+ def upload(cls, path: FilePath, image_file: str) -> "HubServer":
45
+ db_client = cls._SplightDatabaseBaseModel__get_database_client()
46
+ image_path = os.path.join(path, image_file)
47
+ spec = get_spec(path)
48
+ name = spec.get("name")
49
+ version = spec.get("version")
50
+
51
+ raw_hub_server = db_client.get(
52
+ resource_name=cls.__name__,
53
+ name=name,
54
+ version=version,
55
+ )
56
+ server = cls.model_validate(spec)
57
+ if raw_hub_server:
58
+ old_hub_server = cls.model_validate(raw_hub_server[0])
59
+ server.id = old_hub_server.id
60
+
61
+ server.save()
62
+
63
+ file_name = f"{name}-{version}.{COMPRESSION_TYPE}"
64
+ ignore_pathspec = get_ignore_pathspec(path)
65
+ versioned_path = f"{name}-{version}"
66
+ # TODO: Add required files validations
67
+ readme_path = os.path.join(path, README_FILE_1)
68
+ if not os.path.exists(readme_path):
69
+ raise FileNotFoundError(f"README.md file not found: {readme_path}")
70
+ with py7zr.SevenZipFile(file_name, "w") as archive:
71
+ all_files = glob(f"{path}/**", recursive=True)
72
+ for filepath in all_files:
73
+ if ignore_pathspec and ignore_pathspec.match_file(filepath):
74
+ continue
75
+ if os.path.isdir(filepath):
76
+ continue
77
+ if image_file in filepath:
78
+ continue
79
+ rel_filename = os.path.relpath(filepath, path)
80
+ new_filepath = os.path.join(versioned_path, rel_filename)
81
+ archive.write(filepath, new_filepath)
82
+
83
+ data = server.model_dump()
84
+ try:
85
+ db_client.upload(
86
+ "hubserverversion",
87
+ instance=data,
88
+ file_path=file_name,
89
+ type_="source",
90
+ )
91
+ db_client.upload(
92
+ "hubserverversion",
93
+ instance=data,
94
+ file_path=readme_path,
95
+ type_="readme",
96
+ )
97
+ db_client.upload(
98
+ "hubserverversion",
99
+ instance=data,
100
+ file_path=image_path,
101
+ type_="image",
102
+ )
103
+ except Exception as exc:
104
+ raise exc
105
+ finally:
106
+ if os.path.exists(file_name):
107
+ os.remove(file_name)
108
+ return server
109
+
110
+ def download(self) -> NamedTemporaryFile:
111
+ db_client = self._SplightDatabaseBaseModel__get_database_client()
112
+ return db_client.download(
113
+ resource_name="hubserverversion",
114
+ instance=self.model_dump(),
115
+ type_="source",
116
+ )
@@ -0,0 +1,196 @@
1
+ from collections import namedtuple
2
+ import re
3
+ import warnings
4
+ from datetime import datetime
5
+ from enum import auto
6
+ from typing import Any, NamedTuple, Optional
7
+
8
+ from pydantic import AnyUrl, BaseModel, Field, computed_field
9
+ from strenum import LowercaseStrEnum, PascalCaseStrEnum
10
+
11
+ from splight_lib.models.base import SplightDatabaseBaseModel
12
+ from splight_lib.models.exceptions import InvalidArgument, InvalidServerConfigType
13
+ from splight_lib.models.file import File
14
+ from splight_lib.models.hub_server import HubServer
15
+ from splight_lib.models.secret import Secret
16
+
17
+ warnings.filterwarnings("ignore", category=RuntimeWarning)
18
+
19
+ NATIVE_TYPES = {
20
+ "int": int,
21
+ "bool": bool,
22
+ "str": str,
23
+ "float": float,
24
+ "datetime": datetime,
25
+ "url": AnyUrl,
26
+ }
27
+
28
+ DATABASE_TYPES = {
29
+ "File": File,
30
+ }
31
+
32
+ DB_MODEL_TYPE_MAPPING = {
33
+ **NATIVE_TYPES,
34
+ **DATABASE_TYPES,
35
+ }
36
+
37
+ def get_field_value(field: dict):
38
+ field_multiple = field.get("multiple", False)
39
+ field_type = field.get("type")
40
+ field_value = field.get("value")
41
+
42
+ if not field_value:
43
+ return [] if field_multiple else None
44
+
45
+ if field_type in NATIVE_TYPES:
46
+ value = (
47
+ field_value
48
+ if not isinstance(field_value, str)
49
+ else parse_variable_string(field_value)
50
+ )
51
+ elif field_type in DATABASE_TYPES:
52
+ model_class = DATABASE_TYPES[field_type]
53
+ value = (
54
+ model_class.retrieve(field_value)
55
+ if not field_multiple
56
+ else [model_class.retrieve(item) for item in field_value]
57
+ )
58
+ return value
59
+
60
+ def get_model_class(config: BaseModel, name: str) -> NamedTuple:
61
+ config_class = namedtuple(name, [x.name for x in config])
62
+ return config_class
63
+
64
+ def load_server_config(
65
+ config: list[dict], config_class: NamedTuple
66
+ ) -> NamedTuple:
67
+ config_dict = {}
68
+ for item in config:
69
+ if item["type"] not in DB_MODEL_TYPE_MAPPING:
70
+ raise InvalidServerConfigType(item["name"], item["type"])
71
+ config_dict.update({item["name"]: get_field_value(item)})
72
+ config = config_class(**config_dict)
73
+ return config
74
+
75
+ def load_server_ports(
76
+ ports: list[dict], model_class: NamedTuple
77
+ ) -> namedtuple:
78
+ ports_dict = {}
79
+ for port in ports:
80
+ if isinstance(port["internal_port"], int) and not (
81
+ 0 <= port["internal_port"] <= 65535
82
+ ):
83
+ raise ValueError(
84
+ f"Internal port {port["internal_port"]} is out of valid range (0-65535)"
85
+ )
86
+ if isinstance(port["exposed_port"], int) and not (
87
+ 0 <= port["exposed_port"] <= 65535
88
+ ):
89
+ raise ValueError(
90
+ f"External port {port["exposed_port"]} is out of valid range (0-65535)"
91
+ )
92
+ ports_dict.update({port["name"]: port})
93
+ resources = model_class(**ports_dict)
94
+ return resources
95
+
96
+ class ServerStatus(PascalCaseStrEnum):
97
+ RUNNING = auto()
98
+ FAILED = auto()
99
+ SUCCEEDED = auto()
100
+
101
+
102
+ class PrivacyPolicy(LowercaseStrEnum):
103
+ PUBLIC = auto()
104
+ PRIVATE = auto()
105
+
106
+ class Port(BaseModel):
107
+ name: Optional[str]
108
+ internal_port: int
109
+ exposed_port: int
110
+
111
+
112
+ class SplightObject(SplightDatabaseBaseModel):
113
+ id: Optional[str] = None
114
+ name: str
115
+ server_id: Optional[str] = None
116
+ description: str = Field(default="")
117
+
118
+ def save(self):
119
+ if self.server_id is None:
120
+ raise ValueError("server_id cannot be None.")
121
+ super().save()
122
+
123
+
124
+ class Server(SplightDatabaseBaseModel):
125
+ id: Optional[str] = None
126
+ name: Optional[str] = None
127
+ version: str
128
+ hub_server: HubServer
129
+ raw_config: list[dict] = Field(alias="config")
130
+ raw_ports: list[dict] = Field(alias="ports")
131
+ raw_env_vars: list[dict] = Field(alias="env_vars")
132
+
133
+ def model_dump(self, *args, **kwargs):
134
+ kwargs.update({"by_alias": True})
135
+ return super().model_dump(*args, **kwargs)
136
+
137
+ def model_dump_json(self, *args, **kwargs):
138
+ kwargs.update({"by_alias": True})
139
+ return super().model_dump_json(*args, **kwargs)
140
+
141
+ @computed_field(alias="parsed_config")
142
+ @property
143
+ def config(self) -> NamedTuple:
144
+ model_class = get_model_class(self.hub_server.config, "Config")
145
+ config = load_server_config(self.raw_config, model_class)
146
+ return config
147
+
148
+ @computed_field(alias="parsed_ports")
149
+ @property
150
+ def ports(self) -> NamedTuple:
151
+ model_class = get_model_class(self.hub_server.ports, "Ports")
152
+ ports = load_server_ports(self.raw_ports, model_class)
153
+ return ports
154
+
155
+ def update_config(self, **kwargs: dict):
156
+ valid_params = [x["name"] for x in self.raw_config]
157
+ for key, value in kwargs.items():
158
+ if key not in valid_params:
159
+ raise InvalidArgument(
160
+ (
161
+ f"Got invalid parameter {key}. Valid config parameter "
162
+ f"are {valid_params}"
163
+ )
164
+ )
165
+ for item in self.raw_config:
166
+ if item["name"] == key:
167
+ item["value"] = value
168
+ break
169
+
170
+ def update_ports(self, **kwargs: dict):
171
+ valid_params = [x["name"] for x in self.raw_ports]
172
+ for key, value in kwargs.items():
173
+ if key not in valid_params:
174
+ raise InvalidArgument(
175
+ (
176
+ f"Got invalid parameter {key}. Valid port "
177
+ f"parameter are {valid_params}"
178
+ )
179
+ )
180
+ for item in self.raw_ports:
181
+ if item["name"] == key:
182
+ item["value"] = value
183
+ break
184
+
185
+
186
+ def parse_variable_string(raw_value: Optional[str]) -> Any:
187
+ if raw_value is None:
188
+ return ""
189
+ pattern = re.compile(r"^\$\{\{(\w+)\.(\w+)\}\}$")
190
+ match = pattern.search(raw_value)
191
+ if not match:
192
+ return raw_value
193
+ _, secret_name = match.groups()
194
+ # TODO: handle errors (not found or not allowed)
195
+ secret = Secret.decrypt(name=secret_name)
196
+ return secret.value
@@ -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,18 @@
1
+ import json
2
+ import os
3
+
4
+ from splight_lib.models import Server
5
+ from splight_lib.server.exceptions import (
6
+ MissingInstanceEnvVar,
7
+ )
8
+
9
+ ENV_VAR = "SPLIGHT_SERVER_ID"
10
+
11
+
12
+ class ServerLoader:
13
+ @classmethod
14
+ def from_env(cls) -> Server:
15
+ if not (instance_id := os.environ.get(ENV_VAR)):
16
+ raise MissingInstanceEnvVar(ENV_VAR)
17
+ server = Server.retrieve(instance_id)
18
+ 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()
@@ -65,8 +65,7 @@ with open(
65
65
  GEOMETRIES.insert(0, None)
66
66
 
67
67
 
68
- class GeometryCollectionFactory(ModelFactory[GeometryCollection]):
69
- ...
68
+ class GeometryCollectionFactory(ModelFactory[GeometryCollection]): ...
70
69
 
71
70
 
72
71
  class AttributeFactory(ModelFactory[Attribute]):
File without changes