Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions apps/api/app/services/demo/shared_document_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

import asyncio
import mimetypes
from pathlib import PurePath
from typing import Any
from urllib.parse import quote

Expand Down Expand Up @@ -99,8 +99,9 @@ async def get_catalog(self, db: AsyncSession) -> dict[str, Any]:
continue
metadata: dict[str, Any] = revision.document_metadata or {}
mimeType: str = (
mimetypes.guess_type(document.source_file_name or document.title)[0]
or "application/octet-stream"
JobFileStorage.get_content_type(
PurePath(document.source_file_name or document.title).suffix
)
)
source: dict[str, Any] = {
"demo_source_id": document.demo_source_id,
Expand Down Expand Up @@ -206,7 +207,7 @@ async def get_media_url(
)
if not key:
raise NotFoundException(resource="Demo original", resource_id=source_id)
mimeType: str = mimetypes.guess_type(fileName)[0] or "application/octet-stream"
mimeType: str = JobFileStorage.get_content_type(PurePath(fileName).suffix)
# Redirects let the storage service handle Range without proxying large
# original files through an API worker. The GET signature overrides MIME.
return await asyncio.to_thread(
Expand Down
33 changes: 30 additions & 3 deletions apps/api/tests/contract/test_shared_demo_corpus_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,16 +464,43 @@ async def test_demo_agent_tools_pin_history_and_private_isolation(


@pytest.mark.asyncio
@pytest.mark.parametrize(
("file_name", "original_mime"),
[
("original.pdf", "application/pdf"),
(
"original.docx",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
),
(
"original.DOCX",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
),
],
)
async def test_demo_revision_media_validation_and_job_retention(
developer_api_client_factory: ClientFactory, monkeypatch: pytest.MonkeyPatch
developer_api_client_factory: ClientFactory,
monkeypatch: pytest.MonkeyPatch,
file_name: str,
original_mime: str,
) -> None:
async with developer_api_client_factory() as client:
job = await create_demo(
client, source_id="media-demo", file_name="original.pdf"
client, source_id="media-demo", file_name=file_name
)
revision = await publish_demo(job, has_page=True)
import mimetypes
from pathlib import PurePath
from shared.services.storage.job_file_storage import JobFileStorage

# Slim runtime images need not include the operating system MIME registry.
mimetypes.init()
monkeypatch.delitem(mimetypes.types_map, PurePath(file_name).suffix.lower(), raising=False)
catalog = (await client.get("/api/v1/demo/catalog")).json()
source = next(item for item in catalog["sources"] if item["demo_source_id"] == "media-demo")
assert source["mime_type"] == original_mime
assert source["original_file"]["mime_type"] == original_mime

calls: list[dict[str, Any]] = []

def sign(key: str, **arguments: Any) -> str:
Expand All @@ -487,7 +514,7 @@ class SignedStorage:
JobFileStorage, "storage_adapter", property(lambda self: SignedStorage())
)
for path, mime in (
("original", "application/pdf"),
("original", original_mime),
("assets/images/rocket.png", "image/png"),
("assets/tables/launch.html", "text/html"),
):
Expand Down
Loading