Skip to content

feat: implement remaining listAssets contract fields (display_name, hash filter, include_public)#14690

Open
mattmillerai wants to merge 2 commits into
masterfrom
matt/be-1899-listassets-contract-fields
Open

feat: implement remaining listAssets contract fields (display_name, hash filter, include_public)#14690
mattmillerai wants to merge 2 commits into
masterfrom
matt/be-1899-listassets-contract-fields

Conversation

@mattmillerai

Copy link
Copy Markdown
Contributor

ELI-5

The list-assets endpoint (GET /api/assets) was missing three fields that the OpenAPI spec already promises. This adds them so the API matches the spec:

  • display_name — a copy of name in each asset, for older clients that expect that field.
  • hash — a query filter so you can ask "give me only the asset with exactly this content hash."
  • include_public — a query flag accepted for compatibility with the cloud API. Core has no public/shared asset pool, so it does nothing here; it's accepted (not rejected) so frontends don't need a special-case branch.

What changed

Brings GET /api/assets into param-for-param / field-for-field parity with the listAssets operation and Asset schema already projected into openapi.yaml. Cursor pagination (after/next_cursor) and the optional size field were already implemented and are untouched.

  • app/assets/api/schemas_out.py — add display_name: str | None to Asset (nullable, mirrors name). AssetCreated inherits it.
  • app/assets/api/routes.py — populate display_name=result.ref.name in _build_asset_response, which uniformly covers list, get, create, update, and upload responses.
  • app/assets/api/schemas_in.py — add hash (the spec's query-param name — not asset_hash, which remains the response-body field) and include_public: bool = True to ListAssetsQuery. A mode="before" validator normalizes hash with .strip().lower() to match stored lowercase blake3:<hex> values; malformed values are not rejected (the spec param is a plain string with no pattern), they simply yield an empty page.
  • app/assets/services/asset_management.py + app/assets/database/queries/asset_reference.py — thread asset_hash through list_assets_pagelist_references_page, applying Asset.hash == asset_hash to both the page and count statements (which already join Asset) so page rows and total stay consistent. include_public is intentionally not passed to the service layer — it is inert.

Tests

Added integration tests in tests-unit/assets_test/test_list_filter.py mirroring the existing http-session style:

  1. display_name is emitted and mirrors name.
  2. hash filter returns only the exact content-hash match (total == 1).
  3. A well-formed but unknown hash returns an empty page (assets == [], total == 0, 200).
  4. include_public=false and include_public=true both return 200 and still return the caller's own assets (parity, inert).

No openapi.yaml change is needed — the spec already declares all three; this PR closes the implementation gap against it. is_immutable, file_path, and short_url are out of scope.

Verification notes / judgment calls

  • Param named hash, not asset_hash: the parent ticket prose said asset_hash, but the projected openapi.yaml names the query param hash ("Filter assets by exact content hash."). The spec is the acceptance source of truth, so the query param is hash. The response-body field asset_hash is left as-is.
  • Local test execution: the assets integration suite boots a full ComfyUI server subprocess, which requires torch and Python ≥ 3.10. The build host here has only Python 3.9 and no torch, so the suite could not be executed locally — CI must exercise it. What was verified locally: ruff check passes on all changed files; py_compile passes; the surface was diffed param-for-param against openapi.yaml; all existing callers of the two touched query functions pass arguments by keyword (so inserting asset_hash mid-signature is safe); and Asset.hash is an existing column already used elsewhere in the same query module.

…ash filter, include_public)

Bring GET /api/assets into param-for-param parity with the projected
openapi.yaml listAssets contract for the three remaining fields:

- Add display_name to the Asset response schema (nullable, mirrors name)
  and populate it from ref.name in _build_asset_response, covering list,
  get, create, update, and upload responses uniformly.
- Add the hash query param to ListAssetsQuery (named hash per the spec,
  not asset_hash) with before-validation strip/lower normalization, and
  thread it through list_assets_page -> list_references_page, filtering
  both the page and count statements on Asset.hash for consistency.
- Accept include_public (bool, default true) for contract parity; it is
  inert in core (no public asset pool) and intentionally not passed to
  the service layer.

Cursor pagination and size optionality are untouched.

