From 31d16fca0325075f359729b71fd5b8b35c1c7676 Mon Sep 17 00:00:00 2001 From: Phillip Warner Date: Fri, 29 Apr 2016 12:50:15 -0600 Subject: [PATCH 1/4] Updated jaxrsserver base and example for DSTU3 Added support for specifying DSTU3 in the constructors, where the default is DSTU2, to maintain backwards compatibility. Updated interface usage to IBase... (e.g., IResource -> IBaseResource), which is compatible with DSTU3. Added tests specifically for DSTU3; kept the existing tests as DSTU2. Provided examples for DSTU3. --- .../uhn/fhir/rest/client/GenericClient.java | 7 +- .../uhn/fhir/rest/client/IGenericClient.java | 3 +- .../method/ValidateMethodBindingDstu3.java | 84 + hapi-fhir-jaxrsserver-base/pom.xml | 5 + .../AbstractJaxRsConformanceProvider.java | 54 +- .../server/AbstractJaxRsPageProvider.java | 14 + .../jaxrs/server/AbstractJaxRsProvider.java | 18 +- .../server/AbstractJaxRsResourceProvider.java | 34 +- .../fhir/jaxrs/server/util/JaxRsRequest.java | 25 +- .../client/GenericJaxRsClientDstu3Test.java | 2207 +++++++++++++++++ ...ractJaxRsConformanceProviderDstu3Test.java | 110 + ...bstractJaxRsResourceProviderDstu3Test.java | 443 ++++ ...TestJaxRsConformanceRestProviderDstu3.java | 34 + .../TestJaxRsDummyPatientProviderDstu3.java | 21 + .../test/TestJaxRsMockPageProviderDstu3.java | 26 + ...TestJaxRsMockPatientRestProviderDstu3.java | 141 ++ .../util/JaxRsMethodBindingsDstu3Test.java | 129 + .../server/util/JaxRsRequestDstu3Test.java | 119 + .../server/util/JaxRsResponseDstu3Test.java | 159 ++ .../JaxRsConformanceProviderDstu3.java | 46 + .../example/JaxRsPageProviderDstu3.java | 27 + .../JaxRsPatientRestProviderDstu3.java | 258 ++ .../JaxRsPatientProviderDstu3Test.java | 315 +++ 23 files changed, 4255 insertions(+), 24 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsMethodBindingsDstu3Test.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java create mode 100644 hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java create mode 100644 hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsConformanceProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPageProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java create mode 100644 hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java 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); + } + +} From 5cb211886e1dda5c799efde25f5de9310be9c261 Mon Sep 17 00:00:00 2001 From: Phillip Warner Date: Fri, 29 Apr 2016 14:18:44 -0600 Subject: [PATCH 2/4] Use getValueAsString() instead of toString(). Simple oversight, where toString() on the java.util.Date lastUpdated was wrong. Needed to getLastUpdatedElement().getValueAsString(). --- .../ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 index ae226b5203f..a40f160b20d 100644 --- 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 @@ -1329,9 +1329,7 @@ public class GenericJaxRsClientDstu3Test { .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()); + assertEquals("2015-06-22T15:48:57.554-04:00", response.getMeta().getLastUpdatedElement().getValueAsString()); } @Test From ff562a3f007d5660120a38f5a9511e440b03f4b4 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 4 May 2016 10:07:35 -0400 Subject: [PATCH 3/4] Fix #360 - Correctly parse spaces in URL parameters --- .../ca/uhn/fhir/rest/server/RestfulServer.java | 8 +++++++- .../src/main/java/ca/uhn/fhir/util/UrlUtil.java | 17 ++++++++++------- .../fhir/rest/server/ServerSearchDstu2Test.java | 11 +++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 70e44e23ddd..4f4817b9f5c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -151,7 +151,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer */ public void addHeadersToResponse(HttpServletResponse theHttpResponse) { - theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server"); + theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " REST Server (FHIR Server; FHIR " + myFhirContext.getVersion().getVersion().name() + ")"); } private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) { @@ -570,6 +570,12 @@ public class RestfulServer extends HttpServlet implements IRestfulServer params = null; if (StringUtils.isNotBlank(theRequest.getQueryString())) { completeUrl = requestUrl + "?" + theRequest.getQueryString(); + /* + * By default, we manually parse the request params (the URL params, or the body for + * POST form queries) since Java containers can't be trusted to use UTF-8 encoding + * when parsing. Specifically Tomcat 7 and Glassfish 4.0 use 8859-1 for some dumb + * reason.... grr..... + */ if (isIgnoreServerParsedRequestParameters()) { String contentType = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE); if (theRequestType == RequestTypeEnum.POST && isNotBlank(contentType) && contentType.startsWith(Constants.CT_X_FORM_URLENCODED)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index 6f0261e93f0..36e5c700787 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -289,14 +289,17 @@ public class UrlUtil { if (theString == null) { return null; } - if (theString.indexOf('%') == -1) { - return theString; - } - try { - return URLDecoder.decode(theString, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new Error("UTF-8 not supported, this shouldn't happen", e); + for (int i = 0; i < theString.length(); i++) { + char nextChar = theString.charAt(i); + if (nextChar == '%' || nextChar == '+') { + try { + return URLDecoder.decode(theString, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new Error("UTF-8 not supported, this shouldn't happen", e); + } + } } + return theString; } public static class UrlParts { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java index 652f32df1cc..98e5ce60857 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java @@ -81,6 +81,17 @@ public class ServerSearchDstu2Test { assertEquals("param2value", ourLastRef.getValue()); } + @Test + public void testSearchParamWithSpace() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param2=param+value&foo=bar"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals("searchParam2", ourLastMethod); + assertEquals("param value", ourLastRef.getValue()); + } + @Test public void testUnknownSearchParam() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?foo=bar"); From 82c1e687fd1964d03a00a3da881e2b14ec536f8f Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 4 May 2016 11:48:50 -0400 Subject: [PATCH 4/4] Fix NPE in LoggingInterceptor --- .../interceptor/LoggingInterceptor.java | 55 +++++++----- .../server/servlet/ServletRequestDetails.java | 6 +- .../java/ca/uhn/fhir/jpa/dao/PathAndRef.java | 20 +++++ .../dao/dstu3/FhirResourceDaoDstu3Test.java | 52 ++++++------ ...roviderQuestionnaireResponseDstu3Test.java | 84 ++++++++++++++++++ .../ca/uhn/fhirtest/config/CommonConfig.java | 2 +- .../src/main/resources/logback.xml | 2 +- .../LoggingInterceptorDstu2Test.java | 85 +++++++++++++++++-- .../DefaultProfileValidationSupport.java | 3 + .../dstu3/validation/InstanceValidator.java | 2 +- .../uhn/fhir/parser/XmlParserDstu3Test.java | 4 +- 11 files changed, 254 insertions(+), 61 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java index 39d9faa90a4..436fd42eb6e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.server.interceptor; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + import java.io.IOException; /* @@ -31,12 +33,10 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.Charsets; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.text.StrLookup; import org.apache.commons.lang3.text.StrSubstitutor; -import org.apache.http.util.EncodingUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,15 +58,18 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; * * * ${idOrResourceName} - * The resource ID associated with this request, or the resource name if the request applies to a type but not an instance, or "" otherwise + * The resource ID associated with this request, or the resource name if the request applies to a type but not an + * instance, or "" otherwise * * * ${operationName} - * If the request is an extended operation (e.g. "$validate") this value will be the operation name, or "" otherwise + * If the request is an extended operation (e.g. "$validate") this value will be the operation name, or "" + * otherwise * * * ${operationType} - * A code indicating the operation type for this request, e.g. "read", "history-instance", "extended-operation-instance", etc.) + * A code indicating the operation type for this request, e.g. "read", "history-instance", + * "extended-operation-instance", etc.) * * * ${remoteAddr} @@ -74,7 +77,8 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; * * * ${requestHeader.XXXX} - * The value of the HTTP request header named XXXX. For example, a substitution variable named "${requestHeader.x-forwarded-for} will yield the value of the first header named "x-forwarded-for + * The value of the HTTP request header named XXXX. For example, a substitution variable named + * "${requestHeader.x-forwarded-for} will yield the value of the first header named "x-forwarded-for * ", or "" if none. * * @@ -83,15 +87,18 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; * * * ${responseEncodingNoDefault} - * The encoding format requested by the client via the _format parameter or the Accept header. Value will be "json" or "xml", or "" if the client did not explicitly request a format + * The encoding format requested by the client via the _format parameter or the Accept header. Value will be "json" + * or "xml", or "" if the client did not explicitly request a format * * * ${servletPath} - * The part of thre requesting URL that corresponds to the particular Servlet being called (see {@link HttpServletRequest#getServletPath()}) + * The part of thre requesting URL that corresponds to the particular Servlet being called (see + * {@link HttpServletRequest#getServletPath()}) * * * ${requestBodyFhir} - * The complete body of the request if the request has a FHIR content-type (this can be quite large!). Will emit an empty string if the content type is not a FHIR content type + * The complete body of the request if the request has a FHIR content-type (this can be quite large!). Will emit an + * empty string if the content type is not a FHIR content type * * * ${requestUrl} @@ -122,10 +129,9 @@ public class LoggingInterceptor extends InterceptorAdapter { public String getErrorMessageFormat() { return myErrorMessageFormat; } - + @Override - public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws ServletException, IOException { + public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException { if (myLogExceptions) { // Perform any string substitutions from the message format StrLookup lookup = new MyLookup(theServletRequest, theException, theRequestDetails); @@ -187,7 +193,8 @@ public class LoggingInterceptor extends InterceptorAdapter { } /** - * Sets the message format itself. See the {@link LoggingInterceptor class documentation} for information on the format + * Sets the message format itself. See the {@link LoggingInterceptor class documentation} for information on the + * format */ public void setMessageFormat(String theMessageFormat) { Validate.notBlank(theMessageFormat, "Message format can not be null/empty"); @@ -290,16 +297,18 @@ public class LoggingInterceptor extends InterceptorAdapter { return myRequest.getMethod(); } else if (theKey.equals("requestBodyFhir")) { String contentType = myRequest.getContentType(); - int colonIndex = contentType.indexOf(';'); - if (colonIndex != -1) { - contentType = contentType.substring(0, colonIndex); - } - contentType = contentType.trim(); - - EncodingEnum encoding = EncodingEnum.forContentType(contentType); - if (encoding != null) { - byte[] requestContents = myRequestDetails.loadRequestContents(); - return new String(requestContents, Charsets.UTF_8); + if (isNotBlank(contentType)) { + int colonIndex = contentType.indexOf(';'); + if (colonIndex != -1) { + contentType = contentType.substring(0, colonIndex); + } + contentType = contentType.trim(); + + EncodingEnum encoding = EncodingEnum.forContentType(contentType); + if (encoding != null) { + byte[] requestContents = myRequestDetails.loadRequestContents(); + return new String(requestContents, Charsets.UTF_8); + } } return ""; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index cc97eb5b3bd..556c9a816f5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -103,8 +103,10 @@ public class ServletRequestDetails extends RequestDetails { String contentEncoding = myServletRequest.getHeader(Constants.HEADER_CONTENT_ENCODING); if ("gzip".equals(contentEncoding)) { ourLog.debug("Uncompressing (GZip) incoming content"); - GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(requestContents)); - requestContents = IOUtils.toByteArray(gis); + if (requestContents.length > 0) { + GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(requestContents)); + requestContents = IOUtils.toByteArray(gis); + } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/PathAndRef.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/PathAndRef.java index 79bbe93e473..a68b82db1c5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/PathAndRef.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/PathAndRef.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.dao; +/* + * #%L + * HAPI FHIR JPA Server + * %% + * 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% + */ + public class PathAndRef { private final String myPath; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index ed8ca7b2e63..108fa422640 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -9,6 +9,7 @@ import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -32,6 +33,9 @@ import java.util.Map; import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; import org.hl7.fhir.dstu3.model.BaseResource; @@ -118,7 +122,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { TestUtil.clearAllStaticFieldsForUnitTest(); } - private void assertGone(IIdType theId) { try { assertNotGone(theId); @@ -240,7 +243,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } } - @Test public void testChoiceParamDate() { Observation o2 = new Observation(); @@ -254,7 +256,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertEquals(id2, found.getResources(0, 1).get(0).getIdElement()); } } - + @Test public void testCreateDifferentTypesWithSameForcedId() { String idName = "forcedId"; @@ -264,17 +266,17 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { pat.addName().addFamily("FAM"); IIdType patId = myPatientDao.update(pat, mySrd).getId(); assertEquals("Patient/" + idName, patId.toUnqualifiedVersionless().getValue()); - + Observation obs = new Observation(); obs.setId(idName); obs.getCode().addCoding().setSystem("foo").setCode("testCreateDifferentTypesWithSameForcedId"); IIdType obsId = myObservationDao.update(obs, mySrd).getId(); assertEquals("Observation/" + idName, obsId.toUnqualifiedVersionless().getValue()); - + pat = myPatientDao.read(patId.toUnqualifiedVersionless(), mySrd); obs = myObservationDao.read(obsId.toUnqualifiedVersionless(), mySrd); } - + @Test public void testChoiceParamDateAlt() { Observation o2 = new Observation(); @@ -468,7 +470,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { NamingSystem res = myFhirCtx.newXmlParser().parseResource(NamingSystem.class, input); IIdType id = myNamingSystemDao.create(res, mySrd).getId().toUnqualifiedVersionless(); - + assertThat(toUnqualifiedVersionlessIdValues(myNamingSystemDao.search(NamingSystem.SP_NAME, new StringParam("NDF"))), contains(id.getValue())); } @@ -497,7 +499,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertEquals("my message", oo.getIssue().get(0).getDiagnostics()); assertEquals(IssueType.INCOMPLETE, oo.getIssue().get(0).getCode()); } - + @Test public void testCreateOperationOutcomeInfo() { FhirResourceDaoDstu3 dao = new FhirResourceDaoDstu3(); @@ -593,7 +595,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { myBundleDao.create(bundle, mySrd); } - + @Test public void testCreateWithIfNoneExistBasic() { String methodName = "testCreateWithIfNoneExistBasic"; @@ -773,7 +775,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { // Lose typing so we can put the wrong type in @SuppressWarnings("rawtypes") IFhirResourceDao dao = myNamingSystemDao; - + Patient resource = new Patient(); resource.addName().addFamily("My Name"); try { @@ -1473,10 +1475,10 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { @Test public void testHistoryWithFutureSinceDate() throws Exception { - + Date before = new Date(); Thread.sleep(10); - + Patient inPatient = new Patient(); inPatient.addName().addFamily("version1"); inPatient.getMeta().addProfile("http://example.com/1"); @@ -1484,18 +1486,18 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { Thread.sleep(10); Date after = new Date(); - + // No since - + IBundleProvider history = myPatientDao.history(null, mySrd); assertEquals(1, history.size()); Patient outPatient = (Patient) history.getResources(0, 1).get(0); assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString()); List profiles = toStringList(outPatient.getMeta().getProfile()); assertThat(profiles, contains("http://example.com/1")); - + // Before since - + history = myPatientDao.history(before, mySrd); assertEquals(1, history.size()); outPatient = (Patient) history.getResources(0, 1).get(0); @@ -1504,12 +1506,12 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertThat(profiles, contains("http://example.com/1")); // After since - + history = myPatientDao.history(after, mySrd); assertEquals(0, history.size()); } - + @Test public void testHistoryReflectsMetaOperations() throws Exception { Patient inPatient = new Patient(); @@ -1523,25 +1525,25 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString()); List profiles = toStringList(outPatient.getMeta().getProfile()); assertThat(profiles, contains("http://example.com/1")); - + /* * Change metadata */ - + inPatient.getMeta().addProfile("http://example.com/2"); myPatientDao.metaAddOperation(id, inPatient.getMeta(), mySrd); - + history = myPatientDao.history(null, mySrd); assertEquals(1, history.size()); outPatient = (Patient) history.getResources(0, 1).get(0); assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString()); profiles = toStringList(outPatient.getMeta().getProfile()); assertThat(profiles, containsInAnyOrder("http://example.com/1", "http://example.com/2")); - + /* * Do an update */ - + inPatient.setId(id); inPatient.getMeta().addProfile("http://example.com/3"); inPatient.getName().get(0).addFamily("version2"); @@ -1559,7 +1561,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertEquals("version1", outPatient.getName().get(0).getFamilyAsSingleString()); profiles = toStringList(outPatient.getMeta().getProfile()); assertThat(profiles, containsInAnyOrder("http://example.com/1", "http://example.com/2")); -} + } @Test public void testHistoryWithDeletedResource() throws Exception { @@ -1603,7 +1605,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertEquals("Resource Patient/FOOFOOFOO is not known", e.getMessage()); } } - + @Test public void testIdParam() { Patient patient = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java index 18e1f858a28..6e380f2d839 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java @@ -1,12 +1,23 @@ package ca.uhn.fhir.jpa.provider.dstu3; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import java.io.IOException; import java.util.Collection; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.hl7.fhir.dstu3.model.DecimalType; +import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType; @@ -17,6 +28,7 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; +import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -99,5 +111,77 @@ public class ResourceProviderQuestionnaireResponseDstu3Test extends BaseResource assertThat(e.toString(), containsString("Answer value must be of type string")); } } + + @Test + public void testSaveQuestionnaire() throws Exception { + String input = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse"); + post.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + CloseableHttpResponse response = ourHttpClient.execute(post); + final IdType id2; + try { + String responseString = IOUtils.toString(response.getEntity().getContent()); + ourLog.info("Response: {}", responseString); + assertEquals(201, response.getStatusLine().getStatusCode()); + String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); + assertThat(newIdString, startsWith(ourServerBase + "/QuestionnaireResponse/")); + id2 = new IdType(newIdString); + } finally { + IOUtils.closeQuietly(response); + } + + HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/" + id2.getIdPart() + "?_format=xml&_pretty=true"); + response = ourHttpClient.execute(get); + try { + String responseString = IOUtils.toString(response.getEntity().getContent()); + ourLog.info("Response: {}", responseString); + assertThat(responseString, containsString("Exclusion Criteria")); + } finally { + IOUtils.closeQuietly(response); + } + + + + } + } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java index 078e9b63f1c..0d1b9620cc3 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java @@ -30,7 +30,7 @@ public class CommonConfig { public IServerInterceptor requestLoggingInterceptor() { LoggingInterceptor retVal = new LoggingInterceptor(); retVal.setLoggerName("fhirtest.request"); - retVal.setMessageFormat("Path[${servletPath}] ${requestBodyFhir}"); + retVal.setMessageFormat("${requestVerb} ${servletPath} -\n${requestBodyFhir}"); retVal.setLogExceptions(false); return retVal; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/logback.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/logback.xml index 7716824761c..b0d0d5a9bdc 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/logback.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/logback.xml @@ -73,7 +73,7 @@ - %d{yyyy-MM-dd HH:mm:ss.SSS} %msg%n + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %msg%n diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java index 9ba41e36c96..b3b87623223 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java @@ -70,11 +70,13 @@ public class LoggingInterceptorDstu2Test { private static Server ourServer; private static RestfulServer servlet; private IServerInterceptor myInterceptor; + private static Exception ourThrowException; @Before public void before() { myInterceptor = mock(IServerInterceptor.class); servlet.setInterceptors(Collections.singletonList(myInterceptor)); + ourThrowException = null; } @Test @@ -139,7 +141,48 @@ public class LoggingInterceptorDstu2Test { } @Test - public void testCreate() throws Exception { + public void testRequestBodyRead() throws Exception { + + LoggingInterceptor interceptor = new LoggingInterceptor(); + interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); + servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + + Logger logger = mock(Logger.class); + interceptor.setLogger(logger); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + + HttpResponse status = ourClient.execute(httpGet); + IOUtils.closeQuietly(status.getEntity().getContent()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(logger, times(1)).info(captor.capture()); + assertEquals("read - - Patient/1 - ", captor.getValue()); + } + + @Test + public void testRequestBodyReadWithContentTypeHeader() throws Exception { + + LoggingInterceptor interceptor = new LoggingInterceptor(); + interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); + servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + + Logger logger = mock(Logger.class); + interceptor.setLogger(logger); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + httpGet.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_XML); + + HttpResponse status = ourClient.execute(httpGet); + IOUtils.closeQuietly(status.getEntity().getContent()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(logger, times(1)).info(captor.capture()); + assertEquals("read - - Patient/1 - ", captor.getValue()); + } + + @Test + public void testRequestBodyCreate() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); @@ -151,10 +194,37 @@ public class LoggingInterceptorDstu2Test { Patient p = new Patient(); p.addIdentifier().setValue("VAL"); String input = ourCtx.newXmlParser().encodeResourceToString(p); - + HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML+";charset=utf-8"))); - + httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + ";charset=utf-8"))); + + HttpResponse status = ourClient.execute(httpPost); + IOUtils.closeQuietly(status.getEntity().getContent()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(logger, times(1)).info(captor.capture()); + assertEquals("create - - Patient - ", captor.getValue()); + } + + @Test + public void testRequestBodyCreateException() throws Exception { + + LoggingInterceptor interceptor = new LoggingInterceptor(); + interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); + servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + + Logger logger = mock(Logger.class); + interceptor.setLogger(logger); + + Patient p = new Patient(); + p.addIdentifier().setValue("VAL"); + String input = ourCtx.newXmlParser().encodeResourceToString(p); + + ourThrowException = new NullPointerException("FOO"); + + HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + ";charset=utf-8"))); + HttpResponse status = ourClient.execute(httpPost); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -348,10 +418,13 @@ public class LoggingInterceptorDstu2Test { } @Create - public MethodOutcome create(@ResourceParam Patient thePatient) { + public MethodOutcome create(@ResourceParam Patient thePatient) throws Exception { + if (ourThrowException != null) { + throw ourThrowException; + } return new MethodOutcome(new IdDt("Patient/1")); } - + @Operation(name = "$everything", idempotent = true) public Bundle patientTypeOperation(@OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java index 3e74dc71cc2..bc96dbc76ce 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java @@ -12,6 +12,7 @@ import java.util.Map; import java.util.Set; import org.apache.commons.io.Charsets; +import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.CodeSystem; @@ -89,6 +90,8 @@ public class DefaultProfileValidationSupport implements IValidationSupport { @SuppressWarnings("unchecked") @Override public T fetchResource(FhirContext theContext, Class theClass, String theUri) { + Validate.notBlank(theUri, "theUri must not be null or blank"); + if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) { return (T) fetchStructureDefinition(theContext, theUri); } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java index a7c3b5335dd..3bcbb91485a 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/validation/InstanceValidator.java @@ -1541,7 +1541,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateQuestionannaireResponse(List errors, Element element, NodeStack stack) { Element q = element.getNamedChild("questionnaire"); - if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), q != null, "No questionnaire is identified, so no validation can be performed against the base questionnaire")) { + if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), q != null && isNotBlank(q.getNamedChildValue("reference")), "No questionnaire is identified, so no validation can be performed against the base questionnaire")) { long t = System.nanoTime(); Questionnaire qsrc = context.fetchResource(Questionnaire.class, q.getNamedChildValue("reference")); sdTime = sdTime + (System.nanoTime() - t); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java index 8bbb3664402..d579851fd1f 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java @@ -120,14 +120,14 @@ public class XmlParserDstu3Test { } @Test - public void testEncodeContained() throws Exception { + public void testEncodeContainedWithNonLocalId() throws Exception { Patient p = new Patient(); p.setId("Patient1"); p.setBirthDate(new SimpleDateFormat("yyyy-mm-dd HH:mm:ss").parse("2016-04-15 10:15:30")); ProcedureRequest pr = new ProcedureRequest(); - pr.setId("#1234567"); + pr.setId("1234567"); pr.setSubject(new Reference(p)); pr.setCode(new CodeableConcept().addCoding(new Coding("breastfeeding-readiness-assessment", "Breastfeeding Readiness Assessment", "Breastfeeding Readiness Assessment"))); // pr.setReason(new StringType("Single Live Birth"));