Merge pull request #34 from jjathman/remove-validation-exception

Fixes #32 - removes new validation API that doesn't use exceptions
This commit is contained in:
James Agnew 2014-10-17 15:41:21 -04:00
commit d140e0b829
4 changed files with 240 additions and 57 deletions

View File

@ -20,18 +20,15 @@ package ca.uhn.fhir.validation;
* #L% * #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.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome; 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.) * 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. * 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 * The resource to validate
* @throws ValidationFailureException * @throws ValidationFailureException
* If the validation fails * If the validation fails
* @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.Bundle)} instead
*/ */
@Deprecated
public void validate(Bundle theBundle) { public void validate(Bundle theBundle) {
Validate.notNull(theBundle, "theBundle must not be null"); ValidationResult validationResult = validateWithResult(theBundle);
if (!validationResult.isSuccessful()) {
ValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle); throw new ValidationFailureException(validationResult.getOperationOutcome());
}
for (IValidator next : myValidators) {
next.validateBundle(ctx);
}
OperationOutcome oo = ctx.getOperationOutcome();
if (oo != null && oo.getIssue().size() > 0) {
throw new ValidationFailureException(oo);
}
} }
/** /**
@ -161,21 +151,54 @@ public class FhirValidator {
* The resource to validate * The resource to validate
* @throws ValidationFailureException * @throws ValidationFailureException
* If the validation fails * If the validation fails
* @deprecated use {@link #validateWithResult(ca.uhn.fhir.model.api.IResource)} instead
*/ */
@Deprecated
public void validate(IResource theResource) throws ValidationFailureException { 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<IResource> 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) { ValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);
next.validateResource(ctx);
}
OperationOutcome oo = ctx.getOperationOutcome(); for (IValidator next : myValidators) {
if (oo != null && oo.getIssue().size() > 0) { next.validateBundle(ctx);
throw new ValidationFailureException(oo); }
}
} 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<IResource> ctx = ValidationContext.forResource(myContext, theResource);
for (IValidator next : myValidators) {
next.validateResource(ctx);
}
OperationOutcome oo = ctx.getOperationOutcome();
return ValidationResult.valueOf(oo);
}
} }

View File

@ -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();
}
}

View File

@ -1,17 +1,22 @@
package ca.uhn.fhir.validation; 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.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; 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.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.ContactSystemEnum; 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 { public class ResourceValidatorTest {
@ -45,9 +50,7 @@ public class ResourceValidatorTest {
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml")); String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml"));
Bundle b = ourCtx.newXmlParser().parseBundle(res); Bundle b = ourCtx.newXmlParser().parseBundle(res);
FhirValidator val = ourCtx.newValidator(); FhirValidator val = createFhirValidator();
val.setValidateAgainstStandardSchema(true);
val.setValidateAgainstStandardSchematron(true);
val.validate(b); val.validate(b);
@ -64,7 +67,6 @@ public class ResourceValidatorTest {
} }
} }
@Test @Test
public void testSchematronResourceValidator() throws IOException { public void testSchematronResourceValidator() throws IOException {
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("patient-example-dicom.xml")); String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("patient-example-dicom.xml"));
@ -74,20 +76,60 @@ public class ResourceValidatorTest {
val.setValidateAgainstStandardSchema(false); val.setValidateAgainstStandardSchema(false);
val.setValidateAgainstStandardSchematron(true); val.setValidateAgainstStandardSchematron(true);
val.validate(p); ValidationResult validationResult = val.validateWithResult(p);
assertTrue(validationResult.isSuccessful());
p.getTelecomFirstRep().setValue("123-4567"); p.getTelecomFirstRep().setValue("123-4567");
try { validationResult = val.validateWithResult(p);
val.validate(p); assertFalse(validationResult.isSuccessful());
fail(); OperationOutcome operationOutcome = validationResult.getOperationOutcome();
} catch (ValidationFailureException e) { ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); assertEquals(1, operationOutcome.getIssue().size());
assertEquals(1, e.getOperationOutcome().getIssue().size()); assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
assertThat(e.getOperationOutcome().getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
}
p.getTelecomFirstRep().setSystem(ContactSystemEnum.EMAIL); 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;
}
} }

View File

@ -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<OperationOutcome.Issue> 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));
}
}