diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 7aa3bda5ab6..042333a6197 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -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 conformance = (Class) myContext.getResourceDefinition("Conformance").getImplementingClass(); ResourceResponseHandler binding = new ResourceResponseHandler(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 handler = new ResourceResponseHandler(null, null); - BaseOperationOutcome outcome = invoke(null, handler, invocation); + IBaseOperationOutcome outcome = invoke(null, handler, invocation); MethodOutcome retVal = new MethodOutcome(); retVal.setOperationOutcome(outcome); return retVal; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java index 4c27657931e..4bd785e753b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java @@ -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 diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java new file mode 100644 index 00000000000..fc76589cacb --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java @@ -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 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 newParams = new ArrayList(); + 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; + } + + +} diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index f8643f80eeb..df4a15d0587 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -40,6 +40,11 @@ hapi-fhir-structures-dstu2 1.6-SNAPSHOT + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu3 + 1.6-SNAPSHOT + javax.ws.rs diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java index 24d29ab438b..fd34c238c80 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java @@ -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 getResourceType() { - return Conformance.class; + public Class 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; } } diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java index 6de4af611ed..0da2522045d 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java @@ -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 { diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java index 3cf0b43067a..d5a52771419 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java @@ -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 diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java index 91b76b1160c..204b9e39e33 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java @@ -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 extends AbstractJaxRsProvider +public abstract class AbstractJaxRsResourceProvider extends AbstractJaxRsProvider implements IRestfulServer, IResourceProvider { /** the method bindings for this class */ @@ -73,9 +72,19 @@ public abstract class AbstractJaxRsResourceProvider 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 extends * @param theProviderClass the interface of the class */ protected AbstractJaxRsResourceProvider(Class 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 theProviderClass) { + super(ctx); + theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass); + } + /** * The base for request for a resource provider has the following form:
* {@link AbstractJaxRsResourceProvider#getBaseForServer() diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java index ffc806dcf29..46e0d3ecce7 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java @@ -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)); + } } } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java new file mode 100644 index 00000000000..ae226b5203f --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java @@ -0,0 +1,2207 @@ +package ca.uhn.fhir.jaxrs.client; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent; +import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; +import org.hl7.fhir.dstu3.model.CodeType; +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.InstantType; +import org.hl7.fhir.dstu3.model.Meta; +import org.hl7.fhir.dstu3.model.Observation; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory; +import ca.uhn.fhir.jaxrs.server.test.RandomServerPortProvider; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.primitive.UriDt; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.IGenericClient; +import ca.uhn.fhir.rest.client.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.method.SearchStyleEnum; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.EncodingEnum; + +public class GenericJaxRsClientDstu3Test { + private static FhirContext ourCtx; + private static int ourPort; + private static Server ourServer; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericJaxRsClientDstu3Test.class); + private static int ourResponseCount = 0; + private static String[] ourResponseBodies; + private static String ourResponseBody; + private static String ourResponseContentType; + private static int ourResponseStatus; + private static String ourRequestUri; + + @Before + public void before() { + JaxRsRestfulClientFactory clientFactory = new JaxRsRestfulClientFactory(ourCtx); + clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); + + ourCtx.setRestfulClientFactory(clientFactory); + ourResponseCount = 0; + } + + private String getPatientFeedWithOneResult() { + //@formatter:off + String msg = "\n" + + "d039f91a-cc3c-4013-988e-af4d8d0614bd\n" + + "\n" + + "" + + "" + + "
John Cardinal: 444333333
" + + "" + + "" + + "" + + "" + + "
" + + "
" + + "
\n" + + "
\n" + + "
"; + //@formatter:on + return msg; + } + + @Test + public void testAcceptHeaderFetchConformance() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final String respString = p.encodeResourceToString(conf); + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + client.fetchConformance().ofType(Conformance.class).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(1, ourRequestHeaders.get("Accept").size()); + assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON)); + + + client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); + assertEquals(1, ourRequestHeaders.get("Accept").size()); + assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + + + client.fetchConformance().ofType(Conformance.class).encodedXml().execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); + assertEquals(1, ourRequestHeaders.get("Accept").size()); + assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + + } + + @Test + public void testAcceptHeaderPreflightConformance() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final Patient patient = new Patient(); + patient.addName().addFamily("FAMILY"); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBodies = new String[] { p.encodeResourceToString(conf), p.encodeResourceToString(patient) }; + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient resp = client.read(Patient.class, new IdType("123").getValue()); + assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); + assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON)); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); + assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON)); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + } + + @Test + public void testAcceptHeaderPreflightConformancePreferJson() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final Patient patient = new Patient(); + patient.addName().addFamily("FAMILY"); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBodies = new String[] { p.encodeResourceToString(conf), p.encodeResourceToString(patient) }; + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + client.setEncoding(EncodingEnum.JSON); + + Patient resp = client.read(Patient.class, new IdType("123").getValue()); + assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); + assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); + assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + } + + @Test + @SuppressWarnings("deprecation") + public void testConformance() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final String respString = p.encodeResourceToString(conf); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Conformance resp = (Conformance)client.conformance(); + + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals("COPY", resp.getCopyright()); + assertEquals("GET", ourRequestMethod); + + + } + + @Test + public void testProviderWhereWeForgotToSetTheContext() throws Exception { + JaxRsRestfulClientFactory clientFactory = new JaxRsRestfulClientFactory(); // no ctx + clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); + + ourCtx.setRestfulClientFactory(clientFactory); + + try { + ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + fail(); + } catch (IllegalStateException e) { + assertEquals("JaxRsRestfulClientFactory does not have FhirContext defined. This must be set via JaxRsRestfulClientFactory#setFhirContext(FhirContext)", e.getMessage()); + } + } + + + @Test + public void testCreate() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create().resource(p).execute(); + + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("POST", ourRequestMethod); + + + p.setId("123"); + + client.create().resource(p).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + String body = ourRequestBodyString; + assertThat(body, containsString("")); + assertThat(body, not(containsString("123"))); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("POST", ourRequestMethod); + + + } + + @Test + public void testCreateConditional() throws Exception { + + + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create().resource(p).conditionalByUrl("Patient?name=foo").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", ourRequestMethod); + + + client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", ourRequestMethod); + + + client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", ourRequestMethod); + + + } + + @SuppressWarnings("deprecation") + @Test + public void testCreateNonFluent() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create(p); + + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("POST", ourRequestMethod); + + } + + @Test + public void testCreatePrefer() throws Exception { + + + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + + + client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + + + } + + @Test + public void testCreateReturningResourceBody() throws Exception { + Patient p = new Patient(); + p.setId("123"); + final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); + + + + ourResponseStatus = Constants.STATUS_HTTP_200_OK; + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = formatted; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + p = new Patient(); + p.setId(new IdType("1")); + p.addName().addFamily("FOOFAMILY"); + + MethodOutcome output = client.create().resource(p).execute(); + assertNotNull(output.getResource()); + assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue()); + } + + @Test + public void testDeleteConditional() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + client.delete().resourceById(new IdType("Patient/123")).execute(); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + + + client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + + client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + + } + + @SuppressWarnings("deprecation") + @Test + public void testDeleteNonFluent() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + client.delete(Patient.class, new IdType("Patient/123").getValue()); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + + + client.delete(Patient.class, "123"); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + + + } + + @Test + public void testHistory() throws Exception { + + final String msg = getPatientFeedWithOneResult(); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + org.hl7.fhir.dstu3.model.Bundle response; + + //@formatter:off + response = client + .history() + .onServer() + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + + //@formatter:off + response = client + .history() + .onServer() + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .since((Date)null) + .count(null) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + + //@formatter:off + response = client + .history() + .onServer() + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .since(new InstantType()) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + + //@formatter:off + response = client + .history() + .onType(Patient.class) + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + + //@formatter:off + response = client + .history() + .onInstance(new IdType("Patient", "123")) + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + + //@formatter:off + response = client + .history() + .onInstance(new IdType("Patient", "123")) + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .count(123) + .since(new InstantType("2001-01-02T11:22:33Z")) + .execute(); + //@formatter:on + assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertEquals(1, response.getEntry().size()); + + + //@formatter:off + response = client + .history() + .onInstance(new IdType("Patient", "123")) + .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .since(new InstantType("2001-01-02T11:22:33Z").getValue()) + .execute(); + //@formatter:on + assertThat(ourRequestUri, containsString("_since=2001-01")); + assertEquals(1, response.getEntry().size()); + + } + + @Test + public void testMetaAdd() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Meta inMeta = new Meta().addProfile("urn:profile:in"); + + Parameters outParams = new Parameters(); + outParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:out")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Meta resp = client + .meta() + .add() + .onResource(new IdType("Patient/123")) + .meta(inMeta) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + + + } + + @Test + public void testMetaGet() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:in")); + + Parameters outParams = new Parameters(); + outParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:out")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Meta resp = client + .meta() + .get(Meta.class) + .fromServer() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("GET", ourRequestMethod); + + + //@formatter:off + resp = client + .meta() + .get(Meta.class) + .fromType("Patient") + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("GET", ourRequestMethod); + + + //@formatter:off + resp = client + .meta() + .get(Meta.class) + .fromResource(new IdType("Patient/123")) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("GET", ourRequestMethod); + + + } + + @Test + public void testOperationAsGetWithInParameters() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("param1").setValue(new StringType("STRINGVALIN1")); + inParams.addParameter().setName("param1").setValue(new StringType("STRINGVALIN1b")); + inParams.addParameter().setName("param2").setValue(new StringType("STRINGVALIN2")); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringType("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onInstance(new IdType("Patient", "123")) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + + // @formatter:off + resp = client + .operation() + .onInstance(new IdType("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + + } + + @Test + public void testOperationAsGetWithNoInParameters() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringType("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onInstance(new IdType("Patient", "123")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + + // @formatter:off + resp = client + .operation() + .onInstance(new IdType("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + + } + + @Test + public void testOperationWithBundleResponseJson() throws Exception { + + ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + "}"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + 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")); + + // Invoke $everything on "Patient/1" + Parameters outParams = client.operation().onInstance(new IdType("Patient", "18066")).named("$everything").withParameters(inParams).execute(); + + /* + * Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation + * methods return a Parameters instance however, so HAPI creates a Parameters object + * with a single parameter containing the value. + */ + org.hl7.fhir.dstu3.model.Bundle responseBundle = (org.hl7.fhir.dstu3.model.Bundle) outParams.getParameter().get(0).getResource(); + + // Print the response bundle + assertEquals("8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19", responseBundle.getId()); + } + + @Test + public void testOperationWithBundleResponseXml() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setValue(new StringType("STRINGVALIN1")); + inParams.addParameter().setValue(new StringType("STRINGVALIN2")); + String reqString = p.encodeResourceToString(inParams); + + org.hl7.fhir.dstu3.model.Bundle outParams = new org.hl7.fhir.dstu3.model.Bundle(); + outParams.setTotal(123); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + assertEquals(1, resp.getParameter().size()); + assertEquals(org.hl7.fhir.dstu3.model.Bundle.class, resp.getParameter().get(0).getResource().getClass()); + + } + + @Test + public void testOperationWithInlineParams() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringType("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new StringType("value1")) + .andParameter("name2", new StringType("value1")) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", ourRequestMethod); + assertEquals("", (ourRequestBodyString)); + + + /* + * Composite type + */ + + //@formatter:off + resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new Identifier().setSystem("system1").setValue("value1")) + .andParameter("name2", new StringType("value1")) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", ourRequestMethod); + assertEquals("", + (ourRequestBodyString)); + + + /* + * Resource + */ + + //@formatter:off + resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new Identifier().setSystem("system1").setValue("value1")) + .andParameter("name2", new Patient().setActive(true)) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", ourRequestMethod); + assertEquals( + "", + (ourRequestBodyString)); + + + } + + @Test(expected = IllegalArgumentException.class) + public void testOperationWithInvalidParam() { + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + // Who knows what the heck this is! + IBase weirdBase = new IBase() { + private static final long serialVersionUID = 1L; + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean hasFormatComment() { + return false; + } + + @Override + public List getFormatCommentsPre() { + return null; + } + + @Override + public List getFormatCommentsPost() { + return null; + } + }; + + //@formatter:off + client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", weirdBase) + .execute(); + //@formatter:on + } + + @Test + public void testOperationWithProfiledDatatypeParam() throws IOException, Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringType("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + client + .operation() + .onInstance(new IdType("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://loinc.org")) + .useHttpGet() + .execute(); + //@formatter:off + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + + //@formatter:off + + client + .operation() + .onInstance(new IdType("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://loinc.org")) + .execute(); + //@formatter:off + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); + ourLog.info(ourRequestBodyString); + assertEquals("",ourRequestBodyString); + + } + + @Test + public void testOperationWithListOfParameterResponse() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setValue(new StringType("STRINGVALIN1")); + inParams.addParameter().setValue(new StringType("STRINGVALIN2")); + String reqString = p.encodeResourceToString(inParams); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringType("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onInstance(new IdType("Patient", "123")) + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + + resp = client.operation().onInstance(new IdType("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + + } + + @Test + public void testOperationWithNoInParameters() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + final String reqString = p.encodeResourceToString(inParams); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringType("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + + //@formatter:off + resp = client + .operation() + .onInstance(new IdType("Patient", "123")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + + // @formatter:off + resp = client + .operation() + .onInstance(new IdType("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + + } + + @Test + public void testPageNext() throws Exception { + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = getPatientFeedWithOneResult(); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + + //@formatter:off + org.hl7.fhir.dstu3.model.Bundle resp = client + .loadPage() + .next(sourceBundle) + .execute(); + //@formatter:on + + assertEquals(1, resp.getEntry().size()); + assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + + + } + + @Test + public void testPageNextNoLink() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); + try { + client.loadPage().next(sourceBundle).execute(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("Can not perform paging operation because no link was found in Bundle with relation \"next\"")); + } + } + + @Test + public void testPagePrev() throws Exception { + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = getPatientFeedWithOneResult(); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); + sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + + //@formatter:off + org.hl7.fhir.dstu3.model.Bundle resp = client + .loadPage() + .previous(sourceBundle) + .execute(); + //@formatter:on + + assertEquals(1, resp.getEntry().size()); + assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + + + /* + * Try with "prev" instead of "previous" + */ + + sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); + sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + + //@formatter:off + resp = client + .loadPage() + .previous(sourceBundle) + .execute(); + //@formatter:on + + assertEquals(1, resp.getEntry().size()); + assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + + + } + + @Test + public void testReadByUri() throws Exception { + + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient response; + + + response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); + } + + @Test + public void testReadFluentByUri() throws Exception { + + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient response; + + response = (Patient) client.read().resource(Patient.class).withUrl(new IdType("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); + assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); + } + + @Test + public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception { + + //@formatter:off + final String input = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + //@formatter:on + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = input; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + org.hl7.fhir.dstu3.model.Bundle response; + + //@formatter:off + response = client + .search() + .forResource(Patient.class) + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + + // FIXME ? +// assertEquals("2015-06-22T15:48:57.554-04:00", response.getMeta().getLastUpdated().toString()); + assertEquals("Mon Jun 22 13:48:57 MDT 2015", response.getMeta().getLastUpdated().toString()); + } + + @Test + public void testReadWithElementsParam() throws Exception { + String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + IBaseResource response = client.read() + .resource("Patient") + .withId("123") + .elementsSubset("name", "identifier") + .execute(); + //@formatter:on + + assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertEquals(Patient.class, response.getClass()); + + } + + @Test + public void testReadWithSummaryInvalid() throws Exception { + String msg = "<>>>><<<<>"; + + + + + ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + try { + client.read() + .resource(Patient.class) + .withId("123") + .summaryMode(SummaryEnum.TEXT) + .execute(); + fail(); + } catch (InvalidResponseException e) { + assertThat(e.getMessage(), containsString("Unable to Parse HTML - node")); + } + //@formatter:on + } + + @Test + public void testReadWithSummaryParamHtml() throws Exception { + String msg = "
HELP IM A DIV
"; + + + + + ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Patient response = client.read() + .resource(Patient.class) + .withId("123") + .summaryMode(SummaryEnum.TEXT) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(Patient.class, response.getClass()); + assertEquals("
HELP IM A DIV
", response.getText().getDiv().getValueAsString()); + + } + + @Test + public void testSearchByString() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + } + + @Test + public void testSearchByUrl() throws Exception { + + final String msg = getPatientFeedWithOneResult(); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + //@formatter:off + org.hl7.fhir.dstu3.model.Bundle response = client.search() + .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .encodedJson() + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertNotNull(response); + + + //@formatter:off + response = client.search() + .byUrl("Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertNotNull(response); + + + //@formatter:off + response = client.search() + .byUrl("/Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertNotNull(response); + + + //@formatter:off + response = client.search() + .byUrl("Patient") + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertNotNull(response); + + + //@formatter:off + response = client.search() + .byUrl("Patient?") + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertNotNull(response); + + + try { + client.search().byUrl("foo/bar?test=1"); + } catch (IllegalArgumentException e) { + assertEquals("Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]", e.getMessage()); + } + } + + /** + * See #191 + */ + @Test + public void testSearchReturningDstu2Bundle() throws Exception { + String msg = IOUtils.toString(GenericJaxRsClientDstu3Test.class.getResourceAsStream("/bundle_orion.xml")); + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + org.hl7.fhir.dstu3.model.Bundle response = client.search() + .forResource("Observation") + .where(Patient.NAME.matches().value("FOO")) + .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) + .execute(); + //@formatter:on + + BundleLinkComponent link = response.getLink().get(0); + assertEquals("just trying add link", link.getRelation()); + assertEquals("blarion", link.getUrl()); + + BundleEntryComponent entry = response.getEntry().get(0); + link = entry.getLink().get(0); + assertEquals("orionhealth.edit", link.getRelation()); + assertEquals("Observation", link.getUrl()); + } + + @Test + public void testSearchWithElementsParam() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + } + + @Test + public void testSearchByPost() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + + // assertThat(ourRequestUri, + // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + assertEquals("name=james", ourRequestBodyString); + + assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + } + + @Test + public void testSearchByPostUseJson() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .encodedJson() + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); + assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(ourRequestUri, containsString("_format=json")); + + // assertThat(ourRequestUri, + // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + assertEquals("name=james", ourRequestBodyString); + + assertEquals("application/x-www-form-urlencoded", ourRequestContentType); + assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + } + + @Test + public void testSearchWithLastUpdated() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .lastUpdated(new DateRangeParam("2011-01-01", "2012-01-01")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + } + + @Test + public void testSearchWithProfileAndSecurity() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .withProfile("http://foo1") + .withProfile("http://foo2") + .withSecurity("system1", "code1") + .withSecurity("system2", "code2") + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithReverseInclude() throws Exception { + + String msg = getPatientFeedWithOneResult(); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource(Patient.class) + .encodedJson() + .revInclude(new Include("Provenance:target")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + + } + + @Test + public void testSearchWithSummaryParam() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .summaryMode(SummaryEnum.FALSE) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); + + } + + @Test + public void testTransactionWithListOfResources() throws Exception { + + org.hl7.fhir.dstu3.model.Bundle resp = new org.hl7.fhir.dstu3.model.Bundle(); + resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); + String respString = ourCtx.newJsonParser().encodeResourceToString(resp); + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + List input = new ArrayList(); + + Patient p1 = new Patient(); // No ID + p1.addName().addFamily("PATIENT1"); + input.add(p1); + + Patient p2 = new Patient(); // Yes ID + p2.addName().addFamily("PATIENT2"); + p2.setId("Patient/2"); + input.add(p2); + + //@formatter:off + List response = client.transaction() + .withResources(input) + .encodedJson() + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir?_format=json", ourRequestUri); + assertEquals(2, response.size()); + + String requestString = ourRequestBodyString; + org.hl7.fhir.dstu3.model.Bundle requestBundle = ourCtx.newJsonParser().parseResource(org.hl7.fhir.dstu3.model.Bundle.class, requestString); + assertEquals(2, requestBundle.getEntry().size()); + assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod()); + assertEquals(HTTPVerb.PUT, requestBundle.getEntry().get(1).getRequest().getMethod()); + assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); + + p1 = (Patient) response.get(0); + assertEquals(new IdType("Patient/1/_history/1"), p1.getIdElement()); + // assertEquals("PATIENT1", p1.getName().get(0).getFamily().get(0).getValue()); + + p2 = (Patient) response.get(1); + assertEquals(new IdType("Patient/2/_history/2"), p2.getIdElement()); + // assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue()); + } + + @Test + public void testTransactionWithString() throws Exception { + + org.hl7.fhir.dstu3.model.Bundle req = new org.hl7.fhir.dstu3.model.Bundle(); + Patient patient = new Patient(); + patient.addName().addFamily("PAT_FAMILY"); + req.addEntry().setResource(patient); + Observation observation = new Observation(); + observation.getCode().setText("OBS_TEXT"); + req.addEntry().setResource(observation); + String reqString = ourCtx.newJsonParser().encodeResourceToString(req); + + org.hl7.fhir.dstu3.model.Bundle resp = new org.hl7.fhir.dstu3.model.Bundle(); + resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = reqString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + String response = client.transaction() + .withBundle(reqString) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertThat(response, containsString("\"Bundle\"")); + assertEquals("application/json+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + + //@formatter:off + response = client.transaction() + .withBundle(reqString) + .encodedXml() + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/?_format=xml", ourRequestUri); + assertEquals("application/xml+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + + } + + @Test + public void testTransactionWithTransactionResource() throws Exception { + + org.hl7.fhir.dstu3.model.Bundle resp = new org.hl7.fhir.dstu3.model.Bundle(); + resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); + String respString = ourCtx.newJsonParser().encodeResourceToString(resp); + + + + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + org.hl7.fhir.dstu3.model.Bundle input = new org.hl7.fhir.dstu3.model.Bundle(); + + Patient p1 = new Patient(); // No ID + p1.addName().addFamily("PATIENT1"); + input.addEntry().setResource(p1); + + Patient p2 = new Patient(); // Yes ID + p2.addName().addFamily("PATIENT2"); + p2.setId("Patient/2"); + input.addEntry().setResource(p2); + + //@formatter:off + org.hl7.fhir.dstu3.model.Bundle response = client.transaction() + .withBundle(input) + .encodedJson() + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir?_format=json", ourRequestUri); + assertEquals(2, response.getEntry().size()); + + assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); + assertEquals("Patient/2/_history/2", response.getEntry().get(1).getResponse().getLocation()); + } + + @Test + public void testUpdateConditional() throws Exception { + + + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.update().resource(p).conditionalByUrl("Patient?name=foo").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + + client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + + + client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + + client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + + + client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + + + } + + @Test + public void testUpdateNonFluent() throws Exception { + + + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.update(new IdType("Patient/123").getValue(), p); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("PUT", ourRequestMethod); + + + client.update("123", p); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("PUT", ourRequestMethod); + + } + + @Test + public void testUpdatePrefer() throws Exception { + + + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + + + Patient p = new Patient(); + p.setId(new IdType("1")); + p.addName().addFamily("FOOFAMILY"); + + client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + + + client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + + + } + + @Test + public void testUpdateReturningResourceBody() throws Exception { + Patient p = new Patient(); + p.setId("123"); + final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); + + + + ourResponseStatus = Constants.STATUS_HTTP_200_OK; + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = formatted; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + p = new Patient(); + p.setId(new IdType("1")); + p.addName().addFamily("FOOFAMILY"); + + MethodOutcome output = client.update().resource(p).execute(); + assertNotNull(output.getResource()); + assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue()); + } + + @Test + public void testValidateFluent() throws Exception { + + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("FOOBAR"); + final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); + + + + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addGiven("GIVEN"); + + + MethodOutcome response; + + response = client.validate().resource(p).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); + + + response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); + + + response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=json", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); + + + response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=json&_pretty=true", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertThat(ourRequestBodyString, containsString("\"resourceType\":\"Parameters\",\n")); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); + + } + + @Test + public void testValidateNonFluent() throws Exception { + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("FOOBAR"); + final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addGiven("GIVEN"); + + + MethodOutcome response; + + //@formatter:off + response = client.validate(p); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + assertNotNull(response.getResource()); + assertEquals("FOOBAR", toOo(response.getResource()).getIssue().get(0).getDiagnosticsElement().getValue()); + + } + + private OperationOutcome toOo(IBaseResource theOperationOutcome) { + return (OperationOutcome) theOperationOutcome; + } + + @Before + public void beforeReset() { + ourRequestUri = null; + ourRequestUriAll = Lists.newArrayList(); + ourResponseStatus = 200; + ourResponseBody = null; + ourResponseBodies = null; + ourResponseCount = 0; + + ourResponseContentType = null; + ourRequestContentType = null; + ourRequestBodyBytes = null; + ourRequestBodyString = null; + ourRequestHeaders = null; + ourRequestFirstHeaders = null; + ourRequestMethod = null; + ourRequestHeadersAll = Lists.newArrayList(); + } + + private static List ourRequestUriAll; + private static String ourRequestMethod; + private static String ourRequestContentType; + private static byte[] ourRequestBodyBytes; + private static String ourRequestBodyString; + private static ArrayListMultimap ourRequestHeaders; + private static List> ourRequestHeadersAll; + private static Map ourRequestFirstHeaders; + + @BeforeClass + public static void beforeClass() throws Exception { + ourCtx = FhirContext.forDstu3(); + + ourPort = RandomServerPortProvider.findFreePort(); + ourServer = new Server(ourPort); + ourLog.info("Starting server on port {}", ourPort); + ourServer.setHandler(new AbstractHandler() { + + @Override + public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException, ServletException { + theRequest.setHandled(true); + ourRequestUri = "http:" + theRequest.getHttpURI().toString(); + ourRequestUriAll.add(ourRequestUri); + ourRequestMethod = theRequest.getMethod(); + ourRequestContentType = theServletRequest.getContentType(); + ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); + ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); + + ourRequestHeaders = ArrayListMultimap.create(); + ourRequestHeadersAll.add(ourRequestHeaders); + ourRequestFirstHeaders = Maps.newHashMap(); + + for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { + String nextName = headerNameEnum.nextElement(); + for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { + String nextValue = headerValueEnum.nextElement(); + if (ourRequestFirstHeaders.containsKey(nextName)==false) { + ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); + } + ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); + } + } + + theResp.setStatus(ourResponseStatus); + + if (ourResponseBody != null) { + theResp.setContentType(ourResponseContentType); + theResp.getWriter().write(ourResponseBody); + } else if (ourResponseBodies != null) { + theResp.setContentType(ourResponseContentType); + theResp.getWriter().write(ourResponseBodies[ourResponseCount]); + } + + ourResponseCount++; + } + }); + + ourServer.start(); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java new file mode 100644 index 00000000000..c12f12b0b4d --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java @@ -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, IResourceProvider> providers; + private ContainerRequest headers; + private MultivaluedHashMap 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(); + + + providers = new ConcurrentHashMap, 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(" ")); + assertTrue(response.getEntity().toString().contains("\"$someCustomOperation")); + System.out.println(response.getEntity()); + } + + private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap, IResourceProvider> providers) + throws Exception { + AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu3(), null, null, null) { + @Override + protected ConcurrentHashMap, 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; + } + +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java new file mode 100644 index 00000000000..5eeb72ad589 --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java @@ -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 idCaptor; + private ArgumentCaptor 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 createPatients(int firstId, int lastId) { + List result = new ArrayList(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 { + 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 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) { + + } + } + +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java new file mode 100644 index 00000000000..1cbaff2a585 --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java @@ -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, IResourceProvider> getProviders() { + ConcurrentHashMap, IResourceProvider> map = new ConcurrentHashMap, IResourceProvider>(); + map.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3()); + map.put(TestJaxRsConformanceRestProviderDstu3.class, new TestJaxRsConformanceRestProviderDstu3()); + return map; + } +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java new file mode 100644 index 00000000000..32fa1bc439e --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java @@ -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 { + + public TestJaxRsDummyPatientProviderDstu3() { + super(FhirContext.forDstu3()); + } + + @Override + public Class getResourceType() { + return Patient.class; + } +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java new file mode 100644 index 00000000000..e6351fb47e0 --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java @@ -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; + } + +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java new file mode 100644 index 00000000000..1cf3caed218 --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java @@ -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 { + + 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 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 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 getResourceType() { + return Patient.class; + } + + @Override + public IPagingProvider getPagingProvider() { + return PAGING_PROVIDER; + } + +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsMethodBindingsDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsMethodBindingsDstu3Test.java new file mode 100644 index 00000000000..744b1f32fb7 --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsMethodBindingsDstu3Test.java @@ -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 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 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 search1(@RequiredParam(name = Patient.SP_NAME) final StringParam name) { + return null; + } + + @Search + public List 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 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){ + } + } + +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java new file mode 100644 index 00000000000..87331c24b4d --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java @@ -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 = ""; + private static final String BASEURI = "http://baseuri"; + private static final String REQUESTURI = "http://baseuri/test"; + + private JaxRsRequest details; + private MultivaluedMap queryParameters = new MultivaluedHashMap(); + 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); + } + +} diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java new file mode 100644 index 00000000000..57646f10d8e --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java @@ -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 theSummaryMode; + + @Before + public void setUp() throws URISyntaxException { + request = new JaxRsRequestDstu3Test().createRequestDetails(); + this.response = (JaxRsResponse) request.getResponse(); + bundle = getSinglePatientResource(); + theSummaryMode = Collections.emptySet(); + } + + @Test + public void testGetResponseWriterNoZipNoBrowser() throws IOException { + boolean theRequestIsBrowser = false; + boolean respondGzip = false; + Set theSummaryMode = Collections.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(" 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; + } + +} diff --git a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsConformanceProviderDstu3.java b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsConformanceProviderDstu3.java new file mode 100644 index 00000000000..1ec716d0395 --- /dev/null +++ b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsConformanceProviderDstu3.java @@ -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, IResourceProvider> getProviders() { + ConcurrentHashMap, IResourceProvider> map = new ConcurrentHashMap, IResourceProvider>(); + map.put(JaxRsConformanceProviderDstu3.class, this); + map.put(JaxRsPatientRestProvider.class, patientProvider); + return map; + } +} diff --git a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPageProviderDstu3.java b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPageProviderDstu3.java new file mode 100644 index 00000000000..b3e795dcdd1 --- /dev/null +++ b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPageProviderDstu3.java @@ -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; + } + +} diff --git a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java new file mode 100644 index 00000000000..a625becd8ad --- /dev/null +++ b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java @@ -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 { + + 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> patients = new ConcurrentHashMap>(); + + 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 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 getInterceptors() { + return Collections.emptyList(); + } + + private Patient getLast(final List list) { + return list.get(list.size() - 1); + } + + @Override + public IPagingProvider getPagingProvider() { + return PAGE_PROVIDER; + } + + @Override + public Class 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 search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) { + final List result = new LinkedList(); + for (final List 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 searchCompartment(@IdParam IdType thePatientId) { + List retVal = new ArrayList(); + 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 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 createPatient(final Patient patient) { + patient.setId(createId(counter, 1L)); + final LinkedList list = new LinkedList(); + list.add(patient); + counter++; + return list; + } + + private static List createPatient(final String name) { + final Patient patient = new Patient(); + patient.getName().add(new HumanName().addFamily(name)); + return createPatient(patient); + } + +} diff --git a/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java b/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java new file mode 100644 index 00000000000..bc01e6e6755 --- /dev/null +++ b/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java @@ -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 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 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); + } + +}