diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index b0e7d69c9ac..f9bf329011f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.context.api.AddProfileTagEnum; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IFhirVersion; @@ -625,12 +625,21 @@ public class FhirContext { } /** - * Creates a new FluentPath engine which can be used to exvaluate + * @since 2.2 + * @deprecated Deprecated in HAPI FHIR 5.0.0. Use {@link #newFhirPath()} instead. + */ + @Deprecated + public IFhirPath newFluentPath() { + return newFhirPath(); + } + + /** + * Creates a new FhirPath engine which can be used to evaluate * path expressions over FHIR resources. Note that this engine will use the * {@link IValidationSupport context validation support} module which is * configured on the context at the time this method is called. *

- * In other words, call {@link #setValidationSupport(IValidationSupport)} before + * In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before * calling {@link #newFluentPath()} *

*

@@ -640,9 +649,9 @@ public class FhirContext { * {@link UnsupportedOperationException} *

* - * @since 2.2 + * @since 5.0.0 */ - public IFluentPath newFluentPath() { + public IFhirPath newFhirPath() { return myVersion.createFluentPathExecutor(this); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/FluentPathExecutionException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/FhirPathExecutionException.java similarity index 73% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/FluentPathExecutionException.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/FhirPathExecutionException.java index 8cd3e96f0a6..04413995318 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/FluentPathExecutionException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/FhirPathExecutionException.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.fluentpath; +package ca.uhn.fhir.fhirpath; /* * #%L @@ -23,18 +23,18 @@ package ca.uhn.fhir.fluentpath; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; /** - * This exception is thrown if a FluentPath expression can not be executed successfully + * This exception is thrown if a FHIRPath expression can not be executed successfully * for any reason */ -public class FluentPathExecutionException extends InternalErrorException { +public class FhirPathExecutionException extends InternalErrorException { private static final long serialVersionUID = 1L; - public FluentPathExecutionException(Throwable theCause) { + public FhirPathExecutionException(Throwable theCause) { super(theCause); } - public FluentPathExecutionException(String theMessage) { + public FhirPathExecutionException(String theMessage) { super(theMessage); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java similarity index 96% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java index 8d225b20c27..a15f716c37f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.fluentpath; +package ca.uhn.fhir.fhirpath; /* * #%L @@ -25,7 +25,7 @@ import java.util.Optional; import org.hl7.fhir.instance.model.api.IBase; -public interface IFluentPath { +public interface IFhirPath { /** * Apply the given FluentPath expression against the given input and return diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java index f3e8c02542d..164f5969b9b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java @@ -23,10 +23,10 @@ package ca.uhn.fhir.model.api; import java.io.InputStream; import java.util.Date; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.*; -import ca.uhn.fhir.fluentpath.IFluentPath; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; /** @@ -38,7 +38,7 @@ import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; */ public interface IFhirVersion { - IFluentPath createFluentPathExecutor(FhirContext theFhirContext); + IFhirPath createFluentPathExecutor(FhirContext theFhirContext); IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java index f8a4193cf16..c73c545c407 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java @@ -24,7 +24,7 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IBase; @@ -120,7 +120,7 @@ public abstract class BaseNarrativeGenerator implements INarrativeGenerator { if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) { return Collections.singletonList(theResource); } - IFluentPath fhirPath = theFhirContext.newFluentPath(); + IFhirPath fhirPath = theFhirContext.newFluentPath(); return fhirPath.evaluate(theResource, theContextPath, IBase.class); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 50aec5b8393..8678d76cae8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -259,6 +259,7 @@ public class Constants { *

*/ public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source"; + public static final String PARAM_FHIRPATH = "_fhirpath"; static { CHARSET_UTF8 = StandardCharsets.UTF_8; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java index f4db4b682c2..42292361c26 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java @@ -238,7 +238,7 @@ public class ParametersUtil { addPart(theContext, theParameter, theName, coding); } - private static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) { + public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) { BaseRuntimeElementCompositeDefinition def = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(theParameter.getClass()); BaseRuntimeChildDefinition partChild = def.getChildByName("part"); @@ -252,4 +252,19 @@ public class ParametersUtil { partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue); } + + public static void addPartResource(FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) { + BaseRuntimeElementCompositeDefinition def = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(theParameter.getClass()); + BaseRuntimeChildDefinition partChild = def.getChildByName("part"); + + BaseRuntimeElementCompositeDefinition partChildElem = (BaseRuntimeElementCompositeDefinition) partChild.getChildByName("part"); + IBase part = partChildElem.newInstance(); + partChild.getMutator().addValue(theParameter, part); + + IPrimitiveType name = (IPrimitiveType) theContext.getElementDefinition("string").newInstance(); + name.setValue(theName); + partChildElem.getChildByName("name").getMutator().addValue(part, name); + + partChildElem.getChildByName("resource").getMutator().addValue(part, theValue); + } } diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java index 96803d8bda2..eaf84d6ce1f 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java @@ -32,11 +32,11 @@ import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import java.util.Arrays; -@SuppressWarnings("serial") +@SuppressWarnings({"serial", "RedundantThrows", "InnerClassMayBeStatic"}) public class ServletExamples { // START SNIPPET: loggingInterceptor - @WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server") + @WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server") public class RestfulServerWithLogging extends RestfulServer { @Override @@ -121,6 +121,24 @@ public class ServletExamples { } // END SNIPPET: exceptionInterceptor + // START SNIPPET: fhirPathInterceptor + @WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server") + public class RestfulServerWithFhirPath extends RestfulServer { + + @Override + protected void initialize() throws ServletException { + + // ... define your resource providers here ... + + // Now register the interceptor + FhirPathFilterInterceptor interceptor = new FhirPathFilterInterceptor(); + registerInterceptor(interceptor); + + } + + } + // END SNIPPET: fhirPathInterceptor + // START SNIPPET: responseHighlighterInterceptor @WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server") public class RestfulServerWithResponseHighlighter extends RestfulServer { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1769-add-fhirpath-interceptor.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1769-add-fhirpath-interceptor.yaml new file mode 100644 index 00000000000..c6e9a32c0d5 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1769-add-fhirpath-interceptor.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 1769 +title: A new built-in server interceptor called FhirPathFilterInterceptor has been added. This interceptor + evaluates an arbitrary FHIRPath expression against the resource being returned and replaces the response + with a Parameters resource containing the results of the evaluation. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml index aa375e46680..08691d1826a 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml @@ -17,3 +17,11 @@ [Migrating to HAPI FHIR 5.x](/hapi-fhir/docs/validation/instance_validator.html#migrating-to-hapi-fhir-5x) for details on how to account for this change in your code. " +- item: + issue: "1769" + type: "change" + title: "**Breaking Change**: + The `IFluentPath` interface has been renamed to `IFhirPath`, and the `FhirContext#newFluentPath()` method + has been replaced with an equivalent `FhirContext.newFhirPath()`. The FhirPath expression language was initially + called FluentPath before being renamed, so this change brings HAPI FHIR inline with the correct naming. + " diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md index 6c166e8cce9..35f20dc14ca 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md @@ -50,6 +50,52 @@ The following example shows how to register the ExceptionHandlingInterceptor. {{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|exceptionInterceptor}} ``` +# Response Customizing: Evaluate FHIRPath + +The FhirPathFilterInterceptor looks for a request URL parameter in the form `_fhirpath=(expression)` in all REST requests. If this parameter is found, the value is treated as a [FHIRPath](http://hl7.org/fhirpath/) expression. The response resource will be replaced with a [Parameters](hl7.org/fhir/parameters.html) resource containing the results of the given expression applied against the response resource. + +* [FhirPathFilterInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptor.html) +* [FhirPathFilterInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptor.java) + +The following example shows how to register the ExceptionHandlingInterceptor. + +```java +{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|exceptionInterceptor}} +``` + +An example URL to invoke this function is shown below: + +```url +https://hapi.fhir.org/baseR4/Patient?_fhirpath=Bundle.entry.resource.as(Patient).name&_pretty=true +``` + +A sample response to this query is shown below: + +```json +{ + "resourceType": "Parameters", + "parameter": [ { + "name": "result", + "part": [ { + "name": "expression", + "valueString": "Bundle.entry.resource.as(Patient).name" + }, { + "name": "result", + "valueHumanName": { + "family": "Simpson", + "given": [ "Homer", "Jay" ] + } + }, { + "name": "result", + "valueHumanName": { + "family": "Simpson", + "given": [ "Grandpa" ] + } + } ] + } ] +} +``` + # Validation: Request and Response Validation HAPI FHIR provides a pair of interceptors that can be used to validate incoming requests received by the server, as well as outgoing responses generated by the server. diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index e2a164287b9..4a767f35d5f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.BanUnsupportedHttpMethodsInterceptor; import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; +import ca.uhn.fhir.rest.server.interceptor.FhirPathFilterInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; import ca.uhn.fhirtest.config.TestDstu2Config; import ca.uhn.fhirtest.config.TestDstu3Config; @@ -198,6 +199,11 @@ public class TestRestfulServer extends RestfulServer { CorsInterceptor corsInterceptor = new CorsInterceptor(); registerInterceptor(corsInterceptor); + /* + * Enable FHIRPath evaluation + */ + registerInterceptor(new FhirPathFilterInterceptor()); + /* * Enable version conversion */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptor.java new file mode 100644 index 00000000000..c254be28f19 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptor.java @@ -0,0 +1,70 @@ +package ca.uhn.fhir.rest.server.interceptor; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.fhirpath.FhirPathExecutionException; +import ca.uhn.fhir.fhirpath.IFhirPath; +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.ResponseDetails; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.ParametersUtil; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.util.List; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +/** + * This interceptor looks for a URL parameter on requests called _fhirpath and + * replaces the resource being returned with a Parameters resource containing the results of + * the given FHIRPath expression evaluated against the resource that would otherwise + * have been returned. + * + * @see Interceptors - Response Customization: Evaluate FHIRPath + * @since 5.0.0 + */ +public class FhirPathFilterInterceptor { + + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) + public void preProcessOutgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails) { + IBaseResource responseResource = theResponseDetails.getResponseResource(); + if (responseResource != null) { + String[] fhirPathParams = theRequestDetails.getParameters().get(Constants.PARAM_FHIRPATH); + if (fhirPathParams != null) { + + FhirContext ctx = theRequestDetails.getFhirContext(); + IBaseParameters responseParameters = ParametersUtil.newInstance(ctx); + + for (String expression : fhirPathParams) { + if (isNotBlank(expression)) { + IBase resultPart = ParametersUtil.addParameterToParameters(ctx, responseParameters, "result"); + ParametersUtil.addPartString(ctx, resultPart, "expression", expression); + + IFhirPath fhirPath = ctx.newFhirPath(); + List outputs; + try { + outputs = fhirPath.evaluate(responseResource, expression, IBase.class); + } catch (FhirPathExecutionException e) { + throw new InvalidRequestException("Error parsing FHIRPath expression: " + e.getMessage()); + } + + for (IBase nextOutput : outputs) { + if (nextOutput instanceof IBaseResource) { + ParametersUtil.addPartResource(ctx, resultPart, "result", (IBaseResource) nextOutput); + } else { + ParametersUtil.addPart(ctx, resultPart, "result", nextOutput); + } + } + } + } + + theResponseDetails.setResponseResource(responseParameters); + } + } + } + +} diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java index 6ed05ffa58a..731db1e8a57 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java @@ -60,7 +60,7 @@ import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimeResourceBlockDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.support.IContextValidationSupport; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFluentPath; import ca.uhn.fhir.model.api.ICompositeDatatype; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IPrimitiveDatatype; diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/ctx/FhirDstu2_1.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/ctx/FhirDstu2_1.java index 250d7170d10..dd9a1c39a1c 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/ctx/FhirDstu2_1.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/ctx/FhirDstu2_1.java @@ -30,7 +30,7 @@ import org.hl7.fhir.dstu2016may.model.*; import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.*; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; @@ -41,7 +41,7 @@ public class FhirDstu2_1 implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { throw new UnsupportedOperationException("FluentPath is not supported in DSTU2 contexts"); } diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java index ea7ad545b30..53b45ab795a 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java @@ -23,11 +23,11 @@ package ca.uhn.fhir.model.dstu2; import java.io.InputStream; import java.util.Date; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.*; -import ca.uhn.fhir.fluentpath.IFluentPath; import ca.uhn.fhir.model.api.*; import ca.uhn.fhir.model.base.composite.*; import ca.uhn.fhir.model.dstu2.composite.*; @@ -42,7 +42,7 @@ public class FhirDstu2 implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { throw new UnsupportedOperationException("FluentPath is not supported in DSTU2 contexts"); } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java index a2037e1d788..1980ce93b25 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java @@ -24,13 +24,13 @@ 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.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.util.ReflectionUtil; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.dstu3.hapi.fluentpath.FluentPathDstu3; +import org.hl7.fhir.dstu3.hapi.fluentpath.FhirPathDstu3; import org.hl7.fhir.dstu3.hapi.rest.server.Dstu3BundleFactory; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.*; @@ -44,8 +44,8 @@ public class FhirDstu3 implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { - return new FluentPathDstu3(theFhirContext); + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { + return new FhirPathDstu3(theFhirContext); } @Override diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FluentPathDstu3.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java similarity index 74% rename from hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FluentPathDstu3.java rename to hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java index 9eb25f1d369..84bbca753dc 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FluentPathDstu3.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java @@ -2,8 +2,8 @@ package org.hl7.fhir.dstu3.hapi.fluentpath; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.fluentpath.FluentPathExecutionException; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.FhirPathExecutionException; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.model.Base; import org.hl7.fhir.dstu3.utils.FHIRPathEngine; @@ -13,11 +13,11 @@ import org.hl7.fhir.instance.model.api.IBase; import java.util.List; import java.util.Optional; -public class FluentPathDstu3 implements IFluentPath { +public class FhirPathDstu3 implements IFhirPath { private FHIRPathEngine myEngine; - public FluentPathDstu3(FhirContext theCtx) { + public FhirPathDstu3(FhirContext theCtx) { IValidationSupport validationSupport = theCtx.getValidationSupport(); myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport)); } @@ -29,12 +29,12 @@ public class FluentPathDstu3 implements IFluentPath { try { result = myEngine.evaluate((Base)theInput, thePath); } catch (FHIRException e) { - throw new FluentPathExecutionException(e); + throw new FhirPathExecutionException(e); } for (Base next : result) { if (!theReturnType.isAssignableFrom(next.getClass())) { - throw new FluentPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); + throw new FhirPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/ctx/FhirDstu2Hl7Org.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/ctx/FhirDstu2Hl7Org.java index 6048745858f..dfc192139b1 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/ctx/FhirDstu2Hl7Org.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/ctx/FhirDstu2Hl7Org.java @@ -24,12 +24,12 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Date; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.model.*; import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.*; -import ca.uhn.fhir.fluentpath.IFluentPath; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; @@ -41,7 +41,7 @@ public class FhirDstu2Hl7Org implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { throw new UnsupportedOperationException("FluentPath is not supported in DSTU2 contexts"); } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/FhirR4.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/FhirR4.java index cb1eeeb2358..1014f31e2b4 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/FhirR4.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/FhirR4.java @@ -26,12 +26,12 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.*; -import org.hl7.fhir.r4.hapi.fluentpath.FluentPathR4; +import org.hl7.fhir.r4.hapi.fluentpath.FhirPathR4; import org.hl7.fhir.r4.hapi.rest.server.R4BundleFactory; import org.hl7.fhir.r4.model.*; import ca.uhn.fhir.context.*; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; @@ -42,8 +42,8 @@ public class FhirR4 implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { - return new FluentPathR4(theFhirContext); + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { + return new FhirPathR4(theFhirContext); } @Override diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FluentPathR4.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java similarity index 75% rename from hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FluentPathR4.java rename to hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java index 5a34d3d8ff3..eb1ca366b43 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FluentPathR4.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java @@ -2,8 +2,8 @@ package org.hl7.fhir.r4.hapi.fluentpath; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.fluentpath.FluentPathExecutionException; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.FhirPathExecutionException; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; @@ -13,11 +13,11 @@ import org.hl7.fhir.r4.utils.FHIRPathEngine; import java.util.List; import java.util.Optional; -public class FluentPathR4 implements IFluentPath { +public class FhirPathR4 implements IFhirPath { private FHIRPathEngine myEngine; - public FluentPathR4(FhirContext theCtx) { + public FhirPathR4(FhirContext theCtx) { IValidationSupport validationSupport = theCtx.getValidationSupport(); myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport)); } @@ -29,12 +29,12 @@ public class FluentPathR4 implements IFluentPath { try { result = myEngine.evaluate((Base) theInput, thePath); } catch (FHIRException e) { - throw new FluentPathExecutionException(e); + throw new FhirPathExecutionException(e); } for (Base next : result) { if (!theReturnType.isAssignableFrom(next.getClass())) { - throw new FluentPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); + throw new FhirPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptorTest.java new file mode 100644 index 00000000000..68bc9ad406d --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/FhirPathFilterInterceptorTest.java @@ -0,0 +1,170 @@ +package ca.uhn.fhir.rest.server.interceptor; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.test.utilities.HttpClientRule; +import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderRule; +import ca.uhn.fhir.test.utilities.server.RestfulServerRule; +import ca.uhn.fhir.util.UrlUtil; +import com.google.common.base.Charsets; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Patient; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class FhirPathFilterInterceptorTest { + + private static final Logger ourLog = LoggerFactory.getLogger(FhirPathFilterInterceptorTest.class); + @ClassRule + public static HttpClientRule ourClientRule = new HttpClientRule(); + private static FhirContext ourCtx = FhirContext.forR4(); + @ClassRule + public static RestfulServerRule ourServerRule = new RestfulServerRule(ourCtx); + @ClassRule + public static HashMapResourceProviderRule ourProviderRule = new HashMapResourceProviderRule<>(ourServerRule, Patient.class); + private IGenericClient myClient; + private String myBaseUrl; + private CloseableHttpClient myHttpClient; + private IIdType myPatientId; + + @Before + public void before() { + ourProviderRule.clear(); + ourServerRule.getRestfulServer().getInterceptorService().unregisterAllInterceptors(); + ourServerRule.getRestfulServer().getInterceptorService().registerInterceptor(new FhirPathFilterInterceptor()); + + myClient = ourServerRule.getFhirClient(); + myBaseUrl = "http://localhost:" + ourServerRule.getPort(); + myHttpClient = ourClientRule.getClient(); + } + + @Test + public void testUnfilteredResponse() throws IOException { + createPatient(); + + HttpGet request = new HttpGet(myPatientId.getValue()); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertThat(responseText, containsString("\"system\": \"http://identifiers/1\"")); + assertThat(responseText, containsString("\"given\": [ \"Homer\", \"Jay\" ]")); + } + } + + + @Test + public void testUnfilteredResponse_WithResponseHighlightingInterceptor() throws IOException { + ourServerRule.getRestfulServer().registerInterceptor(new ResponseHighlighterInterceptor()); + createPatient(); + + HttpGet request = new HttpGet(myPatientId.getValue() + "?_format=" + Constants.FORMATS_HTML_JSON); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertThat(responseText, containsString(""system": "http://identifiers/1"")); + assertThat(responseText, containsString(""given": [ "Homer", "Jay" ]")); + } + } + + @Test + public void testFilteredResponse() throws IOException { + createPatient(); + + HttpGet request = new HttpGet(myPatientId + "?_fhirpath=Patient.identifier&_pretty=true"); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertThat(responseText, containsString("\"system\": \"http://identifiers/1\"")); + assertThat(responseText, not(containsString("\"given\": [ \"Homer\", \"Jay\" ]"))); + } + + } + + @Test + public void testFilteredResponse_ExpressionReturnsResource() throws IOException { + createPatient(); + + HttpGet request = new HttpGet(myPatientId + "?_fhirpath=Patient&_pretty=true"); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertThat(responseText, containsString("\"resource\": {")); + assertThat(responseText, containsString("\"system\": \"http://identifiers/1\"")); + assertThat(responseText, containsString("\"given\": [ \"Homer\", \"Jay\" ]")); + } + + } + + @Test + public void testFilteredResponse_ExpressionIsInvalid() throws IOException { + createPatient(); + + HttpGet request = new HttpGet(myPatientId + "?_fhirpath=" + UrlUtil.escapeUrlParam("***")); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertEquals(400, response.getStatusLine().getStatusCode()); + assertThat(responseText, containsString("Error parsing FHIRPath expression: Error performing *: left operand has more than one value")); + } + + } + + @Test + public void testFilteredResponseBundle() throws IOException { + createPatient(); + + HttpGet request = new HttpGet(myBaseUrl + "/Patient?_fhirpath=Bundle.entry.resource.as(Patient).name&_pretty=true"); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertThat(responseText, containsString( + " \"valueHumanName\": {\n" + + " \"family\": \"Simpson\",\n" + + " \"given\": [ \"Homer\", \"Jay\" ]\n" + + " }" + )); + } + + } + + @Test + public void testFilteredResponse_WithResponseHighlightingInterceptor() throws IOException { + ourServerRule.getRestfulServer().registerInterceptor(new ResponseHighlighterInterceptor()); + createPatient(); + + HttpGet request = new HttpGet(myPatientId + "?_fhirpath=Patient.identifier&_format=" + Constants.FORMATS_HTML_JSON); + try (CloseableHttpResponse response = myHttpClient.execute(request)) { + String responseText = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response:\n{}", responseText); + assertThat(responseText, containsString(""system": "http://identifiers/1"")); + assertThat(responseText, not(containsString(""given": [ "Homer", "Jay" ]"))); + } + + } + + private void createPatient() { + Patient p = new Patient(); + p.setActive(true); + p.addIdentifier().setSystem("http://identifiers/1").setValue("value-1"); + p.addIdentifier().setSystem("http://identifiers/2").setValue("value-2"); + p.addName().setFamily("Simpson").addGiven("Homer").addGiven("Jay"); + p.addName().setFamily("Simpson").addGiven("Grandpa"); + myPatientId = myClient.create().resource(p).execute().getId().withServerBase(myBaseUrl, "Patient"); + } + +} diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/FhirR5.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/FhirR5.java index c4c2e210235..c303fd18107 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/FhirR5.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/FhirR5.java @@ -24,7 +24,7 @@ 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.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; @@ -52,7 +52,7 @@ public class FhirR5 implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { return new FhirPathR5(theFhirContext); } diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java index 1aab76cf9ed..d82be431bc1 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java @@ -2,8 +2,8 @@ package org.hl7.fhir.r5.hapi.fhirpath; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.fluentpath.FluentPathExecutionException; -import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.fhirpath.FhirPathExecutionException; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; @@ -13,7 +13,7 @@ import org.hl7.fhir.r5.utils.FHIRPathEngine; import java.util.List; import java.util.Optional; -public class FhirPathR5 implements IFluentPath { +public class FhirPathR5 implements IFhirPath { private FHIRPathEngine myEngine; @@ -29,12 +29,12 @@ public class FhirPathR5 implements IFluentPath { try { result = myEngine.evaluate((Base) theInput, thePath); } catch (FHIRException e) { - throw new FluentPathExecutionException(e); + throw new FhirPathExecutionException(e); } for (Base next : result) { if (!theReturnType.isAssignableFrom(next.getClass())) { - throw new FluentPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); + throw new FhirPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); } } diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/HttpClientRule.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/HttpClientRule.java new file mode 100644 index 00000000000..4c834c1eff4 --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/HttpClientRule.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.test.utilities; + +/*- + * #%L + * HAPI FHIR Test Utilities + * %% + * Copyright (C) 2014 - 2020 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 org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class HttpClientRule implements TestRule { + private CloseableHttpClient myClient; + + @Override + public Statement apply(Statement theBase, Description theDescription) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + startClient(); + theBase.evaluate(); + stopClient(); + } + }; + } + + + private void stopClient() throws Exception { + myClient.close(); + } + + private void startClient() { + myClient = HttpClientBuilder + .create() + .build(); + } + + public CloseableHttpClient getClient() { + return myClient; + } +} diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/fluentpath/FluentPathTest.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/fhirpath/FluentPathTest.java similarity index 88% rename from hapi-fhir-validation/src/test/java/ca/uhn/fhir/fluentpath/FluentPathTest.java rename to hapi-fhir-validation/src/test/java/ca/uhn/fhir/fhirpath/FluentPathTest.java index 4acf3a2d406..6bc7da3d654 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/fluentpath/FluentPathTest.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/fhirpath/FluentPathTest.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.fluentpath; +package ca.uhn.fhir.fhirpath; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.util.TestUtil; @@ -21,7 +21,7 @@ public class FluentPathTest { p.addName().setFamily("N1F1").addGiven("N1G1").addGiven("N1G2"); p.addName().setFamily("N2F1").addGiven("N2G1").addGiven("N2G2"); - IFluentPath fp = ourCtx.newFluentPath(); + IFhirPath fp = ourCtx.newFluentPath(); List names = fp.evaluate(p, "Patient.name", HumanName.class); assertEquals(2, names.size()); assertEquals("N1F1", names.get(0).getFamily()); @@ -36,7 +36,7 @@ public class FluentPathTest { p.addName().setFamily("N1F1").addGiven("N1G1").addGiven("N1G2"); p.addName().setFamily("N2F1").addGiven("N2G1").addGiven("N2G2"); - IFluentPath fp = ourCtx.newFluentPath(); + IFhirPath fp = ourCtx.newFluentPath(); List names = fp.evaluate(p, "Patient.nameFOO", HumanName.class); assertEquals(0, names.size()); } @@ -47,10 +47,10 @@ public class FluentPathTest { p.addName().setFamily("N1F1").addGiven("N1G1").addGiven("N1G2"); p.addName().setFamily("N2F1").addGiven("N2G1").addGiven("N2G2"); - IFluentPath fp = ourCtx.newFluentPath(); + IFhirPath fp = ourCtx.newFluentPath(); try { fp.evaluate(p, "Patient....nameFOO", HumanName.class); - } catch (FluentPathExecutionException e) { + } catch (FhirPathExecutionException e) { assertThat(e.getMessage(), containsString("termination at unexpected token")); } } @@ -61,10 +61,10 @@ public class FluentPathTest { p.addName().setFamily("N1F1").addGiven("N1G1").addGiven("N1G2"); p.addName().setFamily("N2F1").addGiven("N2G1").addGiven("N2G2"); - IFluentPath fp = ourCtx.newFluentPath(); + IFhirPath fp = ourCtx.newFluentPath(); try { fp.evaluate(p, "Patient.name", StringType.class); - } catch (FluentPathExecutionException e) { + } catch (FhirPathExecutionException e) { assertEquals("FluentPath expression \"Patient.name\" returned unexpected type HumanName - Expected org.hl7.fhir.dstu3.model.StringType", e.getMessage()); } } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java index 06a10a7948b..a95a1109967 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java @@ -23,11 +23,11 @@ package ca.uhn.fhir.model.dstu2; import java.io.InputStream; import java.util.Date; +import ca.uhn.fhir.fhirpath.IFhirPath; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.*; -import ca.uhn.fhir.fluentpath.IFluentPath; import ca.uhn.fhir.model.api.*; import ca.uhn.fhir.model.base.composite.*; import ca.uhn.fhir.model.dstu2.composite.*; @@ -41,7 +41,7 @@ public class FhirDstu2 implements IFhirVersion { private String myId; @Override - public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) { + public IFhirPath createFluentPathExecutor(FhirContext theFhirContext) { throw new UnsupportedOperationException("FluentPath is not supported in DSTU2 contexts"); }