From e0cfb13c8bbd31a10d16794f01cf546fb5bff67c Mon Sep 17 00:00:00 2001 From: Matus Beke Date: Mon, 20 Jul 2026 07:58:07 +0200 Subject: [PATCH 1/3] Add bitstream-level access status/embargo date REST endpoint Ports the intent of upstream commits ed02a605, 43e41e0f, 6a229f15, and 0a0b5752 (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/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 (6a229f15, 0a0b5752) -- that's 9.x-specific complexity not present in zcu-pub's simpler anonymous-only model. Co-Authored-By: Claude Sonnet 5 --- .../access/status/AccessStatusHelper.java | 24 +++++++ .../status/AccessStatusServiceImpl.java | 11 +++ .../status/DefaultAccessStatusHelper.java | 57 +++++++++++++++- .../status/service/AccessStatusService.java | 21 ++++++ .../status/AccessStatusServiceTest.java | 52 ++++++++++++++ .../status/DefaultAccessStatusHelperTest.java | 36 +++++++++- .../app/rest/model/AccessStatusRest.java | 11 +++ .../dspace/app/rest/model/BitstreamRest.java | 5 ++ .../BitstreamAccessStatusLinkRepository.java | 67 +++++++++++++++++++ .../ItemAccessStatusLinkRepository.java | 5 ++ .../app/rest/BitstreamRestRepositoryIT.java | 48 +++++++++++++ .../app/rest/matcher/BitstreamMatcher.java | 6 +- .../app/rest/model/AccessStatusRestTest.java | 13 +++- 13 files changed, 349 insertions(+), 7 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java index 2d782dc3b82..080033c6caa 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusHelper.java @@ -10,6 +10,7 @@ import java.sql.SQLException; import java.util.Date; +import org.dspace.content.Bitstream; import org.dspace.content.Item; import org.dspace.core.Context; @@ -39,4 +40,27 @@ public String getAccessStatusFromItem(Context context, Item item, Date threshold * @throws SQLException An exception that provides information on a database access error or other errors. */ public String getEmbargoFromItem(Context context, Item item, Date threshold) throws SQLException; + + /** + * Calculate the access status for the bitstream. + * + * @param context the DSpace context + * @param bitstream the bitstream + * @param threshold the embargo threshold date + * @return an access status value + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public String getAccessStatusFromBitstream(Context context, Bitstream bitstream, Date threshold) + throws SQLException; + + /** + * Retrieve embargo information for the bitstream + * + * @param context the DSpace context + * @param bitstream the bitstream to check for embargo information + * @param threshold the embargo threshold date + * @return an embargo date + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public String getEmbargoFromBitstream(Context context, Bitstream bitstream, Date threshold) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java index e1f11285d84..977e7c2e4a4 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/access/status/AccessStatusServiceImpl.java @@ -11,6 +11,7 @@ import java.util.Date; import org.dspace.access.status.service.AccessStatusService; +import org.dspace.content.Bitstream; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.core.service.PluginService; @@ -68,4 +69,14 @@ public String getAccessStatus(Context context, Item item) throws SQLException { public String getEmbargoFromItem(Context context, Item item) throws SQLException { return helper.getEmbargoFromItem(context, item, forever_date); } + + @Override + public String getAccessStatus(Context context, Bitstream bitstream) throws SQLException { + return helper.getAccessStatusFromBitstream(context, bitstream, forever_date); + } + + @Override + public String getEmbargoFromBitstream(Context context, Bitstream bitstream) throws SQLException { + return helper.getEmbargoFromBitstream(context, bitstream, forever_date); + } } diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index 5f0e6d8b259..2355e25912a 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -59,7 +59,7 @@ public DefaultAccessStatusHelper() { } /** - * Look at the item's policies to determine an access status value. + * Look at the item policies to determine an access status value. * It is also considering a date threshold for embargoes and restrictions. * * If the item is null, simply returns the "unknown" value. @@ -96,7 +96,7 @@ public String getAccessStatusFromItem(Context context, Item item, Date threshold } /** - * Look at the DSpace object's policies to determine an access status value. + * Look at the DSpace object policies to determine an access status value. * * If the object is null, returns the "metadata.only" value. * If any policy attached to the object is valid for the anonymous group, @@ -170,7 +170,8 @@ private String calculateAccessStatusForDso(Context context, DSpaceObject dso, Da * * @param context the DSpace context * @param item the item to embargo - * @return an access status value + * @param threshold the embargo threshold date + * @return an embargo date */ @Override public String getEmbargoFromItem(Context context, Item item, Date threshold) @@ -211,7 +212,57 @@ public String getEmbargoFromItem(Context context, Item item, Date threshold) } /** + * Look at the policies attached directly to the bitstream to determine an access status value. + * It is also considering a date threshold for embargoes and restrictions. + * + * If the bitstream is null, simply returns the "unknown" value. + * + * @param context the DSpace context + * @param bitstream the bitstream to check for embargoes + * @param threshold the embargo threshold date + * @return an access status value + */ + @Override + public String getAccessStatusFromBitstream(Context context, Bitstream bitstream, Date threshold) + throws SQLException { + if (bitstream == null) { + return UNKNOWN; + } + return calculateAccessStatusForDso(context, bitstream, threshold); + } + + /** + * Look at the policies of the bitstream to retrieve its embargo. + * + * If the bitstream is null, simply returns no embargo date. + * + * @param context the DSpace context + * @param bitstream the bitstream to embargo + * @param threshold the embargo threshold date + * @return an embargo date + */ + @Override + public String getEmbargoFromBitstream(Context context, Bitstream bitstream, Date threshold) + throws SQLException { + if (bitstream == null) { + return null; + } + // If Bitstream status is not "embargo" then return a null embargo date. + String accessStatus = getAccessStatusFromBitstream(context, bitstream, threshold); + if (!accessStatus.equals(EMBARGO)) { + return null; + } + Date embargoDate = this.retrieveShortestEmbargo(context, bitstream); + + return embargoDate != null ? embargoDate.toString() : null; + } + + /** + * Look at the read policies of a bitstream to retrieve the shortest active embargo date. * + * @param context the DSpace context + * @param bitstream the bitstream + * @return the shortest embargo date, or null if there is none */ private Date retrieveShortestEmbargo(Context context, Bitstream bitstream) throws SQLException { Date embargoDate = null; diff --git a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java index 2ed47bde4cd..e4bc88f0eb5 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java +++ b/dspace-api/src/main/java/org/dspace/access/status/service/AccessStatusService.java @@ -9,6 +9,7 @@ import java.sql.SQLException; +import org.dspace.content.Bitstream; import org.dspace.content.Item; import org.dspace.core.Context; @@ -54,4 +55,24 @@ public interface AccessStatusService { * @throws SQLException An exception that provides information on a database access error or other errors. */ public String getEmbargoFromItem(Context context, Item item) throws SQLException; + + /** + * Calculate the access status for a Bitstream while considering the forever embargo date threshold. + * + * @param context the DSpace context + * @param bitstream the bitstream + * @return an access status value + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public String getAccessStatus(Context context, Bitstream bitstream) throws SQLException; + + /** + * Retrieve embargo information for the bitstream + * + * @param context the DSpace context + * @param bitstream the bitstream to check for embargo information + * @return an embargo date + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public String getEmbargoFromBitstream(Context context, Bitstream bitstream) throws SQLException; } diff --git a/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java b/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java index 87127f9cf8f..8aa46820352 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/AccessStatusServiceTest.java @@ -8,8 +8,12 @@ package org.dspace.access.status; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.sql.SQLException; import org.apache.logging.log4j.Logger; @@ -17,15 +21,20 @@ import org.dspace.access.status.factory.AccessStatusServiceFactory; import org.dspace.access.status.service.AccessStatusService; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.BitstreamService; +import org.dspace.content.service.BundleService; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; +import org.dspace.core.Constants; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -40,6 +49,8 @@ public class AccessStatusServiceTest extends AbstractUnitTest { private Collection collection; private Community owningCommunity; private Item item; + private Bundle bundle; + private Bitstream bitstream; protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); @@ -47,6 +58,10 @@ public class AccessStatusServiceTest extends AbstractUnitTest { ContentServiceFactory.getInstance().getCollectionService(); protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + protected BundleService bundleService = + ContentServiceFactory.getInstance().getBundleService(); + protected BitstreamService bitstreamService = + ContentServiceFactory.getInstance().getBitstreamService(); protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); protected InstallItemService installItemService = @@ -71,6 +86,10 @@ public void init() { collection = collectionService.create(context, owningCommunity); item = installItemService.installItem(context, workspaceItemService.create(context, collection, true)); + bundle = bundleService.create(context, item, Constants.CONTENT_BUNDLE_NAME); + bitstream = bitstreamService.create(context, bundle, + new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8))); + bitstream.setName(context, "primary"); context.restoreAuthSystemState(); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); @@ -78,6 +97,9 @@ public void init() { } catch (SQLException ex) { log.error("SQL Error in init", ex); fail("SQL Error in init: " + ex.getMessage()); + } catch (IOException ex) { + log.error("IO Error in init", ex); + fail("IO Error in init: " + ex.getMessage()); } } @@ -92,6 +114,16 @@ public void init() { @Override public void destroy() { context.turnOffAuthorisationSystem(); + try { + bitstreamService.delete(context, bitstream); + } catch (Exception e) { + // ignore + } + try { + bundleService.delete(context, bundle); + } catch (Exception e) { + // ignore + } try { itemService.delete(context, item); } catch (Exception e) { @@ -108,6 +140,8 @@ public void destroy() { // ignore } context.restoreAuthSystemState(); + bitstream = null; + bundle = null; item = null; collection = null; owningCommunity = null; @@ -123,4 +157,22 @@ public void testGetAccessStatus() throws Exception { String status = accessStatusService.getAccessStatus(context, item); assertNotEquals("testGetAccessStatus 0", status, DefaultAccessStatusHelper.UNKNOWN); } + + @Test + public void testGetEmbargoFromItem() throws Exception { + String embargo = accessStatusService.getEmbargoFromItem(context, item); + assertNull("testGetEmbargoFromItem 0", embargo); + } + + @Test + public void testGetAccessStatusFromBitstream() throws Exception { + String status = accessStatusService.getAccessStatus(context, bitstream); + assertNotEquals("testGetAccessStatusFromBitstream 0", status, DefaultAccessStatusHelper.UNKNOWN); + } + + @Test + public void testGetEmbargoFromBitstream() throws Exception { + String embargo = accessStatusService.getEmbargoFromBitstream(context, bitstream); + assertNull("testGetEmbargoFromBitstream 0", embargo); + } } diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java index 1134990e84f..999cba057a3 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java @@ -9,6 +9,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; @@ -208,6 +209,18 @@ public void testWithNullItem() throws Exception { assertThat("testWithNullItem 0", status, equalTo(DefaultAccessStatusHelper.UNKNOWN)); } + /** + * Test for a null bitstream + * @throws java.lang.Exception passed through. + */ + @Test + public void testWithNullBitstream() throws Exception { + String status = helper.getAccessStatusFromBitstream(context, null, threshold); + assertThat("testWithNullBitstream 0", status, equalTo(DefaultAccessStatusHelper.UNKNOWN)); + String embargoDate = helper.getEmbargoFromBitstream(context, null, threshold); + assertNull("testWithNullBitstream 1", embargoDate); + } + /** * Test for an item with no bundle * @throws java.lang.Exception passed through. @@ -246,6 +259,10 @@ public void testWithBitstream() throws Exception { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithBitstream, threshold); assertThat("testWithBitstream 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); + String bitstreamStatus = helper.getAccessStatusFromBitstream(context, bitstream, threshold); + assertThat("testWithBitstream 1", bitstreamStatus, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); + String bitstreamEmbargoDate = helper.getEmbargoFromBitstream(context, bitstream, threshold); + assertNull("testWithBitstream 2", bitstreamEmbargoDate); } /** @@ -275,6 +292,10 @@ public void testWithEmbargo() throws Exception { assertThat("testWithEmbargo 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo, threshold); assertThat("testWithEmbargo 1", embargoDate, equalTo(policy.getStartDate().toString())); + String bitstreamStatus = helper.getAccessStatusFromBitstream(context, bitstream, threshold); + assertThat("testWithEmbargo 2", bitstreamStatus, equalTo(DefaultAccessStatusHelper.EMBARGO)); + String bitstreamEmbargoDate = helper.getEmbargoFromBitstream(context, bitstream, threshold); + assertThat("testWithEmbargo 3", bitstreamEmbargoDate, equalTo(policy.getStartDate().toString())); } /** @@ -302,6 +323,10 @@ public void testWithDateRestriction() throws Exception { context.restoreAuthSystemState(); String status = helper.getAccessStatusFromItem(context, itemWithDateRestriction, threshold); assertThat("testWithDateRestriction 0", status, equalTo(DefaultAccessStatusHelper.RESTRICTED)); + String bitstreamStatus = helper.getAccessStatusFromBitstream(context, bitstream, threshold); + assertThat("testWithDateRestriction 1", bitstreamStatus, equalTo(DefaultAccessStatusHelper.RESTRICTED)); + String bitstreamEmbargoDate = helper.getEmbargoFromBitstream(context, bitstream, threshold); + assertNull("testWithDateRestriction 2", bitstreamEmbargoDate); } /** @@ -374,7 +399,7 @@ public void testWithPrimaryAndMultipleBitstreams() throws Exception { context.turnOffAuthorisationSystem(); Bundle bundle = bundleService.create(context, itemWithPrimaryAndMultipleBitstreams, Constants.CONTENT_BUNDLE_NAME); - bitstreamService.create(context, bundle, + Bitstream otherBitstream = bitstreamService.create(context, bundle, new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8))); Bitstream primaryBitstream = bitstreamService.create(context, bundle, new ByteArrayInputStream("1".getBytes(StandardCharsets.UTF_8))); @@ -394,6 +419,12 @@ public void testWithPrimaryAndMultipleBitstreams() throws Exception { assertThat("testWithPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); String embargoDate = helper.getEmbargoFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold); assertThat("testWithPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(policy.getStartDate().toString())); + String primaryBitstreamStatus = helper.getAccessStatusFromBitstream(context, primaryBitstream, threshold); + assertThat("testWithPrimaryAndMultipleBitstreams 2", primaryBitstreamStatus, + equalTo(DefaultAccessStatusHelper.EMBARGO)); + String otherBitstreamStatus = helper.getAccessStatusFromBitstream(context, otherBitstream, threshold); + assertThat("testWithPrimaryAndMultipleBitstreams 3", otherBitstreamStatus, + equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); } /** @@ -425,5 +456,8 @@ public void testWithNoPrimaryAndMultipleBitstreams() throws Exception { assertThat("testWithNoPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.OPEN_ACCESS)); String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo, threshold); assertThat("testWithNoPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(null)); + String otherBitstreamStatus = helper.getAccessStatusFromBitstream(context, anotherBitstream, threshold); + assertThat("testWithNoPrimaryAndMultipleBitstreams 2", otherBitstreamStatus, + equalTo(DefaultAccessStatusHelper.EMBARGO)); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AccessStatusRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AccessStatusRest.java index c7dc2d11985..a2b82b8fc5a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AccessStatusRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AccessStatusRest.java @@ -18,6 +18,7 @@ public class AccessStatusRest implements RestModel { public static final String NAME = "accessStatus"; String status; + String embargoDate; @Override @JsonProperty(access = Access.READ_ONLY) @@ -33,10 +34,12 @@ public String getTypePlural() { public AccessStatusRest() { setStatus(null); + setEmbargoDate(null); } public AccessStatusRest(String status) { setStatus(status); + setEmbargoDate(null); } public String getStatus() { @@ -46,4 +49,12 @@ public String getStatus() { public void setStatus(String status) { this.status = status; } + + public String getEmbargoDate() { + return embargoDate; + } + + public void setEmbargoDate(String embargoDate) { + this.embargoDate = embargoDate; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java index a1c3156a01a..3f64acfb1c1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java @@ -31,6 +31,10 @@ @LinkRest( name = BitstreamRest.CHECKSUM, method = "getChecksum" + ), + @LinkRest( + name = BitstreamRest.ACCESS_STATUS, + method = "getAccessStatus" ) }) public class BitstreamRest extends DSpaceObjectRest { @@ -42,6 +46,7 @@ public class BitstreamRest extends DSpaceObjectRest { public static final String FORMAT = "format"; public static final String THUMBNAIL = "thumbnail"; public static final String CHECKSUM = "checksum"; + public static final String ACCESS_STATUS = "accessStatus"; private String bundleName; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java new file mode 100644 index 00000000000..90661a57a57 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java @@ -0,0 +1,67 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import java.util.UUID; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.access.status.DefaultAccessStatusHelper; +import org.dspace.access.status.service.AccessStatusService; +import org.dspace.app.rest.model.AccessStatusRest; +import org.dspace.app.rest.model.BitstreamRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.Bitstream; +import org.dspace.content.service.BitstreamService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * Link repository for calculating the access status of a Bitstream, + * including the embargo date. + */ +@Component(BitstreamRest.CATEGORY + "." + BitstreamRest.NAME + "." + BitstreamRest.ACCESS_STATUS) +public class BitstreamAccessStatusLinkRepository extends AbstractDSpaceRestRepository + implements LinkRestRepository { + + @Autowired + BitstreamService bitstreamService; + + @Autowired + AccessStatusService accessStatusService; + + @PreAuthorize("hasPermission(#bitstreamId, 'BITSTREAM', 'METADATA_READ')") + public AccessStatusRest getAccessStatus(@Nullable HttpServletRequest request, + UUID bitstreamId, + @Nullable Pageable optionalPageable, + Projection projection) { + try { + Context context = obtainContext(); + Bitstream bitstream = bitstreamService.find(context, bitstreamId); + if (bitstream == null) { + throw new ResourceNotFoundException("No such bitstream: " + bitstreamId); + } + AccessStatusRest accessStatusRest = new AccessStatusRest(); + String status = accessStatusService.getAccessStatus(context, bitstream); + if (status.equals(DefaultAccessStatusHelper.EMBARGO)) { + String embargoDate = accessStatusService.getEmbargoFromBitstream(context, bitstream); + accessStatusRest.setEmbargoDate(embargoDate); + } + accessStatusRest.setStatus(status); + return accessStatusRest; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java index b2660f51e09..30a73f64ead 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java @@ -13,6 +13,7 @@ import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; +import org.dspace.access.status.DefaultAccessStatusHelper; import org.dspace.access.status.service.AccessStatusService; import org.dspace.app.rest.model.AccessStatusRest; import org.dspace.app.rest.model.ItemRest; @@ -52,6 +53,10 @@ public AccessStatusRest getAccessStatus(@Nullable HttpServletRequest request, } AccessStatusRest accessStatusRest = new AccessStatusRest(); String accessStatus = accessStatusService.getAccessStatus(context, item); + if (accessStatus.equals(DefaultAccessStatusHelper.EMBARGO)) { + String embargoDate = accessStatusService.getEmbargoFromItem(context, item); + accessStatusRest.setEmbargoDate(embargoDate); + } accessStatusRest.setStatus(accessStatus); return accessStatusRest; } catch (SQLException e) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java index 60379713d4f..187b56fc1d5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -33,6 +33,7 @@ import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; +import org.dspace.access.status.DefaultAccessStatusHelper; import org.dspace.app.rest.matcher.BitstreamFormatMatcher; import org.dspace.app.rest.matcher.BitstreamMatcher; import org.dspace.app.rest.matcher.BundleMatcher; @@ -2929,6 +2930,53 @@ public void deleteBitstreamsInBulk_communityAdmin() throws Exception { .andExpect(status().isNoContent()); } + @Test + public void findAccessStatusForBitstreamBadRequestTest() throws Exception { + getClient().perform(get("/api/core/bitstreams/{uuid}/accessStatus", "1")) + .andExpect(status().isBadRequest()); + } + + @Test + public void findAccessStatusForBitstreamNotFoundTest() throws Exception { + // Unlike the item-level accessStatus link (gated on plain 'READ'), the bitstream-level link is + // gated on 'METADATA_READ' (see BitstreamMetadataReadPermissionEvaluatorPlugin), whose evaluator + // denies permission outright when the target bitstream cannot be resolved, so an anonymous request + // never reaches the controller's not-found check and instead gets a 401. + UUID fakeUUID = UUID.randomUUID(); + getClient().perform(get("/api/core/bitstreams/{uuid}/accessStatus", fakeUUID)) + .andExpect(status().isUnauthorized()); + } + + @Test + public void findAccessStatusForBitstreamTest() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection 1") + .build(); + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Test item 1") + .build(); + String bitstreamContent = "ThisIsSomeDummyText"; + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publicItem1, is) + .withName("Bitstream") + .withDescription("Description") + .withMimeType("text/plain") + .build(); + } + context.restoreAuthSystemState(); + + // Bitstream access status should still be accessible by anonymous request + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/accessStatus")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.type", is("accessStatus"))) + .andExpect(jsonPath("$.status", is(DefaultAccessStatusHelper.OPEN_ACCESS))); + } + public boolean bitstreamExists(String token, Bitstream ...bitstreams) throws Exception { for (Bitstream bitstream : bitstreams) { if (getClient(token).perform(get("/api/core/bitstreams/" + bitstream.getID())) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java index 9c9c0513c18..97746e75140 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/BitstreamMatcher.java @@ -103,7 +103,8 @@ public static Matcher matchFullEmbeds() { "bundle", "format", "thumbnail", - "checksum" + "checksum", + "accessStatus" ); } @@ -117,7 +118,8 @@ public static Matcher matchLinks(UUID uuid) { "format", "self", "thumbnail", - "checksum" + "checksum", + "accessStatus" ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/model/AccessStatusRestTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/model/AccessStatusRestTest.java index 7dfe3e69e0e..471e5d3704b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/model/AccessStatusRestTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/model/AccessStatusRestTest.java @@ -15,7 +15,7 @@ import org.junit.Test; /** - * Test the AccessStatusRestTest class + * Test the AccessStatusRest class */ public class AccessStatusRestTest { @@ -36,4 +36,15 @@ public void testAccessStatusIsNotNullAfterStatusSet() throws Exception { accessStatusRest.setStatus(DefaultAccessStatusHelper.UNKNOWN); assertNotNull(accessStatusRest.getStatus()); } + + @Test + public void testEmbargoDateIsNullBeforeEmbargoDateSet() throws Exception { + assertNull(accessStatusRest.getEmbargoDate()); + } + + @Test + public void testEmbargoDateIsNotNullAfterEmbargoDateSet() throws Exception { + accessStatusRest.setEmbargoDate("2050-01-01"); + assertNotNull(accessStatusRest.getEmbargoDate()); + } } From 4b4ce9fdffc7e3fff9afe4b8326d7c7c602e4062 Mon Sep 17 00:00:00 2001 From: Matus Beke Date: Mon, 20 Jul 2026 14:40:40 +0200 Subject: [PATCH 2/3] 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. --- .../MetadataBitstreamWrapperConverter.java | 34 +++++++++++++++++++ .../model/MetadataBitstreamWrapperRest.java | 18 ++++++++++ .../MetadataBitstreamRestRepository.java | 5 +++ .../MetadataBitstreamRestRepositoryIT.java | 32 ++++++++++++++++- 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java index e42c023f72a..09551b84389 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataBitstreamWrapperConverter.java @@ -7,9 +7,15 @@ */ package org.dspace.app.rest.converter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.access.status.DefaultAccessStatusHelper; +import org.dspace.access.status.service.AccessStatusService; import org.dspace.app.rest.model.MetadataBitstreamWrapperRest; import org.dspace.app.rest.model.wrapper.MetadataBitstreamWrapper; import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.core.Context; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -24,6 +30,8 @@ public class MetadataBitstreamWrapperConverter implements DSpaceConverter { + private static final Logger log = LogManager.getLogger(MetadataBitstreamWrapperConverter.class); + @Lazy @Autowired private ConverterService converter; @@ -32,6 +40,9 @@ public class MetadataBitstreamWrapperConverter implements DSpaceConverter getModelClass() { return MetadataBitstreamWrapper.class; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java index 8e4d60d4bfa..9bfb77590f7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/MetadataBitstreamWrapperRest.java @@ -29,6 +29,8 @@ public class MetadataBitstreamWrapperRest extends BaseObjectRest { private String format; private String href; private boolean canPreview; + private String status; + private String embargoDate; public MetadataBitstreamWrapperRest(String name, String description, long fileSize, String checksum, List fileInfo, String format, String href, boolean canPreview) { @@ -109,6 +111,22 @@ public void setChecksum(String checksum) { this.checksum = checksum; } + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getEmbargoDate() { + return embargoDate; + } + + public void setEmbargoDate(String embargoDate) { + this.embargoDate = embargoDate; + } + @Override public String getCategory() { return CATEGORY; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 4778cec279e..e6c85514a9d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -419,6 +419,11 @@ private boolean findOutCanPreview(Context context, Bitstream bitstream) throws S return true; } catch (MissingLicenseAgreementException e) { return false; + } catch (AuthorizeException e) { + // The requesting user (e.g. anonymous) doesn't have READ on this bitstream at all - + // e.g. it's under embargo. Same "can't preview" outcome as the license case above; + // this used to propagate uncaught and 500 the entire file listing for the item. + return false; } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index c966ecfa58e..a14e75a1c0d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -10,6 +10,7 @@ import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -22,6 +23,7 @@ import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.dspace.access.status.DefaultAccessStatusHelper; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.app.util.Util; import org.dspace.authorize.service.AuthorizeService; @@ -124,9 +126,37 @@ public void findByHandle() throws Exception { .andExpect(jsonPath("$._embedded.metadatabitstreams[*].checksum") .value(Matchers.containsInAnyOrder(Matchers.containsString(bts.getChecksum())))) .andExpect(jsonPath("$._embedded.metadatabitstreams[*].href") - .value(Matchers.containsInAnyOrder(Matchers.containsString(url)))); + .value(Matchers.containsInAnyOrder(Matchers.containsString(url)))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[*].status") + .value(Matchers.containsInAnyOrder(DefaultAccessStatusHelper.OPEN_ACCESS))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[0].embargoDate").value(nullValue())); + + + } + @Test + public void findByHandleEmbargoedBitstream() throws Exception { + context.turnOffAuthorisationSystem(); + Bitstream embargoedBitstream; + String bitstreamContent = "ThisIsSomeEmbargoedText"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + embargoedBitstream = BitstreamBuilder.createBitstream(context, publicItem, is) + .withName("Embargoed Bitstream") + .withDescription("Embargoed description") + .withMimeType("application/x-gzip") + .withEmbargoPeriod("3 months") + .build(); + } + context.restoreAuthSystemState(); + getClient().perform(get(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT) + .param("handle", publicItem.getHandle()) + .param("fileGrpType", FILE_GRP_TYPE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.metadatabitstreams[?(@.name == '" + embargoedBitstream.getName() + + "')].status", Matchers.contains(DefaultAccessStatusHelper.EMBARGO))) + .andExpect(jsonPath("$._embedded.metadatabitstreams[?(@.name == '" + embargoedBitstream.getName() + + "')].embargoDate", Matchers.contains(notNullValue()))); } @Test From 355f77c8b63e6ab18a86b6e7b1fbfb4b86e58669 Mon Sep 17 00:00:00 2001 From: Matus Beke Date: Tue, 21 Jul 2026 10:24:11 +0200 Subject: [PATCH 3/3] 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 --- .../status/DefaultAccessStatusHelper.java | 22 +++++++++++++++++-- .../status/DefaultAccessStatusHelperTest.java | 10 ++++++--- .../BitstreamAccessStatusLinkRepository.java | 2 +- .../ItemAccessStatusLinkRepository.java | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index 2355e25912a..81844f5017e 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -8,6 +8,7 @@ package org.dspace.access.status; import java.sql.SQLException; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Objects; @@ -47,6 +48,11 @@ public class DefaultAccessStatusHelper implements AccessStatusHelper { public static final String RESTRICTED = "restricted"; public static final String UNKNOWN = "unknown"; + // REST date fields use a plain ISO calendar date (e.g. "2050-01-01"), matching the format used + // elsewhere in the REST API (see ResourcePolicyRest). Date#toString() is locale/timezone-dependent + // and must not be used for values exposed over REST. + private static final String REST_DATE_FORMAT = "yyyy-MM-dd"; + protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); protected ResourcePolicyService resourcePolicyService = @@ -208,7 +214,7 @@ public String getEmbargoFromItem(Context context, Item item, Date threshold) embargoDate = this.retrieveShortestEmbargo(context, bitstream); - return embargoDate != null ? embargoDate.toString() : null; + return formatEmbargoDate(embargoDate); } /** @@ -254,7 +260,19 @@ public String getEmbargoFromBitstream(Context context, Bitstream bitstream, Date } Date embargoDate = this.retrieveShortestEmbargo(context, bitstream); - return embargoDate != null ? embargoDate.toString() : null; + return formatEmbargoDate(embargoDate); + } + + /** + * Format an embargo date for REST exposure as a plain ISO calendar date (yyyy-MM-dd), + * matching the format used elsewhere in the REST API. SimpleDateFormat is not thread-safe, + * so a new instance is created per call. + * + * @param date the date to format, may be null + * @return the formatted date, or null if the given date is null + */ + private String formatEmbargoDate(Date date) { + return date != null ? new SimpleDateFormat(REST_DATE_FORMAT).format(date) : null; } /** diff --git a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java index 999cba057a3..eadda2da48c 100644 --- a/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java +++ b/dspace-api/src/test/java/org/dspace/access/status/DefaultAccessStatusHelperTest.java @@ -15,6 +15,7 @@ import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -291,11 +292,13 @@ public void testWithEmbargo() throws Exception { String status = helper.getAccessStatusFromItem(context, itemWithEmbargo, threshold); assertThat("testWithEmbargo 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); String embargoDate = helper.getEmbargoFromItem(context, itemWithEmbargo, threshold); - assertThat("testWithEmbargo 1", embargoDate, equalTo(policy.getStartDate().toString())); + assertThat("testWithEmbargo 1", embargoDate, + equalTo(new SimpleDateFormat("yyyy-MM-dd").format(policy.getStartDate()))); String bitstreamStatus = helper.getAccessStatusFromBitstream(context, bitstream, threshold); assertThat("testWithEmbargo 2", bitstreamStatus, equalTo(DefaultAccessStatusHelper.EMBARGO)); String bitstreamEmbargoDate = helper.getEmbargoFromBitstream(context, bitstream, threshold); - assertThat("testWithEmbargo 3", bitstreamEmbargoDate, equalTo(policy.getStartDate().toString())); + assertThat("testWithEmbargo 3", bitstreamEmbargoDate, + equalTo(new SimpleDateFormat("yyyy-MM-dd").format(policy.getStartDate()))); } /** @@ -418,7 +421,8 @@ public void testWithPrimaryAndMultipleBitstreams() throws Exception { String status = helper.getAccessStatusFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold); assertThat("testWithPrimaryAndMultipleBitstreams 0", status, equalTo(DefaultAccessStatusHelper.EMBARGO)); String embargoDate = helper.getEmbargoFromItem(context, itemWithPrimaryAndMultipleBitstreams, threshold); - assertThat("testWithPrimaryAndMultipleBitstreams 1", embargoDate, equalTo(policy.getStartDate().toString())); + assertThat("testWithPrimaryAndMultipleBitstreams 1", embargoDate, + equalTo(new SimpleDateFormat("yyyy-MM-dd").format(policy.getStartDate()))); String primaryBitstreamStatus = helper.getAccessStatusFromBitstream(context, primaryBitstream, threshold); assertThat("testWithPrimaryAndMultipleBitstreams 2", primaryBitstreamStatus, equalTo(DefaultAccessStatusHelper.EMBARGO)); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java index 90661a57a57..5c85cf4c7d6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/BitstreamAccessStatusLinkRepository.java @@ -54,7 +54,7 @@ public AccessStatusRest getAccessStatus(@Nullable HttpServletRequest request, } AccessStatusRest accessStatusRest = new AccessStatusRest(); String status = accessStatusService.getAccessStatus(context, bitstream); - if (status.equals(DefaultAccessStatusHelper.EMBARGO)) { + if (DefaultAccessStatusHelper.EMBARGO.equals(status)) { String embargoDate = accessStatusService.getEmbargoFromBitstream(context, bitstream); accessStatusRest.setEmbargoDate(embargoDate); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java index 30a73f64ead..372fc16579d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemAccessStatusLinkRepository.java @@ -53,7 +53,7 @@ public AccessStatusRest getAccessStatus(@Nullable HttpServletRequest request, } AccessStatusRest accessStatusRest = new AccessStatusRest(); String accessStatus = accessStatusService.getAccessStatus(context, item); - if (accessStatus.equals(DefaultAccessStatusHelper.EMBARGO)) { + if (DefaultAccessStatusHelper.EMBARGO.equals(accessStatus)) { String embargoDate = accessStatusService.getEmbargoFromItem(context, item); accessStatusRest.setEmbargoDate(embargoDate); }