plane icon indicating copy to clipboard operation
plane copied to clipboard

[WIKI-419] chore: new asset duplicate endpoint added

Open aaryan610 opened this issue 6 months ago • 2 comments

Description

This PR introduces a new endpoint to duplicate assets and attach them to an entiyt without the need to re-upload them.

Type of Change

  • [x] Improvement (change that would cause existing functionality to not work as expected)

Summary by CodeRabbit

  • New Features

    • Duplicate file assets via API and receive a new asset ID.
    • Per-asset rate limiting to reduce excessive duplicate requests.
    • Editor support for duplicating embedded assets (rich text/images) with retry UI on failure.
    • Paste handling now processes and deduplicates embedded assets; editor paste sanitization that removed img tags has been removed.
  • Bug Fixes

    • None.

aaryan610 avatar Jun 05 '25 11:06 aaryan610

[!NOTE]

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a workspace-scoped POST endpoint to duplicate uploaded assets (per-asset throttled), server logic to copy storage and create a new FileAsset, client-side service/store/hook wiring to call it, editor file-handler duplicate support, paste-time duplication handlers, and custom-image duplication lifecycle and UI retry controls.

Changes

Cohort / File(s) Change Summary
API route & export
apps/api/plane/app/urls/asset.py, apps/api/plane/app/views/__init__.py
Register DuplicateAssetEndpoint route at assets/v2/workspaces/<str:slug>/duplicate-assets/<uuid:asset_id>/ and export the endpoint from views.
API implementation & throttling
apps/api/plane/app/views/asset/v2.py, apps/api/plane/throttles/asset.py, apps/api/plane/settings/common.py
Add DuplicateAssetEndpoint (POST) that validates inputs, maps entity context to FK, copies storage object to a new key, creates a new FileAsset (uploaded=true) and returns new asset id; add AssetRateThrottle keyed by asset_id and register asset_id = 5/minute (and anon = 30/minute).
Web client — service & store
apps/web/core/services/file.service.ts, apps/web/core/store/editor/asset.store.ts, apps/web/core/hooks/store/use-editor-asset
Add fileService.duplicateAsset(...); expose duplicateEditorAsset in store/hook that calls the service and returns { asset_id }.
Web UI integrations
apps/web/core/components/**, apps/web/core/components/editor/rich-text/description-input/root.tsx, apps/web/core/components/inbox/modals/create-modal/issue-description.tsx, apps/web/core/components/issues/issue-detail/issue-activity/helper.tsx, apps/web/core/components/issues/issue-modal/components/description-editor.tsx, apps/web/app/.../page.tsx
Wire duplicateEditorAsset into editor/file handlers and RichTextEditor via new duplicateFile prop; handlers call duplication with workspace/project/entity params, propagate returned asset id, and surface consistent error messages on failure.
Editor paste plugin & helpers
packages/editor/src/ce/helpers/asset-duplication.ts, packages/editor/src/core/plugins/paste-asset.ts, packages/editor/src/core/extensions/utility.ts, packages/editor/src/core/props.ts
Add asset-duplication handlers (image handler injecting new UUID and STATUS=DUPLICATING), add PasteAssetPlugin to process pasted HTML via handlers and re-dispatch modified paste, register plugin in utility extension, and remove prior transformPastedHTML hook.
Custom-image types, utils, extension
packages/editor/src/core/extensions/custom-image/types.ts, .../utils.ts, .../extension.tsx
Add ECustomImageStatus enum (PENDING, UPLOADING, UPLOADED, DUPLICATING, DUPLICATION_FAILED), include STATUS in defaults, add helpers (isImageDuplicating, hasImageDuplicationFailed, etc.), and expose optional duplicateImage option from fileHandler.
Custom-image components (node-view / uploader / block)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx, .../uploader.tsx, .../block.tsx
Integrate duplication lifecycle: node-view triggers duplicateImage when status is DUPLICATING and updates attrs or sets DUPLICATION_FAILED; uploader accepts hasDuplicationFailed, shows Retry UI and blocks picker while failed; block component adjusts loader/toolbar/resizer visibility during duplication.
Editor types & props
packages/editor/src/core/types/config.ts, packages/editor/src/core/props.ts, packages/editor/src/core/extensions/utility.ts, packages/editor/src/core/plugins/paste-asset.ts
Add duplicate(assetId: string) => Promise<string> to TFileHandler; remove transformPastedHTML from CoreEditorProps; register PasteAssetPlugin() in utility plugins.
Site app fallback
apps/space/helpers/editor.helper.ts
Add a no-op duplicate(assetId) handler returning the same id as fallback (marked unsupported for sites/space).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Editor as Editor UI
    participant Store as EditorStore / FileService
    participant API as DuplicateAssetEndpoint
    participant Throttle as AssetRateThrottle
    participant DB as FileAsset DB
    participant Storage as ObjectStorage

    Editor->>Store: duplicateEditorAsset(assetId, entityType, ...)
    Store->>API: POST /assets/v2/workspaces/{slug}/duplicate-assets/{assetId}/ { entity_type, entity_id?, project_id? }
    API->>Throttle: get_cache_key(view.kwargs) -> "throttle_asset_{assetId}"
    Throttle-->>API: allow/deny
    API->>DB: GET original FileAsset(assetId)
    API->>Storage: copy original_key -> new_dest_key
    API->>DB: CREATE FileAsset (uploaded=true, linked FK)
    API-->>Store: 201 { asset_id: new_asset_id }
    Store-->>Editor: return { asset_id: new_asset_id }

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • Storage copy semantics and chosen destination key naming in DuplicateAssetEndpoint.post.
    • Correctness of entity-type → FK mapping in get_entity_id_field.
    • Throttle key logic in AssetRateThrottle.get_cache_key and matching DEFAULT_THROTTLE_RATES key.
    • Paste plugin HTML mutation, idempotency marking (data-uploaded) and re-dispatch behavior.
    • Concurrency guards and retry transitions in custom-image node-view/uploader (DUPLICATING → UPLOADED / DUPLICATION_FAILED).
    • Client-side error propagation and consistent UI messages across duplicate flows.

Poem

I’m a rabbit with a tiny hat,
I clone pixels where they sat.
Keys get twins and paste gets wise,
Throttle hums and small retries.
Hop again — the duplicate flies! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. While it mentions the feature (duplicating assets), it lacks required sections like Test Scenarios, Screenshots/Media, and References, and contains a typo ('entiyt'). Add Test Scenarios section describing how the duplication feature was tested, include any relevant Screenshots/Media, and add References linking to the related issue (WIKI-419). Fix the typo 'entiyt' to 'entity'.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a new asset duplicate endpoint, which is the core feature across all modified files.
✨ Finishing touches
  • [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment
  • [ ] Commit unit tests in branch chore/asset-duplicate-endpoint

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.

❤️ Share

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

coderabbitai[bot] avatar Jun 05 '25 11:06 coderabbitai[bot]

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

makeplane[bot] avatar Jun 05 '25 11:06 makeplane[bot]