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

[DPC-4175] Create PoC for pagination #2255

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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 @@ -23,7 +23,7 @@ protected AbstractPatientResource() {
}

@GET
public abstract Bundle patientSearch(OrganizationPrincipal organization, @NoHtml String patientMBI);
public abstract Bundle patientSearch(OrganizationPrincipal organization, @NoHtml String patientMBI, int limit, int page);

@POST
public abstract Response submitPatient(OrganizationPrincipal organization, @Valid @Profiled Patient patient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import gov.cms.dpc.api.resources.AbstractPatientResource;
import gov.cms.dpc.bluebutton.client.BlueButtonClient;
import gov.cms.dpc.common.annotations.NoHtml;
import gov.cms.dpc.common.utils.PagingUtils;
import gov.cms.dpc.fhir.DPCIdentifierSystem;
import gov.cms.dpc.fhir.DPCResourceType;
import gov.cms.dpc.fhir.FHIRExtractors;
Expand Down Expand Up @@ -77,16 +78,20 @@ public PatientResource(@Named("attribution") IGenericClient client, FhirValidato
public Bundle patientSearch(@ApiParam(hidden = true)
@Auth OrganizationPrincipal organization,
@ApiParam(value = "Patient MBI")
@QueryParam(value = Patient.SP_IDENTIFIER) @NoHtml String patientMBI) {
@QueryParam(value = Patient.SP_IDENTIFIER) @NoHtml String patientMBI,
@ApiParam(value = "Patients per page")
@QueryParam(value = "_limit") int limit,
@ApiParam(value = "Page number")
@QueryParam(value = "_page") int page) {

final var request = this.client
var request = this.client
.search()
.forResource(Patient.class)
.encodedJson()
.where(Patient.ORGANIZATION.hasId(organization.getOrganization().getId()))
.returnBundle(Bundle.class);

if (patientMBI != null && !patientMBI.equals("")) {
if (patientMBI != null && !patientMBI.isEmpty()) {

// Handle MBI parsing
// This should come out as part of DPC-432
Expand All @@ -96,12 +101,18 @@ public Bundle patientSearch(@ApiParam(hidden = true)
} else {
expandedMBI = String.format("%s|%s", DPCIdentifierSystem.MBI.getSystem(), patientMBI);
}
return request
.where(Patient.IDENTIFIER.exactly().identifier(expandedMBI))
.execute();
request = request.where(Patient.IDENTIFIER.exactly().identifier(expandedMBI));
}

return request.execute();
return PagingUtils.handlePaging(request, limit, page, "/v1/Patient");
}

public Bundle patientSearch(OrganizationPrincipal organization, String patientMBI, int page) {
return patientSearch(organization, patientMBI, PagingUtils.defaultLimit, page);
}

public Bundle patientSearch(OrganizationPrincipal organization, String patientMBI) {
return patientSearch(organization, patientMBI, PagingUtils.defaultLimit, 1);
}

@FHIR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import gov.cms.dpc.api.auth.OrganizationPrincipal;
import gov.cms.dpc.bluebutton.client.BlueButtonClient;
import gov.cms.dpc.common.utils.NPIUtil;
import gov.cms.dpc.common.utils.PagingUtils;
import gov.cms.dpc.fhir.DPCIdentifierSystem;
import gov.cms.dpc.fhir.DPCResourceType;
import gov.cms.dpc.queue.service.DataService;
Expand Down Expand Up @@ -116,6 +117,83 @@ public void testPatientSearchNoIdentifier() {
assertEquals(bundle, actualResponse);
}

@Test
public void testPatientSearchPaging() {
UUID orgId = UUID.randomUUID();
Organization organization = new Organization();
organization.setId(orgId.toString());
OrganizationPrincipal organizationPrincipal = new OrganizationPrincipal(organization);
Patient p1 = new Patient();
p1.setId("patient-1");
p1.setManagingOrganizationTarget(organization);
Patient p2 = new Patient();
p2.setId("patient-2");
p2.setManagingOrganizationTarget(organization);
Patient p3 = new Patient();
p3.setId("patient-3");
p3.setManagingOrganizationTarget(organization);
Bundle bundle = new Bundle();
bundle.addEntry().setResource(p1);
bundle.setTotal(3);

@SuppressWarnings("unchecked")
IQuery<IBaseBundle> queryExec = mock(IQuery.class, Answers.RETURNS_DEEP_STUBS);
@SuppressWarnings("unchecked")
IQuery<Bundle> mockQuery = mock(IQuery.class);
when(attributionClient
.search()
.forResource(Patient.class)
.encodedJson()
).thenReturn(queryExec);
when(queryExec.where(any(ICriterion.class)).returnBundle(Bundle.class)).thenReturn(mockQuery);
when(mockQuery.execute()).thenReturn(bundle);

Bundle actualResponse = patientResource.patientSearch(organizationPrincipal, null);
assertEquals(bundle, actualResponse);
assertEquals(bundle.getEntry().size(), PagingUtils.defaultLimit);
assertEquals(bundle.getEntryFirstRep().getResource().getId(), "patient-1");

String requestPath = "/v1/Patient?page=";
assertEquals(bundle.getLink("self").getUrl(), requestPath + "1");
assertEquals(bundle.getLink("first").getUrl(), requestPath + "1");
assertEquals(bundle.getLink("next").getUrl(), requestPath + "2");
assertEquals(bundle.getLink("last").getUrl(), requestPath + "3");

Bundle bundle2 = new Bundle();
bundle2.addEntry().setResource(p2);
bundle2.setTotal(3);

when(mockQuery.execute()).thenReturn(bundle2);

Bundle response2 = patientResource.patientSearch(organizationPrincipal, null, 2);
assertEquals(bundle2, response2);
assertEquals(bundle2.getEntry().size(), PagingUtils.defaultLimit);
assertEquals(bundle2.getEntryFirstRep().getResource().getId(), "patient-2");

assertEquals(bundle2.getLink("self").getUrl(), requestPath + "2");
assertEquals(bundle2.getLink("first").getUrl(), requestPath + "1");
assertEquals(bundle2.getLink("previous").getUrl(), requestPath + "1");
assertEquals(bundle2.getLink("next").getUrl(), requestPath + "3");
assertEquals(bundle2.getLink("last").getUrl(), requestPath + "3");

Bundle bundle3 = new Bundle();
bundle3.addEntry().setResource(p3);
bundle3.setTotal(3);

when(mockQuery.execute()).thenReturn(bundle3);

Bundle response3 = patientResource.patientSearch(organizationPrincipal, null, 3);
assertEquals(bundle3, response3);
assertEquals(bundle3.getEntry().size(), PagingUtils.defaultLimit);
assertEquals(bundle3.getEntryFirstRep().getResource().getId(), "patient-3");

assertEquals(bundle3.getLink("self").getUrl(), requestPath + "3");
assertEquals(bundle3.getLink("first").getUrl(), requestPath + "1");
assertEquals(bundle3.getLink("previous").getUrl(), requestPath + "2");
assertEquals(bundle3.getLink("last").getUrl(), requestPath + "3");

}

@Test
public void testSubmitPatient() {
UUID orgId = UUID.randomUUID();
Expand Down
32 changes: 32 additions & 0 deletions dpc-common/src/main/java/gov/cms/dpc/common/utils/PagingUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package gov.cms.dpc.common.utils;

import ca.uhn.fhir.rest.gclient.IQuery;
import org.hl7.fhir.dstu3.model.Bundle;

public class PagingUtils {
public static final int defaultLimit = 1;

private static String formatURL(String url, int page) {
return url + "?page=" + page;
}

private static void addRelationLink(Bundle bundle, String name, String path, int page) {
bundle.addLink().setRelation(name).setUrl(formatURL(path, page));
}

public static Bundle handlePaging(IQuery<Bundle> request, int limit, int page, String requestPath) {
request.offset(limit*(page-1));
request.count(limit);
Bundle resultBundle = request.execute();

addRelationLink(resultBundle, "self", requestPath, page);
addRelationLink(resultBundle, "first", requestPath, 1);
if (page > 1) addRelationLink(resultBundle, "previous", requestPath, page-1);

int lastPage = (int) Math.ceil((float) resultBundle.getTotal() / limit);
if (page + 1 <= lastPage) addRelationLink(resultBundle, "next", requestPath, page+1);
addRelationLink(resultBundle, "last", requestPath, lastPage);

return resultBundle;
}
}
Loading