Fix review issues: tags validation, size nullability, type annotation, hash mismatch check, and add tag histogram tests

- Remove contradictory min_length=1 from CreateFromHashBody.tags default
- Restore size field to int|None=None for proper null semantics
- Add Union type annotation to _build_asset_response result param
- Add hash mismatch validation on idempotent upload path (409 HASH_MISMATCH)
- Add unit tests for list_tag_histogram service function

Amp-Thread-ID: https://ampcode.com/threads/T-019cd993-f43c-704e-b3d7-6cfc3d4d4a80
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Luke Mino-Altherr
2026-03-10 14:23:02 -07:00
parent d115a4d343
commit 607a873af0
4 changed files with 136 additions and 4 deletions

View File

@@ -13,6 +13,7 @@ from pydantic import ValidationError
import folder_paths
from app import user_manager
from app.assets.api import schemas_in, schemas_out
from app.assets.services import schemas
from app.assets.api.schemas_in import (
AssetValidationError,
UploadError,
@@ -123,7 +124,7 @@ def _validate_sort_field(requested: str | None) -> str:
return "created_at"
def _build_asset_response(result) -> schemas_out.Asset:
def _build_asset_response(result: schemas.AssetDetailResult | schemas.UploadResult) -> schemas_out.Asset:
"""Build an Asset response from a service result."""
preview_url = None
if result.ref.preview_id:
@@ -132,7 +133,7 @@ def _build_asset_response(result) -> schemas_out.Asset:
id=result.ref.id,
name=result.ref.name,
asset_hash=result.asset.hash if result.asset else None,
size=int(result.asset.size_bytes) if result.asset else 0,
size=int(result.asset.size_bytes) if result.asset else None,
mime_type=result.asset.mime_type if result.asset else None,
tags=result.tags,
preview_url=preview_url,
@@ -386,6 +387,14 @@ async def upload_asset(request: web.Request) -> web.Response:
owner_id=owner_id,
)
if existing:
# Validate that uploaded content matches existing asset
if spec.hash and existing.asset and existing.asset.hash != spec.hash:
delete_temp_file_if_exists(parsed.tmp_path)
return _build_error_response(
409,
"HASH_MISMATCH",
"Uploaded file hash does not match existing asset.",
)
delete_temp_file_if_exists(parsed.tmp_path)
asset = _build_asset_response(existing)
payload_out = schemas_out.AssetCreated(

View File

@@ -121,7 +121,7 @@ class CreateFromHashBody(BaseModel):
hash: str
name: str | None = None
tags: list[str] = Field(default_factory=list, min_length=1)
tags: list[str] = Field(default_factory=list)
user_metadata: dict[str, Any] = Field(default_factory=dict)
mime_type: str | None = None

View File

@@ -8,7 +8,7 @@ class Asset(BaseModel):
id: str
name: str
asset_hash: str | None = None
size: int = 0
size: int | None = None
mime_type: str | None = None
tags: list[str] = Field(default_factory=list)
preview_url: str | None = None