digitalkin 0.1.1__py3-none-any.whl → 0.2.1__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 +124 -0
- base_server/server_async_secure.py +142 -0
- base_server/server_sync_insecure.py +102 -0
- base_server/server_sync_secure.py +121 -0
- digitalkin/__init__.py +1 -11
- digitalkin/__version__.py +1 -4
- digitalkin/{grpc → grpc_servers}/__init__.py +1 -13
- digitalkin/{grpc → grpc_servers}/_base_server.py +3 -3
- digitalkin/{grpc → grpc_servers}/module_server.py +31 -13
- digitalkin/{grpc → grpc_servers}/module_servicer.py +30 -14
- digitalkin/{grpc → grpc_servers}/registry_server.py +13 -8
- digitalkin/{grpc → grpc_servers}/registry_servicer.py +8 -2
- digitalkin/{grpc → grpc_servers}/utils/factory.py +6 -4
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +68 -0
- digitalkin/{grpc → grpc_servers}/utils/models.py +1 -1
- digitalkin/models/__init__.py +1 -4
- digitalkin/models/module/__init__.py +8 -2
- digitalkin/models/module/module_types.py +10 -0
- digitalkin/models/services/__init__.py +0 -5
- digitalkin/modules/__init__.py +3 -3
- digitalkin/modules/_base_module.py +64 -27
- digitalkin/modules/archetype_module.py +2 -6
- digitalkin/modules/job_manager.py +46 -28
- digitalkin/modules/tool_module.py +3 -7
- digitalkin/modules/trigger_module.py +2 -7
- digitalkin/services/__init__.py +7 -9
- digitalkin/services/agent/__init__.py +2 -2
- digitalkin/services/agent/agent_strategy.py +3 -6
- digitalkin/services/agent/default_agent.py +1 -4
- digitalkin/services/base_strategy.py +18 -0
- digitalkin/services/cost/__init__.py +4 -3
- digitalkin/services/cost/cost_strategy.py +35 -5
- digitalkin/services/cost/default_cost.py +22 -5
- digitalkin/services/cost/grpc_cost.py +81 -0
- digitalkin/services/filesystem/__init__.py +4 -3
- digitalkin/services/filesystem/default_filesystem.py +197 -17
- digitalkin/services/filesystem/filesystem_strategy.py +54 -15
- digitalkin/services/filesystem/grpc_filesystem.py +209 -0
- digitalkin/services/identity/__init__.py +2 -2
- digitalkin/services/identity/default_identity.py +1 -1
- digitalkin/services/identity/identity_strategy.py +3 -1
- digitalkin/services/registry/__init__.py +2 -2
- digitalkin/services/registry/default_registry.py +1 -4
- digitalkin/services/registry/registry_strategy.py +3 -6
- digitalkin/services/services_config.py +176 -0
- digitalkin/services/services_models.py +61 -0
- digitalkin/services/setup/default_setup.py +222 -0
- digitalkin/services/setup/grpc_setup.py +307 -0
- digitalkin/services/setup/setup_strategy.py +145 -0
- digitalkin/services/snapshot/__init__.py +2 -2
- digitalkin/services/snapshot/default_snapshot.py +1 -1
- digitalkin/services/snapshot/snapshot_strategy.py +3 -4
- digitalkin/services/storage/__init__.py +4 -3
- digitalkin/services/storage/default_storage.py +184 -57
- digitalkin/services/storage/grpc_storage.py +76 -170
- digitalkin/services/storage/storage_strategy.py +195 -24
- digitalkin/utils/arg_parser.py +16 -17
- {digitalkin-0.1.1.dist-info → digitalkin-0.2.1.dist-info}/METADATA +8 -7
- digitalkin-0.2.1.dist-info/RECORD +78 -0
- {digitalkin-0.1.1.dist-info → digitalkin-0.2.1.dist-info}/WHEEL +1 -1
- digitalkin-0.2.1.dist-info/top_level.txt +3 -0
- modules/__init__.py +0 -0
- modules/minimal_llm_module.py +162 -0
- modules/storage_module.py +187 -0
- modules/text_transform_module.py +201 -0
- digitalkin/services/default_service.py +0 -13
- digitalkin/services/development_service.py +0 -10
- digitalkin/services/service_provider.py +0 -27
- digitalkin-0.1.1.dist-info/RECORD +0 -59
- digitalkin-0.1.1.dist-info/top_level.txt +0 -1
- /digitalkin/{grpc → grpc_servers}/utils/exceptions.py +0 -0
- /digitalkin/{grpc → grpc_servers}/utils/types.py +0 -0
- {digitalkin-0.1.1.dist-info → digitalkin-0.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from digitalkin.services.base_strategy import BaseStrategy
|
|
6
7
|
|
|
7
|
-
class SnapshotStrategy(ABC):
|
|
8
|
-
"""Abstract base class for snapshot strategies."""
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
class SnapshotStrategy(BaseStrategy, ABC):
|
|
10
|
+
"""Abstract base class for snapshot strategies."""
|
|
12
11
|
|
|
13
12
|
@abstractmethod
|
|
14
13
|
def create(self, data: dict[str, Any]) -> str:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""This module is responsible for handling the storage service."""
|
|
2
2
|
|
|
3
|
-
from .default_storage import DefaultStorage
|
|
4
|
-
from .
|
|
3
|
+
from digitalkin.services.storage.default_storage import DefaultStorage
|
|
4
|
+
from digitalkin.services.storage.grpc_storage import GrpcStorage
|
|
5
|
+
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
5
6
|
|
|
6
|
-
__all__ = ["DefaultStorage", "StorageStrategy"]
|
|
7
|
+
__all__ = ["DefaultStorage", "GrpcStorage", "StorageStrategy"]
|
|
@@ -1,91 +1,218 @@
|
|
|
1
1
|
"""This module implements the default storage strategy."""
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
3
5
|
import logging
|
|
4
|
-
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
5
8
|
|
|
6
|
-
from
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
from digitalkin.services.storage.storage_strategy import DataType, StorageRecord, StorageStrategy
|
|
7
12
|
|
|
8
13
|
logger = logging.getLogger(__name__)
|
|
9
14
|
|
|
10
15
|
|
|
11
16
|
class DefaultStorage(StorageStrategy):
|
|
12
|
-
"""This class implements the default storage strategy.
|
|
13
|
-
|
|
14
|
-
storage: dict[str, list[dict[str, Any]]]
|
|
17
|
+
"""This class implements the default storage strategy with file persistence.
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
self.storage = {"setups": []}
|
|
19
|
+
The storage persists data in a local JSON file, enabling data retention
|
|
20
|
+
across multiple requests or module instances.
|
|
21
|
+
"""
|
|
20
22
|
|
|
21
|
-
def
|
|
22
|
-
"""
|
|
23
|
+
def _load_from_file(self) -> dict[str, StorageRecord]:
|
|
24
|
+
"""Load storage data from the file.
|
|
23
25
|
|
|
24
26
|
Returns:
|
|
25
|
-
|
|
27
|
+
A dictionary containing the loaded storage records
|
|
26
28
|
"""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
file_path = Path(self.storage_file_path)
|
|
30
|
+
if not file_path.exists():
|
|
31
|
+
return {}
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
records = {}
|
|
35
|
+
file_content = json.loads(file_path.read_text(encoding="utf-8"))
|
|
36
|
+
|
|
37
|
+
for key, record_dict in file_content.items():
|
|
38
|
+
# Get the stored record data
|
|
39
|
+
name = record_dict.get("name", "")
|
|
40
|
+
model_class = self.config.get(name)
|
|
41
|
+
|
|
42
|
+
if not model_class:
|
|
43
|
+
logger.warning("No model found for record %s", name)
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
# Create a model instance from the stored data
|
|
47
|
+
data_dict = record_dict.get("data", {})
|
|
48
|
+
try:
|
|
49
|
+
data_model = model_class.model_validate(data_dict)
|
|
50
|
+
except Exception:
|
|
51
|
+
logger.exception("Failed to validate data for record %s", name)
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
# Create a StorageRecord object
|
|
55
|
+
record = StorageRecord(
|
|
56
|
+
mission_id=record_dict.get("mission_id", ""),
|
|
57
|
+
name=name,
|
|
58
|
+
data=data_model,
|
|
59
|
+
data_type=DataType[record_dict.get("data_type", "OUTPUT")],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Set dates if they exist
|
|
63
|
+
if "creation_date" in record_dict:
|
|
64
|
+
record.creation_date = datetime.datetime.fromisoformat(record_dict["creation_date"])
|
|
65
|
+
if "update_date" in record_dict:
|
|
66
|
+
record.update_date = datetime.datetime.fromisoformat(record_dict["update_date"])
|
|
67
|
+
|
|
68
|
+
records[key] = record
|
|
69
|
+
except json.JSONDecodeError:
|
|
70
|
+
logger.exception("Error decoding JSON from file")
|
|
71
|
+
return {}
|
|
72
|
+
except FileNotFoundError:
|
|
73
|
+
logger.info("Storage file not found, starting with empty storage")
|
|
74
|
+
return {}
|
|
75
|
+
except Exception:
|
|
76
|
+
logger.exception("Unexpected error loading storage")
|
|
77
|
+
return {}
|
|
78
|
+
return records
|
|
79
|
+
|
|
80
|
+
def _save_to_file(self) -> None:
|
|
81
|
+
"""Save storage data to the file using a safe write pattern."""
|
|
82
|
+
# Usage of pathlib for file operations
|
|
83
|
+
file_path = Path(self.storage_file_path)
|
|
84
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
# Convert storage to a serializable format
|
|
88
|
+
serializable_data = {}
|
|
89
|
+
for key, record in self.storage.items():
|
|
90
|
+
record_dict = {
|
|
91
|
+
"mission_id": record.mission_id,
|
|
92
|
+
"name": record.name,
|
|
93
|
+
"data_type": record.data_type.name, # Convert enum to string
|
|
94
|
+
"data": record.data.model_dump(), # Convert Pydantic model to dict
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Handle dates (convert to ISO format strings)
|
|
98
|
+
if record.creation_date:
|
|
99
|
+
record_dict["creation_date"] = record.creation_date.isoformat()
|
|
100
|
+
if record.update_date:
|
|
101
|
+
record_dict["update_date"] = record.update_date.isoformat()
|
|
102
|
+
|
|
103
|
+
serializable_data[key] = record_dict
|
|
104
|
+
|
|
105
|
+
# usage of NamedTemporaryFile for atomic writes
|
|
106
|
+
with tempfile.NamedTemporaryFile(
|
|
107
|
+
mode="w", encoding="utf-8", dir=str(file_path.parent), delete=False, suffix=".tmp"
|
|
108
|
+
) as temp_file:
|
|
109
|
+
json.dump(serializable_data, temp_file, indent=2)
|
|
110
|
+
temp_path = temp_file.name
|
|
111
|
+
|
|
112
|
+
# Creation of a backup if the file already exists
|
|
113
|
+
if file_path.exists():
|
|
114
|
+
backup_path = f"{self.storage_file_path}.bak"
|
|
115
|
+
file_path.replace(backup_path)
|
|
116
|
+
|
|
117
|
+
# Remplacement du fichier (opération atomique) avec pathlib
|
|
118
|
+
Path(temp_path).replace(str(file_path))
|
|
119
|
+
|
|
120
|
+
except PermissionError:
|
|
121
|
+
logger.exception("Permission denied when saving to file")
|
|
122
|
+
except OSError:
|
|
123
|
+
logger.exception("OS error when saving to file")
|
|
124
|
+
except Exception:
|
|
125
|
+
logger.exception("Unexpected error saving storage")
|
|
126
|
+
|
|
127
|
+
def _store(self, record: StorageRecord) -> StorageRecord:
|
|
128
|
+
"""Store a new record in the database and persist to file.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
record: The record to store
|
|
31
132
|
|
|
32
133
|
Returns:
|
|
33
|
-
|
|
134
|
+
str: The ID of the new record
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
ValueError: If the record already exists
|
|
34
138
|
"""
|
|
35
|
-
|
|
139
|
+
name = record.name
|
|
140
|
+
if name in self.storage:
|
|
141
|
+
msg = f"Record with name {name} already exists"
|
|
142
|
+
raise ValueError(msg)
|
|
143
|
+
self.storage[name] = record
|
|
144
|
+
self.storage[name].creation_date = datetime.datetime.now(datetime.timezone.utc)
|
|
145
|
+
self.storage[name].update_date = datetime.datetime.now(datetime.timezone.utc)
|
|
36
146
|
|
|
37
|
-
|
|
38
|
-
|
|
147
|
+
# Persist to file
|
|
148
|
+
self._save_to_file()
|
|
39
149
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"""
|
|
43
|
-
if table not in self.storage:
|
|
44
|
-
self.storage[table] = []
|
|
45
|
-
self.storage[table].append(data["data"])
|
|
46
|
-
logger.info("CREATE %s:%s succesfull", table, data["data"])
|
|
47
|
-
return f"{len(self.storage[table]) - 1}"
|
|
150
|
+
logger.info("CREATE %s:%s successful", name, record)
|
|
151
|
+
return self.storage[name]
|
|
48
152
|
|
|
49
|
-
def
|
|
153
|
+
def _read(self, name: str) -> StorageRecord | None:
|
|
50
154
|
"""Get records from the database.
|
|
51
155
|
|
|
156
|
+
Args:
|
|
157
|
+
name: The unique name to retrieve data for
|
|
158
|
+
|
|
52
159
|
Returns:
|
|
53
|
-
|
|
160
|
+
StorageRecord: The corresponding record
|
|
54
161
|
"""
|
|
55
|
-
logger.info("GET
|
|
56
|
-
if
|
|
57
|
-
logger.info("GET
|
|
58
|
-
return
|
|
59
|
-
return
|
|
162
|
+
logger.info("GET record linked to the key = %s", name)
|
|
163
|
+
if name not in self.storage:
|
|
164
|
+
logger.info("GET key = %s: DOESN'T EXIST", name)
|
|
165
|
+
return None
|
|
166
|
+
return self.storage[name]
|
|
167
|
+
|
|
168
|
+
def _modify(self, name: str, data: BaseModel) -> StorageRecord | None:
|
|
169
|
+
"""Update records in the database and persist to file.
|
|
60
170
|
|
|
61
|
-
|
|
62
|
-
|
|
171
|
+
Args:
|
|
172
|
+
name: The unique name to store the data under
|
|
173
|
+
data: The data to modify
|
|
63
174
|
|
|
64
175
|
Returns:
|
|
65
|
-
|
|
176
|
+
StorageRecord: The modified record
|
|
66
177
|
"""
|
|
67
|
-
if
|
|
68
|
-
logger.info("UPDATE
|
|
69
|
-
return
|
|
70
|
-
self.storage[
|
|
71
|
-
|
|
178
|
+
if name not in self.storage:
|
|
179
|
+
logger.info("UPDATE key = %s: DOESN'T EXIST", name)
|
|
180
|
+
return None
|
|
181
|
+
self.storage[name].data = data
|
|
182
|
+
self.storage[name].update_date = datetime.datetime.now(datetime.timezone.utc)
|
|
72
183
|
|
|
73
|
-
|
|
74
|
-
|
|
184
|
+
# Persist to file
|
|
185
|
+
self._save_to_file()
|
|
75
186
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
logger.info("UPDATE table = %s: TABLE DOESN'T EXIST", table)
|
|
81
|
-
return 0
|
|
82
|
-
del self.storage[table][data["delete_id"]]
|
|
83
|
-
return 1
|
|
187
|
+
return self.storage[name]
|
|
188
|
+
|
|
189
|
+
def _remove(self, name: str) -> bool:
|
|
190
|
+
"""Delete records from the database and update file.
|
|
84
191
|
|
|
85
|
-
|
|
86
|
-
|
|
192
|
+
Args:
|
|
193
|
+
name: The unique name to remove a record
|
|
87
194
|
|
|
88
195
|
Returns:
|
|
89
|
-
|
|
196
|
+
bool: True if the record was removed, False otherwise
|
|
90
197
|
"""
|
|
91
|
-
|
|
198
|
+
if name not in self.storage:
|
|
199
|
+
logger.info("DELETE key = %s: DOESN'T EXIST", name)
|
|
200
|
+
return False
|
|
201
|
+
del self.storage[name]
|
|
202
|
+
|
|
203
|
+
# Persist to file
|
|
204
|
+
self._save_to_file()
|
|
205
|
+
|
|
206
|
+
return True
|
|
207
|
+
|
|
208
|
+
def __init__(
|
|
209
|
+
self,
|
|
210
|
+
mission_id: str,
|
|
211
|
+
config: dict[str, type[BaseModel]],
|
|
212
|
+
storage_file_path: str = "local_storage",
|
|
213
|
+
**kwargs, # noqa: ANN003, ARG002
|
|
214
|
+
) -> None:
|
|
215
|
+
"""Initialize the storage."""
|
|
216
|
+
super().__init__(mission_id=mission_id, config=config)
|
|
217
|
+
self.storage_file_path = f"{self.mission_id}_{storage_file_path}.json"
|
|
218
|
+
self.storage = self._load_from_file()
|
|
@@ -1,207 +1,113 @@
|
|
|
1
1
|
"""This module implements the default storage strategy."""
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
4
3
|
import logging
|
|
5
|
-
from enum import Enum, auto
|
|
6
|
-
from typing import Any
|
|
7
4
|
|
|
8
|
-
import grpc
|
|
9
5
|
from digitalkin_proto.digitalkin.storage.v2 import data_pb2, storage_service_pb2_grpc
|
|
10
6
|
from google.protobuf import json_format
|
|
11
7
|
from google.protobuf.struct_pb2 import Struct
|
|
12
8
|
from pydantic import BaseModel
|
|
13
9
|
|
|
14
|
-
from digitalkin.
|
|
15
|
-
from digitalkin.
|
|
16
|
-
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
10
|
+
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
11
|
+
from digitalkin.grpc_servers.utils.models import ServerConfig
|
|
12
|
+
from digitalkin.services.storage.storage_strategy import StorageRecord, StorageServiceError, StorageStrategy
|
|
17
13
|
|
|
18
14
|
logger = logging.getLogger(__name__)
|
|
19
15
|
|
|
20
16
|
|
|
21
|
-
class
|
|
22
|
-
"""."""
|
|
23
|
-
|
|
24
|
-
OUTPUT = auto()
|
|
25
|
-
VIEW = auto()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class StorageData(BaseModel):
|
|
29
|
-
"""."""
|
|
30
|
-
|
|
31
|
-
data: dict[str, Any]
|
|
32
|
-
mission_id: str
|
|
33
|
-
name: str
|
|
34
|
-
timestamp: datetime.datetime
|
|
35
|
-
type: DataType
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class GrpcStorage(StorageStrategy):
|
|
17
|
+
class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
39
18
|
"""This class implements the default storage strategy."""
|
|
40
19
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
with open(config.credentials.server_cert_path, "rb") as cert_file: # noqa: FURB101
|
|
53
|
-
certificate_chain = cert_file.read()
|
|
54
|
-
|
|
55
|
-
root_certificates = None
|
|
56
|
-
if config.credentials.root_cert_path:
|
|
57
|
-
with open(config.credentials.root_cert_path, "rb") as root_cert_file: # noqa: FURB101
|
|
58
|
-
root_certificates = root_cert_file.read()
|
|
59
|
-
|
|
60
|
-
# Create channel credentials
|
|
61
|
-
channel_credentials = grpc.ssl_channel_credentials(root_certificates=root_certificates or certificate_chain)
|
|
62
|
-
|
|
63
|
-
return grpc.secure_channel(f"{config.host}:{config.port}", channel_credentials)
|
|
64
|
-
# Insecure channel
|
|
65
|
-
return grpc.insecure_channel(f"{config.host}:{config.port}")
|
|
66
|
-
|
|
67
|
-
def __post_init__(self, config: ServerConfig) -> None:
|
|
68
|
-
"""Init the channel from a config file.
|
|
69
|
-
|
|
70
|
-
Need to be call if the user register a gRPC channel.
|
|
71
|
-
"""
|
|
72
|
-
channel = self._init_channel(config)
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
mission_id: str,
|
|
23
|
+
config: dict[str, type[BaseModel]],
|
|
24
|
+
server_config: ServerConfig,
|
|
25
|
+
**kwargs, # noqa: ANN003, ARG002
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Initialize the storage."""
|
|
28
|
+
super().__init__(mission_id=mission_id, config=config)
|
|
29
|
+
|
|
30
|
+
channel = self._init_channel(server_config)
|
|
73
31
|
self.stub = storage_service_pb2_grpc.StorageServiceStub(channel)
|
|
74
32
|
logger.info("Channel client 'storage' initialized succesfully")
|
|
75
33
|
|
|
76
|
-
def
|
|
77
|
-
""".
|
|
78
|
-
try:
|
|
79
|
-
# Call the register method
|
|
80
|
-
logger.warning("send request to %s", query_endpoint)
|
|
81
|
-
response = getattr(self.stub, query_endpoint)(request)
|
|
82
|
-
logger.warning("recive response from request to registry: %s", response)
|
|
83
|
-
|
|
84
|
-
if response.success:
|
|
85
|
-
logger.info("Module registered successfully")
|
|
86
|
-
else:
|
|
87
|
-
logger.error("Module registration failed")
|
|
88
|
-
return response
|
|
89
|
-
except grpc.RpcError:
|
|
90
|
-
logger.exception("RPC error during registration:")
|
|
91
|
-
raise ServerError
|
|
92
|
-
|
|
93
|
-
def connect(self) -> bool: # noqa: PLR6301
|
|
94
|
-
"""Establish connection to the database.
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
bool: True if the connection is successful, False otherwise
|
|
98
|
-
"""
|
|
99
|
-
return True
|
|
34
|
+
def _store(self, record: StorageRecord) -> StorageRecord:
|
|
35
|
+
"""Create a new record in the database.
|
|
100
36
|
|
|
101
|
-
|
|
102
|
-
|
|
37
|
+
Parameters:
|
|
38
|
+
record: The record to store
|
|
103
39
|
|
|
104
40
|
Returns:
|
|
105
|
-
|
|
106
|
-
"""
|
|
107
|
-
return True
|
|
108
|
-
|
|
109
|
-
def create(self, table: str, data: dict[str, Any]) -> str:
|
|
110
|
-
"""Create a new record in the database.
|
|
41
|
+
StorageRecord: The corresponding record
|
|
111
42
|
|
|
112
|
-
|
|
113
|
-
|
|
43
|
+
Raises:
|
|
44
|
+
StorageServiceError: If there is an error while storing the record
|
|
114
45
|
"""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
data_struct.update(data
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
)
|
|
126
|
-
return self.exec_grpc_query("StoreData", request)
|
|
127
|
-
|
|
128
|
-
def get_data_by_mission(self, table: str, data: dict[str, Any]):
|
|
129
|
-
request = data_pb2.GetDataByMissionRequest(mission_id=data["mission_id"])
|
|
130
|
-
response = self.exec_grpc_query("GetDataByMission", request)
|
|
131
|
-
return [
|
|
132
|
-
StorageData(
|
|
133
|
-
data=data.data,
|
|
134
|
-
mission_id=data.mission_id,
|
|
135
|
-
name=data.name,
|
|
136
|
-
timestamp=data.timestamp,
|
|
137
|
-
type=data.type,
|
|
138
|
-
)
|
|
139
|
-
for data in response.data_items
|
|
140
|
-
]
|
|
141
|
-
|
|
142
|
-
def get_data_by_name(self, table: str, data: dict[str, Any]):
|
|
143
|
-
request = data_pb2.GetDataByNameRequest(mission_id=data["mission_id"], name=data_pb2.DataType())
|
|
144
|
-
response = self.exec_grpc_query("GetDataByName", request)
|
|
145
|
-
return [
|
|
146
|
-
StorageData(
|
|
147
|
-
data=data.data,
|
|
148
|
-
mission_id=data.mission_id,
|
|
149
|
-
name=data.name,
|
|
150
|
-
timestamp=data.timestamp,
|
|
151
|
-
type=data.type,
|
|
152
|
-
)
|
|
153
|
-
for data in response.stored_data
|
|
154
|
-
]
|
|
155
|
-
|
|
156
|
-
def get_data_by_type(self, table: str, data: dict[str, Any]):
|
|
157
|
-
request = data_pb2.GetDataByTypeRequest(
|
|
158
|
-
mission_id=data["mission_id"],
|
|
159
|
-
type=data_pb2.DataType.Name(data["type"]),
|
|
160
|
-
)
|
|
161
|
-
response = self.exec_grpc_query("GetDataByType", request)
|
|
162
|
-
return [
|
|
163
|
-
StorageData(
|
|
164
|
-
data=json_format.MessageToDict(data.data),
|
|
165
|
-
mission_id=data.mission_id,
|
|
166
|
-
name=data.name,
|
|
167
|
-
timestamp=data.timestamp,
|
|
168
|
-
type=data.type,
|
|
46
|
+
try:
|
|
47
|
+
# Create a Struct for the data
|
|
48
|
+
data_struct = Struct()
|
|
49
|
+
data_struct.update(record.data.model_dump())
|
|
50
|
+
|
|
51
|
+
request = data_pb2.StoreRecordRequest(
|
|
52
|
+
data=data_struct,
|
|
53
|
+
mission_id=record.mission_id,
|
|
54
|
+
name=record.name,
|
|
55
|
+
data_type=record.data_type.name,
|
|
169
56
|
)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
57
|
+
return self.exec_grpc_query("StoreRecord", request)
|
|
58
|
+
except Exception:
|
|
59
|
+
msg = f"Error while storing record {record.name}"
|
|
60
|
+
logger.exception(msg)
|
|
61
|
+
raise StorageServiceError(msg)
|
|
175
62
|
|
|
176
|
-
|
|
177
|
-
int: The number of records deleted
|
|
178
|
-
"""
|
|
179
|
-
request = data_pb2.DeleteDataRequest(
|
|
180
|
-
mission_id=data["mission_id"],
|
|
181
|
-
name=data_pb2.DataType.Name(data["name"]),
|
|
182
|
-
)
|
|
183
|
-
return self.exec_grpc_query("DeleteData", request)
|
|
184
|
-
|
|
185
|
-
def get(self, table: str, data: dict[str, Any]) -> list[dict[str, Any]]:
|
|
63
|
+
def _read(self, name: str) -> StorageRecord | None:
|
|
186
64
|
"""Get records from the database.
|
|
187
65
|
|
|
188
66
|
Returns:
|
|
189
|
-
list[
|
|
67
|
+
list[StorageData]: The list of records
|
|
190
68
|
"""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
69
|
+
try:
|
|
70
|
+
request = data_pb2.ReadRecordRequest(mission_id=self.mission_id, name=name)
|
|
71
|
+
response: data_pb2.ReadRecordResponse = self.exec_grpc_query("ReadRecord", request)
|
|
72
|
+
return StorageRecord(**json_format.MessageToDict(response.stored_data))
|
|
73
|
+
except Exception:
|
|
74
|
+
msg = f"Error while reading record {name}"
|
|
75
|
+
logger.exception(msg)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
def _modify(self, name: str, data: BaseModel) -> StorageRecord | None:
|
|
194
79
|
"""Update records in the database.
|
|
195
80
|
|
|
196
81
|
Returns:
|
|
197
82
|
int: The number of records updated
|
|
198
83
|
"""
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
84
|
+
try:
|
|
85
|
+
# Create a Struct for the data
|
|
86
|
+
data_struct = Struct()
|
|
87
|
+
data_struct.update(data.model_dump())
|
|
88
|
+
|
|
89
|
+
request = data_pb2.ModifyRecordRequest(data=data_struct, mission_id=self.mission_id, name=name)
|
|
90
|
+
response: data_pb2.ModifyRecordResponse = self.exec_grpc_query("ModifyRecord", request)
|
|
91
|
+
return StorageRecord(**json_format.MessageToDict(response.stored_data))
|
|
92
|
+
except Exception:
|
|
93
|
+
msg = f"Error while modifing record {name}"
|
|
94
|
+
logger.exception(msg)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def _remove(self, name: str) -> bool:
|
|
98
|
+
"""Delete records from the database.
|
|
203
99
|
|
|
204
100
|
Returns:
|
|
205
|
-
|
|
101
|
+
int: The number of records deleted
|
|
206
102
|
"""
|
|
207
|
-
|
|
103
|
+
try:
|
|
104
|
+
request = data_pb2.RemoveRecordRequest(
|
|
105
|
+
mission_id=self.mission_id,
|
|
106
|
+
name=name,
|
|
107
|
+
)
|
|
108
|
+
self.exec_grpc_query("RemoveRecord", request)
|
|
109
|
+
except Exception:
|
|
110
|
+
msg = f"Error while removin record {name}"
|
|
111
|
+
logger.exception(msg)
|
|
112
|
+
return False
|
|
113
|
+
return True
|