splight-cli 5.1.5__tar.gz → 5.2.0__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 (89) hide show
  1. {splight_cli-5.1.5 → splight_cli-5.2.0}/PKG-INFO +2 -2
  2. {splight_cli-5.1.5 → splight_cli-5.2.0}/pyproject.toml +2 -2
  3. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/cli.py +2 -2
  4. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/context/workspace.py +1 -1
  5. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/__init__.py +0 -2
  6. splight_cli-5.1.5/splight_cli/context/exceptions.py +0 -3
  7. splight_cli-5.1.5/splight_cli/hub/solution/__init__.py +0 -60
  8. splight_cli-5.1.5/splight_cli/hub/solution/constants.py +0 -4
  9. splight_cli-5.1.5/splight_cli/hub/solution/exceptions.py +0 -30
  10. splight_cli-5.1.5/splight_cli/hub/solution/solution_manager.py +0 -112
  11. splight_cli-5.1.5/splight_cli/solution/__init__.py +0 -88
  12. splight_cli-5.1.5/splight_cli/solution/dict.py +0 -50
  13. splight_cli-5.1.5/splight_cli/solution/parser.py +0 -103
  14. splight_cli-5.1.5/splight_cli/solution/resource/__init__.py +0 -5
  15. splight_cli-5.1.5/splight_cli/solution/resource/diff.py +0 -39
  16. splight_cli-5.1.5/splight_cli/solution/resource/logger.py +0 -36
  17. splight_cli-5.1.5/splight_cli/solution/resource/manager.py +0 -237
  18. splight_cli-5.1.5/splight_cli/solution/resource/models/__init__.py +0 -25
  19. splight_cli-5.1.5/splight_cli/solution/resource/models/alert.py +0 -14
  20. splight_cli-5.1.5/splight_cli/solution/resource/models/asset.py +0 -8
  21. splight_cli-5.1.5/splight_cli/solution/resource/models/attribute.py +0 -8
  22. splight_cli-5.1.5/splight_cli/solution/resource/models/base.py +0 -77
  23. splight_cli-5.1.5/splight_cli/solution/resource/models/component.py +0 -12
  24. splight_cli-5.1.5/splight_cli/solution/resource/models/component_object.py +0 -8
  25. splight_cli-5.1.5/splight_cli/solution/resource/models/file.py +0 -8
  26. splight_cli-5.1.5/splight_cli/solution/resource/models/function.py +0 -14
  27. splight_cli-5.1.5/splight_cli/solution/resource/models/metadata.py +0 -8
  28. splight_cli-5.1.5/splight_cli/solution/resource/models/routine.py +0 -9
  29. splight_cli-5.1.5/splight_cli/solution/resource/models/secret.py +0 -38
  30. splight_cli-5.1.5/splight_cli/solution/state.py +0 -56
  31. {splight_cli-5.1.5 → splight_cli-5.2.0}/README.md +0 -0
  32. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/__init__.py +0 -0
  33. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/__init__.py +0 -0
  34. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/component.py +0 -0
  35. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/exceptions.py +0 -0
  36. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/loaders.py +0 -0
  37. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/.splightignore +0 -0
  38. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/Initialization +0 -0
  39. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/README.md +0 -0
  40. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/auto_readme.md +0 -0
  41. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/main.py +0 -0
  42. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/spec.json +0 -0
  43. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/templates/tests.py +0 -0
  44. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/tests/__init__.py +0 -0
  45. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/tests/test_component_manager.py +0 -0
  46. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/component/utils.py +0 -0
  47. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/config/__init__.py +0 -0
  48. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/constants.py +0 -0
  49. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/context/__init__.py +0 -0
  50. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/__init__.py +0 -0
  51. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/alert/__init__.py +0 -0
  52. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/asset/__init__.py +0 -0
  53. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/attribute/__init__.py +0 -0
  54. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/component/__init__.py +0 -0
  55. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/datalake/__init__.py +0 -0
  56. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/file/__init__.py +0 -0
  57. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/manager/__init__.py +0 -0
  58. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/manager/exceptions.py +0 -0
  59. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/manager/manager.py +0 -0
  60. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/manager/tests/test_datalake_manager.py +0 -0
  61. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/manager/tests/test_resource_manager.py +0 -0
  62. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/engine/secret/__init__.py +0 -0
  63. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/__init__.py +0 -0
  64. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/component_builder.py +0 -0
  65. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/exceptions.py +0 -0
  66. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/hub_manager.py +0 -0
  67. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/resources/Dockerfile +0 -0
  68. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/resources/installer.py +0 -0
  69. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/component/tests/test_hub_manager.py +0 -0
  70. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/exceptions.py +0 -0
  71. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/server/__init__.py +0 -0
  72. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/server/exceptions.py +0 -0
  73. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/server/server_builder.py +0 -0
  74. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/hub/server/server_manager.py +0 -0
  75. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/settings.py +0 -0
  76. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/tests/test_component/Initialization +0 -0
  77. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/tests/test_component/README.md +0 -0
  78. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/tests/test_component/main.py +0 -0
  79. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/tests/test_component/spec.json +0 -0
  80. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/tests/test_component/tests.py +0 -0
  81. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/__init__.py +0 -0
  82. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/input.py +0 -0
  83. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/json.py +0 -0
  84. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/loader.py +0 -0
  85. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/pprint.py +0 -0
  86. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/template.py +0 -0
  87. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/utils/yaml.py +0 -0
  88. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/version.py +0 -0
  89. {splight_cli-5.1.5 → splight_cli-5.2.0}/splight_cli/workspace/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: splight-cli
