diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java index cdd8ee28349..652d89941c2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java @@ -20,18 +20,15 @@ package ca.uhn.fhir.validation; * #L% */ -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.lang3.Validate; - -import com.phloc.schematron.ISchematronResource; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu.resource.OperationOutcome; +import org.apache.commons.lang3.Validate; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; /** * Resource validator, which checks resources for compliance against various validation schemes (schemas, schematrons, etc.) @@ -133,25 +130,18 @@ public class FhirValidator { /** * Validates a bundle instance, throwing a {@link ValidationFailureException} if the validation fails. This validation includes validation of all resources in the bundle. * - * @param theResource + * @param theBundle * The resource to validate * @throws ValidationFailureException * If the validation fails + * @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.Bundle)} instead */ + @Deprecated public void validate(Bundle theBundle) { - Validate.notNull(theBundle, "theBundle must not be null"); - - ValidationContext ctx = ValidationContext.forBundle(myContext, theBundle); - - for (IValidator next : myValidators) { - next.validateBundle(ctx); - } - - OperationOutcome oo = ctx.getOperationOutcome(); - if (oo != null && oo.getIssue().size() > 0) { - throw new ValidationFailureException(oo); - } - + ValidationResult validationResult = validateWithResult(theBundle); + if (!validationResult.isSuccessful()) { + throw new ValidationFailureException(validationResult.getOperationOutcome()); + } } /** @@ -161,21 +151,54 @@ public class FhirValidator { * The resource to validate * @throws ValidationFailureException * If the validation fails + * @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.IResource)} instead */ + @Deprecated public void validate(IResource theResource) throws ValidationFailureException { - Validate.notNull(theResource, "theResource must not be null"); + ValidationResult validationResult = validateWithResult(theResource); + if (!validationResult.isSuccessful()) { + throw new ValidationFailureException(validationResult.getOperationOutcome()); + } + } - ValidationContext ctx = ValidationContext.forResource(myContext, theResource); + /** + * Validates a bundle instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results. + * This validation includes validation of all resources in the bundle. + * + * @param theBundle the bundle to validate + * @return the results of validation + * @since 0.7 + */ + public ValidationResult validateWithResult(Bundle theBundle) { + Validate.notNull(theBundle, "theBundle must not be null"); - for (IValidator next : myValidators) { - next.validateResource(ctx); - } + ValidationContext ctx = ValidationContext.forBundle(myContext, theBundle); - OperationOutcome oo = ctx.getOperationOutcome(); - if (oo != null && oo.getIssue().size() > 0) { - throw new ValidationFailureException(oo); - } + for (IValidator next : myValidators) { + next.validateBundle(ctx); + } - } + OperationOutcome oo = ctx.getOperationOutcome(); + return ValidationResult.valueOf(oo); + } + /** + * Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results. + * + * @param theResource the resource to validate + * @return the results of validation + * @since 0.7 + */ + public ValidationResult validateWithResult(IResource theResource) { + Validate.notNull(theResource, "theResource must not be null"); + + ValidationContext ctx = ValidationContext.forResource(myContext, theResource); + + for (IValidator next : myValidators) { + next.validateResource(ctx); + } + + OperationOutcome oo = ctx.getOperationOutcome(); + return ValidationResult.valueOf(oo); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java new file mode 100644 index 00000000000..06126f1fdcc --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationResult.java @@ -0,0 +1,72 @@ +package ca.uhn.fhir.validation; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.model.dstu.resource.OperationOutcome; + +/** + * Encapsulates the results of validation + * + * @see ca.uhn.fhir.validation.FhirValidator + * @since 0.7 + */ +public class ValidationResult { + private OperationOutcome myOperationOutcome; + + private ValidationResult(OperationOutcome myOperationOutcome) { + this.myOperationOutcome = myOperationOutcome; + } + + public static ValidationResult valueOf(OperationOutcome myOperationOutcome) { + return new ValidationResult(myOperationOutcome); + } + + public OperationOutcome getOperationOutcome() { + return myOperationOutcome; + } + + @Override + public String toString() { + return "ValidationResult{" + + "myOperationOutcome=" + myOperationOutcome + + ", description='" + toDescription() + '\'' + + '}'; + } + + private String toDescription() { + StringBuilder b = new StringBuilder(100); + if (myOperationOutcome != null) { + OperationOutcome.Issue issueFirstRep = myOperationOutcome.getIssueFirstRep(); + b.append(issueFirstRep.getDetails().getValue()); + b.append(" - "); + b.append(issueFirstRep.getLocationFirstRep().getValue()); + } + return b.toString(); + } + + /** + * Was the validation successful + * @return true if the validation was successful + */ + public boolean isSuccessful() { + return myOperationOutcome == null || myOperationOutcome.getIssue().isEmpty(); + } +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java index e61b1329867..9ec1ebdf5b5 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java @@ -1,17 +1,22 @@ package ca.uhn.fhir.validation; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - -import java.io.IOException; - -import org.apache.commons.io.IOUtils; -import org.junit.Test; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.dstu.resource.OperationOutcome; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.valueset.ContactSystemEnum; +import org.apache.commons.io.IOUtils; +import org.junit.Test; + +import java.io.IOException; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public class ResourceValidatorTest { @@ -45,9 +50,7 @@ public class ResourceValidatorTest { String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml")); Bundle b = ourCtx.newXmlParser().parseBundle(res); - FhirValidator val = ourCtx.newValidator(); - val.setValidateAgainstStandardSchema(true); - val.setValidateAgainstStandardSchematron(true); + FhirValidator val = createFhirValidator(); val.validate(b); @@ -64,7 +67,6 @@ public class ResourceValidatorTest { } } - @Test public void testSchematronResourceValidator() throws IOException { String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("patient-example-dicom.xml")); @@ -74,20 +76,60 @@ public class ResourceValidatorTest { val.setValidateAgainstStandardSchema(false); val.setValidateAgainstStandardSchematron(true); - val.validate(p); + ValidationResult validationResult = val.validateWithResult(p); + assertTrue(validationResult.isSuccessful()); + + p.getTelecomFirstRep().setValue("123-4567"); + validationResult = val.validateWithResult(p); + assertFalse(validationResult.isSuccessful()); + OperationOutcome operationOutcome = validationResult.getOperationOutcome(); + ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome)); + assertEquals(1, operationOutcome.getIssue().size()); + assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided.")); - p.getTelecomFirstRep().setValue("123-4567"); - try { - val.validate(p); - fail(); - } catch (ValidationFailureException e) { - ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); - assertEquals(1, e.getOperationOutcome().getIssue().size()); - assertThat(e.getOperationOutcome().getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided.")); - } - p.getTelecomFirstRep().setSystem(ContactSystemEnum.EMAIL); - val.validate(p); + validationResult = val.validateWithResult(p); + assertTrue(validationResult.isSuccessful()); } - + + @Test + public void testSchemaBundleValidatorIsSuccessful() throws IOException { + String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml")); + Bundle b = ourCtx.newXmlParser().parseBundle(res); + + FhirValidator val = createFhirValidator(); + + ValidationResult result = val.validateWithResult(b); + assertTrue(result.isSuccessful()); + OperationOutcome operationOutcome = result.getOperationOutcome(); + assertNotNull(operationOutcome); + assertEquals(0, operationOutcome.getIssue().size()); + } + + @Test + public void testSchemaBundleValidatorFails() throws IOException { + String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml")); + Bundle b = ourCtx.newXmlParser().parseBundle(res); + + FhirValidator val = createFhirValidator(); + + ValidationResult validationResult = val.validateWithResult(b); + assertTrue(validationResult.isSuccessful()); + + Patient p = (Patient) b.getEntries().get(0).getResource(); + p.getTelecomFirstRep().setValue("123-4567"); + validationResult = val.validateWithResult(b); + assertFalse(validationResult.isSuccessful()); + OperationOutcome operationOutcome = validationResult.getOperationOutcome(); + ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome)); + assertEquals(1, operationOutcome.getIssue().size()); + assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided.")); + } + + private FhirValidator createFhirValidator() { + FhirValidator val = ourCtx.newValidator(); + val.setValidateAgainstStandardSchema(true); + val.setValidateAgainstStandardSchematron(true); + return val; + } } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ValidationResultTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ValidationResultTest.java new file mode 100644 index 00000000000..2fe2979119e --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ValidationResultTest.java @@ -0,0 +1,46 @@ +package ca.uhn.fhir.validation; + +import ca.uhn.fhir.model.dstu.resource.OperationOutcome; +import org.junit.Test; + +import java.util.List; +import java.util.UUID; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class ValidationResultTest { + + @Test + public void isSuccessful_IsTrueForNullOperationOutcome() { + ValidationResult result = ValidationResult.valueOf(null); + assertTrue(result.isSuccessful()); + } + + @Test + public void isSuccessful_IsTrueForNoIssues() { + OperationOutcome operationOutcome = new OperationOutcome(); + // make sure a non-null ID doesn't cause the validation result to be a fail + operationOutcome.setId(UUID.randomUUID().toString()); + ValidationResult result = ValidationResult.valueOf(operationOutcome); + assertTrue(result.isSuccessful()); + } + + @Test + public void isSuccessful_FalseForIssues() { + OperationOutcome operationOutcome = new OperationOutcome(); + OperationOutcome.Issue issue = operationOutcome.addIssue(); + String errorMessage = "There was a validation problem"; + issue.setDetails(errorMessage); + ValidationResult result = ValidationResult.valueOf(operationOutcome); + assertFalse(result.isSuccessful()); + List issues = result.getOperationOutcome().getIssue(); + assertEquals(1, issues.size()); + assertEquals(errorMessage, issues.get(0).getDetails().getValue()); + + assertThat("ValidationResult#toString should contain the issue description", result.toString(), containsString(errorMessage)); + } +} \ No newline at end of file