Skip to content

Commit 41c2e49

Browse files
TipzCMleif stawnyczyBulandMalikpeartree
authored
Validation with unknown profile (hapifhir#6608)
* failing test for invalid profile during validation * to address validation to fail for unknown profile * updates to address bringing back the class name that was mistakenly dropped out * updated the logic and some dependent test cases * spotless:apply changes * fixed the existing test cases that were failling due to invalid profile validation logic being added * remove unnecessary comments * added changelog * added jira: SMILE-9129 to changelog * addressing PR comments * spotless:apply updates * addressing code review suggestions * code review updates --------- Co-authored-by: leif stawnyczy <[email protected]> Co-authored-by: Buland Malik <[email protected]> Co-authored-by: peartree <[email protected]>
1 parent 250dae3 commit 41c2e49

File tree

6 files changed

+85
-12
lines changed

6 files changed

+85
-12
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: fix
3+
issue: 6608
4+
jira: SMILE-9129
5+
title: "Previously, attempting to validate a resource ($validate) with an invalid profile was not generating any error. This issue is fixed."

hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import ca.uhn.fhir.util.OperationOutcomeUtil;
3333
import ca.uhn.fhir.util.StopWatch;
3434
import ca.uhn.fhir.validation.IValidatorModule;
35+
import ca.uhn.fhir.validation.ResultSeverityEnum;
3536
import ca.uhn.test.util.LogbackTestExtension;
3637
import ca.uhn.test.util.LogbackTestExtensionAssert;
3738
import ch.qos.logback.classic.Level;
@@ -981,7 +982,10 @@ public void testValidateValueSet() {
981982
OperationOutcome oo = validateAndReturnOutcome(vs);
982983
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
983984

984-
assertEquals("The code '123' is not valid in the system https://bb (Validation failed)", oo.getIssue().get(0).getDiagnostics());
985+
assertThat(oo.getIssue().stream())
986+
.anyMatch(r ->
987+
r.getDiagnostics().equals("The code '123' is not valid in the system https://bb (Validation failed)") );
988+
985989
}
986990

987991
@Test

hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ public List<ValidationMessage> validate(
143143
List<ValidationMessage> messages = new ArrayList<>();
144144

145145
List<StructureDefinition> profiles = new ArrayList<>();
146+
List<ValidationMessage> invalidProfileValidationMessages = new ArrayList<>();
146147
for (String nextProfileUrl : theValidationContext.getOptions().getProfiles()) {
147-
fetchAndAddProfile(theWorkerContext, profiles, nextProfileUrl, messages);
148+
fetchAndAddProfile(theWorkerContext, profiles, nextProfileUrl, invalidProfileValidationMessages);
148149
}
149150

150151
String input = theValidationContext.getResourceAsString();
@@ -167,7 +168,7 @@ public List<ValidationMessage> validate(
167168
// Determine if meta/profiles are present...
168169
ArrayList<String> profileUrls = determineIfProfilesSpecified(document);
169170
for (String nextProfileUrl : profileUrls) {
170-
fetchAndAddProfile(theWorkerContext, profiles, nextProfileUrl, messages);
171+
fetchAndAddProfile(theWorkerContext, profiles, nextProfileUrl, invalidProfileValidationMessages);
171172
}
172173

173174
Manager.FhirFormat format = Manager.FhirFormat.XML;
@@ -185,7 +186,8 @@ public List<ValidationMessage> validate(
185186
JsonArray profilesArray = profileElement.getAsJsonArray();
186187
for (JsonElement element : profilesArray) {
187188
String nextProfileUrl = element.getAsString();
188-
fetchAndAddProfile(theWorkerContext, profiles, nextProfileUrl, messages);
189+
fetchAndAddProfile(
190+
theWorkerContext, profiles, nextProfileUrl, invalidProfileValidationMessages);
189191
}
190192
}
191193
}
@@ -196,6 +198,10 @@ public List<ValidationMessage> validate(
196198
throw new IllegalArgumentException(Msg.code(649) + "Unknown encoding: " + encoding);
197199
}
198200

201+
if (profiles.isEmpty() && !invalidProfileValidationMessages.isEmpty()) {
202+
messages.addAll(invalidProfileValidationMessages);
203+
}
204+
199205
// TODO: are these still needed?
200206
messages = messages.stream()
201207
.filter(m -> m.getMessageId() == null
@@ -234,11 +240,17 @@ private void fetchAndAddProfile(
234240
IWorkerContext theWorkerContext,
235241
List<StructureDefinition> theProfileStructureDefinitions,
236242
String theUrl,
237-
List<ValidationMessage> theMessages) {
243+
List<ValidationMessage> theValidationMessages) {
238244
try {
239245
StructureDefinition structureDefinition = theWorkerContext.fetchResource(StructureDefinition.class, theUrl);
240246
if (structureDefinition != null) {
241247
theProfileStructureDefinitions.add(structureDefinition);
248+
} else {
249+
ValidationMessage m = new ValidationMessage();
250+
m.setMessageId(I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN);
251+
m.setLevel(ValidationMessage.IssueSeverity.ERROR);
252+
m.setMessage("Invalid profile. Failed to retrieve profile with url=" + theUrl);
253+
theValidationMessages.add(m);
242254
}
243255
} catch (FHIRException e) {
244256
ourLog.debug("Failed to load profile: {}", theUrl);

hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ public void testValidateStorageResponseCodeBad() {
233233
List<SingleValidationMessage> all = logResultsAndReturnErrorOnes(result);
234234
assertThat(result.isSuccessful()).as(all.toString()).isFalse();
235235
assertThat(result.getMessages().get(0).getMessage()).startsWith("Unknown code 'https://hapifhir.io/fhir/CodeSystem/hapi-fhir-storage-response-code#foo'");
236-
237236
}
238237

239238
@Test
@@ -1123,9 +1122,11 @@ public void testValidateResourceContainingProfileDeclarationDoesntResolve() {
11231122
ValidationResult output = myFhirValidator.validateWithResult(input);
11241123
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
11251124

1126-
assertThat(errors).hasSize(1);
1127-
assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found", errors.get(0).getMessage());
1128-
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
1125+
assertThat(errors).hasSize(2);
1126+
assertThat(errors.stream())
1127+
.anyMatch(r ->
1128+
(r.getSeverity() == ResultSeverityEnum.ERROR) &&
1129+
(r.getMessage().equals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found")) );
11291130
}
11301131

11311132
@Test

hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/ParserWithValidationR4Test.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
55
import ca.uhn.fhir.context.support.IValidationSupport;
66
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
7+
import ca.uhn.fhir.parser.IParser;
78
import ca.uhn.fhir.validation.FhirValidator;
9+
import ca.uhn.fhir.validation.ResultSeverityEnum;
10+
import ca.uhn.fhir.validation.ValidationOptions;
811
import ca.uhn.fhir.validation.ValidationResult;
912
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
1013
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
1114
import org.hl7.fhir.r4.model.MedicationRequest;
15+
import org.hl7.fhir.r4.model.Patient;
1216
import org.junit.jupiter.api.Test;
1317

1418
import java.io.IOException;
1519

20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.junit.jupiter.api.Assertions.assertFalse;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
1624
public class ParserWithValidationR4Test extends BaseValidationTestWithInlineMocks {
1725
private static final FhirContext ourCtx = FhirContext.forR4();
1826

@@ -30,6 +38,46 @@ public void testActivityDefinitionElementsOrder() throws IOException {
3038
validationResult.getMessages().forEach(System.out::println);
3139
}
3240

41+
// https://www.hl7.org/fhir/r4/resource-operation-validate.html
42+
@Test
43+
public void validate_withUnknownProfile_shouldFail() {
44+
// setup
45+
IParser parser = ourCtx.newJsonParser();
46+
Patient patient;
47+
{
48+
String patientStr = """
49+
{
50+
"resourceType": "Patient",
51+
"name": [{
52+
"family": "Hirasawa",
53+
"given": [ "yui" ]
54+
}]
55+
}
56+
""";
57+
patient = parser.parseResource(Patient.class, patientStr);
58+
}
59+
60+
// test
61+
ValidationOptions options = new ValidationOptions();
62+
String profileUrl = "http://tempuri.org/does-not-exist";
63+
options.addProfile(profileUrl);
64+
65+
final FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ourCtx);
66+
FhirValidator validator = ourCtx.newValidator();
67+
68+
// test
69+
validator.registerValidatorModule(instanceValidator);
70+
ValidationResult validationResult = validator.validateWithResult(patient, options);
71+
72+
// verify
73+
assertFalse(validationResult.isSuccessful());
74+
assertThat(validationResult.getMessages().stream())
75+
.anyMatch(r ->
76+
(r.getSeverity() == ResultSeverityEnum.ERROR) &&
77+
(r.getMessage().equals("Invalid profile. Failed to retrieve profile with url="+profileUrl)) );
78+
}
79+
80+
3381
private IValidationSupport getValidationSupport() {
3482
return new ValidationSupportChain(new DefaultProfileValidationSupport(ourCtx));
3583
}

hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,9 +1022,12 @@ public void testValidateResourceContainingProfileDeclarationDoesntResolve() {
10221022
ValidationResult output = myFhirValidator.validateWithResult(input);
10231023
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
10241024

1025-
assertThat(errors).hasSize(1);
1026-
assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found", errors.get(0).getMessage());
1027-
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
1025+
assertThat(errors).hasSize(2);
1026+
assertThat(errors.stream())
1027+
.anyMatch(r ->
1028+
(r.getSeverity() == ResultSeverityEnum.ERROR) &&
1029+
(r.getMessage().equals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found")) );
1030+
10281031
}
10291032

10301033
@Test

0 commit comments

Comments
 (0)