Skip to content

Commit 12de297

Browse files
AdamF42vins01-4science
authored andcommitted
Merged in task/dspace-cris-2024_02_x/DSC-2304 (pull request DSpace#4110)
Task/dspace cris 2024 02 x/DSC-2304 Approved-by: Vincenzo Mecca Approved-by: Giuseppe Digilio
2 parents 75d7b9f + f819faa commit 12de297

File tree

14 files changed

+1278
-50
lines changed

14 files changed

+1278
-50
lines changed

dspace-api/src/main/java/org/dspace/content/integration/crosswalks/virtualfields/ItemDOIService.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import java.util.Comparator;
1111
import java.util.List;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
1214

1315
import org.dspace.content.Item;
1416
import org.dspace.content.MetadataValue;
@@ -18,26 +20,41 @@
1820

1921

2022
public class ItemDOIService {
21-
static final String CFG_PREFIX = "identifier.doi.prefix";
2223

24+
static final String DOI_RESOLVER_REGEX = "^(?:https?://[^/]*)/(.*$)";
25+
static final Pattern DOI_PATTERN = Pattern.compile(DOI_RESOLVER_REGEX);
26+
static final String CFG_PREFIX = "identifier.doi.prefix";
2327
static final String DOI_METADATA = "dc.identifier.doi";
2428

2529
@Autowired
2630
protected ItemService itemService;
2731
@Autowired
2832
private ConfigurationService configurationService;
2933

34+
static String formatDOI(String doi) {
35+
36+
if (doi == null) {
37+
return null;
38+
}
39+
40+
Matcher matcher = DOI_PATTERN.matcher(doi);
41+
if (matcher.matches()) {
42+
doi = matcher.group(1);
43+
}
44+
return doi;
45+
}
46+
3047
public String[] getAlternativeDOIFromItem(Item item) {
3148
List<MetadataValue> metadataValueList = itemService.getMetadataByMetadataString(item, DOI_METADATA);
3249
return getAlternativeDOI(metadataValueList, getPrimaryDOI(metadataValueList));
3350
}
3451
private String[] getAlternativeDOI(List<MetadataValue> metadataValueList, String primaryValue) {
35-
return metadataValueList.stream().map(MetadataValue::getValue)
36-
.filter(value -> !value.equals(primaryValue)).toArray(String[]::new);
52+
return metadataValueList.stream().map(MetadataValue::getValue).filter(value -> !value.equals(primaryValue))
53+
.map(ItemDOIService::formatDOI).toArray(String[]::new);
3754
}
3855

3956
public String getPrimaryDOIFromItem(Item item) {
40-
return getPrimaryDOI(itemService.getMetadataByMetadataString(item, DOI_METADATA));
57+
return formatDOI(getPrimaryDOI(itemService.getMetadataByMetadataString(item, DOI_METADATA)));
4158
}
4259

4360
private String getPrimaryDOI(List<MetadataValue> metadataValueList) {

dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ public void list(String processName, PrintStream out, PrintStream err, Integer .
442442
* @param doiRow DOI to register
443443
* @param filter logical item filter to override
444444
* @throws IllegalArgumentException
445-
* if {@link doiRow} does not name an Item.
445+
* if {@code doiRow} does not name an Item.
446446
* @throws IllegalStateException
447447
* on invalid DOI.
448448
* @throws RuntimeException
@@ -737,7 +737,7 @@ public DOI resolveToDOI(String identifier)
737737
DOI doiRow = null;
738738
String doi = null;
739739

740-
// detect it identifer is ItemID, handle or DOI.
740+
// detect if identifier is ItemID, handle or DOI.
741741
// try to detect ItemID
742742
if (identifier
743743
.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[34][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}")) {

dspace-api/src/test/data/dspaceFolder/config/spring/api/identifier-service.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
</bean>
4242

4343
<bean id="org.dspace.identifier.doi.DOIConnector"
44-
class="org.dspace.identifier.doi.DataCiteConnector">
44+
class="org.dspace.identifier.doi.MockDataCiteConnector">
4545
<property name='DATACITE_SCHEME' value='https'/>
4646
<property name='DATACITE_HOST' value='mds.test.datacite.org'/>
4747
<property name='DATACITE_DOI_PATH' value='/doi/'/>

dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import org.dspace.externalservices.scopus.factory.CrisMetricsServiceFactory;
5757
import org.dspace.harvest.factory.HarvestServiceFactory;
5858
import org.dspace.harvest.service.HarvestedCollectionService;
59+
import org.dspace.identifier.dao.DOIDAO;
60+
import org.dspace.identifier.dao.impl.DOIDAOImpl;
5961
import org.dspace.identifier.factory.IdentifierServiceFactory;
6062
import org.dspace.identifier.service.DOIService;
6163
import org.dspace.layout.factory.CrisLayoutServiceFactory;
@@ -136,7 +138,6 @@ public abstract class AbstractBuilder<T, S> {
136138
static SubscribeService subscribeService;
137139
static RequestItemService requestItemService;
138140
static VersioningService versioningService;
139-
static DOIService doiService;
140141
static OrcidHistoryService orcidHistoryService;
141142
static OrcidQueueService orcidQueueService;
142143
static OrcidTokenService orcidTokenService;
@@ -150,6 +151,8 @@ public abstract class AbstractBuilder<T, S> {
150151
static QAEventService qaEventService;
151152
static SolrSuggestionStorageService solrSuggestionService;
152153
static LDNMessageService ldnMessageService;
154+
static DOIService doiService;
155+
static DOIDAO doiDao;
153156

154157
protected Context context;
155158

@@ -235,6 +238,8 @@ public static void init() {
235238
qaEventService = new DSpace().getSingletonService(QAEventService.class);
236239
solrSuggestionService = new DSpace().getSingletonService(SolrSuggestionStorageService.class);
237240
ldnMessageService = NotifyServiceFactory.getInstance().getLDNMessageService();
241+
doiService = IdentifierServiceFactory.getInstance().getDOIService();
242+
doiDao = new DSpace().getSingletonService(DOIDAOImpl.class);
238243
}
239244

240245

@@ -276,7 +281,6 @@ public static void destroy() {
276281
harvestedCollectionService = null;
277282
requestItemService = null;
278283
versioningService = null;
279-
doiService = null;
280284
orcidTokenService = null;
281285
notifyService = null;
282286
inboundPatternService = null;
@@ -287,6 +291,7 @@ public static void destroy() {
287291
subscribeService = null;
288292
supervisionOrderService = null;
289293
ldnMessageService = null;
294+
doiService = null;
290295
}
291296

292297
public static void cleanupObjects() throws Exception {

dspace-api/src/test/java/org/dspace/builder/DOIBuilder.java

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@
77
*/
88
package org.dspace.builder;
99

10+
1011
import java.sql.SQLException;
1112

12-
import org.dspace.authorize.AuthorizeException;
1313
import org.dspace.content.DSpaceObject;
1414
import org.dspace.core.Context;
1515
import org.dspace.identifier.DOI;
1616
import org.dspace.identifier.service.DOIService;
1717

18+
1819
/**
19-
* Builder for {@link DOI} entities.
20-
*/
20+
* Builder to construct DOI entities for tests
21+
*
22+
* @author Adamo Fapohunda (adamo.fapohunda at 4science.com)
23+
**/
2124
public class DOIBuilder extends AbstractBuilder<DOI, DOIService> {
2225

2326
private DOI doi;
@@ -26,58 +29,68 @@ protected DOIBuilder(Context context) {
2629
super(context);
2730
}
2831

29-
public static DOIBuilder createDOI(final Context context) {
30-
DOIBuilder builder = new DOIBuilder(context);
31-
return builder.create(context);
32+
public static DOIBuilder createDOI(Context context, DSpaceObject dso) {
33+
return new DOIBuilder(context).create(dso);
3234
}
3335

34-
private DOIBuilder create(final Context context) {
36+
private DOIBuilder create(DSpaceObject dso) {
3537
try {
36-
this.doi = doiService.create(context);
38+
if (dso != null) {
39+
doi = doiService.findDOIByDSpaceObject(context, dso);
40+
}
41+
42+
if (doi == null) {
43+
doi = doiService.create(context);
44+
doi.setDSpaceObject(dso); // can be null
45+
}
3746
} catch (SQLException e) {
38-
throw new RuntimeException(e);
47+
e.printStackTrace();
48+
return null;
3949
}
4050
return this;
4151
}
4252

43-
public DOIBuilder withDoi(final String doi) {
44-
this.doi.setDoi(doi);
45-
return this;
46-
}
4753

48-
public DOIBuilder withDSpaceObject(final DSpaceObject dSpaceObject) {
49-
this.doi.setDSpaceObject(dSpaceObject);
54+
public DOIBuilder withDoiString(String doiString) {
55+
doi.setDoi(doiString);
5056
return this;
5157
}
5258

53-
public DOIBuilder withStatus(final Integer status) {
54-
this.doi.setStatus(status);
59+
public DOIBuilder withStatus(int status) {
60+
doi.setStatus(status);
5561
return this;
5662
}
5763

5864
@Override
59-
public DOI build() throws SQLException, AuthorizeException {
60-
return this.doi;
65+
public DOI build() {
66+
try {
67+
doiService.update(context, doi);
68+
context.dispatchEvents();
69+
indexingService.commit();
70+
} catch (Exception e) {
71+
e.printStackTrace();
72+
return null;
73+
}
74+
return doi;
6175
}
6276

6377
@Override
64-
public void delete(Context c, DOI doi) throws Exception {
65-
try {
66-
doiService.delete(c, doi);
67-
} catch (SQLException e) {
68-
throw new RuntimeException(e);
78+
public void delete(Context c, DOI dso) throws Exception {
79+
if (dso != null) {
80+
doiDao.delete(c, doi);
81+
doi = null;
6982
}
7083
}
7184

7285
@Override
7386
public void cleanup() throws Exception {
74-
try (Context context = new Context()) {
75-
context.setDispatcher("noindex");
76-
context.turnOffAuthorisationSystem();
77-
this.doi = context.reloadEntity(this.doi);
78-
if (this.doi != null) {
79-
delete(context, this.doi);
80-
context.complete();
87+
try (Context c = new Context()) {
88+
c.setDispatcher("noindex");
89+
c.turnOffAuthorisationSystem();
90+
doi = c.reloadEntity(doi);
91+
if (doi != null) {
92+
doiDao.delete(c, doi);
93+
c.complete();
8194
}
8295
}
8396
}
@@ -86,5 +99,4 @@ public void cleanup() throws Exception {
8699
protected DOIService getService() {
87100
return doiService;
88101
}
89-
90102
}

dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.dspace.builder.CrisLayoutMetric2BoxBuilder;
2424
import org.dspace.builder.CrisLayoutTabBuilder;
2525
import org.dspace.builder.CrisMetricsBuilder;
26+
import org.dspace.builder.DOIBuilder;
2627
import org.dspace.builder.EPersonBuilder;
2728
import org.dspace.builder.EntityTypeBuilder;
2829
import org.dspace.builder.GroupBuilder;
@@ -92,6 +93,7 @@ private void initMap() {
9293
map.put(MetadataSchemaBuilder.class.getName(), new ArrayList<>());
9394
map.put(SiteBuilder.class.getName(), new ArrayList<>());
9495
map.put(ProcessBuilder.class.getName(), new ArrayList<>());
96+
map.put(DOIBuilder.class.getName(), new ArrayList<>());
9597
}
9698

9799
/**
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.content.integration.crosswalks.virtualfields;
9+
10+
import static org.dspace.builder.CollectionBuilder.createCollection;
11+
import static org.dspace.builder.CommunityBuilder.createCommunity;
12+
import static org.dspace.content.integration.crosswalks.virtualfields.ItemDOIService.formatDOI;
13+
import static org.hamcrest.MatcherAssert.assertThat;
14+
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
15+
import static org.hamcrest.Matchers.is;
16+
import static org.junit.Assert.assertEquals;
17+
18+
import org.dspace.AbstractIntegrationTestWithDatabase;
19+
import org.dspace.builder.ItemBuilder;
20+
import org.dspace.content.Collection;
21+
import org.dspace.content.Item;
22+
import org.dspace.services.ConfigurationService;
23+
import org.dspace.services.factory.DSpaceServicesFactory;
24+
import org.dspace.utils.DSpace;
25+
import org.junit.Before;
26+
import org.junit.Test;
27+
28+
/**
29+
* @author Adamo Fapohunda (adamo.fapohunda at 4science.com)
30+
**/
31+
public class ItemDOIServiceIT extends AbstractIntegrationTestWithDatabase {
32+
33+
private ItemDOIService itemDOIService;
34+
35+
private Collection collection;
36+
37+
private ConfigurationService configurationService;
38+
39+
@Before
40+
public void setup() {
41+
itemDOIService = new DSpace().getServiceManager().getServicesByType(ItemDOIService.class).get(0);
42+
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
43+
44+
45+
context.setCurrentUser(admin);
46+
parentCommunity = createCommunity(context).build();
47+
collection = createCollection(context, parentCommunity).build();
48+
49+
// Set test DOI prefix in configuration
50+
configurationService.setProperty("identifier.doi.prefix", "10.1234");
51+
}
52+
53+
@Test
54+
public void testPrimaryAndAlternativeDOIExtraction() {
55+
Item item = ItemBuilder.createItem(context, collection)
56+
.withDoiIdentifier("10.1234/primary-doi")
57+
.withDoiIdentifier("10.5678/other-doi")
58+
.withDoiIdentifier("https://doi.org/10.5678/resolved-doi")
59+
.build();
60+
61+
String primary = itemDOIService.getPrimaryDOIFromItem(item);
62+
assertThat(primary, is("10.1234/primary-doi"));
63+
64+
String[] alternatives = itemDOIService.getAlternativeDOIFromItem(item);
65+
assertThat(alternatives.length, is(2));
66+
assertThat(alternatives, arrayContainingInAnyOrder(
67+
"10.5678/other-doi", "10.5678/resolved-doi"));
68+
}
69+
70+
@Test
71+
public void testFallbackToFirstDOIWhenNoMatchWithPrefix() {
72+
configurationService.setProperty("identifier.doi.prefix", "10.9999");
73+
74+
Item item = ItemBuilder.createItem(context, collection)
75+
.withDoiIdentifier("10.5678/first")
76+
.withDoiIdentifier("10.5678/second")
77+
.build();
78+
79+
String primary = itemDOIService.getPrimaryDOIFromItem(item);
80+
assertThat(primary, is("10.5678/first"));
81+
82+
String[] alternatives = itemDOIService.getAlternativeDOIFromItem(item);
83+
assertThat(alternatives.length, is(1));
84+
assertThat(alternatives[0], is("10.5678/second"));
85+
}
86+
87+
88+
@Test
89+
public void testFormatDOI_Normalization() {
90+
assertEquals("10.25560/119170", formatDOI("https://doi.org/10.25560/119170"));
91+
assertEquals("10.25561/95151", formatDOI("https://www.dx.doi.org/10.25561/95151"));
92+
assertEquals("10.25560/31589", formatDOI("https://doi.org/10.25560/31589"));
93+
assertEquals("10.25561/93710", formatDOI("https://www.dx.doi.org/10.25561/93710"));
94+
assertEquals("10.1214/14-AAP1038", formatDOI("https://www.dx.doi.org/10.1214/14-AAP1038"));
95+
assertEquals("10.25561/109825", formatDOI("https://doi.org/10.25561/109825"));
96+
assertEquals("10.1016/j.econlet.2013.02.011", formatDOI("https://www.dx.doi.org/10.1016/j.econlet.2013.02.011"));
97+
assertEquals("10.1101/024349", formatDOI("https://www.dx.doi.org/10.1101/024349"));
98+
assertEquals("10.1038/s41586-020-2405-7", formatDOI("https://www.dx.doi.org/10.1038/s41586-020-2405-7"));
99+
assertEquals("10.1093/comnet/cnu039", formatDOI("https://www.dx.doi.org/10.1093/comnet/cnu039"));
100+
assertEquals("10.3310/hsdr04290", formatDOI("https://www.dx.doi.org/10.3310/hsdr04290"));
101+
assertEquals("10.2140/ant.2016.10.843", formatDOI("https://www.dx.doi.org/10.2140/ant.2016.10.843"));
102+
assertEquals("10.1215/00127094-2885764", formatDOI("https://www.dx.doi.org/10.1215/00127094-2885764"));
103+
assertEquals("10.1016/S2214-109X(20)30288-6", formatDOI("https://www.dx.doi.org/10.1016/S2214-109X(20)30288-6"));
104+
}
105+
106+
@Test
107+
public void testFormatDOI_PlainText() {
108+
assertEquals("text_value", formatDOI("text_value"));
109+
assertEquals("10.25561/54217", formatDOI("10.25561/54217"));
110+
}
111+
112+
}

0 commit comments

Comments
 (0)