Work on support for $everything operation
This commit is contained in:
parent
c8a70c1904
commit
cffda7539f
|
@ -364,6 +364,29 @@ public class GenericClientExample {
|
|||
public static void main(String[] args) {
|
||||
operation();
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
private static void operationHttpGet() {
|
||||
// START SNIPPET: operationHttpGet
|
||||
// Create a client to talk to the HeathIntersections server
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
|
||||
client.registerInterceptor(new LoggingInterceptor(true));
|
||||
|
||||
// Create the input parameters to pass to the server
|
||||
Parameters inParams = new Parameters();
|
||||
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
|
||||
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||
|
||||
// Invoke $everything on "Patient/1"
|
||||
Parameters outParams = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("Patient", "1"))
|
||||
.named("$everything")
|
||||
.withParameters(inParams)
|
||||
.useHttpGet() // Use HTTP GET instead of POST
|
||||
.execute();
|
||||
// END SNIPPET: operationHttpGet
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void operation() {
|
||||
|
@ -388,4 +411,22 @@ public class GenericClientExample {
|
|||
// END SNIPPET: operation
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void operationNoIn() {
|
||||
// START SNIPPET: operationNoIn
|
||||
// Create a client to talk to the HeathIntersections server
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
|
||||
client.registerInterceptor(new LoggingInterceptor(true));
|
||||
|
||||
// Invoke $everything on "Patient/1"
|
||||
Parameters outParams = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("Patient", "1"))
|
||||
.named("$everything")
|
||||
.withNoParameters(Parameters.class) // No input parameters
|
||||
.execute();
|
||||
// END SNIPPET: operationNoIn
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1030,6 +1030,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
private String myOperationName;
|
||||
private IBaseParameters myParameters;
|
||||
private Class<? extends IBaseResource> myType;
|
||||
private boolean myUseHttpGet;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
|
@ -1047,7 +1048,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
id = null;
|
||||
}
|
||||
|
||||
HttpPostClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters);
|
||||
BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet);
|
||||
|
||||
IClientResponseHandler handler;
|
||||
handler = new ResourceResponseHandler(myParameters.getClass(), null);
|
||||
|
@ -1103,6 +1104,27 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IBaseParameters> IOperationUntypedWithInput<T> withNoParameters(Class<T> theOutputParameterType) {
|
||||
Validate.notNull(theOutputParameterType, "theOutputParameterType may not be null");
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theOutputParameterType);
|
||||
if (def == null) {
|
||||
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName());
|
||||
}
|
||||
if (!"Parameters".equals(def.getName())) {
|
||||
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() + " is a resource named: " + def.getName());
|
||||
}
|
||||
myParameters = (IBaseParameters) def.newInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IOperationUntypedWithInput useHttpGet() {
|
||||
myUseHttpGet = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<BaseOperationOutcome> {
|
||||
|
|
|
@ -24,6 +24,20 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|||
|
||||
public interface IOperationUntyped {
|
||||
|
||||
/**
|
||||
* Use the given parameters resource as the input to the operation
|
||||
*
|
||||
* @param theParameters The parameters to use as input. May also be <code>null</code> if the operation
|
||||
* does not require any input parameters.
|
||||
*/
|
||||
<T extends IBaseParameters> IOperationUntypedWithInput<T> withParameters(T theParameters);
|
||||
|
||||
/**
|
||||
* The operation does not require any input parameters
|
||||
*
|
||||
* @param theOutputParameterType The type to use for the output parameters (this should be set to
|
||||
* <code>Parameters.class</code> drawn from the version of the FHIR structures you are using)
|
||||
*/
|
||||
<T extends IBaseParameters> IOperationUntypedWithInput<T> withNoParameters(Class<T> theOutputParameterType);
|
||||
|
||||
}
|
||||
|
|
|
@ -24,4 +24,15 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|||
|
||||
public interface IOperationUntypedWithInput<T extends IBaseParameters> extends IClientExecutable<IOperationUntypedWithInput<T>, T> {
|
||||
|
||||
/**
|
||||
* The client should invoke this method using an HTTP GET instead of an HTTP POST. Note that
|
||||
* according the the FHIR specification, all methods must support using the POST method, but
|
||||
* only certain methods may support the HTTP GET method, so it is generally not necessary
|
||||
* to use this feature.
|
||||
* <p>
|
||||
* If you have a specific reason for needing to use a GET however, this method will enable it.
|
||||
* </p>
|
||||
*/
|
||||
IOperationUntypedWithInput<T> useHttpGet();
|
||||
|
||||
}
|
||||
|
|
|
@ -20,15 +20,22 @@ package ca.uhn.fhir.rest.method;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.MethodNotSupportedException;
|
||||
import org.hl7.fhir.instance.model.IBase;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.IPrimitiveType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
|
@ -38,6 +45,7 @@ import ca.uhn.fhir.model.api.Bundle;
|
|||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
|
@ -48,27 +56,33 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
|
|||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
|
||||
private Integer myIdParamIndex;
|
||||
private String myName;
|
||||
private boolean myHttpGetPermitted;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationMethodBinding.class);
|
||||
private final boolean myHttpGetPermitted;
|
||||
private final Integer myIdParamIndex;
|
||||
private final String myName;
|
||||
private final ReturnTypeEnum myReturnType;
|
||||
|
||||
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Operation theAnnotation) {
|
||||
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
|
||||
Operation theAnnotation) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
|
||||
myHttpGetPermitted = theAnnotation.idempotent();
|
||||
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
|
||||
myName = theAnnotation.name();
|
||||
if (isBlank(myName)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getName() + " is annotated with @" + Operation.class.getSimpleName() + " but this annotation has no name defined");
|
||||
|
||||
String name = theAnnotation.name();
|
||||
if (isBlank(name)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getName() + " is annotated with @" + Operation.class.getSimpleName()
|
||||
+ " but this annotation has no name defined");
|
||||
}
|
||||
if (myName.startsWith("$") == false) {
|
||||
myName = "$" + myName;
|
||||
if (name.startsWith("$") == false) {
|
||||
name = "$" + name;
|
||||
}
|
||||
myName = name;
|
||||
|
||||
if (theContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU1)) {
|
||||
throw new ConfigurationException("@" + Operation.class.getSimpleName() + " methods are not supported on servers for FHIR version " + theContext.getVersion().getVersion().name());
|
||||
|
@ -77,7 +91,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
if (theReturnTypeFromRp != null) {
|
||||
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
|
||||
} else {
|
||||
if (Modifier.isAbstract(theAnnotation.type().getModifiers())== false) {
|
||||
if (Modifier.isAbstract(theAnnotation.type().getModifiers()) == false) {
|
||||
setResourceName(theContext.getResourceDefinition(theAnnotation.type()).getName());
|
||||
} else {
|
||||
setResourceName(null);
|
||||
|
@ -85,7 +99,14 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
if (theMethod.getReturnType().isAssignableFrom(Bundle.class)) {
|
||||
throw new ConfigurationException("Can not return a DSTU1 bundle from an @" + Operation.class.getSimpleName() + " method. Found in method " + theMethod.getName() + " defined in type " + theMethod.getDeclaringClass().getName());
|
||||
throw new ConfigurationException("Can not return a DSTU1 bundle from an @" + Operation.class.getSimpleName() + " method. Found in method " + theMethod.getName() + " defined in type "
|
||||
+ theMethod.getDeclaringClass().getName());
|
||||
}
|
||||
|
||||
if (theMethod.getReturnType().equals(IBundleProvider.class)) {
|
||||
myReturnType = ReturnTypeEnum.BUNDLE;
|
||||
} else {
|
||||
myReturnType = ReturnTypeEnum.RESOURCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -102,7 +123,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
@Override
|
||||
public ReturnTypeEnum getReturnType() {
|
||||
return ReturnTypeEnum.RESOURCE;
|
||||
return myReturnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,20 +149,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return myName.equals(theRequest.getOperation());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object parseRequestObject(Request theRequest) throws IOException {
|
||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
BufferedReader requestReader = theRequest.getServletRequest().getReader();
|
||||
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> wantedResourceType = getContext().getResourceDefinition("Parameters").getImplementingClass();
|
||||
return parser.parseResource(wantedResourceType, requestReader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||
String id = null;
|
||||
|
@ -158,7 +165,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
return createOperationInvocation(getContext(), getResourceName(), id, myName, parameters);
|
||||
return createOperationInvocation(getContext(), getResourceName(), id, myName, parameters, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,7 +182,8 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name());
|
||||
throw new MethodNotAllowedException(message, RequestTypeEnum.POST);
|
||||
} else {
|
||||
String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), RequestTypeEnum.POST.name());
|
||||
String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(),
|
||||
RequestTypeEnum.POST.name());
|
||||
throw new MethodNotAllowedException(message, RequestTypeEnum.GET, RequestTypeEnum.POST);
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +197,22 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public static HttpPostClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput) {
|
||||
@Override
|
||||
protected Object parseRequestObject(Request theRequest) throws IOException {
|
||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
BufferedReader requestReader = theRequest.getServletRequest().getReader();
|
||||
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> wantedResourceType = getContext().getResourceDefinition("Parameters").getImplementingClass();
|
||||
return parser.parseResource(wantedResourceType, requestReader);
|
||||
}
|
||||
|
||||
public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput,
|
||||
boolean theUseHttpGet) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
if (theResourceName != null) {
|
||||
b.append(theResourceName);
|
||||
|
@ -206,7 +229,35 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
b.append(theOperationName);
|
||||
|
||||
if (!theUseHttpGet) {
|
||||
return new HttpPostClientInvocation(theContext, theInput, b.toString());
|
||||
} else {
|
||||
FhirTerser t = theContext.newTerser();
|
||||
List<Object> parameters = t.getValues(theInput, "Parameters.parameter");
|
||||
|
||||
Map<String, List<String>> params = new HashMap<String, List<String>>();
|
||||
for (Object nextParameter : parameters) {
|
||||
StringDt nextNameDt = (StringDt) t.getSingleValueOrNull((IBase) nextParameter, "name");
|
||||
if (nextNameDt == null || nextNameDt.isEmpty()) {
|
||||
ourLog.warn("Ignoring input parameter with no value in Parameters.parameter.name in operation client invocation");
|
||||
continue;
|
||||
}
|
||||
String nextName = nextNameDt.getValue();
|
||||
if (!params.containsKey(nextName)) {
|
||||
params.put(nextName, new ArrayList<String>());
|
||||
}
|
||||
|
||||
IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]");
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
if (!(value instanceof IPrimitiveType)) {
|
||||
throw new IllegalArgumentException("Can not invoke operation as HTTP GET when it has parameters with a composite (non priitive) datatype as the value. Found value: " + value.getClass().getName());
|
||||
}
|
||||
IPrimitiveType<?> primitive = (IPrimitiveType<?>) value;
|
||||
params.get(nextName).add(primitive.getValueAsString());
|
||||
}
|
||||
return new HttpGetClientInvocation(params, b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,14 +54,16 @@ public class FifoMemoryPagingProvider implements IPagingProvider {
|
|||
return myBundleProviders.get(theId);
|
||||
}
|
||||
|
||||
public void setDefaultPageSize(int theDefaultPageSize) {
|
||||
public FifoMemoryPagingProvider setDefaultPageSize(int theDefaultPageSize) {
|
||||
Validate.isTrue(theDefaultPageSize > 0, "size must be greater than 0");
|
||||
myDefaultPageSize = theDefaultPageSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setMaximumPageSize(int theMaximumPageSize) {
|
||||
public FifoMemoryPagingProvider setMaximumPageSize(int theMaximumPageSize) {
|
||||
Validate.isTrue(theMaximumPageSize > 0, "size must be greater than 0");
|
||||
myMaximumPageSize = theMaximumPageSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.IBase;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
|
@ -204,7 +205,7 @@ public class FhirTerser {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public List<Object> getValues(IResource theResource, String thePath) {
|
||||
public List<Object> getValues(IBaseResource theResource, String thePath) {
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> currentDef = def;
|
||||
|
@ -323,4 +324,25 @@ public class FhirTerser {
|
|||
visit(theResource, null, def, theVisitor);
|
||||
}
|
||||
|
||||
public Object getSingleValueOrNull(IBase theTarget, String thePath) {
|
||||
Validate.notNull(theTarget, "theTarget must not be null");
|
||||
Validate.notBlank(thePath, "thePath must not be empty");
|
||||
|
||||
BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theTarget.getClass());
|
||||
if (!(def instanceof BaseRuntimeElementCompositeDefinition)) {
|
||||
throw new IllegalArgumentException("Target is not a composite type: " + theTarget.getClass().getName());
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> currentDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||
Object currentObj = theTarget;
|
||||
|
||||
List<String> parts = Arrays.asList(thePath.split("\\."));
|
||||
List<Object> retVal = getValues(currentDef, currentObj, parts);
|
||||
if (retVal.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return retVal.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1246,7 +1246,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
entity.getTags().remove(next);
|
||||
}
|
||||
}
|
||||
//@formatter:off
|
||||
//@formatter:on
|
||||
|
||||
if (entity.getTags().isEmpty()) {
|
||||
entity.setHasTags(false);
|
||||
|
@ -1321,18 +1321,23 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
|
||||
// Any ressources which weren't matched by the sort get added to the bottom
|
||||
for (Long next : originalPids) {
|
||||
if (loadPids.contains(next)==false) {
|
||||
if (loadPids.contains(next) == false) {
|
||||
pids.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
} else {
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
}
|
||||
} else {
|
||||
pids = new ArrayList<Long>(loadPids);
|
||||
}
|
||||
|
||||
// Load _revinclude resources
|
||||
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
|
||||
loadReverseIncludes(pids, theParams.getRevIncludes());
|
||||
}
|
||||
|
||||
IBundleProvider retVal = new IBundleProvider() {
|
||||
|
||||
@Override
|
||||
|
@ -1352,12 +1357,11 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
List<IResource> retVal = new ArrayList<IResource>();
|
||||
loadResourcesByPid(pidsSubList, retVal, BundleEntrySearchModeEnum.MATCH);
|
||||
|
||||
// Load _revinclude resources
|
||||
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty()==false) {
|
||||
loadReverseIncludes(pidsSubList, retVal, theParams.getRevIncludes());
|
||||
}
|
||||
|
||||
// Load _include resources
|
||||
/*
|
||||
* Load _include resources - Note that _revincludes are handled differently
|
||||
* than _include ones, as they are counted towards the total count and paged,
|
||||
* so they are loaded outside the bundle provider
|
||||
*/
|
||||
if (theParams.getIncludes() != null && theParams.getIncludes().isEmpty() == false) {
|
||||
Set<IdDt> previouslyLoadedPids = new HashSet<IdDt>();
|
||||
|
||||
|
@ -1401,7 +1405,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
|
||||
if (previouslyLoadedPids.size() >= getConfig().getIncludeLimit()) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Not all _include resources were actually included as the request surpassed the limit of " + getConfig().getIncludeLimit() + " resources");
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING)
|
||||
.setDetails("Not all _include resources were actually included as the request surpassed the limit of " + getConfig().getIncludeLimit() + " resources");
|
||||
retVal.add(0, oo);
|
||||
}
|
||||
}
|
||||
|
@ -1435,14 +1440,24 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
return resources;
|
||||
}
|
||||
|
||||
protected void loadReverseIncludes(List<Long> theMatches, List<IResource> theResourceListToPopulate, Set<Include> theRevIncludes) {
|
||||
protected void loadReverseIncludes(List<Long> theMatches, Set<Include> theRevIncludes) {
|
||||
if (theMatches.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Long> pidsToInclude = new HashSet<Long>();
|
||||
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
||||
|
||||
for (Include nextInclude : theRevIncludes) {
|
||||
boolean matchAll = "*".equals(nextInclude.getValue());
|
||||
if (matchAll) {
|
||||
String sql = "SELECT r FROM ResourceLink r WHERE r.myTargetResourcePid IN (:target_pids)";
|
||||
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
||||
q.setParameter("target_pids", theMatches);
|
||||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
}
|
||||
} else {
|
||||
int colonIdx = nextInclude.getValue().indexOf(':');
|
||||
if (colonIdx < 2) {
|
||||
continue;
|
||||
|
@ -1454,7 +1469,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
continue;
|
||||
}
|
||||
|
||||
String paramName = nextInclude.getValue().substring(colonIdx+1);
|
||||
String paramName = nextInclude.getValue().substring(colonIdx + 1);
|
||||
RuntimeSearchParam param = def.getSearchParam(paramName);
|
||||
if (param == null) {
|
||||
ourLog.warn("Unknown param name in _revinclude=" + nextInclude.getValue());
|
||||
|
@ -1471,21 +1486,19 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
}
|
||||
}
|
||||
|
||||
loadResourcesByPid(pidsToInclude, theResourceListToPopulate, BundleEntrySearchModeEnum.INCLUDE);
|
||||
}
|
||||
}
|
||||
|
||||
theMatches.addAll(pidsToInclude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider search(String theParameterName, IQueryParameterType theValue) {
|
||||
return search(Collections.singletonMap(theParameterName, theValue));
|
||||
}
|
||||
|
||||
|
||||
protected abstract List<Object> getIncludeValues(FhirTerser theTerser, Include theInclude, IResource theResource, RuntimeResourceDefinition theResourceDef);
|
||||
|
||||
|
||||
@Override
|
||||
public Set<Long> searchForIds(Map<String, IQueryParameterType> theParams) {
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
|
@ -1632,8 +1645,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
}
|
||||
|
||||
/**
|
||||
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to
|
||||
* share the same value.
|
||||
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to share the same value.
|
||||
*/
|
||||
public void setSecondaryPrimaryKeyParamName(String theSecondaryPrimaryKeyParamName) {
|
||||
mySecondaryPrimaryKeyParamName = theSecondaryPrimaryKeyParamName;
|
||||
|
@ -1760,7 +1772,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
|
||||
private void validateResourceType(BaseHasResource entity) {
|
||||
if (!myResourceName.equals(entity.getResourceType())) {
|
||||
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type " + entity.getResourceType());
|
||||
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type "
|
||||
+ entity.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1818,7 +1831,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
}
|
||||
}
|
||||
}
|
||||
//@formatter:off
|
||||
//@formatter:on
|
||||
|
||||
if (entity.getTags().isEmpty()) {
|
||||
entity.setHasTags(false);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Encounter;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDstu2<Encounter> {
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu2<Patient> {
|
||||
|
||||
@Operation(name="$everything", idempotent=true)
|
||||
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdDt theId) {
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
paramMap.setRevIncludes(Collections.singleton(new Include("*")));
|
||||
paramMap.setIncludes(Collections.singleton(new Include("*")));
|
||||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
||||
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap);
|
||||
return retVal;
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -124,7 +124,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
@OperationParam(name="return", type=MetaDt.class)
|
||||
})
|
||||
//@formatter:on
|
||||
public Parameters metaAdd(@IdParam IdDt theId, @OperationParam(name="meta") MetaDt theMeta) {
|
||||
public Parameters metaAdd(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta) {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(getDao().metaAddOperation(theId, theMeta));
|
||||
return parameters;
|
||||
|
@ -135,7 +135,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
@OperationParam(name="return", type=MetaDt.class)
|
||||
})
|
||||
//@formatter:on
|
||||
public Parameters metaDelete(@IdParam IdDt theId, @OperationParam(name="meta") MetaDt theMeta) {
|
||||
public Parameters metaDelete(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta) {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta));
|
||||
return parameters;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
@ -10,7 +11,9 @@ import static org.junit.Assert.*;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -37,18 +40,23 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.dstu.resource.Device;
|
||||
import ca.uhn.fhir.model.dstu.resource.Practitioner;
|
||||
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
|
||||
import ca.uhn.fhir.model.dstu2.resource.DocumentManifest;
|
||||
import ca.uhn.fhir.model.dstu2.resource.DocumentReference;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Encounter;
|
||||
import ca.uhn.fhir.model.dstu2.resource.ImagingStudy;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Location;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
|
||||
|
@ -298,6 +306,53 @@ public class ResourceProviderDstu2Test {
|
|||
assertEquals(BundleEntrySearchModeEnum.INCLUDE, found.getEntries().get(1).getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingOperation() throws Exception {
|
||||
String methodName = "testEverythingOperation";
|
||||
|
||||
Organization org1 = new Organization();
|
||||
org1.setName(methodName + "1");
|
||||
IdDt orgId1 = ourClient.create().resource(org1).execute().getId();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily(methodName);
|
||||
p.getManagingOrganization().setReference(orgId1);
|
||||
IdDt patientId = ourClient.create().resource(p).execute().getId();
|
||||
|
||||
Organization org2 = new Organization();
|
||||
org2.setName(methodName + "1");
|
||||
IdDt orgId2 = ourClient.create().resource(org2).execute().getId();
|
||||
|
||||
Device dev = new Device();
|
||||
dev.setModel(methodName);
|
||||
dev.getOwner().setReference(orgId2);
|
||||
IdDt devId = ourClient.create().resource(dev).execute().getId();
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId);
|
||||
obs.getDevice().setReference(devId);
|
||||
IdDt obsId = ourClient.create().resource(obs).execute().getId();
|
||||
|
||||
Encounter enc = new Encounter();
|
||||
enc.getPatient().setReference(patientId);
|
||||
IdDt encId = ourClient.create().resource(enc).execute().getId();
|
||||
|
||||
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withNoParameters(Parameters.class).execute();
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
|
||||
|
||||
Set<IdDt> ids = new HashSet<IdDt>();
|
||||
for (Entry next : b.getEntry()) {
|
||||
ids.add(next.getResource().getId());
|
||||
}
|
||||
|
||||
ourLog.info(ids.toString());
|
||||
|
||||
assertThat(ids, containsInAnyOrder(patientId, devId, obsId, encId, orgId1, orgId2));
|
||||
|
||||
// _revinclude's are counted but not _include's
|
||||
assertEquals(3, b.getTotal().intValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCountParam() throws Exception {
|
||||
|
@ -680,7 +735,7 @@ public class ResourceProviderDstu2Test {
|
|||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
ourFhirCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
||||
ourFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
|
||||
ourClient = ourFhirCtx.newRestfulGenericClient(ourServerBase);
|
||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ public class IdDtTest {
|
|||
ResourceReferenceDt ref = actual.getManagingOrganization();
|
||||
assertEquals("Organization", ref.getReference().getResourceType());
|
||||
assertEquals("123", ref.getReference().getIdPart());
|
||||
assertEquals("999", ref.getReference().getVersionIdPart());
|
||||
assertEquals(null, ref.getReference().getVersionIdPart());
|
||||
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ public class IdDtTest {
|
|||
ResourceReferenceDt ref = actual.getManagingOrganization();
|
||||
assertEquals(null, ref.getReference().getResourceType());
|
||||
assertEquals("123", ref.getReference().getIdPart());
|
||||
assertEquals("999", ref.getReference().getVersionIdPart());
|
||||
assertEquals(null, ref.getReference().getVersionIdPart());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ public class ReferenceParameterTest {
|
|||
assertThat(status.getFirstHeader("Content-Location").getValue(), containsString("Patient/22/_history/33"));
|
||||
|
||||
assertEquals("44", p.getManagingOrganization().getReference().getIdPart());
|
||||
assertEquals("55", p.getManagingOrganization().getReference().getVersionIdPart());
|
||||
assertEquals(null, p.getManagingOrganization().getReference().getVersionIdPart());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -119,7 +119,7 @@ public class ReferenceParameterTest {
|
|||
assertEquals("33", p.getId().getVersionIdPart());
|
||||
|
||||
assertEquals("44", p.getManagingOrganization().getReference().getIdPart());
|
||||
assertEquals("55", p.getManagingOrganization().getReference().getVersionIdPart());
|
||||
assertEquals(null, p.getManagingOrganization().getReference().getVersionIdPart());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -36,6 +36,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationDefinition;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceInteraction;
|
||||
|
@ -50,6 +51,7 @@ import ca.uhn.fhir.rest.annotation.Metadata;
|
|||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.IParameter;
|
||||
import ca.uhn.fhir.rest.method.OperationMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
@ -166,6 +168,10 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
handleSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
|
||||
} else if (nextMethodBinding instanceof DynamicSearchMethodBinding) {
|
||||
handleDynamicSearchMethodBinding(resource, def, includes, (DynamicSearchMethodBinding) nextMethodBinding);
|
||||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding)nextMethodBinding;
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
// op.
|
||||
}
|
||||
|
||||
Collections.sort(resource.getInteraction(), new Comparator<RestResourceInteraction>() {
|
||||
|
|
|
@ -269,6 +269,253 @@ public class GenericClientTestDstu2 {
|
|||
idx++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationWithNoInParameters() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
Parameters inParams = new Parameters();
|
||||
final String reqString = p.encodeResourceToString(inParams);
|
||||
|
||||
Parameters outParams = new Parameters();
|
||||
outParams.addParameter().setValue(new StringDt("STRINGVALOUT1"));
|
||||
outParams.addParameter().setValue(new StringDt("STRINGVALOUT2"));
|
||||
final String respString = p.encodeResourceToString(outParams);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
Parameters resp = client
|
||||
.operation()
|
||||
.onServer()
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class).execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertEquals(extractBody(capt, idx), reqString);
|
||||
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onType(Patient.class)
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class).execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertEquals(extractBody(capt, idx), reqString);
|
||||
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("Patient", "123"))
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class).execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertEquals(extractBody(capt, idx), reqString);
|
||||
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
// @formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22"))
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class)
|
||||
.execute();
|
||||
// @formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
idx++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationAsGetWithNoInParameters() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
Parameters outParams = new Parameters();
|
||||
outParams.addParameter().setValue(new StringDt("STRINGVALOUT1"));
|
||||
outParams.addParameter().setValue(new StringDt("STRINGVALOUT2"));
|
||||
final String respString = p.encodeResourceToString(outParams);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
Parameters resp = client
|
||||
.operation()
|
||||
.onServer()
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals("GET", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onType(Patient.class)
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals("GET", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("Patient", "123"))
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals("GET", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
// @formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22"))
|
||||
.named("$SOMEOPERATION")
|
||||
.withNoParameters(Parameters.class)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
// @formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
idx++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationAsGetWithInParameters() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
Parameters inParams = new Parameters();
|
||||
inParams.addParameter().setName("param1").setValue(new StringDt("STRINGVALIN1"));
|
||||
inParams.addParameter().setName("param1").setValue(new StringDt("STRINGVALIN1b"));
|
||||
inParams.addParameter().setName("param2").setValue(new StringDt("STRINGVALIN2"));
|
||||
|
||||
Parameters outParams = new Parameters();
|
||||
outParams.addParameter().setValue(new StringDt("STRINGVALOUT1"));
|
||||
outParams.addParameter().setValue(new StringDt("STRINGVALOUT2"));
|
||||
final String respString = p.encodeResourceToString(outParams);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
Parameters resp = client
|
||||
.operation()
|
||||
.onServer()
|
||||
.named("$SOMEOPERATION")
|
||||
.withParameters(inParams)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals("GET", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onType(Patient.class)
|
||||
.named("$SOMEOPERATION")
|
||||
.withParameters(inParams)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals("GET", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("Patient", "123"))
|
||||
.named("$SOMEOPERATION")
|
||||
.withParameters(inParams)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals("GET", capt.getAllValues().get(idx).getRequestLine().getMethod());
|
||||
idx++;
|
||||
|
||||
// @formatter:off
|
||||
resp = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22"))
|
||||
.named("$SOMEOPERATION")
|
||||
.withParameters(inParams)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
// @formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
idx++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationWithBundleResponse() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.rest.server;
|
|||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -162,6 +163,19 @@ public class OperationServerTest {
|
|||
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||
}
|
||||
|
||||
@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 testOperationWithListParam() throws Exception {
|
||||
Parameters p = new Parameters();
|
||||
|
@ -317,6 +331,9 @@ public class OperationServerTest {
|
|||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer();
|
||||
|
||||
servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2));
|
||||
|
||||
servlet.setFhirContext(ourCtx);
|
||||
servlet.setResourceProviders(new PatientProvider());
|
||||
servlet.setPlainProviders(new PlainProvider());
|
||||
|
@ -334,6 +351,22 @@ public class OperationServerTest {
|
|||
|
||||
public static class PlainProvider {
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$OP_INSTANCE_BUNDLE_PROVIDER", idempotent=true)
|
||||
public IBundleProvider opInstanceReturnsBundleProvider() {
|
||||
ourLastMethod = "$OP_INSTANCE_BUNDLE_PROVIDER";
|
||||
|
||||
List<IResource> resources = new ArrayList<IResource>();
|
||||
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(
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.Set;
|
|||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -26,7 +27,9 @@ import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource;
|
|||
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
|
@ -37,9 +40,9 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
|
|||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
|
||||
|
||||
public class ServerConformanceProviderTest {
|
||||
public class ServerConformanceProviderDstu2Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerConformanceProviderTest.class);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerConformanceProviderDstu2Test.class);
|
||||
private FhirContext myCtx = new FhirContext();
|
||||
|
||||
@Test
|
||||
|
@ -75,6 +78,58 @@ public class ServerConformanceProviderTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingOperationDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer();
|
||||
rs.setProviders(new ProviderWithOperations());
|
||||
|
||||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationDocumentation() throws Exception {
|
||||
|
||||
RestfulServer rs = new RestfulServer();
|
||||
rs.setProviders(new SearchProvider());
|
||||
|
||||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
boolean found=false;
|
||||
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
||||
for (ResourceBinding resourceBinding : resourceBindings) {
|
||||
if (resourceBinding.getResourceName().equals("Patient")) {
|
||||
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
|
||||
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
|
||||
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
|
||||
assertEquals("The patient's identifier (MRN or other card number)", param.getDescription());
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
|
||||
assertThat(conf, containsString("<type value=\"token\"/>"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateGeneratedStatement() throws Exception {
|
||||
|
@ -203,9 +258,23 @@ public class ServerConformanceProviderTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class ProviderWithOperations implements IResourceProvider {
|
||||
|
||||
@Operation(name="everything", idempotent=true)
|
||||
public ca.uhn.fhir.rest.server.IBundleProvider everything(
|
||||
javax.servlet.http.HttpServletRequest theServletRequest,
|
||||
@IdParam ca.uhn.fhir.model.primitive.IdDt theId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private HttpServletRequest createHttpServletRequest() {
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
|
@ -112,4 +112,28 @@ public class ${className}ResourceProvider extends JpaResourceProvider${versionCa
|
|||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if ( $version != 'dstu' && (${className} == 'Patient' || ${className} == 'Encounter') )
|
||||
@Operation(name="everything", idempotent=true)
|
||||
public ca.uhn.fhir.rest.server.IBundleProvider everything(
|
||||
javax.servlet.http.HttpServletRequest theServletRequest,
|
||||
@IdParam ca.uhn.fhir.model.primitive.IdDt theId) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
paramMap.setRevIncludes(Collections.singleton(new Include("*")));
|
||||
paramMap.setIncludes(Collections.singleton(new Include("*")));
|
||||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
||||
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap);
|
||||
return retVal;
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
#end
|
||||
|
||||
}
|
||||
|
|
|
@ -408,9 +408,34 @@
|
|||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="operation" />
|
||||
<param name="file"
|
||||
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||
<param name="file" value="examples/src/main/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
Note that if the operation does not require any input parameters,
|
||||
you may also invoke the operation using the following form. Note that
|
||||
the <code>withNoParameters</code> still requires you to provide the
|
||||
type of the Parameters resource so that it can return the correct type in
|
||||
the response.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="operationNoIn" />
|
||||
<param name="file" value="examples/src/main/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
|
||||
<subsection name="Using the HTTP GET Form">
|
||||
<p>
|
||||
By default, the client will invoke operations using the HTTP POST form.
|
||||
The FHIR specification also allows requests to use the HTTP GET verb
|
||||
if the operation is idempotent and has no composite/resource parameters.
|
||||
Use the following form to invoke operation with HTTP GET.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="operationHttpGet" />
|
||||
<param name="file" value="examples/src/main/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue