Skip to content

Commit

Permalink
Address changes:
Browse files Browse the repository at this point in the history
 - Add README documentation
 - Move Magic Strings to constants file
 - Add functionality to a separate use case of when `/List?_id` is used
 - Update Tests with these changes
  • Loading branch information
lincmba committed Feb 2, 2024
1 parent 9b0e0ef commit 5fb275a
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 7 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,25 @@ URL:
This approach ensures consistency and clarity when accessing various endpoint
types through the gateway.

### Pagination

Pagination is supported in fetching data from a FHIR server using the List
endpoint. This can be useful when dealing with Resources with high volume of
entries like Locations.

To enable pagination, you need to include two parameters in the request URL:

- `_page`: This parameter specifies the page number and has a default value
of 1.
- `_count`: This parameter sets the number of items per page and has a default
value of 20.

Example:

```
[GET] /List?_id=<some-id>&_count=<page-size>&_page=<page-number>&_sort=<some-sort>
```

#### Important Note:

Developers, please update your client applications accordingly to accommodate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ public class Constants {
public static final String KEYCLOAK_UUID = "keycloak-uuid";
public static final String IDENTIFIER = "identifier";

public static final String PAGINATION_PAGE_SIZE = "_count";

public static final String PAGINATION_PAGE_NUMBER = "_page";

public static final int PAGINATION_DEFAULT_PAGE_SIZE = 20;

public static final int PAGINATION_DEFAULT_PAGE_NUMBER = 1;

public interface Literals {
String EQUALS = "=";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,17 +220,17 @@ private static Bundle processListEntriesGatewayModeByListResource(
Bundle requestBundle = new Bundle();
requestBundle.setType(Bundle.BundleType.BATCH);

String[] pageSize = request.getParameters().get("_count");
String[] pageNumber = request.getParameters().get("_page");
String[] pageSize = request.getParameters().get(Constants.PAGINATION_PAGE_SIZE);
String[] pageNumber = request.getParameters().get(Constants.PAGINATION_PAGE_NUMBER);

int count =
pageSize != null && pageSize.length > 0
? Integer.parseInt(pageSize[0])
: 20; // Default page size to 20 if not provided
: Constants.PAGINATION_DEFAULT_PAGE_SIZE;
int page =
pageNumber != null && pageNumber.length > 0
? Integer.parseInt(pageNumber[0])
: 1; // Default page number to 1 if not provided
: Constants.PAGINATION_DEFAULT_PAGE_NUMBER;

int start = Math.max(0, (page - 1)) * count;
int end = start + count;
Expand All @@ -248,10 +248,25 @@ private static Bundle processListEntriesGatewayModeByListResource(
return requestBundle;
}

private Bundle processListEntriesGatewayModeByBundle(IBaseResource responseResource) {
private Bundle processListEntriesGatewayModeByBundle(
IBaseResource responseResource, RequestDetailsReader request) {
Bundle requestBundle = new Bundle();
requestBundle.setType(Bundle.BundleType.BATCH);

String[] pageSize = request.getParameters().get(Constants.PAGINATION_PAGE_SIZE);
String[] pageNumber = request.getParameters().get(Constants.PAGINATION_PAGE_NUMBER);

int count =
pageSize != null && pageSize.length > 0
? Integer.parseInt(pageSize[0])
: Constants.PAGINATION_DEFAULT_PAGE_SIZE;
int page =
pageNumber != null && pageNumber.length > 0
? Integer.parseInt(pageNumber[0])
: Constants.PAGINATION_DEFAULT_PAGE_NUMBER;

int start = Math.max(0, (page - 1)) * count;

List<Bundle.BundleEntryComponent> bundleEntryComponentList =
((Bundle) responseResource)
.getEntry().stream()
Expand All @@ -260,6 +275,8 @@ private Bundle processListEntriesGatewayModeByBundle(IBaseResource responseResou
bundleEntryComponent ->
((ListResource) bundleEntryComponent.getResource())
.getEntry().stream())
.skip(start)
.limit(count)
.map(
listEntryComponent ->
createBundleEntryComponent(
Expand Down Expand Up @@ -306,7 +323,7 @@ private Bundle postProcessModeListEntries(

} else if (responseResource instanceof Bundle) {

requestBundle = processListEntriesGatewayModeByBundle(responseResource);
requestBundle = processListEntriesGatewayModeByBundle(responseResource, request);
}

return fhirR4Client.transaction().withBundle(requestBundle).execute();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ public void testPostProcessWithListModeHeaderSearchByTagShouldFetchListEntriesBu
}

@Test
public void testPostProcessWithListModePaginateEntriesBundle() throws IOException {
public void testPostProcessWithListModeHeaderPaginateEntriesBundle() throws IOException {
locationIds.add("Location-1");
testInstance = Mockito.spy(createSyncAccessDecisionTestInstance(Constants.LOCATION));

Expand Down Expand Up @@ -568,6 +568,88 @@ public void testPostProcessWithListModePaginateEntriesBundle() throws IOExceptio
resultContent);
}

@Test
public void testPostProcessWithListModeHeaderSearchByTagPaginateEntriesBundle()
throws IOException {
locationIds.add("Location-1");
testInstance = Mockito.spy(createSyncAccessDecisionTestInstance(Constants.LOCATION));

FhirContext fhirR4Context = mock(FhirContext.class);
IGenericClient iGenericClient = mock(IGenericClient.class);
ITransaction iTransaction = mock(ITransaction.class);
ITransactionTyped<Bundle> iClientExecutable = mock(ITransactionTyped.class);

Mockito.when(iGenericClient.transaction()).thenReturn(iTransaction);
Mockito.when(iTransaction.withBundle(any(Bundle.class))).thenReturn(iClientExecutable);

Bundle resultBundle = new Bundle();
resultBundle.setType(Bundle.BundleType.BATCHRESPONSE);
resultBundle.setId("bundle-result-id");

Mockito.when(iClientExecutable.execute()).thenReturn(resultBundle);

ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);

testInstance.setFhirR4Context(fhirR4Context);

RequestDetailsReader requestDetailsSpy = Mockito.mock(RequestDetailsReader.class);

Mockito.when(
requestDetailsSpy.getHeader(
SyncAccessDecision.SyncAccessDecisionConstants.FHIR_GATEWAY_MODE))
.thenReturn(SyncAccessDecision.SyncAccessDecisionConstants.LIST_ENTRIES);

Map<String, String[]> params = new HashMap<>();
params.put("_count", new String[] {"1"});
params.put("_page", new String[] {"1"});

Mockito.when(requestDetailsSpy.getParameters()).thenReturn(params);

URL listUrl = Resources.getResource("test_list_resource.json");
String testListJson = Resources.toString(listUrl, StandardCharsets.UTF_8);

ListResource listResource =
(ListResource) FhirContext.forR4().newJsonParser().parseResource(testListJson);

Bundle bundle = new Bundle();
Bundle.BundleEntryComponent bundleEntryComponent = new Bundle.BundleEntryComponent();
bundleEntryComponent.setResource(listResource);
bundle.setType(Bundle.BundleType.BATCHRESPONSE);
bundle.setEntry(Arrays.asList(bundleEntryComponent));

HttpResponse fhirResponseMock =
Mockito.mock(HttpResponse.class, Answers.RETURNS_DEEP_STUBS);

TestUtil.setUpFhirResponseMock(
fhirResponseMock,
FhirContext.forR4().newJsonParser().encodeResourceToString(bundle));

testInstance.setFhirR4Client(iGenericClient);
testInstance.setFhirR4Context(FhirContext.forR4());
String resultContent = testInstance.postProcess(requestDetailsSpy, fhirResponseMock);

Mockito.verify(iTransaction).withBundle(bundleArgumentCaptor.capture());
Bundle requestBundle = bundleArgumentCaptor.getValue();

Assert.assertNotNull(requestBundle);
Assert.assertEquals(Bundle.BundleType.BATCH, requestBundle.getType());
List<Bundle.BundleEntryComponent> requestBundleEntries = requestBundle.getEntry();

// Only one bundle is returned based on the _page and _count params provided above
Assert.assertEquals(1, requestBundleEntries.size());

Assert.assertEquals(
Bundle.HTTPVerb.GET, requestBundleEntries.get(0).getRequest().getMethod());
Assert.assertEquals(
"Group/proxy-list-entry-id-1", requestBundleEntries.get(0).getRequest().getUrl());

// Verify returned result content from the server request
Assert.assertNotNull(resultContent);
Assert.assertEquals(
"{\"resourceType\":\"Bundle\",\"id\":\"bundle-result-id\",\"type\":\"batch-response\"}",
resultContent);
}

@After
public void cleanUp() {
locationIds.clear();
Expand Down

0 comments on commit 5fb275a

Please sign in to comment.