diff --git a/CHANGELOG.md b/CHANGELOG.md index 66cd2d44..bd21006c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * [UIBULKED-561](https://folio-org.atlassian.net/browse/UUIBULKED-561) Add administrative data accordion to MARC bulk edit form. * [UIBULKED-562](https://folio-org.atlassian.net/browse/UIBULKED-562) Include statistical code option on Instances bulk edit forms. * [UIBULKED-574](https://folio-org.atlassian.net/browse/UIBULKED-574) Updates to Errors component. +* [UIBULKED-571](https://folio-org.atlassian.net/browse/UIBULKED-571) Errors in response to UI calls. ## [4.2.2](https://github.com/folio-org/ui-bulk-edit/tree/v4.2.2) (2024-11-15) diff --git a/package.json b/package.json index bddf384f..0b77dee3 100644 --- a/package.json +++ b/package.json @@ -233,6 +233,7 @@ "file-saver": "^2.0.5", "history": "^5.1.0", "lodash": "^4.17.5", + "http-status-codes": "^2.3.0", "moment": "^2.29.1", "prop-types": "^15.5.10", "query-string": "^6.1.0", diff --git a/src/components/BulkEditPane/BulkEditListResult/BulkEditListResult.test.js b/src/components/BulkEditPane/BulkEditListResult/BulkEditListResult.test.js index be287896..49667332 100644 --- a/src/components/BulkEditPane/BulkEditListResult/BulkEditListResult.test.js +++ b/src/components/BulkEditPane/BulkEditListResult/BulkEditListResult.test.js @@ -29,7 +29,6 @@ jest.mock('../../../hooks/api', () => ({ useErrorsPreview: () => ({ errors: [], }), - useUserGroupsMap: () => ({}), })); const setCountOfRecordsMock = jest.fn(); diff --git a/src/components/PermissionsModal/hooks/useAllPermissions.js b/src/components/PermissionsModal/hooks/useAllPermissions.js index cc40c3fd..f5d2b3d5 100644 --- a/src/components/PermissionsModal/hooks/useAllPermissions.js +++ b/src/components/PermissionsModal/hooks/useAllPermissions.js @@ -2,13 +2,14 @@ import { useQuery } from 'react-query'; import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { FILTER_KEYS } from '../constants/core'; import { useErrorMessages } from '../../../hooks/useErrorMessages'; +import { MOD_PERMISSIONS } from '../../../constants'; export const ALL_PERMISSIONS_KEY = 'ALL_PERMISSIONS_KEY'; export const useAllPermissions = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: ALL_PERMISSIONS_KEY }); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data: permissions, isLoading: isPermissionsLoading } = useQuery( { @@ -20,8 +21,7 @@ export const useAllPermissions = (options = {}) => { ...permission, type: permission.mutable ? FILTER_KEYS.PERMISSION_SETS : FILTER_KEYS.PERMISSIONS, })), - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_PERMISSIONS, error), ...options, }, ); diff --git a/src/constants/index.js b/src/constants/index.js index a0d4c124..7fb42390 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -5,3 +5,4 @@ export * from './inAppActions'; export * from '../utils/date'; export * from './files'; export * from './logsActions'; +export * from './moduleNames'; diff --git a/src/constants/moduleNames.js b/src/constants/moduleNames.js new file mode 100644 index 00000000..7ee3d8da --- /dev/null +++ b/src/constants/moduleNames.js @@ -0,0 +1,5 @@ +export const MOD_INVENTORY_STORAGE = 'mod-inventory-storage'; +export const MOD_USERS = 'mod-users'; +export const MOD_PERMISSIONS = 'mod-permissions'; +export const MOD_FQM_MANAGER = 'mod-fqm-manager'; +export const MOD_CONSORTIA = 'mod-consortia'; diff --git a/src/hooks/api/index.js b/src/hooks/api/index.js index cc770080..a72adb3e 100644 --- a/src/hooks/api/index.js +++ b/src/hooks/api/index.js @@ -5,7 +5,6 @@ export * from './useBulkOperationStart'; export * from './useUpload'; export * from './useBulkEditLogs'; export * from './useErrorsPreview'; -export * from './useUserGroupsMap'; export * from './usePatronGroup'; export * from './useLoanTypes'; export * from './useBulkOperationDetails'; diff --git a/src/hooks/api/useBulkOperationTenants.js b/src/hooks/api/useBulkOperationTenants.js index 3a5c6997..a960371d 100644 --- a/src/hooks/api/useBulkOperationTenants.js +++ b/src/hooks/api/useBulkOperationTenants.js @@ -1,12 +1,14 @@ import { useQuery } from 'react-query'; import { useNamespace, useOkapiKy } from '@folio/stripes/core'; +import { useErrorMessages } from '../useErrorMessages'; export const BULK_TENANTS_KEY = 'BULK_TENANTS_KEY'; export const useBulkOperationTenants = (id, options = {}) => { const ky = useOkapiKy(); const [namespace] = useNamespace({ key: BULK_TENANTS_KEY }); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading } = useQuery({ queryKey: [namespace], @@ -15,6 +17,7 @@ export const useBulkOperationTenants = (id, options = {}) => { cacheTime: Infinity, staleTime: Infinity, enabled: !!id, + onError: showExternalModuleError, ...options, }); diff --git a/src/hooks/api/useElectronicAccess.js b/src/hooks/api/useElectronicAccess.js index cb4003e8..d0f90da8 100644 --- a/src/hooks/api/useElectronicAccess.js +++ b/src/hooks/api/useElectronicAccess.js @@ -1,13 +1,14 @@ import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useQuery } from 'react-query'; import { useErrorMessages } from '../useErrorMessages'; +import { MOD_INVENTORY_STORAGE } from '../../constants'; export const ELECTRONIC_ACCESS_RELATIONSHIPS_KEY = 'ELECTRONIC_ACCESS_RELATIONSHIPS_KEY'; export const useElectronicAccessRelationships = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: ELECTRONIC_ACCESS_RELATIONSHIPS_KEY }); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading: isElectronicAccessLoading } = useQuery( { @@ -15,8 +16,7 @@ export const useElectronicAccessRelationships = (options = {}) => { cacheTime: Infinity, staleTime: Infinity, queryFn: () => ky.get('electronic-access-relationships?limit=1000&query=cql.allRecords=1 sortby name').json(), - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_INVENTORY_STORAGE, error), ...options, }, ); diff --git a/src/hooks/api/useHoldingsNotes.js b/src/hooks/api/useHoldingsNotes.js index 0b818ef5..446e1996 100644 --- a/src/hooks/api/useHoldingsNotes.js +++ b/src/hooks/api/useHoldingsNotes.js @@ -2,17 +2,18 @@ import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useQuery } from 'react-query'; import { useIntl } from 'react-intl'; import { useMemo } from 'react'; -import { OPTIONS, PARAMETERS_KEYS } from '../../constants'; +import { OPTIONS, PARAMETERS_KEYS, MOD_INVENTORY_STORAGE } from '../../constants'; import { getMappedAndSortedNotes } from '../../utils/helpers'; import { useErrorMessages } from '../useErrorMessages'; + export const HOLDINGS_NOTES_KEY = 'HOLDINGS_NOTES_KEY'; export const useHoldingsNotes = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: HOLDINGS_NOTES_KEY }); const { formatMessage } = useIntl(); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading: isHoldingsNotesLoading } = useQuery( { @@ -20,8 +21,7 @@ export const useHoldingsNotes = (options = {}) => { cacheTime: Infinity, staleTime: Infinity, queryFn: () => ky.get('holdings-note-types', { searchParams: { limit: 1000 } }).json(), - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_INVENTORY_STORAGE, error), ...options, }, ); diff --git a/src/hooks/api/useInstanceNotes.js b/src/hooks/api/useInstanceNotes.js index cc7e3c88..ebf92865 100644 --- a/src/hooks/api/useInstanceNotes.js +++ b/src/hooks/api/useInstanceNotes.js @@ -2,7 +2,7 @@ import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useQuery } from 'react-query'; import { useIntl } from 'react-intl'; import { useMemo } from 'react'; -import { OPTIONS, PARAMETERS_KEYS } from '../../constants'; +import { MOD_INVENTORY_STORAGE, OPTIONS, PARAMETERS_KEYS } from '../../constants'; import { getMappedAndSortedNotes } from '../../utils/helpers'; import { useErrorMessages } from '../useErrorMessages'; @@ -12,7 +12,7 @@ export const useInstanceNotes = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: INSTANCE_NOTES_KEY }); const { formatMessage } = useIntl(); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading: isInstanceNotesLoading } = useQuery( { @@ -20,8 +20,7 @@ export const useInstanceNotes = (options = {}) => { cacheTime: Infinity, staleTime: Infinity, queryFn: () => ky.get('instance-note-types', { searchParams: { limit: 1000 } }).json(), - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_INVENTORY_STORAGE, error), ...options, }, ); diff --git a/src/hooks/api/useItemNotes.js b/src/hooks/api/useItemNotes.js index e6b73dc0..fd5042fc 100644 --- a/src/hooks/api/useItemNotes.js +++ b/src/hooks/api/useItemNotes.js @@ -2,7 +2,7 @@ import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useQuery } from 'react-query'; import { useIntl } from 'react-intl'; import { useMemo } from 'react'; -import { OPTIONS, PARAMETERS_KEYS } from '../../constants'; +import { MOD_INVENTORY_STORAGE, OPTIONS, PARAMETERS_KEYS } from '../../constants'; import { getMappedAndSortedNotes } from '../../utils/helpers'; import { useErrorMessages } from '../useErrorMessages'; @@ -12,7 +12,7 @@ export const useItemNotes = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: ITEM_NOTES_KEY }); const { formatMessage } = useIntl(); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading: isItemNotesLoading } = useQuery( { @@ -20,8 +20,7 @@ export const useItemNotes = (options = {}) => { cacheTime: Infinity, staleTime: Infinity, queryFn: () => ky.get('item-note-types', { searchParams: { limit: 1000 } }).json(), - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_INVENTORY_STORAGE, error), ...options, }, ); diff --git a/src/hooks/api/useLoanTypes.js b/src/hooks/api/useLoanTypes.js index c20cac76..0f81127b 100644 --- a/src/hooks/api/useLoanTypes.js +++ b/src/hooks/api/useLoanTypes.js @@ -1,13 +1,14 @@ import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useQuery } from 'react-query'; import { useErrorMessages } from '../useErrorMessages'; +import { MOD_INVENTORY_STORAGE } from '../../constants'; export const LOAN_TYPES_KEY = 'LOAN_TYPES_KEY'; export const useLoanTypes = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: LOAN_TYPES_KEY }); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading } = useQuery( { @@ -15,8 +16,7 @@ export const useLoanTypes = (options = {}) => { cacheTime: Infinity, staleTime: Infinity, queryFn: () => ky.get('loan-types?query=cql.allRecords%3D1%20sortby%20name&limit=1000').json(), - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_INVENTORY_STORAGE, error), ...options, }, ); diff --git a/src/hooks/api/useLoanTypes.test.js b/src/hooks/api/useLoanTypes.test.js new file mode 100644 index 00000000..d747e7bf --- /dev/null +++ b/src/hooks/api/useLoanTypes.test.js @@ -0,0 +1,87 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useQuery } from 'react-query'; + +import { useNamespace, useOkapiKy } from '@folio/stripes/core'; + +import { useErrorMessages } from '../useErrorMessages'; +import { useLoanTypes, LOAN_TYPES_KEY } from './useLoanTypes'; + +jest.mock('@folio/stripes/core', () => ({ + useNamespace: jest.fn(), + useOkapiKy: jest.fn(), +})); + +jest.mock('react-query', () => ({ + useQuery: jest.fn(), +})); + +jest.mock('../useErrorMessages', () => ({ + useErrorMessages: jest.fn(), +})); + +describe('useLoanTypes', () => { + const mockGet = jest.fn(); + const mockShowExternalModuleError = jest.fn(); + const kyMock = { get: mockGet }; + + beforeEach(() => { + jest.clearAllMocks(); + + useNamespace.mockReturnValue([`${LOAN_TYPES_KEY}_NAMESPACE`]); + useOkapiKy.mockReturnValue(kyMock); + useErrorMessages.mockReturnValue({ showExternalModuleError: mockShowExternalModuleError }); + }); + + it('should return loan types when data is available', async () => { + const mockData = { + loantypes: [ + { id: '1', name: 'Loan Type 1' }, + { id: '2', name: 'Loan Type 2' }, + ], + }; + + useQuery.mockReturnValue({ + data: mockData, + isLoading: false, + }); + + const { result } = renderHook(() => useLoanTypes()); + + expect(result.current.loanTypes).toEqual([ + { label: 'Loan Type 1', value: '1' }, + { label: 'Loan Type 2', value: '2' }, + ]); + expect(result.current.isLoading).toBe(false); + expect(useNamespace).toHaveBeenCalledWith({ key: LOAN_TYPES_KEY }); + expect(useQuery).toHaveBeenCalledWith( + expect.objectContaining({ queryKey: [`${LOAN_TYPES_KEY}_NAMESPACE`] }) + ); + }); + + it('should handle loading state correctly', () => { + useQuery.mockReturnValue({ + data: null, + isLoading: true, + }); + + const { result } = renderHook(() => useLoanTypes()); + + expect(result.current.loanTypes).toEqual([]); + expect(result.current.isLoading).toBe(true); + }); + + it('should allow passing additional options to useQuery', () => { + const additionalOptions = { refetchInterval: 5000 }; + + useQuery.mockReturnValue({ + data: null, + isLoading: false, + }); + + renderHook(() => useLoanTypes(additionalOptions)); + + expect(useQuery).toHaveBeenCalledWith( + expect.objectContaining(additionalOptions) + ); + }); +}); diff --git a/src/hooks/api/usePatronGroup.js b/src/hooks/api/usePatronGroup.js index e87edf81..18866acd 100644 --- a/src/hooks/api/usePatronGroup.js +++ b/src/hooks/api/usePatronGroup.js @@ -4,21 +4,21 @@ import { import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useErrorMessages } from '../useErrorMessages'; +import { MOD_USERS } from '../../constants'; export const PATRON_GROUP_KEY = 'PATRON_GROUP_KEY'; export const usePatronGroup = (options = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: PATRON_GROUP_KEY }); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading } = useQuery( { queryKey: [namespaceKey], cacheTime: Infinity, staleTime: Infinity, - onSuccess: showErrorMessage, - onError: showErrorMessage, + onError: (error) => showExternalModuleError(MOD_USERS, error), queryFn: async () => { const { usergroups } = await ky.get('groups', { searchParams: { limit: 200 } }).json(); diff --git a/src/hooks/api/useRecordTypes.js b/src/hooks/api/useRecordTypes.js index e472e12c..b3b5c394 100644 --- a/src/hooks/api/useRecordTypes.js +++ b/src/hooks/api/useRecordTypes.js @@ -1,13 +1,14 @@ import { useNamespace, useOkapiKy } from '@folio/stripes/core'; import { useQuery } from 'react-query'; import { useErrorMessages } from '../useErrorMessages'; +import { MOD_FQM_MANAGER } from '../../constants'; export const ENTITY_TYPE_KEY = 'ENTITY_TYPE_KEY'; export const useRecordTypes = ({ enabled } = {}) => { const ky = useOkapiKy(); const [namespaceKey] = useNamespace({ key: ENTITY_TYPE_KEY }); - const { showErrorMessage } = useErrorMessages(); + const { showExternalModuleError } = useErrorMessages(); const { data, isLoading, error } = useQuery({ queryKey: [namespaceKey], @@ -18,8 +19,7 @@ export const useRecordTypes = ({ enabled } = {}) => { }, cacheTime: Infinity, staleTime: Infinity, - onError: showErrorMessage, - onSuccess: showErrorMessage, + onError: (err) => showExternalModuleError(MOD_FQM_MANAGER, err), enabled }); diff --git a/src/hooks/api/useUserGroupsMap.js b/src/hooks/api/useUserGroupsMap.js deleted file mode 100644 index 50e17f68..00000000 --- a/src/hooks/api/useUserGroupsMap.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - useQuery, -} from 'react-query'; - -import { useNamespace, useOkapiKy } from '@folio/stripes/core'; -import { useErrorMessages } from '../useErrorMessages'; - -export const GROUP_MAP_KEYS = 'GROUP_MAP_KEYS'; - -export const useUserGroupsMap = () => { - const ky = useOkapiKy(); - const [namespaceKey] = useNamespace({ key: GROUP_MAP_KEYS }); - const { showErrorMessage } = useErrorMessages(); - - const { data } = useQuery( - { - queryKey: [namespaceKey], - cacheTime: Infinity, - staleTime: Infinity, - onError: showErrorMessage, - onSuccess: showErrorMessage, - queryFn: async () => { - const { usergroups } = await ky.get('groups', { searchParams: { limit: 200 } }).json(); - - return usergroups.reduce((acc, curr) => ( - { - ...acc, - [curr.id]: curr.group, - } - ), {}); - }, - }, - ); - - return ({ - userGroups: data || {}, - }); -}; diff --git a/src/hooks/useErrorMessages.js b/src/hooks/useErrorMessages.js index c5c216f2..0c6c2019 100644 --- a/src/hooks/useErrorMessages.js +++ b/src/hooks/useErrorMessages.js @@ -1,5 +1,6 @@ import { useIntl } from 'react-intl'; import { useShowCallout } from '@folio/stripes-acq-components'; +import { getReasonPhrase } from 'http-status-codes'; import { ERRORS } from '../constants'; import { useSearchParams } from './useSearchParams'; @@ -41,7 +42,26 @@ export const useErrorMessages = () => { } }; + const showExternalModuleError = (moduleName, error) => { + const status = error?.status ?? 500; + const initialErrorMessage = error?.message; + + let displayMessage = ''; + + try { + displayMessage = getReasonPhrase(initialErrorMessage); // Some modules return the error message as a known status + } catch { + const statusErrorMessage = getReasonPhrase(status); + + // Determine displayMessage details based on priority: initialErrorMessage > statusErrorMessage + displayMessage = initialErrorMessage || statusErrorMessage; + } + + showError(`${moduleName} returns status code: ${status} - ${displayMessage}.`); + }; + return { showErrorMessage, + showExternalModuleError, }; }; diff --git a/src/hooks/useErrorMessages.test.js b/src/hooks/useErrorMessages.test.js index 6926c47d..b1fb218c 100644 --- a/src/hooks/useErrorMessages.test.js +++ b/src/hooks/useErrorMessages.test.js @@ -1,6 +1,9 @@ import { renderHook } from '@testing-library/react-hooks'; import { useIntl } from 'react-intl'; +import { getReasonPhrase } from 'http-status-codes'; + import { useShowCallout } from '@folio/stripes-acq-components'; + import { useErrorMessages } from './useErrorMessages'; import { ERRORS } from '../constants'; @@ -115,4 +118,87 @@ describe('useErrorMessages', () => { message: 'ui-bulk-edit.error.incorrectFormatted.testFileName', }); }); + + describe('showExternalModuleError', () => { + it('should show an error message using the status code if message not provided', () => { + const { result } = renderHook(() => useErrorMessages()); + + result.current.showExternalModuleError('TestModule', { status: 404 }); + + expect(showCalloutMock) + .toHaveBeenCalledWith({ + type: 'error', + message: `TestModule returns status code: 404 - ${getReasonPhrase(404)}.`, + }); + }); + + it('should show an error message using the "message" property if it exists', () => { + const { result } = renderHook(() => useErrorMessages()); + + const errorMessage = 'Something went wrong'; + result.current.showExternalModuleError('TestModule', { message: errorMessage }); + + expect(showCalloutMock) + .toHaveBeenCalledWith({ + type: 'error', + message: `TestModule returns status code: 500 - ${errorMessage}.`, + }); + }); + + it('should show an error message using the message if message corresponds to a known status string', () => { + const { result } = renderHook(() => useErrorMessages()); + + const errorMessage = 'Not Found'; + result.current.showExternalModuleError('TestModule', { + status: 403, + message: errorMessage + }); + + expect(showCalloutMock) + .toHaveBeenCalledWith({ + type: 'error', + message: 'TestModule returns status code: 403 - Not Found.', + }); + }); + + it('should show a generic status error message if neither a valid message nor a recognized message key is provided', () => { + const { result } = renderHook(() => useErrorMessages()); + + result.current.showExternalModuleError('TestModule', { status: 418 }); + + expect(showCalloutMock) + .toHaveBeenCalledWith({ + type: 'error', + message: `TestModule returns status code: 418 - ${getReasonPhrase(418)}.`, + }); + }); + + it('should default to 500 if no status is provided and message is empty', () => { + const { result } = renderHook(() => useErrorMessages()); + + result.current.showExternalModuleError('TestModule', {}); + + expect(showCalloutMock) + .toHaveBeenCalledWith({ + type: 'error', + message: `TestModule returns status code: 500 - ${getReasonPhrase(500)}.`, + }); + }); + + it('should handle a scenario where the module returns a non-standard error message', () => { + const { result } = renderHook(() => useErrorMessages()); + + const nonStandardMessage = 'Custom module error occurred'; + result.current.showExternalModuleError('TestModule', { + status: 501, + message: nonStandardMessage + }); + + expect(showCalloutMock) + .toHaveBeenCalledWith({ + type: 'error', + message: `TestModule returns status code: 501 - ${nonStandardMessage}.`, + }); + }); + }); }); diff --git a/src/hooks/usePublishCoordinator.js b/src/hooks/usePublishCoordinator.js index 60de5116..eb5b2790 100644 --- a/src/hooks/usePublishCoordinator.js +++ b/src/hooks/usePublishCoordinator.js @@ -9,6 +9,9 @@ import { useStripes, } from '@folio/stripes/core'; +import { MOD_CONSORTIA } from '../constants'; +import { useErrorMessages } from './useErrorMessages'; + export const PUBLISH_COORDINATOR_STATUSES = { COMPLETE: 'COMPLETE', ERROR: 'ERROR', @@ -44,6 +47,7 @@ export const usePublishCoordinator = (namespace, options = {}) => { const ky = useOkapiKy(); const stripes = useStripes(); const abortController = useRef(new AbortController()); + const { showExternalModuleError } = useErrorMessages(); const consortium = stripes.user?.user?.consortium; const baseApi = `${CONSORTIA_API}/${consortium?.id}/${PUBLICATIONS_API}`; @@ -51,7 +55,8 @@ export const usePublishCoordinator = (namespace, options = {}) => { const getPublicationResults = useCallback((id, { signal }) => { return ky.get(`${baseApi}/${id}/results`, { signal }) .json() - .then(formatPublicationResult); + .then(formatPublicationResult) + .catch((error) => showExternalModuleError(MOD_CONSORTIA, error)); }, [ky, baseApi]); const getPublicationDetails = useCallback(async (requestId, { signal } = {}) => { @@ -83,7 +88,8 @@ export const usePublishCoordinator = (namespace, options = {}) => { return ky.post(baseApi, { json, signal }) .json() - .then(res => getPublicationResponse(res, { signal })); + .then(res => getPublicationResponse(res, { signal })) + .catch((error) => showExternalModuleError(MOD_CONSORTIA, error)); }, [baseApi, getPublicationResponse, ky, options.signal]); return { diff --git a/src/hooks/usePublishCoordinator.test.js b/src/hooks/usePublishCoordinator.test.js index 0c8f5293..050c0cb1 100644 --- a/src/hooks/usePublishCoordinator.test.js +++ b/src/hooks/usePublishCoordinator.test.js @@ -18,6 +18,10 @@ import { PUBLISH_COORDINATOR_STATUSES } from './usePublishCoordinator'; +jest.mock('./useErrorMessages', () => ({ + useErrorMessages: jest.fn().mockReturnValue({ showExternalModuleError: jest.fn() }), +})); + const queryClient = new QueryClient(); // eslint-disable-next-line react/prop-types