3
- Version: 5.1.5
3
+ Version: 5.2.0
4
4
  Summary: Splight Command Line Interface
5
5
  Author: Splight Dev
6
6
  Author-email: dev@splight-ae.com
@@ -20,7 +20,7 @@ Requires-Dist: py7zr (==0.20.8)
20
20
  Requires-Dist: requests (==2.32.2)
21
21
  Requires-Dist: rich (==13.0.1)
22
22
  Requires-Dist: setuptools (==70.0.0)
23
- Requires-Dist: splight-lib (>=5.11.4,<6.0.0)
23
+ Requires-Dist: splight-lib (>=5.12.0,<6.0.0)
24
24
  Requires-Dist: toposort (>=1.10,<2.0)
25
25
  Requires-Dist: typer (==0.9.0)
26
26
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "splight-cli"
3
- version = "5.1.5"
3
+ version = "5.2.0"
4
4
  description = "Splight Command Line Interface"
5
5
  authors = ["Splight Dev <dev@splight-ae.com>"]
6
6
  readme = "README.md"
@@ -11,7 +11,7 @@ case-converter = "1.1.0"
11
11
  click = "8.0.4"
12
12
  click-default-group = "1.2.2"
13
13
  setuptools = "70.0.0"
14
- splight-lib = "^5.11.4"
14
+ splight-lib = "^5.12.0"
15
15
  py7zr = "0.20.8"
16
16
  typer = "0.9.0"
17
17
  colorama = "0.4.4"
@@ -7,7 +7,7 @@ from splight_cli.component import component_app
7
7
  from splight_cli.config import config_app
8
8
  from splight_cli.constants import error_style
9
9
  from splight_cli.context import Context
10
- from splight_cli.context.exceptions import MissingConfigurationFile
10
+ from splight_cli.context.workspace import ConfigurationError
11
11
  from splight_cli.engine import engine_app
12
12
  from splight_cli.hub import hub_app
13
13
  from splight_cli.solution import solution_app
@@ -50,7 +50,7 @@ def main(
50
50
  True if ctx.invoked_subcommand == "configure" else False
51
51
  )
52
52
  )
