Skip to content

Commit

Permalink
Merged in task/dspace-cris-2023_02_x/DSC-1662 (pull request DSpace#2635)
Browse files Browse the repository at this point in the history
[DSC-1662] Implement the accessStatus endpoint in DSpace-CRIS using the automatic metadata

Approved-by: Stefano Maffei
  • Loading branch information
eskander17 authored and steph-ieffam committed Oct 4, 2024
2 parents 85f0e68 + 5e91333 commit 96d33a9
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* 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.access.status;

/**
* responsible to process what’s stored in datacite.rights
* or any other configured metadata to the constant values
* of access status
*
* @author Mohamed Eskander(mohamed.eskander at 4science.com)
*/
public enum AccessStatus {
OPEN_ACCESS(AccessStatusHelper.OPEN_ACCESS),
EMBARGO(AccessStatusHelper.EMBARGO),
METADATA_ONLY(AccessStatusHelper.METADATA_ONLY),
RESTRICTED(AccessStatusHelper.RESTRICTED),
UNKNOWN(AccessStatusHelper.UNKNOWN);

private final String status;

AccessStatus(String status) {
this.status = status;
}

public String getStatus() {
return status;
}

public static AccessStatus toAccessStatus(String value) {
if (value == null) {
return UNKNOWN;
}

switch (value.toLowerCase()) {
case "openaccess":
return OPEN_ACCESS;
case "embargo":
return EMBARGO;
case "metadata-only":
return METADATA_ONLY;
case "restricted":
return RESTRICTED;
default:
return UNKNOWN;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
* Plugin interface for the access status calculation.
*/
public interface AccessStatusHelper {

public static final String EMBARGO = "embargo";
public static final String METADATA_ONLY = "metadata.only";
public static final String OPEN_ACCESS = "open.access";
public static final String RESTRICTED = "restricted";
public static final String UNKNOWN = "unknown";

/**
* Calculate the access status for the item.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@
* * Users can override this method for enhanced functionality.
*/
public class DefaultAccessStatusHelper implements AccessStatusHelper {
public static final String EMBARGO = "embargo";
public static final String METADATA_ONLY = "metadata.only";
public static final String OPEN_ACCESS = "open.access";
public static final String RESTRICTED = "restricted";
public static final String UNKNOWN = "unknown";

protected ItemService itemService =
ContentServiceFactory.getInstance().getItemService();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* 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.access.status;

import static org.dspace.content.Item.ANY;

import java.sql.SQLException;
import java.util.Date;

import org.dspace.content.Item;
import org.dspace.content.MetadataFieldName;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.joda.time.LocalDate;

/**
* implementation of the access status helper.
* The getAccessStatusFromItem method provides a simple logic to
* retrieve the access status of an item based on the provided metadata
*
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
*/
public class MetadataAccessStatusHelper implements AccessStatusHelper {

protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
private ConfigurationService configurationService =
DSpaceServicesFactory.getInstance().getConfigurationService();

private String accessStatusMetadata;
private String availabilityDateMetadata;

public MetadataAccessStatusHelper() {
super();
this.accessStatusMetadata = configurationService.getProperty(
"access.status.access-status-metadata", "datacite.rights");
this.availabilityDateMetadata = configurationService.getProperty(
"access.status.availability-date-metadata", "datacite.available");
}

/**
* Determines the access status of an item based on metadata.
*
* @param context the DSpace context
* @param item the item to check for embargoes
* @param threshold the embargo threshold date
* @return an access status value
*/
@Override
public String getAccessStatusFromItem(Context context, Item item, Date threshold) {

if (item == null) {
return UNKNOWN;
}

String status = itemService.getMetadataFirstValue(item,
new MetadataFieldName(accessStatusMetadata), ANY);
String date = itemService.getMetadataFirstValue(item,
new MetadataFieldName(availabilityDateMetadata), ANY);

if (status == null) {
return UNKNOWN;
}

if (EMBARGO.equals(status)) {
if (date != null) {
LocalDate embargoDate = parseDate(date);
if (embargoDate == null || embargoDate.isBefore(LocalDate.now())) {
return OPEN_ACCESS;
}
}
}

return AccessStatus.toAccessStatus(status).getStatus();
}

private LocalDate parseDate(String dateStr) {
try {
return LocalDate.parse(dateStr);
} catch (IllegalArgumentException e) {
return null;
}
}

/**
*
* @param context the DSpace context
* @param item the item to embargo
* @return an embargo date
*/
@Override
public String getEmbargoFromItem(Context context, Item item, Date threshold) throws SQLException {

// If Item status is not "embargo" then return a null embargo date.
String accessStatus = getAccessStatusFromItem(context, item, threshold);

if (item == null || !accessStatus.equals(EMBARGO)) {
return null;
}

return itemService.getMetadataFirstValue(item, new MetadataFieldName(availabilityDateMetadata), ANY);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ public void destroy() {

@Test
public void testGetAccessStatus() throws Exception {
context.turnOffAuthorisationSystem();
itemService.addMetadata(context, item, "datacite", "rights", null, Item.ANY, "openaccess");
itemService.update(context, item);
context.restoreAuthSystemState();
String status = accessStatusService.getAccessStatus(context, item);
assertNotEquals("testGetAccessStatus 0", status, DefaultAccessStatusHelper.UNKNOWN);
}
Expand Down
8 changes: 8 additions & 0 deletions dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,14 @@ public ItemBuilder withJournalAnce(String ance) {
return addMetadataValue(item, "miur", "journal", "ance", ance);
}

public ItemBuilder withDataCiteRights(String rights) {
return addMetadataValue(item, "datacite", "rights", null, rights);
}

public ItemBuilder withDataCiteAvailable(String available) {
return addMetadataValue(item, "datacite", "available", null, available);
}

@Override
public Item build() {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* 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 static org.dspace.access.status.AccessStatusHelper.EMBARGO;
import static org.dspace.access.status.AccessStatusHelper.METADATA_ONLY;
import static org.dspace.access.status.AccessStatusHelper.OPEN_ACCESS;
import static org.dspace.access.status.AccessStatusHelper.RESTRICTED;
import static org.dspace.access.status.AccessStatusHelper.UNKNOWN;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.hamcrest.Matchers;
import org.joda.time.LocalDate;
import org.junit.Before;
import org.junit.Test;

/**
* Test class for {@link ItemAccessStatusLinkRepository}
*
* @author Mohamed Eskander(mohamed.eskander at 4science.com)
*/
public class ItemAccessStatusLinkRepositoryIT extends AbstractControllerIntegrationTest {

private Community parentCommunity;
private Collection collection1;
private Item item;

@Before
@Override
public void setUp() throws Exception {
super.setUp();
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.build();
collection1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1")
.build();
context.restoreAuthSystemState();
}

@Test
public void getItemAccessStatusUnknownTest() throws Exception {
context.turnOffAuthorisationSystem();
item = ItemBuilder.createItem(context, collection1)
.withTitle("test item")
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);

getClient(adminToken).perform(get("/api/core/items/" + item.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(UNKNOWN)));
}

@Test
public void getItemAccessStatusMetadataOnlyTest() throws Exception {
context.turnOffAuthorisationSystem();
item = ItemBuilder.createItem(context, collection1)
.withTitle("test item")
.withDataCiteRights("metadata-only")
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);

getClient(adminToken).perform(get("/api/core/items/" + item.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(METADATA_ONLY)));
}

@Test
public void getItemAccessStatusRestrictedTest() throws Exception {
context.turnOffAuthorisationSystem();
item = ItemBuilder.createItem(context, collection1)
.withTitle("test item")
.withDataCiteRights("restricted")
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);

getClient(adminToken).perform(get("/api/core/items/" + item.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(RESTRICTED)));
}

@Test
public void getItemAccessStatusOpenAccessTest() throws Exception {
context.turnOffAuthorisationSystem();
item = ItemBuilder.createItem(context, collection1)
.withTitle("test item")
.withDataCiteRights("openaccess")
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);

getClient(adminToken).perform(get("/api/core/items/" + item.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(OPEN_ACCESS)));
}

@Test
public void getItemAccessStatusValidEmbargoTest() throws Exception {
context.turnOffAuthorisationSystem();
item = ItemBuilder.createItem(context, collection1)
.withTitle("test item")
.withDataCiteRights("embargo")
.withDataCiteAvailable(LocalDate.now().plusDays(20).toString())
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);

getClient(adminToken).perform(get("/api/core/items/" + item.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(EMBARGO)));
}

@Test
public void getItemAccessStatusInvalidEmbargoTest() throws Exception {
context.turnOffAuthorisationSystem();
item = ItemBuilder.createItem(context, collection1)
.withTitle("test item")
.withDataCiteRights("embargo")
.withDataCiteAvailable(LocalDate.now().minusDays(20).toString())
.build();

Item itemTwo = ItemBuilder.createItem(context, collection1)
.withTitle("test item two")
.withDataCiteRights("embargo")
.withDataCiteAvailable("fake")
.build();
Item itemThree = ItemBuilder.createItem(context, collection1)
.withTitle("test item three")
.withDataCiteRights("embargo")
.withDataCiteAvailable("")
.build();
context.restoreAuthSystemState();
String adminToken = getAuthToken(admin.getEmail(), password);

getClient(adminToken).perform(get("/api/core/items/" + item.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(OPEN_ACCESS)));

getClient(adminToken).perform(get("/api/core/items/" + itemTwo.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(OPEN_ACCESS)));

getClient(adminToken).perform(get("/api/core/items/" + itemThree.getID() + "/accessStatus"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", Matchers.is(OPEN_ACCESS)));
}

}
8 changes: 7 additions & 1 deletion dspace/config/dspace.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,13 @@ access.status.embargo.forever.day = 1
# implementation of access status helper plugin - replace with local implementation if applicable
# This default access status helper provides an item status based on the policies of the primary
# bitstream (or first bitstream in the original bundles if no primary file is specified).
plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.DefaultAccessStatusHelper
#plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.DefaultAccessStatusHelper

plugin.single.org.dspace.access.status.AccessStatusHelper = org.dspace.access.status.MetadataAccessStatusHelper

#access status metadata
access.status.access-status-metadata = datacite.rights
access.status.availability-date-metadata = datacite.available

#### Checksum Checker Settings ####
# Default dispatcher in case none specified
Expand Down
Loading

0 comments on commit 96d33a9

Please sign in to comment.