digitalkin 0.3.2.dev2__py3-none-any.whl
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.
- base_server/__init__.py +1 -0
- base_server/mock/__init__.py +5 -0
- base_server/mock/mock_pb2.py +39 -0
- base_server/mock/mock_pb2_grpc.py +102 -0
- base_server/server_async_insecure.py +125 -0
- base_server/server_async_secure.py +143 -0
- base_server/server_sync_insecure.py +103 -0
- base_server/server_sync_secure.py +122 -0
- digitalkin/__init__.py +8 -0
- digitalkin/__version__.py +8 -0
- digitalkin/core/__init__.py +1 -0
- digitalkin/core/common/__init__.py +9 -0
- digitalkin/core/common/factories.py +156 -0
- digitalkin/core/job_manager/__init__.py +1 -0
- digitalkin/core/job_manager/base_job_manager.py +288 -0
- digitalkin/core/job_manager/single_job_manager.py +354 -0
- digitalkin/core/job_manager/taskiq_broker.py +311 -0
- digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
- digitalkin/core/task_manager/__init__.py +1 -0
- digitalkin/core/task_manager/base_task_manager.py +539 -0
- digitalkin/core/task_manager/local_task_manager.py +108 -0
- digitalkin/core/task_manager/remote_task_manager.py +87 -0
- digitalkin/core/task_manager/surrealdb_repository.py +266 -0
- digitalkin/core/task_manager/task_executor.py +249 -0
- digitalkin/core/task_manager/task_session.py +406 -0
- digitalkin/grpc_servers/__init__.py +1 -0
- digitalkin/grpc_servers/_base_server.py +486 -0
- digitalkin/grpc_servers/module_server.py +208 -0
- digitalkin/grpc_servers/module_servicer.py +516 -0
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +29 -0
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
- digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
- digitalkin/logger.py +157 -0
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +110 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +93 -0
- digitalkin/mixins/filesystem_mixin.py +46 -0
- digitalkin/mixins/logger_mixin.py +51 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/__init__.py +8 -0
- digitalkin/models/core/__init__.py +1 -0
- digitalkin/models/core/job_manager_models.py +36 -0
- digitalkin/models/core/task_monitor.py +70 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/models/grpc_servers/models.py +275 -0
- digitalkin/models/grpc_servers/types.py +24 -0
- digitalkin/models/module/__init__.py +25 -0
- digitalkin/models/module/module.py +40 -0
- digitalkin/models/module/module_context.py +149 -0
- digitalkin/models/module/module_types.py +393 -0
- digitalkin/models/module/utility.py +146 -0
- digitalkin/models/services/__init__.py +10 -0
- digitalkin/models/services/cost.py +54 -0
- digitalkin/models/services/registry.py +42 -0
- digitalkin/models/services/storage.py +44 -0
- digitalkin/modules/__init__.py +11 -0
- digitalkin/modules/_base_module.py +517 -0
- digitalkin/modules/archetype_module.py +23 -0
- digitalkin/modules/tool_module.py +23 -0
- digitalkin/modules/trigger_handler.py +48 -0
- digitalkin/modules/triggers/__init__.py +12 -0
- digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
- digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
- digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
- digitalkin/py.typed +0 -0
- digitalkin/services/__init__.py +30 -0
- digitalkin/services/agent/__init__.py +6 -0
- digitalkin/services/agent/agent_strategy.py +19 -0
- digitalkin/services/agent/default_agent.py +13 -0
- digitalkin/services/base_strategy.py +22 -0
- digitalkin/services/communication/__init__.py +7 -0
- digitalkin/services/communication/communication_strategy.py +76 -0
- digitalkin/services/communication/default_communication.py +101 -0
- digitalkin/services/communication/grpc_communication.py +223 -0
- digitalkin/services/cost/__init__.py +14 -0
- digitalkin/services/cost/cost_strategy.py +100 -0
- digitalkin/services/cost/default_cost.py +114 -0
- digitalkin/services/cost/grpc_cost.py +138 -0
- digitalkin/services/filesystem/__init__.py +7 -0
- digitalkin/services/filesystem/default_filesystem.py +417 -0
- digitalkin/services/filesystem/filesystem_strategy.py +252 -0
- digitalkin/services/filesystem/grpc_filesystem.py +317 -0
- digitalkin/services/identity/__init__.py +6 -0
- digitalkin/services/identity/default_identity.py +15 -0
- digitalkin/services/identity/identity_strategy.py +14 -0
- digitalkin/services/registry/__init__.py +27 -0
- digitalkin/services/registry/default_registry.py +141 -0
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +306 -0
- digitalkin/services/registry/registry_models.py +43 -0
- digitalkin/services/registry/registry_strategy.py +98 -0
- digitalkin/services/services_config.py +200 -0
- digitalkin/services/services_models.py +65 -0
- digitalkin/services/setup/__init__.py +1 -0
- digitalkin/services/setup/default_setup.py +219 -0
- digitalkin/services/setup/grpc_setup.py +343 -0
- digitalkin/services/setup/setup_strategy.py +145 -0
- digitalkin/services/snapshot/__init__.py +6 -0
- digitalkin/services/snapshot/default_snapshot.py +39 -0
- digitalkin/services/snapshot/snapshot_strategy.py +30 -0
- digitalkin/services/storage/__init__.py +7 -0
- digitalkin/services/storage/default_storage.py +228 -0
- digitalkin/services/storage/grpc_storage.py +214 -0
- digitalkin/services/storage/storage_strategy.py +273 -0
- digitalkin/services/user_profile/__init__.py +12 -0
- digitalkin/services/user_profile/default_user_profile.py +55 -0
- digitalkin/services/user_profile/grpc_user_profile.py +69 -0
- digitalkin/services/user_profile/user_profile_strategy.py +40 -0
- digitalkin/utils/__init__.py +29 -0
- digitalkin/utils/arg_parser.py +92 -0
- digitalkin/utils/development_mode_action.py +51 -0
- digitalkin/utils/dynamic_schema.py +483 -0
- digitalkin/utils/llm_ready_schema.py +75 -0
- digitalkin/utils/package_discover.py +357 -0
- digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
- digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
- digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
- digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
- digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
- modules/__init__.py +0 -0
- modules/cpu_intensive_module.py +280 -0
- modules/dynamic_setup_module.py +338 -0
- modules/minimal_llm_module.py +347 -0
- modules/text_transform_module.py +203 -0
- services/filesystem_module.py +200 -0
- services/storage_module.py +206 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""This module contains the strategy models for the services."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from digitalkin.logger import logger
|
|
9
|
+
from digitalkin.services.agent import AgentStrategy
|
|
10
|
+
from digitalkin.services.communication import CommunicationStrategy
|
|
11
|
+
from digitalkin.services.cost import CostStrategy
|
|
12
|
+
from digitalkin.services.filesystem import FilesystemStrategy
|
|
13
|
+
from digitalkin.services.identity import IdentityStrategy
|
|
14
|
+
from digitalkin.services.registry import RegistryStrategy
|
|
15
|
+
from digitalkin.services.snapshot import SnapshotStrategy
|
|
16
|
+
from digitalkin.services.storage import StorageStrategy
|
|
17
|
+
from digitalkin.services.user_profile import UserProfileStrategy
|
|
18
|
+
|
|
19
|
+
# Define type variables
|
|
20
|
+
T = TypeVar(
|
|
21
|
+
"T",
|
|
22
|
+
bound=AgentStrategy
|
|
23
|
+
| CommunicationStrategy
|
|
24
|
+
| CostStrategy
|
|
25
|
+
| FilesystemStrategy
|
|
26
|
+
| IdentityStrategy
|
|
27
|
+
| RegistryStrategy
|
|
28
|
+
| SnapshotStrategy
|
|
29
|
+
| StorageStrategy
|
|
30
|
+
| UserProfileStrategy,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ServicesMode(str, Enum):
|
|
35
|
+
"""Mode for strategy execution."""
|
|
36
|
+
|
|
37
|
+
LOCAL = "local"
|
|
38
|
+
REMOTE = "remote"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ServicesStrategy(BaseModel, Generic[T]):
|
|
42
|
+
"""Service class describing the available services in a Module with local and remote attributes.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
local: type
|
|
46
|
+
remote: type
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
local: type[T]
|
|
50
|
+
remote: type[T]
|
|
51
|
+
|
|
52
|
+
def __getitem__(self, mode: str) -> type[T]:
|
|
53
|
+
"""Get the service strategy based on the mode.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
mode (str): The mode to get the strategy for.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The strategy based on the mode.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
return getattr(self, mode)
|
|
63
|
+
except AttributeError:
|
|
64
|
+
logger.exception("Unknown mode: %s, available modes are: %s", mode, ServicesMode.__members__)
|
|
65
|
+
return getattr(self, ServicesMode.LOCAL.value)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""This module is responsible for handling the setup service."""
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""This module contains the abstract base class for setup strategies."""
|
|
2
|
+
|
|
3
|
+
import secrets
|
|
4
|
+
import string
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
|
|
9
|
+
from digitalkin.logger import logger
|
|
10
|
+
from digitalkin.services.setup.setup_strategy import SetupData, SetupServiceError, SetupStrategy, SetupVersionData
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DefaultSetup(SetupStrategy):
|
|
14
|
+
"""Abstract base class for setup strategies."""
|
|
15
|
+
|
|
16
|
+
setups: dict[str, SetupData]
|
|
17
|
+
setup_versions: dict[str, dict[str, SetupVersionData]]
|
|
18
|
+
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
"""Initialize the default setup strategy."""
|
|
21
|
+
super().__init__()
|
|
22
|
+
self.setups = {}
|
|
23
|
+
self.setup_versions = {}
|
|
24
|
+
|
|
25
|
+
def create_setup(self, setup_dict: dict[str, Any]) -> str:
|
|
26
|
+
"""Create a new setup with comprehensive validation.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
setup_dict: Dictionary containing setup details.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
bool: Success status of setup creation.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ValidationError: If setup data is invalid.
|
|
36
|
+
GrpcOperationError: If gRPC operation fails.
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
valid_data = SetupData.model_validate(setup_dict["data"]) # Revalidates instance
|
|
40
|
+
except ValidationError:
|
|
41
|
+
logger.exception("Validation failed for model SetupData")
|
|
42
|
+
return ""
|
|
43
|
+
|
|
44
|
+
setup_id = setup_dict.get(
|
|
45
|
+
"setup_id", "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(16))
|
|
46
|
+
)
|
|
47
|
+
valid_data.id = setup_id
|
|
48
|
+
self.setups[setup_id] = valid_data
|
|
49
|
+
logger.debug("CREATE SETUP DATA %s:%s successful", setup_id, valid_data)
|
|
50
|
+
return setup_id
|
|
51
|
+
|
|
52
|
+
def get_setup(self, setup_dict: dict[str, Any]) -> SetupData:
|
|
53
|
+
"""Retrieve a setup by its unique identifier.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
setup_dict: Dictionary with 'name' and optional 'version'.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
SetupServiceError: setup_id does not exist.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Dict[str, Any]: Setup details including optional setup version.
|
|
63
|
+
"""
|
|
64
|
+
logger.debug("GET setup_id = %s", setup_dict["setup_id"])
|
|
65
|
+
if setup_dict["setup_id"] not in self.setups:
|
|
66
|
+
msg = f"GET setup_id = {setup_dict['setup_id']}: setup_id DOESN'T EXIST"
|
|
67
|
+
logger.error(msg)
|
|
68
|
+
raise SetupServiceError(msg)
|
|
69
|
+
return self.setups[setup_dict["setup_id"]]
|
|
70
|
+
|
|
71
|
+
def update_setup(self, setup_dict: dict[str, Any]) -> bool:
|
|
72
|
+
"""Update an existing setup.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
setup_dict: Dictionary with setup update details.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ValidationError: setup object failed validation.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
bool: Success status of the update operation.
|
|
82
|
+
"""
|
|
83
|
+
if setup_dict["setup_id"] not in self.setups:
|
|
84
|
+
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_dict["setup_id"])
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
valid_data = SetupData.model_validate(setup_dict["data"]) # Revalidates instance
|
|
89
|
+
except ValidationError:
|
|
90
|
+
logger.exception("Validation failed for model SetupData")
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
self.setups[setup_dict["update_id"]] = valid_data
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
def delete_setup(self, setup_dict: dict[str, Any]) -> bool:
|
|
97
|
+
"""Delete a setup by its unique identifier.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
setup_dict: Dictionary with the setup 'name'.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
bool: Success status of deletion.
|
|
104
|
+
"""
|
|
105
|
+
if setup_dict["setup_id"] not in self.setups:
|
|
106
|
+
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_dict["setup_id"])
|
|
107
|
+
return False
|
|
108
|
+
del self.setups[setup_dict["setup_id"]]
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
def create_setup_version(self, setup_version_dict: dict[str, Any]) -> str:
|
|
112
|
+
"""Create a new setup version.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
setup_version_dict: Dictionary with setup version details.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
SetupServiceError: setup object failed validation.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
str: version of setup version creation.
|
|
122
|
+
"""
|
|
123
|
+
try:
|
|
124
|
+
valid_data = SetupVersionData.model_validate(setup_version_dict["data"]) # Revalidates instance
|
|
125
|
+
except ValidationError:
|
|
126
|
+
msg = "Validation failed for model SetupVersionData"
|
|
127
|
+
logger.exception(msg)
|
|
128
|
+
raise SetupServiceError(msg)
|
|
129
|
+
|
|
130
|
+
if setup_version_dict["setup_id"] not in self.setup_versions:
|
|
131
|
+
self.setup_versions[setup_version_dict["setup_id"]] = {}
|
|
132
|
+
self.setup_versions[setup_version_dict["setup_id"]][valid_data.version] = valid_data
|
|
133
|
+
logger.debug("CREATE SETUP VERSION DATA %s:%s successful", setup_version_dict["setup_id"], valid_data)
|
|
134
|
+
return valid_data.version
|
|
135
|
+
|
|
136
|
+
def get_setup_version(self, setup_version_dict: dict[str, Any]) -> SetupVersionData:
|
|
137
|
+
"""Retrieve a setup version by its unique identifier.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
setup_version_dict: Dictionary with the setup version 'name'.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
SetupServiceError: setup_id does not exist.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Dict[str, Any]: Setup version details.
|
|
147
|
+
"""
|
|
148
|
+
logger.debug("GET setup_id = %s: version = %s", setup_version_dict["setup_id"], setup_version_dict["version"])
|
|
149
|
+
if setup_version_dict["setup_id"] not in self.setup_versions:
|
|
150
|
+
msg = f"GET setup_id = {setup_version_dict['setup_id']}: setup_id DOESN'T EXIST"
|
|
151
|
+
logger.error(msg)
|
|
152
|
+
raise SetupServiceError(msg)
|
|
153
|
+
|
|
154
|
+
return self.setup_versions[setup_version_dict["setup_id"]][setup_version_dict["version"]]
|
|
155
|
+
|
|
156
|
+
def search_setup_versions(self, setup_version_dict: dict[str, Any]) -> list[SetupVersionData]:
|
|
157
|
+
"""Search for setup versions based on filters.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
setup_version_dict: Dictionary with optional 'name' or 'query_versions' filters.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
SetupServiceError: setup_id does not exist.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
List[SetupVersionData]: A list of matching setup version details.
|
|
167
|
+
"""
|
|
168
|
+
if setup_version_dict["setup_id"] not in self.setup_versions:
|
|
169
|
+
msg = f"GET setup_id = {setup_version_dict['setup_id']}: setup_id DOESN'T EXIST"
|
|
170
|
+
logger.error(msg)
|
|
171
|
+
raise SetupServiceError(msg)
|
|
172
|
+
|
|
173
|
+
return [
|
|
174
|
+
value
|
|
175
|
+
for value in self.setup_versions[setup_version_dict["setup_id"]].values()
|
|
176
|
+
if setup_version_dict["query_versions"] in value.version
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
def update_setup_version(self, setup_version_dict: dict[str, Any]) -> bool:
|
|
180
|
+
"""Update an existing setup version.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
setup_version_dict: Dictionary with setup version update details.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
bool: Success status of the update operation.
|
|
187
|
+
"""
|
|
188
|
+
if setup_version_dict["setup_id"] not in self.setup_versions:
|
|
189
|
+
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
if setup_version_dict["version"] not in self.setup_versions[setup_version_dict["setup_id"]]:
|
|
193
|
+
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
valid_data = SetupVersionData.model_validate(setup_version_dict["data"])
|
|
198
|
+
except ValidationError:
|
|
199
|
+
logger.exception("Validation failed for model SetupVersionData")
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
self.setup_versions[setup_version_dict["setup_id"]][setup_version_dict["version"]] = valid_data
|
|
203
|
+
return True
|
|
204
|
+
|
|
205
|
+
def delete_setup_version(self, setup_version_dict: dict[str, Any]) -> bool:
|
|
206
|
+
"""Delete a setup version by its unique identifier.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
setup_version_dict: Dictionary with the setup version 'name'.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
bool: Success status of version deletion.
|
|
213
|
+
"""
|
|
214
|
+
if setup_version_dict["setup_id"] not in self.setup_versions:
|
|
215
|
+
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
del self.setup_versions[setup_version_dict["setup_id"]][setup_version_dict["version"]]
|
|
219
|
+
return True
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""Digital Kin Setup Service gRPC Client."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import grpc
|
|
8
|
+
from agentic_mesh_protocol.setup.v1 import (
|
|
9
|
+
setup_pb2,
|
|
10
|
+
setup_service_pb2_grpc,
|
|
11
|
+
)
|
|
12
|
+
from google.protobuf import json_format
|
|
13
|
+
from google.protobuf.struct_pb2 import Struct
|
|
14
|
+
from pydantic import ValidationError
|
|
15
|
+
|
|
16
|
+
from digitalkin.grpc_servers.utils.exceptions import ServerError
|
|
17
|
+
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
18
|
+
from digitalkin.logger import logger
|
|
19
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
20
|
+
from digitalkin.services.setup.setup_strategy import SetupData, SetupServiceError, SetupStrategy, SetupVersionData
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
24
|
+
"""This class implements the gRPC setup service."""
|
|
25
|
+
|
|
26
|
+
def __post_init__(self, config: ClientConfig) -> None:
|
|
27
|
+
"""Init the channel from a config file.
|
|
28
|
+
|
|
29
|
+
Need to be call if the user register a gRPC channel.
|
|
30
|
+
"""
|
|
31
|
+
channel = self._init_channel(config)
|
|
32
|
+
self.stub = setup_service_pb2_grpc.SetupServiceStub(channel)
|
|
33
|
+
logger.debug("Channel client 'setup' initialized successfully")
|
|
34
|
+
|
|
35
|
+
@contextmanager
|
|
36
|
+
def handle_grpc_errors(self, operation: str) -> Generator[Any, Any, Any]: # noqa: PLR6301
|
|
37
|
+
"""Context manager for consistent gRPC error handling.
|
|
38
|
+
|
|
39
|
+
Yields:
|
|
40
|
+
Allow error handling in context.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
operation: Description of the operation being performed.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
ValueError: Error wiht the model validation.
|
|
47
|
+
ServerError: from gRPC Client.
|
|
48
|
+
SetupServiceError: setup service internal.
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
yield
|
|
52
|
+
except ValidationError as e:
|
|
53
|
+
msg = f"Invalid data for {operation}"
|
|
54
|
+
logger.exception(msg)
|
|
55
|
+
raise ValueError(msg) from e
|
|
56
|
+
except grpc.RpcError as e:
|
|
57
|
+
msg = f"gRPC {operation} failed: {e}"
|
|
58
|
+
logger.exception(msg)
|
|
59
|
+
raise ServerError(msg) from e
|
|
60
|
+
except Exception as e:
|
|
61
|
+
msg = f"Unexpected error in {operation}"
|
|
62
|
+
logger.exception(msg)
|
|
63
|
+
raise SetupServiceError(msg) from e
|
|
64
|
+
|
|
65
|
+
def create_setup(self, setup_dict: dict[str, Any]) -> str:
|
|
66
|
+
"""Create a new setup with comprehensive validation.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
setup_dict: Dictionary containing setup details.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
bool: Success status of setup creation.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValidationError: If setup data is invalid.
|
|
76
|
+
ServerError: If gRPC operation fails.
|
|
77
|
+
SetupServiceError: For any unexpected internal error.
|
|
78
|
+
"""
|
|
79
|
+
with self.handle_grpc_errors("Setup Creation"):
|
|
80
|
+
valid_data = SetupData.model_validate(setup_dict)
|
|
81
|
+
|
|
82
|
+
request = setup_pb2.CreateSetupRequest(
|
|
83
|
+
name=valid_data.name,
|
|
84
|
+
organisation_id=valid_data.organisation_id,
|
|
85
|
+
owner_id=valid_data.owner_id,
|
|
86
|
+
module_id=valid_data.module_id,
|
|
87
|
+
current_setup_version=setup_pb2.SetupVersion(**valid_data.current_setup_version.model_dump()),
|
|
88
|
+
)
|
|
89
|
+
response = self.exec_grpc_query("CreateSetup", request)
|
|
90
|
+
logger.debug("Setup '%s' query sent successfully", valid_data.name)
|
|
91
|
+
return response
|
|
92
|
+
|
|
93
|
+
def get_setup(self, setup_dict: dict[str, Any]) -> SetupData:
|
|
94
|
+
"""Retrieve a setup by its unique identifier.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
setup_dict: Dictionary with 'name' and optional 'version'.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
dict[str, Any]: Setup details including optional setup version.
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
ValidationError: If the setup name is missing.
|
|
104
|
+
ServerError: If gRPC operation fails.
|
|
105
|
+
SetupServiceError: For any unexpected internal error.
|
|
106
|
+
"""
|
|
107
|
+
with self.handle_grpc_errors("Get Setup"):
|
|
108
|
+
if "setup_id" not in setup_dict:
|
|
109
|
+
msg = "Setup name is required"
|
|
110
|
+
raise ValidationError(msg)
|
|
111
|
+
request = setup_pb2.GetSetupRequest(
|
|
112
|
+
setup_id=setup_dict["setup_id"],
|
|
113
|
+
version=setup_dict.get("version", ""),
|
|
114
|
+
)
|
|
115
|
+
response = self.exec_grpc_query("GetSetup", request)
|
|
116
|
+
response_data = json_format.MessageToDict(response, preserving_proto_field_name=True)
|
|
117
|
+
return SetupData(**response_data["setup"])
|
|
118
|
+
|
|
119
|
+
def update_setup(self, setup_dict: dict[str, Any]) -> bool:
|
|
120
|
+
"""Update an existing setup.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
setup_dict: Dictionary with setup update details.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
bool: Success status of the update operation.
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
ValidationError: If setup data is invalid.
|
|
130
|
+
ServerError: If gRPC operation fails.
|
|
131
|
+
SetupServiceError: For any unexpected internal error.
|
|
132
|
+
"""
|
|
133
|
+
current_setup_version = None
|
|
134
|
+
|
|
135
|
+
with self.handle_grpc_errors("Setup Update"):
|
|
136
|
+
valid_data = SetupData.model_validate(setup_dict)
|
|
137
|
+
|
|
138
|
+
if valid_data.current_setup_version is not None:
|
|
139
|
+
current_setup_version = setup_pb2.SetupVersion(**valid_data.current_setup_version.model_dump())
|
|
140
|
+
|
|
141
|
+
request = setup_pb2.UpdateSetupRequest(
|
|
142
|
+
setup_id=valid_data.id,
|
|
143
|
+
name=valid_data.name,
|
|
144
|
+
owner_id=valid_data.owner_id or "",
|
|
145
|
+
current_setup_version=current_setup_version,
|
|
146
|
+
)
|
|
147
|
+
response = self.exec_grpc_query("UpdateSetup", request)
|
|
148
|
+
logger.debug("Setup '%s' query sent successfully", valid_data.name)
|
|
149
|
+
return getattr(response, "success", False)
|
|
150
|
+
|
|
151
|
+
def delete_setup(self, setup_dict: dict[str, Any]) -> bool:
|
|
152
|
+
"""Delete a setup by its unique identifier.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
setup_dict: Dictionary with the setup 'setup_id'.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
bool: Success status of deletion.
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
ValidationError: If the setup setup_id is missing.
|
|
162
|
+
ServerError: If gRPC operation fails.
|
|
163
|
+
SetupServiceError: For any unexpected internal error.
|
|
164
|
+
"""
|
|
165
|
+
with self.handle_grpc_errors("Setup Deletion"):
|
|
166
|
+
setup_id = setup_dict.get("setup_id")
|
|
167
|
+
if not setup_id:
|
|
168
|
+
msg = "Setup name is required for deletion"
|
|
169
|
+
raise ValidationError(msg)
|
|
170
|
+
request = setup_pb2.DeleteSetupRequest(setup_id=setup_id)
|
|
171
|
+
response = self.exec_grpc_query("DeleteSetup", request)
|
|
172
|
+
logger.debug("Setup '%s' query sent successfully", setup_id)
|
|
173
|
+
return getattr(response, "success", False)
|
|
174
|
+
|
|
175
|
+
def create_setup_version(self, setup_version_dict: dict[str, Any]) -> str:
|
|
176
|
+
"""Create a new setup version.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
setup_version_dict: Dictionary with setup version details.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
str: version of setup version creation.
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
ValidationError: If setup version data is invalid.
|
|
186
|
+
ServerError: If gRPC operation fails.
|
|
187
|
+
SetupServiceError: For any unexpected internal error.
|
|
188
|
+
"""
|
|
189
|
+
with self.handle_grpc_errors("Setup Version Creation"):
|
|
190
|
+
valid_data = SetupVersionData.model_validate(setup_version_dict)
|
|
191
|
+
content_struct = Struct()
|
|
192
|
+
content_struct.update(valid_data.content)
|
|
193
|
+
request = setup_pb2.CreateSetupVersionRequest(
|
|
194
|
+
setup_id=valid_data.setup_id,
|
|
195
|
+
version=valid_data.version,
|
|
196
|
+
content=content_struct,
|
|
197
|
+
)
|
|
198
|
+
logger.debug(
|
|
199
|
+
"Setup Version '%s' for setup '%s' query sent successfully",
|
|
200
|
+
valid_data.version,
|
|
201
|
+
valid_data.setup_id,
|
|
202
|
+
)
|
|
203
|
+
return self.exec_grpc_query("CreateSetupVersion", request)
|
|
204
|
+
|
|
205
|
+
def get_setup_version(self, setup_version_dict: dict[str, Any]) -> SetupVersionData:
|
|
206
|
+
"""Retrieve a setup version by its unique identifier.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
setup_version_dict: Dictionary with the setup version 'setup_version_id'.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
dict[str, Any]: Setup version details.
|
|
213
|
+
|
|
214
|
+
Raises:
|
|
215
|
+
ValidationError: If the setup version id is missing.
|
|
216
|
+
ServerError: If gRPC operation fails.
|
|
217
|
+
SetupServiceError: For any unexpected internal error.
|
|
218
|
+
"""
|
|
219
|
+
with self.handle_grpc_errors("Get Setup Version"):
|
|
220
|
+
setup_version_id = setup_version_dict.get("setup_version_id")
|
|
221
|
+
if not setup_version_id:
|
|
222
|
+
msg = "Setup version id is required"
|
|
223
|
+
raise ValidationError(msg)
|
|
224
|
+
request = setup_pb2.GetSetupVersionRequest(setup_version_id=setup_version_id)
|
|
225
|
+
response = self.exec_grpc_query("GetSetupVersion", request)
|
|
226
|
+
return SetupVersionData(
|
|
227
|
+
**json_format.MessageToDict(response.setup_version, preserving_proto_field_name=True)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def search_setup_versions(self, setup_version_dict: dict[str, Any]) -> list[SetupVersionData]:
|
|
231
|
+
"""Search for setup versions based on filters.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
setup_version_dict: Dictionary with optional 'name' and 'version' filters.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
list[dict[str, Any]]: A list of matching setup version details.
|
|
238
|
+
|
|
239
|
+
Raises:
|
|
240
|
+
ServerError: If gRPC operation fails.
|
|
241
|
+
SetupServiceError: For any unexpected internal error.
|
|
242
|
+
ValidationError: If both name and version are not provided.
|
|
243
|
+
"""
|
|
244
|
+
with self.handle_grpc_errors("Search Setup Versions"):
|
|
245
|
+
if "name" not in setup_version_dict and "version" not in setup_version_dict:
|
|
246
|
+
msg = "Either name or version must be provided"
|
|
247
|
+
raise ValidationError(msg)
|
|
248
|
+
request = setup_pb2.SearchSetupVersionsRequest(
|
|
249
|
+
setup_id=setup_version_dict.get("setup_id", ""),
|
|
250
|
+
version=setup_version_dict.get("version", ""),
|
|
251
|
+
)
|
|
252
|
+
response = self.exec_grpc_query("SearchSetupVersions", request)
|
|
253
|
+
return [
|
|
254
|
+
SetupVersionData(**json_format.MessageToDict(sv, preserving_proto_field_name=True))
|
|
255
|
+
for sv in response.setup_versions
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
def update_setup_version(self, setup_version_dict: dict[str, Any]) -> bool:
|
|
259
|
+
"""Update an existing setup version.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
setup_version_dict: Dictionary with setup version update details.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
bool: Success status of the update operation.
|
|
266
|
+
|
|
267
|
+
Raises:
|
|
268
|
+
ValidationError: If setup version data is invalid.
|
|
269
|
+
ServerError: If gRPC operation fails.
|
|
270
|
+
SetupServiceError: For any unexpected internal error.
|
|
271
|
+
"""
|
|
272
|
+
with self.handle_grpc_errors("Setup Version Update"):
|
|
273
|
+
valid_data = SetupVersionData.model_validate(setup_version_dict)
|
|
274
|
+
content_struct = Struct()
|
|
275
|
+
content_struct.update(valid_data.content)
|
|
276
|
+
request = setup_pb2.UpdateSetupVersionRequest(
|
|
277
|
+
setup_version_id=valid_data.id,
|
|
278
|
+
version=valid_data.version,
|
|
279
|
+
content=content_struct,
|
|
280
|
+
)
|
|
281
|
+
response = self.exec_grpc_query("UpdateSetupVersion", request)
|
|
282
|
+
logger.debug(
|
|
283
|
+
"Setup Version '%s' for setup '%s' query sent successfully",
|
|
284
|
+
valid_data.id,
|
|
285
|
+
valid_data.setup_id,
|
|
286
|
+
)
|
|
287
|
+
return getattr(response, "success", False)
|
|
288
|
+
|
|
289
|
+
def delete_setup_version(self, setup_version_dict: dict[str, Any]) -> bool:
|
|
290
|
+
"""Delete a setup version by its unique identifier.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
setup_version_dict: Dictionary with the setup version 'name'.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
bool: Success status of version deletion.
|
|
297
|
+
|
|
298
|
+
Raises:
|
|
299
|
+
ValidationError: If the setup version name is missing.
|
|
300
|
+
ServerError: If gRPC operation fails.
|
|
301
|
+
SetupServiceError: For any unexpected internal error.
|
|
302
|
+
"""
|
|
303
|
+
with self.handle_grpc_errors("Setup Version Deletion"):
|
|
304
|
+
setup_version_id = setup_version_dict.get("setup_version_id")
|
|
305
|
+
if not setup_version_id:
|
|
306
|
+
msg = "Setup version id is required for deletion"
|
|
307
|
+
raise ValidationError(msg)
|
|
308
|
+
request = setup_pb2.DeleteSetupVersionRequest(setup_version_id=setup_version_id)
|
|
309
|
+
response = self.exec_grpc_query("DeleteSetupVersion", request)
|
|
310
|
+
logger.debug("Setup Version '%s' query sent successfully", setup_version_id)
|
|
311
|
+
return getattr(response, "success", False)
|
|
312
|
+
|
|
313
|
+
def list_setups(self, list_dict: dict[str, Any]) -> dict[str, Any]:
|
|
314
|
+
"""List setups with optional filtering and pagination.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
list_dict: Dictionary with optional filters:
|
|
318
|
+
- organisation_id: Filter by organisation
|
|
319
|
+
- owner_id: Filter by owner
|
|
320
|
+
- limit: Maximum number of results
|
|
321
|
+
- offset: Number of results to skip
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
dict[str, Any]: Dictionary with 'setups' list and 'total_count'.
|
|
325
|
+
|
|
326
|
+
Raises:
|
|
327
|
+
ServerError: If gRPC operation fails.
|
|
328
|
+
SetupServiceError: For any unexpected internal error.
|
|
329
|
+
"""
|
|
330
|
+
with self.handle_grpc_errors("List Setups"):
|
|
331
|
+
request = setup_pb2.ListSetupsRequest(
|
|
332
|
+
organisation_id=list_dict.get("organisation_id", ""),
|
|
333
|
+
owner_id=list_dict.get("owner_id", ""),
|
|
334
|
+
limit=list_dict.get("limit", 0),
|
|
335
|
+
offset=list_dict.get("offset", 0),
|
|
336
|
+
)
|
|
337
|
+
response = self.exec_grpc_query("ListSetups", request)
|
|
338
|
+
return {
|
|
339
|
+
"setups": [
|
|
340
|
+
json_format.MessageToDict(setup, preserving_proto_field_name=True) for setup in response.setups
|
|
341
|
+
],
|
|
342
|
+
"total_count": response.total_count,
|
|
343
|
+
}
|