Updated jaxrsserver base and example for DSTU3

Added support for specifying DSTU3 in the constructors, where the default is DSTU2, to maintain backwards compatibility.
Updated interface usage to IBase... (e.g., IResource -> IBaseResource), which is compatible with DSTU3.
Added tests specifically for DSTU3; kept the existing tests as DSTU2.
Provided examples for DSTU3.
This commit is contained in:
Phillip Warner 2016-04-29 12:50:15 -06:00
parent 2bc1950bc1
commit 31d16fca03
23 changed files with 4255 additions and 24 deletions

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -169,7 +170,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public BaseConformance conformance() {
public IBaseConformance conformance() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/DSTU3+ structures");
}
@ -183,7 +184,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
Class<BaseConformance> conformance = (Class<BaseConformance>) myContext.getResourceDefinition("Conformance").getImplementingClass();
ResourceResponseHandler<? extends BaseConformance> binding = new ResourceResponseHandler<BaseConformance>(conformance);
BaseConformance resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
IBaseConformance resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -2308,7 +2309,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public MethodOutcome execute() {
BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, myResource);
ResourceResponseHandler<BaseOperationOutcome> handler = new ResourceResponseHandler<BaseOperationOutcome>(null, null);
BaseOperationOutcome outcome = invoke(null, handler, invocation);
IBaseOperationOutcome outcome = invoke(null, handler, invocation);
MethodOutcome retVal = new MethodOutcome();
retVal.setOperationOutcome(outcome);
return retVal;

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.client;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
@ -58,7 +59,7 @@ public interface IGenericClient extends IRestfulClient {
* @deprecated Use {@link #fetchConformance()} instead
*/
@Deprecated
BaseConformance conformance();
IBaseConformance conformance();
/**
* Fluent method for the "create" operation, which creates a new resource instance on the server

View File

@ -0,0 +1,84 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.ParametersUtil;
public class ValidateMethodBindingDstu3 extends OperationMethodBinding {
public ValidateMethodBindingDstu3(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
Validate theAnnotation) {
super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION);
List<IParameter> newParams = new ArrayList<IParameter>();
int idx = 0;
for (IParameter next : getParameters()) {
if (next instanceof ResourceParameter) {
if (IBaseResource.class.isAssignableFrom(((ResourceParameter) next).getResourceType())) {
Class<?> parameterType = theMethod.getParameterTypes()[idx];
if (String.class.equals(parameterType) || EncodingEnum.class.equals(parameterType)) {
newParams.add(next);
} else {
OperationParameter parameter = new OperationParameter(theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_RESOURCE, 0, 1);
parameter.initializeTypes(theMethod, null, null, parameterType);
newParams.add(parameter);
}
} else {
newParams.add(next);
}
} else {
newParams.add(next);
}
idx++;
}
setParameters(newParams);
}
public static BaseHttpClientInvocation createValidateInvocation(FhirContext theContext, IBaseResource theResource) {
IBaseParameters parameters = (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
ParametersUtil.addParameterToParameters(theContext, parameters, theResource, "resource");
String resourceName = theContext.getResourceDefinition(theResource).getName();
String resourceId = theResource.getIdElement().getIdPart();
BaseHttpClientInvocation retVal = createOperationInvocation(theContext, resourceName, resourceId, Constants.EXTOP_VALIDATE, parameters, false);
return retVal;
}
}

View File

@ -40,6 +40,11 @@
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>1.6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>

View File

@ -39,12 +39,16 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.hapi.rest.server.ServerConformanceProvider;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
@ -56,7 +60,6 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IRestfulResponse;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
import ca.uhn.fhir.util.ReflectionUtil;
/**
@ -78,7 +81,8 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
private RestulfulServerConfiguration serverConfiguration = new RestulfulServerConfiguration();
/** the conformance. It is created once during startup */
private Conformance myConformance;
private Conformance myDstu3Conformance;
private ca.uhn.fhir.model.dstu2.resource.Conformance myDstu2Conformance;
/**
* Constructor allowing the description, servername and server to be set
@ -92,6 +96,21 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
serverConfiguration.setServerName(StringUtils.defaultIfEmpty(serverName, ""));
serverConfiguration.setServerVersion(StringUtils.defaultIfEmpty(serverVersion, ""));
}
/**
* Constructor allowing the description, servername and server to be set
* @param ctx the {@link FhirContext} instance.
* @param implementationDescription the implementation description. If null, "" is used
* @param serverName the server name. If null, "" is used
* @param serverVersion the server version. If null, "" is used
*/
protected AbstractJaxRsConformanceProvider(FhirContext ctx, String implementationDescription, String serverName, String serverVersion) {
super(ctx);
serverConfiguration.setFhirContext(ctx);
serverConfiguration.setImplementationDescription(StringUtils.defaultIfEmpty(implementationDescription, ""));
serverConfiguration.setServerName(StringUtils.defaultIfEmpty(serverName, ""));
serverConfiguration.setServerVersion(StringUtils.defaultIfEmpty(serverVersion, ""));
}
/**
* This method will set the conformance during the postconstruct phase. The
@ -112,9 +131,15 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy();
hardcodedServerAddressStrategy.setValue(getBaseForServer());
serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy);
ServerConformanceProvider serverConformanceProvider = new ServerConformanceProvider(serverConfiguration);
serverConformanceProvider.initializeOperations();
myConformance = serverConformanceProvider.getServerConformance(null);
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
ServerConformanceProvider serverConformanceProvider = new ServerConformanceProvider(serverConfiguration);
serverConformanceProvider.initializeOperations();
myDstu3Conformance = serverConformanceProvider.getServerConformance(null);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider serverConformanceProvider = new ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider(serverConfiguration);
serverConformanceProvider.initializeOperations();
myDstu2Conformance = serverConformanceProvider.getServerConformance(null);
}
}
/**
@ -145,7 +170,12 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
Builder request = getRequest(RequestTypeEnum.OPTIONS, RestOperationTypeEnum.METADATA);
IRestfulResponse response = request.build().getResponse();
response.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, "*");
return (Response) response.returnResponse(ParseAction.create(myConformance), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
return (Response) response.returnResponse(ParseAction.create(myDstu3Conformance), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
return (Response) response.returnResponse(ParseAction.create(myDstu2Conformance), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
}
return (Response) response.returnResponse(null, Constants.STATUS_HTTP_500_INTERNAL_ERROR, true, null, getResourceType().getSimpleName());
}
/**
@ -217,9 +247,15 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
return count;
}
@SuppressWarnings("unchecked")
@Override
public Class<Conformance> getResourceType() {
return Conformance.class;
public Class<IBaseResource> getResourceType() {
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
return Class.class.cast(Conformance.class);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
return Class.class.cast(ca.uhn.fhir.model.dstu2.resource.Conformance.class);
}
return null;
}
}

View File

@ -31,6 +31,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
@ -65,6 +66,19 @@ public abstract class AbstractJaxRsPageProvider extends AbstractJaxRsProvider im
}
}
/**
* Provides the ability to set the {@link FhirContext} instance.
* @param ctx the {@link FhirContext} instance.
*/
protected AbstractJaxRsPageProvider(FhirContext ctx) {
super(ctx);
try {
myBinding = new PageMethodBinding(getFhirContext(), PageProvider.class.getMethod("getPage"));
} catch (Exception e) {
throw new ca.uhn.fhir.context.ConfigurationException(e);
}
}
@Override
public String getBaseForRequest() {
try {

View File

@ -53,8 +53,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
*/
public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
/** a static initialization for the fhircontext. Only DSTU2 is supported */
private static final FhirContext CTX = FhirContext.forDstu2();
private final FhirContext CTX;
/** the uri info */
@Context
@ -67,7 +66,22 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
public FhirContext getFhirContext() {
return CTX;
}
/**
* Default is DSTU2. Use {@link AbstractJaxRsProvider#AbstractJaxRsProvider(FhirContext)} to specify a DSTU3 context.
*/
protected AbstractJaxRsProvider() {
CTX = FhirContext.forDstu2();
}
/**
*
* @param ctx the {@link FhirContext} to support.
*/
protected AbstractJaxRsProvider(FhirContext ctx) {
CTX = ctx;
}
/**
* This method returns the query parameters
* @return the query parameters

View File

@ -22,8 +22,6 @@ package ca.uhn.fhir.jaxrs.server;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import javax.interceptor.Interceptors;
import javax.ws.rs.Consumes;
@ -37,12 +35,14 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
@ -51,7 +51,6 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IRestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
/**
* This server is the abstract superclass for all resource providers. It exposes
@ -62,7 +61,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
@Consumes({ MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON,
Constants.CT_FHIR_XML })
@Interceptors(JaxRsExceptionInterceptor.class)
public abstract class AbstractJaxRsResourceProvider<R extends IResource> extends AbstractJaxRsProvider
public abstract class AbstractJaxRsResourceProvider<R extends IBaseResource> extends AbstractJaxRsProvider
implements IRestfulServer<JaxRsRequest>, IResourceProvider {
/** the method bindings for this class */
@ -73,9 +72,19 @@ public abstract class AbstractJaxRsResourceProvider<R extends IResource> extends
* being constructed.
*/
protected AbstractJaxRsResourceProvider() {
super();
theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
}
/**
* Provides the ability to specify the {@link FhirContext}.
* @param ctx the {@link FhirContext} instance.
*/
protected AbstractJaxRsResourceProvider(FhirContext ctx) {
super(ctx);
theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
}
/**
* This constructor takes in an explicit interface class. This subclass
* should be identical to the class being constructed but is given
@ -85,9 +94,24 @@ public abstract class AbstractJaxRsResourceProvider<R extends IResource> extends
* @param theProviderClass the interface of the class
*/
protected AbstractJaxRsResourceProvider(Class<? extends AbstractJaxRsProvider> theProviderClass) {
super();
theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
}
/**
* This constructor takes in an explicit interface class. This subclass
* should be identical to the class being constructed but is given
* explicitly in order to avoid issues with proxy classes in a jee
* environment.
*
* @param ctx the {@link FhirContext} instance.
* @param theProviderClass the interface of the class
*/
protected AbstractJaxRsResourceProvider(FhirContext ctx, Class<? extends AbstractJaxRsProvider> theProviderClass) {
super(ctx);
theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
}
/**
* The base for request for a resource provider has the following form:</br>
* {@link AbstractJaxRsResourceProvider#getBaseForServer()

View File

@ -31,7 +31,9 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.IdType;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
@ -129,18 +131,33 @@ public class JaxRsRequest extends RequestDetails {
throw new InvalidRequestException("Don't know how to handle request path: "
+ myServer.getUriInfo().getRequestUri().toASCIIString());
}
FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion();
if (StringUtils.isNotBlank(myVersion)) {
result.setId(
new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
if (FhirVersionEnum.DSTU3.equals(fhirContextVersion)) {
result.setId(
new IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
} else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) {
result.setId(
new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
}
} else if (StringUtils.isNotBlank(myId)) {
result.setId(new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
if (FhirVersionEnum.DSTU3.equals(fhirContextVersion)) {
result.setId(new IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
} else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) {
result.setId(new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
}
}
if (myRestOperation == RestOperationTypeEnum.UPDATE) {
String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
if (contentLocation != null) {
result.setId(new IdDt(contentLocation));
if (FhirVersionEnum.DSTU3.equals(fhirContextVersion)) {
result.setId(new IdType(contentLocation));
} else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) {
result.setId(new IdDt(contentLocation));
}
}
}

View File

@ -0,0 +1,110 @@
package ca.uhn.fhir.jaxrs.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.net.URI;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu3;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
public class AbstractJaxRsConformanceProviderDstu3Test {
private static final String BASEURI = "http://basiuri";
private static final String REQUESTURI = BASEURI + "/metadata";
AbstractJaxRsConformanceProvider provider;
private ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers;
private ContainerRequest headers;
private MultivaluedHashMap<String, String> queryParameters;
@Before
public void setUp() throws Exception {
// headers
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null,
new MapPropertiesDelegate());
// uri info
queryParameters = new MultivaluedHashMap<String, String>();
providers = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
provider = createConformanceProvider(providers);
}
@Test
public void testConformance() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider());
Response response = createConformanceProvider(providers).conformance();
System.out.println(response);
}
@Test
public void testConformanceUsingOptions() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider());
Response response = createConformanceProvider(providers).conformanceUsingOptions();
System.out.println(response);
}
@Test
public void testConformanceWithMethods() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
assertTrue(response.getEntity().toString().contains("\"type\":\"Patient\""));
assertTrue(response.getEntity().toString().contains("\"$someCustomOperation"));
System.out.println(response);
System.out.println(response.getEntity());
}
@Test
public void testConformanceInXml() throws Exception {
queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML));
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
System.out.println(response.getEntity());
assertTrue(response.getEntity().toString().contains(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("\"$someCustomOperation"));
System.out.println(response.getEntity());
}
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
throws Exception {
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu3(), null, null, null) {
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
return providers;
}
};
// mocks
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI));
when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo"));
result.setUriInfo(uriInfo);
result.setHeaders(headers);
result.setUpPostConstruct();
return result;
}
}

View File

@ -0,0 +1,443 @@
package ca.uhn.fhir.jaxrs.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Identifier;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.test.RandomServerPortProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsConformanceRestProviderDstu3;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPageProviderDstu3;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu3;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class AbstractJaxRsResourceProviderDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractJaxRsResourceProviderDstu3Test.class);
private static IGenericClient client;
private static final FhirContext ourCtx = FhirContext.forDstu3();
private static final String PATIENT_NAME = "Van Houte";
private static int ourPort;
private static String serverBase;
private static Server jettyServer;
private TestJaxRsMockPatientRestProviderDstu3 mock;
private ArgumentCaptor<IdType> idCaptor;
private ArgumentCaptor<Patient> patientCaptor;
private void compareResultId(int id, IBaseResource resource) {
assertEquals(id, Integer.parseInt(resource.getIdElement().getIdPart()));
}
private void compareResultUrl(String url, IBaseResource resource) {
assertEquals(url, resource.getIdElement().getValueAsString().substring(serverBase.length() - 1));
}
private Patient createPatient(long id) {
Patient theResource = new Patient();
theResource.setId(new IdType(id));
return theResource;
}
private List<Patient> createPatients(int firstId, int lastId) {
List<Patient> result = new ArrayList<Patient>(lastId - firstId);
for (long i = firstId; i <= lastId; i++) {
result.add(createPatient(i));
}
return result;
}
/** Find By Id */
@Test
public void findUsingGenericClientById() {
when(mock.find(any(IdType.class))).thenReturn(createPatient(1));
Patient result = client.read(Patient.class, "1");
compareResultId(1, result);
compareResultUrl("/Patient/1", result);
reset(mock);
when(mock.find(eq(result.getIdElement()))).thenReturn(createPatient(1));
result = (Patient) client.read(new UriDt(result.getId()));
compareResultId(1, result);
compareResultUrl("/Patient/1", result);
}
private Bundle getPatientBundle(int size) {
Bundle result = new Bundle();
for (long i = 0; i < size; i++) {
Patient patient = createPatient(i);
BundleEntryComponent entry = new BundleEntryComponent();
entry.setResource(patient);
result.addEntry(entry);
}
return result;
}
@Before
public void setUp() {
this.mock = TestJaxRsMockPatientRestProviderDstu3.mock;
idCaptor = ArgumentCaptor.forClass(IdType.class);
patientCaptor = ArgumentCaptor.forClass(Patient.class);
reset(mock);
}
/** Conditional Creates */
@Test
public void testConditionalCreate() throws Exception {
Patient toCreate = createPatient(1);
MethodOutcome outcome = new MethodOutcome();
toCreate.getIdentifier().add(new Identifier().setValue("myIdentifier"));
outcome.setResource(toCreate);
when(mock.create(patientCaptor.capture(), eq("Patient?_format=json&identifier=2"))).thenReturn(outcome);
client.setEncoding(EncodingEnum.JSON);
MethodOutcome response = client.create().resource(toCreate).conditional()
.where(Patient.IDENTIFIER.exactly().identifier("2")).prefer(PreferReturnEnum.REPRESENTATION).execute();
assertEquals("myIdentifier", patientCaptor.getValue().getIdentifier().get(0).getValue());
IBaseResource resource = response.getResource();
compareResultId(1, resource);
}
/** Conformance - Server */
@Test
public void testConformance() {
final Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
assertEquals(conf.getRest().get(0).getResource().get(0).getType().toString(), "Patient");
}
@Test
public void testCreatePatient() throws Exception {
Patient toCreate = createPatient(1);
MethodOutcome outcome = new MethodOutcome();
toCreate.getIdentifier().add(new Identifier().setValue("myIdentifier"));
outcome.setResource(toCreate);
when(mock.create(patientCaptor.capture(), isNull(String.class))).thenReturn(outcome);
client.setEncoding(EncodingEnum.JSON);
final MethodOutcome response = client.create().resource(toCreate).prefer(PreferReturnEnum.REPRESENTATION)
.execute();
IBaseResource resource = (IBaseResource) response.getResource();
compareResultId(1, resource);
assertEquals("myIdentifier", patientCaptor.getValue().getIdentifier().get(0).getValue());
}
@Test
public void testDeletePatient() {
when(mock.delete(idCaptor.capture())).thenReturn(new MethodOutcome());
final BaseOperationOutcome results = client.delete().resourceById("Patient", "1").execute();
assertEquals("1", idCaptor.getValue().getIdPart());
}
/** Extended Operations */
@Test
public void testExtendedOperations() {
// prepare mock
Parameters resultParameters = new Parameters();
resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringType("outputValue"));
when(mock.someCustomOperation(any(IdType.class), argThat(new StringTypeMatcher(new StringType("myAwesomeDummyValue"))))).thenReturn(resultParameters);
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateType("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateType("2015-03-01"));
inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue"));
//invoke
Parameters outParams = client.operation().onInstance(new IdType("Patient", "1")).named("$someCustomOperation")
.withParameters(inParams).execute();
//verify
assertEquals("outputValue", ((StringType)outParams.getParameter().get(0).getValue()).getValueAsString());
}
class StringTypeMatcher extends ArgumentMatcher<StringType> {
private StringType myStringType;
public StringTypeMatcher(StringType stringType) {
myStringType = stringType;
}
@Override
public boolean matches(Object argument) {
return myStringType.getValue().equals(((StringType)argument).getValue());
}
}
@Test
public void testExtendedOperationsUsingGet() {
// prepare mock
Parameters resultParameters = new Parameters();
resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringType("outputValue"));
when(mock.someCustomOperation(any(IdType.class), argThat(new StringTypeMatcher(new StringType("myAwesomeDummyValue"))))).thenReturn(resultParameters);
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateType("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateType("2015-03-01"));
inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue"));
// invoke
Parameters outParams = client.operation().onInstance(new IdType("Patient", "1")).named("$someCustomOperation")
.withParameters(inParams).useHttpGet().execute();
// verify
assertEquals("outputValue", ((StringType)outParams.getParameter().get(0).getValue()).getValueAsString());
}
/** Search using other query options */
public void testOther() {
// missing
}
@Test
public void testRead() {
when(mock.find(idCaptor.capture())).thenReturn(createPatient(1));
final Patient patient = client.read(Patient.class, "1");
compareResultId(1, patient);
compareResultUrl("/Patient/1", patient);
assertEquals("1", idCaptor.getValue().getIdPart());
}
/** Search - Compartments */
@Test
public void testSearchCompartements() {
when(mock.searchCompartment(any(IdType.class))).thenReturn(Arrays.asList((IBaseResource) createPatient(1)));
org.hl7.fhir.dstu3.model.Bundle response = client.search().forResource(Patient.class).withIdAndCompartment("1", "Condition")
.returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute();
Resource resource = response.getEntry().get(0).getResource();
compareResultId(1, resource);
compareResultUrl("/Patient/1", resource);
}
/** */
@Test
public void testSearchPost() {
when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)))
.thenReturn(createPatients(1, 13));
org.hl7.fhir.dstu3.model.Bundle result = client.search().forResource("Patient").usingStyle(SearchStyleEnum.POST)
.returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute();
Resource resource = result.getEntry().get(0).getResource();
compareResultId(1, resource);
compareResultUrl("/Patient/1", resource);
}
/** Search/Query - Type */
@Test
public void testSearchUsingGenericClientBySearch() {
// Perform a search
when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)))
.thenReturn(Arrays.asList(createPatient(1)));
final Bundle results = client.search().forResource(Patient.class)
.where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).returnBundle(Bundle.class).execute();
verify(mock).search(any(StringParam.class), Matchers.isNull(StringAndListParam.class));
IBaseResource resource = results.getEntry().get(0).getResource();
compareResultId(1, resource);
compareResultUrl("/Patient/1", resource);
}
/** Search - Multi-valued Parameters (ANY/OR) */
@Test
public void testSearchUsingGenericClientBySearchWithMultiValues() {
when(mock.search(any(StringParam.class), Matchers.isNotNull(StringAndListParam.class)))
.thenReturn(Arrays.asList(createPatient(1)));
final Bundle results = client.search().forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario"))
.and(Patient.ADDRESS.matches().values("Canada"))
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).returnBundle(Bundle.class).execute();
IBaseResource resource = results.getEntry().get(0).getResource();
compareResultId(1, resource);
compareResultUrl("/Patient/1", resource);
}
/** Search - Paging */
@Test
public void testSearchWithPaging() {
// Perform a search
when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)))
.thenReturn(createPatients(1, 13));
final org.hl7.fhir.dstu3.model.Bundle results = client.search().forResource(Patient.class).count(8).returnBundle(org.hl7.fhir.dstu3.model.Bundle.class)
.execute();
assertEquals(results.getEntry().size(), 8);
IBaseResource resource = results.getEntry().get(0).getResource();
compareResultId(1, resource);
compareResultUrl("/Patient/1", resource);
compareResultId(8, results.getEntry().get(7).getResource());
// ourLog.info("Next: " + results.getLink("next").getUrl());
// String url = results.getLink("next").getUrl().replace("?", "Patient?");
// results.getLink("next").setUrl(url);
// ourLog.info("New Next: " + results.getLink("next").getUrl());
// load next page
final org.hl7.fhir.dstu3.model.Bundle nextPage = client.loadPage().next(results).execute();
resource = nextPage.getEntry().get(0).getResource();
compareResultId(9, resource);
compareResultUrl("/Patient/9", resource);
assertNull(nextPage.getLink(org.hl7.fhir.dstu3.model.Bundle.LINK_NEXT));
}
/** Search - Subsetting (_summary and _elements) */
@Test
@Ignore
public void testSummary() {
Object response = client.search().forResource(Patient.class)
.returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute();
}
/** Transaction - Server */
// @Ignore
// @Test
// public void testTransaction() {
// ca.uhn.fhir.model.api.Bundle bundle = new ca.uhn.fhir.model.api.Bundle();
// BundleEntry entry = bundle.addEntry();
// final Patient existing = new Patient();
// existing.getName().get(0).addFamily("Created with bundle");
// entry.setResource(existing);
//
// BoundCodeDt<BundleEntryTransactionMethodEnum> theTransactionOperation = new BoundCodeDt(
// BundleEntryTransactionMethodEnum.VALUESET_BINDER, BundleEntryTransactionMethodEnum.POST);
// entry.setTransactionMethod(theTransactionOperation);
// ca.uhn.fhir.model.api.Bundle response = client.transaction().withBundle(bundle).execute();
// }
@Test
public void testUpdateById() throws Exception {
when(mock.update(idCaptor.capture(), patientCaptor.capture())).thenReturn(new MethodOutcome());
client.update("1", createPatient(1));
assertEquals("1", idCaptor.getValue().getIdPart());
compareResultId(1, patientCaptor.getValue());
}
@SuppressWarnings("unchecked")
@Ignore
@Test
public void testResourceNotFound() throws Exception {
when(mock.update(idCaptor.capture(), patientCaptor.capture())).thenThrow(ResourceNotFoundException.class);
try {
client.update("1", createPatient(2));
fail();
} catch (ResourceNotFoundException e) {
// good
}
}
@Test
public void testVRead() {
when(mock.findHistory(idCaptor.capture())).thenReturn(createPatient(1));
final Patient patient = client.vread(Patient.class, "1", "2");
compareResultId(1, patient);
compareResultUrl("/Patient/1", patient);
assertEquals("1", idCaptor.getValue().getIdPart());
assertEquals("2", idCaptor.getValue().getVersionIdPart());
}
@Test
public void testXFindUnknownPatient() {
try {
JaxRsResponseException notFoundException = new JaxRsResponseException(new ResourceNotFoundException(new IdType("999955541264")));
when(mock.find(idCaptor.capture())).thenThrow(notFoundException);
client.read(Patient.class, "999955541264");
fail();
} catch (final ResourceNotFoundException e) {
assertEquals(ResourceNotFoundException.STATUS_CODE, e.getStatusCode());
assertTrue(e.getMessage().contains("999955541264"));
}
}
@BeforeClass
public static void setUpClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
System.out.println(ourPort);
jettyServer = new Server(ourPort);
jettyServer.setHandler(context);
ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
//@formatter:off
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
StringUtils.join(Arrays.asList(
TestJaxRsMockPatientRestProviderDstu3.class.getCanonicalName(),
JaxRsExceptionInterceptor.class.getCanonicalName(),
TestJaxRsConformanceRestProviderDstu3.class.getCanonicalName(),
TestJaxRsMockPageProviderDstu3.class.getCanonicalName()
), ";"));
//@formatter:on
jettyServer.start();
ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx));
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
serverBase = "http://localhost:" + ourPort + "/";
client = ourCtx.newRestfulGenericClient(serverBase);
client.setEncoding(EncodingEnum.JSON);
client.registerInterceptor(new LoggingInterceptor(true));
}
@AfterClass
public static void tearDownClass() throws Exception {
try {
jettyServer.destroy();
} catch (Exception e) {
}
}
}

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.jaxrs.server.test;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
/**
* A conformance provider exposes the mock patient and this provider
*/
@Path("")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class TestJaxRsConformanceRestProviderDstu3 extends AbstractJaxRsConformanceProvider {
public TestJaxRsConformanceRestProviderDstu3() {
super(FhirContext.forDstu3(), "description", "name", "version");
}
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
map.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3());
map.put(TestJaxRsConformanceRestProviderDstu3.class, new TestJaxRsConformanceRestProviderDstu3());
return map;
}
}

