Skip to content

Commit

Permalink
Merge pull request #40 from onaio/flatten_location_hierarchy
Browse files Browse the repository at this point in the history
Flatten the LocationHierarchy endpoint response
  • Loading branch information
lincmba authored Apr 15, 2024
2 parents a9f3c09 + 4b90bd6 commit 26905cb
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 119 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,19 @@ Example:
[GET] /List?_id=<some-id>&_count=<page-size>&_page=<page-number>&_sort=<some-sort>
```

##### LocationHierarchy list mode

The LocationHierarchy endpoint supports two response formats: tree and list. By
default, the response format remains a tree, providing hierarchical location
data. In addition, clients can request the endpoint to return location resources
in a flat list format by providing a request parameter `mode=list`.

Example:

```
[GET] /LocationHierarchy?identifier=<some-location-id>&mode=list&_count=<page-size>&_page=<page-number>&_sort=<some-sort>
```

#### Important Note:

Developers, please update your client applications accordingly to accommodate
Expand Down
4 changes: 2 additions & 2 deletions exec/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.smartregister</groupId>
<artifactId>opensrp-gateway-plugin</artifactId>
<version>1.0.8</version>
<version>1.0.9</version>
</parent>

<artifactId>exec</artifactId>
Expand Down Expand Up @@ -70,7 +70,7 @@
<dependency>
<groupId>org.smartregister</groupId>
<artifactId>plugins</artifactId>
<version>1.0.8</version>
<version>1.0.9</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion plugins/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.smartregister</groupId>
<artifactId>opensrp-gateway-plugin</artifactId>
<version>1.0.8</version>
<version>1.0.9</version>
</parent>

<artifactId>plugins</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
package org.smartregister.fhir.gateway.plugins;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServlet;

import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Resource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;

public abstract class BaseEndpoint extends HttpServlet {
public Bundle createBundle(List<Resource> resourceList) {
Bundle responseBundle = new Bundle();
List<Bundle.BundleEntryComponent> bundleEntryComponentList = new ArrayList<>();

for (Resource resource : resourceList) {
bundleEntryComponentList.add(new Bundle.BundleEntryComponent().setResource(resource));
}

responseBundle.setEntry(bundleEntryComponentList);
responseBundle.setTotal(bundleEntryComponentList.size());
return responseBundle;
}

public Bundle createEmptyBundle(String requestURL) {
Bundle responseBundle = new Bundle();
responseBundle.setId(UUID.randomUUID().toString());
Bundle.BundleLinkComponent linkComponent = new Bundle.BundleLinkComponent();
linkComponent.setRelation(Bundle.LINK_SELF);
linkComponent.setUrl(requestURL);
responseBundle.setLink(Collections.singletonList(linkComponent));
responseBundle.setType(Bundle.BundleType.SEARCHSET);
responseBundle.setTotal(0);
return responseBundle;
}
protected final FhirContext fhirR4Context = FhirContext.forR4();
protected final IParser fhirR4JsonParser = fhirR4Context.newJsonParser().setPrettyPrint(true);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.Location;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
Expand All @@ -16,6 +17,8 @@ public enum CacheHelper {

Cache<String, DomainResource> resourceCache;

Cache<String, List<Location>> locationListCache;

CacheHelper() {
cache =
Caffeine.newBuilder()
Expand All @@ -27,6 +30,11 @@ public enum CacheHelper {
.expireAfterWrite(getCacheExpiryDurationInSeconds(), TimeUnit.SECONDS)
.maximumSize(DEFAULT_CACHE_SIZE)
.build();
locationListCache =
Caffeine.newBuilder()
.expireAfterWrite(getCacheExpiryDurationInSeconds(), TimeUnit.SECONDS)
.maximumSize(DEFAULT_CACHE_SIZE)
.build();
}

private int getCacheExpiryDurationInSeconds() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public class Constants {
public static final String DEFAULT_RELATED_ENTITY_TAG_URL =
"https://smartregister.org/related-entity-location-tag-id";
public static final String ROLE_ALL_LOCATIONS = "ALL_LOCATIONS";
public static final String MODE = "mode";
public static final String LIST = "list";

public interface Literals {
String EQUALS = "=";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpStatus;
import org.hl7.fhir.r4.model.Bundle;
import org.smartregister.model.location.LocationHierarchy;

import com.google.fhir.gateway.TokenVerifier;
Expand Down Expand Up @@ -38,21 +39,32 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)
try {
RestUtils.checkAuthentication(request, tokenVerifier);
String identifier = request.getParameter(Constants.IDENTIFIER);
String mode = request.getParameter(Constants.MODE);

LocationHierarchy locationHierarchy =
locationHierarchyEndpointHelper.getLocationHierarchy(identifier);
String resultContent;
if (Constants.LIST.equals(mode)) {
Bundle resultBundle =
locationHierarchyEndpointHelper.getPaginatedLocations(request);
resultContent = fhirR4JsonParser.encodeResourceToString(resultBundle);

if (org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND.equals(
locationHierarchy.getId())) {
resultContent =
fhirR4JsonParser.encodeResourceToString(
createEmptyBundle(
request.getRequestURL() + "?" + request.getQueryString()));
} else {
resultContent =
fhirR4JsonParser.encodeResourceToString(
createBundle(Collections.singletonList(locationHierarchy)));
LocationHierarchy locationHierarchy =
locationHierarchyEndpointHelper.getLocationHierarchy(identifier);

if (org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND.equals(
locationHierarchy.getId())) {
resultContent =
fhirR4JsonParser.encodeResourceToString(
Utils.createEmptyBundle(
request.getRequestURL()
+ "?"
+ request.getQueryString()));
} else {
resultContent =
fhirR4JsonParser.encodeResourceToString(
Utils.createBundle(
Collections.singletonList(locationHierarchy)));
}
}
response.setContentType("application/json");
response.getOutputStream().print(resultContent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
import static org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;

import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -56,7 +60,7 @@ public LocationHierarchy getLocationHierarchyCore(String locationId) {
LocationHierarchy locationHierarchy = new LocationHierarchy();
if (location != null) {
logger.info("Building Location Hierarchy of Location Id : " + locationId);
locationHierarchyTree.buildTreeFromList(getLocationHierarchy(locationId, location));
locationHierarchyTree.buildTreeFromList(getDescendants(locationId, location));
StringType locationIdString = new StringType().setId(locationId).getIdElement();
locationHierarchy.setLocationId(locationIdString);
locationHierarchy.setId(LOCATION_RESOURCE + locationId);
Expand All @@ -69,11 +73,21 @@ public LocationHierarchy getLocationHierarchyCore(String locationId) {
return locationHierarchy;
}

private List<Location> getLocationHierarchy(String locationId, Location parentLocation) {
return descendants(locationId, parentLocation);
private List<Location> getLocationHierarchyLocations(
String locationId, Location parentLocation) {
List<Location> descendants;

if (CacheHelper.INSTANCE.skipCache()) {
descendants = getDescendants(locationId, parentLocation);
} else {
descendants =
CacheHelper.INSTANCE.locationListCache.get(
locationId, key -> getDescendants(locationId, parentLocation));
}
return descendants;
}

public List<Location> descendants(String locationId, Location parentLocation) {
public List<Location> getDescendants(String locationId, Location parentLocation) {

Bundle childLocationBundle =
getFhirClientForR4()
Expand All @@ -93,14 +107,14 @@ public List<Location> descendants(String locationId, Location parentLocation) {
Location childLocationEntity = (Location) childLocation.getResource();
allLocations.add(childLocationEntity);
allLocations.addAll(
descendants(childLocationEntity.getIdElement().getIdPart(), null));
getDescendants(childLocationEntity.getIdElement().getIdPart(), null));
}
}

return allLocations;
}

private @Nullable Location getLocationById(String locationId) {
public @Nullable Location getLocationById(String locationId) {
Location location = null;
try {
location =
Expand All @@ -111,4 +125,43 @@ public List<Location> descendants(String locationId, Location parentLocation) {
}
return location;
}

public Bundle getPaginatedLocations(HttpServletRequest request) {
String identifier = request.getParameter(Constants.IDENTIFIER);
String pageSize = request.getParameter(Constants.PAGINATION_PAGE_SIZE);
String pageNumber = request.getParameter(Constants.PAGINATION_PAGE_NUMBER);
Map<String, String[]> parameters = new HashMap<>(request.getParameterMap());

int count =
pageSize != null
? Integer.parseInt(pageSize)
: Constants.PAGINATION_DEFAULT_PAGE_SIZE;
int page =
pageNumber != null
? Integer.parseInt(pageNumber)
: Constants.PAGINATION_DEFAULT_PAGE_NUMBER;

int start = Math.max(0, (page - 1)) * count;
Location parentLocation = getLocationById(identifier);
List<Location> locations = getLocationHierarchyLocations(identifier, parentLocation);
List<Resource> resourceLocations = new ArrayList<>(locations);
int totalEntries = locations.size();

int end = Math.min(start + count, resourceLocations.size());
List<Resource> paginatedResourceLocations = resourceLocations.subList(start, end);
Bundle resultBundle;

if (locations.isEmpty()) {
resultBundle =
Utils.createEmptyBundle(
request.getRequestURL() + "?" + request.getQueryString());
} else {
resultBundle = Utils.createBundle(paginatedResourceLocations);
StringBuilder urlBuilder = new StringBuilder(request.getRequestURL());
Utils.addPaginationLinks(
urlBuilder, resultBundle, page, totalEntries, count, parameters);
}

return resultBundle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,11 @@

import com.google.fhir.gateway.TokenVerifier;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;

@WebServlet("/PractitionerDetail")
public class PractitionerDetailEndpoint extends BaseEndpoint {
private final TokenVerifier tokenVerifier;
private final FhirContext fhirR4Context = FhirContext.forR4();
private final IParser fhirR4JsonParser = fhirR4Context.newJsonParser().setPrettyPrint(true);
private final PractitionerDetailsEndpointHelper practitionerDetailsEndpointHelper;

public PractitionerDetailEndpoint() throws IOException {
Expand All @@ -47,12 +43,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)
practitionerDetails.getId())) {
resultContent =
fhirR4JsonParser.encodeResourceToString(
createEmptyBundle(
Utils.createEmptyBundle(
request.getRequestURL() + "?" + request.getQueryString()));
} else {
resultContent =
fhirR4JsonParser.encodeResourceToString(
createBundle(Collections.singletonList(practitionerDetails)));
Utils.createBundle(Collections.singletonList(practitionerDetails)));
}

response.setContentType("application/json");
Expand Down
Loading

0 comments on commit 26905cb

Please sign in to comment.