Skip to content

Commit

Permalink
Add a new field to the CLIENT_RESPONSE pointcut in order to allow cli…
Browse files Browse the repository at this point in the history
…ents to mutate an HTTP response from the BaseClient. (#5488)

* Add a new field to the CLIENT_RESPONSE pointcut in order to allow clients to mutate an HTTP response from the BaseClient.

* Add FhirContext to ClientResponseContext.

* Introduce a ModifiedStringApacheHttpResponse.  Run spotless.

* Remove TDOOs, add and update javadoc.

* Spotless and copyright header.

* Add changelog.

* Unique error message code.

* Only trigger the interceptor if the expected return type is a Bundle.

* Fix spotless.

* Code review feedback.

* Spotless.

* Bump to 6.11.3-SNAPSHOT
  • Loading branch information
lukedegruchy committed Nov 30, 2023
1 parent e15d043 commit 8fbbaef
Show file tree
Hide file tree
Showing 82 changed files with 346 additions and 80 deletions.
2 changes: 1 addition & 1 deletion hapi-deployable-pom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-android/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public enum Pointcut implements IPointcut {
* <li>
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
* </li>
* <li>
* ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient
* and also allows the client to mutate the contained IHttpResponse
* </li>
* </ul>
* </p>
* Hook methods must return <code>void</code>.
Expand All @@ -101,7 +105,8 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse",
"ca.uhn.fhir.rest.client.api.IRestfulClient"),
"ca.uhn.fhir.rest.client.api.IRestfulClient",
"ca.uhn.fhir.rest.client.api.ClientResponseContext"),

/**
* <b>Server Hook:</b>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
* %%
* 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:https://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%
*/
package ca.uhn.fhir.rest.client.api;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.Pointcut;
import org.hl7.fhir.instance.model.api.IBaseResource;

import java.util.Objects;
import java.util.StringJoiner;

/**
* Used to pass context to {@link Pointcut#CLIENT_RESPONSE}, including a mutable {@link IHttpResponse}
*/
public class ClientResponseContext {
private final IHttpRequest myHttpRequest;
private IHttpResponse myHttpResponse;
private final IRestfulClient myRestfulClient;
private final FhirContext myFhirContext;
private final Class<? extends IBaseResource> myReturnType;

public ClientResponseContext(
IHttpRequest myHttpRequest,
IHttpResponse theHttpResponse,
IRestfulClient myRestfulClient,
FhirContext theFhirContext,
Class<? extends IBaseResource> theReturnType) {
this.myHttpRequest = myHttpRequest;
this.myHttpResponse = theHttpResponse;
this.myRestfulClient = myRestfulClient;
this.myFhirContext = theFhirContext;
this.myReturnType = theReturnType;
}

public IHttpRequest getHttpRequest() {
return myHttpRequest;
}

public IHttpResponse getHttpResponse() {
return myHttpResponse;
}

public IRestfulClient getRestfulClient() {
return myRestfulClient;
}

public FhirContext getFhirContext() {
return myFhirContext;
}

public Class<? extends IBaseResource> getReturnType() {
return myReturnType;
}

public void setHttpResponse(IHttpResponse theHttpResponse) {
this.myHttpResponse = theHttpResponse;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClientResponseContext that = (ClientResponseContext) o;
return Objects.equals(myHttpRequest, that.myHttpRequest)
&& Objects.equals(myHttpResponse, that.myHttpResponse)
&& Objects.equals(myRestfulClient, that.myRestfulClient)
&& Objects.equals(myFhirContext, that.myFhirContext)
&& Objects.equals(myReturnType, that.myReturnType);
}

@Override
public int hashCode() {
return Objects.hash(myHttpRequest, myHttpResponse, myRestfulClient, myFhirContext, myReturnType);
}

@Override
public String toString() {
return new StringJoiner(", ", ClientResponseContext.class.getSimpleName() + "[", "]")
.add("myHttpRequest=" + myHttpRequest)
.add("myHttpResponse=" + myHttpResponse)
.add("myRestfulClient=" + myRestfulClient)
.add("myFhirContext=" + myFhirContext)
.add("myReturnType=" + myReturnType)
.toString();
}
}
4 changes: 2 additions & 2 deletions hapi-fhir-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>

<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-checkstyle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-client-okhttp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.11.2-SNAPSHOT</version>
<version>6.11.3-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* #%L
* HAPI FHIR - Client Framework
* %%
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
* %%
* 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:https://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%
*/
package ca.uhn.fhir.rest.client.apache;

import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
* Process a modified copy of an existing {@link IHttpResponse} with a String containing new content.
* <p/>
* Meant to be used with custom interceptors that need to hijack an existing IHttpResponse with new content.
*/
public class ModifiedStringApacheHttpResponse extends BaseHttpResponse implements IHttpResponse {
private static final org.slf4j.Logger ourLog =
org.slf4j.LoggerFactory.getLogger(ModifiedStringApacheHttpResponse.class);
private boolean myEntityBuffered = false;
private final String myNewContent;
private final IHttpResponse myOrigHttpResponse;
private byte[] myEntityBytes = null;

public ModifiedStringApacheHttpResponse(
IHttpResponse theOrigHttpResponse, String theNewContent, StopWatch theResponseStopWatch) {
super(theResponseStopWatch);
myOrigHttpResponse = theOrigHttpResponse;
myNewContent = theNewContent;
}

@Override
public void bufferEntity() throws IOException {
if (myEntityBuffered) {
return;
}
try (InputStream respEntity = readEntity()) {
if (respEntity != null) {
try {
myEntityBytes = IOUtils.toByteArray(respEntity);
} catch (IllegalStateException exception) {
throw new InternalErrorException(Msg.code(2447) + exception);
}
myEntityBuffered = true;
}
}
}

@Override
public void close() {
if (myOrigHttpResponse instanceof CloseableHttpResponse) {
try {
((CloseableHttpResponse) myOrigHttpResponse).close();
} catch (IOException exception) {
ourLog.debug("Failed to close response", exception);
}
}
}

@Override
public Reader createReader() throws IOException {
return new InputStreamReader(readEntity(), StandardCharsets.UTF_8);
}

@Override
public Map<String, List<String>> getAllHeaders() {
return myOrigHttpResponse.getAllHeaders();
}

@Override
public List<String> getHeaders(String theName) {
return myOrigHttpResponse.getHeaders(theName);
}

@Override
public String getMimeType() {
return myOrigHttpResponse.getMimeType();
}

@Override
public StopWatch getRequestStopWatch() {
return myOrigHttpResponse.getRequestStopWatch();
}

@Override
public Object getResponse() {
return null;
}

@Override
public int getStatus() {
return myOrigHttpResponse.getStatus();
}

@Override
public String getStatusInfo() {
return myOrigHttpResponse.getStatusInfo();
}

@Override
public InputStream readEntity() {
if (myEntityBuffered) {
return new ByteArrayInputStream(myEntityBytes);
} else {
return new ByteArrayInputStream(myNewContent.getBytes());
}
}
}
Loading

0 comments on commit 8fbbaef

Please sign in to comment.