Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIIN-2811 Use consolidated locations endpoint to fetch all locations when in central tenant context. #2471

Merged
merged 2 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Jest/RTL: Cover InstancePlugin component with unit tests. Refs UIIN-2668.
* Jest/RTL: Cover ImportRecord component with unit test. Refs UIIN-2667.
* Jest/RTL: Cover MoveHoldingContext component with unit tests. Refs UIIN-2664.
* Use consolidated locations endpoint to fetch all locations when in central tenant context. Refs UIIN-2811.

## [11.0.4](https://github.com/folio-org/ui-inventory/tree/v11.0.4) (2024-04-30)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v11.0.3...v11.0.4)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,8 @@
"browse.subjects.instances.collection.get",
"browse.classification-numbers.instances.collection.get",
"perms.users.get",
"inventory-storage.bound-with-parts.collection.get"
"inventory-storage.bound-with-parts.collection.get",
"consortium-search.locations.collection.get"
],
"visible": true
},
Expand Down
22 changes: 18 additions & 4 deletions src/hooks/useLocationsForTenants/useLocationsForTenants.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { useQueries } from 'react-query';
import {
useQueries,
useQuery,
} from 'react-query';

import {
useNamespace,
useOkapiKy,
useStripes,
checkIfUserInCentralTenant,
} from '@folio/stripes/core';

import { isUserInConsortiumMode } from '../../utils';
Expand All @@ -18,9 +22,11 @@ const useLocationsForTenants = ({ tenantIds = [] }) => {
const stripes = useStripes();
const ky = useOkapiKy();

const isUserInCentralTenant = checkIfUserInCentralTenant(stripes);

const queries = useQueries(tenantIds.map(tenantId => {
return {
enabled: Boolean(tenantIds.length && isUserInConsortiumMode(stripes)),
enabled: Boolean(tenantIds.length && isUserInConsortiumMode(stripes) && !isUserInCentralTenant),
queryKey: [namespace, 'locations', tenantId],
queryFn: () => ky.get('locations', {
searchParams: {
Expand All @@ -38,10 +44,18 @@ const useLocationsForTenants = ({ tenantIds = [] }) => {
};
}));

const locationsFromAllTenants = queries.map(({ data }) => data?.locations).filter(Boolean).flat();
const { data: consolidatedLocations } = useQuery({
queryKey: [namespace, 'consolidatedLocations'],
queryFn: () => ky.get('search/consortium/locations').json(),
enabled: Boolean(isUserInConsortiumMode(stripes) && isUserInCentralTenant),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we use this endpoint on member tenants? This would improve performance (avoid sending 65 requests for 65 tenants for example) and also will allow us to display locations for Consortial holdings accordion for tenants with limited permissions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a limitation on back-end side unfortunately, the endpoint was implemented with this in mind

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use similar endpoints on member tenants - useConsortiumHoldings and useConsortiumItems, but sending requests in the context of central tenant. Or are there some other limitations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I misunderstood your question - useConsortiumHoldings and useConsortiumItems send requests when a user is in central tenant. Same with the locations endpoint. We only use it when user is in central tenant. If it's a member tenant then we should send multiple requests to a regular /locations endpoint
I think it's the same limitation as for holdings and items - user in a member tenant may not have permissions in central or other tenants to fetch locations from other tenants

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a situation where we have an admin from a member tenant who is affiliated with all other tenants in the consortia?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-no, useConsortiumHoldings and useConsortiumItems are also sent from member tenants but in the context of the central tenant (x-okapi-tenant: central). 1 difference here is that they are sent only with the condition that a particular member tenant does not have permission to get holdings/items from inventory storage. You can find more here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint still requires central tenant permissions, and in our case we want users without central tenant permissions to fetch locations from other tenants.


const locationsFromAllTenants = isUserInCentralTenant
? consolidatedLocations?.locations
: queries.map(({ data }) => data?.locations).filter(Boolean).flat();

return {
data: locationsFromAllTenants,
data: locationsFromAllTenants || [],
isLoading: queries.some(({ isFetching }) => isFetching),
};
};
Expand Down
65 changes: 53 additions & 12 deletions src/hooks/useLocationsForTenants/useLocationsForTenants.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {
QueryClientProvider,
} from 'react-query';

import { useOkapiKy } from '@folio/stripes/core';
import {
useOkapiKy,
checkIfUserInCentralTenant,
} from '@folio/stripes/core';

import {
renderHook,
Expand Down Expand Up @@ -37,22 +40,60 @@ describe('useLocationsForTenants', () => {

const tenantIds = ['cs00000int', 'cs00000int_0002'];

it('should fetch locations of all tenants', async () => {
const { result } = renderHook(() => useLocationsForTenants({ tenantIds }), { wrapper });
describe('when user is in a member tenant', () => {
it('should fetch locations of all tenants via multiple requests', async () => {
const { result } = renderHook(() => useLocationsForTenants({ tenantIds }), { wrapper });

await act(() => !result.current.isLoading);

expect(mockGet.mock.calls[0][0]).toBe('locations');
expect(mockGet.mock.calls[1][0]).toBe('locations');
expect(result.current.data).toEqual([
{ id: 'location-id' },
{ id: 'location-id' },
]);
});

it('should not call consolidated locations endpoint', async () => {
const { result } = renderHook(() => useLocationsForTenants({ tenantIds }), { wrapper });

await act(() => !result.current.isLoading);
await act(() => !result.current.isLoading);

expect(result.current.data).toEqual([
{ id: 'location-id' },
{ id: 'location-id' },
]);
expect(mockGet).not.toHaveBeenCalledWith('search/consortium/locations');
});

describe('when tenantIds is empty', () => {
it('should not make a request', () => {
renderHook(() => useLocationsForTenants({ tenantIds: [] }), { wrapper });

expect(mockGet).not.toHaveBeenCalled();
});
});
});

describe('when tenantIds is empty', () => {
it('should not make a request', () => {
renderHook(() => useLocationsForTenants({ tenantIds: [] }), { wrapper });
describe('when user is in a central tenant', () => {
beforeEach(() => {
checkIfUserInCentralTenant.mockClear().mockReturnValue(true);
});

it('should fetch locations of all tenants via consolidated endpoint', async () => {
const { result } = renderHook(() => useLocationsForTenants({ tenantIds }), { wrapper });

await act(() => !result.current.isLoading);

expect(mockGet).toHaveBeenCalledWith('search/consortium/locations');

expect(result.current.data).toEqual([
{ id: 'location-id' },
]);
});

it('should not call multiple locations endpoints', async () => {
const { result } = renderHook(() => useLocationsForTenants({ tenantIds }), { wrapper });

await act(() => !result.current.isLoading);

expect(mockGet).not.toHaveBeenCalled();
expect(mockGet).not.toHaveBeenCalledWith('locations');
});
});
});
4 changes: 4 additions & 0 deletions src/providers/DataProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const DataProvider = ({
const { data: locationsOfAllTenants } = useLocationsForTenants({ tenantIds });

useEffect(() => {
if (isUserInConsortiumMode(stripes)) {
return;
}

mutator.locations.GET({ tenant: stripes.okapi.tenant });
}, [stripes.okapi.tenant]);

Expand Down
Loading