Reject client-provided id, fix preview URLs, rename tags→total_tags

- Reject 'id' field in multipart upload with 400 UNSUPPORTED_FIELD
  instead of silently ignoring it
- Build preview URL from the preview asset's own metadata rather than
  the parent asset's
- Rename 'tags' to 'total_tags' in TagsAdd/TagsRemove response schemas
  for clarity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Luke Mino-Altherr
2026-03-12 16:29:51 -07:00
parent 70a9ba7495
commit fa51437eed
4 changed files with 17 additions and 10 deletions

View File

@@ -152,7 +152,14 @@ def _build_preview_url_from_view(tags: list[str], user_metadata: dict[str, Any]
def _build_asset_response(result: schemas.AssetDetailResult | schemas.UploadResult) -> schemas_out.Asset:
"""Build an Asset response from a service result."""
preview_url = _build_preview_url_from_view(result.tags, result.ref.user_metadata)
if result.ref.preview_id:
preview_detail = get_asset_detail(result.ref.preview_id)
if preview_detail:
preview_url = _build_preview_url_from_view(preview_detail.tags, preview_detail.ref.user_metadata)
else:
preview_url = None
else:
preview_url = _build_preview_url_from_view(result.tags, result.ref.user_metadata)
return schemas_out.Asset(
id=result.ref.id,
name=result.ref.name,
@@ -382,7 +389,6 @@ async def upload_asset(request: web.Request) -> web.Response:
"name": parsed.provided_name,
"user_metadata": parsed.user_metadata_raw,
"hash": parsed.provided_hash,
"id": parsed.provided_id,
"mime_type": parsed.provided_mime_type,
"preview_id": parsed.provided_preview_id,
}
@@ -605,7 +611,7 @@ async def add_asset_tags(request: web.Request) -> web.Response:
payload = schemas_out.TagsAdd(
added=result.added,
already_present=result.already_present,
tags=result.total_tags,
total_tags=result.total_tags,
)
except PermissionError as pe:
return _build_error_response(403, "FORBIDDEN", str(pe), {"id": reference_id})
@@ -652,7 +658,7 @@ async def delete_asset_tags(request: web.Request) -> web.Response:
payload = schemas_out.TagsRemove(
removed=result.removed,
not_present=result.not_present,
tags=result.total_tags,
total_tags=result.total_tags,
)
except PermissionError as pe:
return _build_error_response(403, "FORBIDDEN", str(pe), {"id": reference_id})

View File

@@ -45,7 +45,6 @@ class ParsedUpload:
user_metadata_raw: str | None
provided_hash: str | None
provided_hash_exists: bool | None
provided_id: str | None = None
provided_mime_type: str | None = None
provided_preview_id: str | None = None

View File

@@ -54,14 +54,14 @@ class TagsAdd(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
added: list[str] = Field(default_factory=list)
already_present: list[str] = Field(default_factory=list)
tags: list[str] = Field(default_factory=list)
total_tags: list[str] = Field(default_factory=list)
class TagsRemove(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
removed: list[str] = Field(default_factory=list)
not_present: list[str] = Field(default_factory=list)
tags: list[str] = Field(default_factory=list)
total_tags: list[str] = Field(default_factory=list)
class TagHistogram(BaseModel):

View File

@@ -52,7 +52,6 @@ async def parse_multipart_upload(
user_metadata_raw: str | None = None
provided_hash: str | None = None
provided_hash_exists: bool | None = None
provided_id: str | None = None
provided_mime_type: str | None = None
provided_preview_id: str | None = None
@@ -132,7 +131,11 @@ async def parse_multipart_upload(
elif fname == "user_metadata":
user_metadata_raw = (await field.text()) or None
elif fname == "id":
provided_id = ((await field.text()) or "").strip() or None
raise UploadError(
400,
"UNSUPPORTED_FIELD",
"Client-provided 'id' is not supported. Asset IDs are assigned by the server.",
)
elif fname == "mime_type":
provided_mime_type = ((await field.text()) or "").strip() or None
elif fname == "preview_id":
@@ -161,7 +164,6 @@ async def parse_multipart_upload(
user_metadata_raw=user_metadata_raw,
provided_hash=provided_hash,
provided_hash_exists=provided_hash_exists,
provided_id=provided_id,
provided_mime_type=provided_mime_type,
provided_preview_id=provided_preview_id,
)