From 52557ade587c09cddd5e207547fc46fd0466fbbd Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Thu, 21 Sep 2023 15:09:51 +0300 Subject: [PATCH] Hot fix for location hiearchy view. (#1266) * Add new fixtures for generating location hierarchy * Add util functions to nest locations from flat array * Use new locations hieararchy hook * Update envs and documentation * filter for the root location using its id * add tests for helper utils * Fix regressions in fhir location management * Fix regressions in fhir-team-management * Fix some more lint errors * Fix even more lint errors --- .github/workflows/automation-ci.yml | 2 +- app/.env.sample | 2 + app/src/configs/env.ts | 2 +- docker/config.js.tmpl | 2 +- docs/env.md | 4 +- docs/fhir-web-docker-deployment.md | 3 +- .../components/AddEditLocationUnit/index.tsx | 4 +- .../AddEditLocationUnit/tests/index.test.tsx | 28 +- .../src/components/LocationForm/index.tsx | 12 +- .../src/components/LocationUnitList/index.tsx | 29 +- .../LocationUnitList/tests/index.test.tsx | 45 +- .../tests/{fixtures.ts => fixtures/index.ts} | 3 + .../ducks/tests/fixtures/locationsBundle.json | 925 ++++++++++++++++++ .../src/helpers/flat-to-nested.ts | 84 ++ .../src/helpers/tests/utils.test.ts | 14 + .../src/helpers/utils.ts | 43 +- .../OrganizationAffiliation/index.tsx | 25 +- .../tests/index.test.tsx | 41 +- 18 files changed, 1153 insertions(+), 115 deletions(-) rename packages/fhir-location-management/src/ducks/tests/{fixtures.ts => fixtures/index.ts} (99%) create mode 100644 packages/fhir-location-management/src/ducks/tests/fixtures/locationsBundle.json create mode 100644 packages/fhir-location-management/src/helpers/flat-to-nested.ts diff --git a/.github/workflows/automation-ci.yml b/.github/workflows/automation-ci.yml index ab6a1329b..6b288a255 100644 --- a/.github/workflows/automation-ci.yml +++ b/.github/workflows/automation-ci.yml @@ -101,7 +101,7 @@ jobs: REACT_APP_DEPLOYMENT_ENVIRONMENT=production REACT_APP_ENABLE_FHIR_ORGANIZATION=true REACT_APP_ENABLE_FHIR_TEAMS=true - REACT_APP_FHIR_ROOT_LOCATION_IDENTIFIER=eff94f33-c356-4634-8795-d52340706ba9 + REACT_APP_FHIR_ROOT_LOCATION_ID=eff94f33-c356-4634-8795-d52340706ba9 REACT_APP_FHIR_PATIENT_SORT_FIELDS=-_lastUpdated REACT_APP_FHIR_PATIENT_BUNDLE_SIZE=5000 REACT_APP_ENABLE_FHIR_HEALTHCARE_SERVICES=false diff --git a/app/.env.sample b/app/.env.sample index 3563e4a90..bea652efe 100644 --- a/app/.env.sample +++ b/app/.env.sample @@ -36,6 +36,7 @@ REACT_APP_SUPPORTED_LANGUAGES=en,fr REACT_APP_ENABLE_LANGUAGE_SWITCHER=true # user roles REACT_APP_OPENSRP_ROLES="{"USERS": "ROLE_EDIT_KEYCLOAK_USERS", "PLANS": "ROLE_VIEW_KEYCLOAK_USERS", "LOCATIONS": "ROLE_VIEW_KEYCLOAK_USERS", "CARD_SUPPORT": "ROLE_VIEW_KEYCLOAK_USERS", "INVENTORY": "ROLE_VIEW_KEYCLOAK_USERS", "TEAMS": "ROLE_VIEW_KEYCLOAK_USERS", "PRODUCT_CATALOGUE": "ROLE_VIEW_KEYCLOAK_USERS", "FORM_CONFIGURATION": "ROLE_VIEW_KEYCLOAK_USERS", "CARE_TEAM": "ROLE_VIEW_KEYCLOAK_USERS", "SERVER_SETTINGS": "ROLE_VIEW_KEYCLOAK_USERS", "QUEST": "ROLE_VIEW_KEYCLOAK_USERS", "MANAGE_REPORTS": "ROLE_MANAGE_REPORTS", "DISTRICT_REPORT": "ROLE_DISTRICT_REPORT"}" + REACT_APP_DISABLE_TEAM_MEMBER_REASSIGNMENT=false REACT_APP_USER_FORM_HIDDEN_FIELDS="" REACT_APP_USER_FORM_RENDER_FIELDS="" @@ -50,3 +51,4 @@ REACT_APP_ENABLE_FHIR_GROUP=true REACT_APP_ENABLE_FHIR_COMMODITY=true REACT_APP_COMMODITIES_LIST_RESOURCE_ID="uuid" REACT_APP_PRACTITIONER_TO_ORG_ASSIGNMENT_STRATEGY=ONE_TO_MANY +REACT_APP_FHIR_ROOT_LOCATION_ID=uuid diff --git a/app/src/configs/env.ts b/app/src/configs/env.ts index 43e19261a..04a06ae9e 100644 --- a/app/src/configs/env.ts +++ b/app/src/configs/env.ts @@ -217,7 +217,7 @@ export const FHIR_PATIENT_SORT_FIELDS = setEnv( export const FHIR_PATIENT_BUNDLE_SIZE = Number(setEnv('REACT_APP_FHIR_PATIENT_BUNDLE_SIZE', 5000)); -export const FHIR_ROOT_LOCATION_IDENTIFIER = setEnv('REACT_APP_FHIR_ROOT_LOCATION_IDENTIFIER', ''); +export const FHIR_ROOT_LOCATION_IDENTIFIER = setEnv('REACT_APP_FHIR_ROOT_LOCATION_ID', ''); export const ENABLE_SERVER_SETTINGS = setEnv('REACT_APP_ENABLE_SERVER_SETTINGS', false) === 'true'; diff --git a/docker/config.js.tmpl b/docker/config.js.tmpl index 497770dfa..83c5ac52c 100644 --- a/docker/config.js.tmpl +++ b/docker/config.js.tmpl @@ -65,7 +65,7 @@ window._env_ = { REACT_APP_OPENSRP_WEB_VERSION: "{{ getv "/react/app/opensrp/web/version" "" }}", REACT_APP_SENTRY_CONFIG_JSON: "{{ getv "/react/app/opensrp/sentry/config/json" "" }}", REACT_APP_ENABLE_FHIR_LOCATIONS: "{{ getv "/react/app/enable/fhir/locations" "false" }}", - REACT_APP_FHIR_ROOT_LOCATION_IDENTIFIER: "{{ getv "react/app/fhir/root/location/identifier" "" }}", + REACT_APP_FHIR_ROOT_LOCATION_ID: "{{ getv "react/app/fhir/root/location/id" "" }}", REACT_APP_ENABLE_QUEST: "{{ getv "react/app/enable/quest" "false" }}", REACT_APP_ENABLE_FHIR_HEALTHCARE_SERVICES: "{{ getv "/react/app/enable/fhir/healthcare/services" "false" }}", REACT_APP_ENABLE_FHIR_GROUP: "{{ getv "/react/app/enable/fhir/group" "false" }}", diff --git a/docs/env.md b/docs/env.md index 264639519..267e4f76f 100644 --- a/docs/env.md +++ b/docs/env.md @@ -209,9 +209,9 @@ Below is a list of currently supported environment variables: - **Optional**(`boolean`) - default: `"false"` -- **REACT_APP_FHIR_ROOT_LOCATION_IDENTIFIER** +- **REACT_APP_FHIR_ROOT_LOCATION_ID** - - FHIR Hierarchy root location UUID + - FHIR Hierarchy root location id - **Optional**_(`string`)_ - **REACT_APP_OPENSRP_WEB_VERSION** diff --git a/docs/fhir-web-docker-deployment.md b/docs/fhir-web-docker-deployment.md index 43d065496..61d3fd6fc 100644 --- a/docs/fhir-web-docker-deployment.md +++ b/docs/fhir-web-docker-deployment.md @@ -145,8 +145,7 @@ We use different technologies to deploy fhir-web. This documentation will focus REACT_APP_FHIR_API_BASE_URL: '/fhir', // UUID's - REACT_APP_FHIR_ROOT_LOCATION_IDENTIFIER: - '', + REACT_APP_FHIR_ROOT_LOCATION_ID: '', REACT_APP_COMMODITIES_LIST_RESOURCE_ID: '', // toggle fhir-web modules diff --git a/packages/fhir-location-management/src/components/AddEditLocationUnit/index.tsx b/packages/fhir-location-management/src/components/AddEditLocationUnit/index.tsx index bc3fe6508..c5bdd1872 100644 --- a/packages/fhir-location-management/src/components/AddEditLocationUnit/index.tsx +++ b/packages/fhir-location-management/src/components/AddEditLocationUnit/index.tsx @@ -6,7 +6,7 @@ import { Row, Col, Spin } from 'antd'; import { PageHeader } from '@opensrp/react-utils'; import { Helmet } from 'react-helmet'; import { BrokenPage, Resource404 } from '@opensrp/react-utils'; -import { useGetLocation, useGetLocationHierarchy } from '../../helpers/utils'; +import { useGetLocation, useGetLocationsAsHierarchy } from '../../helpers/utils'; import { useTranslation } from '../../mls'; export type LocationRouteProps = { id?: string }; @@ -49,7 +49,7 @@ export const NewEditLocationUnit = (props: NewEditLocationUnitProps) => { history.push(cancelURL); }; - const { data, error, isLoading } = useGetLocationHierarchy( + const { data, error, isLoading } = useGetLocationsAsHierarchy( fhirBaseURL, fhirRootLocationIdentifier ); diff --git a/packages/fhir-location-management/src/components/AddEditLocationUnit/tests/index.test.tsx b/packages/fhir-location-management/src/components/AddEditLocationUnit/tests/index.test.tsx index 7d3a34c4a..ce2e5bec0 100644 --- a/packages/fhir-location-management/src/components/AddEditLocationUnit/tests/index.test.tsx +++ b/packages/fhir-location-management/src/components/AddEditLocationUnit/tests/index.test.tsx @@ -7,8 +7,8 @@ import { Provider } from 'react-redux'; import { authenticateUser } from '@onaio/session-reducer'; import { QueryClient, QueryClientProvider } from 'react-query'; import nock from 'nock'; -import { locationHierarchyResourceType } from '../../../constants'; -import { fhirHierarchy } from '../../../ducks/tests/fixtures'; +import { locationHierarchyResourceType, locationResourceType } from '../../../constants'; +import { locationSData } from '../../../ducks/tests/fixtures'; import { waitForElementToBeRemoved } from '@testing-library/dom'; import { createdLocation1 } from '../../LocationForm/tests/fixtures'; import { cleanup, render, screen, waitFor } from '@testing-library/react'; @@ -28,7 +28,7 @@ const queryClient = new QueryClient({ const props = { fhirBaseURL: 'http://test.server.org', - fhirRootLocationIdentifier: 'someId', + fhirRootLocationIdentifier: '2252', }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -80,9 +80,14 @@ test('renders correctly for new locations', async () => { const cancelUrlGenerator = '/cancelled'; nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) - .reply(200, fhirHierarchy); + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) + .reply(200, { total: 1000 }); + + nock(props.fhirBaseURL) + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) + .reply(200, locationSData); nock(props.fhirBaseURL).get('/Location/someId').reply(200, createdLocation1); @@ -108,9 +113,14 @@ test('renders correctly for edit locations', async () => { history.push(`/add/${createdLocation1.partOf.identifier}?parentId=Location/303`); nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) - .reply(200, fhirHierarchy); + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) + .reply(200, { total: 1000 }); + + nock(props.fhirBaseURL) + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) + .reply(200, locationSData); nock(props.fhirBaseURL) .get(`/Location/${createdLocation1.partOf.identifier}`) diff --git a/packages/fhir-location-management/src/components/LocationForm/index.tsx b/packages/fhir-location-management/src/components/LocationForm/index.tsx index 9bd8352e1..8e0ec8e57 100644 --- a/packages/fhir-location-management/src/components/LocationForm/index.tsx +++ b/packages/fhir-location-management/src/components/LocationForm/index.tsx @@ -9,7 +9,7 @@ import { postPutLocationUnit, validationRulesFactory, } from './utils'; -import { locationHierarchyResourceType } from '../../constants'; +import { locationResourceType } from '../../constants'; import { CustomTreeSelect, CustomTreeSelectProps } from './CustomTreeSelect'; import { IfhirR4 } from '@smile-cdr/fhirts'; import { TreeNode } from '../../helpers/types'; @@ -154,14 +154,8 @@ const LocationForm = (props: LocationFormProps) => { const successUrl = successURLGenerator(payload); sendSuccessNotification(successMessage); afterSubmit?.(payload); - // hierarchy request usually takes quite a while to resolve, this coupled with react-query's request - // de-duping mechanism means that more recent requests will get deduped on the pending request. - // the pending request then resolves with stale data. - // This cancels any pending request so that after invalidation we can get a fresh promise launched then. - queryClient.cancelQueries([locationHierarchyResourceType]).catch((err) => { - throw err; - }); - queryClient.invalidateQueries([locationHierarchyResourceType]).catch((err) => { + + queryClient.refetchQueries([locationResourceType]).catch((err) => { throw err; }); setSuccessUrl(successUrl); diff --git a/packages/fhir-location-management/src/components/LocationUnitList/index.tsx b/packages/fhir-location-management/src/components/LocationUnitList/index.tsx index 0c16d42a8..05eb0cbd8 100644 --- a/packages/fhir-location-management/src/components/LocationUnitList/index.tsx +++ b/packages/fhir-location-management/src/components/LocationUnitList/index.tsx @@ -6,15 +6,13 @@ import { PageHeader } from '@opensrp/react-utils'; import { PlusOutlined } from '@ant-design/icons'; import { LocationUnitDetail } from '../LocationUnitDetail'; import { useHistory } from 'react-router-dom'; -import { FHIRServiceClass, BrokenPage, Resource404 } from '@opensrp/react-utils'; -import { locationHierarchyResourceType, URL_LOCATION_UNIT_ADD } from '../../constants'; -import { useQuery } from 'react-query'; +import { BrokenPage, Resource404 } from '@opensrp/react-utils'; +import { URL_LOCATION_UNIT_ADD } from '../../constants'; import Table, { TableData } from './Table'; import Tree from '../LocationTree'; -import { convertApiResToTree } from '../../helpers/utils'; +import { useGetLocationsAsHierarchy } from '../../helpers/utils'; import './LocationUnitList.css'; import { TreeNode } from '../../helpers/types'; -import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; import { useSelector, useDispatch } from 'react-redux'; import reducerRegistry from '@onaio/redux-reducer-registry'; import { @@ -70,31 +68,12 @@ export const LocationUnitList: React.FC = (props: Locatio const { t } = useTranslation(); const history = useHistory(); - const hierarchyParams = { - identifier: fhirRootLocationIdentifier, - }; - - // get the root locations. the root node is the opensrp root location, its immediate children - // are the user-defined root locations. const { data: treeData, isLoading: treeIsLoading, error: treeError, isFetching: treeIsFetching, - } = useQuery( - [locationHierarchyResourceType, hierarchyParams], - async ({ signal }) => { - return new FHIRServiceClass(fhirBaseURL, locationHierarchyResourceType, signal).list( - hierarchyParams - ); - }, - { - select: (res) => { - return convertApiResToTree(res); - }, - staleTime: Infinity, // prevent refetches on things like window refocus - } - ); + } = useGetLocationsAsHierarchy(fhirBaseURL, fhirRootLocationIdentifier); if (treeIsLoading) { return ; diff --git a/packages/fhir-location-management/src/components/LocationUnitList/tests/index.test.tsx b/packages/fhir-location-management/src/components/LocationUnitList/tests/index.test.tsx index 2b2e87b66..d171fd322 100644 --- a/packages/fhir-location-management/src/components/LocationUnitList/tests/index.test.tsx +++ b/packages/fhir-location-management/src/components/LocationUnitList/tests/index.test.tsx @@ -11,13 +11,13 @@ import { screen, within, fireEvent, - waitFor, } from '@testing-library/react'; import { Router } from 'react-router'; import { createBrowserHistory } from 'history'; -import { fhirHierarchy, onaOfficeSubLocation } from '../../../ducks/tests/fixtures'; +import { onaOfficeSubLocation } from '../../../ducks/tests/fixtures'; import { Provider } from 'react-redux'; -import { locationHierarchyResourceType } from '../../../constants'; +import { locationResourceType } from '../../../constants'; +import { locationSData } from '../../../ducks/tests/fixtures'; const history = createBrowserHistory(); @@ -26,7 +26,7 @@ jest.mock('fhirclient', () => { }); const props = { - fhirRootLocationIdentifier: 'eff94f33-c356-4634-8795-d52340706ba9', + fhirRootLocationIdentifier: '2252', fhirBaseURL: 'http://test.server.org', }; @@ -84,17 +84,23 @@ describe('location-management/src/components/LocationUnitList', () => { it('shows broken page', async () => { nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) + .reply(200, { total: 1000 }) + .persist(); + + nock(props.fhirBaseURL) + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) .replyWithError({ message: 'something awful happened', code: 'AWFUL_ERROR', }); nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: 'missing' }) - .reply(200, {}); + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) + .reply(200, []); const { rerender } = render(); @@ -114,9 +120,14 @@ describe('location-management/src/components/LocationUnitList', () => { it('works correctly', async () => { nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) - .reply(200, fhirHierarchy) + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) + .reply(200, { total: 1000 }); + + nock(props.fhirBaseURL) + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) + .reply(200, locationSData) .persist(); nock(props.fhirBaseURL).get('/Location/303').reply(200, onaOfficeSubLocation); @@ -187,15 +198,5 @@ describe('location-management/src/components/LocationUnitList', () => { // table change- node deselect expect(document.querySelectorAll('table tbody tr')).toHaveLength(1); - - // invalidate queries to initiate a refetch of locationhierarchy - queryClient.invalidateQueries([locationHierarchyResourceType]).catch((err) => { - throw err; - }); - - await waitFor(() => { - expect(screen.getByText(/Refreshing data/i)).toBeInTheDocument(); - }); - await waitForElementToBeRemoved(screen.getByText(/Refreshing data/i)); }); }); diff --git a/packages/fhir-location-management/src/ducks/tests/fixtures.ts b/packages/fhir-location-management/src/ducks/tests/fixtures/index.ts similarity index 99% rename from packages/fhir-location-management/src/ducks/tests/fixtures.ts rename to packages/fhir-location-management/src/ducks/tests/fixtures/index.ts index 93958190e..7f9db7ff3 100644 --- a/packages/fhir-location-management/src/ducks/tests/fixtures.ts +++ b/packages/fhir-location-management/src/ducks/tests/fixtures/index.ts @@ -1,5 +1,8 @@ import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; import { ILocation } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ILocation'; +import * as locationSData from './locationsBundle.json'; + +export { locationSData }; export const onaOfficeSubLocation = { resourceType: 'Location', diff --git a/packages/fhir-location-management/src/ducks/tests/fixtures/locationsBundle.json b/packages/fhir-location-management/src/ducks/tests/fixtures/locationsBundle.json new file mode 100644 index 000000000..1ccb0c759 --- /dev/null +++ b/packages/fhir-location-management/src/ducks/tests/fixtures/locationsBundle.json @@ -0,0 +1,925 @@ +{ + "resourceType": "Bundle", + "id": "a4f1fd42-7ebd-40f6-8ea7-42fc600afd9f", + "meta": { + "lastUpdated": "2023-09-20T16:22:52.530+00:00" + }, + "type": "searchset", + "total": 711, + "link": [ + { + "relation": "self", + "url": "https://fhir.labs.smartregister.org/fhir/Location/_search?_count=711&_total=accurate" + } + ], + "entry": [ + { + "resource": { + "resourceType": "Location", + "id": "2252", + "meta": { + "versionId": "3", + "lastUpdated": "2021-10-14T13:10:14.524+00:00", + "source": "#5887f723a045b500" + }, + "identifier": [ + { + "use": "official", + "value": "eff94f33-c356-4634-8795-d52340706ba9" + } + ], + "status": "active", + "name": "Root FHIR Location", + "alias": ["Root Location"], + "description": "This is the Root Location that all other locations are part of. Any locations that are directly part of this should be displayed as the root location.", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/2252", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "303", + "meta": { + "versionId": "4", + "lastUpdated": "2021-10-14T13:12:22.740+00:00", + "source": "#13bbc7f09daa1751" + }, + "identifier": [ + { + "use": "official", + "value": "93bc9c3d-6321-41b0-9b93-1275d7114e22" + } + ], + "status": "active", + "name": "Ona Office Sub Location", + "alias": ["ona office"], + "description": "The Sub location", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/2252", + "display": "Root FHIR Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/303", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3700", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-25T17:35:03.037+00:00", + "source": "#29bc16483de49917" + }, + "identifier": [ + { + "use": "official", + "value": "aefb6487-952f-43e0-890e-13bc49c66d7a" + } + ], + "status": "active", + "name": "Level 0", + "alias": ["alias 0"], + "description": "This is a test desc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/303", + "display": "Ona Office Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3700", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "971", + "meta": { + "versionId": "15", + "lastUpdated": "2021-10-22T13:59:17.824+00:00", + "source": "#8c44090bcdecfecd" + }, + "identifier": [ + { + "use": "official", + "value": "0f184aac-83e7-498e-8232-5c8b360ad97b" + } + ], + "status": "active", + "name": "Arundel mobile clinic", + "alias": ["Part of you"], + "description": "Vaccination Site", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/303", + "display": "Ona Office Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/971", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3453", + "meta": { + "versionId": "2", + "lastUpdated": "2021-10-14T13:12:50.738+00:00", + "source": "#ee7f951054c1498e" + }, + "identifier": [ + { + "use": "official", + "value": "c3ff106e-91cb-4346-b45f-d7ad5346af5a" + } + ], + "status": "active", + "name": "Part Of Sub Location", + "alias": ["part off"], + "description": "Testing the part of ", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/303", + "display": "Ona Office Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3453", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3698", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-25T11:47:40.060+00:00", + "source": "#7b5eb661a6a154a6" + }, + "identifier": [ + { + "use": "official", + "value": "cef7b7fd-46cd-4975-bd47-b1a6d045dc3d" + } + ], + "status": "active", + "name": "Nairobii", + "alias": ["Desc alias"], + "description": "Desc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3453", + "display": "Part Of Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3698", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3701", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-25T17:39:30.390+00:00", + "source": "#ed9c4d2c21507afc" + }, + "identifier": [ + { + "use": "official", + "value": "b4bed6fc-35eb-4de1-ab3a-d7df5bf7f346" + } + ], + "status": "active", + "name": "Langata", + "alias": ["langata alias"], + "description": "langata desc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3698", + "display": "Nairobii" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3701", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3699", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-25T12:13:50.248+00:00", + "source": "#648444f01bec6b9e" + }, + "identifier": [ + { + "use": "official", + "value": "6f7b3491-3651-4986-b5a7-befd3fbc9905" + } + ], + "status": "active", + "name": "Ngong", + "alias": ["Ngongy"], + "description": "Ngong Location", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3698", + "display": "Nairobii" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3699", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3691", + "meta": { + "versionId": "8", + "lastUpdated": "2021-10-25T09:25:24.653+00:00", + "source": "#6d829f7729f539f0" + }, + "identifier": [ + { + "use": "official", + "value": "4991bf8d-fdbd-48a5-8b02-75d57369573e" + } + ], + "status": "active", + "name": "Test Sub Loc", + "alias": ["Test Sub"], + "description": "The Test Sub Loc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3453", + "display": "Part Of Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3691", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3752", + "meta": { + "versionId": "3", + "lastUpdated": "2021-10-25T18:23:41.730+00:00", + "source": "#e53fb49dd17be516" + }, + "identifier": [ + { + "use": "official", + "value": "40d75934-2a30-4426-9100-912e3c3fc459" + } + ], + "status": "active", + "name": "Embakasiii", + "alias": ["Emba"], + "description": "Emba Desc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3691", + "display": "Test Sub Loc" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3752", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3590", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-15T09:59:46.554+00:00", + "source": "#bc4c70fa7e68ac36" + }, + "identifier": [ + { + "use": "official", + "value": "9f7f985b-8226-4971-ae67-c3ef241cbe01" + } + ], + "status": "active", + "name": "Test Loc 113", + "alias": ["113"], + "description": "Desc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3453", + "display": "Part Of Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3590", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "739", + "meta": { + "versionId": "19", + "lastUpdated": "2021-10-15T12:09:59.809+00:00", + "source": "#68c24697322968a2" + }, + "identifier": [ + { + "use": "official", + "value": "2829f50c-9189-43ee-8def-a775437c2f0b" + } + ], + "status": "active", + "name": "Test Loc 1145", + "alias": ["ona officee"], + "description": "This is a test", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3453", + "display": "Part Of Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/739", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3464", + "meta": { + "versionId": "7", + "lastUpdated": "2021-10-25T18:30:13.490+00:00", + "source": "#a6eb7dc2fdf82484" + }, + "identifier": [ + { + "use": "official", + "value": "f725bbe8-989d-4dd9-8346-d9630e813660" + } + ], + "status": "active", + "name": "MOH HQ 2", + "alias": ["mohh"], + "description": "HL7 Headquarters", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3453", + "display": "Part Of Sub Location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3464", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3753", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-25T18:32:20.171+00:00", + "source": "#00e92dd273348bed" + }, + "identifier": [ + { + "use": "official", + "value": "fe124438-6c7f-4869-bd89-b3c0fd5703ee" + } + ], + "status": "active", + "name": "MOH Sub location", + "alias": ["MOH Sub location"], + "description": "MOH Sub location desc", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3464", + "display": "MOH HQ 2" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3753", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3756", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T07:46:41.904+00:00", + "source": "#ce245f0bc54b347b" + }, + "identifier": [ + { + "use": "official", + "value": "d8e58b43-141e-4b29-a789-81ab6677c2ba" + } + ], + "status": "active", + "name": "MOH SUB SUB LOCATION 1", + "alias": ["MOH SUB SUB LOCATION 1"], + "description": "MOH SUB SUB LOCATION 1", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3753", + "display": "MOH Sub location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3756", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3757", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T07:48:19.889+00:00", + "source": "#d11c67f354d91869" + }, + "identifier": [ + { + "use": "official", + "value": "26401d5f-167a-4df8-a4b8-243add38779b" + } + ], + "status": "active", + "name": "MOH SUB SUB SUB LOCATION 1", + "alias": ["MOH SUB SUB SUB LOCATION 1"], + "description": "MOH SUB SUB SUB LOCATION 1", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3756", + "display": "MOH SUB SUB LOCATION 1" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3757", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3754", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T07:43:36.298+00:00", + "source": "#18b051aedc5f2f73" + }, + "identifier": [ + { + "use": "official", + "value": "e13e4c1c-d5c6-43e3-94b0-7efd3f2153c4" + } + ], + "status": "active", + "name": "MOH SUB SUB LOCATION", + "alias": ["MOH SUB SUB LOCATION"], + "description": "MOH SUB SUB LOCATION", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3753", + "display": "MOH Sub location" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3754", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3755", + "meta": { + "versionId": "2", + "lastUpdated": "2021-10-26T07:50:53.540+00:00", + "source": "#5339f0868dd39478" + }, + "identifier": [ + { + "use": "official", + "value": "08fd0492-c513-4814-8dca-b5d19feb79d1" + } + ], + "status": "active", + "name": "MOH HQ", + "alias": ["MOH HQ"], + "description": "MOH HQ", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3754", + "display": "MOH SUB SUB LOCATION" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3755", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3773", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T10:57:59.491+00:00", + "source": "#10a430746e2909fd" + }, + "identifier": [ + { + "use": "official", + "value": "baa79c00-1605-4a3a-bb51-856cee820a96" + } + ], + "status": "active", + "name": "MOH SUB LOCATION 1", + "alias": ["MOH SUB LOCATION 1"], + "description": "MOH SUB LOCATION 1", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3755", + "display": "MOH HQ" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3773", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3774", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T10:59:20.719+00:00", + "source": "#03c5b06d6f5c392c" + }, + "identifier": [ + { + "use": "official", + "value": "4e6a37e9-d832-42f8-b019-cd5a951f385d" + } + ], + "status": "active", + "name": "MOH SUB SUB LOCATION 1", + "alias": ["MOH SUB SUB LOCATION 1"], + "description": "MOH SUB SUB LOCATION 1", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3773", + "display": "MOH SUB LOCATION 1" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3774", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3758", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T08:21:48.281+00:00", + "source": "#fb1ac76193a49328" + }, + "identifier": [ + { + "use": "official", + "value": "60eb5ec3-0800-4cb1-b316-2c8ca996db2d" + } + ], + "status": "active", + "name": "MOH SUB LOCATION", + "alias": ["MOH SUB LOCATION"], + "description": "MOH SUB LOCATION", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3755", + "display": "MOH HQ" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3758", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3772", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T10:56:51.495+00:00", + "source": "#8dec0ad9b4b20d3c" + }, + "identifier": [ + { + "use": "official", + "value": "adba8456-6bdd-470e-a056-ece49867f60e" + } + ], + "status": "active", + "name": "MOH SUB SUB LOCATION", + "alias": ["MOH SUB SUB LOCATION"], + "description": "MOH SUB SUB LOCATION", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3758", + "display": "MOH SUB LOCATION" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3772", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3775", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T11:32:51.171+00:00", + "source": "#55d61406a3186f08" + }, + "identifier": [ + { + "use": "official", + "value": "58d49744-bb61-47b4-990e-c4b88e7aa1c6" + } + ], + "status": "active", + "name": "MOH SUB SUB SUB LOCATION", + "alias": ["MOH SUB SUB SUB LOCATION"], + "description": "MOH SUB SUB SUB LOCATION", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3772", + "display": "MOH SUB SUB LOCATION" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3775", + "search": { + "mode": "match" + } + }, + { + "resource": { + "resourceType": "Location", + "id": "3776", + "meta": { + "versionId": "1", + "lastUpdated": "2021-10-26T11:40:02.091+00:00", + "source": "#3b9b650a82a1ebf0" + }, + "identifier": [ + { + "use": "official", + "value": "1815f774-c85b-4bb1-825d-f6af11753d08" + } + ], + "status": "active", + "name": "MOH SUB SUB SUB LOCATION 1", + "alias": ["MOH SUB SUB SUB LOCATION 1"], + "description": "MOH SUB SUB SUB LOCATION 1", + "physicalType": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/location-physical-type", + "code": "jdn", + "display": "Jurisdiction" + } + ] + }, + "partOf": { + "reference": "Location/3775", + "display": "MOH SUB SUB SUB LOCATION" + } + }, + "fullUrl": "https://fhir.labs.smartregister.org/fhir/Location/3776", + "search": { + "mode": "match" + } + } + ] +} diff --git a/packages/fhir-location-management/src/helpers/flat-to-nested.ts b/packages/fhir-location-management/src/helpers/flat-to-nested.ts new file mode 100644 index 000000000..26d3bbd4e --- /dev/null +++ b/packages/fhir-location-management/src/helpers/flat-to-nested.ts @@ -0,0 +1,84 @@ +import { ILocation } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ILocation'; +import { CommonHierarchyNode } from './types'; + +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +/** + * Convert a hierarchy from flat to nested representation. + * + * @param {Array} locations The array with the hierachy flat representation. + */ +export function nestLocations(locations: ILocation[]) { + let id, parentId; + + const roots = []; + const temp: Record = {}; + const pendingChildOf: Record = {}; + + for (let i = 0, len = locations.length; i < len; i++) { + const rawLocation = locations[i]; + const location = { + nodeId: rawLocation.id as string, + label: rawLocation.name ?? '', + node: rawLocation, + }; + id = rawLocation.id as string; + parentId = getParentResourceId(rawLocation); + temp[id] = location; + if (parentId === undefined || parentId === null) { + // Current object has no parent, so it's a root element. + roots.push(location); + } else { + if (temp[parentId] !== undefined) { + // Parent is already in temp, adding the current object to its children array. + initPush('children', temp[parentId], location); + } else { + // Parent for this object is not yet in temp, adding it to pendingChildOf. + initPush(parentId, pendingChildOf, location); + } + } + if (pendingChildOf[id] !== undefined) { + // Current object has children pending for it. Adding these to the object. + multiInitPush('children', location, pendingChildOf[id]); + } + } + return roots; +} + +const initPush = (arrayName: string, obj: unknown, toPush: unknown) => { + const typedObj = obj as Record; + if (typedObj[arrayName] === undefined) { + typedObj[arrayName] = []; + } + typedObj[arrayName].push(toPush); +}; + +const multiInitPush = (arrayName: string, obj: unknown, toPushArray: unknown[]) => { + let len; + len = toPushArray.length; + const typedObj = obj as Record; + if (typedObj[arrayName] === undefined) { + typedObj[arrayName] = []; + } + while (len-- > 0) { + typedObj[arrayName].push(toPushArray.shift()); + } +}; + +/** + * Gets id of parent location from a location resource. + * + * @param obj - location object to get parent id from + * @returns - parent id or undefined + */ +const getParentResourceId = (obj: ILocation) => { + const reference = obj.partOf?.reference; + if (reference === undefined) { + return undefined; + } + const parts = reference.split('/'); + if (parts.length < 2) { + return undefined; + } + return parts[parts.length - 1]; +}; diff --git a/packages/fhir-location-management/src/helpers/tests/utils.test.ts b/packages/fhir-location-management/src/helpers/tests/utils.test.ts index 91da5be88..c6b0907b2 100644 --- a/packages/fhir-location-management/src/helpers/tests/utils.test.ts +++ b/packages/fhir-location-management/src/helpers/tests/utils.test.ts @@ -1,5 +1,11 @@ import { convertApiResToTree, serializeTree } from '../utils'; import { fhirHierarchy, serializedSample } from '../../ducks/tests/fixtures'; +import { locationSData } from '../../ducks/tests/fixtures'; +import { getResourcesFromBundle } from '@opensrp/react-utils'; +import { ILocation } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ILocation'; +import TreeModel from 'tree-model'; +import { nestLocations } from '../flat-to-nested'; +import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; test('parses fhir hierarchy correctly', () => { const tree = convertApiResToTree(fhirHierarchy); @@ -56,3 +62,11 @@ test('serializes tress', () => { expect(serializeTree([tree])).toEqual(serializedSample); expect(serializeTree()).toBeUndefined(); }); + +test('can create tree from flat array of locations', () => { + const locations = getResourcesFromBundle(locationSData as IBundle); + const roots = nestLocations(locations); + + const tree = new TreeModel().parse(roots[0]); + expect(tree.model.node.id).toEqual('2252'); +}); diff --git a/packages/fhir-location-management/src/helpers/utils.ts b/packages/fhir-location-management/src/helpers/utils.ts index 223cce8da..c81364bdc 100644 --- a/packages/fhir-location-management/src/helpers/utils.ts +++ b/packages/fhir-location-management/src/helpers/utils.ts @@ -1,13 +1,20 @@ -import { ChildNodeList, LocationHierarchyResource, ParsedHierarchyNode, TreeNode } from './types'; +import { + ChildNodeList, + CommonHierarchyNode, + LocationHierarchyResource, + ParsedHierarchyNode, + TreeNode, +} from './types'; import { cloneDeep } from 'lodash'; import cycle from 'cycle'; import TreeModel from 'tree-model'; import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; import { useQuery } from 'react-query'; -import { FHIRServiceClass } from '@opensrp/react-utils'; +import { FHIRServiceClass, getResourcesFromBundle, loadAllResources } from '@opensrp/react-utils'; import { locationHierarchyResourceType, locationResourceType } from '../constants'; import { ILocation } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ILocation'; +import { nestLocations } from './flat-to-nested'; /** * Parse the raw child hierarchy node map @@ -69,6 +76,38 @@ export const serializeTree = (trees?: TreeNode[] | TreeNode) => { return JSON.stringify(sanitizeTrees.map((tree) => JSON.stringify(cycle.decycle(tree)))); }; +/** + * get locations and create a hiearrchy from which we extract the location tree whose + * root location id is given. + * + * @param baseUrl - the server base url + * @param rootIdentifier - the location id + */ +export const useGetLocationsAsHierarchy = (baseUrl: string, rootIdentifier: string) => { + return useQuery( + [locationResourceType, rootIdentifier], + async () => { + return loadAllResources(baseUrl, locationResourceType); + }, + { + select: (res: IBundle) => { + const rawLocations = cloneDeep(getResourcesFromBundle(res)); + const nested = nestLocations(rawLocations); + const interestingRoot = nested.filter((root) => root.nodeId === `${rootIdentifier}`)[0] as + | CommonHierarchyNode + | undefined; + if (!interestingRoot) { + return undefined; + } + const tree = new TreeModel().parse(interestingRoot); + return tree; + }, + refetchInterval: false, + staleTime: Infinity, // prevent refetches on things like window refocus + } + ); +}; + /** * get the location hierarchy of location with given identifier * diff --git a/packages/fhir-team-management/src/components/OrganizationAffiliation/index.tsx b/packages/fhir-team-management/src/components/OrganizationAffiliation/index.tsx index 8b6d78770..3cfecaad5 100644 --- a/packages/fhir-team-management/src/components/OrganizationAffiliation/index.tsx +++ b/packages/fhir-team-management/src/components/OrganizationAffiliation/index.tsx @@ -2,18 +2,15 @@ import React from 'react'; import { Helmet } from 'react-helmet'; import { PageHeader } from '@opensrp/react-utils'; import { Row, Col, Spin } from 'antd'; -import { FHIRServiceClass, BrokenPage, Resource404 } from '@opensrp/react-utils'; -import { useQuery } from 'react-query'; +import { BrokenPage, Resource404 } from '@opensrp/react-utils'; import AffiliationTable from './Table'; -import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; import { useSelector, useDispatch } from 'react-redux'; import reducerRegistry from '@onaio/redux-reducer-registry'; import { - locationHierarchyResourceType, - convertApiResToTree, Tree, TreeNode, locationTreeStateDucks, + useGetLocationsAsHierarchy, } from '@opensrp/fhir-location-management'; import { useTranslation } from '../../mls'; @@ -32,29 +29,13 @@ export const AffiliationList: React.FC = (props: Location const dispatch = useDispatch(); const { t } = useTranslation(); - const hierarchyParams = { - identifier: fhirRootLocationIdentifier, - }; - // get the root locations. the root node is the opensrp root location, its immediate children // are the user-defined root locations. const { data: treeData, isLoading: treeIsLoading, error: treeError, - } = useQuery( - [locationHierarchyResourceType, hierarchyParams], - async () => { - return new FHIRServiceClass(fhirBaseURL, locationHierarchyResourceType).list( - hierarchyParams - ); - }, - { - select: (res) => { - return res && convertApiResToTree(res); - }, - } - ); + } = useGetLocationsAsHierarchy(fhirBaseURL, fhirRootLocationIdentifier); if (treeIsLoading) { return ; diff --git a/packages/fhir-team-management/src/components/OrganizationAffiliation/tests/index.test.tsx b/packages/fhir-team-management/src/components/OrganizationAffiliation/tests/index.test.tsx index 8a1b2e9a9..4112672c0 100644 --- a/packages/fhir-team-management/src/components/OrganizationAffiliation/tests/index.test.tsx +++ b/packages/fhir-team-management/src/components/OrganizationAffiliation/tests/index.test.tsx @@ -9,17 +9,12 @@ import { cleanup, render, screen, waitFor, within } from '@testing-library/react import { fireEvent, waitForElementToBeRemoved } from '@testing-library/dom'; import { createMemoryHistory } from 'history'; import { authenticateUser } from '@onaio/session-reducer'; -import { locationHierarchyResourceType } from '@opensrp/fhir-location-management'; -import { - allAffiliations, - allOrgs, - createdAffiliation1, - createdAffiliation2, - fhirHierarchy, -} from './fixures'; +import { locationResourceType } from '@opensrp/fhir-location-management'; +import { allAffiliations, allOrgs, createdAffiliation1, createdAffiliation2 } from './fixures'; import { organizationAffiliationResourceType, organizationResourceType } from '../../../constants'; import userEvent from '@testing-library/user-event'; import * as notifications from '@opensrp/notifications'; +import { locationSData } from '@opensrp/fhir-location-management/src/ducks/tests/fixtures'; jest.mock('@opensrp/notifications', () => ({ __esModule: true, @@ -52,7 +47,7 @@ const queryClient = new QueryClient({ const props = { fhirBaseURL: 'http://test.server.org', - fhirRootLocationIdentifier: 'rootLoc', + fhirRootLocationIdentifier: '2252', }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -108,9 +103,15 @@ test('Edits organization affiliation correctly', async () => { history.push('/assignments'); nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) - .reply(200, fhirHierarchy); + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) + .reply(200, { total: 1000 }); + + nock(props.fhirBaseURL) + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) + .reply(200, locationSData) + .persist(); nock(props.fhirBaseURL) .get(`/${organizationAffiliationResourceType}/_search`) @@ -279,8 +280,8 @@ test('api error response', async () => { history.push('/assignments'); nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) .replyWithError('Something awful happened'); render( @@ -302,9 +303,15 @@ test('api undefined response', async () => { history.push('/assignments'); nock(props.fhirBaseURL) - .get(`/${locationHierarchyResourceType}/_search`) - .query({ identifier: props.fhirRootLocationIdentifier }) - .reply(200, null); + .get(`/${locationResourceType}/_search`) + .query({ _summary: 'count' }) + .reply(200, { total: 1000 }); + + nock(props.fhirBaseURL) + .get(`/${locationResourceType}/_search`) + .query({ _count: 1000 }) + .reply(200, []) + .persist(); render(