From 29f1b36bba287ce9b03e4ceae5bae1308e3616a0 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Thu, 24 Oct 2024 20:47:15 +0500 Subject: [PATCH 1/5] UISACQCOMP-223: ECS - Add reusable custom hooks to fix invalid reference issues related to holding names and locations --- CHANGELOG.md | 1 + lib/hooks/index.js | 2 + lib/hooks/useHoldingsAndLocations/index.js | 1 + .../useHoldingsAndLocations.js | 80 +++++++++++++ .../useHoldingsAndLocations.test.js | 92 +++++++++++++++ .../index.js | 1 + .../useReceivingTenantIdsAndLocations.js | 43 +++++++ lib/utils/extendKyWithTenant.js | 13 +++ lib/utils/getHoldingLocations.js | 99 +++++++++++++++++ lib/utils/getHoldingLocations.test.js | 105 ++++++++++++++++++ lib/utils/index.js | 2 + 11 files changed, 439 insertions(+) create mode 100644 lib/hooks/useHoldingsAndLocations/index.js create mode 100644 lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js create mode 100644 lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js create mode 100644 lib/hooks/useReceivingTenantIdsAndLocations/index.js create mode 100644 lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js create mode 100644 lib/utils/extendKyWithTenant.js create mode 100644 lib/utils/getHoldingLocations.js create mode 100644 lib/utils/getHoldingLocations.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c3086e61..b3b3d592 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * Add "Amount must be a positive number" validation for "Set exchange rate" field. Refs UISACQCOMP-218. * Create common utilities for managing response errors. Refs UISACQCOMP-219. * ECS - expand `ConsortiumFieldInventory` component with `additionalAffiliationIds` prop to display affiliation name for User without affiliation in specific tenant. Refs UISACQCOMP-220. +* ECS - Add reusable custom hooks to fix invalid reference issues related to holding names and locations. Refs UISACQCOMP-223. ## [5.1.2](https://github.com/folio-org/stripes-acq-components/tree/v5.1.2) (2024-09-13) [Full Changelog](https://github.com/folio-org/stripes-acq-components/compare/v5.1.1...v5.1.2) diff --git a/lib/hooks/index.js b/lib/hooks/index.js index 801f9e82..540b4111 100644 --- a/lib/hooks/index.js +++ b/lib/hooks/index.js @@ -12,6 +12,7 @@ export * from './useDefaultReceivingSearchSettings'; export * from './useEventEmitter'; export * from './useExchangeRateValue'; export * from './useFunds'; +export * from './useHoldingsAndLocations'; export * from './useIdentifierTypes'; export * from './useInstanceHoldings'; export * from './useInstanceHoldingsQuery'; @@ -29,6 +30,7 @@ export * from './useModalToggle'; export * from './useOrderLine'; export * from './useOrganization'; export * from './usePaneFocus'; +export * from './useReceivingTenantIdsAndLocations'; export * from './useShowCallout'; export * from './useTags'; export * from './useToggle'; diff --git a/lib/hooks/useHoldingsAndLocations/index.js b/lib/hooks/useHoldingsAndLocations/index.js new file mode 100644 index 00000000..a7d8eab2 --- /dev/null +++ b/lib/hooks/useHoldingsAndLocations/index.js @@ -0,0 +1 @@ +export { useHoldingsAndLocations } from './useHoldingsAndLocations'; diff --git a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js b/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js new file mode 100644 index 00000000..40ed7886 --- /dev/null +++ b/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js @@ -0,0 +1,80 @@ +import { useQuery } from 'react-query'; + +import { + useNamespace, + useOkapiKy, +} from '@folio/stripes/core'; + +import { LIMIT_MAX } from '../../constants'; +import { + getHoldingLocations, + getHoldingLocationsByTenants, +} from '../../utils'; + +const DEFAULT_DATA = []; + +export const useHoldingsAndLocations = ({ + instanceId, + options = {}, + tenantId, + /* + `receivingTenantIds` is a unique list of tenantIds from the pieces list. + The purpose is that we need to be able to fetch locations from other + tenants so that we can display all the locations on the full-screen page + */ + receivingTenantIds = DEFAULT_DATA, + /* + ECS mode: + `additionalTenantLocationIdsMap` is a map of tenantId to locationIds for ECS mode. + The format can be: { tenantId: [locationId1, locationId2] } + The purpose is that we need to fetch newly added locations when we select a location + from "Create new holdings for location" modal so that the value is displayed in the selection + */ + additionalTenantLocationIdsMap = {}, + /* + Non-ECS mode: + `additionalLocationIds` is a list of locationIds for the case when we need to fetch additional + locations for the selected location in the form so that the value is displayed in the selection. + */ + additionalLocationIds = [], +}) => { + const { enabled = true, ...queryOptions } = options; + + const ky = useOkapiKy({ tenant: tenantId }); + const [namespace] = useNamespace({ key: 'holdings-and-location' }); + const queryKey = [ + namespace, + tenantId, + instanceId, + ...receivingTenantIds, + ...additionalLocationIds, + ...Object.values(additionalTenantLocationIdsMap), + ]; + const searchParams = { + query: `instanceId==${instanceId}`, + limit: LIMIT_MAX, + }; + + const { + data, + isFetching, + isLoading, + } = useQuery({ + queryKey, + queryFn: ({ signal }) => { + return receivingTenantIds.length + ? getHoldingLocationsByTenants({ ky, instanceId, receivingTenantIds, additionalTenantLocationIdsMap }) + : getHoldingLocations({ ky, searchParams, signal, additionalLocationIds }); + }, + enabled: enabled && Boolean(instanceId), + ...queryOptions, + }); + + return ({ + holdings: data?.holdings || DEFAULT_DATA, + locations: data?.locations || DEFAULT_DATA, + locationIds: data?.locationIds || DEFAULT_DATA, + isFetching, + isLoading, + }); +}; diff --git a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js b/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js new file mode 100644 index 00000000..6a52f5d9 --- /dev/null +++ b/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js @@ -0,0 +1,92 @@ +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; + +import { + renderHook, + waitFor, +} from '@testing-library/react-hooks'; +import { useOkapiKy } from '@folio/stripes/core'; + +import { HOLDINGS_API } from '../../constants'; +import { extendKyWithTenant } from '../../utils'; +import { useHoldingsAndLocations } from './useHoldingsAndLocations'; + +jest.mock('../../utils', () => ({ + ...jest.requireActual('../../utils'), + extendKyWithTenant: jest.fn().mockReturnValue({ extend: jest.fn() }), +})); + +const queryClient = new QueryClient(); +const wrapper = ({ children }) => ( + + {children} + +); + +const holdingsRecords = [ + { + 'id': 'd7e99892-6d7d-46eb-8a4c-3aa471785819', + 'instanceId': '8804aeeb-db18-4c42-b0e9-28d63c7855e6', + 'permanentLocationId': '9d1b77e8-f02e-4b7f-b296-3f2042ddac54', + }, + { + 'id': '5636949f-8f97-4b25-a0fc-90fdb050ffb0', + 'instanceId': '8804aeeb-db18-4c42-b0e9-28d63c7855e6', + 'permanentLocationId': '9d1b77e8-f02e-4b7f-b296-3f2042ddac54', + }, +]; + +const locations = [ + { + 'id': '9d1b77e8-f02e-4b7f-b296-3f2042ddac54', + 'name': 'DCB', + 'code': '000', + }, +]; + +const getMock = jest.fn() + .mockReturnValueOnce({ json: () => Promise.resolve({ holdingsRecords }) }) + .mockReturnValue({ json: () => Promise.resolve({ locations }) }); + +describe('useHoldingsAndLocations', () => { + beforeEach(() => { + useOkapiKy + .mockClear() + .mockReturnValue({ + get: getMock, + extend: jest.fn(() => ({ + get: jest.fn((path) => { + if (path === HOLDINGS_API) { + return { + json: jest.fn().mockResolvedValue({ holdingsRecords }), + }; + } + + return ({ + json: jest.fn().mockResolvedValue({ locations }), + }); + }), + })), + }); + extendKyWithTenant.mockClear().mockReturnValue({ extend: jest.fn() }); + }); + + it('should fetch holding locations', async () => { + const { result } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', tenantId: '2' }), { wrapper }); + + await waitFor(() => expect(result.current.isLoading).toBeFalsy()); + + expect(result.current.locations).toEqual(locations); + }); + + it('should fetch holding locations with different tenants', async () => { + const receivingTenantIds = ['1', '2']; + const { result } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', receivingTenantIds, tenantId: '2' }), { wrapper }); + + await waitFor(() => expect(result.current.isLoading).toBeFalsy()); + + expect(result.current.locations).toHaveLength(2); + }); +}); diff --git a/lib/hooks/useReceivingTenantIdsAndLocations/index.js b/lib/hooks/useReceivingTenantIdsAndLocations/index.js new file mode 100644 index 00000000..41ba8fd3 --- /dev/null +++ b/lib/hooks/useReceivingTenantIdsAndLocations/index.js @@ -0,0 +1 @@ +export * from './useReceivingTenantIdsAndLocations'; diff --git a/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js b/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js new file mode 100644 index 00000000..c54ca64e --- /dev/null +++ b/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js @@ -0,0 +1,43 @@ +import uniq from 'lodash/uniq'; +import { useMemo } from 'react'; + +import { useCurrentUserTenants } from '../consortia'; + +export const useReceivingTenantIdsAndLocations = ({ + currentReceivingTenantId, + currentLocationId: locationId, + receivingTenantIds = [], +}) => { + const currentUserTenants = useCurrentUserTenants(); + + const receivingTenants = useMemo(() => { + if (receivingTenantIds.length) { + const currentUserTenantIds = currentUserTenants?.map(({ id: tenantId }) => tenantId); + + // should get unique tenantIds from poLine locations and filter out tenantIds where the current user has assigned + return uniq([ + ...receivingTenantIds, + currentReceivingTenantId, + ].filter((tenantId) => currentUserTenantIds?.includes(tenantId)) + .filter(Boolean)); + } + + return []; + }, [receivingTenantIds, currentUserTenants, currentReceivingTenantId]); + + const additionalLocations = useMemo(() => { + const locationIds = locationId ? [locationId] : []; + const tenantLocationIdsMap = currentReceivingTenantId ? { [currentReceivingTenantId]: locationIds } : {}; + + return { + additionalLocationIds: locationIds, + additionalTenantLocationIdsMap: tenantLocationIdsMap, + }; + }, [locationId, currentReceivingTenantId]); + + return { + receivingTenantIds: receivingTenants, + tenantId: currentReceivingTenantId, + ...additionalLocations, + }; +}; diff --git a/lib/utils/extendKyWithTenant.js b/lib/utils/extendKyWithTenant.js new file mode 100644 index 00000000..0c3b31b3 --- /dev/null +++ b/lib/utils/extendKyWithTenant.js @@ -0,0 +1,13 @@ +import { OKAPI_TENANT_HEADER } from '../constants'; + +export const extendKyWithTenant = (ky, tenantId) => { + return ky.extend({ + hooks: { + beforeRequest: [ + request => { + request.headers.set(OKAPI_TENANT_HEADER, tenantId); + }, + ], + }, + }); +}; diff --git a/lib/utils/getHoldingLocations.js b/lib/utils/getHoldingLocations.js new file mode 100644 index 00000000..a82691b3 --- /dev/null +++ b/lib/utils/getHoldingLocations.js @@ -0,0 +1,99 @@ +import { + HOLDINGS_API, + LIMIT_MAX, + LOCATIONS_API, +} from '../constants'; +import { batchRequest } from './batchFetch'; +import { extendKyWithTenant } from './extendKyWithTenant'; + +const DEFAULT_DATA = []; + +export const getHoldingLocations = async ({ + ky, + searchParams, + signal, + /* + Non-ECS mode: + `additionalLocationIds` is a list of locationIds for the case when we need to fetch additional + locations for the selected location in the form so that the value is displayed in the selection. + */ + additionalLocationIds = [], + tenantId, +}) => { + const holdings = await ky + .get(HOLDINGS_API, { searchParams, signal }) + .json() + .then(response => { + return tenantId ? + response.holdingsRecords.map(holding => ({ ...holding, tenantId })) + : response.holdingsRecords; + }); + + const locationIds = holdings?.map(({ permanentLocationId }) => permanentLocationId) || DEFAULT_DATA; + const uniqueLocationIds = [...new Set([...locationIds, ...additionalLocationIds])]; + + const locations = await batchRequest( + ({ params }) => ky.get(LOCATIONS_API, { searchParams: params, signal }).json(), + uniqueLocationIds, + ).then(responses => responses.flatMap(response => { + return tenantId + ? response.locations.map(location => ({ ...location, tenantId })) + : response.locations; + })); + + return { + holdings, + locations, + locationIds, + }; +}; + +export const getHoldingLocationsByTenants = async ({ + ky, + instanceId, + /* + ECS mode: + `additionalTenantLocationIdsMap` is a map of tenantId to locationIds for ECS mode. + The format can be: { tenantId: [locationId1, locationId2] } + The purpose is that we need to fetch newly added locations when we select a location + from "Create new holdings for location" modal so that the value is displayed in the selection + */ + additionalTenantLocationIdsMap = {}, + receivingTenantIds = DEFAULT_DATA, +}) => { + const searchParams = { + query: `instanceId==${instanceId}`, + limit: LIMIT_MAX, + }; + + if (!receivingTenantIds.length) { + return { + locations: DEFAULT_DATA, + locationIds: DEFAULT_DATA, + holdings: DEFAULT_DATA, + }; + } + + const locationsRequest = receivingTenantIds.map(async (tenantId) => { + const tenantKy = extendKyWithTenant(ky, tenantId); + + return getHoldingLocations({ + ky: tenantKy, + searchParams, + tenantId, + additionalLocationIds: additionalTenantLocationIdsMap[tenantId], + }); + }); + + const locationsResponse = await Promise.all(locationsRequest).then(responses => { + return responses.reduce((acc, item) => { + return { + locations: acc.locations.concat(item.locations), + locationIds: acc.locationIds.concat(item.locationIds), + holdings: acc.holdings.concat(item.holdings), + }; + }, { locations: DEFAULT_DATA, locationIds: DEFAULT_DATA, holdings: DEFAULT_DATA }); + }); + + return locationsResponse; +}; diff --git a/lib/utils/getHoldingLocations.test.js b/lib/utils/getHoldingLocations.test.js new file mode 100644 index 00000000..905304e7 --- /dev/null +++ b/lib/utils/getHoldingLocations.test.js @@ -0,0 +1,105 @@ +import { HOLDINGS_API } from '../constants'; +import { extendKyWithTenant } from './extendKyWithTenant'; +import { + getHoldingLocations, + getHoldingLocationsByTenants, +} from './getHoldingLocations'; + +jest.mock('./utils', () => ({ + extendKyWithTenant: jest.fn(), +})); + +const holdingsRecords = [{ + id: 'holding-id', + permanentLocationId: 'location-id', +}]; + +const locations = [{ + id: 'location-id', + name: 'location-name', +}]; + +describe('utils', () => { + let ky; + + beforeEach(() => { + ky = { + get: jest.fn((path) => { + if (path === HOLDINGS_API) { + return { + json: jest.fn().mockResolvedValue({ holdingsRecords }), + }; + } + + return ({ + json: jest.fn().mockResolvedValue({ locations }), + }); + }), + }; + }); + + describe('getHoldingLocations', () => { + it('should return holdings, locations and locationIds', async () => { + const searchParams = {}; + const signal = { signal: 'signal' }; + + const result = await getHoldingLocations({ ky, searchParams, signal }); + + expect(result).toEqual({ + holdings: holdingsRecords, + locations, + locationIds: locations.map(({ id }) => id), + }); + }); + + it('should return holdings, locations and locationIds with tenantId', async () => { + const searchParams = {}; + const signal = { signal: 'signal' }; + const tenantId = 'tenant-id'; + + const result = await getHoldingLocations({ ky, searchParams, signal, tenantId }); + + expect(result).toEqual({ + holdings: holdingsRecords.map(holding => ({ ...holding, tenantId })), + locations: locations.map(location => ({ ...location, tenantId })), + locationIds: locations.map(({ id }) => id), + }); + }); + }); + + describe('getHoldingLocationsByTenants', () => { + beforeEach(() => { + extendKyWithTenant.mockImplementation((tenantKy, tenantId) => { + return { ...tenantKy, tenantId }; + }); + }); + + it('should return locationsResponse', async () => { + const instanceId = 'instance-id'; + const receivingTenantIds = ['tenant-id']; + + const result = await getHoldingLocationsByTenants({ ky, instanceId, receivingTenantIds }); + + expect(result).toEqual({ + holdings: holdingsRecords.map(holding => ({ ...holding, tenantId: receivingTenantIds[0] })), + locations: locations.map(location => ({ ...location, tenantId: receivingTenantIds[0] })), + locationIds: locations.map(({ id }) => id), + }); + }); + + it('should return empty array of holdings, locations and locationIds when `receivingTenantIds` and not present or empty array', async () => { + const instanceId = 'instance-id'; + + const result = await getHoldingLocationsByTenants({ + ky, + instanceId, + }); + + expect(result).toEqual({ + holdings: [], + locations: [], + locationIds: [], + }); + }); + }); +}); diff --git a/lib/utils/index.js b/lib/utils/index.js index b8af9be5..809d4f4c 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -6,6 +6,7 @@ export * from './createClearFilterHandler'; export * from './downloadBase64'; export * from './errorHandling'; export * from './EventEmitter'; +export * from './extendKyWithTenant'; export * from './fetchAllRecords'; export * from './fetchExportDataByIds'; export * from './filterArrayValues'; @@ -20,6 +21,7 @@ export * from './getConfigSetting'; export * from './getControlledVocabTranslations'; export * from './getErrorCodeFromResponse'; export * from './getFundsForSelect'; +export * from './getHoldingLocations'; export * from './getLocationOptions'; export * from './getMoneyMultiplier'; export * from './getOrganizationsOptions'; From fe3f9c431950acb2c90c9ec3f48158f0cb1bafd4 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Fri, 25 Oct 2024 14:17:29 +0500 Subject: [PATCH 2/5] test: fix failing tests --- .../useHoldingsAndLocations/useHoldingsAndLocations.test.js | 5 ++--- lib/utils/getHoldingLocations.test.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js b/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js index 6a52f5d9..53ab0c02 100644 --- a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js +++ b/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js @@ -5,7 +5,6 @@ import { import { renderHook, - waitFor, } from '@testing-library/react-hooks'; import { useOkapiKy } from '@folio/stripes/core'; @@ -74,7 +73,7 @@ describe('useHoldingsAndLocations', () => { }); it('should fetch holding locations', async () => { - const { result } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', tenantId: '2' }), { wrapper }); + const { result, waitFor } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', tenantId: '2' }), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBeFalsy()); @@ -83,7 +82,7 @@ describe('useHoldingsAndLocations', () => { it('should fetch holding locations with different tenants', async () => { const receivingTenantIds = ['1', '2']; - const { result } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', receivingTenantIds, tenantId: '2' }), { wrapper }); + const { result, waitFor } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', receivingTenantIds, tenantId: '2' }), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBeFalsy()); diff --git a/lib/utils/getHoldingLocations.test.js b/lib/utils/getHoldingLocations.test.js index 905304e7..916a39f5 100644 --- a/lib/utils/getHoldingLocations.test.js +++ b/lib/utils/getHoldingLocations.test.js @@ -5,7 +5,7 @@ import { getHoldingLocationsByTenants, } from './getHoldingLocations'; -jest.mock('./utils', () => ({ +jest.mock('./extendKyWithTenant', () => ({ extendKyWithTenant: jest.fn(), })); From fc8fe8b2cd08aa776690c01e7ac7c9402e6a87b4 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Fri, 25 Oct 2024 14:46:07 +0500 Subject: [PATCH 3/5] test: add test coverage --- .../useReceivingTenantIdsAndLocations.test.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.test.js diff --git a/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.test.js b/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.test.js new file mode 100644 index 00000000..d74a6ec7 --- /dev/null +++ b/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.test.js @@ -0,0 +1,33 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useReceivingTenantIdsAndLocations } from './useReceivingTenantIdsAndLocations'; + +jest.mock('../consortia', () => ({ + useCurrentUserTenants: jest.fn(() => [{ id: 'central' }, { id: 'college' }]), +})); + +describe('useReceivingTenantIdsAndLocations', () => { + it('should return receivingTenantIds', () => { + const tenants = ['central', 'college']; + const { result } = renderHook(() => useReceivingTenantIdsAndLocations({ + receivingTenantIds: tenants, + currentReceivingTenantId: 'central', + })); + + expect(result.current.receivingTenantIds).toEqual(tenants); + }); + + it('should return tenantId', () => { + const currentReceivingTenantId = 'central'; + + const { result } = renderHook(() => useReceivingTenantIdsAndLocations({ currentReceivingTenantId })); + + expect(result.current.tenantId).toBe(currentReceivingTenantId); + }); + + it('should return additionalLocationIds', () => { + const { result } = renderHook(() => useReceivingTenantIdsAndLocations({})); + + expect(result.current.additionalLocationIds).toEqual([]); + }); +}); From e8abe63560245e8ce09b0bcfbc4dcebc441e3bdc Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Fri, 25 Oct 2024 20:28:53 +0500 Subject: [PATCH 4/5] fix hook key --- .../consortia/useConsortiumLocations/useConsortiumLocations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hooks/consortia/useConsortiumLocations/useConsortiumLocations.js b/lib/hooks/consortia/useConsortiumLocations/useConsortiumLocations.js index 029a4665..b98acc0a 100644 --- a/lib/hooks/consortia/useConsortiumLocations/useConsortiumLocations.js +++ b/lib/hooks/consortia/useConsortiumLocations/useConsortiumLocations.js @@ -15,7 +15,7 @@ export const useConsortiumLocations = (options = {}) => { const stripes = useStripes(); const centralTenantId = getConsortiumCentralTenantId(stripes); const ky = useOkapiKy({ tenant: centralTenantId }); - const [namespace] = useNamespace({ key: 'locations' }); + const [namespace] = useNamespace({ key: 'consortium-locations' }); const { enabled = true, From cae8bfc4d2ae29b0a1912155a128a754e1db49af Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Mon, 28 Oct 2024 19:18:29 +0500 Subject: [PATCH 5/5] refactor: rename custom hooks --- lib/hooks/index.js | 2 +- lib/hooks/useHoldingsAndLocations/index.js | 1 - .../useReceivingTenantIdsAndLocations.js | 4 ++++ .../useTenantHoldingsAndLocations/index.js | 1 + .../useTenantHoldingsAndLocations.js} | 14 +++++++++----- .../useTenantHoldingsAndLocations.test.js} | 8 ++++---- ...ons.js => getTenantHoldingsAndLocations.js} | 6 +++--- ...s => getTenantHoldingsAndLocations.test.js} | 18 +++++++++--------- lib/utils/index.js | 2 +- 9 files changed, 32 insertions(+), 24 deletions(-) delete mode 100644 lib/hooks/useHoldingsAndLocations/index.js create mode 100644 lib/hooks/useTenantHoldingsAndLocations/index.js rename lib/hooks/{useHoldingsAndLocations/useHoldingsAndLocations.js => useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.js} (80%) rename lib/hooks/{useHoldingsAndLocations/useHoldingsAndLocations.test.js => useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.test.js} (84%) rename lib/utils/{getHoldingLocations.js => getTenantHoldingsAndLocations.js} (95%) rename lib/utils/{getHoldingLocations.test.js => getTenantHoldingsAndLocations.test.js} (81%) diff --git a/lib/hooks/index.js b/lib/hooks/index.js index 540b4111..46e1b71e 100644 --- a/lib/hooks/index.js +++ b/lib/hooks/index.js @@ -12,7 +12,6 @@ export * from './useDefaultReceivingSearchSettings'; export * from './useEventEmitter'; export * from './useExchangeRateValue'; export * from './useFunds'; -export * from './useHoldingsAndLocations'; export * from './useIdentifierTypes'; export * from './useInstanceHoldings'; export * from './useInstanceHoldingsQuery'; @@ -33,6 +32,7 @@ export * from './usePaneFocus'; export * from './useReceivingTenantIdsAndLocations'; export * from './useShowCallout'; export * from './useTags'; +export * from './useTenantHoldingsAndLocations'; export * from './useToggle'; export * from './useTranslatedCategories'; export * from './useUser'; diff --git a/lib/hooks/useHoldingsAndLocations/index.js b/lib/hooks/useHoldingsAndLocations/index.js deleted file mode 100644 index a7d8eab2..00000000 --- a/lib/hooks/useHoldingsAndLocations/index.js +++ /dev/null @@ -1 +0,0 @@ -export { useHoldingsAndLocations } from './useHoldingsAndLocations'; diff --git a/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js b/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js index c54ca64e..40306ac6 100644 --- a/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js +++ b/lib/hooks/useReceivingTenantIdsAndLocations/useReceivingTenantIdsAndLocations.js @@ -3,6 +3,10 @@ import { useMemo } from 'react'; import { useCurrentUserTenants } from '../consortia'; +/* + The purpose of this hook is to generate the list of unique tenantIds and locationIds + for the receiving tenant and locations when we need to fetch locations from other tenants via `useTenantHoldingsAndLocations` +*/ export const useReceivingTenantIdsAndLocations = ({ currentReceivingTenantId, currentLocationId: locationId, diff --git a/lib/hooks/useTenantHoldingsAndLocations/index.js b/lib/hooks/useTenantHoldingsAndLocations/index.js new file mode 100644 index 00000000..c9f0a6bc --- /dev/null +++ b/lib/hooks/useTenantHoldingsAndLocations/index.js @@ -0,0 +1 @@ +export { useTenantHoldingsAndLocations } from './useTenantHoldingsAndLocations'; diff --git a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js b/lib/hooks/useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.js similarity index 80% rename from lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js rename to lib/hooks/useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.js index 40ed7886..dfa8bd82 100644 --- a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.js +++ b/lib/hooks/useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.js @@ -7,13 +7,17 @@ import { import { LIMIT_MAX } from '../../constants'; import { - getHoldingLocations, - getHoldingLocationsByTenants, + getHoldingsAndLocations, + getHoldingsAndLocationsByTenants, } from '../../utils'; const DEFAULT_DATA = []; -export const useHoldingsAndLocations = ({ +/* + The purpose of this hook is to fetch holdings and locations for a given instanceId + and tenants when we need to fetch locations from other tenants when Central ordering is enabled. +*/ +export const useTenantHoldingsAndLocations = ({ instanceId, options = {}, tenantId, @@ -63,8 +67,8 @@ export const useHoldingsAndLocations = ({ queryKey, queryFn: ({ signal }) => { return receivingTenantIds.length - ? getHoldingLocationsByTenants({ ky, instanceId, receivingTenantIds, additionalTenantLocationIdsMap }) - : getHoldingLocations({ ky, searchParams, signal, additionalLocationIds }); + ? getHoldingsAndLocationsByTenants({ ky, instanceId, receivingTenantIds, additionalTenantLocationIdsMap }) + : getHoldingsAndLocations({ ky, searchParams, signal, additionalLocationIds }); }, enabled: enabled && Boolean(instanceId), ...queryOptions, diff --git a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js b/lib/hooks/useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.test.js similarity index 84% rename from lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js rename to lib/hooks/useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.test.js index 53ab0c02..fd8eb5e4 100644 --- a/lib/hooks/useHoldingsAndLocations/useHoldingsAndLocations.test.js +++ b/lib/hooks/useTenantHoldingsAndLocations/useTenantHoldingsAndLocations.test.js @@ -10,7 +10,7 @@ import { useOkapiKy } from '@folio/stripes/core'; import { HOLDINGS_API } from '../../constants'; import { extendKyWithTenant } from '../../utils'; -import { useHoldingsAndLocations } from './useHoldingsAndLocations'; +import { useTenantHoldingsAndLocations } from './useTenantHoldingsAndLocations'; jest.mock('../../utils', () => ({ ...jest.requireActual('../../utils'), @@ -49,7 +49,7 @@ const getMock = jest.fn() .mockReturnValueOnce({ json: () => Promise.resolve({ holdingsRecords }) }) .mockReturnValue({ json: () => Promise.resolve({ locations }) }); -describe('useHoldingsAndLocations', () => { +describe('useTenantHoldingsAndLocations', () => { beforeEach(() => { useOkapiKy .mockClear() @@ -73,7 +73,7 @@ describe('useHoldingsAndLocations', () => { }); it('should fetch holding locations', async () => { - const { result, waitFor } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', tenantId: '2' }), { wrapper }); + const { result, waitFor } = renderHook(() => useTenantHoldingsAndLocations({ instanceId: '1', tenantId: '2' }), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBeFalsy()); @@ -82,7 +82,7 @@ describe('useHoldingsAndLocations', () => { it('should fetch holding locations with different tenants', async () => { const receivingTenantIds = ['1', '2']; - const { result, waitFor } = renderHook(() => useHoldingsAndLocations({ instanceId: '1', receivingTenantIds, tenantId: '2' }), { wrapper }); + const { result, waitFor } = renderHook(() => useTenantHoldingsAndLocations({ instanceId: '1', receivingTenantIds, tenantId: '2' }), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBeFalsy()); diff --git a/lib/utils/getHoldingLocations.js b/lib/utils/getTenantHoldingsAndLocations.js similarity index 95% rename from lib/utils/getHoldingLocations.js rename to lib/utils/getTenantHoldingsAndLocations.js index a82691b3..823f85a2 100644 --- a/lib/utils/getHoldingLocations.js +++ b/lib/utils/getTenantHoldingsAndLocations.js @@ -8,7 +8,7 @@ import { extendKyWithTenant } from './extendKyWithTenant'; const DEFAULT_DATA = []; -export const getHoldingLocations = async ({ +export const getHoldingsAndLocations = async ({ ky, searchParams, signal, @@ -48,7 +48,7 @@ export const getHoldingLocations = async ({ }; }; -export const getHoldingLocationsByTenants = async ({ +export const getHoldingsAndLocationsByTenants = async ({ ky, instanceId, /* @@ -77,7 +77,7 @@ export const getHoldingLocationsByTenants = async ({ const locationsRequest = receivingTenantIds.map(async (tenantId) => { const tenantKy = extendKyWithTenant(ky, tenantId); - return getHoldingLocations({ + return getHoldingsAndLocations({ ky: tenantKy, searchParams, tenantId, diff --git a/lib/utils/getHoldingLocations.test.js b/lib/utils/getTenantHoldingsAndLocations.test.js similarity index 81% rename from lib/utils/getHoldingLocations.test.js rename to lib/utils/getTenantHoldingsAndLocations.test.js index 916a39f5..13432e8e 100644 --- a/lib/utils/getHoldingLocations.test.js +++ b/lib/utils/getTenantHoldingsAndLocations.test.js @@ -1,9 +1,9 @@ import { HOLDINGS_API } from '../constants'; import { extendKyWithTenant } from './extendKyWithTenant'; import { - getHoldingLocations, - getHoldingLocationsByTenants, -} from './getHoldingLocations'; + getHoldingsAndLocations, + getHoldingsAndLocationsByTenants, +} from './getTenantHoldingsAndLocations'; jest.mock('./extendKyWithTenant', () => ({ extendKyWithTenant: jest.fn(), @@ -38,12 +38,12 @@ describe('utils', () => { }; }); - describe('getHoldingLocations', () => { + describe('getHoldingsAndLocations', () => { it('should return holdings, locations and locationIds', async () => { const searchParams = {}; const signal = { signal: 'signal' }; - const result = await getHoldingLocations({ ky, searchParams, signal }); + const result = await getHoldingsAndLocations({ ky, searchParams, signal }); expect(result).toEqual({ holdings: holdingsRecords, @@ -57,7 +57,7 @@ describe('utils', () => { const signal = { signal: 'signal' }; const tenantId = 'tenant-id'; - const result = await getHoldingLocations({ ky, searchParams, signal, tenantId }); + const result = await getHoldingsAndLocations({ ky, searchParams, signal, tenantId }); expect(result).toEqual({ holdings: holdingsRecords.map(holding => ({ ...holding, tenantId })), @@ -67,7 +67,7 @@ describe('utils', () => { }); }); - describe('getHoldingLocationsByTenants', () => { + describe('getHoldingsAndLocationsByTenants', () => { beforeEach(() => { extendKyWithTenant.mockImplementation((tenantKy, tenantId) => { return { ...tenantKy, tenantId }; @@ -78,7 +78,7 @@ describe('utils', () => { const instanceId = 'instance-id'; const receivingTenantIds = ['tenant-id']; - const result = await getHoldingLocationsByTenants({ ky, instanceId, receivingTenantIds }); + const result = await getHoldingsAndLocationsByTenants({ ky, instanceId, receivingTenantIds }); expect(result).toEqual({ holdings: holdingsRecords.map(holding => ({ ...holding, tenantId: receivingTenantIds[0] })), @@ -90,7 +90,7 @@ describe('utils', () => { it('should return empty array of holdings, locations and locationIds when `receivingTenantIds` and not present or empty array', async () => { const instanceId = 'instance-id'; - const result = await getHoldingLocationsByTenants({ + const result = await getHoldingsAndLocationsByTenants({ ky, instanceId, }); diff --git a/lib/utils/index.js b/lib/utils/index.js index 809d4f4c..efd2c9a3 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -21,7 +21,7 @@ export * from './getConfigSetting'; export * from './getControlledVocabTranslations'; export * from './getErrorCodeFromResponse'; export * from './getFundsForSelect'; -export * from './getHoldingLocations'; +export * from './getTenantHoldingsAndLocations'; export * from './getLocationOptions'; export * from './getMoneyMultiplier'; export * from './getOrganizationsOptions';