ZCU-PUB/Add bitstream-level access status/embargo date REST endpoint - #1377
Conversation
Ports the intent of upstream commits ed02a60, 43e41e0, 6a229f1, and 0a0b575 (dataquest-dev/DSpace 9.x line) onto zcu-pub's DSpace 7.6.1 codebase. A raw cherry-pick was not viable: upstream uses java.time.LocalDate + jakarta.* and refactors the helper around a Pair<String, LocalDate>/AccessStatus object with a current-user-vs- anonymous calculation type, none of which match zcu-pub's simpler joda-time + javax.* shape. Instead, re-derive the minimal intent by generalizing the existing item-only helper methods to also accept a Bitstream, reusing the already-DSpaceObject-generic calculateAccessStatusForDso() and the bitstream-scoped retrieveShortestEmbargo(). - AccessStatusHelper / DefaultAccessStatusHelper / AccessStatusServiceImpl: add getAccessStatusFromBitstream / getEmbargoFromBitstream and their AccessStatusService-level counterparts. - AccessStatusRest: add the embargoDate field (previously only used at item level; zcu-pub's item-level link repository never actually populated it despite the service already computing it, so ItemAccessStatusLinkRepository now also sets it for consistency). - BitstreamRest: add the ACCESS_STATUS link. - New BitstreamAccessStatusLinkRepository, mirroring ItemAccessStatusLinkRepository and BitstreamFormatLinkRepository's existing METADATA_READ permission convention. - Tests: extend AccessStatusServiceTest, DefaultAccessStatusHelperTest, AccessStatusRestTest, and BitstreamRestRepositoryIT for the new bitstream-level behavior. Deliberately does not port the AccessStatus-object/Pair refactor or the current-user-vs-anonymous calculation type from the later upstream commits (6a229f1, 0a0b575) -- that's 9.x-specific complexity not present in zcu-pub's simpler anonymous-only model. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
|
Companion frontend PR: dataquest-dev/dspace-angular#1378 (the badge that consumes this endpoint). Tracking issue: dataquest-dev/dspace-customers#823. |
|
Sibling PR for the ZCU-DATA branch (same fix, same REST contract): #1380 |
There was a problem hiding this comment.
Pull request overview
Adds a bitstream-level accessStatus REST endpoint/link (parity with item-level) and extends the AccessStatusRest DTO to include an embargoDate, enabling the frontend to display a bitstream embargo badge with an end date.
Changes:
- Add
GET /api/core/bitstreams/{id}/accessStatusvia a newBitstreamAccessStatusLinkRepositoryand a newaccessStatuslink onBitstreamRest. - Extend
AccessStatusRestwith anembargoDatefield, and start populating it for both bitstreams (new) and items (scope expansion). - Add/adjust unit + integration tests to cover the new DTO field and new bitstream link behavior.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| dspace-server-webapp/src/test/java/org/dspace/app/rest/model/AccessStatusRestTest.java | Adds basic DTO tests for the new embargoDate field. |
| dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java | Updates expected default embeds/links to include accessStatus. |
| dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java | Adds IT coverage for bitstream /accessStatus (bad UUID, not-found/unauthorized, happy path). |
| dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java | Populates embargoDate on item access status responses when status is embargo. |
| dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java | New link repository exposing bitstream access status (and embargo date when applicable). |
| dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java | Adds ACCESS_STATUS link declaration on bitstreams. |
| dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AccessStatusRest.java | Adds embargoDate field + getters/setters and constructor initialization. |
| dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java | Extends helper tests to cover bitstream access status/embargo behavior. |
| dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java | Extends service tests to cover bitstream access status/embargo methods. |
| dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java | Adds getAccessStatus(Context, Bitstream) and getEmbargoFromBitstream(...) to the public service API. |
| dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java | Implements bitstream-level access status + embargo retrieval. |
| dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java | Wires new bitstream-level methods through to the helper using the configured threshold date. |
| dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java | Extends helper interface with bitstream-level access status + embargo retrieval methods. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
getEmbargoFromItem/getEmbargoFromBitstream returned Date#toString(), a locale/timezone-dependent format (e.g. "Tue Jan 01 00:00:00 UTC 2050"), instead of the plain ISO yyyy-MM-dd format used elsewhere in the REST API (see ResourcePolicyRest / ResourcePolicyRestRepositoryIT). Since this PR is what first exposes embargoDate over REST, format it consistently so the frontend (dspace-angular#1378) can reliably parse it. Updated DefaultAccessStatusHelperTest assertions to match. Also swap the EMBARGO.equals(status) comparisons in Item/BitstreamAccessStatusLinkRepository to be constant-first, so a null status from a custom AccessStatusHelper plugin can't NPE. Addresses Copilot review comments on PR #1377. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
getEmbargoFromItem/getEmbargoFromBitstream returned Date#toString(), a locale/timezone-dependent format (e.g. "Tue Jan 01 00:00:00 UTC 2050"), instead of the plain ISO yyyy-MM-dd format used elsewhere in the REST API (see ResourcePolicyRest / ResourcePolicyRestRepositoryIT). Since this PR is what first exposes embargoDate over REST, format it consistently so the frontend can reliably parse it. Updated DefaultAccessStatusHelperTest assertions to match. (Same fix as sibling PR #1377, which shares this code.) Also swap the EMBARGO.equals(status) comparisons in Item/BitstreamAccessStatusLinkRepository to be constant-first, so a null status from a custom AccessStatusHelper plugin can't NPE, as flagged directly by Copilot review on this PR. Addresses Copilot review comments on PR #1380. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…#1380) * Add bitstream-level access status/embargo date REST endpoint Ports the intent of upstream commits ed02a60, 43e41e0, 6a229f1, and 0a0b575 (dataquest-dev/DSpace 9.x line) onto zcu-pub's DSpace 7.6.1 codebase. A raw cherry-pick was not viable: upstream uses java.time.LocalDate + jakarta.* and refactors the helper around a Pair<String, LocalDate>/AccessStatus object with a current-user-vs- anonymous calculation type, none of which match zcu-pub's simpler joda-time + javax.* shape. Instead, re-derive the minimal intent by generalizing the existing item-only helper methods to also accept a Bitstream, reusing the already-DSpaceObject-generic calculateAccessStatusForDso() and the bitstream-scoped retrieveShortestEmbargo(). - AccessStatusHelper / DefaultAccessStatusHelper / AccessStatusServiceImpl: add getAccessStatusFromBitstream / getEmbargoFromBitstream and their AccessStatusService-level counterparts. - AccessStatusRest: add the embargoDate field (previously only used at item level; zcu-pub's item-level link repository never actually populated it despite the service already computing it, so ItemAccessStatusLinkRepository now also sets it for consistency). - BitstreamRest: add the ACCESS_STATUS link. - New BitstreamAccessStatusLinkRepository, mirroring ItemAccessStatusLinkRepository and BitstreamFormatLinkRepository's existing METADATA_READ permission convention. - Tests: extend AccessStatusServiceTest, DefaultAccessStatusHelperTest, AccessStatusRestTest, and BitstreamRestRepositoryIT for the new bitstream-level behavior. Deliberately does not port the AccessStatus-object/Pair refactor or the current-user-vs-anonymous calculation type from the later upstream commits (6a229f1, 0a0b575) -- that's 9.x-specific complexity not present in zcu-pub's simpler anonymous-only model. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Expose access status/embargo date on the metadatabitstream endpoint The ZCU-DATA Item View file listing doesn't use the standard /bitstreams REST resource (and its new accessStatus link from this same PR) at all - it goes through a separate, CLARIN-specific endpoint (metadatabitstreams/search/byHandle -> MetadataBitstreamWrapperRest) that returns a flat, lightweight DTO per file. Extend that DTO directly instead, reusing the same AccessStatusService methods added earlier in this PR. - MetadataBitstreamWrapperRest gains status/embargoDate fields. - MetadataBitstreamWrapperConverter populates them from the same Bitstream object it already has on hand, via AccessStatusService.getAccessStatus()/getEmbargoFromBitstream(). Wrapped in try/catch so a failure here degrades to no status rather than 500ing the whole file listing. Also fixes a real pre-existing bug surfaced while testing this against an embargoed bitstream: MetadataBitstreamRestRepository.findOutCanPreview() only caught MissingLicenseAgreementException, not the general AuthorizeException thrown when the requesting user (e.g. anonymous) lacks READ on the bitstream - e.g. exactly the embargo case. That propagated uncaught and 500'd the entire file listing for the item today, independent of this change. Fixed by catching it the same way as the license case (same "can't preview" outcome). Test evidence: mvn checkstyle:check -pl dspace-server-webapp -> BUILD SUCCESS mvn verify -pl dspace-server-webapp -DskipIntegrationTests=false -Dit.test=MetadataBitstreamRestRepositoryIT -> Tests run: 6, Failures: 0, Errors: 0 New test (findByHandleEmbargoedBitstream) exercises exactly the path that used to 500: an item with an embargoed ORIGINAL bitstream, requested anonymously. * Fix embargoDate REST format and null-safe EMBARGO comparison getEmbargoFromItem/getEmbargoFromBitstream returned Date#toString(), a locale/timezone-dependent format (e.g. "Tue Jan 01 00:00:00 UTC 2050"), instead of the plain ISO yyyy-MM-dd format used elsewhere in the REST API (see ResourcePolicyRest / ResourcePolicyRestRepositoryIT). Since this PR is what first exposes embargoDate over REST, format it consistently so the frontend can reliably parse it. Updated DefaultAccessStatusHelperTest assertions to match. (Same fix as sibling PR #1377, which shares this code.) Also swap the EMBARGO.equals(status) comparisons in Item/BitstreamAccessStatusLinkRepository to be constant-first, so a null status from a custom AccessStatusHelper plugin can't NPE, as flagged directly by Copilot review on this PR. Addresses Copilot review comments on PR #1380. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Problem
The bitstream-level embargo badge on the frontend (dataquest-dev/dspace-angular#1378) needs to know a restricted bitstream's embargo end date. On DSpace 9.x lines (e.g.
customer/mendelu) this is exposed viaGET /bitstreams/{id}/accessStatus; oncustomer/zcu-pub(DSpace 7.6.1) that endpoint doesn't exist — only the item-level equivalent does.Fixes the backend half of dataquest-dev/dspace-customers#823.
Root cause
Never ported: upstream introduced this in 4 commits on the 9.x line (
ed02a605,43e41e0f,6a229f15,0a0b5752), none of which landed on the 7.x/zcu-publine.Change set
A raw cherry-pick of those 4 commits was not viable — upstream 9.x uses
java.time.LocalDate+jakarta.*and a larger type-parameterized refactor (culminating in aPair<String, LocalDate>→AccessStatusobject replacement) that doesn't fit zcu-pub's simpler joda-time/javax.*/anonymous-only model. Re-derived the minimal intent instead, reusing zcu-pub's existing generalized helper methods (calculateAccessStatusForDso,retrieveShortestEmbargo) rather than duplicating them:dspace-api:AccessStatusHelper/DefaultAccessStatusHelper/AccessStatusServiceImpl/AccessStatusService— addedgetAccessStatusFromBitstream/getEmbargoFromBitstream.dspace-server-webapp:AccessStatusRestgains anembargoDatefield.BitstreamRestgains anACCESS_STATUSlink.BitstreamAccessStatusLinkRepository(mirrorsItemAccessStatusLinkRepository;@PreAuthorizeonMETADATA_READ, matchingBitstreamFormatLinkRepository's existing convention).ItemAccessStatusLinkRepositoryalso now populatesembargoDate(was status-only, even though the service could already compute it) — a small scope addition beyond strictly "bitstream," done for DTO consistency between the item- and bitstream-level endpoints. Easy to drop if you'd rather keep this PR strictly bitstream-scoped — flagging for review.GET /server/api/core/bitstreams/{id}/accessStatus->{status, embargoDate}— field names match what the frontend (PR CLARIN-DSpace v9/Port #1350 + #1347 + #1338 (DOI dedup, Shib special groups, tgz preview) to the v9 base #1378) expects.Out of scope: the 9.x-specific current-user-vs-anonymous status calculation and the
Pair→AccessStatusobject refactor (commits 2-4) — not applicable to zcu-pub's simpler anonymous-only model, so only their intent (bitstream embargo exposed via REST) was folded in, not their exact diffs.Test evidence
The integration test run surfaced two wrong assumptions in the tests themselves (not production code), both fixed:
@LinkResthas noembedOptional— every declared link auto-embeds, soaccessStatuslegitimately became a 5th default embed on bitstreams; updatedBitstreamMatcher.matchFullEmbeds()to expect it.METADATA_READ, andBitstreamMetadataReadPermissionEvaluatorPlugindenies with 401 (not 404) for a nonexistent bitstream — pre-existing platform behavior, differs from the item-level test; fixed the not-found test's expectation with a comment explaining why.Risk & rollback
Low risk: additive REST surface (new link + new field on an existing DTO), no changes to existing item-level behavior apart from populating a previously-unset field. Revert is a straight
git revertof this PR.Notes / assumptions
ItemAccessStatusLinkRepositoryembargoDate addition mentioned above — deliberate scope call, want a second opinion.