validate operation ignores parameter profile (#1417)
* This should be working now - need to check tests * Add a changelog and some docs * One more test fix * Tests should be passing * Fix compile * Test fixes * Ignore outdated DSTU2 validation test
This commit is contained in:
parent
1b17097f62
commit
dd0cb10dbb
|
@ -181,7 +181,13 @@ public class ValidatorExamples {
|
|||
|
||||
// Validate
|
||||
ValidationResult result = validator.validateWithResult(obs);
|
||||
|
||||
|
||||
/*
|
||||
* Note: You can also explicitly declare a profile to validate against
|
||||
* using the block below.
|
||||
*/
|
||||
// ValidationResult result = validator.validateWithResult(obs, new ValidationOptions().addProfile("http://myprofile.com"));
|
||||
|
||||
// Do we have any errors or fatal errors?
|
||||
System.out.println(result.isSuccessful()); // false
|
||||
|
||||
|
|
|
@ -31,10 +31,17 @@ abstract class BaseValidationContext<T> implements IValidationContext<T> {
|
|||
protected final FhirContext myFhirContext;
|
||||
|
||||
private List<SingleValidationMessage> myMessages;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
BaseValidationContext(FhirContext theFhirContext) {
|
||||
this(theFhirContext, new ArrayList<SingleValidationMessage>());
|
||||
this(theFhirContext, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
BaseValidationContext(FhirContext theFhirContext, List<SingleValidationMessage> theMessages) {
|
||||
myFhirContext = theFhirContext;
|
||||
myMessages = theMessages;
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.validation.schematron.SchematronProvider;
|
||||
|
||||
/**
|
||||
|
@ -46,7 +45,7 @@ public class FhirValidator {
|
|||
|
||||
private static volatile Boolean ourPhPresentOnClasspath;
|
||||
private final FhirContext myContext;
|
||||
private List<IValidatorModule> myValidators = new ArrayList<IValidatorModule>();
|
||||
private List<IValidatorModule> myValidators = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
|
||||
|
@ -173,25 +172,6 @@ public class FhirValidator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a resource instance, throwing a {@link ValidationFailureException} if the validation fails
|
||||
*
|
||||
* @param theResource
|
||||
* The resource to validate
|
||||
* @throws ValidationFailureException
|
||||
* If the validation fails
|
||||
* @deprecated use {@link #validateWithResult(IBaseResource)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void validate(IResource theResource) throws ValidationFailureException {
|
||||
|
||||
applyDefaultValidators();
|
||||
|
||||
ValidationResult validationResult = validateWithResult(theResource);
|
||||
if (!validationResult.isSuccessful()) {
|
||||
throw new ValidationFailureException(myContext, validationResult.toOperationOutcome());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -203,11 +183,37 @@ public class FhirValidator {
|
|||
* @since 0.7
|
||||
*/
|
||||
public ValidationResult validateWithResult(IBaseResource theResource) {
|
||||
return validateWithResult(theResource, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 1.1
|
||||
*/
|
||||
public ValidationResult validateWithResult(String theResource) {
|
||||
return validateWithResult(theResource, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a resource instance returning a {@link ca.uhn.fhir.validation.ValidationResult} which contains the results.
|
||||
*
|
||||
* @param theResource
|
||||
* the resource to validate
|
||||
* @param theOptions
|
||||
* Optionally provides options to the validator
|
||||
* @return the results of validation
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public ValidationResult validateWithResult(IBaseResource theResource, ValidationOptions theOptions) {
|
||||
Validate.notNull(theResource, "theResource must not be null");
|
||||
|
||||
|
||||
applyDefaultValidators();
|
||||
|
||||
IValidationContext<IBaseResource> ctx = ValidationContext.forResource(myContext, theResource);
|
||||
|
||||
IValidationContext<IBaseResource> ctx = ValidationContext.forResource(myContext, theResource, theOptions);
|
||||
|
||||
for (IValidatorModule next : myValidators) {
|
||||
next.validateResource(ctx);
|
||||
|
@ -221,15 +227,17 @@ public class FhirValidator {
|
|||
*
|
||||
* @param theResource
|
||||
* the resource to validate
|
||||
* @param theOptions
|
||||
* Optionally provides options to the validator
|
||||
* @return the results of validation
|
||||
* @since 1.1
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public ValidationResult validateWithResult(String theResource) {
|
||||
public ValidationResult validateWithResult(String theResource, ValidationOptions theOptions) {
|
||||
Validate.notNull(theResource, "theResource must not be null");
|
||||
|
||||
|
||||
applyDefaultValidators();
|
||||
|
||||
IValidationContext<IBaseResource> ctx = ValidationContext.forText(myContext, theResource);
|
||||
|
||||
IValidationContext<IBaseResource> ctx = ValidationContext.forText(myContext, theResource, theOptions);
|
||||
|
||||
for (IValidatorModule next : myValidators) {
|
||||
next.validateResource(ctx);
|
||||
|
@ -237,5 +245,4 @@ public class FhirValidator {
|
|||
|
||||
return ctx.toResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import java.util.List;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface IValidationContext<T> {
|
||||
|
||||
FhirContext getFhirContext();
|
||||
|
@ -41,4 +43,7 @@ public interface IValidationContext<T> {
|
|||
|
||||
ValidationResult toResult();
|
||||
|
||||
@Nonnull
|
||||
ValidationOptions getOptions();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import net.sf.saxon.lib.Validation;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -23,30 +34,23 @@ import java.util.List;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
|
||||
public class ValidationContext<T> extends BaseValidationContext<T> implements IValidationContext<T> {
|
||||
|
||||
private final IEncoder myEncoder;
|
||||
private final T myResource;
|
||||
private String myResourceAsString;
|
||||
private final EncodingEnum myResourceAsStringEncoding;
|
||||
private final ValidationOptions myOptions;
|
||||
private String myResourceAsString;
|
||||
|
||||
private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder) {
|
||||
this(theContext, theResource, theEncoder, new ArrayList<SingleValidationMessage>());
|
||||
private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder, ValidationOptions theOptions) {
|
||||
this(theContext, theResource, theEncoder, new ArrayList<>(), theOptions);
|
||||
}
|
||||
|
||||
private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder, List<SingleValidationMessage> theMessages) {
|
||||
|
||||
private ValidationContext(FhirContext theContext, T theResource, IEncoder theEncoder, List<SingleValidationMessage> theMessages, ValidationOptions theOptions) {
|
||||
super(theContext, theMessages);
|
||||
myResource = theResource;
|
||||
myEncoder = theEncoder;
|
||||
myOptions = theOptions;
|
||||
if (theEncoder != null) {
|
||||
myResourceAsStringEncoding = theEncoder.getEncoding();
|
||||
} else {
|
||||
|
@ -72,8 +76,24 @@ public class ValidationContext<T> extends BaseValidationContext<T> implements IV
|
|||
return myResourceAsStringEncoding;
|
||||
}
|
||||
|
||||
public static <T extends IBaseResource> IValidationContext<T> forResource(final FhirContext theContext, final T theResource) {
|
||||
return new ValidationContext<T>(theContext, theResource, new IEncoder() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public ValidationOptions getOptions() {
|
||||
return myOptions;
|
||||
}
|
||||
|
||||
private interface IEncoder {
|
||||
String encode();
|
||||
|
||||
EncodingEnum getEncoding();
|
||||
}
|
||||
|
||||
public static <T extends IBaseResource> IValidationContext<T> forResource(final FhirContext theContext, final T theResource, ValidationOptions theOptions) {
|
||||
ObjectUtil.requireNonNull(theContext, "theContext can not be null");
|
||||
ObjectUtil.requireNonNull(theResource, "theResource can not be null");
|
||||
ValidationOptions options = defaultIfNull(theOptions, ValidationOptions.empty());
|
||||
|
||||
IEncoder encoder = new IEncoder() {
|
||||
@Override
|
||||
public String encode() {
|
||||
return theContext.newXmlParser().encodeResourceToString(theResource);
|
||||
|
@ -83,18 +103,15 @@ public class ValidationContext<T> extends BaseValidationContext<T> implements IV
|
|||
public EncodingEnum getEncoding() {
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
});
|
||||
};
|
||||
return new ValidationContext<>(theContext, theResource, encoder, options);
|
||||
}
|
||||
|
||||
private interface IEncoder {
|
||||
String encode();
|
||||
|
||||
EncodingEnum getEncoding();
|
||||
}
|
||||
|
||||
public static IValidationContext<IBaseResource> forText(final FhirContext theContext, final String theResourceBody) {
|
||||
public static IValidationContext<IBaseResource> forText(final FhirContext theContext, final String theResourceBody, final ValidationOptions theOptions) {
|
||||
ObjectUtil.requireNonNull(theContext, "theContext can not be null");
|
||||
ObjectUtil.requireNotEmpty(theResourceBody, "theResourceBody can not be null or empty");
|
||||
ValidationOptions options = defaultIfNull(theOptions, ValidationOptions.empty());
|
||||
|
||||
return new BaseValidationContext<IBaseResource>(theContext) {
|
||||
|
||||
private EncodingEnum myEncoding;
|
||||
|
@ -128,11 +145,17 @@ public class ValidationContext<T> extends BaseValidationContext<T> implements IV
|
|||
return myEncoding;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ValidationOptions getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static IValidationContext<IBaseResource> subContext(final IValidationContext<IBaseResource> theCtx, final IBaseResource theResource) {
|
||||
return new ValidationContext<IBaseResource>(theCtx.getFhirContext(), theResource, new IEncoder() {
|
||||
public static IValidationContext<IBaseResource> subContext(final IValidationContext<IBaseResource> theCtx, final IBaseResource theResource, ValidationOptions theOptions) {
|
||||
return new ValidationContext<>(theCtx.getFhirContext(), theResource, new IEncoder() {
|
||||
@Override
|
||||
public String encode() {
|
||||
return theCtx.getFhirContext().newXmlParser().encodeResourceToString(theResource);
|
||||
|
@ -142,6 +165,6 @@ public class ValidationContext<T> extends BaseValidationContext<T> implements IV
|
|||
public EncodingEnum getEncoding() {
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
}, theCtx.getMessages());
|
||||
}, theCtx.getMessages(), theOptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ValidationOptions {
|
||||
|
||||
private static ValidationOptions ourEmpty;
|
||||
private Set<String> myProfiles;
|
||||
|
||||
public Set<String> getProfiles() {
|
||||
return myProfiles != null ? Collections.unmodifiableSet(myProfiles) : Collections.emptySet();
|
||||
}
|
||||
|
||||
public ValidationOptions addProfile(String theProfileUri) {
|
||||
Validate.notBlank(theProfileUri);
|
||||
|
||||
if (myProfiles == null) {
|
||||
myProfiles = new HashSet<>();
|
||||
}
|
||||
myProfiles.add(theProfileUri);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidationOptions addProfileIfNotBlank(String theProfileUri) {
|
||||
if (isNotBlank(theProfileUri)) {
|
||||
return addProfile(theProfileUri);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public static ValidationOptions empty() {
|
||||
ValidationOptions retVal = ourEmpty;
|
||||
if (retVal == null) {
|
||||
retVal = new ValidationOptions();
|
||||
retVal.myProfiles = Collections.emptySet();
|
||||
ourEmpty = retVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
|
@ -69,7 +69,7 @@ public class SchematronBaseValidator implements IValidatorModule {
|
|||
IBaseBundle bundle = (IBaseBundle) theCtx.getResource();
|
||||
List<IBaseResource> subResources = BundleUtil.toListOfResources(myCtx, bundle);
|
||||
for (IBaseResource nextSubResource : subResources) {
|
||||
validateResource(ValidationContext.subContext(theCtx, nextSubResource));
|
||||
validateResource(ValidationContext.subContext(theCtx, nextSubResource, theCtx.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.cli;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -26,6 +27,7 @@ public class ValidateTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testValidateUsingIgPackSucceedingDstu2() {
|
||||
String resourcePath = ValidateTest.class.getResource("/argo-dstu2-observation-good.json").getFile();
|
||||
ourLog.info(resourcePath);
|
||||
|
|
|
@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetai
|
|||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.*;
|
||||
import ca.uhn.fhir.validation.*;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
|
@ -178,6 +179,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return createOperationOutcome(OO_SEVERITY_INFO, theMessage, "informational");
|
||||
}
|
||||
|
||||
protected abstract IValidatorModule getInstanceValidator();
|
||||
|
||||
protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode);
|
||||
|
||||
@Override
|
||||
|
@ -1404,6 +1407,74 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return update(theResource, theMatchUrl, thePerformIndexing, false, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
||||
if (theRequest != null) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
||||
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
|
||||
}
|
||||
|
||||
if (theMode == ValidationModeEnum.DELETE) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
|
||||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId, theRequest);
|
||||
|
||||
// Validate that there are no resources pointing to the candidate that
|
||||
// would prevent deletion
|
||||
DeleteConflictList deleteConflicts = new DeleteConflictList();
|
||||
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
|
||||
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
|
||||
}
|
||||
myDeleteConflictService.validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
|
||||
|
||||
IBaseOperationOutcome oo = createInfoOperationOutcome("Ok to delete");
|
||||
return new MethodOutcome(new IdDt(theId.getValue()), oo);
|
||||
}
|
||||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
||||
validator.registerValidatorModule(getInstanceValidator());
|
||||
validator.registerValidatorModule(new IdChecker(theMode));
|
||||
|
||||
IBaseResource resourceToValidateById = null;
|
||||
if (theId != null && theId.hasResourceType() && theId.hasIdPart()) {
|
||||
Class<? extends IBaseResource> type = getContext().getResourceDefinition(theId.getResourceType()).getImplementingClass();
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
|
||||
resourceToValidateById = dao.read(theId, theRequest);
|
||||
}
|
||||
|
||||
|
||||
ValidationResult result;
|
||||
ValidationOptions options = new ValidationOptions()
|
||||
.addProfileIfNotBlank(theProfile);
|
||||
|
||||
if (theResource == null) {
|
||||
if (resourceToValidateById != null) {
|
||||
result = validator.validateWithResult(resourceToValidateById, options);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
} else if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource, options);
|
||||
} else if (theResource != null) {
|
||||
result = validator.validateWithResult(theResource, options);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(result.toOperationOutcome());
|
||||
return retVal;
|
||||
} else {
|
||||
throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource definition from the criteria which specifies the resource type
|
||||
*
|
||||
|
@ -1439,7 +1510,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void validateResourceType(BaseHasResource entity) {
|
||||
validateResourceType(entity, myResourceName);
|
||||
}
|
||||
|
@ -1451,4 +1521,29 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
}
|
||||
|
||||
private static class IdChecker implements IValidatorModule {
|
||||
|
||||
private ValidationModeEnum myMode;
|
||||
|
||||
IdChecker(ValidationModeEnum theMode) {
|
||||
myMode = theMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateResource(IValidationContext<IBaseResource> theCtx) {
|
||||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.dao;
|
|||
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
||||
import java.util.Set;
|
||||
|
@ -42,10 +41,9 @@ public class FhirResourceDaoBundleDstu2 extends FhirResourceDaoDstu2<Bundle> {
|
|||
}
|
||||
|
||||
for (Entry next : theResource.getEntry()) {
|
||||
next.setFullUrl((String)null);
|
||||
next.setFullUrl((String) null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,33 +1,12 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.jpa.delete.DeleteConflictList;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -50,14 +29,14 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
|
||||
public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResourceDao<T> {
|
||||
|
||||
@Autowired()
|
||||
@Qualifier("myJpaValidationSupportDstu2")
|
||||
private IValidationSupport myJpaValidationSupport;
|
||||
|
||||
@Autowired()
|
||||
@Autowired
|
||||
@Qualifier("myInstanceValidatorDstu2")
|
||||
private IValidatorModule myInstanceValidator;
|
||||
|
||||
@Override
|
||||
protected IValidatorModule getInstanceValidator() {
|
||||
return myInstanceValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
|
||||
|
@ -68,79 +47,5 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
return oo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
||||
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
|
||||
|
||||
if (theMode == ValidationModeEnum.DELETE) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
|
||||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId, theRequest);
|
||||
|
||||
// Validate that there are no resources pointing to the candidate that
|
||||
// would prevent deletion
|
||||
DeleteConflictList deleteConflicts = new DeleteConflictList();
|
||||
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
|
||||
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
|
||||
}
|
||||
myDeleteConflictService.validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Ok to delete");
|
||||
return new MethodOutcome(new IdDt(theId.getValue()), oo);
|
||||
}
|
||||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
||||
validator.registerValidatorModule(myInstanceValidator);
|
||||
|
||||
validator.registerValidatorModule(new IdChecker(theMode));
|
||||
|
||||
ValidationResult result;
|
||||
if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource);
|
||||
} else if (theResource != null) {
|
||||
result = validator.validateWithResult(theResource);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(result.toOperationOutcome());
|
||||
return retVal;
|
||||
} else {
|
||||
throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class IdChecker implements IValidatorModule {
|
||||
|
||||
private ValidationModeEnum myMode;
|
||||
|
||||
public IdChecker(ValidationModeEnum theMode) {
|
||||
myMode = theMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateResource(IValidationContext<IBaseResource> theCtx) {
|
||||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,36 +21,19 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.delete.DeleteConflictList;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirResourceDao<T> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3.class);
|
||||
|
@ -59,6 +42,11 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
|
|||
@Qualifier("myInstanceValidatorDstu3")
|
||||
private IValidatorModule myInstanceValidator;
|
||||
|
||||
@Override
|
||||
protected IValidatorModule getInstanceValidator() {
|
||||
return myInstanceValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
|
@ -74,92 +62,4 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
||||
if (theRequest != null) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
||||
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
|
||||
}
|
||||
|
||||
if (theMode == ValidationModeEnum.DELETE) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
|
||||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId, theRequest);
|
||||
|
||||
// Validate that there are no resources pointing to the candidate that
|
||||
// would prevent deletion
|
||||
DeleteConflictList deleteConflicts = new DeleteConflictList();
|
||||
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
|
||||
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
|
||||
}
|
||||
myDeleteConflictService.validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Ok to delete");
|
||||
return new MethodOutcome(new IdType(theId.getValue()), oo);
|
||||
}
|
||||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
||||
validator.registerValidatorModule(myInstanceValidator);
|
||||
|
||||
validator.registerValidatorModule(new IdChecker(theMode));
|
||||
|
||||
IBaseResource resourceToValidateById = null;
|
||||
if (theId != null && theId.hasResourceType() && theId.hasIdPart()) {
|
||||
Class<? extends IBaseResource> type = getContext().getResourceDefinition(theId.getResourceType()).getImplementingClass();
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
|
||||
resourceToValidateById = dao.read(theId, theRequest);
|
||||
}
|
||||
|
||||
ValidationResult result;
|
||||
if (theResource == null) {
|
||||
if (resourceToValidateById != null) {
|
||||
result = validator.validateWithResult(resourceToValidateById);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
} else if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource);
|
||||
} else {
|
||||
result = validator.validateWithResult(theResource);
|
||||
}
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(result.toOperationOutcome());
|
||||
return retVal;
|
||||
} else {
|
||||
throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class IdChecker implements IValidatorModule {
|
||||
|
||||
private ValidationModeEnum myMode;
|
||||
|
||||
public IdChecker(ValidationModeEnum theMode) {
|
||||
myMode = theMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateResource(IValidationContext<IBaseResource> theCtx) {
|
||||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
|
|||
private IFhirResourceDao<ValueSet> myValueSetDao;
|
||||
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
|
||||
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
|
||||
private IFhirResourceDao<ImplementationGuide> myImplementationGuideDao;
|
||||
@Autowired
|
||||
private FhirContext myDstu3Ctx;
|
||||
private ApplicationContext myApplicationContext;
|
||||
|
@ -148,6 +149,11 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
|
|||
params.setLoadSynchronousUpTo(1);
|
||||
params.add(CodeSystem.SP_URL, new UriParam(theUri));
|
||||
search = myCodeSystemDao.search(params);
|
||||
} else if ("ImplementationGuide".equals(resourceName)) {
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(1);
|
||||
params.add(ImplementationGuide.SP_URL, new UriParam(theUri));
|
||||
search = myImplementationGuideDao.search(params);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
|
||||
}
|
||||
|
@ -185,6 +191,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
|
|||
myValueSetDao = myApplicationContext.getBean("myValueSetDaoDstu3", IFhirResourceDao.class);
|
||||
myQuestionnaireDao = myApplicationContext.getBean("myQuestionnaireDaoDstu3", IFhirResourceDao.class);
|
||||
myCodeSystemDao = myApplicationContext.getBean("myCodeSystemDaoDstu3", IFhirResourceDao.class);
|
||||
myImplementationGuideDao = myApplicationContext.getBean("myImplementationGuideDaoDstu3", IFhirResourceDao.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -59,6 +59,11 @@ public class FhirResourceDaoR4<T extends IAnyResource> extends BaseHapiFhirResou
|
|||
@Qualifier("myInstanceValidatorR4")
|
||||
private IValidatorModule myInstanceValidator;
|
||||
|
||||
@Override
|
||||
protected IValidatorModule getInstanceValidator() {
|
||||
return myInstanceValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
|
@ -74,90 +79,5 @@ public class FhirResourceDaoR4<T extends IAnyResource> extends BaseHapiFhirResou
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
||||
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
|
||||
|
||||
if (theMode == ValidationModeEnum.DELETE) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
|
||||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId, theRequest);
|
||||
|
||||
// Validate that there are no resources pointing to the candidate that
|
||||
// would prevent deletion
|
||||
DeleteConflictList deleteConflicts = new DeleteConflictList();
|
||||
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
|
||||
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
|
||||
}
|
||||
myDeleteConflictService.validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Ok to delete");
|
||||
return new MethodOutcome(new IdType(theId.getValue()), oo);
|
||||
}
|
||||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
||||
validator.registerValidatorModule(myInstanceValidator);
|
||||
|
||||
validator.registerValidatorModule(new IdChecker(theMode));
|
||||
|
||||
IBaseResource resourceToValidateById = null;
|
||||
if (theId != null && theId.hasResourceType() && theId.hasIdPart()) {
|
||||
Class<? extends IBaseResource> type = getContext().getResourceDefinition(theId.getResourceType()).getImplementingClass();
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
|
||||
resourceToValidateById = dao.read(theId, theRequest);
|
||||
}
|
||||
|
||||
ValidationResult result;
|
||||
if (theResource == null) {
|
||||
if (resourceToValidateById != null) {
|
||||
result = validator.validateWithResult(resourceToValidateById);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
} else if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource);
|
||||
} else {
|
||||
result = validator.validateWithResult(theResource);
|
||||
}
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(result.toOperationOutcome());
|
||||
return retVal;
|
||||
} else {
|
||||
throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class IdChecker implements IValidatorModule {
|
||||
|
||||
private ValidationModeEnum myMode;
|
||||
|
||||
public IdChecker(ValidationModeEnum theMode) {
|
||||
myMode = theMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateResource(IValidationContext<IBaseResource> theCtx) {
|
||||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
|
|||
private IFhirResourceDao<ValueSet> myValueSetDao;
|
||||
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
|
||||
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
|
||||
private IFhirResourceDao<ImplementationGuide> myImplementationGuideDao;
|
||||
|
||||
@Autowired
|
||||
private FhirContext myR4Ctx;
|
||||
|
@ -142,6 +143,11 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
|
|||
params.setLoadSynchronousUpTo(1);
|
||||
params.add(CodeSystem.SP_URL, new UriParam(theUri));
|
||||
search = myCodeSystemDao.search(params);
|
||||
} else if ("ImplementationGuide".equals(resourceName)) {
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(1);
|
||||
params.add(ImplementationGuide.SP_URL, new UriParam(theUri));
|
||||
search = myImplementationGuideDao.search(params);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
|
||||
}
|
||||
|
@ -179,6 +185,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
|
|||
myValueSetDao = myApplicationContext.getBean("myValueSetDaoR4", IFhirResourceDao.class);
|
||||
myQuestionnaireDao = myApplicationContext.getBean("myQuestionnaireDaoR4", IFhirResourceDao.class);
|
||||
myCodeSystemDao = myApplicationContext.getBean("myCodeSystemDaoR4", IFhirResourceDao.class);
|
||||
myImplementationGuideDao = myApplicationContext.getBean("myImplementationGuideDaoR4", IFhirResourceDao.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,36 +21,15 @@ package ca.uhn.fhir.jpa.dao.r5;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.delete.DeleteConflictList;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r5.model.IdType;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class FhirResourceDaoR5<T extends IAnyResource> extends BaseHapiFhirResourceDao<T> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR5.class);
|
||||
|
@ -59,6 +38,11 @@ public class FhirResourceDaoR5<T extends IAnyResource> extends BaseHapiFhirResou
|
|||
@Qualifier("myInstanceValidatorR5")
|
||||
private IValidatorModule myInstanceValidator;
|
||||
|
||||
@Override
|
||||
protected IValidatorModule getInstanceValidator() {
|
||||
return myInstanceValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
|
@ -74,90 +58,4 @@ public class FhirResourceDaoR5<T extends IAnyResource> extends BaseHapiFhirResou
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
||||
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
|
||||
|
||||
if (theMode == ValidationModeEnum.DELETE) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
|
||||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId, theRequest);
|
||||
|
||||
// Validate that there are no resources pointing to the candidate that
|
||||
// would prevent deletion
|
||||
DeleteConflictList deleteConflicts = new DeleteConflictList();
|
||||
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
|
||||
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
|
||||
}
|
||||
myDeleteConflictService.validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Ok to delete");
|
||||
return new MethodOutcome(new IdType(theId.getValue()), oo);
|
||||
}
|
||||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
||||
validator.registerValidatorModule(myInstanceValidator);
|
||||
|
||||
validator.registerValidatorModule(new IdChecker(theMode));
|
||||
|
||||
IBaseResource resourceToValidateById = null;
|
||||
if (theId != null && theId.hasResourceType() && theId.hasIdPart()) {
|
||||
Class<? extends IBaseResource> type = getContext().getResourceDefinition(theId.getResourceType()).getImplementingClass();
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
|
||||
resourceToValidateById = dao.read(theId, theRequest);
|
||||
}
|
||||
|
||||
ValidationResult result;
|
||||
if (theResource == null) {
|
||||
if (resourceToValidateById != null) {
|
||||
result = validator.validateWithResult(resourceToValidateById);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
} else if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource);
|
||||
} else {
|
||||
result = validator.validateWithResult(theResource);
|
||||
}
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(result.toOperationOutcome());
|
||||
return retVal;
|
||||
} else {
|
||||
throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class IdChecker implements IValidatorModule {
|
||||
|
||||
private ValidationModeEnum myMode;
|
||||
|
||||
public IdChecker(ValidationModeEnum theMode) {
|
||||
myMode = theMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateResource(IValidationContext<IBaseResource> theCtx) {
|
||||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public class JpaValidationSupportR5 implements IJpaValidationSupportR5, Applicat
|
|||
private IFhirResourceDao<ValueSet> myValueSetDao;
|
||||
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
|
||||
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
|
||||
private IFhirResourceDao<ImplementationGuide> myImplementationGuideDao;
|
||||
|
||||
@Autowired
|
||||
private FhirContext myR5Ctx;
|
||||
|
@ -142,6 +143,11 @@ public class JpaValidationSupportR5 implements IJpaValidationSupportR5, Applicat
|
|||
params.setLoadSynchronousUpTo(1);
|
||||
params.add(CodeSystem.SP_URL, new UriParam(theUri));
|
||||
search = myCodeSystemDao.search(params);
|
||||
} else if ("ImplementationGuide".equals(resourceName)) {
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(1);
|
||||
params.add(ImplementationGuide.SP_URL, new UriParam(theUri));
|
||||
search = myImplementationGuideDao.search(params);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
|
||||
}
|
||||
|
@ -179,6 +185,7 @@ public class JpaValidationSupportR5 implements IJpaValidationSupportR5, Applicat
|
|||
myValueSetDao = myApplicationContext.getBean("myValueSetDaoR5", IFhirResourceDao.class);
|
||||
myQuestionnaireDao = myApplicationContext.getBean("myQuestionnaireDaoR5", IFhirResourceDao.class);
|
||||
myCodeSystemDao = myApplicationContext.getBean("myCodeSystemDaoR5", IFhirResourceDao.class);
|
||||
myImplementationGuideDao = myApplicationContext.getBean("myImplementationGuideDaoR5", IFhirResourceDao.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -51,13 +51,13 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
public class HapiTerminologySvcR5 extends BaseHapiTerminologySvcImpl implements IValidationSupport, IHapiTerminologySvcR5 {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myConceptMapDaoR4")
|
||||
@Qualifier("myConceptMapDaoR5")
|
||||
private IFhirResourceDao<ConceptMap> myConceptMapResourceDao;
|
||||
@Autowired
|
||||
@Qualifier("myCodeSystemDaoR4")
|
||||
@Qualifier("myCodeSystemDaoR5")
|
||||
private IFhirResourceDao<CodeSystem> myCodeSystemResourceDao;
|
||||
@Autowired
|
||||
@Qualifier("myValueSetDaoR4")
|
||||
@Qualifier("myValueSetDaoR5")
|
||||
private IFhirResourceDao<ValueSet> myValueSetResourceDao;
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
|
|
@ -2889,8 +2889,9 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
myStructureDefinitionDao.create(sd, mySrd);
|
||||
|
||||
String rawResource = IOUtils.toString(getClass().getResourceAsStream("/binu_testpatient_resource.json"), StandardCharsets.UTF_8);
|
||||
IBaseResource parsedResource = myFhirCtx.newJsonParser().parseResource(rawResource);
|
||||
try {
|
||||
myValueSetDao.validate(null, null, rawResource, EncodingEnum.JSON, ValidationModeEnum.UPDATE, null, mySrd);
|
||||
myPatientDao.validate((Patient) parsedResource, null, rawResource, EncodingEnum.JSON, ValidationModeEnum.UPDATE, null, mySrd);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
|
||||
|
|
|
@ -17,6 +17,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.CachingValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceCategory;
|
||||
import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceClinicalStatus;
|
||||
|
@ -45,6 +46,8 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
|
||||
@Autowired
|
||||
private IHapiTerminologySvc myHapiTerminologySvc;
|
||||
@Autowired
|
||||
private CachingValidationSupport myCachingValidationSupport;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -56,7 +59,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
@Before
|
||||
public void before() {
|
||||
myDaoConfig.setMaximumExpansionSize(5000);
|
||||
// my
|
||||
myCachingValidationSupport.flushCaches();
|
||||
}
|
||||
|
||||
private CodeSystem createExternalCs() {
|
||||
|
|
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.CachingValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
|
@ -35,6 +36,10 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ValidateTest.class);
|
||||
@Autowired
|
||||
private IValidatorModule myValidatorModule;
|
||||
@Autowired
|
||||
private CachingValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
private FhirInstanceValidator myFhirInstanceValidator;
|
||||
|
||||
@Test
|
||||
public void testValidateChangedQuestionnaire() {
|
||||
|
@ -70,7 +75,10 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
|||
MethodOutcome results = myQuestionnaireResponseDao.validate(qr, null, null, null, null, null, null);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(results.getOperationOutcome()));
|
||||
|
||||
TestUtil.sleepAtLeast(2500);
|
||||
ourLog.info("Clearing cache");
|
||||
myValidationSupport.flushCaches();
|
||||
myFhirInstanceValidator.flushCaches();
|
||||
|
||||
try {
|
||||
myQuestionnaireResponseDao.validate(qr, null, null, null, null, null, null);
|
||||
fail();
|
||||
|
@ -273,7 +281,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateResourceContainingProfileDeclarationInvalid() throws Exception {
|
||||
public void testValidateResourceContainingProfileDeclarationInvalid() {
|
||||
String methodName = "testValidateResourceContainingProfileDeclarationInvalid";
|
||||
|
||||
Observation input = new Observation();
|
||||
|
@ -287,11 +295,14 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
|||
|
||||
ValidationModeEnum mode = ValidationModeEnum.CREATE;
|
||||
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
|
||||
MethodOutcome outcome = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
||||
|
||||
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info(ooString);
|
||||
assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved"));
|
||||
try {
|
||||
myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome());
|
||||
ourLog.info(ooString);
|
||||
assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import ca.uhn.fhir.util.TestUtil;
|
|||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.hapi.validation.CachingValidationSupport;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceCategory;
|
||||
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
|
||||
|
@ -43,6 +44,9 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4TerminologyTest.class);
|
||||
@Autowired
|
||||
private IHapiTerminologySvc myHapiTerminologySvc;
|
||||
@Autowired
|
||||
private CachingValidationSupport myCachingValidationSupport;
|
||||
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -54,7 +58,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
|
|||
@Before
|
||||
public void before() {
|
||||
myDaoConfig.setMaximumExpansionSize(5000);
|
||||
// my
|
||||
myCachingValidationSupport.flushCaches();
|
||||
}
|
||||
|
||||
private CodeSystem createExternalCs() {
|
||||
|
|
|
@ -77,7 +77,6 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
}
|
||||
ourLog.info("Done validation");
|
||||
|
||||
// ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -173,11 +172,16 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
|
||||
ValidationModeEnum mode = ValidationModeEnum.CREATE;
|
||||
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
|
||||
MethodOutcome outcome = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
||||
|
||||
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info(ooString);
|
||||
assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved"));
|
||||
try {
|
||||
myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome());
|
||||
ourLog.info(ooString);
|
||||
assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2839,7 +2839,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||
ourLog.info(inputStr);
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient/123/$validate");
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
|
||||
post.setEntity(new StringEntity(inputStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
|
|
|
@ -4087,15 +4087,11 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
IIdType id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart() + "/$validate");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(412, response.getStatusLine().getStatusCode());
|
||||
assertThat(resp, containsString("SHALL at least contain a contact's details or a reference to an organization"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourcePro
|
|||
ourClient.create().resource(qr1).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
||||
assertThat(myFhirCtx.newJsonParser().encodeResourceToString(e.getOperationOutcome()), containsString("Answer value must be of type string"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourcePro
|
|||
ourClient.create().resource(qr1).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
||||
assertThat(myFhirCtx.newJsonParser().encodeResourceToString(e.getOperationOutcome()), containsString("Answer value must be of type string"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -181,19 +181,22 @@ public class RestfulServerUtils {
|
|||
break;
|
||||
}
|
||||
}
|
||||
switch (theRequestDetails.getRestOperationType()) {
|
||||
case SEARCH_SYSTEM:
|
||||
case SEARCH_TYPE:
|
||||
case HISTORY_SYSTEM:
|
||||
case HISTORY_TYPE:
|
||||
case HISTORY_INSTANCE:
|
||||
case GET_PAGE:
|
||||
if (!haveExplicitBundleElement) {
|
||||
parser.setEncodeElementsAppliesToChildResourcesOnly(true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
if (theRequestDetails.getRestOperationType() != null) {
|
||||
switch (theRequestDetails.getRestOperationType()) {
|
||||
case SEARCH_SYSTEM:
|
||||
case SEARCH_TYPE:
|
||||
case HISTORY_SYSTEM:
|
||||
case HISTORY_TYPE:
|
||||
case HISTORY_INSTANCE:
|
||||
case GET_PAGE:
|
||||
if (!haveExplicitBundleElement) {
|
||||
parser.setEncodeElementsAppliesToChildResourcesOnly(true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parser.setEncodeElements(newElements);
|
||||
|
|
|
@ -1,39 +1,44 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.TimingDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.*;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.ConditionVerificationStatusEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.NarrativeStatusEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.UnitsOfTimeEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.parser.XmlParserDstu2Test.TestPatientFor327;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.model.dstu2.composite.*;
|
||||
import ca.uhn.fhir.model.dstu2.resource.*;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.*;
|
||||
import ca.uhn.fhir.model.primitive.*;
|
||||
import ca.uhn.fhir.parser.*;
|
||||
import ca.uhn.fhir.parser.XmlParserDstu2Test.TestPatientFor327;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ResourceValidatorDstu2Test {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceValidatorDstu2Test.class);
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
|
||||
private FhirValidator createFhirValidator() {
|
||||
FhirValidator val = ourCtx.newValidator();
|
||||
|
@ -60,7 +65,7 @@ public class ResourceValidatorDstu2Test {
|
|||
// Put in an invalid date
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
|
||||
String encoded = parser.setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
|
||||
ourLog.info(encoded);
|
||||
|
||||
|
@ -70,9 +75,9 @@ public class ResourceValidatorDstu2Test {
|
|||
String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ourLog.info(resultString);
|
||||
|
||||
assertEquals(2, ((OperationOutcome)result.toOperationOutcome()).getIssue().size());
|
||||
assertEquals(2, ((OperationOutcome) result.toOperationOutcome()).getIssue().size());
|
||||
assertThat(resultString, StringContains.containsString("cvc-pattern-valid"));
|
||||
|
||||
|
||||
try {
|
||||
parser.parseResource(encoded);
|
||||
fail();
|
||||
|
@ -80,16 +85,17 @@ public class ResourceValidatorDstu2Test {
|
|||
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testSchemaBundleValidator() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("bundle-example.json"));
|
||||
String res = IOUtils.toString(ResourceValidatorDstu2Test.class.getResourceAsStream("/bundle-example.json"));
|
||||
Bundle b = ourCtx.newJsonParser().parseResource(Bundle.class, res);
|
||||
|
||||
FhirValidator val = createFhirValidator();
|
||||
|
||||
val.validate(b);
|
||||
ValidationResult result = val.validateWithResult(b);
|
||||
assertTrue(result.isSuccessful());
|
||||
|
||||
MedicationOrder p = (MedicationOrder) b.getEntry().get(0).getResource();
|
||||
TimingDt timing = new TimingDt();
|
||||
|
@ -97,19 +103,17 @@ public class ResourceValidatorDstu2Test {
|
|||
timing.getRepeat().setDurationUnits((UnitsOfTimeEnum) null);
|
||||
p.getDosageInstructionFirstRep().setTiming(timing);
|
||||
|
||||
try {
|
||||
val.validate(b);
|
||||
fail();
|
||||
} catch (ValidationFailureException e) {
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome());
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded, containsString("tim-1:"));
|
||||
}
|
||||
result = val.validateWithResult(b);
|
||||
assertFalse(result.isSuccessful());
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.getOperationOutcome());
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded, containsString("tim-1:"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSchemaBundleValidatorFails() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("bundle-example.json"), StandardCharsets.UTF_8);
|
||||
String res = IOUtils.toString(ResourceValidatorDstu2Test.class.getResourceAsStream("/bundle-example.json"), StandardCharsets.UTF_8);
|
||||
Bundle b = ourCtx.newJsonParser().parseResource(Bundle.class, res);
|
||||
|
||||
|
||||
|
@ -127,18 +131,18 @@ public class ResourceValidatorDstu2Test {
|
|||
ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b));
|
||||
|
||||
validationResult = val.validateWithResult(b);
|
||||
|
||||
|
||||
ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(validationResult.toOperationOutcome()));
|
||||
|
||||
|
||||
assertFalse(validationResult.isSuccessful());
|
||||
|
||||
|
||||
String encoded = logOperationOutcome(validationResult);
|
||||
assertThat(encoded, containsString("tim-1:"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSchemaBundleValidatorIsSuccessful() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("bundle-example.json"), StandardCharsets.UTF_8);
|
||||
String res = IOUtils.toString(ResourceValidatorDstu2Test.class.getResourceAsStream("/bundle-example.json"), StandardCharsets.UTF_8);
|
||||
Bundle b = ourCtx.newJsonParser().parseResource(Bundle.class, res);
|
||||
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b));
|
||||
|
@ -181,7 +185,7 @@ public class ResourceValidatorDstu2Test {
|
|||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testSchemaResourceValidator() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("patient-example-dicom.json"));
|
||||
String res = IOUtils.toString(ResourceValidatorDstu2Test.class.getResourceAsStream("/patient-example-dicom.json"));
|
||||
Patient p = ourCtx.newJsonParser().parseResource(Patient.class, res);
|
||||
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
@ -190,23 +194,21 @@ public class ResourceValidatorDstu2Test {
|
|||
val.setValidateAgainstStandardSchema(true);
|
||||
val.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
val.validate(p);
|
||||
ValidationResult result = val.validateWithResult(p);
|
||||
assertTrue(result.isSuccessful());
|
||||
|
||||
p.getAnimal().getBreed().setText("The Breed");
|
||||
try {
|
||||
val.validate(p);
|
||||
fail();
|
||||
} catch (ValidationFailureException e) {
|
||||
OperationOutcome operationOutcome = (OperationOutcome) e.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
assertEquals(1, operationOutcome.getIssue().size());
|
||||
assertThat(operationOutcome.getIssueFirstRep().getDetailsElement().getValue(), containsString("cvc-complex-type"));
|
||||
}
|
||||
result = val.validateWithResult(p);
|
||||
assertFalse(result.isSuccessful());
|
||||
OperationOutcome operationOutcome = (OperationOutcome) result.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
assertEquals(1, operationOutcome.getIssue().size());
|
||||
assertThat(operationOutcome.getIssueFirstRep().getDetailsElement().getValue(), containsString("cvc-complex-type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSchematronResourceValidator() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("patient-example-dicom.json"), StandardCharsets.UTF_8);
|
||||
String res = IOUtils.toString(ResourceValidatorDstu2Test.class.getResourceAsStream("/patient-example-dicom.json"), StandardCharsets.UTF_8);
|
||||
Patient p = ourCtx.newJsonParser().parseResource(Patient.class, res);
|
||||
|
||||
FhirValidator val = ourCtx.newValidator();
|
||||
|
@ -236,35 +238,35 @@ public class ResourceValidatorDstu2Test {
|
|||
@Test
|
||||
public void testValidateResourceWithResourceElements() {
|
||||
|
||||
TestPatientFor327 patient = new TestPatientFor327();
|
||||
patient.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
|
||||
patient.setId("123");
|
||||
patient.getText().setDiv("<div>FOO</div>");
|
||||
patient.getText().setStatus(NarrativeStatusEnum.GENERATED);
|
||||
patient.getLanguage().setValue("en");
|
||||
patient.addUndeclaredExtension(true, "http://foo").setValue(new StringDt("MOD"));
|
||||
ResourceMetadataKeyEnum.UPDATED.put(patient, new InstantDt(new Date()));
|
||||
TestPatientFor327 patient = new TestPatientFor327();
|
||||
patient.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
|
||||
patient.setId("123");
|
||||
patient.getText().setDiv("<div>FOO</div>");
|
||||
patient.getText().setStatus(NarrativeStatusEnum.GENERATED);
|
||||
patient.getLanguage().setValue("en");
|
||||
patient.addUndeclaredExtension(true, "http://foo").setValue(new StringDt("MOD"));
|
||||
ResourceMetadataKeyEnum.UPDATED.put(patient, new InstantDt(new Date()));
|
||||
|
||||
List<ResourceReferenceDt> conditions = new ArrayList<ResourceReferenceDt>();
|
||||
Condition condition = new Condition();
|
||||
condition.getPatient().setReference("Patient/123");
|
||||
condition.addBodySite().setText("BODY SITE");
|
||||
condition.getCode().setText("CODE");
|
||||
condition.setVerificationStatus(ConditionVerificationStatusEnum.CONFIRMED);
|
||||
List<ResourceReferenceDt> conditions = new ArrayList<>();
|
||||
Condition condition = new Condition();
|
||||
condition.getPatient().setReference("Patient/123");
|
||||
condition.addBodySite().setText("BODY SITE");
|
||||
condition.getCode().setText("CODE");
|
||||
condition.setVerificationStatus(ConditionVerificationStatusEnum.CONFIRMED);
|
||||
conditions.add(new ResourceReferenceDt(condition));
|
||||
patient.setCondition(conditions);
|
||||
patient.addIdentifier().setSystem("http://foo").setValue("123");
|
||||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
patient.setCondition(conditions);
|
||||
patient.addIdentifier().setSystem("http://foo").setValue("123");
|
||||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
|
||||
FhirValidator val = createFhirValidator();
|
||||
ValidationResult result = val.validateWithResult(encoded);
|
||||
|
||||
|
||||
String messageString = logOperationOutcome(result);
|
||||
|
||||
assertTrue(result.isSuccessful());
|
||||
|
||||
|
||||
assertThat(messageString, containsString("No issues"));
|
||||
|
||||
}
|
||||
|
@ -305,13 +307,13 @@ public class ResourceValidatorDstu2Test {
|
|||
FhirValidator val = ourCtx.newValidator();
|
||||
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
|
||||
val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
|
||||
|
||||
|
||||
ValidationResult result = val.validateWithResult(messageString);
|
||||
|
||||
logOperationOutcome(result);
|
||||
|
||||
assertTrue(result.isSuccessful());
|
||||
|
||||
|
||||
assertThat(messageString, containsString("valueReference"));
|
||||
assertThat(messageString, not(containsString("valueResource")));
|
||||
}
|
||||
|
@ -333,7 +335,7 @@ public class ResourceValidatorDstu2Test {
|
|||
IParser p = FhirContext.forDstu2().newXmlParser().setPrettyPrint(true);
|
||||
String messageString = p.encodeResourceToString(myPatient);
|
||||
ourLog.info(messageString);
|
||||
|
||||
|
||||
//@formatter:off
|
||||
assertThat(messageString, stringContainsInOrder(
|
||||
"meta",
|
||||
|
@ -354,13 +356,13 @@ public class ResourceValidatorDstu2Test {
|
|||
FhirValidator val = ourCtx.newValidator();
|
||||
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
|
||||
val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
|
||||
|
||||
|
||||
ValidationResult result = val.validateWithResult(messageString);
|
||||
|
||||
logOperationOutcome(result);
|
||||
|
||||
assertTrue(result.isSuccessful());
|
||||
|
||||
|
||||
assertThat(messageString, containsString("valueReference"));
|
||||
assertThat(messageString, not(containsString("valueResource")));
|
||||
}
|
||||
|
|
|
@ -83,6 +83,19 @@ public class ResponseHighlightingInterceptorTest {
|
|||
assertEquals("Attachment;", status.getFirstHeader("Content-Disposition").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidRequest() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/html?_elements=Patient:foo");
|
||||
httpGet.addHeader("Accept", "text/html");
|
||||
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
status.close();
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), containsString("text/html"));
|
||||
assertThat(responseContent, containsString("Invalid _elements value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBinaryReadAcceptBrowser() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Optional dependencies from RI codebase
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
package org.hl7.fhir.common.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import com.google.gson.*;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r5.utils.ValidationProfileSet;
|
||||
import org.hl7.fhir.r5.validation.InstanceValidator;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ValidatorWrapper {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorWrapper.class);
|
||||
private final DocumentBuilderFactory myDocBuilderFactory;
|
||||
private IResourceValidator.BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private boolean myAnyExtensionsAllowed;
|
||||
private boolean myErrorForUnknownProfiles;
|
||||
private boolean myNoTerminologyChecks;
|
||||
private Collection<? extends String> myExtensionDomains;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ValidatorWrapper() {
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
}
|
||||
|
||||
public ValidatorWrapper setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel theBestPracticeWarningLevel) {
|
||||
myBestPracticeWarningLevel = theBestPracticeWarningLevel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidatorWrapper setAnyExtensionsAllowed(boolean theAnyExtensionsAllowed) {
|
||||
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidatorWrapper setErrorForUnknownProfiles(boolean theErrorForUnknownProfiles) {
|
||||
myErrorForUnknownProfiles = theErrorForUnknownProfiles;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidatorWrapper setNoTerminologyChecks(boolean theNoTerminologyChecks) {
|
||||
myNoTerminologyChecks = theNoTerminologyChecks;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidatorWrapper setExtensionDomains(Collection<? extends String> theExtensionDomains) {
|
||||
myExtensionDomains = theExtensionDomains;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validate(IWorkerContext theWorkerContext, IValidationContext<?> theValidationContext) {
|
||||
InstanceValidator v;
|
||||
FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
|
||||
try {
|
||||
v = new InstanceValidator(theWorkerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
|
||||
v.setAnyExtensionsAllowed(myAnyExtensionsAllowed);
|
||||
v.setResourceIdRule(IResourceValidator.IdStatus.OPTIONAL);
|
||||
v.setNoTerminologyChecks(myNoTerminologyChecks);
|
||||
v.setErrorForUnknownProfiles(myErrorForUnknownProfiles);
|
||||
v.getExtensionDomains().addAll(myExtensionDomains);
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
ValidationProfileSet profileSet = new ValidationProfileSet();
|
||||
for (String next : theValidationContext.getOptions().getProfiles()) {
|
||||
profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(next, true));
|
||||
}
|
||||
|
||||
String input = theValidationContext.getResourceAsString();
|
||||
EncodingEnum encoding = theValidationContext.getResourceAsStringEncoding();
|
||||
if (encoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(input));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(ValidationMessage.IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
messages.add(m);
|
||||
return messages;
|
||||
}
|
||||
|
||||
// Determine if meta/profiles are present...
|
||||
ArrayList<String> profiles = determineIfProfilesSpecified(document);
|
||||
for (String nextProfile : profiles) {
|
||||
profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(nextProfile, true));
|
||||
}
|
||||
|
||||
v.validate(null, messages, document, profileSet);
|
||||
|
||||
} else if (encoding == EncodingEnum.JSON) {
|
||||
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(input, JsonObject.class);
|
||||
|
||||
JsonObject meta = json.getAsJsonObject("meta");
|
||||
if (meta != null) {
|
||||
JsonElement profileElement = meta.get("profile");
|
||||
if (profileElement != null && profileElement.isJsonArray()) {
|
||||
JsonArray profiles = profileElement.getAsJsonArray();
|
||||
for (JsonElement element : profiles) {
|
||||
profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(element.getAsString(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.validate(null, messages, json, profileSet);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + encoding);
|
||||
}
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ValidationMessage next = messages.get(i);
|
||||
String message = next.getMessage();
|
||||
if ("Binding has no source, so can't be checked".equals(message) ||
|
||||
"ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
return list.item(i).getLocalName();
|
||||
}
|
||||
}
|
||||
return theDocument.getDocumentElement().getLocalName();
|
||||
}
|
||||
|
||||
private ArrayList<String> determineIfProfilesSpecified(Document theDocument) {
|
||||
ArrayList<String> profileNames = new ArrayList<>();
|
||||
NodeList list = theDocument.getChildNodes().item(0).getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i).getNodeName().compareToIgnoreCase("meta") == 0) {
|
||||
NodeList metaList = list.item(i).getChildNodes();
|
||||
for (int j = 0; j < metaList.getLength(); j++) {
|
||||
if (metaList.item(j).getNodeName().compareToIgnoreCase("profile") == 0) {
|
||||
profileNames.add(metaList.item(j).getAttributes().item(0).getNodeValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return profileNames;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,18 +3,24 @@ package org.hl7.fhir.dstu3.hapi.validation;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CachingValidationSupport implements IValidationSupport {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(CachingValidationSupport.class);
|
||||
private final IValidationSupport myWrap;
|
||||
private final Cache<String, Object> myCache;
|
||||
|
||||
|
@ -30,13 +36,13 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
|
||||
return (List<IBaseResource>) myCache.get("fetchAllConformanceResources",
|
||||
return loadFromCache("fetchAllConformanceResources",
|
||||
t -> myWrap.fetchAllConformanceResources(theContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
return (List<StructureDefinition>) myCache.get("fetchAllStructureDefinitions",
|
||||
return loadFromCache("fetchAllStructureDefinitions",
|
||||
t -> myWrap.fetchAllStructureDefinitions(theContext));
|
||||
}
|
||||
|
||||
|
@ -52,7 +58,8 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
return myWrap.fetchResource(theContext, theClass, theUri);
|
||||
return loadFromCache("fetchResource " + theClass.getName() + " " + theUri,
|
||||
t -> myWrap.fetchResource(theContext, theClass, theUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,4 +86,19 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return myWrap.generateSnapshot(theInput, theUrl, theName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T loadFromCache(String theKey, Function<String, T> theLoader) {
|
||||
ourLog.trace("Loading: {}", theKey);
|
||||
Function<String, Optional<T>> loaderWrapper = key -> {
|
||||
ourLog.trace("Loading {} from cache", theKey);
|
||||
return Optional.ofNullable(theLoader.apply(theKey));
|
||||
};
|
||||
Optional<T> result = (Optional<T>) myCache.get(theKey, loaderWrapper);
|
||||
return result.orElse(null);
|
||||
}
|
||||
|
||||
public void flushCaches() {
|
||||
myCache.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
import org.hl7.fhir.common.hapi.validation.ValidatorWrapper;
|
||||
import org.hl7.fhir.convertors.VersionConvertor_30_50;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
||||
|
@ -27,12 +25,9 @@ import org.hl7.fhir.r5.context.IWorkerContext;
|
|||
import org.hl7.fhir.r5.formats.IParser;
|
||||
import org.hl7.fhir.r5.formats.ParserType;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.INarrativeGenerator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.IdStatus;
|
||||
import org.hl7.fhir.r5.validation.InstanceValidator;
|
||||
import org.hl7.fhir.utilities.TerminologyServiceOptions;
|
||||
import org.hl7.fhir.utilities.TranslationServices;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
@ -40,11 +35,10 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
@ -64,7 +58,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
private volatile WorkerContextWrapper myWrappedWorkerContext;
|
||||
|
||||
private boolean errorForUnknownProfiles;
|
||||
private List<String> extensionDomains = Collections.emptyList();
|
||||
private List<String> myExtensionDomains = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -90,8 +84,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
|
@ -101,7 +95,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(List<String> extensionDomains) {
|
||||
this.extensionDomains = extensionDomains;
|
||||
this.myExtensionDomains = extensionDomains;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -109,8 +103,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
|
@ -120,7 +114,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(String... extensionDomains) {
|
||||
this.extensionDomains = Arrays.asList(extensionDomains);
|
||||
this.myExtensionDomains = Arrays.asList(extensionDomains);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -225,14 +219,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
public boolean isAnyExtensionsAllowed() {
|
||||
return myAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public boolean isErrorForUnknownProfiles() {
|
||||
return errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
|
||||
this.errorForUnknownProfiles = errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
|
@ -243,13 +229,21 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public boolean isErrorForUnknownProfiles() {
|
||||
return errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
|
||||
this.errorForUnknownProfiles = errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
public boolean isNoTerminologyChecks() {
|
||||
return noTerminologyChecks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
|
@ -261,131 +255,33 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
myStructureDefintion = theStructureDefintion;
|
||||
}
|
||||
|
||||
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
|
||||
private List<String> getExtensionDomains() {
|
||||
return myExtensionDomains;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theValidationCtx) {
|
||||
final FhirContext ctx = theValidationCtx.getFhirContext();
|
||||
|
||||
WorkerContextWrapper wrappedWorkerContext = myWrappedWorkerContext;
|
||||
if (wrappedWorkerContext == null) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(ctx, myValidationSupport);
|
||||
wrappedWorkerContext = new WorkerContextWrapper(workerContext);
|
||||
}
|
||||
myWrappedWorkerContext = wrappedWorkerContext;
|
||||
|
||||
InstanceValidator v;
|
||||
FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
|
||||
try {
|
||||
v = new InstanceValidator(wrappedWorkerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
return new ValidatorWrapper()
|
||||
.setAnyExtensionsAllowed(isAnyExtensionsAllowed())
|
||||
.setBestPracticeWarningLevel(getBestPracticeWarningLevel())
|
||||
.setErrorForUnknownProfiles(isErrorForUnknownProfiles())
|
||||
.setExtensionDomains(getExtensionDomains())
|
||||
.setNoTerminologyChecks(isNoTerminologyChecks())
|
||||
.validate(wrappedWorkerContext, theValidationCtx);
|
||||
|
||||
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
|
||||
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
v.setNoTerminologyChecks(isNoTerminologyChecks());
|
||||
v.setErrorForUnknownProfiles(isErrorForUnknownProfiles());
|
||||
v.getExtensionDomains().addAll(extensionDomains);
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
|
||||
// Determine if meta/profiles are present...
|
||||
ArrayList<String> resourceNames = determineIfProfilesSpecified(document);
|
||||
if (resourceNames.isEmpty()) {
|
||||
resourceNames.add(determineResourceName(document));
|
||||
}
|
||||
|
||||
for (String resourceName : resourceNames) {
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
profile = findStructureDefinitionForResourceName(theCtx, determineResourceName(document));
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
|
||||
ArrayList<String> resourceNames = new ArrayList<String>();
|
||||
JsonArray profiles = null;
|
||||
try {
|
||||
profiles = json.getAsJsonObject("meta").getAsJsonArray("profile");
|
||||
for (JsonElement element : profiles) {
|
||||
resourceNames.add(element.getAsString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
resourceNames.add(json.get("resourceType").getAsString());
|
||||
}
|
||||
|
||||
for (String resourceName : resourceNames) {
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
profile = findStructureDefinitionForResourceName(theCtx, json.get("resourceType").getAsString());
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
|
||||
}
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ValidationMessage next = messages.get(i);
|
||||
String message = next.getMessage();
|
||||
if ("Binding has no source, so can't be checked".equals(message) ||
|
||||
"ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
||||
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
|
||||
}
|
||||
|
||||
|
||||
private class WorkerContextWrapper implements IWorkerContext {
|
||||
private static class WorkerContextWrapper implements IWorkerContext {
|
||||
private final HapiWorkerContext myWrap;
|
||||
private final VersionConvertor_30_50 myConverter;
|
||||
private volatile List<org.hl7.fhir.r5.model.StructureDefinition> myAllStructures;
|
||||
|
@ -404,36 +300,36 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
myFetchResourceCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(10000)
|
||||
.build(new CacheLoader<ResourceKey, org.hl7.fhir.r5.model.Resource>() {
|
||||
@Override
|
||||
public org.hl7.fhir.r5.model.Resource load(ResourceKey key) throws Exception {
|
||||
Resource fetched;
|
||||
switch (key.getResourceName()) {
|
||||
case "StructureDefinition":
|
||||
fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri());
|
||||
break;
|
||||
case "ValueSet":
|
||||
fetched = myWrap.fetchResource(ValueSet.class, key.getUri());
|
||||
break;
|
||||
case "CodeSystem":
|
||||
fetched = myWrap.fetchResource(CodeSystem.class, key.getUri());
|
||||
break;
|
||||
case "Questionnaire":
|
||||
fetched = myWrap.fetchResource(Questionnaire.class, key.getUri());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName());
|
||||
}
|
||||
.build(key -> {
|
||||
Resource fetched;
|
||||
switch (key.getResourceName()) {
|
||||
case "StructureDefinition":
|
||||
fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri());
|
||||
break;
|
||||
case "ValueSet":
|
||||
fetched = myWrap.fetchResource(ValueSet.class, key.getUri());
|
||||
break;
|
||||
case "CodeSystem":
|
||||
fetched = myWrap.fetchResource(CodeSystem.class, key.getUri());
|
||||
break;
|
||||
case "Questionnaire":
|
||||
fetched = myWrap.fetchResource(Questionnaire.class, key.getUri());
|
||||
break;
|
||||
case "ImplementationGuide":
|
||||
fetched = myWrap.fetchResource(ImplementationGuide.class, key.getUri());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName());
|
||||
}
|
||||
|
||||
if (fetched == null) {
|
||||
return null;
|
||||
}
|
||||
if (fetched == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return VersionConvertor_30_50.convertResource(fetched, true);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
try {
|
||||
return VersionConvertor_30_50.convertResource(fetched, true);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -487,19 +383,28 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private ValidationResult convertValidationResult(org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult theResult) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
try {
|
||||
conceptDefinition = VersionConvertor_30_50.convertConceptDefinitionComponent(theResult.asConceptDefinition());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
@Nonnull
|
||||
private ValidationResult convertValidationResult(@Nullable org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult theResult) {
|
||||
ValidationResult retVal = null;
|
||||
if (theResult != null) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
try {
|
||||
conceptDefinition = VersionConvertor_30_50.convertConceptDefinitionComponent(theResult.asConceptDefinition());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
retVal = new ValidationResult(IssueSeverity.ERROR, "Validation failed");
|
||||
}
|
||||
|
||||
ValidationResult retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -655,11 +560,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTypeNames() {
|
||||
return myWrap.getTypeNames();
|
||||
|
@ -670,6 +570,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return myWrap.getVersion();
|
||||
|
@ -716,13 +621,13 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
public ILoggingService getLogger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILoggingService getLogger() {
|
||||
return null;
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -743,7 +648,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
@Override
|
||||
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String system, String code, String display) {
|
||||
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display);
|
||||
// TODO: converted code might be null -> NPE
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
|
@ -794,7 +698,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
@ -815,7 +718,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hl7.fhir.r4.model.ValueSet;
|
|||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -59,7 +60,8 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
return myWrap.fetchResource(theContext, theClass, theUri);
|
||||
return loadFromCache("fetchResource " + theClass.getName() + " " + theUri,
|
||||
t -> myWrap.fetchResource(theContext, theClass, theUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,6 +94,12 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
|
||||
@Nullable
|
||||
private <T> T loadFromCache(String theKey, Function<String, T> theLoader) {
|
||||
return (T) myCache.get(theKey, theLoader);
|
||||
Function<String, Optional<T>> loaderWrapper = key -> Optional.ofNullable(theLoader.apply(theKey));
|
||||
Optional<T> result = (Optional<T>) myCache.get(theKey, loaderWrapper);
|
||||
return result.orElse(null);
|
||||
}
|
||||
|
||||
public void flushCaches() {
|
||||
myCache.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,31 @@
|
|||
package org.hl7.fhir.r4.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
import org.hl7.fhir.common.hapi.validation.ValidatorWrapper;
|
||||
import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.formats.IParser;
|
||||
import org.hl7.fhir.r5.formats.ParserType;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.INarrativeGenerator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.IdStatus;
|
||||
import org.hl7.fhir.r5.validation.InstanceValidator;
|
||||
import org.hl7.fhir.utilities.TerminologyServiceOptions;
|
||||
import org.hl7.fhir.utilities.TranslationServices;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
@ -40,25 +33,17 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings({"PackageAccessibility", "Duplicates"})
|
||||
public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseValidatorBridge implements IValidatorModule {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
|
||||
|
||||
private boolean myAnyExtensionsAllowed = true;
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
private boolean noTerminologyChecks = false;
|
||||
private volatile WorkerContextWrapper myWrappedWorkerContext;
|
||||
|
@ -81,8 +66,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
* @param theValidationSupport The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
|
@ -90,8 +73,8 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
|
@ -109,8 +92,8 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
|
@ -120,53 +103,10 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(String... extensionDomains) {
|
||||
this.extensionDomains = Arrays.asList(extensionDomains);
|
||||
setCustomExtensionDomains(Arrays.asList(extensionDomains));
|
||||
return this;
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
return list.item(i).getLocalName();
|
||||
}
|
||||
}
|
||||
return theDocument.getDocumentElement().getLocalName();
|
||||
}
|
||||
|
||||
private ArrayList<String> determineIfProfilesSpecified(Document theDocument) {
|
||||
ArrayList<String> profileNames = new ArrayList<>();
|
||||
NodeList list = theDocument.getChildNodes().item(0).getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i).getNodeName().compareToIgnoreCase("meta") == 0) {
|
||||
NodeList metaList = list.item(i).getChildNodes();
|
||||
for (int j = 0; j < metaList.getLength(); j++) {
|
||||
if (metaList.item(j).getNodeName().compareToIgnoreCase("profile") == 0) {
|
||||
profileNames.add(metaList.item(j).getAttributes().item(0).getNodeValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return profileNames;
|
||||
}
|
||||
|
||||
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
|
||||
String sdName = null;
|
||||
try {
|
||||
// Test if a URL was passed in specifying the structure definition and test if "StructureDefinition" is part of the URL
|
||||
URL testIfUrl = new URL(resourceName);
|
||||
sdName = resourceName;
|
||||
} catch (MalformedURLException e) {
|
||||
sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
|
||||
}
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName);
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void flushCaches() {
|
||||
myWrappedWorkerContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
|
||||
|
@ -225,14 +165,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
public boolean isAnyExtensionsAllowed() {
|
||||
return myAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public boolean isErrorForUnknownProfiles() {
|
||||
return errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
|
||||
this.errorForUnknownProfiles = errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
|
@ -243,13 +175,21 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public boolean isErrorForUnknownProfiles() {
|
||||
return errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
|
||||
this.errorForUnknownProfiles = errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
public boolean isNoTerminologyChecks() {
|
||||
return noTerminologyChecks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
|
@ -257,144 +197,38 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
noTerminologyChecks = theNoTerminologyChecks;
|
||||
}
|
||||
|
||||
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
|
||||
myStructureDefintion = theStructureDefintion;
|
||||
}
|
||||
|
||||
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theValidationCtx) {
|
||||
|
||||
WorkerContextWrapper wrappedWorkerContext = myWrappedWorkerContext;
|
||||
if (wrappedWorkerContext == null) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theValidationCtx.getFhirContext(), myValidationSupport);
|
||||
wrappedWorkerContext = new WorkerContextWrapper(workerContext);
|
||||
}
|
||||
myWrappedWorkerContext = wrappedWorkerContext;
|
||||
|
||||
InstanceValidator v;
|
||||
FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
|
||||
try {
|
||||
v = new InstanceValidator(wrappedWorkerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
|
||||
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
v.setNoTerminologyChecks(isNoTerminologyChecks());
|
||||
v.setErrorForUnknownProfiles(isErrorForUnknownProfiles());
|
||||
v.getExtensionDomains().addAll(extensionDomains);
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
|
||||
// Determine if meta/profiles are present...
|
||||
ArrayList<String> resourceNames = determineIfProfilesSpecified(document);
|
||||
if (resourceNames.isEmpty()) {
|
||||
resourceNames.add(determineResourceName(document));
|
||||
}
|
||||
|
||||
for (String resourceName : resourceNames) {
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
profile = findStructureDefinitionForResourceName(theCtx, determineResourceName(document));
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
|
||||
ArrayList<String> resourceNames = new ArrayList<String>();
|
||||
JsonArray profiles = null;
|
||||
try {
|
||||
profiles = json.getAsJsonObject("meta").getAsJsonArray("profile");
|
||||
for (JsonElement element : profiles) {
|
||||
resourceNames.add(element.getAsString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
resourceNames.add(json.get("resourceType").getAsString());
|
||||
}
|
||||
|
||||
for (String resourceName : resourceNames) {
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
profile = findStructureDefinitionForResourceName(theCtx, json.get("resourceType").getAsString());
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
|
||||
}
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ValidationMessage next = messages.get(i);
|
||||
String message = next.getMessage();
|
||||
if ("Binding has no source, so can't be checked".equals(message) ||
|
||||
"ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
return new ValidatorWrapper()
|
||||
.setAnyExtensionsAllowed(isAnyExtensionsAllowed())
|
||||
.setBestPracticeWarningLevel(getBestPracticeWarningLevel())
|
||||
.setErrorForUnknownProfiles(isErrorForUnknownProfiles())
|
||||
.setExtensionDomains(getExtensionDomains())
|
||||
.setNoTerminologyChecks(isNoTerminologyChecks())
|
||||
.validate(wrappedWorkerContext, theValidationCtx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
||||
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
|
||||
private List<String> getExtensionDomains() {
|
||||
return extensionDomains;
|
||||
}
|
||||
|
||||
|
||||
private class WorkerContextWrapper implements IWorkerContext {
|
||||
private static class WorkerContextWrapper implements IWorkerContext {
|
||||
private final HapiWorkerContext myWrap;
|
||||
private final VersionConvertor_40_50 myConverter;
|
||||
private volatile List<org.hl7.fhir.r5.model.StructureDefinition> myAllStructures;
|
||||
private LoadingCache<ResourceKey, org.hl7.fhir.r5.model.Resource> myFetchResourceCache;
|
||||
private org.hl7.fhir.r5.model.Parameters myExpansionProfile;
|
||||
|
||||
WorkerContextWrapper(HapiWorkerContext theWorkerContext) {
|
||||
myWrap = theWorkerContext;
|
||||
myConverter = new VersionConvertor_40_50();
|
||||
|
||||
long timeoutMillis = 10 * DateUtils.MILLIS_PER_SECOND;
|
||||
if (System.getProperties().containsKey(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) {
|
||||
|
@ -404,36 +238,36 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
myFetchResourceCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(10000)
|
||||
.build(new CacheLoader<ResourceKey, org.hl7.fhir.r5.model.Resource>() {
|
||||
@Override
|
||||
public org.hl7.fhir.r5.model.Resource load(ResourceKey key) throws Exception {
|
||||
Resource fetched;
|
||||
switch (key.getResourceName()) {
|
||||
case "StructureDefinition":
|
||||
fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri());
|
||||
break;
|
||||
case "ValueSet":
|
||||
fetched = myWrap.fetchResource(ValueSet.class, key.getUri());
|
||||
break;
|
||||
case "CodeSystem":
|
||||
fetched = myWrap.fetchResource(CodeSystem.class, key.getUri());
|
||||
break;
|
||||
case "Questionnaire":
|
||||
fetched = myWrap.fetchResource(Questionnaire.class, key.getUri());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName());
|
||||
}
|
||||
.build(key -> {
|
||||
Resource fetched;
|
||||
switch (key.getResourceName()) {
|
||||
case "StructureDefinition":
|
||||
fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri());
|
||||
break;
|
||||
case "ValueSet":
|
||||
fetched = myWrap.fetchResource(ValueSet.class, key.getUri());
|
||||
break;
|
||||
case "CodeSystem":
|
||||
fetched = myWrap.fetchResource(CodeSystem.class, key.getUri());
|
||||
break;
|
||||
case "Questionnaire":
|
||||
fetched = myWrap.fetchResource(Questionnaire.class, key.getUri());
|
||||
break;
|
||||
case "ImplementationGuide":
|
||||
fetched = myWrap.fetchResource(ImplementationGuide.class, key.getUri());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName());
|
||||
}
|
||||
|
||||
if (fetched == null) {
|
||||
return null;
|
||||
}
|
||||
if (fetched == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return VersionConvertor_40_50.convertResource(fetched);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
try {
|
||||
return VersionConvertor_40_50.convertResource(fetched);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -487,19 +321,28 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private ValidationResult convertValidationResult(org.hl7.fhir.r4.context.IWorkerContext.ValidationResult theResult) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
try {
|
||||
conceptDefinition = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertConceptDefinitionComponent(theResult.asConceptDefinition());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
@Nonnull
|
||||
private ValidationResult convertValidationResult(@Nullable org.hl7.fhir.r4.context.IWorkerContext.ValidationResult theResult) {
|
||||
ValidationResult retVal = null;
|
||||
if (theResult != null) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
try {
|
||||
conceptDefinition = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertConceptDefinitionComponent(theResult.asConceptDefinition());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
retVal = new ValidationResult(IssueSeverity.ERROR, "Validation failed");
|
||||
}
|
||||
|
||||
ValidationResult retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -554,8 +397,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
}
|
||||
}
|
||||
|
||||
ValueSetExpander.ValueSetExpansionOutcome outcome = new ValueSetExpander.ValueSetExpansionOutcome(valueSetExpansion);
|
||||
return outcome;
|
||||
return new ValueSetExpander.ValueSetExpansionOutcome(valueSetExpansion);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -605,10 +447,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
return myWrap.getAbbreviation(name);
|
||||
}
|
||||
|
||||
public VersionConvertor_40_50 getConverter() {
|
||||
return myConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -654,11 +492,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTypeNames() {
|
||||
return myWrap.getTypeNames();
|
||||
|
@ -669,6 +502,11 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return myWrap.getVersion();
|
||||
|
@ -715,13 +553,13 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
public ILoggingService getLogger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILoggingService getLogger() {
|
||||
return null;
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -742,7 +580,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
@Override
|
||||
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String system, String code, String display) {
|
||||
org.hl7.fhir.r4.context.IWorkerContext.ValidationResult result = myWrap.validateCode(theOptions, system, code, display);
|
||||
// TODO: converted code might be null -> NPE
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
|
@ -793,7 +630,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.r4.context.IWorkerContext.ValidationResult result = myWrap.validateCode(theOptions, convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
@ -814,7 +650,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.r4.context.IWorkerContext.ValidationResult result = myWrap.validateCode(theOptions, convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hl7.fhir.r5.model.ValueSet;
|
|||
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -59,7 +60,8 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
return myWrap.fetchResource(theContext, theClass, theUri);
|
||||
return loadFromCache("fetchResource " + theClass.getName() + " " + theUri,
|
||||
t -> myWrap.fetchResource(theContext, theClass, theUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,6 +94,8 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
|
||||
@Nullable
|
||||
private <T> T loadFromCache(String theKey, Function<String, T> theLoader) {
|
||||
return (T) myCache.get(theKey, theLoader);
|
||||
Function<String, Optional<T>> loaderWrapper = key -> Optional.ofNullable(theLoader.apply(theKey));
|
||||
Optional<T> result = (Optional<T>) myCache.get(theKey, loaderWrapper);
|
||||
return result.orElse(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
package org.hl7.fhir.r5.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
import org.hl7.fhir.common.hapi.validation.ValidatorWrapper;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
|
@ -27,44 +24,31 @@ import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
|
|||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.INarrativeGenerator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator.IdStatus;
|
||||
import org.hl7.fhir.r5.validation.InstanceValidator;
|
||||
import org.hl7.fhir.utilities.TerminologyServiceOptions;
|
||||
import org.hl7.fhir.utilities.TranslationServices;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings({"PackageAccessibility", "Duplicates"})
|
||||
public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseValidatorBridge implements IValidatorModule {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
|
||||
|
||||
private boolean myAnyExtensionsAllowed = true;
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
private boolean noTerminologyChecks = false;
|
||||
private volatile WorkerContextWrapper myWrappedWorkerContext;
|
||||
|
||||
private boolean errorForUnknownProfiles;
|
||||
private List<String> extensionDomains = Collections.emptyList();
|
||||
private List<String> myExtensionDomains = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -81,8 +65,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
* @param theValidationSupport The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
|
@ -90,8 +72,8 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
|
@ -101,7 +83,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(List<String> extensionDomains) {
|
||||
this.extensionDomains = extensionDomains;
|
||||
this.myExtensionDomains = extensionDomains;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -109,8 +91,8 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
|
@ -120,54 +102,10 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(String... extensionDomains) {
|
||||
this.extensionDomains = Arrays.asList(extensionDomains);
|
||||
this.myExtensionDomains = Arrays.asList(extensionDomains);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
return list.item(i).getLocalName();
|
||||
}
|
||||
}
|
||||
return theDocument.getDocumentElement().getLocalName();
|
||||
}
|
||||
|
||||
private ArrayList<String> determineIfProfilesSpecified(Document theDocument) {
|
||||
ArrayList<String> profileNames = new ArrayList<String>();
|
||||
NodeList list = theDocument.getChildNodes().item(0).getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i).getNodeName().compareToIgnoreCase("meta") == 0) {
|
||||
NodeList metaList = list.item(i).getChildNodes();
|
||||
for (int j = 0; j < metaList.getLength(); j++) {
|
||||
if (metaList.item(j).getNodeName().compareToIgnoreCase("profile") == 0) {
|
||||
profileNames.add(metaList.item(j).getAttributes().item(0).getNodeValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return profileNames;
|
||||
}
|
||||
|
||||
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
|
||||
String sdName = null;
|
||||
try {
|
||||
// Test if a URL was passed in specifying the structure definition and test if "StructureDefinition" is part of the URL
|
||||
URL testIfUrl = new URL(resourceName);
|
||||
sdName = resourceName;
|
||||
} catch (MalformedURLException e) {
|
||||
sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
|
||||
}
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName);
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void flushCaches() {
|
||||
myWrappedWorkerContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
|
||||
* <p>
|
||||
|
@ -225,14 +163,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
public boolean isAnyExtensionsAllowed() {
|
||||
return myAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public boolean isErrorForUnknownProfiles() {
|
||||
return errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
|
||||
this.errorForUnknownProfiles = errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
|
@ -243,13 +173,21 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public boolean isErrorForUnknownProfiles() {
|
||||
return errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
|
||||
this.errorForUnknownProfiles = errorForUnknownProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
public boolean isNoTerminologyChecks() {
|
||||
return noTerminologyChecks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
|
@ -257,135 +195,30 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
noTerminologyChecks = theNoTerminologyChecks;
|
||||
}
|
||||
|
||||
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
|
||||
myStructureDefintion = theStructureDefintion;
|
||||
public List<String> getExtensionDomains() {
|
||||
return myExtensionDomains;
|
||||
}
|
||||
|
||||
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
|
||||
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theValidationCtx) {
|
||||
WorkerContextWrapper wrappedWorkerContext = myWrappedWorkerContext;
|
||||
if (wrappedWorkerContext == null) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theValidationCtx.getFhirContext(), myValidationSupport);
|
||||
wrappedWorkerContext = new WorkerContextWrapper(workerContext);
|
||||
}
|
||||
myWrappedWorkerContext = wrappedWorkerContext;
|
||||
|
||||
InstanceValidator v;
|
||||
FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
|
||||
try {
|
||||
v = new InstanceValidator(wrappedWorkerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
|
||||
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
v.setNoTerminologyChecks(isNoTerminologyChecks());
|
||||
v.setErrorForUnknownProfiles(isErrorForUnknownProfiles());
|
||||
v.getExtensionDomains().addAll(extensionDomains);
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
|
||||
// Determine if meta/profiles are present...
|
||||
ArrayList<String> resourceNames = determineIfProfilesSpecified(document);
|
||||
if (resourceNames.isEmpty()) {
|
||||
resourceNames.add(determineResourceName(document));
|
||||
}
|
||||
|
||||
for (String resourceName : resourceNames) {
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
profile = findStructureDefinitionForResourceName(theCtx, determineResourceName(document));
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
|
||||
ArrayList<String> resourceNames = new ArrayList<String>();
|
||||
JsonArray profiles = null;
|
||||
try {
|
||||
profiles = json.getAsJsonObject("meta").getAsJsonArray("profile");
|
||||
for (JsonElement element : profiles) {
|
||||
resourceNames.add(element.getAsString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
resourceNames.add(json.get("resourceType").getAsString());
|
||||
}
|
||||
|
||||
for (String resourceName : resourceNames) {
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
profile = findStructureDefinitionForResourceName(theCtx, json.get("resourceType").getAsString());
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during validation", e);
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
|
||||
}
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ValidationMessage next = messages.get(i);
|
||||
String message = next.getMessage();
|
||||
if ("Binding has no source, so can't be checked".equals(message) ||
|
||||
"ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
||||
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
|
||||
return new ValidatorWrapper()
|
||||
.setAnyExtensionsAllowed(isAnyExtensionsAllowed())
|
||||
.setBestPracticeWarningLevel(getBestPracticeWarningLevel())
|
||||
.setErrorForUnknownProfiles(isErrorForUnknownProfiles())
|
||||
.setExtensionDomains(getExtensionDomains())
|
||||
.setNoTerminologyChecks(isNoTerminologyChecks())
|
||||
.validate(wrappedWorkerContext, theValidationCtx);
|
||||
}
|
||||
|
||||
|
||||
private class WorkerContextWrapper implements IWorkerContext {
|
||||
private static class WorkerContextWrapper implements IWorkerContext {
|
||||
private final HapiWorkerContext myWrap;
|
||||
private volatile List<org.hl7.fhir.r5.model.StructureDefinition> myAllStructures;
|
||||
private LoadingCache<ResourceKey, org.hl7.fhir.r5.model.Resource> myFetchResourceCache;
|
||||
|
@ -419,6 +252,9 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
case "Questionnaire":
|
||||
fetched = myWrap.fetchResource(Questionnaire.class, key.getUri());
|
||||
break;
|
||||
case "ImplementationGuide":
|
||||
fetched = myWrap.fetchResource(ImplementationGuide.class, key.getUri());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName());
|
||||
}
|
||||
|
@ -485,31 +321,40 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private ValidationResult convertValidationResult(org.hl7.fhir.r5.context.IWorkerContext.ValidationResult theResult) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
try {
|
||||
conceptDefinition = (theResult.asConceptDefinition());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
@Nonnull
|
||||
private ValidationResult convertValidationResult(@Nullable org.hl7.fhir.r5.context.IWorkerContext.ValidationResult theResult) {
|
||||
ValidationResult retVal = null;
|
||||
if (theResult != null) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
try {
|
||||
conceptDefinition = (theResult.asConceptDefinition());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
retVal = new ValidationResult(IssueSeverity.ERROR, "Validation failed");
|
||||
}
|
||||
|
||||
ValidationResult retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) {
|
||||
public ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) {
|
||||
ValueSet convertedSource;
|
||||
try {
|
||||
convertedSource = (source);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome expanded = myWrap.expandVS(convertedSource, cacheOk, heiarchical);
|
||||
ValueSetExpansionOutcome expanded = myWrap.expandVS(convertedSource, cacheOk, heiarchical);
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet convertedResult = null;
|
||||
if (expanded.getValueset() != null) {
|
||||
|
@ -523,16 +368,16 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
String error = expanded.getError();
|
||||
ValueSetExpander.TerminologyServiceErrorClass result = null;
|
||||
|
||||
return new ValueSetExpander.ValueSetExpansionOutcome(convertedResult, error, result);
|
||||
return new ValueSetExpansionOutcome(convertedResult, error, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heiarchical) {
|
||||
public ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heiarchical) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException {
|
||||
public ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException {
|
||||
ValueSet.ConceptSetComponent convertedInc = null;
|
||||
if (inc != null) {
|
||||
try {
|
||||
|
@ -542,7 +387,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
}
|
||||
}
|
||||
|
||||
org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome expansion = myWrap.expandVS(convertedInc, heirarchical);
|
||||
ValueSetExpansionOutcome expansion = myWrap.expandVS(convertedInc, heirarchical);
|
||||
org.hl7.fhir.r5.model.ValueSet valueSetExpansion = null;
|
||||
if (expansion != null) {
|
||||
try {
|
||||
|
@ -552,7 +397,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
}
|
||||
}
|
||||
|
||||
ValueSetExpander.ValueSetExpansionOutcome outcome = new ValueSetExpander.ValueSetExpansionOutcome(valueSetExpansion);
|
||||
ValueSetExpansionOutcome outcome = new ValueSetExpansionOutcome(valueSetExpansion);
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
@ -648,11 +493,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTypeNames() {
|
||||
return myWrap.getTypeNames();
|
||||
|
@ -663,6 +503,11 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return myWrap.getVersion();
|
||||
|
@ -709,13 +554,13 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
public ILoggingService getLogger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILoggingService getLogger() {
|
||||
return null;
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -736,7 +581,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
@Override
|
||||
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String system, String code, String display) {
|
||||
org.hl7.fhir.r5.context.IWorkerContext.ValidationResult result = myWrap.validateCode(theOptions, system, code, display);
|
||||
// TODO: converted code might be null -> NPE
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
|
@ -787,7 +631,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.r5.context.IWorkerContext.ValidationResult result = myWrap.validateCode(theOptions, convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
@ -808,7 +651,6 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.r5.context.IWorkerContext.ValidationResult result = myWrap.validateCode(theOptions, convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -42,7 +42,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator still likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -66,7 +66,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -81,7 +81,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator still likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -102,7 +102,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -117,7 +117,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator still likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -138,7 +138,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
@ -153,7 +153,7 @@ public class ParserWithValidationDstu3Test {
|
|||
|
||||
// verify that InstanceValidator still likes the format
|
||||
{
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
|
||||
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content, null);
|
||||
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
|
||||
ValidationResult result = validationCtx.toResult();
|
||||
for (SingleValidationMessage msg : result.getMessages()) {
|
||||
|
|
|
@ -985,8 +985,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
myInstanceVal.setValidationSupport(myMockSupport);
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(errors.toString(), 1, errors.size());
|
||||
assertEquals("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved", errors.get(0).getMessage());
|
||||
assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -240,7 +240,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system::code1) is not in the options value set in the questionnaire"));
|
||||
assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1' - QuestionnaireResponse.item[0].answer[0].value.ofType(Coding)"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
|
@ -250,7 +250,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system2::code3) is not in the options value set in the questionnaire"));
|
||||
assertThat(errors.toString(), containsString("Validation failed for 'http://codesystems.com/system2#code3'"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("No LinkId, so can't be validated"));
|
||||
assertThat(errors.toString(), containsString("Element 'QuestionnaireResponse.item[0].linkId': minimum required = 1, but only found 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1033,7 +1033,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system::code1) is not in the options value set in the questionnaire"));
|
||||
assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1' - QuestionnaireResponse.item[0].answer[0].value.ofType(Coding)"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
// Partial code
|
||||
|
|
|
@ -979,8 +979,7 @@ public class FhirInstanceValidatorR4Test {
|
|||
myInstanceVal.setValidationSupport(myMockSupport);
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(errors.toString(), 1, errors.size());
|
||||
assertEquals("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved", errors.get(0).getMessage());
|
||||
assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -237,7 +237,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system::code1) is not in the options value set in the questionnaire"));
|
||||
assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1' - QuestionnaireResponse.item[0].answer[0].value.ofType(Coding)"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
|
@ -248,7 +248,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system2::code3) is not in the options value set in the questionnaire"));
|
||||
assertThat(errors.toString(), containsString("Validation failed for 'http://codesystems.com/system2#code3'"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("ERROR - No LinkId, so can't be validated - QuestionnaireResponse"));
|
||||
assertThat(errors.toString(), containsString("Element 'QuestionnaireResponse.item[0].linkId': minimum required = 1, but only found 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -777,8 +777,7 @@ public class FhirInstanceValidatorR5Test {
|
|||
myInstanceVal.setValidationSupport(myMockSupport);
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(errors.toString(), 1, errors.size());
|
||||
assertEquals("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved", errors.get(0).getMessage());
|
||||
assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -971,6 +970,8 @@ public class FhirInstanceValidatorR5Test {
|
|||
assertEquals(0, all.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testValidateStructureDefinition() throws IOException {
|
||||
|
|
|
@ -0,0 +1,740 @@
|
|||
package org.hl7.fhir.r5.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport.CodeValidationResult;
|
||||
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r5.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
|
||||
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.hl7.fhir.r5.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
|
||||
import org.hl7.fhir.r5.model.QuestionnaireResponse.QuestionnaireResponseStatus;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class QuestionnaireResponseValidatorR5Test {
|
||||
public static final String ID_ICC_QUESTIONNAIRE_SETUP = "Questionnaire/profile";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorR5Test.class);
|
||||
private static final String CODE_ICC_SCHOOLTYPE_PT = "PT";
|
||||
private static final String ID_VS_SCHOOLTYPE = "ValueSet/schooltype";
|
||||
private static final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype";
|
||||
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
|
||||
private static FhirContext ourCtx = FhirContext.forR5();
|
||||
private FhirInstanceValidator myInstanceVal;
|
||||
private FhirValidator myVal;
|
||||
private IValidationSupport myValSupport;
|
||||
private HapiWorkerContext myWorkerCtx;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myValSupport = mock(IValidationSupport.class);
|
||||
// new DefaultProfileValidationSupport();
|
||||
myWorkerCtx = new HapiWorkerContext(ourCtx, myValSupport);
|
||||
|
||||
myVal = ourCtx.newValidator();
|
||||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
||||
}
|
||||
|
||||
private ValidationResult stripBindingHasNoSourceMessage(ValidationResult theErrors) {
|
||||
List<SingleValidationMessage> messages = new ArrayList<>(theErrors.getMessages());
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
if (messages.get(i).getMessage().contains("has no source, so can't")) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return new ValidationResult(ourCtx, messages);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnswerWithCorrectType() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem.setUrl("http://codesystems.com/system");
|
||||
codeSystem.addConcept().setCode("code0");
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
int itemCnt = 16;
|
||||
QuestionnaireItemType[] questionnaireItemTypes = new QuestionnaireItemType[itemCnt];
|
||||
questionnaireItemTypes[0] = QuestionnaireItemType.BOOLEAN;
|
||||
questionnaireItemTypes[1] = QuestionnaireItemType.DECIMAL;
|
||||
questionnaireItemTypes[2] = QuestionnaireItemType.INTEGER;
|
||||
questionnaireItemTypes[3] = QuestionnaireItemType.DATE;
|
||||
questionnaireItemTypes[4] = QuestionnaireItemType.DATETIME;
|
||||
questionnaireItemTypes[5] = QuestionnaireItemType.TIME;
|
||||
questionnaireItemTypes[6] = QuestionnaireItemType.STRING;
|
||||
questionnaireItemTypes[7] = QuestionnaireItemType.TEXT;
|
||||
questionnaireItemTypes[8] = QuestionnaireItemType.TEXT;
|
||||
questionnaireItemTypes[9] = QuestionnaireItemType.URL;
|
||||
questionnaireItemTypes[10] = QuestionnaireItemType.CHOICE;
|
||||
questionnaireItemTypes[11] = QuestionnaireItemType.OPENCHOICE;
|
||||
questionnaireItemTypes[12] = QuestionnaireItemType.OPENCHOICE;
|
||||
questionnaireItemTypes[13] = QuestionnaireItemType.ATTACHMENT;
|
||||
questionnaireItemTypes[14] = QuestionnaireItemType.REFERENCE;
|
||||
questionnaireItemTypes[15] = QuestionnaireItemType.QUANTITY;
|
||||
|
||||
Type[] answerValues = new Type[itemCnt];
|
||||
answerValues[0] = new BooleanType(true);
|
||||
answerValues[1] = new DecimalType(42.0);
|
||||
answerValues[2] = new IntegerType(42);
|
||||
answerValues[3] = new DateType(new Date());
|
||||
answerValues[4] = new DateTimeType(new Date());
|
||||
answerValues[5] = new TimeType("04:47:12");
|
||||
answerValues[6] = new StringType("some text");
|
||||
answerValues[7] = new StringType("some text");
|
||||
answerValues[8] = new MarkdownType("some text");
|
||||
answerValues[9] = new UriType("http://example.com");
|
||||
answerValues[10] = new Coding().setSystem("http://codesystems.com/system").setCode("code0");
|
||||
answerValues[11] = new Coding().setSystem("http://codesystems.com/system").setCode("code0");
|
||||
answerValues[12] = new StringType("some value");
|
||||
answerValues[13] = new Attachment().setData("some data".getBytes()).setContentType("txt");
|
||||
answerValues[14] = new Reference("http://example.com/Questionnaire/q1");
|
||||
answerValues[15] = new Quantity(42);
|
||||
|
||||
|
||||
for (int i = 0; i < itemCnt; i++) {
|
||||
ourLog.info("Testing item {}: {}", i, questionnaireItemTypes[i]);
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
if (questionnaireItemTypes[i] == null) continue;
|
||||
String linkId = "link" + i;
|
||||
QuestionnaireItemComponent questionnaireItemComponent =
|
||||
q.addItem().setLinkId(linkId).setRequired(true).setType(questionnaireItemTypes[i]);
|
||||
if (i == 10 || i == 11) {
|
||||
questionnaireItemComponent.setAnswerValueSet("http://somevalueset");
|
||||
} else if (i == 12) {
|
||||
questionnaireItemComponent.setAnswerOption(
|
||||
Arrays.asList(new Questionnaire.QuestionnaireItemAnswerOptionComponent(new StringType("some value"))));
|
||||
}
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.INPROGRESS);
|
||||
qa.setQuestionnaire("http://example.com/Questionnaire/q" + i);
|
||||
qa.addItem().setLinkId(linkId).addAnswer().setValue(answerValues[i]);
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class),
|
||||
eq(qa.getQuestionnaire()))).thenReturn(q);
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat("index[" + i + "]: " + errors.toString(), errors.getMessages(), empty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnswerWithWrongType() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1");
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaireElement().getValue()))).thenReturn(q);
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Answer value must be of type boolean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCodedAnswer() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setAnswerValueSet("http://somevalueset");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q);
|
||||
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true);
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true);
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent().setCode("code0")));
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any()))
|
||||
.thenReturn(new CodeValidationResult(ValidationMessage.IssueSeverity.ERROR, "Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem.setUrl("http://codesystems.com/system");
|
||||
codeSystem.addConcept().setCode("code0");
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem);
|
||||
|
||||
CodeSystem codeSystem2 = new CodeSystem();
|
||||
codeSystem2.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem2.setUrl("http://codesystems.com/system2");
|
||||
codeSystem2.addConcept().setCode("code2");
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system2"))).thenReturn(codeSystem2);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
|
||||
options.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
ValidationResult errors;
|
||||
|
||||
// Good code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://codesystems.com/system").setCode("code0"));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
assertEquals(errors.toString(), 0, errors.getMessages().size());
|
||||
|
||||
// Bad code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://codesystems.com/system").setCode("code1"));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1' - QuestionnaireResponse.item[0].answer[0].value.ofType(Coding)"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://codesystems.com/system2").setCode("code3"));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Validation failed for 'http://codesystems.com/system2#code3'"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupWithNoLinkIdInQuestionnaireResponse() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
QuestionnaireItemComponent qGroup = q.addItem().setType(QuestionnaireItemType.GROUP);
|
||||
qGroup.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1");
|
||||
QuestionnaireResponseItemComponent qaGroup = qa.addItem();
|
||||
qaGroup.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire()))).thenReturn(q);
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Element 'QuestionnaireResponse.item[0].linkId': minimum required = 1, but only found 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemWithNoType() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
QuestionnaireItemComponent qItem = q.addItem();
|
||||
qItem.setLinkId("link0");
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1");
|
||||
QuestionnaireResponseItemComponent qaItem = qa.addItem().setLinkId("link0");
|
||||
qaItem.addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire()))).thenReturn(q);
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Definition for item link0 does not contain a type"));
|
||||
assertEquals(1, errors.getMessages().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingRequiredQuestion() {
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING);
|
||||
q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1");
|
||||
qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
String reference = qa.getQuestionnaire();
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("No response found for required item with id = 'link0'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmbeddedItemInChoice() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
String valueSetRef = "http://somevalueset";
|
||||
String codeSystemUrl = "http://codesystems.com/system";
|
||||
String codeValue = "code0";
|
||||
|
||||
// create the questionnaire
|
||||
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
|
||||
item1.setLinkId("link1")
|
||||
.setType(QuestionnaireItemType.CHOICE)
|
||||
.setAnswerValueSet(valueSetRef);
|
||||
|
||||
item1.addItem().setLinkId("link11")
|
||||
.setType(QuestionnaireItemType.TEXT);
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem(item1);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireRef)))
|
||||
.thenReturn(q);
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem.setUrl(codeSystemUrl);
|
||||
codeSystem.addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq(codeSystemUrl)))
|
||||
.thenReturn(codeSystem);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
String qXml = xmlParser.encodeResourceToString(q);
|
||||
ourLog.info(qXml);
|
||||
|
||||
// create the response
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.INPROGRESS);
|
||||
qa.setQuestionnaire(questionnaireRef);
|
||||
qa.addItem().setLinkId("link1")
|
||||
.addAnswer()
|
||||
.addItem().setLinkId("link11");
|
||||
|
||||
String rXml = xmlParser.encodeResourceToString(qa);
|
||||
ourLog.info(rXml);
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.getMessages(), empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmbeddedItemInOpenChoice() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
String valueSetRef = "http://somevalueset";
|
||||
String codeSystemUrl = "http://codesystems.com/system";
|
||||
String codeValue = "code0";
|
||||
|
||||
// create the questionnaire
|
||||
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
|
||||
item1.setLinkId("link1")
|
||||
.setType(QuestionnaireItemType.OPENCHOICE)
|
||||
.setAnswerValueSet(valueSetRef);
|
||||
|
||||
item1.addItem().setLinkId("link11")
|
||||
.setType(QuestionnaireItemType.TEXT);
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem(item1);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireRef)))
|
||||
.thenReturn(q);
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem.setUrl(codeSystemUrl);
|
||||
codeSystem.addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq(codeSystemUrl)))
|
||||
.thenReturn(codeSystem);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
String qXml = xmlParser.encodeResourceToString(q);
|
||||
ourLog.info(qXml);
|
||||
|
||||
// create the response
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.INPROGRESS);
|
||||
qa.setQuestionnaire(questionnaireRef);
|
||||
qa.addItem().setLinkId("link1")
|
||||
.addAnswer()
|
||||
.addItem().setLinkId("link11");
|
||||
|
||||
String rXml = xmlParser.encodeResourceToString(qa);
|
||||
ourLog.info(rXml);
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.getMessages(), empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmbeddedItemInString() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
|
||||
// create the questionnaire
|
||||
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
|
||||
item1.setLinkId("link1")
|
||||
.setType(QuestionnaireItemType.TEXT);
|
||||
|
||||
item1.addItem().setLinkId("link11")
|
||||
.setType(QuestionnaireItemType.TEXT);
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem(item1);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireRef)))
|
||||
.thenReturn(q);
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
String qXml = xmlParser.encodeResourceToString(q);
|
||||
ourLog.info(qXml);
|
||||
|
||||
// create the response
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.INPROGRESS);
|
||||
qa.setQuestionnaire(questionnaireRef);
|
||||
qa.addItem().setLinkId("link1")
|
||||
.addAnswer()
|
||||
.addItem().setLinkId("link11");
|
||||
|
||||
String rXml = xmlParser.encodeResourceToString(qa);
|
||||
ourLog.info(rXml);
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.getMessages(), empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingRequiredAnswer() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem().setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.setRequired(true);
|
||||
|
||||
String reference = "http://example.com/Questionnaire/q1";
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference)))
|
||||
.thenReturn(q);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setQuestionnaire(reference);
|
||||
qa.addItem().setLinkId("link0");
|
||||
qa.setStatus(QuestionnaireResponseStatus.INPROGRESS);
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.getMessages(), hasSize(1));
|
||||
assertEquals(ResultSeverityEnum.WARNING, errors.getMessages().get(0).getSeverity());
|
||||
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
|
||||
errors = myVal.validateWithResult(qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.getMessages(), hasSize(1));
|
||||
assertEquals(ResultSeverityEnum.ERROR, errors.getMessages().get(0).getSeverity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenchoiceAnswer() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
QuestionnaireItemComponent item = q.addItem();
|
||||
item.setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.OPENCHOICE).setAnswerValueSet("http://somevalueset");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireRef))).thenReturn(q);
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem.setUrl("http://codesystems.com/system");
|
||||
codeSystem.addConcept().setCode("code0");
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem);
|
||||
|
||||
CodeSystem codeSystem2 = new CodeSystem();
|
||||
codeSystem2.setContent(CodeSystemContentMode.COMPLETE);
|
||||
codeSystem2.setUrl("http://codesystems.com/system2");
|
||||
codeSystem2.addConcept().setCode("code2");
|
||||
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system2"))).thenReturn(codeSystem2);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
|
||||
options.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(String.class))).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
ValidationResult errors;
|
||||
|
||||
// // Good code
|
||||
//
|
||||
// qa = new QuestionnaireResponse();
|
||||
// qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
// qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
// qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
// qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://codesystems.com/system").setCode("code0"));
|
||||
// errors = myVal.validateWithResult(qa);
|
||||
// errors = stripBindingHasNoSourceMessage(errors);
|
||||
// assertEquals(errors.getMessages().toString(), 0, errors.getMessages().size());
|
||||
//
|
||||
// // Bad code
|
||||
//
|
||||
// qa = new QuestionnaireResponse();
|
||||
// qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
// qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
// qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
// qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://codesystems.com/system").setCode("code1"));
|
||||
// errors = myVal.validateWithResult(qa);
|
||||
// errors = stripBindingHasNoSourceMessage(errors);
|
||||
// ourLog.info(errors.toString());
|
||||
// assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system::code1) is not in the options value set in the questionnaire"));
|
||||
// assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
//
|
||||
// // Partial code
|
||||
//
|
||||
// qa = new QuestionnaireResponse();
|
||||
// qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
// qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
// qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
// qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem(null).setCode("code1"));
|
||||
// errors = myVal.validateWithResult(qa);
|
||||
// errors = stripBindingHasNoSourceMessage(errors);
|
||||
// ourLog.info(errors.toString());
|
||||
// assertThat(errors.toString(), containsString("The value provided (null::code1) is not in the options value set in the questionnaire"));
|
||||
// assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
//
|
||||
// qa = new QuestionnaireResponse();
|
||||
// qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
// qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
// qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
// qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("").setCode("code1"));
|
||||
// errors = myVal.validateWithResult(qa);
|
||||
// errors = stripBindingHasNoSourceMessage(errors);
|
||||
// ourLog.info(errors.toString());
|
||||
// assertThat(errors.toString(), containsString("The value provided (null::code1) is not in the options value set in the questionnaire"));
|
||||
// assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
//
|
||||
// // System only in known codesystem
|
||||
// qa = new QuestionnaireResponse();
|
||||
// qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
// qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
// qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
// qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://codesystems.com/system").setCode(null));
|
||||
// errors = myVal.validateWithResult(qa);
|
||||
// ourLog.info(errors.toString());
|
||||
// assertThat(errors.toString(), containsString("The value provided (http://codesystems.com/system::null) is not in the options value set in the questionnaire"));
|
||||
// assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://system").setCode(null));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
ourLog.info(errors.toString());
|
||||
// This is set in InstanceValidator#validateAnswerCode
|
||||
assertThat(errors.toString(), containsString("INFORMATION - Code http://system/null was not validated because the code system is not present"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
// Wrong type
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new IntegerType(123));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Cannot validate integer answer option because no option list is provided"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
// String answer
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setDisplay("Hello"));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.getMessages(), empty());
|
||||
|
||||
// Missing String answer
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue(questionnaireRef);
|
||||
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setDisplay(""));
|
||||
errors = myVal.validateWithResult(qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("No response found for required item with id = 'link0'"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnexpectedAnswer() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1");
|
||||
qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire()))).thenReturn(q);
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString(" - QuestionnaireResponse"));
|
||||
assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnexpectedGroup() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1");
|
||||
qa.addItem().setLinkId("link1").addItem().setLinkId("link2");
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire()))).thenReturn(q);
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString(" - QuestionnaireResponse"));
|
||||
assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateQuestionnaireResponseWithValueSetChoiceAnswer() {
|
||||
/*
|
||||
* Create valueset
|
||||
*/
|
||||
ValueSet iccSchoolTypeVs = new ValueSet();
|
||||
iccSchoolTypeVs.setId(ID_VS_SCHOOLTYPE);
|
||||
iccSchoolTypeVs.getCompose().getIncludeFirstRep().setSystem(SYSTEMURI_ICC_SCHOOLTYPE);
|
||||
iccSchoolTypeVs
|
||||
.getCompose()
|
||||
.getIncludeFirstRep()
|
||||
.addConcept()
|
||||
.setCode(CODE_ICC_SCHOOLTYPE_PT)
|
||||
.setDisplay("Part Time");
|
||||
|
||||
/*
|
||||
* Create Questionnaire
|
||||
*/
|
||||
Questionnaire questionnaire = new Questionnaire();
|
||||
{
|
||||
questionnaire.setId(ID_ICC_QUESTIONNAIRE_SETUP);
|
||||
|
||||
QuestionnaireItemComponent basicGroup = questionnaire.addItem();
|
||||
basicGroup.setLinkId("basic");
|
||||
basicGroup.setType(QuestionnaireItemType.GROUP);
|
||||
basicGroup
|
||||
.addItem()
|
||||
.setLinkId("schoolType")
|
||||
.setType(QuestionnaireItemType.CHOICE)
|
||||
.setAnswerValueSet(ID_VS_SCHOOLTYPE)
|
||||
.setRequired(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create response
|
||||
*/
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.setQuestionnaire(ID_ICC_QUESTIONNAIRE_SETUP);
|
||||
qa.getSubject().setReference("Patient/123");
|
||||
|
||||
QuestionnaireResponseItemComponent basicGroup = qa
|
||||
.addItem();
|
||||
basicGroup.setLinkId("basic");
|
||||
basicGroup
|
||||
.addItem()
|
||||
.setLinkId("schoolType")
|
||||
.addAnswer()
|
||||
.setValue(new Coding(SYSTEMURI_ICC_SCHOOLTYPE, CODE_ICC_SCHOOLTYPE_PT, ""));
|
||||
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire()))).thenReturn(questionnaire);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(ID_VS_SCHOOLTYPE))).thenReturn(iccSchoolTypeVs);
|
||||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("No issues"));
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
myDefaultValidationSupport.flush();
|
||||
myDefaultValidationSupport = null;
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
}
|
5
pom.xml
5
pom.xml
|
@ -833,6 +833,11 @@
|
|||
<artifactId>gson</artifactId>
|
||||
<version>${gson_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
|
|
|
@ -59,12 +59,20 @@
|
|||
a type level operation (i.e. called on the CodeSystem type).
|
||||
]]>
|
||||
</action>
|
||||
<action type="change">
|
||||
<![CDATA[
|
||||
<b>Breaking Change</b>:
|
||||
The FhirValidator#validate(IResource) method has been removed. It was deprecated in HAPI FHIR 0.7 and replaced with
|
||||
FhirValidator#validateWithResults(IBaseResource) so it is unlikely anyone is still depending on the
|
||||
old method.
|
||||
]]>
|
||||
</action>
|
||||
<action type="add">
|
||||
Support for the new R5 draft resources has been added. This support includes the client,
|
||||
server, and JPA server. Note that these definitions will change as the R5 standard is
|
||||
modified until it is released, so use with caution!
|
||||
</action>
|
||||
<action type="add">
|
||||
<action type="add">
|
||||
A new interceptor called
|
||||
<![CDATA[<code>ConsentInterceptor</code>]]> has been added. This interceptor allows
|
||||
JPA based servers to make appropriate consent decisions related to resources that
|
||||
|
@ -373,6 +381,15 @@
|
|||
operations had ambiguous paths that could lead to the wrong method
|
||||
being called. Thanks to Seth Rylan Gainey for the pull request!
|
||||
</action>
|
||||
<action type="fix" issue="1414">
|
||||
The profile validator (FhirInstanceValidator) can now be used to validate a resource
|
||||
using an explicit profile declaration rather than simply relying on the declared
|
||||
URL in the resource itself.
|
||||
</action>
|
||||
<action type="fix">
|
||||
When using the ResponseHighlighterInterceptor, some invalid requests that would normally generate an HTTP
|
||||
400 response (e.g. an invalid _elements value) would cause an HTTP 500 crash.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.8.0" date="2019-05-30" description="Hippo">
|
||||
<action type="fix">
|
||||
|
|
|
@ -124,7 +124,73 @@
|
|||
</macro>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section name="Resource Validation (Profile/StructureDefinition)">
|
||||
|
||||
<p>
|
||||
HAPI also supports validation against StructureDefinition
|
||||
resources. This functionality uses the HL7 "InstanceValidator", which is able
|
||||
to check a resource for conformance to FHIR profiles
|
||||
(StructureDefinitions, ValueSets, and CodeSystems),
|
||||
including validating fields, extensions, and codes for conformance to their given ValueSets.
|
||||
</p>
|
||||
<p>
|
||||
StructureDefinition validation can be used to validate a resource against the
|
||||
official structure definitions (produced by HL7) as well as against custom
|
||||
definitions provided either by HL7 or by the user.
|
||||
</p>
|
||||
|
||||
<p class="doc_info_bubble">
|
||||
The instance validator is experimental in the DSTU2 mode, but has become very stable
|
||||
and full-featured in DSTU3+ mode. Use with caution when validating DSTU2 resources using
|
||||
instance validator.
|
||||
</p>
|
||||
|
||||
<subsection name="Running the Validator">
|
||||
|
||||
<p>
|
||||
To execute the validator, you simply create an instance of
|
||||
<a href="./apidocs-dstu3/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.html">FhirInstanceValidator</a>
|
||||
and register it to new validator, as shown in the example below.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that the example below uses the official FHIR StructureDefintions and ValueSets
|
||||
to validate the resource. It will not work unless you include the
|
||||
<code>hapi-fhir-validation-resources-[version].jar</code> to your classpath.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="instanceValidator" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Supplying your own StructureDefinitions">
|
||||
|
||||
<p>
|
||||
The FhirInstanceValidator relies on the
|
||||
<a href="./apidocs-hl7org-dstu2/ca/uhn/fhir/validation/IValidationSupport.html">IValidationSupport</a>
|
||||
interface to load StructureDefinitions, and validate codes.
|
||||
</p>
|
||||
<p>
|
||||
By default, the
|
||||
<a href="./apidocs-hl7org-dstu2/ca/uhn/fhir/validation/DefaultProfileValidationSupport.html">DefaultProfileValidationSupport</a>
|
||||
implementation is used. This implementation loads the FHIR profiles from the
|
||||
validator resources JAR. If you want to use your own profiles, you may wish to
|
||||
supply your own implementation.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="instanceValidatorCustom" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
<section name="Resource Validation Module: Schema/Schematron">
|
||||
|
||||
<p>
|
||||
|
@ -189,100 +255,6 @@
|
|||
<a name="structure_definition_validation" />
|
||||
</section>
|
||||
|
||||
<section name="Resource Validation (Profile/StructureDefinition)">
|
||||
|
||||
<p>
|
||||
HAPI also supports validation against StructureDefinition
|
||||
resources. This functionality uses the HL7 "InstanceValidator", which is able
|
||||
to check a resource for conformance to FHIR profiles
|
||||
(StructureDefinitions, ValueSets, and CodeSystems),
|
||||
including validating fields, extensions, and codes for conformance to their given ValueSets.
|
||||
</p>
|
||||
<p>
|
||||
StructureDefinition validation can be used to validate a resource against the
|
||||
official structure definitions (produced by HL7) as well as against custom
|
||||
definitions provided either by HL7 or by the user.
|
||||
</p>
|
||||
|
||||
<p class="doc_info_bubble">
|
||||
The instance validator is experimental in the DSTU2 mode, but has become very stable
|
||||
and full-featured in DSTU3 mode. Use with caution when validating DSTU2 resources using
|
||||
instance validator.
|
||||
</p>
|
||||
|
||||
<subsection name="Preparation">
|
||||
|
||||
<p>
|
||||
To use this functionality, you must add the following two dependencies
|
||||
to your classpath (or Maven POM file, Gradle file, etc.):
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<!-- TODO: add search.maven.org links to these -->
|
||||
<b>hapi-fhir-structures-hl7org-dstu2</b>
|
||||
: This file contains the "reference implementation"
|
||||
structures and tooling. You need to include it even if you are not using the RI model
|
||||
(the StructureDefinition validation will work against HAPI structures as well)
|
||||
</li>
|
||||
<li>
|
||||
<b>hapi-fhir-validation-resources-dstu2</b>
|
||||
: This file contains the official FHIR
|
||||
StructureDefinition files, and the ValueSets needed to support them.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
See the
|
||||
<a href="./download.html">download page</a>
|
||||
for more information.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Running the Validator">
|
||||
|
||||
<p>
|
||||
To execute the validator, you simply create an instance of
|
||||
<a href="./apidocs-dstu3/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.html">FhirInstanceValidator</a>
|
||||
and register it to new validator, as shown in the example below.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that the example below uses the official FHIR StructureDefintions and ValueSets
|
||||
to validate the resource. It will not work unless you include the
|
||||
<code>hapi-fhir-validation-resources-[version].jar</code> to your classpath.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="instanceValidator" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Supplying your own StructureDefinitions">
|
||||
|
||||
<p>
|
||||
The FhirInstanceValidator relies on the
|
||||
<a href="./apidocs-hl7org-dstu2/ca/uhn/fhir/validation/IValidationSupport.html">IValidationSupport</a>
|
||||
interface to load StructureDefinitions, and validate codes.
|
||||
</p>
|
||||
<p>
|
||||
By default, the
|
||||
<a href="./apidocs-hl7org-dstu2/ca/uhn/fhir/validation/DefaultProfileValidationSupport.html">DefaultProfileValidationSupport</a>
|
||||
implementation is used. This implementation loads the FHIR profiles from the
|
||||
validator resources JAR. If you want to use your own profiles, you may wish to
|
||||
supply your own implementation.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="instanceValidatorCustom" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
||||
|
|
Loading…
Reference in New Issue