Skip to content

Commit

Permalink
Hot fix for location hiearchy view. (#1266)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
peterMuriuki authored Sep 21, 2023
1 parent 428f78d commit 52557ad
Show file tree
Hide file tree
Showing 18 changed files with 1,153 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/automation-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions app/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -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=""
Expand All @@ -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
2 changes: 1 addition & 1 deletion app/src/configs/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
2 changes: 1 addition & 1 deletion docker/config.js.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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" }}",
Expand Down
4 changes: 2 additions & 2 deletions docs/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
3 changes: 1 addition & 2 deletions docs/fhir-web-docker-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ We use different technologies to deploy fhir-web. This documentation will focus
REACT_APP_FHIR_API_BASE_URL: '<fhir-server-base-url>/fhir',
// UUID's
REACT_APP_FHIR_ROOT_LOCATION_IDENTIFIER:
'<identifier-of-the-root-location-on-the-HAPI-server>',
REACT_APP_FHIR_ROOT_LOCATION_ID: '<identifier-of-the-root-location-on-the-HAPI-server>',
REACT_APP_COMMODITIES_LIST_RESOURCE_ID: '<id-of-a-list-on-HAPI-fhir-server>',
// toggle fhir-web modules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -49,7 +49,7 @@ export const NewEditLocationUnit = (props: NewEditLocationUnitProps) => {
history.push(cancelURL);
};

const { data, error, isLoading } = useGetLocationHierarchy(
const { data, error, isLoading } = useGetLocationsAsHierarchy(
fhirBaseURL,
fhirRootLocationIdentifier
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -70,31 +68,12 @@ export const LocationUnitList: React.FC<LocationUnitListProps> = (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<IBundle, Error, TreeNode | undefined>(
[locationHierarchyResourceType, hierarchyParams],
async ({ signal }) => {
return new FHIRServiceClass<IBundle>(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 <Spin size="large" className="custom-spinner" />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -26,7 +26,7 @@ jest.mock('fhirclient', () => {
});

const props = {
fhirRootLocationIdentifier: 'eff94f33-c356-4634-8795-d52340706ba9',
fhirRootLocationIdentifier: '2252',
fhirBaseURL: 'http://test.server.org',
};

Expand Down Expand Up @@ -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(<AppWrapper {...props} />);

Expand All @@ -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);
Expand Down Expand Up @@ -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));
});
});
Original file line number Diff line number Diff line change
@@ -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',
Expand Down
Loading

0 comments on commit 52557ad

Please sign in to comment.