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.
Files changed (77) hide show
  1. base_server/__init__.py +1 -0
  2. base_server/mock/__init__.py +5 -0
  3. base_server/mock/mock_pb2.py +39 -0
  4. base_server/mock/mock_pb2_grpc.py +102 -0
  5. base_server/server_async_insecure.py +124 -0
  6. base_server/server_async_secure.py +142 -0
  7. base_server/server_sync_insecure.py +102 -0
  8. base_server/server_sync_secure.py +121 -0
  9. digitalkin/__init__.py +1 -11
  10. digitalkin/__version__.py +1 -4
  11. digitalkin/{grpc → grpc_servers}/__init__.py +1 -13
  12. digitalkin/{grpc → grpc_servers}/_base_server.py +3 -3
  13. digitalkin/{grpc → grpc_servers}/module_server.py +31 -13
  14. digitalkin/{grpc → grpc_servers}/module_servicer.py +30 -14
  15. digitalkin/{grpc → grpc_servers}/registry_server.py +13 -8
  16. digitalkin/{grpc → grpc_servers}/registry_servicer.py +8 -2
  17. digitalkin/{grpc → grpc_servers}/utils/factory.py +6 -4
  18. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +68 -0
  19. digitalkin/{grpc → grpc_servers}/utils/models.py +1 -1
  20. digitalkin/models/__init__.py +1 -4
  21. digitalkin/models/module/__init__.py +8 -2
  22. digitalkin/models/module/module_types.py +10 -0
  23. digitalkin/models/services/__init__.py +0 -5
  24. digitalkin/modules/__init__.py +3 -3
  25. digitalkin/modules/_base_module.py +64 -27
  26. digitalkin/modules/archetype_module.py +2 -6
  27. digitalkin/modules/job_manager.py +46 -28
  28. digitalkin/modules/tool_module.py +3 -7
  29. digitalkin/modules/trigger_module.py +2 -7
  30. digitalkin/services/__init__.py +7 -9
  31. digitalkin/services/agent/__init__.py +2 -2
  32. digitalkin/services/agent/agent_strategy.py +3 -6
  33. digitalkin/services/agent/default_agent.py +1 -4
  34. digitalkin/services/base_strategy.py +18 -0
  35. digitalkin/services/cost/__init__.py +4 -3
  36. digitalkin/services/cost/cost_strategy.py +35 -5
  37. digitalkin/services/cost/default_cost.py +22 -5
  38. digitalkin/services/cost/grpc_cost.py +81 -0
  39. digitalkin/services/filesystem/__init__.py +4 -3
  40. digitalkin/services/filesystem/default_filesystem.py +197 -17
  41. digitalkin/services/filesystem/filesystem_strategy.py +54 -15
  42. digitalkin/services/filesystem/grpc_filesystem.py +209 -0
  43. digitalkin/services/identity/__init__.py +2 -2
  44. digitalkin/services/identity/default_identity.py +1 -1
  45. digitalkin/services/identity/identity_strategy.py +3 -1
  46. digitalkin/services/registry/__init__.py +2 -2
  47. digitalkin/services/registry/default_registry.py +1 -4
  48. digitalkin/services/registry/registry_strategy.py +3 -6
  49. digitalkin/services/services_config.py +176 -0
  50. digitalkin/services/services_models.py +61 -0
  51. digitalkin/services/setup/default_setup.py +222 -0
  52. digitalkin/services/setup/grpc_setup.py +307 -0
  53. digitalkin/services/setup/setup_strategy.py +145 -0
  54. digitalkin/services/snapshot/__init__.py +2 -2
  55. digitalkin/services/snapshot/default_snapshot.py +1 -1
  56. digitalkin/services/snapshot/snapshot_strategy.py +3 -4
  57. digitalkin/services/storage/__init__.py +4 -3
  58. digitalkin/services/storage/default_storage.py +184 -57
  59. digitalkin/services/storage/grpc_storage.py +76 -170
  60. digitalkin/services/storage/storage_strategy.py +195 -24
  61. digitalkin/utils/arg_parser.py +16 -17
  62. {digitalkin-0.1.1.dist-info → digitalkin-0.2.1.dist-info}/METADATA +8 -7
  63. digitalkin-0.2.1.dist-info/RECORD +78 -0
  64. {digitalkin-0.1.1.dist-info → digitalkin-0.2.1.dist-info}/WHEEL +1 -1
  65. digitalkin-0.2.1.dist-info/top_level.txt +3 -0
  66. modules/__init__.py +0 -0
  67. modules/minimal_llm_module.py +162 -0
  68. modules/storage_module.py +187 -0
  69. modules/text_transform_module.py +201 -0
  70. digitalkin/services/default_service.py +0 -13
  71. digitalkin/services/development_service.py +0 -10
  72. digitalkin/services/service_provider.py +0 -27
  73. digitalkin-0.1.1.dist-info/RECORD +0 -59
  74. digitalkin-0.1.1.dist-info/top_level.txt +0 -1
  75. /digitalkin/{grpc → grpc_servers}/utils/exceptions.py +0 -0
  76. /digitalkin/{grpc → grpc_servers}/utils/types.py +0 -0
  77. {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
- def __init__(self) -> None:
11
- """Initialize the snapshot strategy."""
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 .storage_strategy import StorageStrategy
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
- from typing import Any
6
+ import tempfile
7
+ from pathlib import Path
5
8
 
6
- from .storage_strategy import StorageStrategy
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
- def __init__(self) -> None:
17
- """Initialize the default storage strategy."""
18
- super().__init__()
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 connect(self) -> bool: # noqa: PLR6301
22
- """Establish connection to the database.
23
+ def _load_from_file(self) -> dict[str, StorageRecord]:
24
+ """Load storage data from the file.
23
25
 
24
26
  Returns:
25
- bool: True if the connection is successful, False otherwise
27
+ A dictionary containing the loaded storage records
26
28
  """
27
- return True
28
-
29
- def disconnect(self) -> bool: # noqa: PLR6301
30
- """Close connection to the database.
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
- bool: True if the connection is closed, False otherwise
134
+ str: The ID of the new record
135
+
136
+ Raises:
137
+ ValueError: If the record already exists
34
138
  """
35
- return True
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
- def create(self, table: str, data: dict[str, Any]) -> str:
38
- """Create a new record in the database.
147
+ # Persist to file
148
+ self._save_to_file()
39
149
 
40
- Returns:
41
- str: The ID of the new record
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 get(self, table: str, data: dict[str, Any]) -> list[dict[str, Any]]:
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
- list[dict[str, Any]]: The list of records
160
+ StorageRecord: The corresponding record
54
161
  """
55
- logger.info("GET table = %s: keys = %s", table, data["keys"])
56
- if table not in self.storage:
57
- logger.info("GET table = %s: TABLE DOESN'T EXIST", table)
58
- return []
59
- return [self.storage[table][int(key)] for key in data["keys"]]
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
- def update(self, table: str, data: dict[str, Any]) -> int:
62
- """Update records in the database.
171
+ Args:
172
+ name: The unique name to store the data under
173
+ data: The data to modify
63
174
 
64
175
  Returns:
65
- int: The number of records updated
176
+ StorageRecord: The modified record
66
177
  """
67
- if table not in self.storage:
68
- logger.info("UPDATE table = %s: TABLE DOESN'T EXIST", table)
69
- return 0
70
- self.storage[table][data["update_id"]] = data["update_value"]
71
- return 1
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
- def delete(self, table: str, data: dict[str, Any]) -> int:
74
- """Delete records from the database.
184
+ # Persist to file
185
+ self._save_to_file()
75
186
 
76
- Returns:
77
- int: The number of records deleted
78
- """
79
- if table not in self.storage:
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
- def get_all(self) -> dict[str, list[dict[str, Any]]]:
86
- """Get all records from the database.
192
+ Args:
193
+ name: The unique name to remove a record
87
194
 
88
195
  Returns:
89
- dict[str, list[dict[str, Any]]]: table with respective list of records
196
+ bool: True if the record was removed, False otherwise
90
197
  """
91
- return self.storage
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.grpc.utils.exceptions import ServerError
15
- from digitalkin.grpc.utils.models import SecurityMode, ServerConfig
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 DataType(Enum):
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 _init_channel(self, config: ServerConfig) -> grpc.Channel:
42
- """Create an appropriate channel to the registry server.
43
-
44
- Returns:
45
- A gRPC channel for communication with the registry.
46
-
47
- Raises:
48
- ValueError: If credentials are required but not provided.
49
- """
50
- if config.security == SecurityMode.SECURE and config.credentials:
51
- # Secure channel
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 exec_grpc_query(self, query_endpoint: str, request: Any) -> Any:
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
- def disconnect(self) -> bool: # noqa: PLR6301
102
- """Close connection to the database.
37
+ Parameters:
38
+ record: The record to store
103
39
 
104
40
  Returns:
105
- bool: True if the connection is closed, False otherwise
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
- Returns:
113
- str: The ID of the new record
43
+ Raises:
44
+ StorageServiceError: If there is an error while storing the record
114
45
  """
115
- # Create a Struct for the data
116
- data_struct = Struct()
117
- if data.get("data"):
118
- data_struct.update(data["data"])
119
-
120
- request = data_pb2.StoreDataRequest(
121
- data=data_struct,
122
- mission_id=data["mission_id"],
123
- name=data["name"],
124
- type=data_pb2.DataType.Name(1),
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
- for data in response.stored_data
171
- ]
172
-
173
- def delete(self, table: str, data: dict[str, Any]) -> int:
174
- """Delete records from the database.
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
- Returns:
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[dict[str, Any]]: The list of records
67
+ list[StorageData]: The list of records
190
68
  """
191
- return []
192
-
193
- def update(self, table: str, data: dict[str, Any]) -> int:
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
- return 1
200
-
201
- def get_all(self) -> dict[str, list[dict[str, Any]]]:
202
- """Get all records from the database.
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
- dict[str, list[dict[str, Any]]]: table with respective list of records
101
+ int: The number of records deleted
206
102
  """
207
- return {}
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