fix(api): resolve description asset URLs to workspace scope without a project - #9776
fix(api): resolve description asset URLs to workspace scope without a project#9776seketman wants to merge 1 commit into
Conversation
… project `FileAsset.asset_url` always interpolated `project_id` into the URL for the description entity types, so an asset with no project resolved to `/api/assets/v2/workspaces/<slug>/projects/None/<id>/`, which 404s. Description assets are not always project-bound: `WorkspaceFileAssetEndpoint` never sets `project_id`, and workspace-level pages have no project at all (`Page` relates to projects through a M2M). Fall back to the workspace-scoped route in that case, mirroring the two branches `getEditorAssetSrc` already picks between on the client. Project-bound assets keep the project-scoped URL. Fixes makeplane#9564
◈ PR Lens
Architecture 1 component touched across 4 lanes. Data flow No data-flow sequence changed in this PR. Drill down
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthrough
ChangesFile asset URL routing
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to Description-image assets uploaded without a project now receive workspace-scoped URLs instead of URLs containing 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Description
FileAsset.asset_urlalways interpolatedproject_idinto the URL for the description entity types (ISSUE_DESCRIPTION,COMMENT_DESCRIPTION,PAGE_DESCRIPTION,DRAFT_ISSUE_DESCRIPTION), so an asset with no project resolved to/api/assets/v2/workspaces/<slug>/projects/None/<id>/, which 404s. The upload itself succeeds, so the file is in storage but the returned URL never resolves.Those assets are not always project-bound:
WorkspaceFileAssetEndpoint.get_entity_id_fieldmaps onlyissue_id/page_id/comment_id, neverproject_id, so the column stays NULL for assets created throughPOST /api/assets/v2/workspaces/{slug}/.Pagerelates to projects through a M2M (db.ProjectPage), so there is nothing to derive.This falls back to the workspace-scoped route when
project_idis NULL, mirroring the two branchesgetEditorAssetSrc(packages/utils/src/editor/common.ts) already picks between on the client. Project-bound assets keep the project-scoped URL, and every other branch of the property is untouched.Both routes already exist and both enforce access control (
has_project_asset_accessonWorkspaceFileAssetEndpoint), so this only makes the URL the API reports match the route the clients already use — it does not widen access.Approaches considered and rejected
project_idfrom the request body, as the public API'sGenericAssetEndpointdoes.WorkspaceFileAssetEndpointis authorized at workspace level, so accepting a client-suppliedproject_idthere would let any workspace member stamp an arbitrary project onto an asset.project_idfrom the related entity. Not possible for pages: workspace-level pages belong to zero projects.Note for maintainers (not addressed here)
ISSUE_ATTACHMENTinterpolatesproject_idthe same way, andDuplicateAssetEndpoint.postacceptsentity_typeandproject_idindependently, so an attachment can also end up with a NULLproject_id. I left it alone because there is no workspace-scoped attachment route to fall back to, so the right behaviour there is a design call rather than a mechanical fix. Happy to follow up in a separate PR if you want it covered.Type of Change
Screenshots and Media (if applicable)
N/A — the change is a URL-building fix in a model property.
Test Scenarios
New unit tests in
apps/api/plane/tests/unit/models/test_file_asset_url.py(9 cases, run withpytest plane/tests/unit/models/test_file_asset_url.py):project_idNULL resolves to/api/assets/v2/workspaces/{slug}/{id}/, and the URL contains noNone./api/assets/v2/workspaces/{slug}/projects/{project_id}/{id}/.WORKSPACE_LOGOstill resolves to the/api/assets/v2/static/{id}/route, guarding the branch above the change.Verified the tests fail without the fix (the 4 project-less cases go red with
projects/None/in the actual value) and pass with it.ruff checkandruff format --checkboth pass on the two files.References
Fixes #9564
Summary by CodeRabbit