splight-cli 4.6.0.dev1__tar.gz → 4.6.1__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_cli-4.6.0.dev1 → splight_cli-4.6.1}/PKG-INFO +9 -4
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/README.md +5 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/pyproject.toml +4 -4
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/apply_exec.py +1 -1
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/importer.py +2 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/models.py +11 -1
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/plan_exec.py +23 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/replacer.py +67 -6
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/solution.py +89 -10
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/solution_checker.py +59 -5
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/tests/constants.py +16 -8
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/tests/test_plan.py +2 -2
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/utils.py +4 -1
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/template.py +1 -1
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/cli.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/component.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/exceptions.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/loaders.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/.splightignore +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/Initialization +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/README.md +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/auto_readme.md +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/main.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/spec.json +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/templates/tests.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/tests/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/tests/test_component_manager.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/utils.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/config/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/constants.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/context/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/context/context.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/context/workspace.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/alert/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/asset/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/attribute/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/component/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/datalake/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/file/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/manager/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/manager/exceptions.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/manager/manager.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/manager/tests/test_datalake_manager.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/manager/tests/test_resource_manager.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/engine/secret/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/hub/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/hub/component/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/hub/component/exceptions.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/hub/component/hub_manager.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/hub/component/tests/test_hub_manager.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/settings.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/destroyer.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/exceptions.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/tests/test_apply.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/tests/test_destroy.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/solution/tests/test_import.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/tests/test_component/Initialization +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/tests/test_component/README.md +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/tests/test_component/main.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/tests/test_component/spec.json +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/tests/test_component/tests.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/__init__.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/input.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/json.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/loader.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/pprint.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/utils/yaml.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/version.py +0 -0
- {splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/workspace/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: splight-cli
|
|
3
|
-
Version: 4.6.
|
|
3
|
+
Version: 4.6.1
|
|
4
4
|
Summary: Splight Command Line Interface
|
|
5
5
|
Author: Splight Dev
|
|
6
6
|
Author-email: dev@splight-ae.com
|
|
@@ -19,16 +19,21 @@ Requires-Dist: deepdiff (==6.3.1)
|
|
|
19
19
|
Requires-Dist: mock (>=5.1.0,<6.0.0)
|
|
20
20
|
Requires-Dist: packaging (==23.0)
|
|
21
21
|
Requires-Dist: pathspec (==0.11.1)
|
|
22
|
-
Requires-Dist: py7zr (==0.
|
|
23
|
-
Requires-Dist: requests (==2.
|
|
22
|
+
Requires-Dist: py7zr (==0.20.8)
|
|
23
|
+
Requires-Dist: requests (==2.31.0)
|
|
24
24
|
Requires-Dist: rich (==13.0.1)
|
|
25
25
|
Requires-Dist: setuptools (==60.9.3)
|
|
26
|
-
Requires-Dist: splight-lib (==4.
|
|
26
|
+
Requires-Dist: splight-lib (==4.6.3)
|
|
27
27
|
Requires-Dist: typer (==0.7.0)
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
|
|
30
30
|
# Splight CLI
|
|
31
31
|
|
|
32
|
+

|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
32
37
|
## Table of Content
|
|
33
38
|
|
|
34
39
|
- [Splight CLI](#splight-cli)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Splight CLI
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
3
8
|
## Table of Content
|
|
4
9
|
|
|
5
10
|
- [Splight CLI](#splight-cli)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "splight-cli"
|
|
3
|
-
version = "4.6.
|
|
3
|
+
version = "4.6.1"
|
|
4
4
|
description = "Splight Command Line Interface"
|
|
5
5
|
authors = ["Splight Dev <dev@splight-ae.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -11,11 +11,11 @@ case-converter = "1.1.0"
|
|
|
11
11
|
click = "8.0.4"
|
|
12
12
|
click-default-group = "1.2.2"
|
|
13
13
|
setuptools = "60.9.3"
|
|
14
|
-
splight-lib = "4.
|
|
15
|
-
py7zr = "0.
|
|
14
|
+
splight-lib = "4.6.3"
|
|
15
|
+
py7zr = "0.20.8"
|
|
16
16
|
typer = "0.7.0"
|
|
17
17
|
colorama = "0.4.4"
|
|
18
|
-
requests = "2.
|
|
18
|
+
requests = "2.31.0"
|
|
19
19
|
PyYAML = "6.0.1"
|
|
20
20
|
rich = "13.0.1"
|
|
21
21
|
pathspec = "0.11.1"
|
|
@@ -3,6 +3,7 @@ from uuid import UUID
|
|
|
3
3
|
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
from splight_lib.models import (
|
|
6
|
+
Alert,
|
|
6
7
|
Asset,
|
|
7
8
|
Component,
|
|
8
9
|
Function,
|
|
@@ -31,6 +32,7 @@ class ImporterExecutor:
|
|
|
31
32
|
"secret": Secret,
|
|
32
33
|
"component": Component,
|
|
33
34
|
"function": Function,
|
|
35
|
+
"alert": Alert,
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
def import_element(self, element: ElementType, id: UUID) -> ImportResult:
|
|
@@ -2,7 +2,14 @@ from enum import Enum
|
|
|
2
2
|
from typing import List, Optional
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
|
-
from splight_lib.models import
|
|
5
|
+
from splight_lib.models import (
|
|
6
|
+
Alert,
|
|
7
|
+
Asset,
|
|
8
|
+
File,
|
|
9
|
+
Function,
|
|
10
|
+
RoutineObject,
|
|
11
|
+
Secret,
|
|
12
|
+
)
|
|
6
13
|
from splight_lib.models.component import Component as LibComponent
|
|
7
14
|
|
|
8
15
|
|
|
@@ -11,6 +18,7 @@ class ElementType(str, Enum):
|
|
|
11
18
|
secret = "secret"
|
|
12
19
|
component = "component"
|
|
13
20
|
function = "function"
|
|
21
|
+
alert = "alert"
|
|
14
22
|
|
|
15
23
|
|
|
16
24
|
class Component(LibComponent):
|
|
@@ -23,12 +31,14 @@ class PlanSolution(BaseModel):
|
|
|
23
31
|
files: List[File]
|
|
24
32
|
components: List[Component]
|
|
25
33
|
functions: List[Function]
|
|
34
|
+
alerts: List[Alert]
|
|
26
35
|
imported_assets: Optional[List[Asset]] = Field(default_factory=list)
|
|
27
36
|
imported_secrets: Optional[List[Secret]] = Field(default_factory=list)
|
|
28
37
|
imported_components: Optional[List[Component]] = Field(
|
|
29
38
|
default_factory=list
|
|
30
39
|
)
|
|
31
40
|
imported_functions: Optional[List[Function]] = Field(default_factory=list)
|
|
41
|
+
imported_alerts: Optional[List[Alert]] = Field(default_factory=list)
|
|
32
42
|
|
|
33
43
|
|
|
34
44
|
class StateSolution(PlanSolution):
|
|
@@ -30,6 +30,29 @@ class PlanExecutor:
|
|
|
30
30
|
"If the plan is applied the following Asset will be deleted:"
|
|
31
31
|
)
|
|
32
32
|
bprint(asset)
|
|
33
|
+
for secret in check_results.secrets_to_delete:
|
|
34
|
+
bprint(
|
|
35
|
+
"If the plan is applied the following Secret will be "
|
|
36
|
+
"deleted:"
|
|
37
|
+
)
|
|
38
|
+
bprint(secret)
|
|
39
|
+
for file in check_results.files_to_delete:
|
|
40
|
+
bprint(
|
|
41
|
+
"If the plan is applied the following File will be " "deleted:"
|
|
42
|
+
)
|
|
43
|
+
bprint(file)
|
|
44
|
+
for function in check_results.functions_to_delete:
|
|
45
|
+
bprint(
|
|
46
|
+
"If the plan is applied the following Function will be "
|
|
47
|
+
"deleted:"
|
|
48
|
+
)
|
|
49
|
+
bprint(function)
|
|
50
|
+
for alert in check_results.alerts_to_delete:
|
|
51
|
+
bprint(
|
|
52
|
+
"If the plan is applied the following alert will be "
|
|
53
|
+
"deleted:"
|
|
54
|
+
)
|
|
55
|
+
bprint(alert)
|
|
33
56
|
for component in check_results.components_to_delete:
|
|
34
57
|
bprint(
|
|
35
58
|
"If the plan is applied the following Component will be "
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from typing import Callable, Dict, List, Optional, Union
|
|
3
3
|
|
|
4
|
+
from splight_lib.models import AlertItem, QueryFilter
|
|
4
5
|
from splight_lib.models.component import (
|
|
5
6
|
InputDataAddress,
|
|
6
7
|
InputParameter,
|
|
@@ -36,6 +37,10 @@ class Replacer:
|
|
|
36
37
|
file_ref = get_ref_str("file", file.name)
|
|
37
38
|
self._reference_map[file_ref] = file.id
|
|
38
39
|
|
|
40
|
+
for secret in self._state.secrets:
|
|
41
|
+
secret_ref = get_ref_str("secret", secret.name)
|
|
42
|
+
self._reference_map[secret_ref] = f"${{{{SECRET.{secret.name}}}}}"
|
|
43
|
+
|
|
39
44
|
def replace_references(self):
|
|
40
45
|
"""Replaces any references in the inputs, outputs and routines of every
|
|
41
46
|
component.
|
|
@@ -66,6 +71,14 @@ class Replacer:
|
|
|
66
71
|
target_attribute=state_functions[i].target_attribute
|
|
67
72
|
)
|
|
68
73
|
|
|
74
|
+
state_alerts = self._state.alerts
|
|
75
|
+
for i in range(len(state_alerts)):
|
|
76
|
+
alert_items = state_alerts[i].alert_items
|
|
77
|
+
for j in range(len(alert_items)):
|
|
78
|
+
self._replace_alert_ref(alert_item=alert_items[j])
|
|
79
|
+
|
|
80
|
+
self._replace_alert_ref(assets=state_alerts[i].assets)
|
|
81
|
+
|
|
69
82
|
def _replace_io_ref(
|
|
70
83
|
self,
|
|
71
84
|
io_elem: Union[InputDataAddress, InputParameter, Output],
|
|
@@ -156,6 +169,54 @@ class Replacer:
|
|
|
156
169
|
value_ref = target_attribute["id"]
|
|
157
170
|
target_attribute["id"] = self._parse_input_output(value_ref)
|
|
158
171
|
|
|
172
|
+
def _replace_alert_ref(
|
|
173
|
+
self, alert_item: AlertItem = None, assets: QueryFilter = None
|
|
174
|
+
):
|
|
175
|
+
"""Replaces references in the alert_item and assets.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
alert_item : Optional[AlertItem]
|
|
180
|
+
The alert item to be processed. Default None.
|
|
181
|
+
assets : Optional[List[QueryFilter]]
|
|
182
|
+
List of assets to be processed. Default None.
|
|
183
|
+
"""
|
|
184
|
+
if alert_item:
|
|
185
|
+
asset = alert_item.query_filter_asset
|
|
186
|
+
attribute = alert_item.query_filter_attribute
|
|
187
|
+
query_plain = alert_item.query_plain
|
|
188
|
+
|
|
189
|
+
if asset:
|
|
190
|
+
value_ref = asset["id"]
|
|
191
|
+
asset["id"] = self._parse_input_output(value_ref)
|
|
192
|
+
alert_item.query_filter_asset = asset
|
|
193
|
+
|
|
194
|
+
if attribute:
|
|
195
|
+
value_ref = attribute["id"]
|
|
196
|
+
attribute["id"] = self._parse_input_output(value_ref)
|
|
197
|
+
alert_item.query_filter_attribute = attribute
|
|
198
|
+
|
|
199
|
+
if query_plain:
|
|
200
|
+
query_dict = json.loads(query_plain)
|
|
201
|
+
|
|
202
|
+
# NOTE: sadly this depends on the query.
|
|
203
|
+
# I'd rather do this than black magic to get the placeholders.
|
|
204
|
+
asset_ref = query_dict[0]["$match"]["asset"]
|
|
205
|
+
attribute_ref = query_dict[0]["$match"]["attribute"]
|
|
206
|
+
|
|
207
|
+
query_dict[0]["$match"]["asset"] = self._parse_input_output(
|
|
208
|
+
asset_ref
|
|
209
|
+
)
|
|
210
|
+
query_dict[0]["$match"][
|
|
211
|
+
"attribute"
|
|
212
|
+
] = self._parse_input_output(attribute_ref)
|
|
213
|
+
alert_item.query_plain = json.dumps(query_dict)
|
|
214
|
+
|
|
215
|
+
if assets:
|
|
216
|
+
for asset in assets:
|
|
217
|
+
value_ref = asset["id"]
|
|
218
|
+
asset["id"] = self._parse_input_output(value_ref)
|
|
219
|
+
|
|
159
220
|
def _get_new_value(
|
|
160
221
|
self,
|
|
161
222
|
elem: Union[InputDataAddress, InputParameter, Output],
|
|
@@ -273,6 +334,12 @@ class Replacer:
|
|
|
273
334
|
elif attr_ref not in self._reference_map:
|
|
274
335
|
missing_ref = attr_ref
|
|
275
336
|
|
|
337
|
+
if missing_ref is not None:
|
|
338
|
+
raise UndefinedID(
|
|
339
|
+
f"The reference '{missing_ref}' "
|
|
340
|
+
f"used in the routine named '{routine_name}' of the "
|
|
341
|
+
f"component '{component_name}' is not a valid reference."
|
|
342
|
+
)
|
|
276
343
|
if self._attr_to_asset_map[attr_ref] != asset_ref:
|
|
277
344
|
raise UndefinedID(
|
|
278
345
|
f"The attribute '{attr_ref}' is not an attribute of "
|
|
@@ -280,12 +347,6 @@ class Replacer:
|
|
|
280
347
|
f"'{routine_name}' of the component '{component_name}'."
|
|
281
348
|
)
|
|
282
349
|
|
|
283
|
-
if missing_ref is not None:
|
|
284
|
-
raise UndefinedID(
|
|
285
|
-
f"The reference '{missing_ref}' "
|
|
286
|
-
f"used in the routine named '{routine_name}' of the "
|
|
287
|
-
f"component '{component_name}' is not a valid reference."
|
|
288
|
-
)
|
|
289
350
|
return {
|
|
290
351
|
"asset": self._reference_map[asset_ref],
|
|
291
352
|
"attribute": self._reference_map[attr_ref],
|
|
@@ -6,6 +6,7 @@ import typer
|
|
|
6
6
|
from pydantic import ValidationError
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from splight_lib.models import (
|
|
9
|
+
Alert,
|
|
9
10
|
Asset,
|
|
10
11
|
Component,
|
|
11
12
|
File,
|
|
@@ -62,6 +63,8 @@ class SolutionManager:
|
|
|
62
63
|
r"root\['config'\]\[\d+\]\['description'\]",
|
|
63
64
|
r"root\['input'\]\[\d+\]\['description'\]",
|
|
64
65
|
r"root\['output'\]\[\d+\]\['description'\]",
|
|
66
|
+
# NOTE: API returns the DataAddress type but we don't use it
|
|
67
|
+
r"root\['(?:input|output)'\]\[\d+\]\['value'\]\['type'\]",
|
|
65
68
|
],
|
|
66
69
|
File.__name__: [
|
|
67
70
|
r"root\['metadata'\]",
|
|
@@ -83,13 +86,14 @@ class SolutionManager:
|
|
|
83
86
|
console.print("\nStarting apply...", style=PRINT_STYLE)
|
|
84
87
|
check_result = self._solution_checker.check()
|
|
85
88
|
self._plan, self._state = check_result.plan, check_result.state
|
|
86
|
-
self.
|
|
89
|
+
self._delete_objects(check_result)
|
|
87
90
|
self._apply_assets_state()
|
|
88
91
|
self._apply_secrets_state()
|
|
89
92
|
self._apply_files_state()
|
|
90
93
|
self._replacer.build_reference_map()
|
|
91
94
|
self._replacer.replace_references()
|
|
92
95
|
self._apply_functions_state()
|
|
96
|
+
self._apply_alerts_state()
|
|
93
97
|
self._apply_components_state()
|
|
94
98
|
|
|
95
99
|
def plan(self):
|
|
@@ -99,10 +103,12 @@ class SolutionManager:
|
|
|
99
103
|
self._plan_exec.plan_elements_to_delete(check_result)
|
|
100
104
|
self._plan_assets_state()
|
|
101
105
|
self._plan_secrets_state()
|
|
106
|
+
# TODO: buscar los files en una carpeta fija
|
|
102
107
|
self._plan_files_state()
|
|
103
108
|
self._replacer.build_reference_map()
|
|
104
109
|
self._replacer.replace_references()
|
|
105
110
|
self._plan_functions_state()
|
|
111
|
+
self._plan_alerts_state()
|
|
106
112
|
self._plan_components_state()
|
|
107
113
|
|
|
108
114
|
def import_element(self, element: ElementType, id: UUID):
|
|
@@ -155,12 +161,20 @@ class SolutionManager:
|
|
|
155
161
|
|
|
156
162
|
state_functions = self._state.functions
|
|
157
163
|
for idx in range(len(state_functions) - 1, -1, -1):
|
|
158
|
-
|
|
159
|
-
destroyed = self._destroyer.destroy(Function,
|
|
164
|
+
function_to_delete = state_functions[idx]
|
|
165
|
+
destroyed = self._destroyer.destroy(Function, function_to_delete)
|
|
160
166
|
if destroyed:
|
|
161
167
|
state_functions.pop(idx)
|
|
162
168
|
save_yaml(self._state_path, self._state)
|
|
163
169
|
|
|
170
|
+
state_alerts = self._state.alerts
|
|
171
|
+
for idx in range(len(state_alerts) - 1, -1, -1):
|
|
172
|
+
alert_to_delete = state_alerts[idx]
|
|
173
|
+
destroyed = self._destroyer.destroy(Alert, alert_to_delete)
|
|
174
|
+
if destroyed:
|
|
175
|
+
state_alerts.pop(idx)
|
|
176
|
+
save_yaml(self._state_path, self._state)
|
|
177
|
+
|
|
164
178
|
state_components = self._state.components
|
|
165
179
|
for idx in range(len(state_components) - 1, -1, -1):
|
|
166
180
|
component_to_delete = state_components[idx]
|
|
@@ -176,8 +190,16 @@ class SolutionManager:
|
|
|
176
190
|
return StateSolution.model_validate(
|
|
177
191
|
load_yaml(self._state_path)
|
|
178
192
|
)
|
|
179
|
-
except ValidationError:
|
|
180
|
-
|
|
193
|
+
except ValidationError as exc:
|
|
194
|
+
raise typer.Abort(
|
|
195
|
+
f"Validation error while opening '{self._state_path}': {exc}"
|
|
196
|
+
)
|
|
197
|
+
except FileNotFoundError as exc:
|
|
198
|
+
raise typer.Abort(f"File '{self._state_path}' not found.")
|
|
199
|
+
except:
|
|
200
|
+
raise typer.Abort(
|
|
201
|
+
f"Unkown error while opening file '{self._state_path}'"
|
|
202
|
+
)
|
|
181
203
|
else:
|
|
182
204
|
self._state_path = self._plan_path.parent / DEFAULT_STATE_PATH
|
|
183
205
|
|
|
@@ -232,7 +254,12 @@ class SolutionManager:
|
|
|
232
254
|
for state_function in function_list:
|
|
233
255
|
self._plan_exec.plan_elem_state(Function, state_function)
|
|
234
256
|
|
|
235
|
-
def
|
|
257
|
+
def _plan_alerts_state(self):
|
|
258
|
+
alert_list = self._state.alerts + self._state.imported_alerts
|
|
259
|
+
for state_alert in alert_list:
|
|
260
|
+
self._plan_exec.plan_elem_state(Alert, state_alert)
|
|
261
|
+
|
|
262
|
+
def _delete_objects(self, check_result: CheckResult):
|
|
236
263
|
"""Deletes assets and/or components that have been removed from the
|
|
237
264
|
plan.
|
|
238
265
|
|
|
@@ -245,6 +272,22 @@ class SolutionManager:
|
|
|
245
272
|
self._apply_exec.delete(Asset, asset)
|
|
246
273
|
save_yaml(self._state_path, self._state)
|
|
247
274
|
|
|
275
|
+
for secret in check_result.secrets_to_delete:
|
|
276
|
+
self._apply_exec.delete(Secret, secret)
|
|
277
|
+
save_yaml(self._state_path, self._state)
|
|
278
|
+
|
|
279
|
+
for file in check_result.files_to_delete:
|
|
280
|
+
self._apply_exec.delete(File, file)
|
|
281
|
+
save_yaml(self._state_path, self._state)
|
|
282
|
+
|
|
283
|
+
for function in check_result.functions_to_delete:
|
|
284
|
+
self._apply_exec.delete(Function, function)
|
|
285
|
+
save_yaml(self._state_path, self._state)
|
|
286
|
+
|
|
287
|
+
for alert in check_result.alerts_to_delete:
|
|
288
|
+
self._apply_exec.delete(Alert, alert)
|
|
289
|
+
save_yaml(self._state_path, self._state)
|
|
290
|
+
|
|
248
291
|
for component in check_result.components_to_delete:
|
|
249
292
|
self._apply_exec.delete(Component, component)
|
|
250
293
|
save_yaml(self._state_path, self._state)
|
|
@@ -275,9 +318,7 @@ class SolutionManager:
|
|
|
275
318
|
|
|
276
319
|
def _apply_secrets_state(self):
|
|
277
320
|
"""Applies secrets states to the engine."""
|
|
278
|
-
secrets_list =
|
|
279
|
-
self._state.secrets
|
|
280
|
-
) # TODO: a chequear + self._state.imported_secrets
|
|
321
|
+
secrets_list = self._state.secrets
|
|
281
322
|
for i in range(len(secrets_list)):
|
|
282
323
|
result = self._apply_exec.apply(
|
|
283
324
|
model=Secret, local_instance=secrets_list[i]
|
|
@@ -362,7 +403,7 @@ class SolutionManager:
|
|
|
362
403
|
return routine_list
|
|
363
404
|
|
|
364
405
|
def _apply_functions_state(self):
|
|
365
|
-
"""Applies
|
|
406
|
+
"""Applies Function states to the engine."""
|
|
366
407
|
functions_list = self._state.functions
|
|
367
408
|
for i in range(len(functions_list)):
|
|
368
409
|
result = self._apply_exec.apply(
|
|
@@ -375,6 +416,44 @@ class SolutionManager:
|
|
|
375
416
|
)
|
|
376
417
|
save_yaml(self._state_path, self._state)
|
|
377
418
|
|
|
419
|
+
imported_functions_list = self._state.imported_functions
|
|
420
|
+
for i in range(len(imported_functions_list)):
|
|
421
|
+
result = self._apply_exec.apply(
|
|
422
|
+
model=Function,
|
|
423
|
+
local_instance=imported_functions_list[i],
|
|
424
|
+
not_found_is_exception=True,
|
|
425
|
+
)
|
|
426
|
+
if result.update:
|
|
427
|
+
imported_functions_list[i] = Function.model_validate(
|
|
428
|
+
result.updated_dict
|
|
429
|
+
)
|
|
430
|
+
save_yaml(self._state_path, self._state)
|
|
431
|
+
|
|
432
|
+
def _apply_alerts_state(self):
|
|
433
|
+
"""Applies Alert states to the engine."""
|
|
434
|
+
alerts_list = self._state.alerts
|
|
435
|
+
for i in range(len(alerts_list)):
|
|
436
|
+
result = self._apply_exec.apply(
|
|
437
|
+
model=Alert,
|
|
438
|
+
local_instance=alerts_list[i],
|
|
439
|
+
)
|
|
440
|
+
if result.update:
|
|
441
|
+
alerts_list[i] = Alert.model_validate(result.updated_dict)
|
|
442
|
+
save_yaml(self._state_path, self._state)
|
|
443
|
+
|
|
444
|
+
imported_alerts_list = self._state.imported_alerts
|
|
445
|
+
for i in range(len(imported_alerts_list)):
|
|
446
|
+
result = self._apply_exec.apply(
|
|
447
|
+
model=Alert,
|
|
448
|
+
local_instance=imported_alerts_list[i],
|
|
449
|
+
not_found_is_exception=True,
|
|
450
|
+
)
|
|
451
|
+
if result.update:
|
|
452
|
+
imported_alerts_list[i] = Alert.model_validate(
|
|
453
|
+
result.updated_dict
|
|
454
|
+
)
|
|
455
|
+
save_yaml(self._state_path, self._state)
|
|
456
|
+
|
|
378
457
|
def _apply_files_state(self):
|
|
379
458
|
"""Applies Files states to the engine."""
|
|
380
459
|
files_list = self._state.files
|
|
@@ -3,6 +3,8 @@ from typing import Callable, List, Type
|
|
|
3
3
|
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
from splight_lib.models import (
|
|
6
|
+
Alert,
|
|
7
|
+
AlertItem,
|
|
6
8
|
Asset,
|
|
7
9
|
Attribute,
|
|
8
10
|
File,
|
|
@@ -11,6 +13,8 @@ from splight_lib.models import (
|
|
|
11
13
|
RoutineObject,
|
|
12
14
|
Secret,
|
|
13
15
|
)
|
|
16
|
+
from splight_lib.models.alert import AlertThreshold
|
|
17
|
+
from splight_lib.models.asset import GeometryCollection
|
|
14
18
|
|
|
15
19
|
from splight_cli.solution.exceptions import ElemnentAlreadyDefined
|
|
16
20
|
from splight_cli.solution.models import Component
|
|
@@ -23,7 +27,8 @@ CheckResult = namedtuple(
|
|
|
23
27
|
"secrets_to_delete",
|
|
24
28
|
"files_to_delete",
|
|
25
29
|
"components_to_delete",
|
|
26
|
-
"
|
|
30
|
+
"functions_to_delete",
|
|
31
|
+
"alerts_to_delete",
|
|
27
32
|
"plan",
|
|
28
33
|
"state",
|
|
29
34
|
),
|
|
@@ -87,6 +92,18 @@ class SolutionChecker:
|
|
|
87
92
|
Function.__name__,
|
|
88
93
|
self._update_function,
|
|
89
94
|
)
|
|
95
|
+
alerts_to_delete = self._check_elements(
|
|
96
|
+
self._plan.alerts,
|
|
97
|
+
self._state.alerts,
|
|
98
|
+
Alert.__name__,
|
|
99
|
+
self._update_alert,
|
|
100
|
+
)
|
|
101
|
+
alerts_to_delete += self._check_elements(
|
|
102
|
+
self._plan.imported_alerts,
|
|
103
|
+
self._state.imported_alerts,
|
|
104
|
+
Alert.__name__,
|
|
105
|
+
self._update_alert,
|
|
106
|
+
)
|
|
90
107
|
components_to_delete = self._check_elements(
|
|
91
108
|
self._plan.components,
|
|
92
109
|
self._state.components,
|
|
@@ -105,7 +122,8 @@ class SolutionChecker:
|
|
|
105
122
|
secrets_to_delete=secrets_to_delete,
|
|
106
123
|
files_to_delete=files_to_delete,
|
|
107
124
|
components_to_delete=components_to_delete,
|
|
108
|
-
|
|
125
|
+
functions_to_delete=functions_to_delete,
|
|
126
|
+
alerts_to_delete=alerts_to_delete,
|
|
109
127
|
plan=self._plan,
|
|
110
128
|
state=self._state,
|
|
111
129
|
)
|
|
@@ -186,7 +204,9 @@ class SolutionChecker:
|
|
|
186
204
|
Updated asset.
|
|
187
205
|
"""
|
|
188
206
|
plan_asset_dict = plan_asset.model_dump(
|
|
189
|
-
exclude={"attributes"
|
|
207
|
+
exclude={"attributes", "geomtry"},
|
|
208
|
+
exclude_none=True,
|
|
209
|
+
exclude_unset=True,
|
|
190
210
|
)
|
|
191
211
|
state_asset = state_asset.model_copy(update=plan_asset_dict)
|
|
192
212
|
self._check_elements(
|
|
@@ -195,6 +215,8 @@ class SolutionChecker:
|
|
|
195
215
|
Attribute.__name__,
|
|
196
216
|
self._update_attribute,
|
|
197
217
|
)
|
|
218
|
+
if geometry := plan_asset_dict.get("geometry", {}):
|
|
219
|
+
state_asset.geometry = GeometryCollection.model_validate(geometry)
|
|
198
220
|
return state_asset
|
|
199
221
|
|
|
200
222
|
def _update_attribute(
|
|
@@ -227,9 +249,9 @@ class SolutionChecker:
|
|
|
227
249
|
|
|
228
250
|
Parameters
|
|
229
251
|
----------
|
|
230
|
-
plan_secret :
|
|
252
|
+
plan_secret : Secret
|
|
231
253
|
Plan secret.
|
|
232
|
-
state_secret :
|
|
254
|
+
state_secret : Secret
|
|
233
255
|
State secret to be updated based on the plan secret.
|
|
234
256
|
|
|
235
257
|
Returns
|
|
@@ -353,3 +375,35 @@ class SolutionChecker:
|
|
|
353
375
|
exclude_unset=True,
|
|
354
376
|
)
|
|
355
377
|
return state_function_item.model_copy(update=plan_function_item_dict)
|
|
378
|
+
|
|
379
|
+
def _update_alert(self, plan_alert: Alert, state_alert: Alert):
|
|
380
|
+
plan_alert_dict = plan_alert.model_dump(
|
|
381
|
+
exclude_none=True, exclude_unset=True, exclude={"alert_items"}
|
|
382
|
+
)
|
|
383
|
+
state_alert = state_alert.model_copy(update=plan_alert_dict)
|
|
384
|
+
|
|
385
|
+
state_alert.stmt_thresholds = []
|
|
386
|
+
for threshold in plan_alert_dict["stmt_thresholds"]:
|
|
387
|
+
state_alert.stmt_thresholds.append(
|
|
388
|
+
AlertThreshold.model_validate(threshold)
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
self._check_elements(
|
|
392
|
+
plan_alert.alert_items,
|
|
393
|
+
state_alert.alert_items,
|
|
394
|
+
AlertItem.__name__,
|
|
395
|
+
self._update_alert_item,
|
|
396
|
+
accesor="ref_id",
|
|
397
|
+
)
|
|
398
|
+
return state_alert
|
|
399
|
+
|
|
400
|
+
def _update_alert_item(
|
|
401
|
+
self,
|
|
402
|
+
plan_alert_item: AlertItem,
|
|
403
|
+
state_alert_item: AlertItem,
|
|
404
|
+
):
|
|
405
|
+
plan_alert_item_dict = plan_alert_item.model_dump(
|
|
406
|
+
exclude_none=True,
|
|
407
|
+
exclude_unset=True,
|
|
408
|
+
)
|
|
409
|
+
return state_alert_item.model_copy(update=plan_alert_item_dict)
|
|
@@ -4,6 +4,10 @@ from splight_cli.solution.solution import StateSolution
|
|
|
4
4
|
from splight_cli.solution.utils import to_dict
|
|
5
5
|
|
|
6
6
|
TEST_PLAN = {
|
|
7
|
+
"secrets": [],
|
|
8
|
+
"files": [],
|
|
9
|
+
"functions": [],
|
|
10
|
+
"alerts": [],
|
|
7
11
|
"assets": [
|
|
8
12
|
{
|
|
9
13
|
"attributes": [
|
|
@@ -171,8 +175,8 @@ TEST_PLAN = {
|
|
|
171
175
|
"sensitive": False,
|
|
172
176
|
"type": "DataAddress",
|
|
173
177
|
"value": {
|
|
174
|
-
"asset": "
|
|
175
|
-
"attribute": "attr_1",
|
|
178
|
+
"asset": f"asset.{{{{test_asset}}}}",
|
|
179
|
+
"attribute": f"asset.{{{{test_asset}}}}.attribute.{{{{attr_1}}}}",
|
|
176
180
|
},
|
|
177
181
|
"value_type": "Number",
|
|
178
182
|
},
|
|
@@ -199,12 +203,12 @@ TEST_PLAN = {
|
|
|
199
203
|
"type": "DataAddress",
|
|
200
204
|
"value": [
|
|
201
205
|
{
|
|
202
|
-
"asset": "
|
|
203
|
-
"attribute": "attr_2",
|
|
206
|
+
"asset": f"asset.{{{{test_asset}}}}",
|
|
207
|
+
"attribute": f"asset.{{{{test_asset}}}}.attribute.{{{{attr_2}}}}",
|
|
204
208
|
},
|
|
205
209
|
{
|
|
206
|
-
"asset": "
|
|
207
|
-
"attribute": "attr_3",
|
|
210
|
+
"asset": f"asset.{{{{test_asset}}}}",
|
|
211
|
+
"attribute": f"asset.{{{{test_asset}}}}.attribute.{{{{attr_3}}}}",
|
|
208
212
|
},
|
|
209
213
|
],
|
|
210
214
|
"value_type": "Number",
|
|
@@ -222,8 +226,8 @@ TEST_PLAN = {
|
|
|
222
226
|
"sensitive": False,
|
|
223
227
|
"type": "DataAddress",
|
|
224
228
|
"value": {
|
|
225
|
-
"asset": "
|
|
226
|
-
"attribute": "attr_4",
|
|
229
|
+
"asset": f"asset.{{{{test_asset}}}}",
|
|
230
|
+
"attribute": f"asset.{{{{test_asset}}}}.attribute.{{{{attr_4}}}}",
|
|
227
231
|
},
|
|
228
232
|
"value_type": "Number",
|
|
229
233
|
},
|
|
@@ -275,6 +279,10 @@ TEST_PLAN = {
|
|
|
275
279
|
"imported_components": [],
|
|
276
280
|
}
|
|
277
281
|
TEST_STATE_FILLED = {
|
|
282
|
+
"secrets": [],
|
|
283
|
+
"files": [],
|
|
284
|
+
"functions": [],
|
|
285
|
+
"alerts": [],
|
|
278
286
|
"assets": [
|
|
279
287
|
{
|
|
280
288
|
"attributes": [
|
|
@@ -22,8 +22,8 @@ def test_plan_print(load_yaml_mock, save_yaml_mock):
|
|
|
22
22
|
def test_plan_raise_undefined_asset_name(load_yaml_mock, save_yaml_mock):
|
|
23
23
|
plan = get_plan()
|
|
24
24
|
plan["components"][0]["routines"][0]["output"][0]["value"] = {
|
|
25
|
-
"asset": "
|
|
26
|
-
"attribute": "attr_5",
|
|
25
|
+
"asset": f"asset.{{{{test_asset}}}}",
|
|
26
|
+
"attribute": f"asset.{{{{test_asset}}}}.attribute.{{{{attr_5}}}}",
|
|
27
27
|
}
|
|
28
28
|
load_yaml_mock.side_effect = [plan, plan]
|
|
29
29
|
solution_manager = SolutionManager("./dummy_path", "./dummy_path")
|
|
@@ -10,6 +10,7 @@ from pydantic import BaseModel
|
|
|
10
10
|
from rich.console import Console
|
|
11
11
|
from rich.style import Style
|
|
12
12
|
from splight_lib.models import (
|
|
13
|
+
Alert,
|
|
13
14
|
Asset,
|
|
14
15
|
Component,
|
|
15
16
|
File,
|
|
@@ -20,7 +21,9 @@ from splight_lib.models import (
|
|
|
20
21
|
|
|
21
22
|
console = Console()
|
|
22
23
|
|
|
23
|
-
SplightTypes = Union[
|
|
24
|
+
SplightTypes = Union[
|
|
25
|
+
Asset, Alert, Secret, Component, RoutineObject, File, Function
|
|
26
|
+
]
|
|
24
27
|
|
|
25
28
|
MatchResult = namedtuple(
|
|
26
29
|
"MatchResult", ["is_id", "type", "asset", "attribute"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/component/tests/test_component_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/hub/component/tests/test_hub_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splight_cli-4.6.0.dev1 → splight_cli-4.6.1}/splight_cli/tests/test_component/Initialization
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|