53
- except MissingConfigurationFile as exc:
53
+ except ConfigurationError as exc:
54
54
  console.print(
55
55
  (
56
56
  f"Error running Splight: {str(exc)}. "
@@ -42,7 +42,7 @@ class WorkspaceManager:
42
42
  self._workspaces = self._config.workspaces
43
43
  self._current_workspace = self._config.current_workspace
44
44
  self._settings = self._workspaces[self._current_workspace]
45
- if not self._settings.is_configured():
45
+ if not self._settings.is_configured() and not new_workspace:
46
46
  raise ConfigurationError(
47
47
  (
48
48
  "There is at least one variable missing. "
@@ -3,7 +3,6 @@ import typer
3
3
  from splight_cli.context import check_credentials
4
4
  from splight_cli.hub.component import component_app
5
5
  from splight_cli.hub.server import server_app
6
- from splight_cli.hub.solution import solution_app
7
6
 
8
7
  hub_app = typer.Typer(
9
8
  name="Splight Hub",
@@ -13,7 +12,6 @@ hub_app = typer.Typer(
13
12
  )
14
13
 
15
14
  hub_app.add_typer(component_app, name="component")
16
- hub_app.add_typer(solution_app, name="solution")
17
15
  hub_app.add_typer(server_app, name="server")
18
16
 
19
17
 
@@ -1,3 +0,0 @@
1
- class MissingConfigurationFile(Exception):
2
- def __init__(self, message):
3
- super().__init__(message)
@@ -1,60 +0,0 @@
1
- import typer
2
- from rich.console import Console
3
- from splight_lib.models import Component
4
-
5
- from splight_cli.constants import error_style
6
- from splight_cli.hub.solution.solution_manager import HubSolutionManager
7
-
8
- solution_app = typer.Typer(
9
- name="Splight Hub Solution",
10
- add_completion=True,
11
- rich_markup_mode="rich",
12
- no_args_is_help=True,
13
- )
14
-
15
- console = Console()
16
- MODEL = Component
17
-
18
-
19
- @solution_app.command()
20
- def push(
21
- ctx: typer.Context,
22
- path: str = typer.Argument(..., help="Path to solution files"),
23
- force: bool = typer.Option(
24
- False,
25
- "--force",
26
- "-f",
27
- help="Overwrite existing solution in Splight HUB",
28
- ),
29
- ):
30
- try:
31
- manager = HubSolutionManager()
32
- manager.push(path, force=force)
33
- except Exception as exc:
34
- console.print(f"Error pushing solution: {exc}", style=error_style)
35
- raise typer.Exit(code=1)
36
-
37
-
38
- # TODO: Define the pull command
39
- @solution_app.command()
40
- def pull(
41
- ctx: typer.Context,
42
- name: str = typer.Argument(..., help="The component's name"),
43
- version: str = typer.Argument(..., help="The component's version"),
44
- ):
45
- try:
46
- manager = HubSolutionManager()
47
- manager.pull(name=name, version=version)
48
- except Exception as exc:
49
- console.print(f"Error pulling solution: {exc}", style=error_style)
50
- raise typer.Exit(code=1)
51
-
52
-
53
- @solution_app.command()
54
- def list(ctx: typer.Context):
55
- try:
56
- manager = HubSolutionManager()
57
- manager.list_solutions()
58
- except Exception as exc:
59
- console.print(f"Error listing components: {exc}", style=error_style)
60
- raise typer.Exit(code=1)
@@ -1,4 +0,0 @@
1
- MAIN_FILE = "main.tf"
2
- VARIABLES_FILE = "variables.tf"
3
- VALUES_FILE = "variables.tfvars"
4
- README_FILE = "README.md"
@@ -1,30 +0,0 @@
1
- class HubSolutionAlreadyExists(Exception):
2
- def __init__(self, name: str, version: str):
3
- self._msg = f"Solution {name}-{version} already exists in Splight Hub"
4
-
5
- def __str__(self) -> str:
6
- return self._msg
7
-
8
-
9
- class HubSolutionNotFound(Exception):
10
- def __init__(self, name: str, version: str):
11
- self._msg = f"Solution {name}-{version} not found in Splight Hub"
12
-
13
- def __str__(self) -> str:
14
- return self._msg
15
-
16
-
17
- class MissingSolutionFile(Exception):
18
- def __init__(self, name: str, path: str):
19
- self._msg = f"Missing solution file {name} in path {path}"
20
-
21
- def __str__(self) -> str:
22
- return self._msg
23
-
24
-
25
- class SolutionDirectoryAlreadyExists(Exception):
26
- def __init__(self, path: str):
27
- self._msg = f"Solution directory {path} already exists"
28
-
29
- def __str__(self) -> str:
30
- return self._msg
@@ -1,112 +0,0 @@
1
- import json
2
- import os
3
- import shutil
4
- from collections import namedtuple
5
-
6
- import py7zr
7
- from pydantic import ValidationError
8
- from rich.console import Console
9
- from rich.table import Table
10
- from splight_lib.models import HubSolution
11
-
12
- from splight_cli.constants import COMPRESSION_TYPE, SPEC_FILE, success_style
13
- from splight_cli.hub.exceptions import SpecFormatError, SpecValidationError
14
- from splight_cli.hub.solution.exceptions import (
15
- HubSolutionAlreadyExists,
16
- HubSolutionNotFound,
17
- SolutionDirectoryAlreadyExists,
18
- )
19
- from splight_cli.utils.loader import Loader
20
-
21
- console = Console()
22
-
23
-
24
- class HubSolutionManager:
25
- def list_solutions(self):
26
- solution_tuple = namedtuple("Solution", ["name", "version"])
27
- all_solutions = HubSolution.list()
28
- solutions = set(
29
- [
30
- solution_tuple(solution.name, solution.version)
31
- for solution in all_solutions
32
- ]
33
- )
34
- table = Table("Name", "Version", title="Hub Solution List")
35
- [table.add_row(sol.name, sol.version) for sol in solutions]
36
- console.print(table)
37
-
38
- def push(
39
- self,
40
- path: str = ".",
41
- force: bool = False,
42
- ):
43
- try:
44
- with open(os.path.join(path, SPEC_FILE)) as fid:
45
- spec = json.load(fid)
46
- except Exception as exc:
47
- raise SpecFormatError(exc)
48
-
49
- # Validate spec fields before pushing the model
50
- try:
51
- instance = HubSolution.model_validate(spec)
52
- except ValidationError as exc:
53
- raise SpecValidationError(exc)
54
-
55
- name = instance.name
56
- version = instance.version
57
-
58
- if not force and self._exists_in_hub(name, version):
59
- raise HubSolutionAlreadyExists(name, version)
60
-
61
- with Loader("Pushing Solution to Splight Hub"):
62
- solution = HubSolution.upload(path)
63
-
64
- console.print(
65
- f"Solution {solution.id} pushed succesfully", style=success_style
66
- )
67
-
68
- def pull(self, name: str, version: str):
69
- with Loader("Pulling solution from Splight Hub"):
70
- self._pull_solution(name, version)
71
- console.print(
72
- f"Solution {name} pulled succesfully", style=success_style
73
- )
74
-
75
- def _pull_solution(self, name: str, version: str):
76
- solution = self._get_hub_solution(name, version)
77
- name, version = solution.name, solution.version
78
- file_wrapper = solution.download()
79
- file_data = file_wrapper.read()
80
-
81
- version_modified = version.replace(".", "_")
82
- solution_path = f"{name}/{version_modified}"
83
- versioned_name = f"{name}-{version}"
84
- file_name = f"{versioned_name}.{COMPRESSION_TYPE}"
85
- if os.path.exists(solution_path):
86
- raise SolutionDirectoryAlreadyExists(solution_path)
87
-
88
- try:
89
- with open(file_name, "wb") as fid:
90
- fid.write(file_data)
91
-
92
- with py7zr.SevenZipFile(file_name, "r") as z:
93
- z.extractall(path=".")
94
- shutil.move(f"{versioned_name}", solution_path)
95
- except Exception as exc:
96
- raise exc
97
- finally:
98
- if os.path.exists(file_name):
99
- os.remove(file_name)
100
-
101
- def _get_hub_solution(self, name: str, version: str) -> HubSolution:
102
- solutions = HubSolution.list(name=name, version=version)
103
- if not solutions:
104
- raise HubSolutionNotFound(name, version)
105
- return solutions[0]
106
-
107
- def _exists_in_hub(self, name: str, version: str) -> bool:
108
- try:
109
- self._get_hub_solution(name, version)
110
- return True
111
- except HubSolutionNotFound:
112
- return False
@@ -1,88 +0,0 @@
1
- import typer
2
- from rich.console import Console
3
-
4
- from splight_cli.context import check_credentials
5
- from splight_cli.solution.parser import Parser
6
- from splight_cli.solution.resource import ResourceManager
7
- from splight_cli.solution.state import State
8
-
9
- solution_app = typer.Typer(
10
- name="Splight Solution",
11
- add_completion=True,
12
- rich_markup_mode="rich",
13
- no_args_is_help=True,
14
- )
15
-
16
- console = Console()
17
-
18
-
19
- @solution_app.callback(invoke_without_command=True)
20
- def callback(ctx: typer.Context):
21
- check_credentials(ctx)
22
-
23
-
24
- # FIXME: find a nice way to print the exceptions.
25
- # Take into account that most exception messages are short and do not have
26
- # that much content. I would include the traceback, or at least some part of it.
27
- # Another option is to not catch them, and show the entire error, so the user can
28
- # debug it.
29
- @solution_app.command()
30
- def apply(
31
- _: typer.Context,
32
- spec_file: str = typer.Argument(..., help="Path to the spec file."),
33
- state_file: str = typer.Option(
34
- "state.json", "--state", "-s", help="Path to the state file."
35
- ),
36
- ) -> None:
37
- state = State(path=state_file)
38
- state.load()
39
-
40
- parser = Parser(spec_file=spec_file)
41
- specs, dependency_graph = parser.parse()
42
-
43
- manager = ResourceManager(
44
- state=state,
45
- specs=specs,
46
- dependency_graph=dependency_graph,
47
- )
48
- manager.refresh()
49
- plan = manager.plan()
50
- if plan:
51
- manager.apply()
52
-
53
-
54
- @solution_app.command()
55
- def plan(
56
- _: typer.Context,
57
- spec_file: str = typer.Argument(..., help="Path to the spec file."),
58
- state_file: str = typer.Option(
59
- "state.json", "--state", "-s", help="Path to the state file."
60
- ),
61
- ) -> None:
62
- state = State(path=state_file)
63
- state.load()
64
-
65
- parser = Parser(spec_file=spec_file)
66
- specs, dependency_graph = parser.parse()
67
-
68
- manager = ResourceManager(
69
- state=state,
70
- specs=specs,
71
- dependency_graph=dependency_graph,
72
- )
73
- manager.refresh()
74
- manager.plan()
75
-
76
-
77
- @solution_app.command()
78
- def refresh(
79
- _: typer.Context,
80
- state_file: str = typer.Option(
81
- "state.json", "--state", "-s", help="Path to the state file."
82
- ),
83
- ) -> None:
84
- state = State(path=state_file)
85
- state.load()
86
-
87
- manager = ResourceManager(state=state)
88
- manager.refresh()
@@ -1,50 +0,0 @@
1
- from typing import Any, Dict, List
2
-
3
-
4
- # FIXME: make this faster using a stack
5
- def walk_dict(data: Dict, current=[]):
6
- result = []
7
-
8
- if isinstance(data, Dict):
9
- for key, value in data.items():
10
- current.append(key)
11
- result.extend(walk_dict(value, current.copy()))
12
- current.pop()
13
- elif isinstance(data, List):
14
- for index, item in enumerate(data):
15
- current.append(index)
16
- result.extend(walk_dict(item, current.copy()))
17
- current.pop()
18
- else:
19
- result.append((current.copy(), data))
20
-
21
- return result
22
-
23
-
24
- def get_dict_value(path: List, data: Dict):
25
- # Assumes the path exists
26
- current = data
27
- for key in path:
28
- if isinstance(current, dict):
29
- current = current[key]
30
- elif isinstance(current, list):
31
- current = current[int(key)]
32
- else:
33
- raise ValueError(f"Invalid path: {path}")
34
- return current
35
-
36
-
37
- def set_dict_value(value: Any, path: List, data: Dict):
38
- # Assumes the path exists
39
- current = data
40
- for key in path[:-1]:
41
- current = current[key]
42
-
43
- last_key = path[-1]
44
-
45
- if isinstance(current, dict):
46
- current[last_key] = value
47
- elif isinstance(current, list):
48
- current[int(last_key)] = value
49
- else:
50
- raise ValueError(f"Invalid path: {path}")
@@ -1,103 +0,0 @@
1
- import re
2
- from typing import Dict, List, Optional
3
-
4
- import yaml
5
-
6
- from splight_cli.solution.dict import walk_dict
7
-
8
-
9
- class DuplicateResourceError(Exception):
10
- pass
11
-
12
-
13
- def parse_reference(value: str) -> Optional[Dict]:
14
- """Return the reference if it matches the regex or None otherwise"""
15
- # FIXME: make it so we are able to reference the 'name' of a resource.
16
- # This is super easy to do:
17
- # 1. Add '.id' and '.name' at the end of the regex
18
- # 2. Save that accessor to the references dict in each spec.
19
- # 3. Change the 'replace_references' function in ResourceManager so it
20
- # gets the correct attribute.
21
-
22
- # Only process strings
23
- if isinstance(value, str):
24
- # Building this with format strings breaks the expression
25
- pattern = r"^\${{(Asset|Attribute|Metadata|File|Function|Secret|Alert|Component|Routine)\.(\w+)}}$"
26
-
27
- # Get the matches
28
- match = re.match(pattern, value)
29
-
30
- if match:
31
- type, name = match.groups()
32
- key = f"{type}:{name}"
33
- return key
34
-
35
-
36
- class Parser:
37
- def __init__(self, spec_file: str) -> None:
38
- try:
39
- with open(spec_file, "r") as fp:
40
- self._spec_file_data = yaml.safe_load(fp)
41
- except FileNotFoundError:
42
- raise Exception(f"Spec file '{spec_file}' not found.")
43
-
44
- def parse(self) -> List[Dict]:
45
- # Here we build the dependency graph using the resource keys
46
- dependency_graph = {}
47
-
48
- # - Does not destroy all elements.
49
- specs = {}
50
- for resource_spec in self._spec_file_data or []:
51
- name = resource_spec["name"]
52
- type = resource_spec["type"]
53
- arguments = resource_spec["arguments"]
54
-
55
- # Parse each resource leaf value to see if its a reference
56
- depends_on = set({})
57
- references = []
58
- for path, value in walk_dict(resource_spec["arguments"]):
59
- result = parse_reference(value)
60
- if result is not None:
61
- key = result
62
-
63
- # Must be done here, otherwise it would affect the dependency graph.
64
- # Paths and values are validated later on, since we may need data
65
- # that is available after creation.
66
- # This also prevents trying to delete a resource which has another
67
- # one referencing it.
68
- # Also prevents self-referencing.
69
- if key not in specs:
70
- raise ValueError(
71
- f"Reference '{value}' in resource '{name}' of type '{type}' points to a non existing resource."
72
- )
73
-
74
- reference = {
75
- "key": key, # Key of the referenced resource
76
- "target": path, # Where to put the value
77
- "string": value, # The string value of the reference
78
- }
79
-
80
- references.append(reference)
81
- depends_on.add(key)
82
-
83
- key = f"{type}:{name}"
84
- if key in specs:
85
- raise DuplicateResourceError(
86
- f"Resource '{name}' of type '{type}' is defined twice."
87
- )
88
-
89
- specs[key] = {
90
- "name": name,
91
- "type": type,
92
- "depends_on": list(depends_on),
93
- "references": references,
94
- "arguments": arguments,
95
- }
96
-
97
- # Add its dependecies to the graph
98
- dependency_graph[key] = depends_on
99
-
100
- # NOTE: Before adding/changing features of the parser, insert a breakpoint
101
- # here and inspect the specs and dependency_graph objects, to see the shape
102
- # of the objects that the rest of the code needs.
103
- return specs, dependency_graph
@@ -1,5 +0,0 @@
1
- from splight_cli.solution.resource.manager import ResourceManager
2
-
3
- __all__ = [
4
- "ResourceManager",
5
- ]
@@ -1,39 +0,0 @@
1
- from typing import Dict
2
-
3
- from colorama import Fore, Style
4
-
5
- from splight_cli.solution.dict import get_dict_value, walk_dict
6
-
7
-
8
- def strike_text(text):
9
- return "".join(["\u0336{}".format(c) for c in str(text)])
10
-
11
-
12
- def Diff(new_arguments: Dict, old_arguments: Dict) -> Dict:
13
- # FIXME: this try catch mechanic could be simplified by improving
14
- # the 'Dict' related functions.
15
-
16
- # FIXME: Try replacing the QueryFilter in a function for a single value.
17
- # The diff will show incorrectly.
18
- diff = []
19
- for path, new_value in walk_dict(new_arguments):
20
- try:
21
- get_dict_value(path, old_arguments)
22
- except Exception:
23
- line = f"\t{Fore.GREEN}+{Style.RESET_ALL} {'.'.join([str(key) for key in path])}: {new_value}"
24
- diff.append(line)
25
- continue
26
-
27
- old_value = get_dict_value(path, old_arguments)
28
- if new_value != old_value:
29
- line = f"\t{Fore.YELLOW}~{Style.RESET_ALL} {'.'.join([str(key) for key in path])}: {strike_text(old_value)} -> {new_value} "
30
- diff.append(line)
31
-
32
- for path, old_value in walk_dict(old_arguments):
33
- try:
34
- get_dict_value(path, new_arguments)
35
- except Exception:
36
- line = f"\t{Fore.RED}-{Style.RESET_ALL} {'.'.join([str(key) for key in path])}: {strike_text(old_value)}"
37
- diff.append(line)
38
-
39
- return diff
@@ -1,36 +0,0 @@
1
- from colorama import Style
2
-
3
-
4
- class ResourceLogger:
5
- template = '{type}["{name}"]: {message} [id={id}]'
6
-
7
- @staticmethod
8
- def _bold(message: str) -> str:
9
- return f"{Style.BRIGHT}{message}{Style.RESET_ALL}"
10
-
11
- def resource(self, message: str, resource):
12
- formatted_message = self.template.format(
13
- type=resource.type,
14
- name=resource.name,
15
- id=resource.id,
16
- message=message,
17
- )
18
- print(self._bold(formatted_message))
19
-
20
- def event(
21
- self,
22
- message: str,
23
- previous_line: bool = False,
24
- new_line: bool = False,
25
- bold: bool = False,
26
- ):
27
- formatted_message = ""
28
- if previous_line:
29
- formatted_message += "\n"
30
- formatted_message += f"{self._bold(message)}" if bold else message
31
- if new_line:
32
- formatted_message += "\n"
33
- print(formatted_message)
34
-
35
- def diff(self, diff):
36
- print("\n".join(diff))