Skip to content

Commit faad3b1

Browse files
committed
Merge remote-tracking branch 'GDCC/GDCC/8914-COAR-compliant_messaging2'
into GDCC/8914
2 parents 54b060c + baa9dca commit faad3b1

File tree

9 files changed

+1386
-11
lines changed

9 files changed

+1386
-11
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Improved COAR Notify Relationship Announcement Support
2+
3+
Dataverse no longer sends duplicate [COAR Notify Relationship Announcement Workflow](https://coar-notify.net/catalogue/workflows/repository-relationship-repository/) messages when new dataset versions are published (and the relationship metadata has not been changed).

src/main/java/edu/harvard/iq/dataverse/Dataset.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,26 @@ public DatasetVersion getReleasedVersion() {
533533
}
534534
return null;
535535
}
536+
537+
/**
538+
* Returns the second-most-recent released version of this dataset.
539+
* Assumes versions are ordered from most recent to oldest.
540+
*
541+
* @return The prior released version, or null if there is only one or no released versions
542+
*/
543+
public DatasetVersion getPriorReleasedVersion() {
544+
boolean foundReleasedVersion = false;
545+
for (DatasetVersion version : this.getVersions()) {
546+
if (version.isReleased()) {
547+
if(foundReleasedVersion) {
548+
return version;
549+
} else {
550+
foundReleasedVersion = true;
551+
}
552+
}
553+
}
554+
return null;
555+
}
536556

