Improve error messages for invalid validate request
This commit is contained in:
parent
a306e3136f
commit
f45ad117fe
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.model.api;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
|
|
||||||
public abstract class BaseIdentifiableElement extends BaseElement implements IIdentifiableElement {
|
public abstract class BaseIdentifiableElement extends BaseElement implements IIdentifiableElement {
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ public abstract class BaseIdentifiableElement extends BaseElement implements IId
|
||||||
* @deprecated Use {@link #getElementSpecificId()} instead. This method will be removed because it is easily
|
* @deprecated Use {@link #getElementSpecificId()} instead. This method will be removed because it is easily
|
||||||
* confused with other ID methods (such as patient#getIdentifier)
|
* confused with other ID methods (such as patient#getIdentifier)
|
||||||
*/
|
*/
|
||||||
|
@CoverageIgnore
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public IdDt getId() {
|
public IdDt getId() {
|
||||||
|
@ -55,6 +57,7 @@ public abstract class BaseIdentifiableElement extends BaseElement implements IId
|
||||||
* @deprecated Use {@link #setElementSpecificId(String)} instead. This method will be removed because it is easily
|
* @deprecated Use {@link #setElementSpecificId(String)} instead. This method will be removed because it is easily
|
||||||
* confused with other ID methods (such as patient#getIdentifier)
|
* confused with other ID methods (such as patient#getIdentifier)
|
||||||
*/
|
*/
|
||||||
|
@CoverageIgnore
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void setId(IdDt theId) {
|
public void setId(IdDt theId) {
|
||||||
|
@ -69,27 +72,33 @@ public abstract class BaseIdentifiableElement extends BaseElement implements IId
|
||||||
* @deprecated Use {@link #setElementSpecificId(String)} instead. This method will be removed because it is easily
|
* @deprecated Use {@link #setElementSpecificId(String)} instead. This method will be removed because it is easily
|
||||||
* confused with other ID methods (such as patient#getIdentifier)
|
* confused with other ID methods (such as patient#getIdentifier)
|
||||||
*/
|
*/
|
||||||
|
@CoverageIgnore
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setId(String theId) {
|
public void setId(String theId) {
|
||||||
myElementSpecificId = theId;
|
myElementSpecificId = theId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CoverageIgnore
|
||||||
private static class LockedId extends IdDt {
|
private static class LockedId extends IdDt {
|
||||||
|
|
||||||
|
@CoverageIgnore
|
||||||
public LockedId() {
|
public LockedId() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CoverageIgnore
|
||||||
public LockedId(String theElementSpecificId) {
|
public LockedId(String theElementSpecificId) {
|
||||||
super(theElementSpecificId);
|
super(theElementSpecificId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@CoverageIgnore
|
||||||
public IdDt setValue(String theValue) throws DataFormatException {
|
public IdDt setValue(String theValue) throws DataFormatException {
|
||||||
throw new UnsupportedOperationException("Use IElement#setElementSpecificId(String) to set the element ID for an element");
|
throw new UnsupportedOperationException("Use IElement#setElementSpecificId(String) to set the element ID for an element");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@CoverageIgnore
|
||||||
public void setValueAsString(String theValue) throws DataFormatException {
|
public void setValueAsString(String theValue) throws DataFormatException {
|
||||||
throw new UnsupportedOperationException("Use IElement#setElementSpecificId(String) to set the element ID for an element");
|
throw new UnsupportedOperationException("Use IElement#setElementSpecificId(String) to set the element ID for an element");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package ca.uhn.fhir.rest.api;
|
package ca.uhn.fhir.rest.api;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
|
@ -27,18 +31,41 @@ public enum ValidationModeEnum {
|
||||||
/**
|
/**
|
||||||
* The server checks the content, and then checks that the content would be acceptable as a create (e.g. that the content would not validate any uniqueness constraints)
|
* The server checks the content, and then checks that the content would be acceptable as a create (e.g. that the content would not validate any uniqueness constraints)
|
||||||
*/
|
*/
|
||||||
CREATE,
|
CREATE("create"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The server checks the content, and then checks that it would accept it as an update against the nominated specific resource (e.g. that there are no changes to immutable fields the server does not allow to change, and checking version integrity if appropriate)
|
* The server checks the content, and then checks that it would accept it as an update against the nominated specific resource (e.g. that there are no changes to immutable fields the server does not allow to change, and checking version integrity if appropriate)
|
||||||
*/
|
*/
|
||||||
UPDATE,
|
UPDATE("update"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The server ignores the content, and checks that the nominated resource is allowed to be deleted (e.g. checking referential integrity rules)
|
* The server ignores the content, and checks that the nominated resource is allowed to be deleted (e.g. checking referential integrity rules)
|
||||||
*/
|
*/
|
||||||
DELETE;
|
DELETE("delete");
|
||||||
|
|
||||||
|
private static HashMap<String, ValidationModeEnum> myCodeToValue;
|
||||||
|
private String myCode;
|
||||||
|
|
||||||
|
static {
|
||||||
|
myCodeToValue = new HashMap<String, ValidationModeEnum>();
|
||||||
|
for (ValidationModeEnum next : values()) {
|
||||||
|
myCodeToValue.put(next.getCode(), next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationModeEnum forCode(String theCode) {
|
||||||
|
Validate.notBlank(theCode, "theCode must not be blank");
|
||||||
|
return myCodeToValue.get(theCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return myCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValidationModeEnum(String theCode) {
|
||||||
|
myCode = theCode;
|
||||||
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public boolean isEmpty() {
|
// public boolean isEmpty() {
|
||||||
// return false;
|
// return false;
|
||||||
|
|
|
@ -498,12 +498,20 @@ public class MethodUtil {
|
||||||
param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_MODE, 0, 1).setConverter(new IConverter() {
|
param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_MODE, 0, 1).setConverter(new IConverter() {
|
||||||
@Override
|
@Override
|
||||||
public Object incomingServer(Object theObject) {
|
public Object incomingServer(Object theObject) {
|
||||||
return ValidationModeEnum.valueOf(theObject.toString().toUpperCase());
|
if (isNotBlank(theObject.toString())) {
|
||||||
|
ValidationModeEnum retVal = ValidationModeEnum.forCode(theObject.toString());
|
||||||
|
if (retVal == null) {
|
||||||
|
OperationParameter.throwInvalidMode(theObject.toString());
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object outgoingClient(Object theObject) {
|
public Object outgoingClient(Object theObject) {
|
||||||
return new StringDt(((ValidationModeEnum)theObject).name().toLowerCase());
|
return new StringDt(((ValidationModeEnum)theObject).getCode());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (nextAnnotation instanceof Validate.Profile) {
|
} else if (nextAnnotation instanceof Validate.Profile) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package ca.uhn.fhir.rest.method;
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
|
@ -179,7 +181,7 @@ public class OperationParameter implements IParameter {
|
||||||
*/
|
*/
|
||||||
isSearchParam &= typeIsConcrete && !IBase.class.isAssignableFrom(myParameterType);
|
isSearchParam &= typeIsConcrete && !IBase.class.isAssignableFrom(myParameterType);
|
||||||
|
|
||||||
myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType) || String.class.equals(myParameterType) || isSearchParam;
|
myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType) || String.class.equals(myParameterType) || isSearchParam || ValidationModeEnum.class.equals(myParameterType);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The parameter can be of type string for validation methods - This is a bit weird. See ValidateDstu2Test. We
|
* The parameter can be of type string for validation methods - This is a bit weird. See ValidateDstu2Test. We
|
||||||
|
@ -291,7 +293,17 @@ public class OperationParameter implements IParameter {
|
||||||
for (String next : paramValues) {
|
for (String next : paramValues) {
|
||||||
matchingParamValues.add(next);
|
matchingParamValues.add(next);
|
||||||
}
|
}
|
||||||
|
} else if (ValidationModeEnum.class.equals(myParameterType)) {
|
||||||
|
|
||||||
|
if (isNotBlank(paramValues[0])) {
|
||||||
|
ValidationModeEnum validationMode = ValidationModeEnum.forCode(paramValues[0]);
|
||||||
|
if (validationMode != null) {
|
||||||
|
matchingParamValues.add(validationMode);
|
||||||
|
} else {
|
||||||
|
throwInvalidMode(paramValues[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (String nextValue : paramValues) {
|
for (String nextValue : paramValues) {
|
||||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||||
|
@ -374,6 +386,10 @@ public class OperationParameter implements IParameter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void throwInvalidMode(String paramValues) {
|
||||||
|
throw new InvalidRequestException("Invalid mode value: \"" + paramValues + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void tryToAddValues(List<IBase> theParamValues, List<Object> theMatchingParamValues) {
|
private void tryToAddValues(List<IBase> theParamValues, List<Object> theMatchingParamValues) {
|
||||||
for (Object nextValue : theParamValues) {
|
for (Object nextValue : theParamValues) {
|
||||||
|
@ -436,4 +452,5 @@ public class OperationParameter implements IParameter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for working with {@link IBundleProvider}
|
* Utility methods for working with {@link IBundleProvider}
|
||||||
|
@ -33,6 +34,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
public class BundleProviders {
|
public class BundleProviders {
|
||||||
|
|
||||||
/** Non instantiable */
|
/** Non instantiable */
|
||||||
|
@CoverageIgnore
|
||||||
private BundleProviders() {
|
private BundleProviders() {
|
||||||
//nothing
|
//nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.hl7.fhir.dstu3.model.IdType;
|
||||||
import org.hl7.fhir.dstu3.model.Parameters;
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
import org.hl7.fhir.dstu3.model.Resource;
|
import org.hl7.fhir.dstu3.model.Resource;
|
||||||
import org.hl7.fhir.dstu3.model.StringType;
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.dstu3.model.UriType;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
@ -138,7 +139,7 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
IBaseParameters inputParameters;
|
IBaseParameters inputParameters;
|
||||||
if (ctx.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
|
if (ctx.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
|
||||||
Parameters p = new Parameters();
|
Parameters p = new Parameters();
|
||||||
p.addParameter().setName("url").setValue(new StringType(termUrl));
|
p.addParameter().setName("url").setValue(new UriType(termUrl));
|
||||||
p.addParameter().setName("localfile").setValue(new StringType(datafile));
|
p.addParameter().setName("localfile").setValue(new StringType(datafile));
|
||||||
inputParameters = p;
|
inputParameters = p;
|
||||||
} else {
|
} else {
|
||||||
|
@ -154,6 +155,7 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
ourLog.info("Upload complete!");
|
ourLog.info("Upload complete!");
|
||||||
|
ourLog.info("Response:\n{}", ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1428,16 +1428,6 @@ public class SearchBuilder {
|
||||||
return singleCode;
|
return singleCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> toCodes(Set<TermConcept> theCodeConcepts) {
|
|
||||||
HashSet<String> retVal = Sets.newHashSet();
|
|
||||||
for (TermConcept next : theCodeConcepts) {
|
|
||||||
if (isNotBlank(next.getCode())) {
|
|
||||||
retVal.add(next.getCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Predicate createResourceLinkPathPredicate(String theParamName, Root<? extends ResourceLink> from) {
|
private Predicate createResourceLinkPathPredicate(String theParamName, Root<? extends ResourceLink> from) {
|
||||||
return createResourceLinkPathPredicate(myContext, theParamName, from, myResourceType);
|
return createResourceLinkPathPredicate(myContext, theParamName, from, myResourceType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,9 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
|
||||||
validator.registerValidatorModule(new IdChecker(theMode));
|
validator.registerValidatorModule(new IdChecker(theMode));
|
||||||
|
|
||||||
ValidationResult result;
|
ValidationResult result;
|
||||||
if (isNotBlank(theRawResource)) {
|
if (theResource == null) {
|
||||||
|
throw new InvalidRequestException("No resource supplied for $validate operation (resource is required unless mode is \"delete\")");
|
||||||
|
} else if (isNotBlank(theRawResource)) {
|
||||||
result = validator.validateWithResult(theRawResource);
|
result = validator.validateWithResult(theRawResource);
|
||||||
} else {
|
} else {
|
||||||
result = validator.validateWithResult(theResource);
|
result = validator.validateWithResult(theResource);
|
||||||
|
|
|
@ -217,12 +217,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Validating code system");
|
ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)");
|
||||||
|
|
||||||
// Validate the code system
|
// Validate the code system
|
||||||
IdentityHashMap<TermConcept, Object> conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
IdentityHashMap<TermConcept, Object> conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
||||||
|
int totalCodeCount = 0;
|
||||||
for (TermConcept next : theCodeSystem.getConcepts()) {
|
for (TermConcept next : theCodeSystem.getConcepts()) {
|
||||||
validateConceptForStorage(next, theCodeSystem, conceptsStack);
|
totalCodeCount += validateConceptForStorage(next, theCodeSystem, conceptsStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Saving version");
|
ourLog.info("Saving version");
|
||||||
|
@ -234,7 +235,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
||||||
codeSystem.setCurrentVersion(theCodeSystem);
|
codeSystem.setCurrentVersion(theCodeSystem);
|
||||||
myCodeSystemDao.save(codeSystem);
|
myCodeSystemDao.save(codeSystem);
|
||||||
|
|
||||||
ourLog.info("Saving concepts...");
|
ourLog.info("Saving {} concepts...", totalCodeCount);
|
||||||
|
|
||||||
conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
||||||
for (TermConcept next : theCodeSystem.getConcepts()) {
|
for (TermConcept next : theCodeSystem.getConcepts()) {
|
||||||
|
@ -275,20 +276,24 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack) {
|
private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack) {
|
||||||
ValidateUtil.isNotNullOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "Codesystem contains a code which does not reference the codesystem");
|
ValidateUtil.isNotNullOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "Codesystem contains a code which does not reference the codesystem");
|
||||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code which does not reference the codesystem");
|
ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code which does not reference the codesystem");
|
||||||
|
|
||||||
|
|
||||||
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
||||||
throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode());
|
throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int retVal = 1;
|
||||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||||
next.setCodeSystem(theCodeSystem);
|
next.setCodeSystem(theCodeSystem);
|
||||||
validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack);
|
retVal += validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
theConceptsStack.remove(theConcept);
|
theConceptsStack.remove(theConcept);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TermConcept findCode(String theCodeSystem, String theCode) {
|
public TermConcept findCode(String theCodeSystem, String theCode) {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
@ -46,6 +47,7 @@ import org.apache.commons.csv.CSVParser;
|
||||||
import org.apache.commons.csv.CSVRecord;
|
import org.apache.commons.csv.CSVRecord;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
@ -68,20 +70,35 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IHapiTerminologySvc myTermSvc;
|
private IHapiTerminologySvc myTermSvc;
|
||||||
|
|
||||||
private void dropCircularRefs(TermConcept theConcept, HashSet<String> theChain) {
|
private void dropCircularRefs(TermConcept theConcept, LinkedHashSet<String> theChain, Map<String, TermConcept> theCode2concept) {
|
||||||
|
|
||||||
|
theChain.add(theConcept.getCode());
|
||||||
for (Iterator<TermConceptParentChildLink> childIter = theConcept.getChildren().iterator(); childIter.hasNext();) {
|
for (Iterator<TermConceptParentChildLink> childIter = theConcept.getChildren().iterator(); childIter.hasNext();) {
|
||||||
TermConceptParentChildLink next = childIter.next();
|
TermConceptParentChildLink next = childIter.next();
|
||||||
TermConcept nextChild = next.getChild();
|
TermConcept nextChild = next.getChild();
|
||||||
if (theChain.contains(nextChild.getCode())) {
|
if (theChain.contains(nextChild.getCode())) {
|
||||||
ourLog.info("Removing circular reference code {} from parent {}", nextChild.getCode(), theConcept.getCode());
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("Removing circular reference code ");
|
||||||
|
b.append(nextChild.getCode());
|
||||||
|
b.append(" from parent ");
|
||||||
|
b.append(nextChild.getCode());
|
||||||
|
b.append(". Chain was: ");
|
||||||
|
for (String nextInChain : theChain) {
|
||||||
|
TermConcept nextCode = theCode2concept.get(nextInChain);
|
||||||
|
b.append(nextCode.getCode());
|
||||||
|
b.append('[');
|
||||||
|
b.append(StringUtils.substring(nextCode.getDisplay(), 0, 20).replace("[", "").replace("]", "").trim());
|
||||||
|
b.append("] ");
|
||||||
|
}
|
||||||
|
ourLog.info(b.toString(), theConcept.getCode());
|
||||||
childIter.remove();
|
childIter.remove();
|
||||||
} else {
|
} else {
|
||||||
theChain.add(theConcept.getCode());
|
dropCircularRefs(nextChild, theChain, theCode2concept);
|
||||||
dropCircularRefs(nextChild, theChain);
|
|
||||||
theChain.remove(theConcept.getCode());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
theChain.remove(theConcept.getCode());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TermConcept getOrCreateConcept(TermCodeSystemVersion codeSystemVersion, Map<String, TermConcept> id2concept, String id) {
|
private TermConcept getOrCreateConcept(TermCodeSystemVersion codeSystemVersion, Map<String, TermConcept> id2concept, String id) {
|
||||||
|
@ -152,7 +169,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.debug("Streaming ZIP entry {} into temporary file", nextEntry.getName());
|
ourLog.info("Streaming ZIP entry {} into temporary file", nextEntry.getName());
|
||||||
|
|
||||||
File nextOutFile = File.createTempFile("hapi_fhir", ".csv");
|
File nextOutFile = File.createTempFile("hapi_fhir", ".csv");
|
||||||
nextOutFile.deleteOnExit();
|
nextOutFile.deleteOnExit();
|
||||||
|
@ -206,7 +223,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
||||||
ourLog.info("Done loading SNOMED CT files - {} root codes, {} total codes", rootConcepts.size(), code2concept.size());
|
ourLog.info("Done loading SNOMED CT files - {} root codes, {} total codes", rootConcepts.size(), code2concept.size());
|
||||||
|
|
||||||
for (TermConcept next : rootConcepts.values()) {
|
for (TermConcept next : rootConcepts.values()) {
|
||||||
dropCircularRefs(next, new HashSet<String>());
|
dropCircularRefs(next, new LinkedHashSet<String>(), code2concept);
|
||||||
}
|
}
|
||||||
|
|
||||||
codeSystemVersion.getConcepts().addAll(rootConcepts.values());
|
codeSystemVersion.getConcepts().addAll(rootConcepts.values());
|
||||||
|
|
|
@ -51,6 +51,7 @@ import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
|
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
|
||||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.dstu3.model.CodeType;
|
||||||
import org.hl7.fhir.dstu3.model.Coding;
|
import org.hl7.fhir.dstu3.model.Coding;
|
||||||
import org.hl7.fhir.dstu3.model.Condition;
|
import org.hl7.fhir.dstu3.model.Condition;
|
||||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||||
|
@ -2693,6 +2694,46 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateBadInputViaPost() throws IOException {
|
||||||
|
|
||||||
|
Parameters input = new Parameters();
|
||||||
|
input.addParameter().setName("mode").setValue(new CodeType("create"));
|
||||||
|
|
||||||
|
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||||
|
ourLog.info(inputStr);
|
||||||
|
|
||||||
|
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);
|
||||||
|
try {
|
||||||
|
String resp = IOUtils.toString(response.getEntity().getContent());
|
||||||
|
ourLog.info(resp);
|
||||||
|
assertThat(resp, containsString("No resource supplied for $validate operation (resource is required unless mode is "delete")"));
|
||||||
|
assertEquals(400, response.getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateBadInputViaGet() throws IOException {
|
||||||
|
|
||||||
|
HttpGet get = new HttpGet(ourServerBase + "/Patient/$validate?mode=create");
|
||||||
|
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||||
|
try {
|
||||||
|
String resp = IOUtils.toString(response.getEntity().getContent());
|
||||||
|
ourLog.info(resp);
|
||||||
|
assertThat(resp, containsString("No resource supplied for $validate operation (resource is required unless mode is "delete")"));
|
||||||
|
assertEquals(400, response.getStatusLine().getStatusCode());
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateResourceWithId() throws IOException {
|
public void testValidateResourceWithId() throws IOException {
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,24 @@ public class OperationServerDstu2Test {
|
||||||
assertThat(response, containsString("HTTP Method GET is not allowed"));
|
assertThat(response, containsString("HTTP Method GET is not allowed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWrongParameterType() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new IntegerDt(123));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
CloseableHttpResponse status = ourClient.execute(httpPost);
|
||||||
|
try {
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
assertThat(response, containsString("Request has parameter PARAM1 of type IntegerDt but method expects type StringDt"));
|
||||||
|
ourLog.info(response);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOperationOnInstance() throws Exception {
|
public void testOperationOnInstance() throws Exception {
|
||||||
Parameters p = new Parameters();
|
Parameters p = new Parameters();
|
||||||
|
|
|
@ -200,6 +200,18 @@ public class SearchDstu2Test {
|
||||||
assertThat(responseContent, containsString("SYSTEM"));
|
assertThat(responseContent, containsString("SYSTEM"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchMethodReturnsNull() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchReturnNull");
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals("searchReturnNull", ourLastMethod);
|
||||||
|
assertThat(responseContent, containsString("<total value=\"0\"/>"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByPostWithBodyAndUrlParams() throws Exception {
|
public void testSearchByPostWithBodyAndUrlParams() throws Exception {
|
||||||
HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json");
|
HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json");
|
||||||
|
@ -513,6 +525,14 @@ public class SearchDstu2Test {
|
||||||
}
|
}
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Search(queryName="searchReturnNull")
|
||||||
|
public List<Patient> searchNoList() {
|
||||||
|
ourLastMethod = "searchReturnNull";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Search()
|
@Search()
|
||||||
public List<Patient> searchQuantity(
|
public List<Patient> searchQuantity(
|
||||||
|
|
|
@ -0,0 +1,721 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.blankOrNullString;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Conformance;
|
||||||
|
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestOperationComponent;
|
||||||
|
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||||
|
import org.hl7.fhir.dstu3.model.IdType;
|
||||||
|
import org.hl7.fhir.dstu3.model.IntegerType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Money;
|
||||||
|
import org.hl7.fhir.dstu3.model.OperationDefinition;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.dstu3.model.UnsignedIntType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
|
||||||
|
public class OperationServerDstu3Test {
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static FhirContext ourCtx;
|
||||||
|
|
||||||
|
private static IdType ourLastId;
|
||||||
|
private static String ourLastMethod;
|
||||||
|
private static StringType ourLastParam1;
|
||||||
|
private static Patient ourLastParam2;
|
||||||
|
private static List<StringType> ourLastParam3;
|
||||||
|
private static Money ourLastParamMoney1;
|
||||||
|
private static UnsignedIntType ourLastParamUnsignedInt1;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu3Test.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastParam1 = null;
|
||||||
|
ourLastParam2 = null;
|
||||||
|
ourLastParam3 = null;
|
||||||
|
ourLastParamUnsignedInt1 = null;
|
||||||
|
ourLastParamMoney1 = null;
|
||||||
|
ourLastId = null;
|
||||||
|
ourLastMethod = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformance() throws Exception {
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||||
|
Conformance p = client.fetchConformance().ofType(Conformance.class).execute();
|
||||||
|
List<ConformanceRestOperationComponent> ops = p.getRest().get(0).getOperation();
|
||||||
|
assertThat(ops.size(), greaterThan(1));
|
||||||
|
assertThat(ops.get(0).getDefinition().getReferenceElement().getValue(), startsWith("#"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstanceEverythingGet() throws Exception {
|
||||||
|
|
||||||
|
// Try with a GET
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything");
|
||||||
|
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("instance $everything", ourLastMethod);
|
||||||
|
assertThat(response, startsWith("<Bundle"));
|
||||||
|
assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstanceEverythingHapiClient() throws Exception {
|
||||||
|
Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute();
|
||||||
|
Bundle b = (Bundle) p.getParameterFirstRep().getResource();
|
||||||
|
|
||||||
|
assertEquals("instance $everything", ourLastMethod);
|
||||||
|
assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstanceEverythingPost() throws Exception {
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters());
|
||||||
|
|
||||||
|
// Try with a POST
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("instance $everything", ourLastMethod);
|
||||||
|
assertThat(response, startsWith("<Bundle"));
|
||||||
|
assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationCantUseGetIfItIsntIdempotent() throws Exception {
|
||||||
|
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("POST", status.getFirstHeader(Constants.HEADER_ALLOW).getValue());
|
||||||
|
assertThat(response, containsString("HTTP Method GET is not allowed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWrongParameterType() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new IntegerType(123));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
CloseableHttpResponse status = ourClient.execute(httpPost);
|
||||||
|
try {
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
assertThat(response, containsString("Request has parameter PARAM1 of type IntegerType but method expects type StringType"));
|
||||||
|
ourLog.info(response);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationOnInstance() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals("123", ourLastId.getIdPart());
|
||||||
|
assertEquals("$OP_INSTANCE", ourLastMethod);
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Against type should fail
|
||||||
|
*/
|
||||||
|
|
||||||
|
httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(response);
|
||||||
|
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationOnInstanceAndType_Instance() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals("123", ourLastId.getIdPart());
|
||||||
|
assertEquals("$OP_INSTANCE_OR_TYPE", ourLastMethod);
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationOnInstanceAndType_Type() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
CloseableHttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals(null, ourLastId);
|
||||||
|
assertEquals("$OP_INSTANCE_OR_TYPE", ourLastMethod);
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationOnServer() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals("$OP_SERVER", ourLastMethod);
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationOnType() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals("$OP_TYPE", ourLastMethod);
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationOnTypeReturnBundle() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals("$OP_TYPE_RET_BUNDLE", ourLastMethod);
|
||||||
|
|
||||||
|
Bundle resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
|
||||||
|
assertEquals("100", resp.getEntryFirstRep().getResponse().getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithBundleProviderResponse() throws Exception {
|
||||||
|
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true");
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(response);
|
||||||
|
|
||||||
|
Bundle resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithGetUsingParams() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||||
|
assertNull(ourLastParam2);
|
||||||
|
assertEquals("$OP_TYPE", ourLastMethod);
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
assertEquals(405, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("POST", status.getFirstHeader(Constants.HEADER_ALLOW).getValue());
|
||||||
|
assertThat(response, containsString("Can not invoke operation $OP_TYPE using HTTP GET because parameter PARAM2 is not a primitive datatype"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithListParam() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val1"));
|
||||||
|
p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val2"));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("$OP_SERVER_LIST_PARAM", ourLastMethod);
|
||||||
|
assertEquals(true, ourLastParam2.getActive());
|
||||||
|
assertEquals(null, ourLastParam1);
|
||||||
|
assertEquals(2, ourLastParam3.size());
|
||||||
|
assertEquals("PARAM3val1", ourLastParam3.get(0).getValue());
|
||||||
|
assertEquals("PARAM3val2", ourLastParam3.get(1).getValue());
|
||||||
|
|
||||||
|
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||||
|
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithProfileDatatypeParams() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new IntegerType("123"));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("$OP_PROFILE_DT", ourLastMethod);
|
||||||
|
assertEquals("123", ourLastParamUnsignedInt1.getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithProfileDatatypeParams2() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
Money money = new Money();
|
||||||
|
money.setCode("CODE");
|
||||||
|
money.setSystem("SYSTEM");
|
||||||
|
money.setValue(123L);
|
||||||
|
p.addParameter().setName("PARAM1").setValue(money);
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT2");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("$OP_PROFILE_DT2", ourLastMethod);
|
||||||
|
assertEquals("CODE", ourLastParamMoney1.getCode());
|
||||||
|
assertEquals("SYSTEM", ourLastParamMoney1.getSystem());
|
||||||
|
assertEquals("123", ourLastParamMoney1.getValue().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWithProfileDatatypeUrl() throws Exception {
|
||||||
|
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT?PARAM1=123");
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("$OP_PROFILE_DT", ourLastMethod);
|
||||||
|
assertEquals("123", ourLastParamUnsignedInt1.getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationWrongParamType() throws Exception {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("PARAM1").setValue(new IntegerType("123"));
|
||||||
|
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||||
|
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE");
|
||||||
|
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||||
|
String response = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info(status.getStatusLine().toString());
|
||||||
|
ourLog.info(response);
|
||||||
|
|
||||||
|
assertThat(response, containsString("Request has parameter PARAM1 of type IntegerType but method expects type StringType"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadWithOperations() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals("read", ourLastMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourCtx = FhirContext.forDstu3();
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||||
|
|
||||||
|
servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2));
|
||||||
|
|
||||||
|
servlet.setFhirContext(ourCtx);
|
||||||
|
servlet.setResourceProviders(new PatientProvider());
|
||||||
|
servlet.setPlainProviders(new PlainProvider());
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] theValue) {
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("start").setValue(new DateTimeType("2001-01-02"));
|
||||||
|
p.addParameter().setName("end").setValue(new DateTimeType("2015-07-10"));
|
||||||
|
String inParamsStr = FhirContext.forDstu2().newXmlParser().encodeResourceToString(p);
|
||||||
|
ourLog.info(inParamsStr.replace("\"", "\\\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_INSTANCE")
|
||||||
|
public Parameters opInstance(
|
||||||
|
@IdParam IdType theId,
|
||||||
|
@OperationParam(name="PARAM1") StringType theParam1,
|
||||||
|
@OperationParam(name="PARAM2") Patient theParam2
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_INSTANCE";
|
||||||
|
ourLastId = theId;
|
||||||
|
ourLastParam1 = theParam1;
|
||||||
|
ourLastParam2 = theParam2;
|
||||||
|
|
||||||
|
Parameters retVal = new Parameters();
|
||||||
|
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_INSTANCE_OR_TYPE")
|
||||||
|
public Parameters opInstanceOrType(
|
||||||
|
@IdParam(optional=true) IdType theId,
|
||||||
|
@OperationParam(name="PARAM1") StringType theParam1,
|
||||||
|
@OperationParam(name="PARAM2") Patient theParam2
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_INSTANCE_OR_TYPE";
|
||||||
|
ourLastId = theId;
|
||||||
|
ourLastParam1 = theParam1;
|
||||||
|
ourLastParam2 = theParam2;
|
||||||
|
|
||||||
|
Parameters retVal = new Parameters();
|
||||||
|
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_PROFILE_DT2", idempotent=true)
|
||||||
|
public Bundle opProfileType(
|
||||||
|
@OperationParam(name="PARAM1") Money theParam1
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_PROFILE_DT2";
|
||||||
|
ourLastParamMoney1 = theParam1;
|
||||||
|
|
||||||
|
Bundle retVal = new Bundle();
|
||||||
|
retVal.addEntry().getResponse().setStatus("100");
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_PROFILE_DT", idempotent=true)
|
||||||
|
public Bundle opProfileType(
|
||||||
|
@OperationParam(name="PARAM1") UnsignedIntType theParam1
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_PROFILE_DT";
|
||||||
|
ourLastParamUnsignedInt1 = theParam1;
|
||||||
|
|
||||||
|
Bundle retVal = new Bundle();
|
||||||
|
retVal.addEntry().getResponse().setStatus("100");
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_TYPE", idempotent=true)
|
||||||
|
public Parameters opType(
|
||||||
|
@OperationParam(name="PARAM1") StringType theParam1,
|
||||||
|
@OperationParam(name="PARAM2") Patient theParam2
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_TYPE";
|
||||||
|
ourLastParam1 = theParam1;
|
||||||
|
ourLastParam2 = theParam2;
|
||||||
|
|
||||||
|
Parameters retVal = new Parameters();
|
||||||
|
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_TYPE_ONLY_STRING", idempotent=true)
|
||||||
|
public Parameters opTypeOnlyString(
|
||||||
|
@OperationParam(name="PARAM1") StringType theParam1
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_TYPE";
|
||||||
|
ourLastParam1 = theParam1;
|
||||||
|
|
||||||
|
Parameters retVal = new Parameters();
|
||||||
|
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_TYPE_RET_BUNDLE")
|
||||||
|
public Bundle opTypeRetBundle(
|
||||||
|
@OperationParam(name="PARAM1") StringType theParam1,
|
||||||
|
@OperationParam(name="PARAM2") Patient theParam2
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_TYPE_RET_BUNDLE";
|
||||||
|
ourLastParam1 = theParam1;
|
||||||
|
ourLastParam2 = theParam2;
|
||||||
|
|
||||||
|
Bundle retVal = new Bundle();
|
||||||
|
retVal.addEntry().getResponse().setStatus("100");
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(name = "$everything", idempotent=true)
|
||||||
|
public Bundle patientEverything(@IdParam IdType thePatientId) {
|
||||||
|
ourLastMethod = "instance $everything";
|
||||||
|
ourLastId = thePatientId;
|
||||||
|
return new Bundle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just to make sure this method doesn't "steal" calls
|
||||||
|
*/
|
||||||
|
@Read
|
||||||
|
public Patient read(@IdParam IdType theId) {
|
||||||
|
ourLastMethod = "read";
|
||||||
|
Patient retVal = new Patient();
|
||||||
|
retVal.setId(theId);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlainProvider {
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_INSTANCE_BUNDLE_PROVIDER", idempotent=true)
|
||||||
|
public IBundleProvider opInstanceReturnsBundleProvider() {
|
||||||
|
ourLastMethod = "$OP_INSTANCE_BUNDLE_PROVIDER";
|
||||||
|
|
||||||
|
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||||
|
for (int i =0; i < 100;i++) {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setId("Patient/" + i);
|
||||||
|
p.addName().addFamily("Patient " + i);
|
||||||
|
resources.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleBundleProvider(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_SERVER")
|
||||||
|
public Parameters opServer(
|
||||||
|
@OperationParam(name="PARAM1") StringType theParam1,
|
||||||
|
@OperationParam(name="PARAM2") Patient theParam2
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_SERVER";
|
||||||
|
ourLastParam1 = theParam1;
|
||||||
|
ourLastParam2 = theParam2;
|
||||||
|
|
||||||
|
Parameters retVal = new Parameters();
|
||||||
|
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Operation(name="$OP_SERVER_LIST_PARAM")
|
||||||
|
public Parameters opServerListParam(
|
||||||
|
@OperationParam(name="PARAM2") Patient theParam2,
|
||||||
|
@OperationParam(name="PARAM3") List<StringType> theParam3
|
||||||
|
) {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ourLastMethod = "$OP_SERVER_LIST_PARAM";
|
||||||
|
ourLastParam2 = theParam2;
|
||||||
|
ourLastParam3 = theParam3;
|
||||||
|
|
||||||
|
Parameters retVal = new Parameters();
|
||||||
|
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,298 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.dstu3.model.CodeType;
|
||||||
|
import org.hl7.fhir.dstu3.model.IdType;
|
||||||
|
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||||
|
import org.hl7.fhir.dstu3.model.Organization;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Validate;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
|
||||||
|
public class ValidateDstu3Test {
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||||
|
private static EncodingEnum ourLastEncoding;
|
||||||
|
private static ValidationModeEnum ourLastMode;
|
||||||
|
private static String ourLastProfile;
|
||||||
|
private static String ourLastResourceBody;
|
||||||
|
private static OperationOutcome ourOutcomeToReturn;
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
public static Patient ourLastPatient;
|
||||||
|
|
||||||
|
@Before()
|
||||||
|
public void before() {
|
||||||
|
ourLastResourceBody = null;
|
||||||
|
ourLastEncoding = null;
|
||||||
|
ourOutcomeToReturn = null;
|
||||||
|
ourLastMode = null;
|
||||||
|
ourLastProfile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidate() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("resource").setResource(patient);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String resp = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertThat(resp, stringContainsInOrder("<OperationOutcome"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateInvalidMode() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("resource").setResource(patient);
|
||||||
|
params.addParameter().setName("mode").setValue(new CodeType("AAA"));
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String resp = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertThat(resp, stringContainsInOrder("Invalid mode value: "AAA""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateBlankMode() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("resource").setResource(patient);
|
||||||
|
params.addParameter().setName("mode").setValue(new CodeType(" "));
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String resp = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateMissingResource() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("mode").setValue(new CodeType("create"));
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String resp = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(null, ourLastPatient);
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateWithNoParsed() throws Exception {
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.addIdentifier().setValue("001");
|
||||||
|
org.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("resource").setResource(org);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertThat(ourLastResourceBody, stringContainsInOrder("\"resourceType\":\"Organization\"", "\"identifier\"", "\"value\":\"001"));
|
||||||
|
assertEquals(EncodingEnum.JSON, ourLastEncoding);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateWithOptions() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("resource").setResource(patient);
|
||||||
|
params.addParameter().setName("profile").setValue(new StringType("http://foo"));
|
||||||
|
params.addParameter().setName("mode").setValue(new StringType(ValidationModeEnum.CREATE.name()));
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String resp = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertThat(resp, stringContainsInOrder("<OperationOutcome"));
|
||||||
|
assertEquals("http://foo", ourLastProfile);
|
||||||
|
assertEquals(ValidationModeEnum.CREATE, ourLastMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateWithResults() throws Exception {
|
||||||
|
|
||||||
|
ourOutcomeToReturn = new OperationOutcome();
|
||||||
|
ourOutcomeToReturn.addIssue().setDiagnostics("FOOBAR");
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
Parameters params = new Parameters();
|
||||||
|
params.addParameter().setName("resource").setResource(patient);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String resp = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertThat(resp, stringContainsInOrder("<OperationOutcome", "FOOBAR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
PatientProvider patientProvider = new PatientProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||||
|
servlet.setResourceProviders(patientProvider, new OrganizationProvider());
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OrganizationProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Organization> getResourceType() {
|
||||||
|
return Organization.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Validate()
|
||||||
|
public MethodOutcome validate(@ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) {
|
||||||
|
ourLastResourceBody = theResourceBody;
|
||||||
|
ourLastEncoding = theEncoding;
|
||||||
|
|
||||||
|
return new MethodOutcome(new IdType("001"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Validate()
|
||||||
|
public MethodOutcome validatePatient(@ResourceParam Patient thePatient, @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile) {
|
||||||
|
|
||||||
|
ourLastPatient = thePatient;
|
||||||
|
IdType id;
|
||||||
|
if (thePatient != null) {
|
||||||
|
id = new IdType(thePatient.getIdentifier().get(0).getValue());
|
||||||
|
if (thePatient.getIdElement().isEmpty() == false) {
|
||||||
|
id = thePatient.getIdElement();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id = new IdType("1");
|
||||||
|
}
|
||||||
|
ourLastMode = theMode;
|
||||||
|
ourLastProfile = theProfile;
|
||||||
|
|
||||||
|
MethodOutcome outcome = new MethodOutcome(id.withVersion("002"));
|
||||||
|
outcome.setOperationOutcome(ourOutcomeToReturn);
|
||||||
|
return outcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -244,6 +244,10 @@
|
||||||
DateTime parser incorrectly parsed times where more than 3 digits of
|
DateTime parser incorrectly parsed times where more than 3 digits of
|
||||||
precision were provided on the seconds after the decimal point
|
precision were provided on the seconds after the decimal point
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Improve error messages when the $validate operation is called but no resource
|
||||||
|
is actually supplied to validate
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.5" date="2016-04-20">
|
<release version="1.5" date="2016-04-20">
|
||||||
<action type="fix" issue="339">
|
<action type="fix" issue="339">
|
||||||
|
|
Loading…
Reference in New Issue