View File

@ -0,0 +1,21 @@
package ca.uhn.fhir.jaxrs.server.test;
import org.hl7.fhir.dstu3.model.Patient;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
/**
* A dummy patient provider exposing no methods
*/
public class TestJaxRsDummyPatientProviderDstu3 extends AbstractJaxRsResourceProvider<Patient> {
public TestJaxRsDummyPatientProviderDstu3() {
super(FhirContext.forDstu3());
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
}

View File

@ -0,0 +1,26 @@
package ca.uhn.fhir.jaxrs.server.test;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IPagingProvider;
@Path("/")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class TestJaxRsMockPageProviderDstu3 extends AbstractJaxRsPageProvider {
public TestJaxRsMockPageProviderDstu3() {
super(FhirContext.forDstu3());
}
@Override
public IPagingProvider getPagingProvider() {
return TestJaxRsMockPatientRestProviderDstu3.PAGING_PROVIDER;
}
}

View File

@ -0,0 +1,141 @@
package ca.uhn.fhir.jaxrs.server.test;
import java.util.List;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.mockito.Mockito;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
/**
* A test server delegating each call to a mock
*/
@Path(TestJaxRsMockPatientRestProviderDstu3.PATH)
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
@Interceptors(JaxRsExceptionInterceptor.class)
public class TestJaxRsMockPatientRestProviderDstu3 extends AbstractJaxRsResourceProvider<Patient> {
static final String PATH = "/Patient";
public static final TestJaxRsMockPatientRestProviderDstu3 mock = Mockito.mock(TestJaxRsMockPatientRestProviderDstu3.class);
public static final FifoMemoryPagingProvider PAGING_PROVIDER;
static
{
PAGING_PROVIDER = new FifoMemoryPagingProvider(10);
PAGING_PROVIDER.setDefaultPageSize(10);
PAGING_PROVIDER.setMaximumPageSize(100);
}
/**
* Constructor
*/
public TestJaxRsMockPatientRestProviderDstu3() {
super(FhirContext.forDstu3());
}
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name, @RequiredParam(name=Patient.SP_ADDRESS) StringAndListParam theAddressParts) {
return mock.search(name, theAddressParts);
}
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient) throws Exception {
return mock.update(theId, patient);
}
@Read
public Patient find(@IdParam final IdType theId) {
return mock.find(theId);
}
@Read(version = true)
public Patient findHistory(@IdParam final IdType theId) {
return mock.findHistory(theId);
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional)
throws Exception {
return mock.create(patient, theConditional);
}
@Delete
public MethodOutcome delete(@IdParam final IdType theId) {
return mock.delete(theId);
}
@Search(compartmentName = "Condition")
public List<IBaseResource> searchCompartment(@IdParam IdType thePatientId) {
return mock.searchCompartment(thePatientId);
}
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringType.class) })
public Parameters someCustomOperation(@IdParam IdType myId, @OperationParam(name = "dummy") StringType dummyInput) {
return mock.someCustomOperation(myId, dummyInput);
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Override
public IPagingProvider getPagingProvider() {
return PAGING_PROVIDER;
}
}