Add integration tests covering display_name mirroring, exact hash match,
unknown-hash empty page, and include_public acceptance.
@mattmillerai mattmillerai added cursor-review Trigger multi-model Cursor code review agent-coded labels Jun 30, 2026
@mattmillerai mattmillerai marked this pull request as ready for review June 30, 2026 09:22
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0bfcca45-5d83-461b-abf9-d916b9ad31dc

📥 Commits

Reviewing files that changed from the base of the PR and between 58df1a3 and 058692a.

📒 Files selected for processing (3)
  • app/assets/api/schemas_in.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/test_list_filter.py
📝 Walkthrough

Walkthrough

The pull request adds hash-based filtering to the GET /api/assets endpoint. ListAssetsQuery gains an optional hash field (with normalization) and an include_public boolean. This hash value is forwarded from the route through list_assets_page down to list_references_page, where it is applied as a WHERE clause on Asset.hash in both the paginated and count queries. The Asset output schema gains an optional display_name field populated with ref.name. Four unit tests cover the new display_name field, exact hash matching, unknown hash, and include_public acceptance.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change set: adding the remaining listAssets contract fields.
Description check ✅ Passed The description accurately and directly explains the API fields and filtering changes in this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests-unit/assets_test/test_list_filter.py (1)

329-367: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for hash normalization.

This PR adds lowercase/trim normalization in ListAssetsQuery._normalize_hash, but the new hash tests only cover already-normalized input. A case like " BLAKE3:... " would lock in the contract you just introduced.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests-unit/assets_test/test_list_filter.py` around lines 329 - 367, The new
hash filter tests in test_list_assets_hash_filter_exact_match and
test_list_assets_hash_filter_no_match only verify already-normalized values, so
they do not cover the normalization behavior added in
ListAssetsQuery._normalize_hash. Add a test in the same area that passes a hash
with uppercase letters and surrounding whitespace (for example a BLAKE3 value
with extra spaces) to /api/assets, then assert it matches the expected asset and
still returns 200. Keep the existing exact-match and no-match coverage, but
extend it to lock in the trim/lowercase normalization contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests-unit/assets_test/test_list_filter.py`:
- Around line 329-367: The new hash filter tests in
test_list_assets_hash_filter_exact_match and
test_list_assets_hash_filter_no_match only verify already-normalized values, so
they do not cover the normalization behavior added in
ListAssetsQuery._normalize_hash. Add a test in the same area that passes a hash
with uppercase letters and surrounding whitespace (for example a BLAKE3 value
with extra spaces) to /api/assets, then assert it matches the expected asset and
still returns 200. Keep the existing exact-match and no-match coverage, but
extend it to lock in the trim/lowercase normalization contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 106dd53a-aa03-4d25-9c89-da39cf12b954

📥 Commits

Reviewing files that changed from the base of the PR and between ba3f697 and 58df1a3.

📒 Files selected for processing (6)
  • app/assets/api/routes.py
  • app/assets/api/schemas_in.py
  • app/assets/api/schemas_out.py
  • app/assets/database/queries/asset_reference.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_list_filter.py

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 3 finding(s).

Severity Count
🟡 Medium 1
🟢 Low 2

Panel: 8/8 reviewers contributed findings.

Comment thread app/assets/api/schemas_in.py Outdated
Comment thread app/assets/api/schemas_in.py
Comment thread app/assets/database/queries/asset_reference.py
Address review findings on the listAssets contract PR:

- Empty `?hash=` no longer collapses to "no filter" (which returned a full
  unfiltered page); it now stays "" and is matched exactly, yielding an
  empty page — consistent with the documented "malformed -> empty page"
  contract. Omitting the param entirely still disables the filter.
- Guard `list_references_page` on `asset_hash is not None` (page + count)
  so the present-empty vs omitted distinction is preserved.
- Add tests for hash normalization (uppercase/whitespace) and for the
  explicit-empty -> empty-page behavior.
- Clarify the `include_public` comment: core reads are owner-scoped with no
  separate public pool, so the flag is inert here; cloud enforces it in its
  own service layer.
@mattmillerai mattmillerai force-pushed the matt/be-1899-listassets-contract-fields branch from 988d1db to 058692a Compare July 1, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded cursor-review Trigger multi-model Cursor code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant