diff --git a/src/palace/manager/service/google_drive/google_drive.py b/src/palace/manager/service/google_drive/google_drive.py index 0befbedaa4..7b8e41a0a8 100644 --- a/src/palace/manager/service/google_drive/google_drive.py +++ b/src/palace/manager/service/google_drive/google_drive.py @@ -17,8 +17,19 @@ def __init__(self, api_client: DriveResource) -> None: self.api_client = api_client def get_file(self, name: str, parent_folder_id: str | None = None) -> File | None: + """ + Return the first non-trashed file or folder with the given name. - query = f"name = '{name}'" + :param name: The exact name to search for. + :param parent_folder_id: If provided, restrict the search to items + whose parent is this folder/drive ID. + :return: The first matching, non-trashed ``File`` object, or ``None`` + if no match is found. + """ + # Explicitly exclude trashed items so that a folder moved to the + # Drive trash is not mistaken for a live folder, which would cause + # uploads to land inside a trashed (invisible) directory. + query = f"name = '{name}' and trashed = false" if parent_folder_id: query += f" and '{parent_folder_id}' in parents" diff --git a/tests/manager/service/google_drive/test_google_drive.py b/tests/manager/service/google_drive/test_google_drive.py index 3434b82495..0eb499fecd 100644 --- a/tests/manager/service/google_drive/test_google_drive.py +++ b/tests/manager/service/google_drive/test_google_drive.py @@ -3,6 +3,7 @@ import json from io import BytesIO from typing import TYPE_CHECKING +from urllib.parse import unquote_plus import pytest from googleapiclient.discovery import build @@ -114,6 +115,40 @@ def test_create_file(self): assert "Hello world" in body assert f"Content-Type: {mime_type}" in body + def test_get_file_excludes_trashed_items(self): + """get_file must include 'trashed = false' in its Drive query.""" + file_name = "some-folder" + file_id = "folder-id" + http_mock_sequence = HttpMockSequence( + [ + ( + {"status": "200"}, + json.dumps( + { + "files": [ + { + "kind": "drive#file", + "id": file_id, + "name": file_name, + "mimeType": "application/vnd.google-apps.folder", + } + ] + } + ), + ), + ] + ) + service = drive_service(http=http_mock_sequence) + + result = service.get_file(name=file_name, parent_folder_id="parent-id") + + assert result is not None + assert result["id"] == file_id + + # Verify the query sent to the Drive API contains the trashed filter. + request_uri = unquote_plus(http_mock_sequence.request_sequence[0][0]) + assert "trashed = false" in request_uri + def test_create_existing_file_fails(self): file_name = "file.txt" file_id = "file-id"