Skip to content

Commit 94f0433

Browse files
eskander17steph-ieffam
authored andcommitted
Merged in DSC-1497 (pull request DSpace#1595)
[DSC-1497] Add Authority SOURCE ref on the lookup results Approved-by: Stefano Maffei
2 parents bc34a4c + 55da2ca commit 94f0433

File tree

16 files changed

+270
-11
lines changed

16 files changed

+270
-11
lines changed

dspace-api/src/main/java/org/dspace/content/authority/Choice.java

+19
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class Choice {
4848

4949
public Map<String, String> extras = new HashMap<String, String>();
5050

51+
public String source = "local";
52+
5153
public Choice() {
5254
}
5355

@@ -80,6 +82,23 @@ public Choice(String authority, String label, String value, Map<String, String>
8082
this.extras = extras;
8183
}
8284

85+
/**
86+
* Constructor to quickly setup the data object for basic authorities. The choice is assumed to be selectable.
87+
*
88+
* @param authority the authority key
89+
* @param value the text value to store in the metadata
90+
* @param label the value to display to the user
91+
* @param extras a key value map of extra information related to this choice
92+
* @param source authority SOURCE reference
93+
*/
94+
public Choice(String authority, String label, String value, Map<String, String> extras, String source) {
95+
this.authority = authority;
96+
this.label = label;
97+
this.value = value;
98+
this.extras = extras;
99+
this.source = source;
100+
}
101+
83102
/**
84103
* Constructor for common need of Hierarchical authorities that want to
85104
* explicitely set the selectable flag

dspace-api/src/main/java/org/dspace/content/authority/ItemAuthority.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public class ItemAuthority implements ChoiceAuthority, LinkableEntityAuthority {
7979
// map of field key to presentation type
8080
protected Map<String, String> externalSource = new HashMap<String, String>();
8181

82+
public static final String DEFAULT = "local";
83+
8284
// punt! this is a poor implementation..
8385
@Override
8486
public Choices getBestMatch(String text, String locale) {
@@ -174,7 +176,7 @@ private List<Choice> getChoiceListFromQueryResults(SolrDocumentList results, Str
174176
Map<String, String> extras = ItemAuthorityUtils.buildExtra(getPluginInstanceName(), doc);
175177
return new Choice((String) doc.getFieldValue("search.resourceid"),
176178
title,
177-
title, extras);
179+
title, extras, DEFAULT);
178180
}).collect(Collectors.toList());
179181
}
180182

@@ -297,4 +299,9 @@ private Context getContext() {
297299
return context != null ? context : new Context();
298300
}
299301

302+
protected String getSource() {
303+
return configurationService.getProperty(
304+
"cris.ItemAuthority." + authorityName + ".source", DEFAULT);
305+
}
306+
300307
}

dspace-api/src/main/java/org/dspace/content/authority/OpenAIREProjectAuthority.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private Choice convertToChoice(ImportRecord record) {
9898
String code = getMetadataValue(record, "oairecerif", "funding", "identifier");
9999
String authority = getAuthorityPrefix() + code;
100100
String label = StringUtils.isNotBlank(code) ? value + "(" + code + ")" : value;
101-
return new Choice(authority, value, label, getOpenAireExtra(code));
101+
return new Choice(authority, value, label, getOpenAireExtra(code), getSource());
102102
}
103103

104104
private String getMetadataValue(ImportRecord record, String schema, String element, String qualifier) {

dspace-api/src/main/java/org/dspace/content/authority/OrcidAuthority.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private Choice convertToChoice(ExpandedResult result) {
113113
String title = getTitle(result);
114114
String authority = composeAuthorityValue(result.getOrcidId());
115115
Map<String, String> extras = composeExtras(result);
116-
return new Choice(authority, title, title, extras);
116+
return new Choice(authority, title, title, extras, getSource());
117117
}
118118

119119
private String getTitle(ExpandedResult result) {

dspace-api/src/main/java/org/dspace/content/authority/RorOrgUnitAuthority.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private List<Choice> getChoiceFromRORQueryResults(Collection<ImportRecord> orgUn
6464
return orgUnits
6565
.stream()
6666
.map(orgUnit -> new Choice(composeAuthorityValue(getIdentifier(orgUnit)), getName(orgUnit),
67-
getName(orgUnit), buildExtras(orgUnit)))
67+
getName(orgUnit), buildExtras(orgUnit), getSource()))
6868
.collect(Collectors.toList());
6969
}
7070

dspace-api/src/main/java/org/dspace/content/authority/SherpaAuthority.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ private Choice convertToChoice(SHERPAJournal journal) {
128128
String authority = composeAuthorityValue(journal);
129129
Map<String, String> extras = getSherpaExtra(journal);
130130
String title = journal.getTitles().get(0);
131-
return new Choice(authority, title, title, extras);
131+
return new Choice(authority, title, title, extras, getSource());
132132
}
133133

134134
private Map<String, String> getSherpaExtra(SHERPAJournal journal) {

dspace-api/src/main/java/org/dspace/content/authority/ZDBAuthority.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ protected Choice[] addExternalResults(String text, Choices choices, int start, i
5050
for (AuthorityValue val : values) {
5151
if (added < max) {
5252
Map<String, String> extras = getZDBExtra(val);
53-
results.add(new Choice(val.generateString(), val.getValue(), val.getValue(), extras));
53+
results.add(
54+
new Choice(val.generateString(), val.getValue(), val.getValue(), extras, getSource())
55+
);
5456
added++;
5557
}
5658
}

dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/VocabularyEntryDetailsRestConverter.java

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public VocabularyEntryDetailsRest convert(Choice choice, Projection projection)
3535
entry.setDisplay(choice.label);
3636
entry.setId(mapToId(choice));
3737
entry.setOtherInformation(choice.extras);
38+
entry.setSource(choice.source);
3839
entry.setSelectable(choice.selectable);
3940
return entry;
4041
}

dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class VocabularyEntryDetailsRest extends BaseObjectRest<String> {
2929
private String display;
3030
private String value;
3131
private Map<String, String> otherInformation;
32+
private String source;
3233
private boolean selectable;
3334
@JsonIgnore
3435
private boolean inHierarchicalVocabulary = false;
@@ -102,4 +103,13 @@ public void setInHierarchicalVocabulary(boolean isInHierarchicalVocabulary) {
102103
public boolean isInHierarchicalVocabulary() {
103104
return inHierarchicalVocabulary;
104105
}
106+
107+
public String getSource() {
108+
return source;
109+
}
110+
111+
public void setSource(String source) {
112+
this.source = source;
113+
}
114+
105115
}

dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryRest.java

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class VocabularyEntryRest implements RestModel {
2626
private String display;
2727
private String value;
2828
private Map<String, String> otherInformation;
29+
private String source;
2930

3031
/**
3132
* The Vocabulary Entry Details resource if available related to this entry
@@ -73,6 +74,14 @@ public VocabularyEntryDetailsRest getVocabularyEntryDetailsRest() {
7374
return vocabularyEntryDetailsRest;
7475
}
7576

77+
public String getSource() {
78+
return source;
79+
}
80+
81+
public void setSource(String source) {
82+
this.source = source;
83+
}
84+
7685
@Override
7786
public String getType() {
7887
return VocabularyEntryRest.NAME;

dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public VocabularyEntryRest convertEntry(Choice choice, String authorityName, boo
129129
if (StringUtils.isNotBlank(choice.authority)) {
130130
entry.setVocabularyEntryDetailsRest(converter.toRest(choice, projection));
131131
}
132+
entry.setSource(choice.source);
132133
return entry;
133134
}
134135

dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemAuthorityIT.java

+55-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.dspace.builder.ItemBuilder;
3030
import org.dspace.content.Collection;
3131
import org.dspace.content.Item;
32+
import org.dspace.content.authority.ItemAuthority;
3233
import org.dspace.content.authority.service.ChoiceAuthorityService;
3334
import org.dspace.core.service.PluginService;
3435
import org.dspace.eperson.EPerson;
@@ -264,7 +265,8 @@ public void singleItemAuthorityWithoutOrgUnitTest() throws Exception {
264265
"Author 1", "Author 1", "vocabularyEntry",
265266
Map.of("data-oairecerif_author_affiliation", "", "oairecerif_author_affiliation", "",
266267
"data-person_identifier_orcid", "",
267-
"person_identifier_orcid", ""))
268+
"person_identifier_orcid", ""),
269+
ItemAuthority.DEFAULT)
268270
)))
269271
.andExpect(jsonPath("$.page.totalElements", Matchers.is(1)));
270272
}
@@ -888,6 +890,58 @@ public void authorCoarseAuthorityTests() throws Exception {
888890
person4Id, "Cortese, Claudio", "Cortese, Claudio", "vocabularyEntry"))));
889891
}
890892

893+
@Test
894+
public void itemAuthoritySourceReferenceTest() throws Exception {
895+
context.turnOffAuthorisationSystem();
896+
897+
configurationService.setProperty("plugin.named.org.dspace.content.authority.ChoiceAuthority",
898+
new String[] { "org.dspace.content.authority.ItemAuthority = PersonAuthority" });
899+
900+
configurationService.setProperty("choices.presentation.dc.contributor.author", "suggest");
901+
configurationService.setProperty("authority.controlled.dc.contributor.author", "true");
902+
903+
configurationService.setProperty("cris.ItemAuthority.PersonAuthority.entityType", "EntityPerson");
904+
905+
// set authority source reference
906+
configurationService.setProperty("cris.ItemAuthority.PersonAuthority.source", "ORCID");
907+
908+
// These clears have to happen so that the config is actually reloaded in those
909+
// classes. This is needed for
910+
// the properties that we're altering above and this is only used within the
911+
// tests
912+
pluginService.clearNamedPluginClasses();
913+
choiceAuthorityService.clearCache();
914+
915+
parentCommunity = CommunityBuilder.createCommunity(context).build();
916+
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).build();
917+
918+
Item person1 = ItemBuilder.createItem(context, col1)
919+
.withTitle("Person 1")
920+
.withType("mytype")
921+
.withEntityType("EntityPerson").build();
922+
923+
ItemBuilder.createItem(context, col1)
924+
.withTitle("Person 2")
925+
.withEntityType("EntityPerson")
926+
.build();
927+
928+
ItemBuilder.createItem(context, col1)
929+
.withTitle("Person 3")
930+
.withType("anotherType")
931+
.withEntityType("EntityPerson").build();
932+
933+
context.restoreAuthSystemState();
934+
935+
String token = getAuthToken(eperson.getEmail(), password);
936+
getClient(token)
937+
.perform(get("/api/submission/vocabularies/PersonAuthority/entries").param("filter", "Person"))
938+
.andExpect(status().isOk()).andExpect(jsonPath("$.page.totalElements", Matchers.is(1)))
939+
.andExpect(jsonPath("$._embedded.entries",
940+
Matchers.containsInAnyOrder(ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(
941+
person1.getID().toString(), "Person 1", "Person 1", "vocabularyEntry", Map.of(),
942+
ItemAuthority.DEFAULT))));
943+
}
944+
891945
@Override
892946
@After
893947
// We need to cleanup the authorities cache once than the configuration has been restored

dspace-server-webapp/src/test/java/org/dspace/app/rest/OrcidAuthorityIT.java

+107
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.dspace.builder.ItemBuilder;
3232
import org.dspace.content.Collection;
3333
import org.dspace.content.Item;
34+
import org.dspace.content.authority.ItemAuthority;
3435
import org.dspace.content.authority.OrcidAuthority;
3536
import org.dspace.orcid.client.OrcidClient;
3637
import org.dspace.orcid.client.OrcidConfiguration;
@@ -712,6 +713,101 @@ public void testWithAffiliationExtra() throws Exception {
712713
verifyNoMoreInteractions(orcidClientMock);
713714
}
714715

716+
@Test
717+
public void testSourceReference() throws Exception {
718+
719+
List<ExpandedResult> orcidSearchResults = List.of(
720+
expandedResult("Author", "From Orcid 1", "0000-1111-2222-3333"),
721+
expandedResult("AUTHOR", "FROM ORCID 2", "0000-2222-3333-4444"));
722+
723+
String expectedQuery = "(given-names:author+OR+family-name:author+OR+other-names:author)";
724+
725+
when(orcidClientMock.expandedSearch(READ_PUBLIC_TOKEN, expectedQuery, 0, 20))
726+
.thenReturn(expandedSearch(2, orcidSearchResults));
727+
728+
String token = getAuthToken(eperson.getEmail(), password);
729+
getClient(token).perform(get("/api/submission/vocabularies/AuthorAuthority/entries")
730+
.param("filter", "author"))
731+
.andExpect(status().isOk())
732+
.andExpect(jsonPath("$._embedded.entries", containsInAnyOrder(
733+
orcidEntry("From Orcid 1 Author", REFERENCE, "0000-1111-2222-3333", getSource()),
734+
orcidEntry("From Orcid 2 Author", REFERENCE, "0000-2222-3333-4444", getSource()))))
735+
.andExpect(jsonPath("$.page.size", Matchers.is(20)))
736+
.andExpect(jsonPath("$.page.totalPages", Matchers.is(1)))
737+
.andExpect(jsonPath("$.page.totalElements", Matchers.is(2)));
738+
739+
verify(orcidClientMock).getReadPublicAccessToken();
740+
verify(orcidClientMock).expandedSearch(READ_PUBLIC_TOKEN, expectedQuery, 0, 20);
741+
verifyNoMoreInteractions(orcidClientMock);
742+
}
743+
744+
@Test
745+
public void testMultipleSourcesReferences() throws Exception {
746+
context.turnOffAuthorisationSystem();
747+
748+
List<ExpandedResult> orcidSearchResults = List.of(
749+
expandedResult("Author", "From Orcid 1", "0000-1111-2222-3333"),
750+
expandedResult("AUTHOR", "FROM ORCID 2", "0000-2222-3333-4444"));
751+
752+
String expectedQuery = "(given-names:author+OR+family-name:author+OR+other-names:author)";
753+
754+
when(orcidClientMock.expandedSearch(READ_PUBLIC_TOKEN, expectedQuery, 0, 18))
755+
.thenReturn(expandedSearch(2, orcidSearchResults));
756+
757+
parentCommunity = CommunityBuilder.createCommunity(context).build();
758+
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).build();
759+
760+
Item orgUnit_1 = ItemBuilder.createItem(context, col1)
761+
.withTitle("OrgUnit_1")
762+
.withEntityType("orgunit")
763+
.build();
764+
765+
Item orgUnit_2 = ItemBuilder.createItem(context, col1)
766+
.withTitle("OrgUnit_2")
767+
.withEntityType("orgunit")
768+
.build();
769+
770+
Item author_1 = ItemBuilder.createItem(context, col1)
771+
.withTitle("Author 1")
772+
.withPersonMainAffiliation(orgUnit_1.getName(), orgUnit_1.getID().toString())
773+
.withEntityType("person")
774+
.build();
775+
776+
Item author_2 = ItemBuilder.createItem(context, col1)
777+
.withTitle("Author 2")
778+
.withPersonMainAffiliation(orgUnit_2.getName(), orgUnit_2.getID().toString())
779+
.withEntityType("person")
780+
.build();
781+
782+
context.restoreAuthSystemState();
783+
784+
String token = getAuthToken(eperson.getEmail(), password);
785+
getClient(token).perform(get("/api/submission/vocabularies/AuthorAuthority/entries")
786+
.param("filter", "author"))
787+
.andExpect(status().isOk())
788+
.andExpect(jsonPath("$._embedded.entries", Matchers.containsInAnyOrder(
789+
// source should be local
790+
ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(author_1.getID().toString(),
791+
"Author 1", "Author 1", "vocabularyEntry",
792+
Map.of("data-oairecerif_author_affiliation", "OrgUnit_1::" + orgUnit_1.getID(),
793+
"oairecerif_author_affiliation", "OrgUnit_1::" + orgUnit_1.getID(),
794+
"data-person_identifier_orcid", "",
795+
"person_identifier_orcid", ""),
796+
ItemAuthority.DEFAULT),
797+
ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(author_2.getID().toString(),
798+
"Author 2", "Author 2", "vocabularyEntry",
799+
Map.of("data-oairecerif_author_affiliation", "OrgUnit_2::" + orgUnit_2.getID(),
800+
"oairecerif_author_affiliation", "OrgUnit_2::" + orgUnit_2.getID(),
801+
"data-person_identifier_orcid", "",
802+
"person_identifier_orcid", ""),
803+
ItemAuthority.DEFAULT),
804+
// source should be orcid as configured
805+
orcidEntry("From Orcid 1 Author", REFERENCE, "0000-1111-2222-3333", getSource()),
806+
orcidEntry("From Orcid 2 Author", REFERENCE, "0000-2222-3333-4444", getSource())
807+
)))
808+
.andExpect(jsonPath("$.page.totalElements", Matchers.is(4)));
809+
}
810+
715811
private ExpandedSearch buildExpandedSearchFromSublist(List<ExpandedResult> totalResults, int start, int rows) {
716812
int total = totalResults.size();
717813
if (start > total) {
@@ -783,6 +879,12 @@ private Matcher<? super Object> orcidEntry(String title, String authorityPrefix,
783879
title, "vocabularyEntry", ORCID_INFO, orcid);
784880
}
785881

882+
private Matcher<? super Object> orcidEntry(String title, String authorityPrefix, String orcid, String source) {
883+
String authority = authorityPrefix + "ORCID::" + orcid;
884+
return ItemAuthorityMatcher.matchItemAuthorityWithOtherInformations(authority, title,
885+
title, "vocabularyEntry", ORCID_INFO, orcid, source);
886+
}
887+
786888
private Matcher<? super Object> orcidEntryWithAffiliation(String title, String authorityPrefix,
787889
String orcid, String affiliation) {
788890
String authority = authorityPrefix + "ORCID::" + orcid;
@@ -800,4 +902,9 @@ private Matcher<? super Object> orcidEntryWithAffiliation(String title, String a
800902
private String id(Item item) {
801903
return item.getID().toString();
802904
}
905+
906+
private String getSource() {
907+
return configurationService.getProperty(
908+
"cris.ItemAuthority.AuthorAuthority.source", ItemAuthority.DEFAULT);
909+
}
803910
}

0 commit comments

Comments
 (0)