View File

@ -0,0 +1,129 @@
package ca.uhn.fhir.jaxrs.server.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProviderDstu3;
import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
@FixMethodOrder(MethodSorters.DEFAULT)
public class JaxRsMethodBindingsDstu3Test {
@Before
public void setUp() {
JaxRsMethodBindings.getClassBindings().clear();
}
@Test(expected = NotImplementedOperationException.class)
public void testFindMethodsForProviderNotDefinedMappingMethods() {
new TestJaxRsDummyPatientProviderDstu3().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "");
}
@Test
public void testFindMethodsForProviderWithMethods() {
class TestFindPatientProvider extends TestJaxRsDummyPatientProviderDstu3 {
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
}
new TestFindPatientProvider();
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getDeclaringClass());
}
@Test
public void testFindMethodsFor2ProvidersWithMethods() {
class TestFindPatientProvider extends TestJaxRsDummyPatientProviderDstu3 {
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
}
class TestUpdatePatientProvider extends TestJaxRsDummyPatientProviderDstu3 {
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient) {
return null;
}
}
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getDeclaringClass());
assertEquals(TestUpdatePatientProvider.class, new TestUpdatePatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "").getMethod().getDeclaringClass());
}
@Test
public void testFindMethodsWithDoubleMethodsDeclaration() {
class TestDoubleSearchProvider extends TestJaxRsDummyPatientProviderDstu3 {
@Search
public List<Patient> search1(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
@Search
public List<Patient> search2(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
}
try {
new TestDoubleSearchProvider();
fail();
} catch(IllegalArgumentException e) {
assertTrue(e.getMessage().contains("search1"));
assertTrue(e.getMessage().contains("search2"));
}
}
@Test
public void testFindMethodsWithMultipleMethods() {
class TestFindPatientProvider extends TestJaxRsDummyPatientProviderDstu3 {
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient) {
return null;
}
@Operation(name = "firstMethod", idempotent = true, returnParameters = { @OperationParam(name = "return", type = StringType.class) })
public Parameters firstMethod(@OperationParam(name = "dummy") StringType dummyInput) {
return null;
}
@Operation(name = "secondMethod", returnParameters = { @OperationParam(name = "return", type = StringType.class) })
public Parameters secondMethod(@OperationParam(name = "dummy") StringType dummyInput) {
return null;
}
}
JaxRsMethodBindings bindings = new TestFindPatientProvider().getBindings();
assertEquals("search", bindings.getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getName());
assertEquals("update", bindings.getBinding(RestOperationTypeEnum.UPDATE, "").getMethod().getName());
assertEquals("firstMethod", bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$firstMethod").getMethod().getName());
assertEquals("secondMethod", bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$secondMethod").getMethod().getName());
try {
bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$thirdMethod");
fail();
} catch(NotImplementedOperationException e){
}
}
}

View File

@ -0,0 +1,119 @@
package ca.uhn.fhir.jaxrs.server.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProviderDstu3;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsResponse;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
public class JaxRsRequestDstu3Test {
private static final String RESOURCE_STRING = "</Patient>";
private static final String BASEURI = "http://baseuri";
private static final String REQUESTURI = "http://baseuri/test";
private JaxRsRequest details;
private MultivaluedMap<String, String> queryParameters = new MultivaluedHashMap<String, String>();
private ContainerRequest headers;
private TestJaxRsDummyPatientProviderDstu3 provider;
@Before
public void setUp() throws URISyntaxException {
details = createRequestDetails();
}
@Test
public void testGetHeader() {
String headerKey = "key";
String headerValue = "location_value";
String headerValue2 = "location_value_2";
assertTrue(StringUtils.isBlank(details.getHeader(headerKey)));
headers.header(headerKey, headerValue);
assertEquals(headerValue, details.getHeader(headerKey));
assertEquals(Arrays.asList(headerValue), details.getHeaders(headerKey));
headers.header(headerKey, headerValue2);
assertEquals(headerValue, details.getHeader(headerKey));
assertEquals(Arrays.asList(headerValue, headerValue2), details.getHeaders(headerKey));
}
@Test
public void testGetByteStreamRequestContents() {
assertEquals(RESOURCE_STRING, new String(details.getByteStreamRequestContents()));
}
@Test
public void testServerBaseForRequest() {
assertEquals(BASEURI, new String(details.getServerBaseForRequest()));
}
@Test
public void testGetResponse() {
JaxRsResponse response = (JaxRsResponse) details.getResponse();
assertEquals(details, response.getRequestDetails());
assertTrue(response == details.getResponse());
}
@Test(expected = UnsupportedOperationException.class)
public void testGetReader() throws IOException {
details.getReader();
}
@Test(expected = UnsupportedOperationException.class)
public void testGetInputStream() {
details.getInputStream();
}
@Test
public void testGetServerBaseForRequest() {
assertEquals(JaxRsRequestDstu3Test.BASEURI, details.getFhirServerBase());
}
@Test
public void testGetServer() {
assertEquals(this.provider, details.getServer());
}
public JaxRsRequest createRequestDetails() throws URISyntaxException {
//headers
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null, new MapPropertiesDelegate());
//uri info
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
//mocks
provider = spy(TestJaxRsDummyPatientProviderDstu3.class);
doReturn(uriInfo).when(provider).getUriInfo();
doReturn(BASEURI).when(provider).getBaseForRequest();
doReturn(BASEURI).when(provider).getBaseForServer();
doReturn(headers).when(provider).getHeaders();
return new JaxRsRequest(provider, RESOURCE_STRING, RequestTypeEnum.GET, RestOperationTypeEnum.HISTORY_TYPE);
}
}

View File

@ -0,0 +1,159 @@
package ca.uhn.fhir.jaxrs.server.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Set;
import javax.ws.rs.core.Response;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsResponse;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.method.ParseAction;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
public class JaxRsResponseDstu3Test {
private JaxRsResponse response;
private JaxRsRequest request;
private Bundle bundle;
private Set<SummaryEnum> theSummaryMode;
@Before
public void setUp() throws URISyntaxException {
request = new JaxRsRequestDstu3Test().createRequestDetails();
this.response = (JaxRsResponse) request.getResponse();
bundle = getSinglePatientResource();
theSummaryMode = Collections.<SummaryEnum>emptySet();
}
@Test
public void testGetResponseWriterNoZipNoBrowser() throws IOException {
boolean theRequestIsBrowser = false;
boolean respondGzip = false;
Set<SummaryEnum> theSummaryMode = Collections.<SummaryEnum>emptySet();
boolean theAddContentLocationHeader = false;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), bundle, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, request);
assertEquals(200, result.getStatus());
assertEquals(Constants.CT_FHIR_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
assertTrue(result.getEntity().toString().contains("Patient"));
assertTrue(result.getEntity().toString().contains("15"));
}
@Test
public void testSendAttachmentResponse() throws IOException {
boolean theRequestIsBrowser = true;
boolean respondGzip = true;
IBaseBinary binary = new Binary();
String contentType = "foo";
byte[] content = new byte[] { 1, 2, 3, 4 };
binary.setContentType(contentType);
binary.setContent(content);
boolean theAddContentLocationHeader = false;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, this.request);
assertEquals(200, result.getStatus());
assertEquals(contentType, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
assertEquals(content, result.getEntity());
}
@Test
public void testSendAttachmentResponseNoContent() throws IOException {
boolean theRequestIsBrowser = true;
boolean respondGzip = true;
IBaseBinary binary = new Binary();
binary.setContent(new byte[]{});
boolean theAddContentLocationHeader = false;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, this.request);
assertEquals(200, result.getStatus());
assertEquals(null, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
assertEquals(null, result.getEntity());
}
@Test
public void testSendAttachmentResponseEmptyContent() throws IOException {
boolean theRequestIsBrowser = true;
boolean respondGzip = true;
IBaseBinary binary = new Binary();
boolean theAddContentLocationHeader = false;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, this.request);
assertEquals(200, result.getStatus());
assertEquals(null, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
assertEquals(null, result.getEntity());
}
@Test
public void testReturnResponse() throws IOException {
IdType theId = new IdType(15L);
ParseAction<?> outcome = ParseAction.create(createPatient());
int operationStatus = 200;
boolean allowPrefer = true;
String resourceName = "Patient";
MethodOutcome methodOutcome = new MethodOutcome(theId);
Response result = response.returnResponse(outcome, operationStatus, allowPrefer, methodOutcome, resourceName);
assertEquals(200, result.getStatus());
assertEquals(Constants.CT_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
System.out.println(result.getEntity().toString());
assertTrue(result.getEntity().toString().contains("resourceType\":\"Patient"));
assertTrue(result.getEntity().toString().contains("15"));
}
@Test
public void testReturnResponseAsXml() throws IOException {
IdType theId = new IdType(15L);
ParseAction<?> outcome = ParseAction.create(createPatient());
int operationStatus = 200;
boolean allowPrefer = true;
String resourceName = "Patient";
MethodOutcome methodOutcome = new MethodOutcome(theId);
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML});
Response result = response.returnResponse(outcome, operationStatus, allowPrefer, methodOutcome, resourceName);
assertEquals(200, result.getStatus());
assertEquals(Constants.CT_XML+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
assertTrue(result.getEntity().toString().contains("<Patient"));
assertTrue(result.getEntity().toString().contains("15"));
}
@Test
public void testNoOutcomeXml() throws IOException {
ParseAction<?> outcome = ParseAction.create((IBaseResource) null);
int operationStatus = Constants.STATUS_HTTP_204_NO_CONTENT;
boolean allowPrefer = true;
String resourceName = "Patient";
MethodOutcome methodOutcome = new MethodOutcome(null);
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML});
Response result = response.returnResponse(outcome, operationStatus, allowPrefer, methodOutcome, resourceName);
assertEquals(204, result.getStatus());
assertEquals(Constants.CT_XML+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
}
private Bundle getSinglePatientResource() {
Patient theResource = createPatient();
Bundle bundle = new Bundle();
bundle.addEntry().setResource(theResource);
return bundle;
}
private Patient createPatient() {
Patient theResource = new Patient();
theResource.setId(new IdType(15L));
return theResource;
}
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.jaxrs.server.example;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
/**
* Conformance Rest Service
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
@Path("")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsConformanceProviderDstu3 extends AbstractJaxRsConformanceProvider {
private static final String SERVER_VERSION = "1.0.0";
private static final String SERVER_DESCRIPTION = "Jax-Rs Test Example Description";
private static final String SERVER_NAME = "Jax-Rs Test Example";
@Inject
private JaxRsPatientRestProvider patientProvider;
/**
* Standard Constructor
*/
public JaxRsConformanceProviderDstu3() {
super(FhirContext.forDstu3(), SERVER_VERSION, SERVER_DESCRIPTION, SERVER_NAME);
}
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
map.put(JaxRsConformanceProviderDstu3.class, this);
map.put(JaxRsPatientRestProvider.class, patientProvider);
return map;
}
}

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.jaxrs.server.example;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IPagingProvider;
@Path("/")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsPageProviderDstu3 extends AbstractJaxRsPageProvider {
public JaxRsPageProviderDstu3() {
super(FhirContext.forDstu3());
}
@Override
public IPagingProvider getPagingProvider() {
return JaxRsPatientRestProviderDstu3.PAGE_PROVIDER;
}
}

