Operations in server generated conformance statement should only appear once per name, since the name needs to be unique.
This commit is contained in:
parent
a774a654ce
commit
99e92d8fca
|
@ -58,6 +58,8 @@ import ca.uhn.fhir.util.FhirTerser;
|
|||
public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationMethodBinding.class);
|
||||
private boolean myCanOperateAtInstanceLevel;
|
||||
private boolean myCanOperateAtServerLevel;
|
||||
private String myDescription;
|
||||
private final boolean myIdempotent;
|
||||
private final Integer myIdParamIndex;
|
||||
|
@ -66,7 +68,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private List<ReturnType> myReturnParams;
|
||||
private final ReturnTypeEnum myReturnType;
|
||||
|
||||
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class<? extends IBaseResource> theOperationType,
|
||||
protected OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class<? extends IBaseResource> theOperationType,
|
||||
OperationParam[] theReturnParams) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
|
||||
|
@ -139,6 +141,14 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
myReturnParams.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (myIdParamIndex != null) {
|
||||
myCanOperateAtInstanceLevel = true;
|
||||
}
|
||||
if (getResourceName() == null) {
|
||||
myCanOperateAtServerLevel = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Operation theAnnotation) {
|
||||
|
@ -250,12 +260,20 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public boolean isCanOperateAtInstanceLevel() {
|
||||
return this.myCanOperateAtInstanceLevel;
|
||||
}
|
||||
|
||||
public boolean isCanOperateAtServerLevel() {
|
||||
return this.myCanOperateAtServerLevel;
|
||||
}
|
||||
|
||||
public boolean isIdempotent() {
|
||||
return myIdempotent;
|
||||
}
|
||||
|
||||
public boolean isInstanceLevel() {
|
||||
return myIdParamIndex != null;
|
||||
public void setDescription(String theDescription) {
|
||||
myDescription = theDescription;
|
||||
}
|
||||
|
||||
public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) {
|
||||
|
@ -309,9 +327,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
public static class ReturnType {
|
||||
private int myMax;
|
||||
|
||||
private int myMin;
|
||||
|
||||
private String myName;
|
||||
/**
|
||||
* http://hl7-fhir.github.io/valueset-operation-parameter-type.html
|
||||
|
|
|
@ -42,6 +42,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.param.CollectionBinder;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -102,12 +103,21 @@ public class OperationParameter implements IParameter {
|
|||
myMax = 1;
|
||||
}
|
||||
|
||||
if (!myParameterType.equals(IBase.class)) {
|
||||
if (!IBase.class.isAssignableFrom(myParameterType) || myParameterType.isInterface() || Modifier.isAbstract(myParameterType.getModifiers())) {
|
||||
/*
|
||||
* The parameter can be of type string for validation methods - This is a bit
|
||||
* weird. See ValidateDstu2Test. We should probably clean this up..
|
||||
*/
|
||||
if (!myParameterType.equals(IBase.class) && !myParameterType.equals(String.class)) {
|
||||
if (IBaseResource.class.isAssignableFrom(myParameterType) && myParameterType.isInterface()) {
|
||||
myParamType = "Resource";
|
||||
} else if (!IBase.class.isAssignableFrom(myParameterType) || myParameterType.isInterface() || Modifier.isAbstract(myParameterType.getModifiers())) {
|
||||
throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName());
|
||||
}
|
||||
} else if (myParameterType.equals(ValidationModeEnum.class)) {
|
||||
myParamType = "code";
|
||||
} else {
|
||||
myParamType = myContext.getElementDefinition((Class<? extends IBase>) myParameterType).getName();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.server.provider.dstu2;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -88,15 +90,56 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
|
||||
private boolean myCache = true;
|
||||
private volatile Conformance myConformance;
|
||||
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
|
||||
private String myPublisher = "Not provided";
|
||||
private final RestfulServer myRestfulServer;
|
||||
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||
private HashMap<String, OperationMethodBinding> myOperationNameToBinding;
|
||||
|
||||
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
||||
myRestfulServer = theRestfulServer;
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getSystemOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getSystemOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteractionEnum sysOp = SystemRestfulInteractionEnum.VALUESET_BINDER.fromCodeString(sysOpCode);
|
||||
if (sysOp == null) {
|
||||
throw new InternalErrorException("Unknown system-restful-interaction: " + sysOpCode);
|
||||
}
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
rest.addInteraction().setCode(sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
|
||||
for (ResourceBinding next : myRestfulServer.getResourceBindings()) {
|
||||
String resourceName = next.getResourceName();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
}
|
||||
for (BaseMethodBinding<?> nextMethodBinding : myRestfulServer.getServerBindings()) {
|
||||
String resourceName = "";
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
return resourceToMethods;
|
||||
}
|
||||
|
||||
private String createOperationName(OperationMethodBinding theMethodBinding) {
|
||||
return theMethodBinding.getName().substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a
|
||||
* mandatory element, the value should not be null (although this is not enforced). The value defaults to
|
||||
|
@ -106,85 +149,6 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
return myPublisher;
|
||||
}
|
||||
|
||||
@Initialize
|
||||
public void initializeOperations() {
|
||||
Set<String> allNames = new HashSet<String>();
|
||||
myOperationBindingToName = new IdentityHashMap<OperationMethodBinding, String>();
|
||||
myOperationNameToBinding = new HashMap<String, OperationMethodBinding>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
List<BaseMethodBinding<?>> nextMethodBindings = nextEntry.getValue();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextMethodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String nextName = methodBinding.getName().substring(1);
|
||||
int count = 1;
|
||||
while (allNames.add(createOperationName(nextName, count)) == false) {
|
||||
count++;
|
||||
}
|
||||
String name = createOperationName(nextName, count);
|
||||
myOperationBindingToName.put(methodBinding, name);
|
||||
myOperationNameToBinding.put(name, methodBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String createOperationName(String theName, int theCount) {
|
||||
if (theCount < 2) {
|
||||
return theName;
|
||||
}
|
||||
return theName + '-' + theCount;
|
||||
}
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdDt theId) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
OperationMethodBinding methodBinding = myOperationNameToBinding.get(theId.getIdPart());
|
||||
if (methodBinding == null) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(ConformanceResourceStatusEnum.ACTIVE);
|
||||
op.setDescription(methodBinding.getDescription());
|
||||
op.setIdempotent(methodBinding.isIdempotent());
|
||||
op.setCode(methodBinding.getName());
|
||||
op.setInstance(methodBinding.isInstanceLevel());
|
||||
op.addType().setValue(methodBinding.getResourceName());
|
||||
|
||||
for (IParameter nextParamUntyped : methodBinding.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
Parameter param = op.addParameter();
|
||||
param.setUse(OperationParameterUseEnum.IN);
|
||||
if (nextParam.getParamType() != null) {
|
||||
param.setType(nextParam.getParamType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : methodBinding.getReturnParams()) {
|
||||
Parameter param = op.addParameter();
|
||||
param.setUse(OperationParameterUseEnum.OUT);
|
||||
if (nextParam.getType() != null) {
|
||||
param.setType(nextParam.getType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Metadata
|
||||
public Conformance getServerConformance(HttpServletRequest theRequest) {
|
||||
|
@ -210,6 +174,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
rest.setMode(RestfulConformanceModeEnum.SERVER);
|
||||
|
||||
Set<SystemRestfulInteractionEnum> systemOps = new HashSet<SystemRestfulInteractionEnum>();
|
||||
Set<String> operationNames = new HashSet<String>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
|
@ -274,8 +239,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = myOperationBindingToName.get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
// Only add each operation (by name) once
|
||||
rest.addOperation().setName(methodBinding.getName()).getDefinition().setReference("OperationDefinition/" + opName);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(resource.getInteraction(), new Comparator<RestResourceInteraction>() {
|
||||
@Override
|
||||
|
@ -306,53 +274,18 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = myOperationBindingToName.get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
rest.addOperation().setName(methodBinding.getName()).getDefinition().setReference("OperationDefinition/" + opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myConformance = retVal;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
|
||||
for (ResourceBinding next : myRestfulServer.getResourceBindings()) {
|
||||
String resourceName = next.getResourceName();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
}
|
||||
for (BaseMethodBinding<?> nextMethodBinding : myRestfulServer.getServerBindings()) {
|
||||
String resourceName = "";
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
return resourceToMethods;
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getSystemOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getSystemOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteractionEnum sysOp = SystemRestfulInteractionEnum.VALUESET_BINDER.fromCodeString(sysOpCode);
|
||||
if (sysOp == null) {
|
||||
throw new InternalErrorException("Unknown system-restful-interaction: " + sysOpCode);
|
||||
}
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
rest.addInteraction().setCode(sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
|
@ -469,6 +402,102 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
}
|
||||
|
||||
@Initialize
|
||||
public void initializeOperations() {
|
||||
myOperationBindingToName = new IdentityHashMap<OperationMethodBinding, String>();
|
||||
myOperationNameToBindings = new HashMap<String, List<OperationMethodBinding>>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
List<BaseMethodBinding<?>> nextMethodBindings = nextEntry.getValue();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextMethodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
if (myOperationBindingToName.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = createOperationName(methodBinding);
|
||||
myOperationBindingToName.put(methodBinding, name);
|
||||
if (myOperationNameToBindings.containsKey(name) == false) {
|
||||
myOperationNameToBindings.put(name, new ArrayList<OperationMethodBinding>());
|
||||
}
|
||||
myOperationNameToBindings.get(name).add(methodBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdDt theId) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
List<OperationMethodBinding> sharedDescriptions = myOperationNameToBindings.get(theId.getIdPart());
|
||||
if (sharedDescriptions == null || sharedDescriptions.isEmpty()) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(ConformanceResourceStatusEnum.ACTIVE);
|
||||
op.setIdempotent(true);
|
||||
|
||||
Set<String> inParams = new HashSet<String>();
|
||||
Set<String> outParams = new HashSet<String>();
|
||||
|
||||
for (OperationMethodBinding sharedDescription : sharedDescriptions) {
|
||||
if (isNotBlank(sharedDescription.getDescription())) {
|
||||
op.setDescription(sharedDescription.getDescription());
|
||||
}
|
||||
if (!sharedDescription.isIdempotent()) {
|
||||
op.setIdempotent(sharedDescription.isIdempotent());
|
||||
}
|
||||
op.setCode(sharedDescription.getName());
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
op.setInstance(sharedDescription.isCanOperateAtInstanceLevel());
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
op.setSystem(sharedDescription.isCanOperateAtServerLevel());
|
||||
}
|
||||
if (isNotBlank(sharedDescription.getResourceName())) {
|
||||
op.addType().setValue(sharedDescription.getResourceName());
|
||||
}
|
||||
|
||||
for (IParameter nextParamUntyped : sharedDescription.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
Parameter param = op.addParameter();
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
param.setUse(OperationParameterUseEnum.IN);
|
||||
if (nextParam.getParamType() != null) {
|
||||
param.setType(nextParam.getParamType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : sharedDescription.getReturnParams()) {
|
||||
if (!outParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
Parameter param = op.addParameter();
|
||||
param.setUse(OperationParameterUseEnum.OUT);
|
||||
if (nextParam.getType() != null) {
|
||||
param.setType(nextParam.getType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
|
||||
* <p>
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
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.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationDefinition;
|
||||
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.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
public class OperationDuplicateServerDstu2Test {
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationDuplicateServerDstu2Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
@Test
|
||||
public void testOperationsAreCollapsed() throws Exception {
|
||||
// Metadata
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String response = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
|
||||
Conformance resp = ourCtx.newXmlParser().parseResource(Conformance.class, response);
|
||||
assertEquals(1, resp.getRest().get(0).getOperation().size());
|
||||
assertEquals("$myoperation", resp.getRest().get(0).getOperation().get(0).getName());
|
||||
assertEquals("OperationDefinition/myoperation", resp.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
|
||||
}
|
||||
|
||||
// OperationDefinition
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/myoperation?_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String response = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
|
||||
OperationDefinition resp = ourCtx.newXmlParser().parseResource(OperationDefinition.class, response);
|
||||
assertEquals(true, resp.getSystemElement().getValue().booleanValue());
|
||||
assertEquals("$myoperation", resp.getCode());
|
||||
assertEquals(true, resp.getIdempotent().booleanValue());
|
||||
assertEquals(2, resp.getType().size());
|
||||
assertThat(Arrays.asList(resp.getType().get(0).getValue(), resp.getType().get(1).getValue()), containsInAnyOrder("Organization", "Patient"));
|
||||
assertEquals(1, resp.getParameter().size());
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
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(), new OrganizationProvider());
|
||||
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 class BaseProvider {
|
||||
|
||||
@Operation(name = "$myoperation", idempotent = true)
|
||||
public Parameters opInstanceReturnsBundleProvider(@OperationParam(name = "myparam") StringDt theString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class OrganizationProvider extends BaseProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Organization.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PatientProvider extends BaseProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PlainProvider {
|
||||
|
||||
@Operation(name = "$myoperation", idempotent = true)
|
||||
public Parameters opInstanceReturnsBundleProvider(@OperationParam(name = "myparam") StringDt theString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,8 @@ package org.hl7.fhir.instance.conf;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -46,6 +48,7 @@ import org.hl7.fhir.instance.model.Conformance.RestfulConformanceMode;
|
|||
import org.hl7.fhir.instance.model.Conformance.SystemRestfulInteraction;
|
||||
import org.hl7.fhir.instance.model.Conformance.TypeRestfulInteraction;
|
||||
import org.hl7.fhir.instance.model.Enumerations.ConformanceResourceStatus;
|
||||
import org.hl7.fhir.instance.model.Enumerations.ResourceType;
|
||||
import org.hl7.fhir.instance.model.OperationDefinition;
|
||||
import org.hl7.fhir.instance.model.OperationDefinition.OperationDefinitionParameterComponent;
|
||||
import org.hl7.fhir.instance.model.OperationDefinition.OperationParameterUse;
|
||||
|
@ -57,7 +60,6 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Initialize;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
|
||||
|
@ -85,17 +87,64 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|||
* </p>
|
||||
*/
|
||||
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||
|
||||
private boolean myCache = true;
|
||||
private volatile Conformance myConformance;
|
||||
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
|
||||
private String myPublisher = "Not provided";
|
||||
private final RestfulServer myRestfulServer;
|
||||
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
|
||||
private HashMap<String, OperationMethodBinding> myOperationNameToBinding;
|
||||
|
||||
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
||||
myRestfulServer = theRestfulServer;
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getSystemOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getSystemOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteraction sysOp;
|
||||
try {
|
||||
sysOp = SystemRestfulInteraction.fromCode(sysOpCode);
|
||||
} catch (Exception e) {
|
||||
sysOp = null;
|
||||
}
|
||||
if (sysOp == null) {
|
||||
throw new InternalErrorException("Unknown system-restful-interaction: " + sysOpCode);
|
||||
}
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
rest.addInteraction().setCode(sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
|
||||
for (ResourceBinding next : myRestfulServer.getResourceBindings()) {
|
||||
String resourceName = next.getResourceName();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
}
|
||||
for (BaseMethodBinding<?> nextMethodBinding : myRestfulServer.getServerBindings()) {
|
||||
String resourceName = "";
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
return resourceToMethods;
|
||||
}
|
||||
|
||||
private String createOperationName(OperationMethodBinding theMethodBinding) {
|
||||
return theMethodBinding.getName().substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a
|
||||
* mandatory element, the value should not be null (although this is not enforced). The value defaults to
|
||||
|
@ -105,84 +154,6 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
return myPublisher;
|
||||
}
|
||||
|
||||
@Initialize
|
||||
public void initializeOperations() {
|
||||
Set<String> allNames = new HashSet<String>();
|
||||
myOperationBindingToName = new IdentityHashMap<OperationMethodBinding, String>();
|
||||
myOperationNameToBinding = new HashMap<String, OperationMethodBinding>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
List<BaseMethodBinding<?>> nextMethodBindings = nextEntry.getValue();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextMethodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String nextName = methodBinding.getName().substring(1);
|
||||
int count = 1;
|
||||
while (allNames.add(createOperationName(nextName, count)) == false) {
|
||||
count++;
|
||||
}
|
||||
String name = createOperationName(nextName, count);
|
||||
myOperationBindingToName.put(methodBinding, name);
|
||||
myOperationNameToBinding.put(name, methodBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String createOperationName(String theName, int theCount) {
|
||||
if (theCount < 2) {
|
||||
return theName;
|
||||
}
|
||||
return theName + '-' + theCount;
|
||||
}
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdDt theId) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
OperationMethodBinding methodBinding = myOperationNameToBinding.get(theId.getIdPart());
|
||||
if (methodBinding == null) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(ConformanceResourceStatus.ACTIVE);
|
||||
op.setDescription(methodBinding.getDescription());
|
||||
op.setIdempotent(methodBinding.isIdempotent());
|
||||
op.setCode(methodBinding.getName());
|
||||
op.setInstance(methodBinding.isInstanceLevel());
|
||||
op.addType(methodBinding.getResourceName());
|
||||
|
||||
for (IParameter nextParamUntyped : methodBinding.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(OperationParameterUse.IN);
|
||||
if (nextParam.getParamType() != null) {
|
||||
param.setType(nextParam.getParamType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : methodBinding.getReturnParams()) {
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(OperationParameterUse.OUT);
|
||||
if (nextParam.getType() != null) {
|
||||
param.setType(nextParam.getType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Metadata
|
||||
public Conformance getServerConformance(HttpServletRequest theRequest) {
|
||||
|
@ -208,6 +179,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
rest.setMode(RestfulConformanceMode.SERVER);
|
||||
|
||||
Set<SystemRestfulInteraction> systemOps = new HashSet<SystemRestfulInteraction>();
|
||||
Set<String> operationNames = new HashSet<String>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
|
@ -277,14 +249,17 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
} else if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = myOperationBindingToName.get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
// Only add each operation (by name) once
|
||||
rest.addOperation().setName(methodBinding.getName()).getDefinition().setReference("OperationDefinition/" + opName);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(resource.getInteraction(), new Comparator<ResourceInteractionComponent>() {
|
||||
@Override
|
||||
public int compare(ResourceInteractionComponent theO1, ResourceInteractionComponent theO2) {
|
||||
TypeRestfulInteraction o1 = theO1.getCodeElement().getValue();
|
||||
TypeRestfulInteraction o2 = theO2.getCodeElement().getValue();
|
||||
TypeRestfulInteraction o1 = theO1.getCode();
|
||||
TypeRestfulInteraction o2 = theO2.getCode();
|
||||
if (o1 == null && o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -309,58 +284,18 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
String opName = myOperationBindingToName.get(methodBinding);
|
||||
if (operationNames.add(opName)) {
|
||||
rest.addOperation().setName(methodBinding.getName()).getDefinition().setReference("OperationDefinition/" + opName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myConformance = retVal;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
|
||||
for (ResourceBinding next : myRestfulServer.getResourceBindings()) {
|
||||
String resourceName = next.getResourceName();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
}
|
||||
for (BaseMethodBinding<?> nextMethodBinding : myRestfulServer.getServerBindings()) {
|
||||
String resourceName = "";
|
||||
if (resourceToMethods.containsKey(resourceName) == false) {
|
||||
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
|
||||
}
|
||||
resourceToMethods.get(resourceName).add(nextMethodBinding);
|
||||
}
|
||||
return resourceToMethods;
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getSystemOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getSystemOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteraction sysOp;
|
||||
try {
|
||||
sysOp = SystemRestfulInteraction.fromCode(sysOpCode);
|
||||
} catch (Exception e) {
|
||||
sysOp=null;
|
||||
}
|
||||
if (sysOp == null) {
|
||||
throw new InternalErrorException("Unknown system-restful-interaction: " + sysOpCode);
|
||||
}
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
rest.addInteraction().setCode(sysOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDynamicSearchMethodBinding(ConformanceRestResourceComponent resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
|
@ -393,8 +328,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
}
|
||||
|
||||
ConformanceRestResourceSearchParamComponent param;
|
||||
param = resource.addSearchParam();
|
||||
ConformanceRestResourceSearchParamComponent param = resource.addSearchParam();
|
||||
|
||||
param.setName(nextParamName);
|
||||
// if (StringUtils.isNotBlank(chain)) {
|
||||
|
@ -467,9 +401,9 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
||||
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
|
||||
if (targetDef != null) {
|
||||
org.hl7.fhir.instance.model.Enumerations.ResourceType code;
|
||||
ResourceType code;
|
||||
try {
|
||||
code = org.hl7.fhir.instance.model.Enumerations.ResourceType.fromCode(targetDef.getName());
|
||||
code = ResourceType.fromCode(targetDef.getName());
|
||||
} catch (Exception e) {
|
||||
code = null;
|
||||
}
|
||||
|
@ -482,6 +416,102 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
}
|
||||
|
||||
@Initialize
|
||||
public void initializeOperations() {
|
||||
myOperationBindingToName = new IdentityHashMap<OperationMethodBinding, String>();
|
||||
myOperationNameToBindings = new HashMap<String, List<OperationMethodBinding>>();
|
||||
|
||||
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
|
||||
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
|
||||
List<BaseMethodBinding<?>> nextMethodBindings = nextEntry.getValue();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextMethodBindings) {
|
||||
if (nextMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
|
||||
if (myOperationBindingToName.containsKey(methodBinding)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = createOperationName(methodBinding);
|
||||
myOperationBindingToName.put(methodBinding, name);
|
||||
if (myOperationNameToBindings.containsKey(name) == false) {
|
||||
myOperationNameToBindings.put(name, new ArrayList<OperationMethodBinding>());
|
||||
}
|
||||
myOperationNameToBindings.get(name).add(methodBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Read(type = OperationDefinition.class)
|
||||
public OperationDefinition readOperationDefinition(@IdParam IdDt theId) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
List<OperationMethodBinding> sharedDescriptions = myOperationNameToBindings.get(theId.getIdPart());
|
||||
if (sharedDescriptions == null || sharedDescriptions.isEmpty()) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
OperationDefinition op = new OperationDefinition();
|
||||
op.setStatus(ConformanceResourceStatus.ACTIVE);
|
||||
op.setIdempotent(true);
|
||||
|
||||
Set<String> inParams = new HashSet<String>();
|
||||
Set<String> outParams = new HashSet<String>();
|
||||
|
||||
for (OperationMethodBinding sharedDescription : sharedDescriptions) {
|
||||
if (isNotBlank(sharedDescription.getDescription())) {
|
||||
op.setDescription(sharedDescription.getDescription());
|
||||
}
|
||||
if (!sharedDescription.isIdempotent()) {
|
||||
op.setIdempotent(sharedDescription.isIdempotent());
|
||||
}
|
||||
op.setCode(sharedDescription.getName());
|
||||
if (sharedDescription.isCanOperateAtInstanceLevel()) {
|
||||
op.setInstance(sharedDescription.isCanOperateAtInstanceLevel());
|
||||
}
|
||||
if (sharedDescription.isCanOperateAtServerLevel()) {
|
||||
op.setSystem(sharedDescription.isCanOperateAtServerLevel());
|
||||
}
|
||||
if (isNotBlank(sharedDescription.getResourceName())) {
|
||||
op.addTypeElement().setValue(sharedDescription.getResourceName());
|
||||
}
|
||||
|
||||
for (IParameter nextParamUntyped : sharedDescription.getParameters()) {
|
||||
if (nextParamUntyped instanceof OperationParameter) {
|
||||
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
if (!inParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
param.setUse(OperationParameterUse.IN);
|
||||
if (nextParam.getParamType() != null) {
|
||||
param.setType(nextParam.getParamType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (ReturnType nextParam : sharedDescription.getReturnParams()) {
|
||||
if (!outParams.add(nextParam.getName())) {
|
||||
continue;
|
||||
}
|
||||
OperationDefinitionParameterComponent param = op.addParameter();
|
||||
param.setUse(OperationParameterUse.OUT);
|
||||
if (nextParam.getType() != null) {
|
||||
param.setType(nextParam.getType());
|
||||
}
|
||||
param.setMin(nextParam.getMin());
|
||||
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
|
||||
param.setName(nextParam.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
|
||||
* <p>
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
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.instance.model.Conformance;
|
||||
import org.hl7.fhir.instance.model.OperationDefinition;
|
||||
import org.hl7.fhir.instance.model.Organization;
|
||||
import org.hl7.fhir.instance.model.Parameters;
|
||||
import org.hl7.fhir.instance.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
public class OperationDuplicateServerHl7OrgDstu2Test {
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationDuplicateServerHl7OrgDstu2Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
@Test
|
||||
public void testOperationsAreCollapsed() throws Exception {
|
||||
// Metadata
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String response = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
|
||||
Conformance resp = ourCtx.newXmlParser().parseResource(Conformance.class, response);
|
||||
assertEquals(1, resp.getRest().get(0).getOperation().size());
|
||||
assertEquals("$myoperation", resp.getRest().get(0).getOperation().get(0).getName());
|
||||
assertEquals("OperationDefinition/myoperation", resp.getRest().get(0).getOperation().get(0).getDefinition().getReference());
|
||||
}
|
||||
|
||||
// OperationDefinition
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/myoperation?_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String response = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
|
||||
OperationDefinition resp = ourCtx.newXmlParser().parseResource(OperationDefinition.class, response);
|
||||
assertEquals(true, resp.getSystemElement().getValue().booleanValue());
|
||||
assertEquals("$myoperation", resp.getCode());
|
||||
assertEquals(true, resp.getIdempotent());
|
||||
assertEquals(2, resp.getType().size());
|
||||
assertThat(Arrays.asList(resp.getType().get(0).getValue(), resp.getType().get(1).getValue()), containsInAnyOrder("Organization", "Patient"));
|
||||
assertEquals(1, resp.getParameter().size());
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourCtx = FhirContext.forDstu2Hl7Org();
|
||||
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(), new OrganizationProvider());
|
||||
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 class BaseProvider {
|
||||
|
||||
@Operation(name = "$myoperation", idempotent = true)
|
||||
public Parameters opInstanceReturnsBundleProvider(@OperationParam(name = "myparam") StringDt theString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class OrganizationProvider extends BaseProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Organization.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PatientProvider extends BaseProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PlainProvider {
|
||||
|
||||
@Operation(name = "$myoperation", idempotent = true)
|
||||
public Parameters opInstanceReturnsBundleProvider(@OperationParam(name = "myparam") StringDt theString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue