diff --git a/proto/digitalkin/filesystem/v1/data.proto b/proto/digitalkin/filesystem/v1/data.proto deleted file mode 100644 index 815cba4..0000000 --- a/proto/digitalkin/filesystem/v1/data.proto +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2025 DigitalKin Inc. -// -// Licensed under the GNU General Public License, Version 3.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.gnu.org/licenses/gpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package digitalkin.filesystem.v1; - -import "buf/validate/validate.proto"; - -// FileType: Enum to file type -enum FileType { - // DOCUMENT: Document file - DOCUMENT = 0; - // IMAGE: Image file - IMAGE = 1; - // VIDEO: Video file - VIDEO = 2; - // AUDIO: Audio file - AUDIO = 3; - // OTHER: Other file - OTHER = 4; -} - -// File: Message to represent stored files -message File { - // mission_id: Mission ID linked to the file - string mission_id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.prefix = "missions:"]; - // name: Name of the file (unique by mission_id) - string name = 2 [(buf.validate.field).required = true]; - // type: Type of the file - FileType type = 3 [(buf.validate.field).required = true]; - // url: URL to access the file - string url = 4 [(buf.validate.field).required = true]; -} - -// UploadFileRequest: Request to upload a file -message UploadFileRequest { - // mission_id: Mission ID linked to the file - string mission_id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.prefix = "missions:"]; - // name: Name of the file (unique by mission_id) - string name = 2 [(buf.validate.field).required = true]; - // type: Type of the file - FileType type = 3 [(buf.validate.field).required = true]; - // content: Content of the file - bytes content = 4 [(buf.validate.field).required = true]; -} - -// UploadFileResponse: Response to uploaded file -message UploadFileResponse { - // success: Success of the operation - bool success = 1 [(buf.validate.field).required = true]; - // file: Uploaded file - File file = 2 [(buf.validate.field).required = true]; -} - -// GetFileRequest: Request to get a file by ID -message GetFileRequest { - // mission_id: Mission ID linked to the file - string mission_id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.prefix = "missions:"]; - // name: Name of the file (unique by mission_id) - string name = 2 [(buf.validate.field).required = true]; -} - -// GetFileResponse: Response to get a file by ID -message GetFileResponse { - // file: Retrieved file - File file = 1 [(buf.validate.field).required = true]; - // content: Content of the file - bytes content = 2 [(buf.validate.field).required = true]; -} - -// GetFilesByMissionRequest: Request to get files by mission -message GetFilesByMissionRequest { - // mission_id: Mission ID linked to the files - string mission_id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.prefix = "missions:"]; -} - -// GetFilesByMissionResponse: Response to get files by mission -message GetFilesByMissionResponse { - // files: Retrieved files - repeated File files = 1; -} - -// GetFilesByNameRequest: Request to get files by name -message GetFilesByNameRequest { - // mission_id: Mission ID linked to the files - string mission_id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.prefix = "missions:"]; - // name: Name of the files - string name = 2 [(buf.validate.field).required = true]; -} - -// GetFilesByNameResponse: Response to get files by name -message GetFilesByNameResponse { - // success: Success of the operation - bool success = 1 [(buf.validate.field).required = true]; - // file: Retrieved file - File file = 2 [(buf.validate.field).required = true]; -} - -// DeleteFileRequest: Request to delete a file -message DeleteFileRequest { - // mission_id: Mission ID linked to the file - string mission_id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.prefix = "missions:"]; - // name: Name of the file (unique by mission_id) - string name = 2; -} - -// DeleteFileResponse: Response to delete a file -message DeleteFileResponse { - // success: Success of the operation - bool success = 1 [(buf.validate.field).required = true]; -} \ No newline at end of file diff --git a/proto/digitalkin/filesystem/v1/filesystem.proto b/proto/digitalkin/filesystem/v1/filesystem.proto new file mode 100644 index 0000000..d61cbfc --- /dev/null +++ b/proto/digitalkin/filesystem/v1/filesystem.proto @@ -0,0 +1,341 @@ +// Copyright 2025 DigitalKin Inc. +// +// Licensed under the GNU General Public License, Version 3.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.gnu.org/licenses/gpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package digitalkin.filesystem.v1; + +import "buf/validate/validate.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; + +// FileType: Enum for file type classification +enum FileType { + // FILE_TYPE_UNSPECIFIED: Default value, should not be used + FILE_TYPE_UNSPECIFIED = 0; + // FILE_TYPE_DOCUMENT: Document files (PDF, DOC, TXT, etc.) + FILE_TYPE_DOCUMENT = 1; + // FILE_TYPE_IMAGE: Image files (PNG, JPG, GIF, etc.) + FILE_TYPE_IMAGE = 2; + // FILE_TYPE_VIDEO: Video files (MP4, AVI, MOV, etc.) + FILE_TYPE_VIDEO = 3; + // FILE_TYPE_AUDIO: Audio files (MP3, WAV, etc.) + FILE_TYPE_AUDIO = 4; + // FILE_TYPE_ARCHIVE: Archive files (ZIP, TAR, etc.) + FILE_TYPE_ARCHIVE = 5; + // FILE_TYPE_CODE: Source code files + FILE_TYPE_CODE = 6; + // FILE_TYPE_OTHER: Other file types + FILE_TYPE_OTHER = 7; +} + +// FileStatus: Enum for file status in the system +enum FileStatus { + // FILE_STATUS_UNSPECIFIED: Default value, should not be used + FILE_STATUS_UNSPECIFIED = 0; + // FILE_STATUS_UPLOADING: File is being uploaded + FILE_STATUS_UPLOADING = 1; + // FILE_STATUS_ACTIVE: File is active and available + FILE_STATUS_ACTIVE = 2; + // FILE_STATUS_PROCESSING: File is being processed + FILE_STATUS_PROCESSING = 3; + // FILE_STATUS_ARCHIVED: File is archived + FILE_STATUS_ARCHIVED = 4; + // FILE_STATUS_DELETED: File is marked for deletion + FILE_STATUS_DELETED = 5; +} + +// File: Message to represent stored files with comprehensive metadata +message File { + // id: Unique identifier for the file (UUID) + string id = 1 [(buf.validate.field).required = true, (buf.validate.field).string.uuid = true]; + + // context: Context ID linked to the file + string context = 2 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + // name: Name of the file (unique within context) + string name = 3 [(buf.validate.field).required = true, (buf.validate.field).string.min_len = 1]; + + // file_type: Type classification of the file + FileType file_type = 4 [(buf.validate.field).required = true, (buf.validate.field).enum.not_in = 0]; + + // content_type: MIME type of the file + string content_type = 5 [(buf.validate.field).required = true]; + + // size_bytes: Size of the file in bytes + int64 size_bytes = 6 [(buf.validate.field).required = true, (buf.validate.field).int64.gte = 0]; + + // checksum: SHA-256 checksum of the file content + string checksum = 7 [(buf.validate.field).required = true]; + + // created: Timestamp when the file was created + google.protobuf.Timestamp created = 8 [(buf.validate.field).required = true]; + + // updated: Timestamp when the file was last updated + google.protobuf.Timestamp updated = 9 [(buf.validate.field).required = true]; + + // metadata: Flexible metadata for additional file properties + google.protobuf.Struct metadata = 10; + + // storage_url: Internal URL for accessing the file content + string storage_url = 11 [(buf.validate.field).required = true]; + + // status: Current status of the file + FileStatus status = 12 [(buf.validate.field).required = true, (buf.validate.field).enum.not_in = 0]; +} + +// FileFilter: Filter criteria for querying files +message FileFilter { + // names: Filter by file names (exact matches) + repeated string names = 1; + + // ids: Filter by file IDs + repeated string ids = 2 [(buf.validate.field).repeated.items.string.uuid = true]; + + // file_types: Filter by file types + repeated FileType file_types = 3 [(buf.validate.field).repeated.items.enum.not_in = 0]; + + // context: Filter by context (required for scoping) + string context = 4 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + // created_after: Filter files created after this timestamp + google.protobuf.Timestamp created_after = 5; + + // created_before: Filter files created before this timestamp + google.protobuf.Timestamp created_before = 6; + + // updated_after: Filter files updated after this timestamp + google.protobuf.Timestamp updated_after = 7; + + // updated_before: Filter files updated before this timestamp + google.protobuf.Timestamp updated_before = 8; + + // status: Filter by file status + FileStatus status = 9; + + // content_type_prefix: Filter by content type prefix (e.g., "image/") + string content_type_prefix = 10; + + // min_size_bytes: Filter files with minimum size + int64 min_size_bytes = 11 [(buf.validate.field).int64.gte = 0]; + + // max_size_bytes: Filter files with maximum size + int64 max_size_bytes = 12 [(buf.validate.field).int64.gte = 0]; + + // prefix: Filter by path prefix (e.g., "folder1/") + string prefix = 13 [(buf.validate.field).string.pattern = "^[^/]*/?[^/]*$"]; + + // content_type: Filter by content type + string content_type = 14; +} + +// FileResult: Wrapper for file operations that may succeed or fail +message FileResult { + oneof result { + // file: Successfully processed file + File file = 1; + // error: Error information if operation failed + string error = 2; + } +} + +// UploadFileData: Data for uploading a single file +message UploadFileData { + // context: Context ID for the file + string context = 1 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + // name: Name of the file + string name = 2 [(buf.validate.field).required = true, (buf.validate.field).string.min_len = 1]; + + // file_type: Type classification of the file + FileType file_type = 3 [(buf.validate.field).required = true, (buf.validate.field).enum.not_in = 0]; + + // content_type: MIME type of the file + string content_type = 4 [(buf.validate.field).required = true]; + + // content: File content + bytes content = 5 [(buf.validate.field).required = true]; + + // metadata: Additional metadata for the file + google.protobuf.Struct metadata = 6; + + // replace_if_exists: Whether to replace existing file with same name + bool replace_if_exists = 7; +} + +// UploadFilesRequest: Request to upload multiple files +message UploadFilesRequest { + // files: List of files to upload + repeated UploadFileData files = 1 [(buf.validate.field).required = true, (buf.validate.field).repeated.min_items = 1]; +} + +// UploadFilesResponse: Response for file upload operations +message UploadFilesResponse { + // results: Results for each uploaded file + repeated FileResult results = 1; + + // total_uploaded: Number of successfully uploaded files + int32 total_uploaded = 2; + + // total_failed: Number of failed uploads + int32 total_failed = 3; +} + +// ListFilesRequest: Request to list files with filtering and pagination +message ListFilesRequest { + // filter: Filter criteria for the files + FileFilter filter = 1 [(buf.validate.field).required = true]; + + // list_size: Number of files to return per page + int32 list_size = 2 [(buf.validate.field).int32.gte = 1, (buf.validate.field).int32.lte = 1000]; + + // offset: Offset of the first file in the list + int32 offset = 3 [(buf.validate.field).int32.gte = 0]; + + // order: Field to order results by + string order = 4; +} + +// ListFilesResponse: Response for listing files +message ListFilesResponse { + // files: List of files matching the criteria + repeated File files = 1 ; + + // total_count: Total number of files matching the filter (optional) + int32 total_count = 2 [(buf.validate.field).int32.gte = 0]; +} + +// GetFileRequest: Request to get a specific file +message GetFileRequest { + // context: Context ID for the file + string context = 1 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + oneof identifier { + // id: File ID + string id = 2 [(buf.validate.field).string.uuid = true]; + // name: File name + string name = 3 [(buf.validate.field).string.min_len = 1]; + } + + // include_content: Whether to include file content in response + bool include_content = 4; +} + +// GetFileResponse: Response containing file information +message GetFileResponse { + // file: The requested file + File file = 1; + + // content: File content (only if requested) + bytes content = 2; +} + +// UpdateFileRequest: Request to update a file +message UpdateFileRequest { + // context: Context ID for the file + string context = 1 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + // name: Current name of the file + string name = 2 [(buf.validate.field).required = true, (buf.validate.field).string.min_len = 1]; + + // new_name: New name for the file (optional) + string new_name = 3; + + // file_type: New file type (optional) + FileType file_type = 4; + + // content_type: New content type (optional) + string content_type = 5; + + // content: New file content (optional) + bytes content = 6; + + // metadata: New metadata (optional, will merge with existing) + google.protobuf.Struct metadata = 7; +} + +// UpdateFileResponse: Response for file update +message UpdateFileResponse { + // result: Result of the file update operation + FileResult result = 1; +} + +// FileIdentifier: Common filter for identifying files +message FileIdentifier { + // ids: Get files by their IDs + repeated string ids = 1 [(buf.validate.field).repeated.items.string.uuid = true]; + + // names: Get files by their names + repeated string names = 2; + + // prefix: Get files by path prefix (e.g., "folder1/") + string prefix = 3 [(buf.validate.field).string.pattern = "^[^/]*/?[^/]*$"]; +} + +// GetFilesRequest: Request to get multiple files by various criteria +message GetFilesRequest { + // context: Context ID for the files + string context = 1 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + // filter: How to identify the files + FileIdentifier filter = 2 [(buf.validate.field).required = true]; + + // list_size: Number of files to return per page + int32 list_size = 3 [(buf.validate.field).int32.gte = 1, (buf.validate.field).int32.lte = 1000]; + + // offset: Offset of the first file in the list + int32 offset = 4 [(buf.validate.field).int32.gte = 0]; + + // order: Field to order results by + string order = 5; + + // include_content: Whether to include file content in response + bool include_content = 6; +} + +// GetFilesResponse: Response containing multiple files +message GetFilesResponse { + // files: List of files matching the criteria + repeated File files = 1; + + // total_count: Total number of files matching the criteria + int32 total_count = 2; +} + +// DeleteFilesRequest: Request to delete multiple files +message DeleteFilesRequest { + // context: Context ID for the files + string context = 1 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^(missions:|setups:).*$"]; + + // filter: How to identify the files + FileIdentifier filter = 2 [(buf.validate.field).required = true]; + + // force: Whether to force delete even if file is in use + bool force = 3; + + // permanent: Whether to permanently delete (vs mark as deleted) + bool permanent = 4; +} + +// DeleteFilesResponse: Response for file deletion +message DeleteFilesResponse { + // results: Results for each file deletion attempt + map results = 1; + + // total_deleted: Number of successfully deleted files + int32 total_deleted = 2; + + // total_failed: Number of failed deletions + int32 total_failed = 3; +} \ No newline at end of file diff --git a/proto/digitalkin/filesystem/v1/filesystem_service.proto b/proto/digitalkin/filesystem/v1/filesystem_service.proto index 8bdd739..a3429f3 100644 --- a/proto/digitalkin/filesystem/v1/filesystem_service.proto +++ b/proto/digitalkin/filesystem/v1/filesystem_service.proto @@ -16,22 +16,70 @@ syntax = "proto3"; package digitalkin.filesystem.v1; -import "digitalkin/filesystem/v1/data.proto"; +import "digitalkin/filesystem/v1/filesystem.proto"; -// FileSystemService: Service to store objects -service FileSystemService { - // UploadFile: Upload a file to the system - rpc UploadFile (UploadFileRequest) returns (UploadFileResponse); - - // GetFile: Get a file by ID - rpc GetFile (GetFileRequest) returns (GetFileResponse); - - // GetFilesByMission: Get files by mission - rpc GetFilesByMission (GetFilesByMissionRequest) returns (GetFilesByMissionResponse); - - // GetFilesByName: Get files by name and mission - rpc GetFilesByName (GetFilesByNameRequest) returns (GetFilesByNameResponse); +// FilesystemService: Service for managing files and file operations +// +// This service provides comprehensive file management capabilities including +// upload, retrieval, update, and deletion operations with rich metadata support, +// filtering, and pagination. +service FilesystemService { + // UploadFiles: Upload multiple files to the system + // + // This method allows batch uploading of files with validation and + // error handling for each individual file. Files are processed + // atomically - if one fails, others may still succeed. + // + rpc UploadFiles(UploadFilesRequest) returns (UploadFilesResponse); + + // ListFiles: Retrieve files with filtering, sorting, and pagination + // + // This method provides flexible file querying capabilities with support for: + // - Multiple filter criteria (name, type, dates, size, etc.) + // - Pagination for large result sets + // - Sorting by various fields + // - Scoped access by kin_context + // + rpc ListFiles(ListFilesRequest) returns (ListFilesResponse); + + // GetFile: Retrieve a specific file by ID or name + // + // This method fetches detailed information about a single file, + // with optional content inclusion. Supports lookup by either + // unique ID or name within a kin_context. + // + rpc GetFile(GetFileRequest) returns (GetFileResponse); - // DeleteFile: Delete a file by name and mission - rpc DeleteFile (DeleteFileRequest) returns (DeleteFileResponse); + // GetFiles: Retrieve multiple files by various criteria + // + // This method provides efficient retrieval of multiple files using: + // - File IDs + // - File names + // - Path prefix + // With support for: + // - Pagination for large result sets + // - Optional content inclusion + // - Total count of matching files + // + rpc GetFiles(GetFilesRequest) returns (GetFilesResponse); + + // UpdateFile: Update file metadata, content, or both + // + // This method allows updating various aspects of a file: + // - Rename files + // - Update content and content type + // - Modify metadata + // - Create new versions + // + rpc UpdateFile(UpdateFileRequest) returns (UpdateFileResponse); + + // DeleteFiles: Delete multiple files + // + // This method supports batch deletion of files with options for: + // - Soft deletion (marking as deleted) + // - Permanent deletion + // - Force deletion of files in use + // - Individual error reporting per file + // + rpc DeleteFiles(DeleteFilesRequest) returns (DeleteFilesResponse); } \ No newline at end of file