Skip to content

Commit

Permalink
Fixes #32
Browse files Browse the repository at this point in the history
Creates new methods that return a ValidationResult object instead of throwing an exception which requires catching and inspecting the exception itself. Migrating from the old to the new API should be pretty simple as the method calls match quite closely.
  • Loading branch information
Joseph Athman committed Oct 15, 2014
1 parent 2a9d92d commit 2741f1c
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 62 deletions.
Expand Up @@ -20,18 +20,15 @@
* #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.)
Expand Down Expand Up @@ -133,25 +130,18 @@ public void setValidateAgainstStandardSchematron(boolean theValidateAgainstStand
/**
* 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<Bundle> 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());
}
}

/**
Expand All @@ -161,21 +151,54 @@ public void validate(Bundle theBundle) {
* 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");

ValidationContext<IResource> ctx = ValidationContext.forResource(myContext, theResource);

for (IValidator next : myValidators) {
next.validateResource(ctx);
}

OperationOutcome oo = ctx.getOperationOutcome();
if (oo != null && oo.getIssue().size() > 0) {
throw new ValidationFailureException(oo);
}

}

ValidationResult validationResult = validateWithResult(theResource);
if (!validationResult.isSuccessful()) {
throw new ValidationFailureException(validationResult.getOperationOutcome());
}
}

/**
* 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");

ValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);

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<IResource> ctx = ValidationContext.forResource(myContext, theResource);

for (IValidator next : myValidators) {
next.validateResource(ctx);
}

OperationOutcome oo = ctx.getOperationOutcome();
return ValidationResult.valueOf(oo);
}
}
@@ -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();
}
}
@@ -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 {

Expand Down Expand Up @@ -45,9 +50,7 @@ public void testSchemaBundleValidator() throws IOException {
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);

Expand All @@ -64,7 +67,6 @@ public void testSchemaBundleValidator() throws IOException {
}
}


@Test
public void testSchematronResourceValidator() throws IOException {
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("patient-example-dicom.xml"));
Expand All @@ -74,20 +76,60 @@ public void testSchematronResourceValidator() throws IOException {
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;
}
}
@@ -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));
}
}

0 comments on commit 2741f1c

Please sign in to comment.