From 6fe6d4d57d53a119997c65e2acf3149eba703bdb Mon Sep 17 00:00:00 2001 From: lincmba Date: Tue, 30 Jul 2024 10:46:34 +0300 Subject: [PATCH 1/3] Move admin level filtering to be handled after fetching the locations fixes #75 --- .../fhir/gateway/plugins/Constants.java | 1 + .../LocationHierarchyEndpointHelper.java | 88 +++++++++++++++---- .../PractitionerDetailsEndpointHelper.java | 3 +- .../LocationHierarchyEndpointHelperTest.java | 19 ++-- 4 files changed, 87 insertions(+), 24 deletions(-) diff --git a/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/Constants.java b/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/Constants.java index 8e03060..ee9cccc 100644 --- a/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/Constants.java +++ b/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/Constants.java @@ -24,6 +24,7 @@ public class Constants { public static final String MIN_ADMIN_LEVEL = "administrativeLevelMin"; public static final String MAX_ADMIN_LEVEL = "administrativeLevelMax"; public static final int DEFAULT_MAX_ADMIN_LEVEL = 10; + public static final int DEFAULT_MIN_ADMIN_LEVEL = 0; 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; diff --git a/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelper.java b/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelper.java index 91bf38a..a931c8a 100644 --- a/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelper.java +++ b/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelper.java @@ -16,6 +16,8 @@ import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r4.model.Binary; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Composition; import org.hl7.fhir.r4.model.Location; import org.hl7.fhir.r4.model.Resource; @@ -52,39 +54,56 @@ private IGenericClient getFhirClientForR4() { return r4FHIRClient; } - public LocationHierarchy getLocationHierarchy(String locationId, List adminLevels) { + public LocationHierarchy getLocationHierarchy( + String locationId, + List preFetchAdminLevels, + List postFetchAdminLevels) { LocationHierarchy locationHierarchy; if (CacheHelper.INSTANCE.skipCache()) { - locationHierarchy = getLocationHierarchyCore(locationId, adminLevels); + locationHierarchy = + getLocationHierarchyCore(locationId, preFetchAdminLevels, postFetchAdminLevels); } else { locationHierarchy = (LocationHierarchy) CacheHelper.INSTANCE.resourceCache.get( locationId, - key -> getLocationHierarchyCore(locationId, adminLevels)); + key -> + getLocationHierarchyCore( + locationId, + preFetchAdminLevels, + postFetchAdminLevels)); } return locationHierarchy; } public List getLocationHierarchies( - List locationIds, List adminLevels) { + List locationIds, + List preFetchAdminLevels, + List postFetchAdminLevels) { List locationHierarchies = new ArrayList<>(); for (String locationId : locationIds) { - locationHierarchies.add(getLocationHierarchy(locationId, adminLevels)); + locationHierarchies.add( + getLocationHierarchy(locationId, preFetchAdminLevels, postFetchAdminLevels)); } return locationHierarchies; } - public LocationHierarchy getLocationHierarchyCore(String locationId, List adminLevels) { + public LocationHierarchy getLocationHierarchyCore( + String locationId, + List preFetchAdminLevels, + List postFetchAdminLevels) { Location location = getLocationById(locationId); LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); LocationHierarchy locationHierarchy = new LocationHierarchy(); if (location != null) { logger.info("Building Location Hierarchy of Location Id : " + locationId); + locationHierarchyTree.buildTreeFromList( - getDescendants(locationId, location, adminLevels)); + filterLocationsByAdminLevels( + getDescendants(locationId, location, preFetchAdminLevels), + postFetchAdminLevels)); StringType locationIdString = new StringType().setId(locationId).getIdElement(); locationHierarchy.setLocationId(locationIdString); locationHierarchy.setId(LOCATION_RESOURCE + locationId); @@ -97,19 +116,22 @@ public LocationHierarchy getLocationHierarchyCore(String locationId, List getLocationHierarchyLocations( - String locationId, Location parentLocation, List adminLevels) { + public List getLocationHierarchyLocations( + String locationId, + Location parentLocation, + List preFetchAdminLevels, + List postFetchAdminLevels) { List descendants; if (CacheHelper.INSTANCE.skipCache()) { - descendants = getDescendants(locationId, parentLocation, adminLevels); + descendants = getDescendants(locationId, parentLocation, preFetchAdminLevels); } else { descendants = CacheHelper.INSTANCE.locationListCache.get( locationId, - key -> getDescendants(locationId, parentLocation, adminLevels)); + key -> getDescendants(locationId, parentLocation, preFetchAdminLevels)); } - return descendants; + return filterLocationsByAdminLevels(descendants, postFetchAdminLevels); } public List getDescendants( @@ -171,14 +193,18 @@ public List getDescendants( public Bundle handleIdentifierRequest(HttpServletRequest request, String identifier) { String administrativeLevelMin = request.getParameter(Constants.MIN_ADMIN_LEVEL); String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL); - List adminLevels = + List preFetchAdminLevels = + generateAdminLevels( + String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax); + List postFetchAdminLevels = generateAdminLevels(administrativeLevelMin, administrativeLevelMax); String mode = request.getParameter(Constants.MODE); if (Constants.LIST.equals(mode)) { List locationIds = Collections.singletonList(identifier); return getPaginatedLocations(request, locationIds); } else { - LocationHierarchy locationHierarchy = getLocationHierarchy(identifier, adminLevels); + LocationHierarchy locationHierarchy = + getLocationHierarchy(identifier, preFetchAdminLevels, postFetchAdminLevels); return Utils.createBundle(Collections.singletonList(locationHierarchy)); } } @@ -191,7 +217,10 @@ public Bundle handleNonIdentifierRequest( String syncLocationsParam = request.getParameter(Constants.SYNC_LOCATIONS); String administrativeLevelMin = request.getParameter(Constants.MIN_ADMIN_LEVEL); String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL); - List adminLevels = + List preFetchAdminLevels = + generateAdminLevels( + String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax); + List postFetchAdminLevels = generateAdminLevels(administrativeLevelMin, administrativeLevelMax); List selectedSyncLocations = extractSyncLocations(syncLocationsParam); String practitionerId = verifiedJwt.getSubject(); @@ -223,7 +252,8 @@ public Bundle handleNonIdentifierRequest( && userRoles.contains(Constants.ROLE_ALL_LOCATIONS) && !selectedSyncLocations.isEmpty()) { List locationHierarchies = - getLocationHierarchies(selectedSyncLocations, adminLevels); + getLocationHierarchies( + selectedSyncLocations, preFetchAdminLevels, postFetchAdminLevels); List resourceList = locationHierarchies.stream() .map(locationHierarchy -> (Resource) locationHierarchy) @@ -294,7 +324,10 @@ public Bundle getPaginatedLocations(HttpServletRequest request, List loc String pageNumber = request.getParameter(Constants.PAGINATION_PAGE_NUMBER); String administrativeLevelMin = request.getParameter(Constants.MIN_ADMIN_LEVEL); String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL); - List adminLevels = + List preFetchAdminLevels = + generateAdminLevels( + String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax); + List postFetchAdminLevels = generateAdminLevels(administrativeLevelMin, administrativeLevelMax); Map parameters = new HashMap<>(request.getParameterMap()); @@ -313,7 +346,8 @@ public Bundle getPaginatedLocations(HttpServletRequest request, List loc for (String identifier : locationIds) { Location parentLocation = getLocationById(identifier); List locations = - getLocationHierarchyLocations(identifier, parentLocation, adminLevels); + getLocationHierarchyLocations( + identifier, parentLocation, preFetchAdminLevels, postFetchAdminLevels); resourceLocations.addAll(locations); } int totalEntries = resourceLocations.size(); @@ -374,4 +408,22 @@ public List generateAdminLevels( return adminLevels; } + + public List filterLocationsByAdminLevels( + List locations, List postFetchAdminLevels) { + List allLocations = new ArrayList<>(); + for (Location location : locations) { + for (CodeableConcept codeableConcept : location.getType()) { + List codings = codeableConcept.getCoding(); + for (Coding coding : codings) { + if (coding.getSystem().equals(Constants.DEFAULT_ADMIN_LEVEL_TYPE_URL)) { + if (postFetchAdminLevels.contains(coding.getCode())) { + allLocations.add(location); + } + } + } + } + } + return allLocations; + } } diff --git a/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/PractitionerDetailsEndpointHelper.java b/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/PractitionerDetailsEndpointHelper.java index 16865b2..7b7a00a 100755 --- a/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/PractitionerDetailsEndpointHelper.java +++ b/plugins/src/main/java/org/smartregister/fhir/gateway/plugins/PractitionerDetailsEndpointHelper.java @@ -575,7 +575,8 @@ public static List getLocationsHierarchy(List locatio LocationHierarchy locationHierarchy; for (String locationsIdentifier : locationsIdentifiers) { locationHierarchy = - locationHierarchyEndpointHelper.getLocationHierarchy(locationsIdentifier, null); + locationHierarchyEndpointHelper.getLocationHierarchy( + locationsIdentifier, null, null); if (!org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND.equals( locationHierarchy.getId())) locationHierarchyList.add(locationHierarchy); } diff --git a/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java b/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java index f96a02c..95c22be 100644 --- a/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java +++ b/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java @@ -54,7 +54,7 @@ public void testGetLocationHierarchyNotFound() { .when(client) .fetchResourceFromUrl(any(), any()); LocationHierarchy locationHierarchy = - locationHierarchyEndpointHelper.getLocationHierarchy("non-existent", null); + locationHierarchyEndpointHelper.getLocationHierarchy("non-existent", null, null); assertEquals( org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND, locationHierarchy.getId()); @@ -69,7 +69,7 @@ public void testGetLocationHierarchyFound() { .when(client) .fetchResourceFromUrl(Location.class, "Location/12345"); LocationHierarchy locationHierarchy = - locationHierarchyEndpointHelper.getLocationHierarchy("12345", null); + locationHierarchyEndpointHelper.getLocationHierarchy("12345", null, null); assertEquals("Location Resource : 12345", locationHierarchy.getId()); } @@ -100,9 +100,12 @@ public void testGetPaginatedLocationsPaginatesLocations() { Mockito.doCallRealMethod() .when(mockLocationHierarchyEndpointHelper) .getPaginatedLocations(request, locationIds); + Mockito.doCallRealMethod() + .when(mockLocationHierarchyEndpointHelper) + .filterLocationsByAdminLevels(locations, adminLevels); Mockito.doReturn(locations) .when(mockLocationHierarchyEndpointHelper) - .getDescendants("12345", null, adminLevels); + .getLocationHierarchyLocations("12345", null, adminLevels, adminLevels); Bundle resultBundle = mockLocationHierarchyEndpointHelper.getPaginatedLocations(request, locationIds); @@ -144,6 +147,7 @@ public void testHandleNonIdentifierRequestListModePaginatesLocations() { .getRequestURL(); Map parameters = new HashMap<>(); + parameters.put(Constants.MODE, new String[] {"list"}); parameters.put(Constants.SYNC_LOCATIONS, new String[] {"1,2,3,4"}); LocationHierarchyEndpointHelper mockLocationHierarchyEndpointHelper = @@ -152,6 +156,7 @@ public void testHandleNonIdentifierRequestListModePaginatesLocations() { mock(PractitionerDetailsEndpointHelper.class); DecodedJWT mockDecodedJWT = mock(DecodedJWT.class); MockedStatic mockJwtUtils = Mockito.mockStatic(JwtUtils.class); + List adminLevels = new ArrayList<>(); List locations = createLocationList(4, false); List locationIds = List.of("1", "2", "3", "4"); @@ -168,9 +173,13 @@ public void testHandleNonIdentifierRequestListModePaginatesLocations() { .when(mockLocationHierarchyEndpointHelper) .handleNonIdentifierRequest( request, mockPractitionerDetailsEndpointHelper, mockDecodedJWT); + Mockito.doCallRealMethod() + .when(mockLocationHierarchyEndpointHelper) + .filterLocationsByAdminLevels(locations, adminLevels); Mockito.doReturn(locations) .when(mockLocationHierarchyEndpointHelper) - .getDescendants(Mockito.anyString(), Mockito.any(), Mockito.any()); + .getLocationHierarchyLocations( + Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any()); Mockito.doReturn(Constants.SyncStrategy.RELATED_ENTITY_LOCATION) .when(mockLocationHierarchyEndpointHelper) .getSyncStrategyByAppId(Mockito.any()); @@ -232,7 +241,7 @@ public void testGenerateAdminLevelsWithInvalidRange() { } @Test - public void testGetDecendantsWithAdminLevelFiltersReturnsLocationsWithinAdminLevel() { + public void testGetDescendantsWithAdminLevelFiltersReturnsLocationsWithinAdminLevel() { String locationId = "12345"; Location parentLocation = new Location(); parentLocation.setId(locationId); From cfd53a92b0a42f227b92ff1babc2b2a0453e6f56 Mon Sep 17 00:00:00 2001 From: lincmba Date: Tue, 30 Jul 2024 11:00:23 +0300 Subject: [PATCH 2/3] Add test --- .../LocationHierarchyEndpointHelperTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java b/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java index 95c22be..184d8d0 100644 --- a/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java +++ b/plugins/src/test/java/org/smartregister/fhir/gateway/plugins/LocationHierarchyEndpointHelperTest.java @@ -285,6 +285,20 @@ public void testGetDescendantsWithAdminLevelFiltersReturnsLocationsWithinAdminLe verify(queryMock, times(2)).execute(); } + @Test + public void testFilterLocationsByAdminLevelsBasic() { + List locations = createLocationList(5, true); + List adminLevels = List.of("1", "3"); + + List filteredLocations = + locationHierarchyEndpointHelper.filterLocationsByAdminLevels( + locations, adminLevels); + + Assert.assertEquals(2, filteredLocations.size()); + Assert.assertEquals("1", filteredLocations.get(0).getId()); + Assert.assertEquals("3", filteredLocations.get(1).getId()); + } + private Bundle getLocationBundle() { Bundle bundleLocation = new Bundle(); bundleLocation.setId("Location/1234"); From 8b11812c673b637c88fa1dcdf5fd662698c36318 Mon Sep 17 00:00:00 2001 From: lincmba Date: Fri, 2 Aug 2024 13:45:30 +0300 Subject: [PATCH 3/3] Tag a new release --- exec/pom.xml | 4 ++-- plugins/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exec/pom.xml b/exec/pom.xml index 63327aa..803c4ad 100755 --- a/exec/pom.xml +++ b/exec/pom.xml @@ -4,7 +4,7 @@ org.smartregister opensrp-gateway-plugin - 2.0.3 + 2.0.4 exec @@ -70,7 +70,7 @@ org.smartregister plugins - 2.0.3 + 2.0.4 diff --git a/plugins/pom.xml b/plugins/pom.xml index 50f76e0..328bcab 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -4,7 +4,7 @@ org.smartregister opensrp-gateway-plugin - 2.0.3 + 2.0.4 plugins diff --git a/pom.xml b/pom.xml index 3fbc386..c2a3ac3 100755 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.smartregister opensrp-gateway-plugin - 2.0.3 + 2.0.4 pom