Skip to content

35023 task implement Bundle management Rest apis#35024

Open
hassandotcms wants to merge 6 commits intomainfrom
35023-task-implement-add-assets-to-bundle-rest-api
Open

35023 task implement Bundle management Rest apis#35024
hassandotcms wants to merge 6 commits intomainfrom
35023-task-implement-add-assets-to-bundle-rest-api

Conversation

@hassandotcms
Copy link
Copy Markdown
Member

New REST API endpoints for managing bundle assets, replacing legacy RemotePublishAjaxAction.addToBundle() and JSP-based asset deletion.

  • POST /v1/bundles/assets — Add assets to a bundle (resolve by ID → name → auto-create)
  • DELETE /v1/bundles/{bundleId}/assets — Remove assets from an unpushed bundle (bulk, per-asset results)

Details

  • BundleManagementResource with Lazy DI, WebResource auth, Swagger/OpenAPI annotations
  • Partial success model: permission errors reported per-asset, don't fail the request
  • DELETE guards against in-progress bundles (409) and validates asset existence in queue
  • Forms: AddAssetsToBundleForm (Validated), RemoveAssetsFromBundleForm (Immutables)
  • Views: AddAssetsToBundleView, RemoveAssetResultView (Immutables + ResponseEntityView wrappers)

Test plan

  • Integration tests cover: add by ID, by name, auto-create, empty/null validation (400)
  • Integration tests cover: single/multi removal, last-asset audit cleanup, not-found (404), in-progress (409), asset-not-in-bundle (success=false

Resolves #34325 #35023

Screenshots

Original Updated
** original screenshot ** ** updated screenshot **

…35023

- AddAssetsToBundleForm (extends Validated) with bundleId, bundleName, assetIds
- AbstractAddAssetsToBundleView (Immutables) with bundleId, bundleName, created, total, errors
- AbstractRemoveAssetsFromBundleForm (Immutables) with assetIds
- AbstractRemoveAssetResultView (Immutables) with assetId, success, message
- ResponseEntity wrappers for both endpoints
…35023

- POST /assets: existing bundle by ID, by name, auto-create, empty/null assetIds
- DELETE /{bundleId}/assets: single/multiple removal, last asset cleanup,
  bundle not found (404), in-progress (409), asset not in bundle (success=false)
- Register in MainSuite2b
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 17, 2026

Claude finished @hassandotcms's task in 2m 21s —— View job


PR Review

  • Gathering context
  • Reading changed files
  • Analyzing code
  • Posting review

Issues

1. DotPublisherException mapped to 400 BadRequestException — wrong HTTP status

BundleManagementResource.java:282-283:

} catch (DotPublisherException e) {
    throw new BadRequestException(e.getMessage());
}

DotPublisherException is thrown by the internal addAssetsToQueue for failures during asset analysis, DB errors, and unsupported asset types — not client errors. Mapping it to 400 will mislead callers into thinking they sent a bad request. This should be a 500, or at minimum distinguish between client-caused and server-caused failures.

Fix this →


2. No authorization check on bundle ownership

BundleManagementResource.java:180-187 and 378-383: Both endpoints only assert requiredBackendUser(true) — any authenticated backend user can add assets to or delete assets from any other user's bundle. There's no check that the requesting user owns or has rights to the target bundle. The legacy action gated this on the owner. At minimum this needs a note about whether this intentional behavior or an omission.


3. total field semantics are misleading in the response

AbstractAddAssetsToBundleView.java:67-73:

// doc says: "Total number of assets submitted for addition"
// actual: total comes from saveBundleAssets() result map, which is assets *successfully* added

addAssetsToQueue (per its own javadoc) sets total = number of assets added to the queue. When all assets are duplicates, total=0 even though N assets were submitted. The doc comment says "subtract errors count for successful adds" — this is doubly wrong: total doesn't include assets that errored OR assets that were pre-filtered as duplicates. Callers have no way to know how many of their submitted assets actually ended up in the bundle.


4. getUnsendBundlesByName uses likeName semantics with a cap of 1000

BundleManagementResource.java:206-210:

bundleAPI.get().getUnsendBundlesByName(user.getUserId(), form.getBundleName(), 1000, 0)
    .stream()
    .filter(b -> b.getName().equalsIgnoreCase(form.getBundleName()))
    .findFirst()
    ...

The underlying BundleAPI.getUnsendBundlesByName is a LIKE query (parameter is named likeName). Fetching 1000 results and then doing exact-match in Java is fragile: if the user has more than 1000 bundles whose names contain the target string, the exact match could be missed and a new bundle created silently. Use limit = -1 (documented as "no limit") or use a dedicated exact-name lookup if one exists.


5. TOCTOU race on in-progress status check for DELETE

BundleManagementResource.java:406-412: The in-progress guard and the actual asset deletion are not atomic. A bundle could transition to BUNDLING between the check and the deletion loop, resulting in modification of an in-progress bundle despite the guard. This is the same problem the guard is trying to prevent. Not trivially fixable without a DB-level lock, but worth documenting the known limitation.


6. Individual asset IDs not validated

BundleManagementResource.java:199-236: form.getAssetIds() is checked for null/empty at the list level but individual entries are not validated. Empty strings or whitespace-only values pass through to saveBundleAssets / the duplicate check set. At minimum, blank entries should be rejected or filtered before being processed.


7. Flaky permission test

BundleManagementResourceIntegrationTest.java:212-229: test_addAssets_nonAdminNoPublishPermission_reportsPerAssetErrors creates a contentlet with ContentletDataGen and assumes contributorUser (Joe Contributor) lacks publish permission on it. Whether that's true depends on how the test environment configures default permissions for the content type. If testContentType is created with permissive defaults or the contributor role has inherited publish rights, errors will be empty and the test fails. The test should explicitly deny publish permission to the contributor user on the created contentlet to make this deterministic.


8. Minor: PublishingJobsHelper taken as a constructor dependency solely for isInProgressStatus()

BundleManagementResource.java:66, 409: The entire PublishingJobsHelper (which pulls in BundleAPI, PublisherAPI, PublishAuditUtil, EnvironmentAPI, PublishingEndPointAPI, PermissionAPI) is injected just to call isInProgressStatus(), which is a trivial static set membership check. Either expose IN_PROGRESS_STATUSES as a public constant and inline the check, or extract a narrower interface.

@hassandotcms hassandotcms marked this pull request as ready for review March 17, 2026 23:05
…-implement-add-assets-to-bundle-rest-api
… in addAssetsToBundle

PublisherAPIImpl.addAssetsToQueue() throws AssetAlreadyLinkWithBundleException
for duplicates, which rolls back the @WrapInTransaction and fails the entire
batch. Pre-filter at the resource layer by reading existing assets from
publishing_queue (same table/column the API checks) and passing only new
assets to saveBundleAssets. Duplicates are silently skipped (idempotent).

Also fixes test_addAssets_existingBundleByName where BundleDataGen set
publishDate=new Date(), making bundles invisible to getUnsendBundlesByName.
Adds non-admin permission test and duplicate-skip tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[TASK] Implement Add Assets to Bundle Rest API [TASK] Implement DELETE /v1/bundles/{bundleId}/assets - Remove assets from unpushed bundle

1 participant