Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OZ-689: Add functionality to remove transfer_encoding header and handle response status code in DelegatingController #2

Merged
merged 3 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,41 @@
*/
package org.openmrs.module.fhirproxy.web.controller;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Base64.getEncoder;
import static org.springframework.http.HttpMethod.GET;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.openmrs.module.fhirproxy.Config;
import org.openmrs.module.fhirproxy.FhirProxyUtils;
import org.openmrs.module.fhirproxy.web.ProxyWebConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Base64.getEncoder;
import static org.springframework.http.HttpMethod.GET;

/**
* Provides a proxy mechanism for all GET requests for ChargeItemDefinition and InventoryItem FHIR
* resources by delegating to a configured external API to process the request.
*/
@RestController("delegatingController")
public class DelegatingController {

private static final Logger LOG = LoggerFactory.getLogger(DelegatingController.class);

private RestTemplate restTemplate;

private static final Logger LOG = LoggerFactory.getLogger(DelegatingController.class);

@GetMapping(ProxyWebConstants.PATH_DELEGATE)
public Object delegate(HttpServletRequest request) throws IOException {
public ResponseEntity<?> delegate(HttpServletRequest request) throws IOException {
LOG.debug("Delegating to external API to process FHIR request -> {}", request.getRequestURI());
if (restTemplate == null) {
restTemplate = new RestTemplate();
}
Expand All @@ -67,6 +69,22 @@ public Object delegate(HttpServletRequest request) throws IOException {
final String auth = getEncoder().encodeToString((cfg.getUsername() + ":" + cfg.getPassword()).getBytes(UTF_8));
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, "Basic " + auth);
return restTemplate.exchange(urlBuilder.encode().toUriString(), GET, new HttpEntity<>(headers), Object.class);

try {
ResponseEntity<?> responseEntity = restTemplate.exchange(urlBuilder.encode().toUriString(), GET,
new HttpEntity<>(headers), Object.class);
HttpHeaders newHeaders = new HttpHeaders();
responseEntity.getHeaders().forEach((key, value) -> {
if (!HttpHeaders.TRANSFER_ENCODING.equalsIgnoreCase(key) && !HttpHeaders.CONNECTION.equalsIgnoreCase(key)) {
newHeaders.put(key, value);
}
});

return new ResponseEntity<>(responseEntity.getBody(), newHeaders, responseEntity.getStatusCode());
}
catch (HttpClientErrorException clientErrorException) {
return new ResponseEntity<>(clientErrorException.getResponseBodyAsString(),
clientErrorException.getStatusCode());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
package org.openmrs.module.fhirproxy.web.controller;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.http.HttpMethod.GET;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -23,9 +14,23 @@
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.http.HttpMethod.GET;

@RunWith(PowerMockRunner.class)
@PrepareForTest(FhirProxyUtils.class)
@PowerMockIgnore({ "javax.management.*", "javax.script.*" })
Expand Down Expand Up @@ -69,7 +74,7 @@ public void delegate_shouldGetTheResourceFromTheExternalApi() throws Exception {
when(mockTemplate.exchange(eq(url), eq(GET), httpEntityCaptor.capture(), eq(Object.class)))
.thenReturn(ResponseEntity.ok(expectedJson));
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_RESOURCE_NAME)).thenReturn(RESOURCE);
Assert.assertEquals(expectedJson, ((HttpEntity) controller.delegate(mockRequest)).getBody());
assertEquals(expectedJson, ((HttpEntity) controller.delegate(mockRequest)).getBody());
}

@Test
Expand All @@ -82,7 +87,7 @@ public void delegate_shouldGetTheResourceByIdFromTheExternalApi() throws Excepti
.thenReturn(ResponseEntity.ok(expectedJson));
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_RESOURCE_NAME)).thenReturn(RESOURCE);
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_RESOURCE_ID)).thenReturn(resId);
Assert.assertEquals(expectedJson, ((HttpEntity) controller.delegate(mockRequest)).getBody());
assertEquals(expectedJson, ((HttpEntity) controller.delegate(mockRequest)).getBody());
}

@Test
Expand All @@ -95,7 +100,40 @@ public void delegate_shouldIncludeTheQueryString() throws Exception {
.thenReturn(ResponseEntity.ok(expectedJson));
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_RESOURCE_NAME)).thenReturn(RESOURCE);
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_QUERY_STR)).thenReturn(queryString);
Assert.assertEquals(expectedJson, ((HttpEntity) controller.delegate(mockRequest)).getBody());
assertEquals(expectedJson, ((HttpEntity) controller.delegate(mockRequest)).getBody());
}

@Test
public void delegate_shouldRemoveTransferEncodingHeader() throws Exception {
final String expectedJson = "{}";
final String url = BASE_URL + "/" + RESOURCE;
ArgumentCaptor<ResponseEntity<?>> httpEntityCaptor = ArgumentCaptor.forClass(ResponseEntity.class);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.TRANSFER_ENCODING, "chunked");
responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/json");
when(mockTemplate.exchange(eq(url), eq(GET), httpEntityCaptor.capture(), eq(Object.class)))
.thenReturn(new ResponseEntity<>(expectedJson, responseHeaders, HttpStatus.OK));
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_RESOURCE_NAME)).thenReturn(RESOURCE);

ResponseEntity<?> response = controller.delegate(mockRequest);

assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(expectedJson, response.getBody());
assertFalse(response.getHeaders().containsKey(HttpHeaders.TRANSFER_ENCODING));
assertTrue(response.getHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
}

@Test
public void delegate_shouldReturnClientErrorStatusCode() throws Exception {
final String url = BASE_URL + "/" + RESOURCE;
ArgumentCaptor<HttpEntity<?>> httpEntityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
when(mockTemplate.exchange(eq(url), eq(GET), httpEntityCaptor.capture(), eq(Object.class))).thenThrow(
new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Bad Request", "Bad Request".getBytes(), UTF_8));
when(mockRequest.getAttribute(ProxyWebConstants.ATTRIB_RESOURCE_NAME)).thenReturn(RESOURCE);

ResponseEntity<?> response = controller.delegate(mockRequest);

assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertEquals("Bad Request", response.getBody());
}
}