537557
public DatasetVersion getVersionFromId(Long datasetVersionId) {
538558
for (DatasetVersion version : this.getVersions()) {

src/main/java/edu/harvard/iq/dataverse/DatasetField.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ public void setDatasetFieldValues(List<DatasetFieldValue> datasetFieldValues) {
197197

198198
@ManyToMany(cascade = {CascadeType.MERGE})
199199
@JoinTable(indexes = {@Index(columnList="datasetfield_id"),@Index(columnList="controlledvocabularyvalues_id")})
200+
@OrderBy("displayOrder ASC")
200201
private List<ControlledVocabularyValue> controlledVocabularyValues = new ArrayList<>();
201202

202203
public List<ControlledVocabularyValue> getControlledVocabularyValues() {
@@ -604,14 +605,15 @@ private DatasetField copy(Object versionOrTemplate, DatasetFieldCompoundValue pa
604605

605606
if (versionOrTemplate != null) {
606607
if (versionOrTemplate instanceof DatasetVersion) {
607-
dsf.setDatasetVersion((DatasetVersion) versionOrTemplate);
608+
dsf.setDatasetVersion((DatasetVersion) versionOrTemplate);
608609
} else {
609610
dsf.setTemplate((Template) versionOrTemplate);
610611
}
611612
}
612613

613614
dsf.setParentDatasetFieldCompoundValue(parent);
614-
dsf.setControlledVocabularyValues(controlledVocabularyValues);
615+
616+
dsf.getControlledVocabularyValues().addAll(controlledVocabularyValues);
615617

616618
for (DatasetFieldValue dsfv : datasetFieldValues) {
617619
dsf.getDatasetFieldValues().add(dsfv.copy(dsf));

src/main/java/edu/harvard/iq/dataverse/DatasetFieldCompoundValue.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import jakarta.persistence.Transient;
2828

2929
import org.apache.commons.lang3.StringUtils;
30+
import org.apache.commons.lang3.Strings;
3031
import org.apache.commons.lang3.tuple.ImmutablePair;
3132
import org.apache.commons.lang3.tuple.Pair;
3233

@@ -254,4 +255,83 @@ private Map<DatasetField, String> removeLastComma(Map<DatasetField, String> mapI
254255

255256
return mapIn;
256257
}
258+
259+
260+
/**
261+
* Compares this DatasetFieldCompoundValue with another for equality based on
262+
* their child fields. Two compound values are considered equal if they have the
263+
* same child fields with the same values in the same order.
264+
*
265+
* @param other The DatasetFieldCompoundValue to compare with
266+
* @return true if both compound values have equal child fields, false otherwise
267+
*/
268+
public boolean valuesEqual(DatasetFieldCompoundValue other) {
269+
if (this == other) {
270+
return true;
271+
}
272+
if (other == null) {
273+
return false;
274+
}
275+
276+
List<DatasetField> children1 = this.getChildDatasetFields();
277+
List<DatasetField> children2 = other.getChildDatasetFields();
278+
279+
if (children1.size() != children2.size()) {
280+
return false;
281+
}
282+
283+
// Compare each child field
284+
for (DatasetField child1 : children1) {
285+
286+
DatasetField child2 = children2.stream()
287+
.filter(c -> c.getDatasetFieldType().equals(child1.getDatasetFieldType())).findFirst().orElse(null);
288+
289+
if (child2 == null) {
290+
return false;
291+
}
292+
293+
// Compare values based on field type
294+
if (child1.getDatasetFieldType().isControlledVocabulary()) {
295+
296+
List<ControlledVocabularyValue> cvs1 = child1.getControlledVocabularyValues();
297+
List<ControlledVocabularyValue> cvs2 = child2.getControlledVocabularyValues();
298+
299+
if (cvs1.size() != cvs2.size()) {
300+
301+
return false;
302+
}
303+
304+
for (ControlledVocabularyValue cv1Val : cvs1) {
305+
boolean found = cvs2.stream().anyMatch(cv2Val -> cv1Val.getStrValue().equals(cv2Val.getStrValue()));
306+
if (!found) {
307+
return false;
308+
}
309+
}
310+
} else {
311+
// Handle regular field values (including multiple values)
312+
List<DatasetFieldValue> dfvs1 = child1.getDatasetFieldValues();
313+
List<DatasetFieldValue> dfvs2 = child2.getDatasetFieldValues();
314+
315+
if (dfvs1.size() != dfvs2.size()) {
316+
return false;
317+
}
318+
319+
for (DatasetFieldValue dfv1 : dfvs1) {
320+
String value1 = dfv1.getValue();
321+
boolean found = dfvs2.stream()
322+
.anyMatch(dfv2 -> {
323+
String value2 = dfv2.getValue();
324+
if (value1 == null && value2 == null) {
325+
return true;
326+
}
327+
return value1 != null && value1.equals(value2);
328+
});
329+
if (!found) {
330+
return false;
331+
}
332+
}
333+
}
334+
}
335+
return true;
336+
}
257337
}

src/main/java/edu/harvard/iq/dataverse/DatasetFieldValue.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import jakarta.persistence.Table;
2323
import jakarta.persistence.Transient;
2424
import org.apache.commons.lang3.StringUtils;
25+
import org.apache.commons.lang3.Strings;
2526

2627
/**
2728
*
@@ -193,6 +194,22 @@ public DatasetFieldValue copy(DatasetField dsf) {
193194
dsfv.setValue(value);
194195

195196
return dsfv;
196-
}
197+
}
197198

199+
/**
200+
* Compares this DatasetFieldValue with another for equality based on their values.
201+
*
202+
* @param other The DatasetFieldValue to compare with
203+
* @return true if both values are equal (case-sensitive), false otherwise
204+
*/
205+
public boolean valuesEqual(DatasetFieldValue other) {
206+
if (this == other) {
207+
return true;
208+
}
209+
if (other == null) {
210+
return false;
211+
}
212+
return Strings.CS.equals(this.getValue(), other.getValue());
213+
}
214+
198215
}

src/main/java/edu/harvard/iq/dataverse/workflow/internalspi/COARNotifyRelationshipAnnouncementStep.java

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package edu.harvard.iq.dataverse.workflow.internalspi;
22

3+
import edu.harvard.iq.dataverse.ControlledVocabularyValue;
34
import edu.harvard.iq.dataverse.Dataset;
45
import edu.harvard.iq.dataverse.DatasetField;
6+
import edu.harvard.iq.dataverse.DatasetFieldCompoundValue;
57
import edu.harvard.iq.dataverse.DatasetFieldType;
8+
import edu.harvard.iq.dataverse.DatasetFieldValue;
69
import edu.harvard.iq.dataverse.DatasetVersion;
710
import edu.harvard.iq.dataverse.GlobalId;
811
import edu.harvard.iq.dataverse.branding.BrandingUtil;
@@ -26,7 +29,7 @@
2629
import java.net.URI;
2730
import java.net.URISyntaxException;
2831
import java.nio.charset.StandardCharsets;
29-
import java.util.Arrays;
32+
import java.util.ArrayList;
3033
import java.util.Collection;
3134
import java.util.HashMap;
3235
import java.util.Iterator;
@@ -88,12 +91,34 @@ public WorkflowStepResult run(WorkflowContext context) {
8891
// First check that we have what is required
8992
Dataset d = context.getDataset();
9093
DatasetVersion dv = d.getReleasedVersion();
94+
DatasetVersion priorVersion = d.getPriorReleasedVersion();
9195
List<DatasetField> dvf = dv.getDatasetFields();
9296
Map<String, DatasetField> fields = new HashMap<String, DatasetField>();
9397
List<String> reqFields = ListSplitUtil.split((String) context.getSettings().getOrDefault(COARNotifyRelationshipAnnouncementTriggerFields.toString(), ""));
98+
99+
Map<String, DatasetField> priorFields = new HashMap<String, DatasetField>();
100+
if (priorVersion != null) {
101+
for (DatasetField pdf : priorVersion.getDatasetFields()) {
102+
if (!pdf.isEmpty() && reqFields.contains(pdf.getDatasetFieldType().getName())) {
103+
priorFields.put(pdf.getDatasetFieldType().getName(), pdf);
104+
}
105+
}
106+
}
107+
94108
for (DatasetField df : dvf) {
95109
if (!df.isEmpty() && reqFields.contains(df.getDatasetFieldType().getName())) {
96-
fields.put(df.getDatasetFieldType().getName(), df);
110+
DatasetField priorField = priorFields.get(df.getDatasetFieldType().getName());
111+
112+
if (priorVersion == null || priorField == null) {
113+
// No prior version, include all values
114+
fields.put(df.getDatasetFieldType().getName(), df);
115+
} else {
116+
// Create a filtered field with only new values
117+
DatasetField filteredField = filterNewValues(df, priorField);
118+
if (!filteredField.isEmpty()) {
119+
fields.put(df.getDatasetFieldType().getName(), filteredField);
120+
}
121+
}
97122
}
98123
}
99124

@@ -412,4 +437,90 @@ private boolean isURI(String number) {
412437
return false;
413438
}
414439

440+
/**
441+
* Create a new DatasetField containing only values that are new compared to the
442+
* prior field. This creates a detached copy to avoid modifying the managed
443+
* entity.
444+
*
445+
* @param currentField The field from the current version
446+
* @param priorField The field from the prior version
447+
* @return A new DatasetField with only new values
448+
*/
449+
private DatasetField filterNewValues(DatasetField currentField, DatasetField priorField) {
450+
DatasetField filtered = new DatasetField();
451+
DatasetFieldType fieldType = currentField.getDatasetFieldType();
452+
filtered.setDatasetFieldType(fieldType);
453+
454+
// Handle primitive fields
455+
if (fieldType.isPrimitive()) {
456+
if (fieldType.isControlledVocabulary()) {
457+
// Handle controlled vocabulary fields
458+
List<ControlledVocabularyValue> currentCVs = currentField.getControlledVocabularyValues();
459+
List<ControlledVocabularyValue> priorCVs = priorField != null ? priorField.getControlledVocabularyValues() : new ArrayList<>();
460+
461+
List<ControlledVocabularyValue> newCVs = new ArrayList<>();
462+
for (ControlledVocabularyValue currentCV : currentCVs) {
463+
boolean isNew = true;
464+
for (ControlledVocabularyValue priorCV : priorCVs) {
465+
if (currentCV.getStrValue().equals(priorCV.getStrValue())) {
466+
isNew = false;
467+
break;
468+
}
469+
}
470+
if (isNew) {
471+
newCVs.add(currentCV);
472+
}
473+
}
474+
filtered.setControlledVocabularyValues(newCVs);
475+
} else {
476+
// Handle regular fields
477+
List<DatasetFieldValue> currentDFVs = currentField.getDatasetFieldValues();
478+
List<DatasetFieldValue> priorDFVs = priorField != null ? priorField.getDatasetFieldValues() : new ArrayList<>();
479+
480+
List<DatasetFieldValue> newDFVs = new ArrayList<>();
481+
for (DatasetFieldValue currentDFV : currentDFVs) {
482+
boolean isNew = true;
483+
for (DatasetFieldValue priorDFV : priorDFVs) {
484+
if (currentDFV.valuesEqual(priorDFV)) {
485+
isNew = false;
486+
break;
487+
}
488+
}
489+
if (isNew) {
490+
newDFVs.add(currentDFV);
491+
}
492+
}
493+
filtered.setDatasetFieldValues(newDFVs);
494+
}
495+
} else {
496+
// Handle compound fields
497+
List<DatasetFieldCompoundValue> currentCompounds = currentField.getDatasetFieldCompoundValues();
498+
List<DatasetFieldCompoundValue> priorCompounds = priorField != null ? priorField.getDatasetFieldCompoundValues() : new ArrayList<>();
499+
500+
List<DatasetFieldCompoundValue> newCompounds = new ArrayList<>();
501+
502+
for (DatasetFieldCompoundValue currentCompound : currentCompounds) {
503+
boolean isNew = true;
504+
505+
for (DatasetFieldCompoundValue priorCompound : priorCompounds) {
506+
507+
if (currentCompound.valuesEqual(priorCompound)) {
508+
isNew = false;
509+
break;
510+
}
511+
}
512+
513+
if (isNew) {
514+
// Create a copy of the compound value with all its children
515+
DatasetFieldCompoundValue newCompound = currentCompound.copy(filtered);
516+
newCompound.setParentDatasetField(filtered);
517+
newCompounds.add(newCompound);
518+
}
519+
}
520+
521+
filtered.setDatasetFieldCompoundValues(newCompounds);
522+
}
523+
524+
return filtered;
525+
}
415526
}

src/test/java/edu/harvard/iq/dataverse/api/LDNInboxIT.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
package edu.harvard.iq.dataverse.api;
32

43
import org.junit.jupiter.api.Test;

src/test/java/edu/harvard/iq/dataverse/api/LDNInboxTest.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
import edu.harvard.iq.dataverse.DataverseRoleServiceBean;
66
import edu.harvard.iq.dataverse.DataverseServiceBean;
77
import edu.harvard.iq.dataverse.GlobalId;
8-
import edu.harvard.iq.dataverse.MailServiceBean;
98
import edu.harvard.iq.dataverse.RoleAssigneeServiceBean;
109
import edu.harvard.iq.dataverse.RoleAssignment;
1110
import edu.harvard.iq.dataverse.UserNotification;
1211
import edu.harvard.iq.dataverse.UserNotificationServiceBean;
13-
import edu.harvard.iq.dataverse.api.ldn.COARNotifyRelationshipAnnouncement;
1412
import edu.harvard.iq.dataverse.authorization.DataverseRole;
1513
import edu.harvard.iq.dataverse.authorization.Permission;
1614
import edu.harvard.iq.dataverse.authorization.RoleAssignee;
@@ -19,7 +17,6 @@
1917
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
2018
import edu.harvard.iq.dataverse.settings.JvmSettings;
2119
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
22-
import edu.harvard.iq.dataverse.util.SystemConfig;
2320
import edu.harvard.iq.dataverse.util.testing.JvmSetting;
2421
import edu.harvard.iq.dataverse.util.testing.LocalJvmSettings;
2522
import jakarta.json.Json;
@@ -40,8 +37,6 @@
4037
import org.mockito.junit.jupiter.MockitoExtension;
4138

4239
import static jakarta.ws.rs.core.Response.Status.OK;
43-
import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
44-
import static jakarta.ws.rs.core.Response.Status.FORBIDDEN;
4540
import static org.junit.jupiter.api.Assertions.assertEquals;
4641
import static org.junit.jupiter.api.Assertions.assertThrows;
4742
import static org.junit.jupiter.api.Assertions.assertTrue;

0 commit comments

Comments
 (0)