Skip to content

Commit 15b7de7

Browse files
authored
Merge pull request #7379 from hapifhir/mergeback-2-rel-8-6
Mergeback 2 rel 8 6
2 parents 1766c60 + 197cb1e commit 15b7de7

File tree

28 files changed

+1090
-80
lines changed

28 files changed

+1090
-80
lines changed

hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,7 +1395,7 @@ public boolean equals(Object theO) {
13951395
TranslateCodeRequest that = (TranslateCodeRequest) theO;
13961396

13971397
return new EqualsBuilder()
1398-
.append(myCodings, that.myCodings)
1398+
.append(CodingEqualsAdapter.fromList(myCodings), CodingEqualsAdapter.fromList(that.myCodings))
13991399
.append(myTargetSystemUrl, that.myTargetSystemUrl)
14001400
.append(myConceptMapUrl, that.myConceptMapUrl)
14011401
.append(myConceptMapVersion, that.myConceptMapVersion)
@@ -1409,7 +1409,7 @@ public boolean equals(Object theO) {
14091409
@Override
14101410
public int hashCode() {
14111411
return new HashCodeBuilder(17, 37)
1412-
.append(myCodings)
1412+
.append(CodingEqualsAdapter.fromList(myCodings))
14131413
.append(myTargetSystemUrl)
14141414
.append(myConceptMapUrl)
14151415
.append(myConceptMapVersion)
@@ -1461,6 +1461,58 @@ public String toString() {
14611461
.append("reverse", myReverse)
14621462
.toString();
14631463
}
1464+
1465+
/**
1466+
* Adapter class that provides semantic equality comparison for {@link IBaseCoding} instances in the context
1467+
* of TranslateCodeRequests.
1468+
* <p>
1469+
* This adapter is used to compare codings based on their semantic content (system, code, and version)
1470+
* rather than object identity. This is essential for proper cache key comparison.
1471+
* </p>
1472+
* <p>
1473+
* The display value is intentionally excluded from equality comparison as it is not semantically
1474+
* significant for code translation operations.
1475+
* </p>
1476+
*/
1477+
private static class CodingEqualsAdapter {
1478+
private final IBaseCoding myCoding;
1479+
1480+
CodingEqualsAdapter(IBaseCoding theCoding) {
1481+
myCoding = theCoding;
1482+
}
1483+
1484+
@Override
1485+
public int hashCode() {
1486+
return new HashCodeBuilder(17, 37)
1487+
.append(myCoding.getSystem())
1488+
.append(myCoding.getCode())
1489+
.append(myCoding.getVersion())
1490+
.toHashCode();
1491+
}
1492+
1493+
@Override
1494+
public boolean equals(Object theO) {
1495+
1496+
if (this == theO) {
1497+
return true;
1498+
}
1499+
1500+
if (theO == null || getClass() != theO.getClass()) {
1501+
return false;
1502+
}
1503+
1504+
CodingEqualsAdapter that = (CodingEqualsAdapter) theO;
1505+
return new EqualsBuilder()
1506+
.append(myCoding.getSystem(), that.myCoding.getSystem())
1507+
.append(myCoding.getCode(), that.myCoding.getCode())
1508+
.append(myCoding.getVersion(), that.myCoding.getVersion())
1509+
.isEquals();
1510+
}
1511+
1512+
static List<CodingEqualsAdapter> fromList(List<IBaseCoding> theList) {
1513+
return theList.stream().map(CodingEqualsAdapter::new).toList();
1514+
}
1515+
}
14641516
}
14651517

14661518
/**

hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SingleValidationMessage.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@
2424
import org.apache.commons.lang3.builder.ToStringBuilder;
2525
import org.apache.commons.lang3.builder.ToStringStyle;
2626

27+
import java.util.ArrayList;
2728
import java.util.List;
2829

30+
import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
31+
2932
public class SingleValidationMessage {
3033

3134
private Integer myLocationCol;
@@ -43,6 +46,19 @@ public SingleValidationMessage() {
4346
super();
4447
}
4548

49+
/**
50+
* Copy constructor
51+
*/
52+
public SingleValidationMessage(SingleValidationMessage theMessage) {
53+
this.myLocationCol = theMessage.myLocationCol;
54+
this.myLocationLine = theMessage.myLocationLine;
55+
this.myLocationString = theMessage.myLocationString;
56+
this.myMessage = theMessage.myMessage;
57+
this.myMessageId = theMessage.myMessageId;
58+
this.mySeverity = theMessage.mySeverity;
59+
this.mySliceMessages = theMessage.mySliceMessages == null ? null : new ArrayList<>(theMessage.mySliceMessages);
60+
}
61+
4662
@Override
4763
public boolean equals(Object obj) {
4864
if (this == obj) {
@@ -155,4 +171,8 @@ public void setSliceMessages(List<String> theSliceMessages) {
155171
public List<String> getSliceMessages() {
156172
return mySliceMessages;
157173
}
174+
175+
public boolean hasSliceMessages() {
176+
return isNotEmpty(mySliceMessages);
177+
}
158178
}

hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import ca.uhn.fhir.context.FhirContext;
2323
import ca.uhn.fhir.rest.api.Constants;
2424
import ca.uhn.fhir.util.OperationOutcomeUtil;
25+
import org.apache.commons.lang3.Validate;
2526
import org.hl7.fhir.instance.model.api.IBase;
2627
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
2728

@@ -42,7 +43,7 @@ public class ValidationResult {
4243
private static final String ourNewLine = System.getProperty("line.separator");
4344
private final FhirContext myCtx;
4445
private final boolean myIsSuccessful;
45-
private final List<SingleValidationMessage> myMessages;
46+
private List<SingleValidationMessage> myMessages;
4647
private int myErrorDisplayLimit = ERROR_DISPLAY_LIMIT_DEFAULT;
4748

4849
public ValidationResult(FhirContext theCtx, List<SingleValidationMessage> theMessages) {
@@ -62,6 +63,11 @@ public List<SingleValidationMessage> getMessages() {
6263
return Collections.unmodifiableList(myMessages);
6364
}
6465

66+
public void setMessages(List<SingleValidationMessage> theMessages) {
67+
Validate.notNull(theMessages, "theMessages must not be null");
68+
myMessages = theMessages;
69+
}
70+
6571
/**
6672
* Was the validation successful (in other words, do we have no issues that are at
6773
* severity {@link ResultSeverityEnum#ERROR} or {@link ResultSeverityEnum#FATAL}. A validation
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package ca.uhn.fhir.validation;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
import static org.junit.jupiter.api.Assertions.assertNotSame;
11+
import static org.junit.jupiter.api.Assertions.assertNull;
12+
13+
class SingleValidationMessageTest {
14+
15+
@Test
16+
void testCopyConstructor_copiesAllFields() {
17+
// Setup
18+
SingleValidationMessage original = new SingleValidationMessage();
19+
original.setLocationCol(10);
20+
original.setLocationLine(20);
21+
original.setLocationString("Patient.name");
22+
original.setMessage("Validation error");
23+
original.setMessageId("msg-123");
24+
original.setSeverity(ResultSeverityEnum.ERROR);
25+
26+
List<String> sliceMessages = new ArrayList<>();
27+
sliceMessages.add("Slice message 1");
28+
sliceMessages.add("Slice message 2");
29+
original.setSliceMessages(sliceMessages);
30+
31+
// Execute
32+
SingleValidationMessage copy = new SingleValidationMessage(original);
33+
34+
// Verify
35+
assertEquals(original.getLocationCol(), copy.getLocationCol());
36+
assertEquals(original.getLocationLine(), copy.getLocationLine());
37+
assertEquals(original.getLocationString(), copy.getLocationString());
38+
assertEquals(original.getMessage(), copy.getMessage());
39+
assertEquals(original.getMessageId(), copy.getMessageId());
40+
assertEquals(original.getSeverity(), copy.getSeverity());
41+
assertEquals(original.getSliceMessages(), copy.getSliceMessages());
42+
}
43+
44+
@Test
45+
void testCopyConstructor_deepCopiesSliceMessages() {
46+
// Setup
47+
SingleValidationMessage original = new SingleValidationMessage();
48+
List<String> sliceMessages = new ArrayList<>();
49+
sliceMessages.add("Slice message 1");
50+
sliceMessages.add("Slice message 2");
51+
original.setSliceMessages(sliceMessages);
52+
53+
// Execute
54+
SingleValidationMessage copy = new SingleValidationMessage(original);
55+
56+
// Verify slice messages list is a deep copy (not same instance)
57+
assertNotSame(original.getSliceMessages(), copy.getSliceMessages());
58+
assertEquals(original.getSliceMessages(), copy.getSliceMessages());
59+
60+
// Modify original slice messages and verify copy is not affected
61+
original.getSliceMessages().add("Slice message 3");
62+
assertThat(copy.getSliceMessages()).hasSize(2);
63+
assertThat(original.getSliceMessages()).hasSize(3);
64+
}
65+
66+
@Test
67+
void testCopyConstructor_handlesNullSliceMessages() {
68+
// Setup
69+
SingleValidationMessage original = new SingleValidationMessage();
70+
original.setLocationCol(5);
71+
original.setMessage("Error message");
72+
original.setSliceMessages(null);
73+
74+
// Execute
75+
SingleValidationMessage copy = new SingleValidationMessage(original);
76+
77+
// Verify
78+
assertNull(copy.getSliceMessages());
79+
assertEquals(original.getLocationCol(), copy.getLocationCol());
80+
assertEquals(original.getMessage(), copy.getMessage());
81+
}
82+
83+
@Test
84+
void testCopyConstructor_copiesEmptySliceMessages() {
85+
// Setup
86+
SingleValidationMessage original = new SingleValidationMessage();
87+
original.setSliceMessages(new ArrayList<>());
88+
89+
// Execute
90+
SingleValidationMessage copy = new SingleValidationMessage(original);
91+
92+
// Verify
93+
assertNotSame(original.getSliceMessages(), copy.getSliceMessages());
94+
assertThat(copy.getSliceMessages()).isEmpty();
95+
}
96+
97+
@Test
98+
void testCopyConstructor_copiedObjectIsEqual() {
99+
// Setup
100+
SingleValidationMessage original = new SingleValidationMessage();
101+
original.setLocationCol(15);
102+
original.setLocationLine(25);
103+
original.setLocationString("Observation.value");
104+
original.setMessage("Value is required");
105+
original.setMessageId("obs-123");
106+
original.setSeverity(ResultSeverityEnum.WARNING);
107+
108+
// Execute
109+
SingleValidationMessage copy = new SingleValidationMessage(original);
110+
111+
// Verify
112+
assertEquals(original, copy);
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: fix
3+
issue: 7355
4+
title: "Previously, the `ValidationMessageSuppressingInterceptor` did not apply message suppression patterns
5+
to the slice messages of a `ValidationResult`. This has been fixed."
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
type: fix
3+
issue: 7361
4+
title: "A bug was fixed where chained search parameters with invalid values (e.g.,
5+
`Observation?subject.birthdate=invaliddate` or `DiagnosticReport?result.value-quantity=invalidquantity`)
6+
incorrectly returned HTTP 500 Internal Server Error instead of HTTP 400 Bad Request."
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
type: fix
3+
issue: 7366
4+
title: "Previously, if multiple validation post-processing interceptors
5+
(`ValidationMessageSuppressingInterceptor`, `ValidationMessagePostProcessingInterceptor`, or `ValidationMessageUnknownCodeSystemPostProcessingInterceptor`)
6+
were registered that modified a `ValidationResult`, only the first interceptor was executed. This has been fixed."
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
type: fix
3+
issue: 7367
4+
title: "Previously, when the Remote Terminology Service was enabled and its base URL was set to
5+
[https://r4.ontoserver.csiro.au/fhir/](https://r4.ontoserver.csiro.au/fhir/), some `$validate` requests returned
6+
a 404 Not Found, and the OperationOutcome in the response contained error messages indicating the referenced `ValueSet`
7+
could not be found. This has now been fixed."
8+
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: fix
3+
issue: 7371
4+
title: "Fixed an issue where ConceptMap $translate operation caching was not working properly,
5+
causing unnecessary cache misses for semantically identical requests."
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
type: fix
3+
issue: 7378
4+
title: "The STORAGE_PARTITION_IDENTIFY_ANY hook was recently removed from RequestTenantPartitionInterceptor which caused BaseRequestPartitionHelperSvc.determineGenericPartitionForRequest() to fail in Request Tenant mode. This change restores the functionality of BaseRequestPartitionHelperSvc.determineGenericPartitionForRequest() by falling back to STORAGE_PARTITION_IDENTIFY_READ if STORAGE_PARTITION_IDENTIFY_ANY is not set."

0 commit comments

Comments
 (0)