This commit is contained in:
Taylor Wilsdon
2026-02-28 18:05:59 -04:00
parent 44205b66e0
commit 6c04e4f5a0
2 changed files with 52 additions and 2 deletions

View File

@@ -237,20 +237,34 @@ MIME_TYPE_PATTERN = re.compile(r"^[A-Za-z0-9!#$&^_.+-]+/[A-Za-z0-9!#$&^_.+-]+$")
# Raw MIME type strings (containing '/') are always accepted as-is. # Raw MIME type strings (containing '/') are always accepted as-is.
FILE_TYPE_MIME_MAP: Dict[str, str] = { FILE_TYPE_MIME_MAP: Dict[str, str] = {
"folder": "application/vnd.google-apps.folder", "folder": "application/vnd.google-apps.folder",
"folders": "application/vnd.google-apps.folder",
"document": "application/vnd.google-apps.document", "document": "application/vnd.google-apps.document",
"doc": "application/vnd.google-apps.document", "doc": "application/vnd.google-apps.document",
"documents": "application/vnd.google-apps.document",
"docs": "application/vnd.google-apps.document",
"spreadsheet": "application/vnd.google-apps.spreadsheet", "spreadsheet": "application/vnd.google-apps.spreadsheet",
"sheet": "application/vnd.google-apps.spreadsheet", "sheet": "application/vnd.google-apps.spreadsheet",
"spreadsheets": "application/vnd.google-apps.spreadsheet",
"sheets": "application/vnd.google-apps.spreadsheet",
"presentation": "application/vnd.google-apps.presentation", "presentation": "application/vnd.google-apps.presentation",
"presentations": "application/vnd.google-apps.presentation",
"slide": "application/vnd.google-apps.presentation",
"slides": "application/vnd.google-apps.presentation", "slides": "application/vnd.google-apps.presentation",
"form": "application/vnd.google-apps.form", "form": "application/vnd.google-apps.form",
"forms": "application/vnd.google-apps.form",
"drawing": "application/vnd.google-apps.drawing", "drawing": "application/vnd.google-apps.drawing",
"drawings": "application/vnd.google-apps.drawing",
"pdf": "application/pdf", "pdf": "application/pdf",
"pdfs": "application/pdf",
"shortcut": "application/vnd.google-apps.shortcut", "shortcut": "application/vnd.google-apps.shortcut",
"shortcuts": "application/vnd.google-apps.shortcut",
"script": "application/vnd.google-apps.script", "script": "application/vnd.google-apps.script",
"scripts": "application/vnd.google-apps.script",
"site": "application/vnd.google-apps.site", "site": "application/vnd.google-apps.site",
"sites": "application/vnd.google-apps.site",
"jam": "application/vnd.google-apps.jam", "jam": "application/vnd.google-apps.jam",
"jamboard": "application/vnd.google-apps.jam", "jamboard": "application/vnd.google-apps.jam",
"jamboards": "application/vnd.google-apps.jam",
} }
@@ -272,12 +286,16 @@ def resolve_file_type_mime(file_type: str) -> str:
ValueError: If the value is not a recognised friendly name and contains no '/'. ValueError: If the value is not a recognised friendly name and contains no '/'.
""" """
normalized = file_type.strip() normalized = file_type.strip()
if not normalized:
raise ValueError("file_type cannot be empty.")
if "/" in normalized: if "/" in normalized:
if not MIME_TYPE_PATTERN.fullmatch(normalized): normalized_mime = normalized.lower()
if not MIME_TYPE_PATTERN.fullmatch(normalized_mime):
raise ValueError( raise ValueError(
f"Invalid MIME type '{file_type}'. Expected format like 'application/pdf'." f"Invalid MIME type '{file_type}'. Expected format like 'application/pdf'."
) )
return normalized return normalized_mime
lower = normalized.lower() lower = normalized.lower()
if lower not in FILE_TYPE_MIME_MAP: if lower not in FILE_TYPE_MIME_MAP:
valid = ", ".join(sorted(FILE_TYPE_MIME_MAP.keys())) valid = ", ".join(sorted(FILE_TYPE_MIME_MAP.keys()))

View File

@@ -707,6 +707,23 @@ async def test_search_file_type_document_alias():
assert "mimeType = 'application/vnd.google-apps.document'" in call_kwargs["q"] assert "mimeType = 'application/vnd.google-apps.document'" in call_kwargs["q"]
@pytest.mark.asyncio
async def test_search_file_type_plural_alias():
"""Plural aliases are resolved for friendlier natural-language usage."""
mock_service = Mock()
mock_service.files().list().execute.return_value = {"files": []}
await _unwrap(search_drive_files)(
service=mock_service,
user_google_email="user@example.com",
query="project",
file_type="folders",
)
call_kwargs = mock_service.files.return_value.list.call_args.kwargs
assert "mimeType = 'application/vnd.google-apps.folder'" in call_kwargs["q"]
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_search_file_type_sheet_alias(): async def test_search_file_type_sheet_alias():
"""Alias 'sheet' resolves to the Google Sheets MIME type.""" """Alias 'sheet' resolves to the Google Sheets MIME type."""
@@ -936,3 +953,18 @@ def test_resolve_file_type_mime_strips_whitespace():
from gdrive.drive_helpers import resolve_file_type_mime from gdrive.drive_helpers import resolve_file_type_mime
assert resolve_file_type_mime(" application/pdf ") == "application/pdf" assert resolve_file_type_mime(" application/pdf ") == "application/pdf"
def test_resolve_file_type_mime_normalizes_case():
"""Raw MIME types are normalized to lowercase for Drive query consistency."""
from gdrive.drive_helpers import resolve_file_type_mime
assert resolve_file_type_mime("Application/PDF") == "application/pdf"
def test_resolve_file_type_mime_empty_raises():
"""Blank values are rejected with a clear validation error."""
from gdrive.drive_helpers import resolve_file_type_mime
with pytest.raises(ValueError, match="cannot be empty"):
resolve_file_type_mime(" ")