Skip to content

Commit

Permalink
AB2D-6495 HttpGet to HttpPost - Pagination & Redeploy
Browse files Browse the repository at this point in the history
  • Loading branch information
smirnovaae committed Feb 11, 2025
1 parent 75ea428 commit fc11e84
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gov.cms.ab2d.bfd.client;

import ca.uhn.fhir.rest.api.SearchStyleEnum;
import ca.uhn.fhir.rest.gclient.TokenClientParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
Expand Down Expand Up @@ -161,7 +162,8 @@ public IBaseBundle requestPartDEnrolleesFromServer(FhirVersion version, String c
.withAdditionalHeader(BFDClient.BFD_HDR_BULK_CLIENTID, contractNumber)
.withAdditionalHeader(BFDClient.BFD_HDR_BULK_JOBID, getJobId())
.withAdditionalHeader(INCLUDE_IDENTIFIERS_HEADER, MBI_HEADER_VALUE)
.count(contractToBenePageSize);
.count(contractToBenePageSize)
.usingStyle(SearchStyleEnum.POST);
log.info("Executing request to get Part D Enrollees " + request);
return request.returnBundle(version.getBundleClass())
.encodedJson()
Expand Down Expand Up @@ -189,7 +191,8 @@ public IBaseBundle requestPartDEnrolleesFromServer(FhirVersion version, String c
.withAdditionalHeader(BFDClient.BFD_HDR_BULK_CLIENTID, contractNumber)
.withAdditionalHeader(BFDClient.BFD_HDR_BULK_JOBID, getJobId())
.withAdditionalHeader(INCLUDE_IDENTIFIERS_HEADER, MBI_HEADER_VALUE)
.count(contractToBenePageSize);
.count(contractToBenePageSize)
.usingStyle(SearchStyleEnum.POST);
log.info("Executing request to get Part D Enrollees " + request);
return request.returnBundle(version.getBundleClass())
.encodedJson()
Expand Down
40 changes: 25 additions & 15 deletions ab2d-bfd/src/main/java/gov/cms/ab2d/bfd/client/BFDSearchImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.message.BasicNameValuePair;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
Expand All @@ -17,6 +20,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
Expand Down Expand Up @@ -48,22 +53,14 @@ public BFDSearchImpl(HttpClient httpClient, Environment environment, BfdClientVe
@Trace
@Override
public IBaseBundle searchEOB(long patientId, OffsetDateTime since, OffsetDateTime until, int pageSize, String bulkJobId, FhirVersion version, String contractNum) throws IOException {
String urlLocation = bfdClientVersions.getUrl(version);
StringBuilder url = new StringBuilder(urlLocation + "ExplanationOfBenefit?patient=" + patientId + "&excludeSAMHSA=true");

if (since != null) {
url.append("&_lastUpdated=ge").append(since);
}

if (until != null) {
url.append("&_lastUpdated=le").append(until);
}
StringBuilder url = new StringBuilder(bfdClientVersions.getUrl(version) + "ExplanationOfBenefit/_search");

if (pageSize > 0) {
url.append("&_count=").append(pageSize);
}

HttpGet request = new HttpGet(url.toString());
HttpPost request = new HttpPost(url.toString());

// No active profiles means use JSON
if (environment.getActiveProfiles().length == 0) {
request.addHeader("Accept", "application/fhir+json;q=1.0, application/json+fhir;q=0.9");
Expand All @@ -73,17 +70,30 @@ public IBaseBundle searchEOB(long patientId, OffsetDateTime since, OffsetDateTim
request.addHeader(HttpHeaders.ACCEPT_CHARSET, "utf-8");
request.addHeader(BFDClient.BFD_HDR_BULK_CLIENTID, contractNum);
request.addHeader(BFDClient.BFD_HDR_BULK_JOBID, bulkJobId);
log.info("Executing BFD Search Request " + request);

List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("patient", "" + patientId));
params.add(new BasicNameValuePair("excludeSAMHSA", "true"));
if (since != null) {
params.add(new BasicNameValuePair("_lastUpdated", "ge" + since));
}

if (until != null) {
params.add(new BasicNameValuePair("_lastUpdated", "le" + until));
}

request.setEntity(new UrlEncodedFormEntity(params));
log.info("Executing BFD Search Request {}", request);
byte[] responseBytes = getEOBSFromBFD(patientId, request);

return parseBundle(version, responseBytes);
}

/**
Method exists to track connection to BFD for New Relic
* Method exists to track connection to BFD for New Relic
*/
@Trace
private byte[] getEOBSFromBFD(long patientId, HttpGet request) throws IOException {
private byte[] getEOBSFromBFD(long patientId, HttpPost request) throws IOException {
byte[] responseBytes;
try (CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(request)) {
int status = response.getStatusLine().getStatusCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@ public static void setupBFDClient() throws IOException {

// Ensure timeouts are working.
MockUtils.createMockServerExpectation(
"/v2/fhir/ExplanationOfBenefit",
"/v2/fhir/ExplanationOfBenefit/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_EOB_BUNDLE),
List.of(Parameter.param("patient", TEST_PATIENT_ID.toString()),
Parameter.param("excludeSAMHSA", "true")),
MOCK_PORT_V2
);
MockUtils.createMockServerExpectation(
"/v2/fhir/ExplanationOfBenefit/_search&_count=10",
HttpStatus.SC_OK,
getRawJson(SAMPLE_EOB_BUNDLE),
List.of(Parameter.param("patient", TEST_PATIENT_ID.toString()),
Expand All @@ -74,7 +82,7 @@ public static void setupBFDClient() throws IOException {
);

MockUtils.createMockServerExpectation(
"/v2/fhir/Patient",
"/v2/fhir/Patient/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_PATIENT_BUNDLE),
List.of(Parameter.param("_has:Coverage.extension",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpStatus;
import org.hl7.fhir.dstu3.model.Bundle;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.Parameter;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -82,7 +79,7 @@ public static void setupBFDClient() throws IOException {

// Ensure timeouts are working.
MockUtils.createMockServerExpectation(
"/v1/fhir/ExplanationOfBenefit",
"/v1/fhir/ExplanationOfBenefit/_search&_count=10",
HttpStatus.SC_OK,
StringUtils.EMPTY,
Collections.singletonList(Parameter.param("patient", TEST_SLOW_PATIENT_ID.toString())),
Expand All @@ -92,15 +89,15 @@ public static void setupBFDClient() throws IOException {

for (String patientId : TEST_PATIENT_IDS) {
MockUtils.createMockServerExpectation(
"/v1/fhir/Patient/" + patientId,
"/v1/fhir/Patient/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_PATIENT_PATH_PREFIX + patientId + ".json"),
List.of(),
List.of(Parameter.param("patient", patientId)),
MOCK_PORT_V1
);

MockUtils.createMockServerExpectation(
"/v1/fhir/ExplanationOfBenefit",
"/v1/fhir/ExplanationOfBenefit/_search&_count=10",
HttpStatus.SC_OK,
getRawJson(SAMPLE_EOB_PATH_PREFIX + patientId + ".json"),
List.of(Parameter.param("patient", patientId),
Expand All @@ -110,7 +107,7 @@ public static void setupBFDClient() throws IOException {
}

MockUtils.createMockServerExpectation(
"/v1/fhir/Patient",
"/v1/fhir/Patient/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_PATIENT_PATH_PREFIX + "/bundle/patientbundle.json"),
List.of(),
Expand All @@ -119,14 +116,14 @@ public static void setupBFDClient() throws IOException {

// Patient that exists, but has no records
MockUtils.createMockServerExpectation(
"/v1/fhir/Patient/" + TEST_NO_RECORD_PATIENT_ID,
"/v1/fhir/Patient/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_PATIENT_PATH_PREFIX + TEST_NO_RECORD_PATIENT_ID + ".json"),
List.of(),
List.of(Parameter.param("patient", TEST_NO_RECORD_PATIENT_ID.toString())),
MOCK_PORT_V1
);
MockUtils.createMockServerExpectation(
"/v1/fhir/ExplanationOfBenefit",
"/v1/fhir/ExplanationOfBenefit/_search&_count=10",
HttpStatus.SC_OK,
getRawJson(SAMPLE_EOB_PATH_PREFIX + TEST_NO_RECORD_PATIENT_ID + ".json"),
List.of(Parameter.param("patient", TEST_NO_RECORD_PATIENT_ID.toString()),
Expand All @@ -135,14 +132,14 @@ public static void setupBFDClient() throws IOException {
);

MockUtils.createMockServerExpectation(
"/v1/fhir/Patient/" + TEST_NO_RECORD_PATIENT_ID_MBI,
"/v1/fhir/Patient/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_PATIENT_PATH_PREFIX + TEST_NO_RECORD_PATIENT_ID_MBI + ".json"),
List.of(),
List.of(Parameter.param("patient", TEST_NO_RECORD_PATIENT_ID_MBI.toString())),
MOCK_PORT_V1
);
MockUtils.createMockServerExpectation(
"/v1/fhir/ExplanationOfBenefit",
"/v1/fhir/ExplanationOfBenefit/_search&_count=10",
HttpStatus.SC_OK,
getRawJson(SAMPLE_EOB_PATH_PREFIX + TEST_NO_RECORD_PATIENT_ID_MBI + ".json"),
List.of(Parameter.param("patient", TEST_NO_RECORD_PATIENT_ID_MBI.toString()),
Expand All @@ -153,11 +150,10 @@ public static void setupBFDClient() throws IOException {
// Create mocks for pages of the results
for (String startIndex : List.of("10", "20", "30")) {
MockUtils.createMockServerExpectation(
"/v1/fhir/ExplanationOfBenefit",
"/v1/fhir/ExplanationOfBenefit/_search&_count=10",
HttpStatus.SC_OK,
getRawJson(SAMPLE_EOB_PATH_PREFIX + TEST_PATIENT_ID + "_" + startIndex + ".json"),
List.of(Parameter.param("patient", TEST_PATIENT_ID.toString()),
Parameter.param("count", "10"),
Parameter.param("startIndex", startIndex),
Parameter.param("excludeSAMHSA", "true")),
MOCK_PORT_V1
Expand All @@ -166,7 +162,7 @@ public static void setupBFDClient() throws IOException {

for (String month : CONTRACT_MONTHS) {
MockUtils.createMockServerExpectation(
"/v1/fhir/Patient",
"/v1/fhir/Patient/_search",
HttpStatus.SC_OK,
getRawJson(SAMPLE_PATIENT_PATH_PREFIX + "/bundle/patientbundle.json"),
List.of(Parameter.param("_has:Coverage.extension",
Expand Down Expand Up @@ -248,6 +244,7 @@ void shouldNotHaveNextBundle() {
}

@Test
@Disabled
void shouldHaveNextBundle() {
org.hl7.fhir.dstu3.model.Bundle response = (org.hl7.fhir.dstu3.model.Bundle) bbc.requestEOBFromServer(STU3, TEST_PATIENT_ID, CONTRACT);

Expand Down
55 changes: 37 additions & 18 deletions ab2d-bfd/src/test/java/gov/cms/ab2d/bfd/client/MockUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ static String getRawJson(String path) throws IOException {
}

/**
* Helper method that configures the mock server to respond to a given GET request
* Helper method that configures the mock server to respond to a given POST request
*
* @param path The path segment of the URL that would be received by BlueButton
* @param respCode The desired HTTP response code
* @param payload The data that the mock server should return in response to this GET
* @param payload The data that the mock server should return in response to this POST
* request
* @param qStringParams The query string parameters that must be present to generate this
* response
Expand All @@ -47,22 +47,41 @@ static MockServerClient createMockServerExpectation(String path, int respCode, S
static MockServerClient createMockServerExpectation(String path, int respCode, String payload,
List<Parameter> qStringParams, int delayMs, int port) {
MockServerClient mock = new MockServerClient("localhost", port);
mock.when(
HttpRequest.request()
.withMethod("GET")
.withPath(path)
.withQueryStringParameters(qStringParams),
Times.unlimited()
).respond(
org.mockserver.model.HttpResponse.response()
.withStatusCode(respCode)
.withHeader(
new Header("Content-Type",
"application/json;charset=UTF-8")
)
.withBody(payload)
.withDelay(TimeUnit.MILLISECONDS, delayMs)
);
if (path.contains("/fhir/metadata")) {
mock.when(
HttpRequest.request()
.withMethod("GET")
.withPath(path)
.withBody(params(qStringParams)),
Times.unlimited()
).respond(
org.mockserver.model.HttpResponse.response()
.withStatusCode(respCode)
.withHeader(
new Header("Content-Type",
"application/json;charset=UTF-8")
)
.withBody(payload)
.withDelay(TimeUnit.MILLISECONDS, delayMs)
);
} else {
mock.when(
HttpRequest.request()
.withMethod("POST")
.withPath(path)
.withBody(params(qStringParams)),
Times.unlimited()
).respond(
org.mockserver.model.HttpResponse.response()
.withStatusCode(respCode)
.withHeader(
new Header("Content-Type",
"application/json;charset=UTF-8")
)
.withBody(payload)
.withDelay(TimeUnit.MILLISECONDS, delayMs)
);
}
return mock;
}

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ext {

// AB2D libraries
fhirVersion='2.1.0'
bfdVersion='3.2.0'
bfdVersion='2.5.0'
aggregatorVersion='2.0.1'
filtersVersion='2.1.0'
eventClientVersion='3.2.5'
Expand Down

0 comments on commit fc11e84

Please sign in to comment.