View File

@ -0,0 +1,258 @@
package ca.uhn.fhir.jaxrs.server.example;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.hl7.fhir.dstu3.model.Condition;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
/**
* A demo JaxRs Patient Rest Provider
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
@Local
@Path(JaxRsPatientRestProviderDstu3.PATH)
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsPatientRestProviderDstu3 extends AbstractJaxRsResourceProvider<Patient> {
private static Long counter = 1L;
/**
* The HAPI paging provider for this server
*/
public static final IPagingProvider PAGE_PROVIDER;
static final String PATH = "/Patient";
private static final ConcurrentHashMap<String, List<Patient>> patients = new ConcurrentHashMap<String, List<Patient>>();
static {
PAGE_PROVIDER = new FifoMemoryPagingProvider(10);
}
static {
patients.put(String.valueOf(counter), createPatient("Van Houte"));
patients.put(String.valueOf(counter), createPatient("Agnew"));
for (int i = 0; i < 20; i++) {
patients.put(String.valueOf(counter), createPatient("Random Patient " + counter));
}
}
public JaxRsPatientRestProviderDstu3() {
super(FhirContext.forDstu3(), JaxRsPatientRestProviderDstu3.class);
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional) throws Exception {
patients.put("" + counter, createPatient(patient));
final MethodOutcome result = new MethodOutcome().setCreated(true);
result.setResource(patient);
result.setId(new IdType(patient.getId()));
return result;
}
@Delete
public MethodOutcome delete(@IdParam final IdType theId) {
final Patient deletedPatient = find(theId);
patients.remove(deletedPatient.getIdElement().getIdPart());
final MethodOutcome result = new MethodOutcome().setCreated(true);
result.setResource(deletedPatient);
return result;
}
@Read
public Patient find(@IdParam final IdType theId) {
if (patients.containsKey(theId.getIdPart())) {
return getLast(patients.get(theId.getIdPart()));
} else {
throw new ResourceNotFoundException(theId);
}
}
@Read(version = true)
public Patient findHistory(@IdParam final IdType theId) {
if (patients.containsKey(theId.getIdPart())) {
final List<Patient> list = patients.get(theId.getIdPart());
for (final Patient patient : list) {
if (patient.getIdElement().getVersionIdPartAsLong().equals(theId.getVersionIdPartAsLong())) {
return patient;
}
}
}
throw new ResourceNotFoundException(theId);
}
@Operation(name = "firstVersion", idempotent = true, returnParameters = { @OperationParam(name = "return", type = StringType.class) })
public Parameters firstVersion(@IdParam final IdType theId, @OperationParam(name = "dummy") StringType dummyInput) {
Parameters parameters = new Parameters();
Patient patient = find(new IdType(theId.getResourceType(), theId.getIdPart(), "0"));
parameters.addParameter().setName("return").setResource(patient).setValue(new StringType((counter - 1) + "" + "inputVariable [ " + dummyInput.getValue() + "]"));
return parameters;
}
@Override
public AddProfileTagEnum getAddProfileTag() {
return AddProfileTagEnum.NEVER;
}
@Override
public BundleInclusionRule getBundleInclusionRule() {
return BundleInclusionRule.BASED_ON_INCLUDES;
}
@Override
public ETagSupportEnum getETagSupport() {
return ETagSupportEnum.DISABLED;
}
/** THE DEFAULTS */
@Override
public List<IServerInterceptor> getInterceptors() {
return Collections.emptyList();
}
private Patient getLast(final List<Patient> list) {
return list.get(list.size() - 1);
}
@Override
public IPagingProvider getPagingProvider() {
return PAGE_PROVIDER;
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Override
public boolean isDefaultPrettyPrint() {
return true;
}
@Override
public boolean isUseBrowserFriendlyContentTypes() {
return true;
}
@GET
@Path("/{id}/$firstVersion")
public Response operationFirstVersionUsingGet(@PathParam("id") String id) throws IOException {
return customOperation(null, RequestTypeEnum.GET, id, "$firstVersion", RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@POST
@Path("/{id}/$firstVersion")
public Response operationFirstVersionUsingGet(@PathParam("id") String id, final String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$firstVersion", RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
final List<Patient> result = new LinkedList<Patient>();
for (final List<Patient> patientIterator : patients.values()) {
Patient single = null;
for (Patient patient : patientIterator) {
if (name == null || patient.getName().get(0).getFamily().get(0).getValueNotNull().equals(name.getValueNotNull())) {
single = patient;
}
}
if (single != null) {
result.add(single);
}
}
return result;
}
@Search(compartmentName = "Condition")
public List<IBaseResource> searchCompartment(@IdParam IdType thePatientId) {
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
Condition condition = new Condition();
condition.setId(new IdType("665577"));
retVal.add(condition);
return retVal;
}
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient) {
final String idPart = theId.getIdPart();
if (patients.containsKey(idPart)) {
final List<Patient> patientList = patients.get(idPart);
final Patient lastPatient = getLast(patientList);
patient.setId(createId(theId.getIdPartAsLong(), lastPatient.getIdElement().getVersionIdPartAsLong() + 1));
patientList.add(patient);
final MethodOutcome result = new MethodOutcome().setCreated(false);
result.setResource(patient);
result.setId(new IdType(patient.getId()));
return result;
} else {
throw new ResourceNotFoundException(theId);
}
}
private static IdType createId(final Long id, final Long theVersionId) {
return new IdType("Patient", "" + id, "" + theVersionId);
}
private static List<Patient> createPatient(final Patient patient) {
patient.setId(createId(counter, 1L));
final LinkedList<Patient> list = new LinkedList<Patient>();
list.add(patient);
counter++;
return list;
}
private static List<Patient> createPatient(final String name) {
final Patient patient = new Patient();
patient.getName().add(new HumanName().addFamily(name));
return createPatient(patient);
}
}

View File

@ -0,0 +1,315 @@
package ca.uhn.fhir.jaxrs.server.example;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class JaxRsPatientProviderDstu3Test {
private static IGenericClient client;
private static final FhirContext ourCtx = FhirContext.forDstu3();
private static final String PATIENT_NAME = "Van Houte";
private static int ourPort;
private static Server jettyServer;
@BeforeClass
public static void setUpClass()
throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
System.out.println(ourPort);
jettyServer = new Server(ourPort);
jettyServer.setHandler(context);
ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
//@formatter:off
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
StringUtils.join(Arrays.asList(
JaxRsConformanceProviderDstu3.class.getCanonicalName(),
JaxRsPatientRestProviderDstu3.class.getCanonicalName(),
JaxRsPageProviderDstu3.class.getCanonicalName()
), ";"));
//@formatter:on
jettyServer.start();
ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx));
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
client.setEncoding(EncodingEnum.JSON);
client.registerInterceptor(new LoggingInterceptor(true));
}
@AfterClass
public static void tearDownClass()
throws Exception {
try {
jettyServer.destroy();
}
catch (Exception e) {
}
}
/** Search/Query - Type */
@Test
public void findUsingGenericClientBySearch() {
// Perform a search
final Bundle results = client.search().forResource(Patient.class)
.where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).returnBundle(Bundle.class).execute();
System.out.println(results.getEntry().get(0));
assertEquals(results.getEntry().size(), 1);
}
/** Search - Multi-valued Parameters (ANY/OR) */
@Test
public void findUsingGenericClientBySearchWithMultiValues() {
final Bundle response = client.search().forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario"))
.and(Patient.ADDRESS.matches().values("Canada"))
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).returnBundle(Bundle.class).execute();
System.out.println(response.getEntry().get(0));
}
/** Search - Paging */
@Test
public void findWithPaging() {
// Perform a search
final Bundle results = client.search().forResource(Patient.class).limitTo(8).returnBundle(Bundle.class).execute();
System.out.println(results.getEntry().size());
if (results.getLink(Bundle.LINK_NEXT) != null) {
// load next page
final Bundle nextPage = client.loadPage().next(results).execute();
System.out.println(nextPage.getEntry().size());
}
}
/** Search using other query options */
public void testOther() {
//missing
}
/** */
@Test
public void testSearchPost() {
Bundle response = client.search()
.forResource("Patient")
.usingStyle(SearchStyleEnum.POST)
.returnBundle(Bundle.class)
.execute();
assertTrue(response.getEntry().size() > 0);
}
/** Search - Compartments */
@Test
public void testSearchCompartements() {
Bundle response = client.search()
.forResource(Patient.class)
.withIdAndCompartment("1", "Condition")
.returnBundle(Bundle.class)
.execute();
assertTrue(response.getEntry().size() > 0);
}
/** Search - Subsetting (_summary and _elements) */
@Test
@Ignore
public void testSummary() {
client.search()
.forResource(Patient.class)
.returnBundle(Bundle.class)
.execute();
}
@Test
public void testCreatePatient() {
final Patient existing = new Patient();
existing.setId((IdType) null);
existing.getName().add(new HumanName().addFamily("Created Patient 54"));
client.setEncoding(EncodingEnum.JSON);
final MethodOutcome results = client.create().resource(existing).prefer(PreferReturnEnum.REPRESENTATION).execute();
System.out.println(results.getId());
final Patient patient = (Patient) results.getResource();
System.out.println(patient);
assertNotNull(client.read(Patient.class, patient.getId()));
client.setEncoding(EncodingEnum.JSON);
}
/** Conditional Creates */
@Test
public void testConditionalCreate() {
final Patient existing = new Patient();
existing.setId((IdType) null);
existing.getName().add(new HumanName().addFamily("Created Patient 54"));
client.setEncoding(EncodingEnum.XML);
final MethodOutcome results = client.create().resource(existing).prefer(PreferReturnEnum.REPRESENTATION).execute();
System.out.println(results.getId());
final Patient patient = (Patient) results.getResource();
client.create()
.resource(patient)
.conditional()
.where(Patient.IDENTIFIER.exactly().identifier(patient.getId()))
.execute();
}
/** Find By Id */
@Test
public void findUsingGenericClientById() {
final Patient results = client.read(Patient.class, "1");
assertEquals(results.getIdElement().getIdPartAsLong().longValue(), 1L);
}
@Test
public void testUpdateById() {
final Patient existing = client.read(Patient.class, "1");
final List<HumanName> name = existing.getName();
name.get(0).addSuffix("The Second");
existing.getName().addAll(name);
client.setEncoding(EncodingEnum.XML);
final MethodOutcome results = client.update("1", existing);
}
@Test
public void testDeletePatient() {
final Patient existing = new Patient();
existing.getName().add(new HumanName().addFamily("Created Patient XYZ"));
final MethodOutcome results = client.create().resource(existing).prefer(PreferReturnEnum.REPRESENTATION).execute();
System.out.println(results.getId());
final Patient patient = (Patient) results.getResource();
client.delete(Patient.class, patient.getId());
try {
client.read(Patient.class, patient.getId());
fail();
}
catch (final Exception e) {
//assertEquals(e.getStatusCode(), Constants.STATUS_HTTP_404_NOT_FOUND);
}
}
/** Transaction - Server */
@Ignore
@Test
public void testTransaction() {
Bundle bundle = new Bundle();
BundleEntryComponent entry = bundle.addEntry();
final Patient existing = new Patient();
existing.getName().get(0).addFamily("Created with bundle");
entry.setResource(existing);
// FIXME ?
// BoundCodeDt<BundleEntryTransactionMethodEnum> theTransactionOperation =
// new BoundCodeDt(
// BundleEntryTransactionMethodEnum.VALUESET_BINDER,
// BundleEntryTransactionMethodEnum.POST);
// entry.setTransactionMethod(theTransactionOperation);
Bundle response = client.transaction().withBundle(bundle).execute();
}
/** Conformance - Server */
@Test
@Ignore
public void testConformance() {
final Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
System.out.println(conf.getRest().get(0).getResource().get(0).getType());
assertEquals(conf.getRest().get(0).getResource().get(0).getType().toString(), "Patient");
}
/** Extended Operations */
// Create a client to talk to the HeathIntersections server
@Test
public void testExtendedOperations() {
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateType("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateType("2015-03-01"));
inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdType("Patient", "1"))
.named("$firstVersion")
.withParameters(inParams)
//.useHttpGet() // Use HTTP GET instead of POST
.execute();
String resultValue = outParams.getParameter().get(0).getValue().toString();
System.out.println(resultValue);
assertEquals("expected but found : "+ resultValue, resultValue.contains("myAwesomeDummyValue"), true);
}
@Test
public void testExtendedOperationsUsingGet() {
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateType("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateType("2015-03-01"));
inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdType("Patient", "1"))
.named("$firstVersion")
.withParameters(inParams)
.useHttpGet() // Use HTTP GET instead of POST
.execute();
String resultValue = outParams.getParameter().get(0).getValue().toString();
System.out.println(resultValue);
assertEquals("expected but found : "+ resultValue, resultValue.contains("myAwesomeDummyValue"), true);
}
@Test
public void testVRead() {
final Patient patient = client.vread(Patient.class, "1", "1");
System.out.println(patient);
}
@Test
public void testRead() {
final Patient patient = client.read(Patient.class, "1");
System.out.println(patient);
}
}