digitalkin 0.2.14__py3-none-any.whl → 0.2.15__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.
- digitalkin/__version__.py +1 -1
- digitalkin/services/filesystem/default_filesystem.py +327 -130
- digitalkin/services/filesystem/filesystem_strategy.py +169 -34
- digitalkin/services/filesystem/grpc_filesystem.py +225 -117
- digitalkin/services/setup/grpc_setup.py +1 -0
- {digitalkin-0.2.14.dist-info → digitalkin-0.2.15.dist-info}/METADATA +1 -1
- {digitalkin-0.2.14.dist-info → digitalkin-0.2.15.dist-info}/RECORD +14 -13
- {digitalkin-0.2.14.dist-info → digitalkin-0.2.15.dist-info}/top_level.txt +1 -0
- modules/cpu_intensive_module.py +0 -1
- modules/text_transform_module.py +0 -1
- services/filesystem_module.py +198 -0
- {modules → services}/storage_module.py +20 -7
- {digitalkin-0.2.14.dist-info → digitalkin-0.2.15.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.14.dist-info → digitalkin-0.2.15.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""This module contains the abstract base class for filesystem strategies."""
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
from pydantic import BaseModel, Field
|
|
7
8
|
|
|
@@ -9,63 +10,197 @@ from digitalkin.services.base_strategy import BaseStrategy
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class FilesystemServiceError(Exception):
|
|
12
|
-
"""Base exception for
|
|
13
|
+
"""Base exception for Filesystem service errors."""
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class
|
|
16
|
-
"""Enum defining the types of data that can be stored."""
|
|
17
|
-
|
|
18
|
-
DOCUMENT = auto()
|
|
19
|
-
IMAGE = auto()
|
|
20
|
-
VIDEO = auto()
|
|
21
|
-
AUDIO = auto()
|
|
22
|
-
ARCHIVE = auto()
|
|
23
|
-
OTHER = auto()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class FilesystemData(BaseModel):
|
|
16
|
+
class FilesystemRecord(BaseModel):
|
|
27
17
|
"""Data model for filesystem operations."""
|
|
28
18
|
|
|
29
|
-
|
|
19
|
+
id: str = Field(description="Unique identifier for the file (UUID)")
|
|
20
|
+
context: str = Field(description="The context of the file in the filesystem")
|
|
30
21
|
name: str = Field(description="The name of the file")
|
|
31
|
-
file_type:
|
|
32
|
-
|
|
22
|
+
file_type: str = Field(default="UNSPECIFIED", description="The type of data stored")
|
|
23
|
+
content_type: str = Field(default="application/octet-stream", description="The MIME type of the file")
|
|
24
|
+
size_bytes: int = Field(default=0, description="Size of the file in bytes")
|
|
25
|
+
checksum: str = Field(default="", description="SHA-256 checksum of the file content")
|
|
26
|
+
metadata: dict[str, Any] | None = Field(default=None, description="Additional metadata for the file")
|
|
27
|
+
storage_url: str = Field(description="Internal URL for accessing the file content")
|
|
28
|
+
status: str = Field(default="UNSPECIFIED", description="Current status of the file")
|
|
29
|
+
content: bytes | None = Field(default=None, description="The content of the file")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FileFilter(BaseModel):
|
|
33
|
+
"""Filter criteria for querying files."""
|
|
34
|
+
|
|
35
|
+
names: list[str] | None = Field(default=None, description="Filter by file names (exact matches)")
|
|
36
|
+
file_ids: list[str] | None = Field(default=None, description="Filter by file IDs")
|
|
37
|
+
file_types: list[str] | None = Field(default=None, description="Filter by file types")
|
|
38
|
+
created_after: datetime | None = Field(default=None, description="Filter files created after this timestamp")
|
|
39
|
+
created_before: datetime | None = Field(default=None, description="Filter files created before this timestamp")
|
|
40
|
+
updated_after: datetime | None = Field(default=None, description="Filter files updated after this timestamp")
|
|
41
|
+
updated_before: datetime | None = Field(default=None, description="Filter files updated before this timestamp")
|
|
42
|
+
status: str | None = Field(default=None, description="Filter by file status")
|
|
43
|
+
content_type_prefix: str | None = Field(default=None, description="Filter by content type prefix (e.g., 'image/')")
|
|
44
|
+
min_size_bytes: int | None = Field(default=None, description="Filter files with minimum size")
|
|
45
|
+
max_size_bytes: int | None = Field(default=None, description="Filter files with maximum size")
|
|
46
|
+
prefix: str | None = Field(default=None, description="Filter by path prefix (e.g., 'folder1/')")
|
|
47
|
+
content_type: str | None = Field(default=None, description="Filter by content type")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UploadFileData(BaseModel):
|
|
51
|
+
"""Data model for uploading a file."""
|
|
52
|
+
|
|
53
|
+
content: bytes = Field(description="The content of the file")
|
|
54
|
+
name: str = Field(description="The name of the file")
|
|
55
|
+
file_type: str = Field(description="The type of the file")
|
|
56
|
+
content_type: str | None = Field(default=None, description="The content type of the file")
|
|
57
|
+
metadata: dict[str, Any] | None = Field(default=None, description="The metadata of the file")
|
|
58
|
+
replace_if_exists: bool = Field(default=False, description="Whether to replace the file if it already exists")
|
|
33
59
|
|
|
34
60
|
|
|
35
61
|
class FilesystemStrategy(BaseStrategy, ABC):
|
|
36
|
-
"""Abstract base class for filesystem strategies.
|
|
62
|
+
"""Abstract base class for filesystem strategies.
|
|
63
|
+
|
|
64
|
+
This strategy provides comprehensive file management capabilities including
|
|
65
|
+
upload, retrieval, update, and deletion operations with rich metadata support,
|
|
66
|
+
filtering, and pagination.
|
|
67
|
+
"""
|
|
37
68
|
|
|
38
|
-
def __init__(self, mission_id: str, setup_version_id: str, config: dict[str,
|
|
69
|
+
def __init__(self, mission_id: str, setup_version_id: str, config: dict[str, Any] | None = None) -> None:
|
|
39
70
|
"""Initialize the strategy.
|
|
40
71
|
|
|
41
72
|
Args:
|
|
42
73
|
mission_id: The ID of the mission this strategy is associated with
|
|
43
74
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
44
|
-
config:
|
|
75
|
+
config: Configuration for the filesystem strategy
|
|
45
76
|
"""
|
|
46
77
|
super().__init__(mission_id, setup_version_id)
|
|
47
|
-
self.config
|
|
78
|
+
self.config = config
|
|
48
79
|
|
|
49
80
|
@abstractmethod
|
|
50
|
-
def
|
|
51
|
-
|
|
81
|
+
def upload_files(
|
|
82
|
+
self,
|
|
83
|
+
files: list[UploadFileData],
|
|
84
|
+
) -> tuple[list[FilesystemRecord], int, int]:
|
|
85
|
+
"""Upload multiple files to the system.
|
|
52
86
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
87
|
+
This method allows batch uploading of files with validation and
|
|
88
|
+
error handling for each individual file. Files are processed
|
|
89
|
+
atomically - if one fails, others may still succeed.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
files: List of tuples containing (content, name, file_type, content_type, metadata, replace_if_exists)
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
tuple[list[FilesystemRecord], int, int]: List of uploaded files, total uploaded count, total failed count
|
|
96
|
+
"""
|
|
56
97
|
|
|
57
98
|
@abstractmethod
|
|
58
|
-
def
|
|
59
|
-
|
|
99
|
+
def get_file(
|
|
100
|
+
self,
|
|
101
|
+
file_id: str,
|
|
102
|
+
*,
|
|
103
|
+
include_content: bool = False,
|
|
104
|
+
) -> tuple[FilesystemRecord, bytes | None]:
|
|
105
|
+
"""Get a specific file by ID or name.
|
|
106
|
+
|
|
107
|
+
This method fetches detailed information about a single file,
|
|
108
|
+
with optional content inclusion. Supports lookup by either
|
|
109
|
+
unique ID or name within a context.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
file_id: The ID of the file to be retrieved
|
|
113
|
+
include_content: Whether to include file content in response
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
tuple[FilesystemRecord, bytes | None]: Metadata about the retrieved file and optional content
|
|
117
|
+
"""
|
|
60
118
|
|
|
61
119
|
@abstractmethod
|
|
62
|
-
def
|
|
63
|
-
|
|
120
|
+
def get_files(
|
|
121
|
+
self,
|
|
122
|
+
filters: FileFilter,
|
|
123
|
+
*,
|
|
124
|
+
list_size: int = 100,
|
|
125
|
+
offset: int = 0,
|
|
126
|
+
order: str | None = None,
|
|
127
|
+
include_content: bool = False,
|
|
128
|
+
) -> tuple[list[FilesystemRecord], int]:
|
|
129
|
+
"""Get multiple files by various criteria.
|
|
130
|
+
|
|
131
|
+
This method provides efficient retrieval of multiple files using:
|
|
132
|
+
- File IDs
|
|
133
|
+
- File names
|
|
134
|
+
- Path prefix
|
|
135
|
+
With support for:
|
|
136
|
+
- Pagination for large result sets
|
|
137
|
+
- Optional content inclusion
|
|
138
|
+
- Total count of matching files
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
filters: Filter criteria for the files
|
|
142
|
+
list_size: Number of files to return per page
|
|
143
|
+
offset: Offset to start listing files from
|
|
144
|
+
order: Field to order results by
|
|
145
|
+
include_content: Whether to include file content in response
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
tuple[list[FilesystemRecord], int]: List of files and total count
|
|
149
|
+
"""
|
|
64
150
|
|
|
65
151
|
@abstractmethod
|
|
66
|
-
def
|
|
67
|
-
|
|
152
|
+
def update_file(
|
|
153
|
+
self,
|
|
154
|
+
file_id: str,
|
|
155
|
+
content: bytes | None = None,
|
|
156
|
+
file_type: str | None = None,
|
|
157
|
+
content_type: str | None = None,
|
|
158
|
+
metadata: dict[str, Any] | None = None,
|
|
159
|
+
new_name: str | None = None,
|
|
160
|
+
status: str | None = None,
|
|
161
|
+
) -> FilesystemRecord:
|
|
162
|
+
"""Update file metadata, content, or both.
|
|
163
|
+
|
|
164
|
+
This method allows updating various aspects of a file:
|
|
165
|
+
- Rename files
|
|
166
|
+
- Update content and content type
|
|
167
|
+
- Modify metadata
|
|
168
|
+
- Create new versions
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
file_id: The ID of the file to be updated
|
|
172
|
+
content: Optional new content of the file
|
|
173
|
+
file_type: Optional new type of data
|
|
174
|
+
content_type: Optional new MIME type
|
|
175
|
+
metadata: Optional new metadata (will merge with existing)
|
|
176
|
+
new_name: Optional new name for the file
|
|
177
|
+
status: Optional new status for the file
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
FilesystemRecord: Metadata about the updated file
|
|
181
|
+
"""
|
|
68
182
|
|
|
69
183
|
@abstractmethod
|
|
70
|
-
def
|
|
71
|
-
|
|
184
|
+
def delete_files(
|
|
185
|
+
self,
|
|
186
|
+
filters: FileFilter,
|
|
187
|
+
*,
|
|
188
|
+
permanent: bool = False,
|
|
189
|
+
force: bool = False,
|
|
190
|
+
) -> tuple[dict[str, bool], int, int]:
|
|
191
|
+
"""Delete multiple files.
|
|
192
|
+
|
|
193
|
+
This method supports batch deletion of files with options for:
|
|
194
|
+
- Soft deletion (marking as deleted)
|
|
195
|
+
- Permanent deletion
|
|
196
|
+
- Force deletion of files in use
|
|
197
|
+
- Individual error reporting per file
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
filters: Filter criteria for the files
|
|
201
|
+
permanent: Whether to permanently delete the files
|
|
202
|
+
force: Whether to force delete even if files are in use
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
tuple[dict[str, bool], int, int]: Results per file, total deleted count, total failed count
|
|
206
|
+
"""
|
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""gRPC filesystem implementation."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Generator
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from digitalkin_proto.digitalkin.filesystem.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
11
|
-
from digitalkin_proto.digitalkin.filesystem.v2.filesystem_pb2 import (
|
|
12
|
-
FileType as FileTypeProto,
|
|
13
|
-
)
|
|
7
|
+
from digitalkin_proto.digitalkin.filesystem.v1 import filesystem_pb2, filesystem_service_pb2_grpc
|
|
8
|
+
from google.protobuf import struct_pb2
|
|
9
|
+
from google.protobuf.json_format import MessageToDict
|
|
14
10
|
|
|
15
11
|
from digitalkin.grpc_servers.utils.exceptions import ServerError
|
|
16
12
|
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
17
13
|
from digitalkin.grpc_servers.utils.models import ClientConfig
|
|
18
14
|
from digitalkin.logger import logger
|
|
19
15
|
from digitalkin.services.filesystem.filesystem_strategy import (
|
|
20
|
-
|
|
16
|
+
FileFilter,
|
|
17
|
+
FilesystemRecord,
|
|
21
18
|
FilesystemServiceError,
|
|
22
19
|
FilesystemStrategy,
|
|
23
|
-
|
|
20
|
+
UploadFileData,
|
|
24
21
|
)
|
|
25
22
|
|
|
26
23
|
|
|
@@ -54,161 +51,272 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
54
51
|
logger.exception(msg)
|
|
55
52
|
raise FilesystemServiceError(msg) from e
|
|
56
53
|
|
|
54
|
+
@staticmethod
|
|
55
|
+
def _file_type_to_enum(file_type: str) -> filesystem_pb2.FileType:
|
|
56
|
+
"""Convert a file type string to a FileType enum.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
file_type: The file type string to convert
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
filesystem_pb2.FileType: The converted file type enum
|
|
63
|
+
"""
|
|
64
|
+
if not file_type.upper().startswith("FILE_TYPE_"):
|
|
65
|
+
file_type = f"FILE_TYPE_{file_type.upper()}"
|
|
66
|
+
try:
|
|
67
|
+
return getattr(filesystem_pb2.FileType, file_type.upper())
|
|
68
|
+
except AttributeError:
|
|
69
|
+
return filesystem_pb2.FileType.FILE_TYPE_UNSPECIFIED
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _file_status_to_enum(file_status: str) -> filesystem_pb2.FileStatus:
|
|
73
|
+
"""Convert a file status string to a FileStatus enum.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
file_status: The file status string to convert
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
filesystem_pb2.FileStatus: The converted file status enum
|
|
80
|
+
"""
|
|
81
|
+
if not file_status.upper().startswith("FILE_STATUS_"):
|
|
82
|
+
file_status = f"FILE_STATUS_{file_status.upper()}"
|
|
83
|
+
try:
|
|
84
|
+
return getattr(filesystem_pb2.FileStatus, file_status.upper())
|
|
85
|
+
except AttributeError:
|
|
86
|
+
return filesystem_pb2.FileStatus.FILE_STATUS_UNSPECIFIED
|
|
87
|
+
|
|
88
|
+
def _filter_to_proto(self, filters: FileFilter) -> filesystem_pb2.FileFilter:
|
|
89
|
+
"""Convert a FileFilter to a FileFilter proto message.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
filters: The FileFilter to convert
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
filesystem_pb2.FileFilter: The converted FileFilter proto message
|
|
96
|
+
"""
|
|
97
|
+
return filesystem_pb2.FileFilter(
|
|
98
|
+
context=self.mission_id,
|
|
99
|
+
**filters.model_dump(exclude={"file_types", "status"}),
|
|
100
|
+
file_types=[self._file_type_to_enum(file_type) for file_type in filters.file_types]
|
|
101
|
+
if filters.file_types
|
|
102
|
+
else None,
|
|
103
|
+
status=self._file_status_to_enum(filters.status) if filters.status else None,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def _file_proto_to_data(self, file: filesystem_pb2.File, content: bytes | None = None) -> FilesystemRecord:
|
|
107
|
+
"""Convert a File proto message to FilesystemRecord.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
file: The File proto message to convert
|
|
111
|
+
content: The content of the file
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
FilesystemRecord: The converted data
|
|
115
|
+
"""
|
|
116
|
+
return FilesystemRecord(
|
|
117
|
+
id=file.file_id,
|
|
118
|
+
context=self.mission_id,
|
|
119
|
+
name=file.name,
|
|
120
|
+
file_type=filesystem_pb2.FileType.Name(file.file_type),
|
|
121
|
+
content_type=file.content_type,
|
|
122
|
+
size_bytes=file.size_bytes,
|
|
123
|
+
checksum=file.checksum,
|
|
124
|
+
metadata=MessageToDict(file.metadata),
|
|
125
|
+
storage_url=file.storage_url,
|
|
126
|
+
status=filesystem_pb2.FileStatus.Name(file.status),
|
|
127
|
+
content=content,
|
|
128
|
+
)
|
|
129
|
+
|
|
57
130
|
def __init__(
|
|
58
131
|
self,
|
|
59
132
|
mission_id: str,
|
|
60
133
|
setup_version_id: str,
|
|
61
|
-
config: dict[str, str],
|
|
62
134
|
client_config: ClientConfig,
|
|
63
|
-
|
|
135
|
+
config: dict[str, Any] | None = None,
|
|
64
136
|
) -> None:
|
|
65
|
-
"""Initialize the
|
|
137
|
+
"""Initialize the gRPC filesystem strategy.
|
|
66
138
|
|
|
67
139
|
Args:
|
|
68
140
|
mission_id: The ID of the mission this strategy is associated with
|
|
69
141
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
kwargs: other optional arguments to pass to the parent class constructor
|
|
142
|
+
client_config: Configuration for the gRPC client connection
|
|
143
|
+
config: Configuration for the filesystem strategy
|
|
73
144
|
"""
|
|
74
145
|
super().__init__(mission_id, setup_version_id, config)
|
|
146
|
+
self.service_name = "FilesystemService"
|
|
75
147
|
channel = self._init_channel(client_config)
|
|
76
148
|
self.stub = filesystem_service_pb2_grpc.FilesystemServiceStub(channel)
|
|
77
|
-
logger.
|
|
149
|
+
logger.debug("Channel client 'Filesystem' initialized succesfully")
|
|
78
150
|
|
|
79
|
-
def
|
|
80
|
-
|
|
151
|
+
def upload_files(
|
|
152
|
+
self,
|
|
153
|
+
files: list[UploadFileData],
|
|
154
|
+
) -> tuple[list[FilesystemRecord], int, int]:
|
|
155
|
+
"""Upload multiple files to the filesystem.
|
|
81
156
|
|
|
82
157
|
Args:
|
|
83
|
-
|
|
84
|
-
name: The name of the file to be created
|
|
85
|
-
file_type: The type of data being uploaded
|
|
158
|
+
files: List of tuples containing (content, name, file_type, content_type, metadata, replace_if_exists)
|
|
86
159
|
|
|
87
160
|
Returns:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Raises:
|
|
91
|
-
ValueError: If the file already exists
|
|
161
|
+
tuple[list[FilesystemRecord], int, int]: List of uploaded files, total uploaded count, total failed count
|
|
92
162
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
163
|
+
logger.debug("Uploading %d files", len(files))
|
|
164
|
+
with GrpcFilesystem._handle_grpc_errors("UploadFiles"):
|
|
165
|
+
upload_files: list[filesystem_pb2.UploadFileData] = []
|
|
166
|
+
for file in files:
|
|
167
|
+
metadata_struct: struct_pb2.Struct | None = None
|
|
168
|
+
if file.metadata:
|
|
169
|
+
metadata_struct = struct_pb2.Struct()
|
|
170
|
+
metadata_struct.update(file.metadata)
|
|
171
|
+
upload_files.append(
|
|
172
|
+
filesystem_pb2.UploadFileData(
|
|
173
|
+
context=self.mission_id,
|
|
174
|
+
name=file.name,
|
|
175
|
+
file_type=self._file_type_to_enum(file.file_type),
|
|
176
|
+
content_type=file.content_type or "application/octet-stream",
|
|
177
|
+
content=file.content,
|
|
178
|
+
metadata=metadata_struct,
|
|
179
|
+
status=filesystem_pb2.FileStatus.FILE_STATUS_UPLOADING,
|
|
180
|
+
replace_if_exists=file.replace_if_exists,
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
request = filesystem_pb2.UploadFilesRequest(files=upload_files)
|
|
184
|
+
response: filesystem_pb2.UploadFilesResponse = self.exec_grpc_query("UploadFiles", request)
|
|
185
|
+
results = [self._file_proto_to_data(result.file) for result in response.results if result.HasField("file")]
|
|
186
|
+
logger.debug("Uploaded files: %s", results)
|
|
187
|
+
return results, response.total_uploaded, response.total_failed
|
|
107
188
|
|
|
108
|
-
def
|
|
109
|
-
|
|
189
|
+
def get_file(
|
|
190
|
+
self,
|
|
191
|
+
file_id: str,
|
|
192
|
+
*,
|
|
193
|
+
include_content: bool = False,
|
|
194
|
+
) -> FilesystemRecord:
|
|
195
|
+
"""Get a file from the filesystem.
|
|
110
196
|
|
|
111
197
|
Args:
|
|
112
|
-
|
|
198
|
+
file_id: The ID of the file to be retrieved
|
|
199
|
+
include_content: Whether to include file content in response
|
|
113
200
|
|
|
114
201
|
Returns:
|
|
115
|
-
|
|
202
|
+
FilesystemRecord: Metadata about the retrieved file
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
FilesystemServiceError: If there is an error retrieving the file
|
|
116
206
|
"""
|
|
117
|
-
with GrpcFilesystem._handle_grpc_errors("
|
|
118
|
-
request = filesystem_pb2.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
name=response.file.name,
|
|
123
|
-
file_type=FileType[FileTypeProto.Name(response.file.file_type)],
|
|
124
|
-
url=response.file.url,
|
|
207
|
+
with GrpcFilesystem._handle_grpc_errors("GetFile"):
|
|
208
|
+
request = filesystem_pb2.GetFileRequest(
|
|
209
|
+
context=self.mission_id,
|
|
210
|
+
file_id=file_id,
|
|
211
|
+
include_content=include_content,
|
|
125
212
|
)
|
|
126
213
|
|
|
127
|
-
|
|
128
|
-
|
|
214
|
+
response: filesystem_pb2.GetFileResponse = self.exec_grpc_query("GetFile", request)
|
|
215
|
+
|
|
216
|
+
return self._file_proto_to_data(response.file, response.content)
|
|
217
|
+
|
|
218
|
+
def update_file(
|
|
219
|
+
self,
|
|
220
|
+
file_id: str,
|
|
221
|
+
content: bytes | None = None,
|
|
222
|
+
file_type: str | None = None,
|
|
223
|
+
content_type: str | None = None,
|
|
224
|
+
metadata: dict[str, Any] | None = None,
|
|
225
|
+
new_name: str | None = None,
|
|
226
|
+
status: str | None = None,
|
|
227
|
+
) -> FilesystemRecord:
|
|
228
|
+
"""Update a file in the filesystem.
|
|
129
229
|
|
|
130
230
|
Args:
|
|
131
|
-
|
|
132
|
-
content:
|
|
133
|
-
file_type:
|
|
231
|
+
file_id: The id of the file to be updated
|
|
232
|
+
content: Optional new content of the file
|
|
233
|
+
file_type: Optional new type of data
|
|
234
|
+
content_type: Optional new MIME type
|
|
235
|
+
metadata: Optional new metadata (will merge with existing)
|
|
236
|
+
new_name: Optional new name for the file
|
|
237
|
+
status: Optional new status for the file
|
|
134
238
|
|
|
135
239
|
Returns:
|
|
136
|
-
|
|
240
|
+
FilesystemRecord: Metadata about the updated file
|
|
241
|
+
|
|
242
|
+
Raises:
|
|
243
|
+
FilesystemServiceError: If there is an error during update
|
|
137
244
|
"""
|
|
138
245
|
with GrpcFilesystem._handle_grpc_errors("UpdateFile"):
|
|
139
246
|
request = filesystem_pb2.UpdateFileRequest(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
file_type=file_type.name,
|
|
247
|
+
context=self.mission_id,
|
|
248
|
+
file_id=file_id,
|
|
143
249
|
content=content,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
name=response.file.name,
|
|
149
|
-
file_type=FileType[FileTypeProto.Name(response.file.file_type)],
|
|
150
|
-
url=response.file.url,
|
|
250
|
+
file_type=self._file_type_to_enum(file_type) if file_type else None,
|
|
251
|
+
content_type=content_type,
|
|
252
|
+
new_name=new_name,
|
|
253
|
+
status=self._file_status_to_enum(status) if status else None,
|
|
151
254
|
)
|
|
152
255
|
|
|
153
|
-
|
|
154
|
-
|
|
256
|
+
if metadata:
|
|
257
|
+
request.metadata.update(metadata)
|
|
155
258
|
|
|
156
|
-
|
|
157
|
-
|
|
259
|
+
response: filesystem_pb2.UpdateFileResponse = self.exec_grpc_query("UpdateFile", request)
|
|
260
|
+
return self._file_proto_to_data(response.result.file)
|
|
158
261
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
262
|
+
def delete_files(
|
|
263
|
+
self,
|
|
264
|
+
filters: FileFilter,
|
|
265
|
+
*,
|
|
266
|
+
permanent: bool = False,
|
|
267
|
+
force: bool = False,
|
|
268
|
+
) -> tuple[dict[str, bool], int, int]:
|
|
269
|
+
"""Delete multiple files from the filesystem.
|
|
166
270
|
|
|
167
|
-
|
|
168
|
-
|
|
271
|
+
Args:
|
|
272
|
+
filters: Filter criteria for the files
|
|
273
|
+
permanent: Whether to permanently delete the files
|
|
274
|
+
force: Whether to force delete even if files are in use
|
|
169
275
|
|
|
170
276
|
Returns:
|
|
171
|
-
|
|
277
|
+
tuple[dict[str, bool], int, int]: Results per file, total deleted count, total failed count
|
|
172
278
|
"""
|
|
173
|
-
with GrpcFilesystem._handle_grpc_errors("
|
|
174
|
-
request = filesystem_pb2.
|
|
175
|
-
|
|
176
|
-
|
|
279
|
+
with GrpcFilesystem._handle_grpc_errors("DeleteFiles"):
|
|
280
|
+
request = filesystem_pb2.DeleteFilesRequest(
|
|
281
|
+
context=self.mission_id,
|
|
282
|
+
filters=self._filter_to_proto(filters),
|
|
283
|
+
permanent=permanent,
|
|
284
|
+
force=force,
|
|
177
285
|
)
|
|
178
|
-
return [
|
|
179
|
-
FilesystemData(
|
|
180
|
-
kin_context=file.kin_context,
|
|
181
|
-
name=file.name,
|
|
182
|
-
file_type=FileType[FileTypeProto.Name(file.file_type)],
|
|
183
|
-
url=file.url,
|
|
184
|
-
)
|
|
185
|
-
for file in response.files
|
|
186
|
-
]
|
|
187
286
|
|
|
188
|
-
|
|
189
|
-
|
|
287
|
+
response: filesystem_pb2.DeleteFilesResponse = self.exec_grpc_query("DeleteFiles", request)
|
|
288
|
+
return dict(response.results), response.total_deleted, response.total_failed
|
|
289
|
+
|
|
290
|
+
def get_files(
|
|
291
|
+
self,
|
|
292
|
+
filters: FileFilter,
|
|
293
|
+
*,
|
|
294
|
+
list_size: int = 100,
|
|
295
|
+
offset: int = 0,
|
|
296
|
+
order: str | None = None,
|
|
297
|
+
include_content: bool = False,
|
|
298
|
+
) -> tuple[list[FilesystemRecord], int]:
|
|
299
|
+
"""Get multiple files from the filesystem.
|
|
190
300
|
|
|
191
301
|
Args:
|
|
192
|
-
|
|
302
|
+
filters: Filter criteria for the files
|
|
303
|
+
list_size: Number of files to return per page
|
|
304
|
+
offset: Offset to start from
|
|
305
|
+
order: Field to order results by
|
|
306
|
+
include_content: Whether to include file content in response
|
|
193
307
|
|
|
194
308
|
Returns:
|
|
195
|
-
list[
|
|
309
|
+
tuple[list[FilesystemRecord], int]: List of files and total count
|
|
196
310
|
"""
|
|
197
|
-
with GrpcFilesystem._handle_grpc_errors("
|
|
198
|
-
request = filesystem_pb2.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
210
|
-
elif which_field == "error":
|
|
211
|
-
# Handle error case
|
|
212
|
-
result[name] = None
|
|
213
|
-
logger.warning("Error retrieving file '%s': %s", name, file_result.error)
|
|
214
|
-
return result
|
|
311
|
+
with GrpcFilesystem._handle_grpc_errors("GetFiles"):
|
|
312
|
+
request = filesystem_pb2.GetFilesRequest(
|
|
313
|
+
context=self.mission_id,
|
|
314
|
+
filters=self._filter_to_proto(filters),
|
|
315
|
+
include_content=include_content,
|
|
316
|
+
list_size=list_size,
|
|
317
|
+
offset=offset,
|
|
318
|
+
order=order,
|
|
319
|
+
)
|
|
320
|
+
response: filesystem_pb2.GetFilesResponse = self.exec_grpc_query("GetFiles", request)
|
|
321
|
+
|
|
322
|
+
return [self._file_proto_to_data(file) for file in response.files], response.total_count
|
|
@@ -139,6 +139,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
139
139
|
current_setup_version = setup_pb2.SetupVersion(**valid_data.current_setup_version.model_dump())
|
|
140
140
|
|
|
141
141
|
request = setup_pb2.UpdateSetupRequest(
|
|
142
|
+
setup_id=valid_data.id,
|
|
142
143
|
name=valid_data.name,
|
|
143
144
|
owner_id=valid_data.owner_id or "",
|
|
144
145
|
current_setup_version=current_setup_version,
|