diff --git a/docs/docs/core/hooks/use-fetch-client.mdx b/docs/docs/core/helper-plugin/hooks/use-fetch-client.mdx similarity index 99% rename from docs/docs/core/hooks/use-fetch-client.mdx rename to docs/docs/core/helper-plugin/hooks/use-fetch-client.mdx index c49cf77ed8..4d6d89ec63 100644 --- a/docs/docs/core/hooks/use-fetch-client.mdx +++ b/docs/docs/core/helper-plugin/hooks/use-fetch-client.mdx @@ -1,11 +1,11 @@ --- title: useFetchClient -slug: /hooks/use-fetch-client description: API reference for the useFetchClient hook in Strapi tags: - hooks - axios - data + - helper-plugin --- ## Usage diff --git a/docs/sidebars.js b/docs/sidebars.js index 930f048fa5..6ecb289c26 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -16,6 +16,17 @@ const sidebars = { // By default, Docusaurus generates a sidebar from the docs folder structure docs: [ 'index', + { + type: 'category', + label: 'Admin', + items: [ + { + type: 'doc', + label: 'Link Strapi Design System', + id: 'core/admin/link-strapi-design-system', + }, + ], + }, { type: 'category', label: 'Core', @@ -55,28 +66,6 @@ const sidebars = { }, ], }, - { - type: 'category', - label: 'Hooks', - items: [ - { - type: 'doc', - label: 'useFetchClient', - id: 'core/hooks/use-fetch-client', - }, - ], - }, - { - type: 'category', - label: 'Admin', - items: [ - { - type: 'doc', - label: 'Link Strapi Design System', - id: 'core/admin/link-strapi-design-system', - }, - ], - }, { type: 'category', label: 'Content Type Builder', @@ -86,6 +75,34 @@ const sidebars = { }, items: ['example'], }, + { + type: 'category', + label: 'Helper Plugin', + items: [ + { + type: 'category', + label: 'Hooks', + items: [ + { + type: 'doc', + label: 'useFetchClient', + id: 'core/helper-plugin/hooks/use-fetch-client', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Utils', + items: [ + { + type: 'doc', + label: 'Async', + id: 'core/utils/async', + }, + ], + }, ], }, { diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js b/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js index 2f03015bb4..3affb819d2 100644 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js +++ b/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js @@ -1,10 +1,11 @@ import axios from 'axios'; +import { getFetchClient } from '@strapi/helper-plugin'; import checkLatestStrapiVersion from './checkLatestStrapiVersion'; -import { axiosInstance } from '../../../core/utils'; import packageJSON from '../../../../../package.json'; const strapiVersion = packageJSON.version; const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF')); +const { get } = getFetchClient(); const fetchStrapiLatestRelease = async (toggleNotification) => { try { @@ -38,7 +39,7 @@ const fetchStrapiLatestRelease = async (toggleNotification) => { const fetchAppInfo = async () => { try { - const { data, headers } = await axiosInstance.get('/admin/information'); + const { data, headers } = await get('/admin/information'); if (!headers['content-type'].includes('application/json')) { throw new Error('Not found'); @@ -52,7 +53,7 @@ const fetchAppInfo = async () => { const fetchCurrentUserPermissions = async () => { try { - const { data, headers } = await axiosInstance.get('/admin/users/me/permissions'); + const { data, headers } = await get('/admin/users/me/permissions'); if (!headers['content-type'].includes('application/json')) { throw new Error('Not found'); @@ -70,7 +71,7 @@ const fetchUserRoles = async () => { data: { data: { roles }, }, - } = await axiosInstance.get('/admin/users/me'); + } = await get('/admin/users/me'); return roles; } catch (err) { diff --git a/packages/core/admin/admin/src/components/LeftMenu/index.js b/packages/core/admin/admin/src/components/LeftMenu/index.js index 243f8e7703..4aa766a070 100644 --- a/packages/core/admin/admin/src/components/LeftMenu/index.js +++ b/packages/core/admin/admin/src/components/LeftMenu/index.js @@ -20,9 +20,14 @@ import { Typography } from '@strapi/design-system/Typography'; import { Stack } from '@strapi/design-system/Stack'; import Write from '@strapi/icons/Write'; import Exit from '@strapi/icons/Exit'; -import { auth, usePersistentState, useAppInfos, useTracking } from '@strapi/helper-plugin'; +import { + auth, + usePersistentState, + useAppInfos, + useTracking, + getFetchClient, +} from '@strapi/helper-plugin'; import { useConfigurations } from '../../hooks'; -import { axiosInstance } from '../../core/utils'; const LinkUserWrapper = styled(Box)` width: ${150 / 16}rem; @@ -64,6 +69,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => { const { trackUsage } = useTracking(); const { pathname } = useLocation(); const history = useHistory(); + const { post } = getFetchClient(); const initials = userDisplayName .split(' ') @@ -74,7 +80,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => { const handleToggleUserLinks = () => setUserLinksVisible((prev) => !prev); const handleLogout = async () => { - await axiosInstance.post('/admin/logout'); + await post('/admin/logout'); auth.clearAppStorage(); handleToggleUserLinks(); history.push('/auth/login'); diff --git a/packages/core/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js b/packages/core/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js index 968007064f..549c6ffaed 100644 --- a/packages/core/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +++ b/packages/core/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js @@ -10,11 +10,11 @@ import { formatContentTypeData, contentManagementUtilRemoveFieldsFromData, useGuidedTour, + useFetchClient, } from '@strapi/helper-plugin'; import { useSelector, useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; import isEqual from 'react-fast-compare'; -import { axiosInstance } from '../../../core/utils'; import { createDefaultForm, getTrad, @@ -51,6 +51,9 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } const allLayoutDataRef = useRef(allLayoutData); + const fetchClient = useFetchClient(); + const { put, post, del } = fetchClient; + const isCreatingEntry = id === null; const requestURL = useMemo(() => { @@ -137,7 +140,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } dispatch(getData()); try { - const { data } = await axiosInstance.get(requestURL, { cancelToken: source.token }); + const { data } = await fetchClient.get(requestURL, { cancelToken: source.token }); dispatch(getDataSucceeded(cleanReceivedData(cleanClonedData(data)))); } catch (err) { @@ -187,6 +190,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } source.cancel('Operation canceled by the user.'); }; }, [ + fetchClient, cleanClonedData, cleanReceivedData, push, @@ -216,12 +220,12 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } const onDelete = useCallback( async (trackerProperty) => { + console.log('onDelete'); + try { trackUsageRef.current('willDeleteEntry', trackerProperty); - const { data } = await axiosInstance.delete( - getRequestUrl(`collection-types/${slug}/${id}`) - ); + const { data } = await del(getRequestUrl(`collection-types/${slug}/${id}`)); toggleNotification({ type: 'success', @@ -237,7 +241,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } return Promise.reject(err); } }, - [id, slug, toggleNotification] + [id, slug, toggleNotification, del] ); const onDeleteSucceeded = useCallback(() => { @@ -247,12 +251,11 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } const onPost = useCallback( async (body, trackerProperty) => { const endPoint = `${getRequestUrl(`collection-types/${slug}`)}${rawQuery}`; - try { // Show a loading button in the EditView/Header.js && lock the app => no navigation dispatch(setStatus('submit-pending')); - const { data } = await axiosInstance.post(endPoint, body); + const { data } = await post(endPoint, body); trackUsageRef.current('didCreateEntry', trackerProperty); toggleNotification({ @@ -291,6 +294,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } toggleNotification, setCurrentStep, queryClient, + post, ] ); @@ -303,7 +307,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } ); dispatch(setStatus('draft-relation-check-pending')); - const numberOfDraftRelations = await axiosInstance.get(endPoint); + const numberOfDraftRelations = await fetchClient.get(endPoint); trackUsageRef.current('didCheckDraftRelations'); dispatch(setStatus('resolved')); @@ -315,7 +319,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } return Promise.reject(err); } - }, [displayErrors, id, slug, dispatch]); + }, [displayErrors, id, slug, dispatch, fetchClient]); const onPublish = useCallback(async () => { try { @@ -324,7 +328,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } dispatch(setStatus('publish-pending')); - const { data } = await axiosInstance.post(endPoint); + const { data } = await post(endPoint); trackUsageRef.current('didPublishEntry'); @@ -343,7 +347,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } return Promise.reject(err); } - }, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification]); + }, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification, post]); const onPut = useCallback( async (body, trackerProperty) => { @@ -354,7 +358,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } dispatch(setStatus('submit-pending')); - const { data } = await axiosInstance.put(endPoint, body); + const { data } = await put(endPoint, body); trackUsageRef.current('didEditEntry', { trackerProperty }); toggleNotification({ @@ -379,7 +383,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } return Promise.reject(err); } }, - [cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification, queryClient] + [cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification, queryClient, put] ); const onUnpublish = useCallback(async () => { @@ -390,7 +394,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } try { trackUsageRef.current('willUnpublishEntry'); - const { data } = await axiosInstance.post(endPoint); + const { data } = await post(endPoint); trackUsageRef.current('didUnpublishEntry'); toggleNotification({ @@ -408,7 +412,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin } return Promise.reject(err); } - }, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification]); + }, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification, post]); return children({ componentsDataStructure, diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js index 315c4e007b..53bfc16818 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js @@ -10,19 +10,18 @@ import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu'; import { Loader } from '@strapi/design-system/Loader'; import styled from 'styled-components'; import { useNotifyAT } from '@strapi/design-system/LiveRegions'; -import { stopPropagation } from '@strapi/helper-plugin'; +import { stopPropagation, useFetchClient } from '@strapi/helper-plugin'; import CellValue from '../CellValue'; -import { axiosInstance } from '../../../../../core/utils'; import { getRequestUrl, getTrad } from '../../../../utils'; const TypographyMaxWidth = styled(Typography)` max-width: 500px; `; -const fetchRelation = async (endPoint, notifyStatus) => { +const fetchRelation = async (endPoint, notifyStatus, get) => { const { data: { results, pagination }, - } = await axiosInstance.get(endPoint); + } = await get(endPoint); notifyStatus(); @@ -37,6 +36,7 @@ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, conte [entityId, name, contentType] ); const [isOpen, setIsOpen] = useState(false); + const { get } = useFetchClient(); const Label = ( @@ -61,7 +61,7 @@ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, conte const { data, status } = useQuery( [fieldSchema.targetModel, entityId], - () => fetchRelation(relationFetchEndpoint, notify), + () => fetchRelation(relationFetchEndpoint, notify, get), { enabled: isOpen, staleTime: 0, diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap index 01b2c15114..540ed9e995 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/__snapshots__/index.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DynamicTabe / Cellcontent / RelationMultiple renders and matches the snapshot 1`] = ` +exports[`DynamicTable / Cellcontent / RelationMultiple renders and matches the snapshot 1`] = ` .c13 { border: 0; -webkit-clip: rect(0 0 0 0); diff --git a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js index 81400f082e..45cc519002 100644 --- a/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js +++ b/packages/core/admin/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/tests/index.test.js @@ -4,23 +4,28 @@ import { ThemeProvider, lightTheme } from '@strapi/design-system'; import { IntlProvider } from 'react-intl'; import { QueryClientProvider, QueryClient } from 'react-query'; -import { axiosInstance } from '../../../../../../core/utils'; +import { useFetchClient } from '@strapi/helper-plugin'; import RelationMultiple from '../index'; -jest.spyOn(axiosInstance, 'get').mockResolvedValue({ - data: { - results: [ - { - id: 1, - name: 'Relation entity 1', - }, - ], +jest.mock('@strapi/helper-plugin', () => ({ + ...jest.requireActual('@strapi/helper-plugin'), + useFetchClient: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ + data: { + results: [ + { + id: 1, + name: 'Relation entity 1', + }, + ], - pagination: { - total: 1, - }, - }, -}); + pagination: { + total: 1, + }, + }, + }), + }), +})); const DEFAULT_PROPS_FIXTURE = { contentType: { @@ -61,21 +66,23 @@ const ComponentFixture = () => { ); }; -describe('DynamicTabe / Cellcontent / RelationMultiple', () => { +describe('DynamicTable / Cellcontent / RelationMultiple', () => { it('renders and matches the snapshot', async () => { const { container } = render(); expect(container).toMatchSnapshot(); - expect(axiosInstance.get).toHaveBeenCalledTimes(0); + const { get } = useFetchClient(); + expect(get).toHaveBeenCalledTimes(0); }); it('fetches relation entities once the menu is opened', async () => { const { container } = render(); + const { get } = useFetchClient(); const button = container.querySelector('[type=button]'); fireEvent(button, new MouseEvent('mousedown', { bubbles: true })); expect(screen.getByText('Relations are loading')).toBeInTheDocument(); - expect(axiosInstance.get).toHaveBeenCalledTimes(1); + expect(get).toHaveBeenCalledTimes(1); await waitFor(() => expect(screen.getByText('Relation entity 1')).toBeInTheDocument()); }); }); diff --git a/packages/core/admin/admin/src/content-manager/components/InputUID/index.js b/packages/core/admin/admin/src/content-manager/components/InputUID/index.js index 2c79c74a24..8dfe16d0c1 100644 --- a/packages/core/admin/admin/src/content-manager/components/InputUID/index.js +++ b/packages/core/admin/admin/src/content-manager/components/InputUID/index.js @@ -1,6 +1,6 @@ import React, { useEffect, useState, useRef } from 'react'; import PropTypes from 'prop-types'; -import { useCMEditViewDataManager } from '@strapi/helper-plugin'; +import { useCMEditViewDataManager, useFetchClient } from '@strapi/helper-plugin'; import { useIntl } from 'react-intl'; import get from 'lodash/get'; import { TextInput } from '@strapi/design-system/TextInput'; @@ -9,7 +9,6 @@ import Refresh from '@strapi/icons/Refresh'; import CheckCircle from '@strapi/icons/CheckCircle'; import ExclamationMarkCircle from '@strapi/icons/ExclamationMarkCircle'; import Loader from '@strapi/icons/Loader'; -import { axiosInstance } from '../../../core/utils'; import { getRequestUrl } from '../../utils'; import useDebounce from './useDebounce'; import UID_REGEX from './regex'; @@ -46,6 +45,7 @@ const InputUID = ({ const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300); const [isCustomized, setIsCustomized] = useState(false); const [regenerateLabel, setRegenerateLabel] = useState(null); + const { post } = useFetchClient(); const label = intlLabel.id ? formatMessage( @@ -67,7 +67,7 @@ const InputUID = ({ try { const { data: { data }, - } = await axiosInstance.post(requestURL, { + } = await post(requestURL, { contentTypeUID, field: name, data: modifiedData, @@ -89,7 +89,7 @@ const InputUID = ({ } try { - const { data } = await axiosInstance.post(requestURL, { + const { data } = await post(requestURL, { contentTypeUID, field: name, value: value ? value.trim() : '', diff --git a/packages/core/admin/admin/src/content-manager/components/SingleTypeFormWrapper/index.js b/packages/core/admin/admin/src/content-manager/components/SingleTypeFormWrapper/index.js index 778923fe20..dd75dade1f 100644 --- a/packages/core/admin/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +++ b/packages/core/admin/admin/src/content-manager/components/SingleTypeFormWrapper/index.js @@ -8,11 +8,11 @@ import { useQueryParams, useNotification, useGuidedTour, + useFetchClient, } from '@strapi/helper-plugin'; import { useSelector, useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; import axios from 'axios'; -import { axiosInstance } from '../../../core/utils'; import { createDefaultForm, getTrad, removePasswordFieldsFromData } from '../../utils'; import { getData, @@ -39,6 +39,8 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { const searchToSend = buildQueryString(query); const toggleNotification = useNotification(); const dispatch = useDispatch(); + const fetchClient = useFetchClient(); + const { post, put, del } = fetchClient; const { componentsDataStructure, contentTypeDataStructure, data, isLoading, status } = useSelector(selectCrudReducer); @@ -103,7 +105,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { setIsCreatingEntry(true); try { - const { data } = await axiosInstance.get(getRequestUrl(`${slug}${searchToSend}`), { + const { data } = await fetchClient.get(getRequestUrl(`${slug}${searchToSend}`), { cancelToken: source.token, }); @@ -136,7 +138,16 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { fetchData(source); return () => source.cancel('Operation canceled by the user.'); - }, [cleanReceivedData, push, slug, dispatch, searchToSend, rawQuery, toggleNotification]); + }, [ + fetchClient, + cleanReceivedData, + push, + slug, + dispatch, + searchToSend, + rawQuery, + toggleNotification, + ]); const displayErrors = useCallback( (err) => { @@ -160,7 +171,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { try { trackUsageRef.current('willDeleteEntry', trackerProperty); - const { data } = await axiosInstance.delete(getRequestUrl(`${slug}${searchToSend}`)); + const { data } = await del(getRequestUrl(`${slug}${searchToSend}`)); toggleNotification({ type: 'success', @@ -178,7 +189,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { return Promise.reject(err); } }, - [slug, displayErrors, toggleNotification, searchToSend] + [del, slug, displayErrors, toggleNotification, searchToSend] ); const onDeleteSucceeded = useCallback(() => { @@ -194,7 +205,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { try { dispatch(setStatus('submit-pending')); - const { data } = await axiosInstance.put(endPoint, body); + const { data } = await put(endPoint, body); trackUsageRef.current('didCreateEntry', trackerProperty); toggleNotification({ @@ -224,6 +235,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { } }, [ + put, cleanReceivedData, displayErrors, slug, @@ -242,7 +254,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { const endPoint = getRequestUrl(`${slug}/actions/numberOfDraftRelations`); dispatch(setStatus('draft-relation-check-pending')); - const numberOfDraftRelations = await axiosInstance.get(endPoint); + const numberOfDraftRelations = await fetchClient.get(endPoint); trackUsageRef.current('didCheckDraftRelations'); dispatch(setStatus('resolved')); @@ -254,7 +266,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { return Promise.reject(err); } - }, [displayErrors, slug, dispatch]); + }, [fetchClient, displayErrors, slug, dispatch]); const onPublish = useCallback(async () => { try { @@ -263,7 +275,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { dispatch(setStatus('publish-pending')); - const { data } = await axiosInstance.post(endPoint); + const { data } = await post(endPoint); trackUsageRef.current('didPublishEntry'); toggleNotification({ @@ -283,7 +295,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { return Promise.reject(err); } - }, [cleanReceivedData, displayErrors, slug, searchToSend, dispatch, toggleNotification]); + }, [post, cleanReceivedData, displayErrors, slug, searchToSend, dispatch, toggleNotification]); const onPut = useCallback( async (body, trackerProperty) => { @@ -294,7 +306,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { dispatch(setStatus('submit-pending')); - const { data } = await axiosInstance.put(endPoint, body); + const { data } = await put(endPoint, body); toggleNotification({ type: 'success', @@ -321,7 +333,16 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { return Promise.reject(err); } }, - [cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification, queryClient] + [ + put, + cleanReceivedData, + displayErrors, + slug, + dispatch, + rawQuery, + toggleNotification, + queryClient, + ] ); // The publish and unpublish method could be refactored but let's leave the duplication for now @@ -333,7 +354,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { try { trackUsageRef.current('willUnpublishEntry'); - const { data } = await axiosInstance.post(endPoint); + const { data } = await post(endPoint); trackUsageRef.current('didUnpublishEntry'); toggleNotification({ @@ -348,7 +369,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => { dispatch(setStatus('resolved')); displayErrors(err); } - }, [cleanReceivedData, toggleNotification, displayErrors, slug, dispatch, searchToSend]); + }, [post, cleanReceivedData, toggleNotification, displayErrors, slug, dispatch, searchToSend]); return children({ componentsDataStructure, diff --git a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/index.js b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/index.js index f479b4b3c0..9fa5e24394 100644 --- a/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/index.js +++ b/packages/core/admin/admin/src/content-manager/hooks/useFetchContentTypeLayout/index.js @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'; import { useSelector, shallowEqual } from 'react-redux'; import axios from 'axios'; -import { axiosInstance } from '../../../core/utils'; +import { useFetchClient } from '@strapi/helper-plugin'; import formatLayouts from './utils/formatLayouts'; import reducer, { initialState } from './reducer'; import { makeSelectModelAndComponentSchemas } from '../../pages/App/selectors'; @@ -12,6 +12,7 @@ const useFetchContentTypeLayout = (contentTypeUID) => { const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []); const { schemas } = useSelector((state) => schemasSelector(state), shallowEqual); const isMounted = useRef(true); + const { get } = useFetchClient(); const getData = useCallback( async (uid, source) => { @@ -27,7 +28,7 @@ const useFetchContentTypeLayout = (contentTypeUID) => { const { data: { data }, - } = await axiosInstance.get(endPoint, { cancelToken: source.token }); + } = await get(endPoint, { cancelToken: source.token }); dispatch({ type: 'GET_DATA_SUCCEEDED', @@ -47,7 +48,7 @@ const useFetchContentTypeLayout = (contentTypeUID) => { } } }, - [layouts, schemas] + [layouts, schemas, get] ); useEffect(() => { diff --git a/packages/core/admin/admin/src/content-manager/hooks/useRelation/tests/useRelation.test.js b/packages/core/admin/admin/src/content-manager/hooks/useRelation/tests/useRelation.test.js index f22a60c6b6..514e6d11d0 100644 --- a/packages/core/admin/admin/src/content-manager/hooks/useRelation/tests/useRelation.test.js +++ b/packages/core/admin/admin/src/content-manager/hooks/useRelation/tests/useRelation.test.js @@ -2,12 +2,12 @@ import React from 'react'; import { QueryClientProvider, QueryClient } from 'react-query'; import { renderHook, act } from '@testing-library/react-hooks'; -import { axiosInstance } from '../../../../core/utils'; +import { useFetchClient } from '@strapi/helper-plugin'; import { useRelation } from '../useRelation'; -jest.mock('../../../../core/utils', () => ({ - ...jest.requireActual('../../../../core/utils'), - axiosInstance: { +jest.mock('@strapi/helper-plugin', () => ({ + ...jest.requireActual('@strapi/helper-plugin'), + useFetchClient: jest.fn().mockReturnValue({ get: jest.fn().mockResolvedValue({ data: { results: [ @@ -17,7 +17,7 @@ jest.mock('../../../../core/utils', () => ({ pagination: { page: 1, pageCount: 10 }, }, }), - }, + }), })); const client = new QueryClient({ @@ -85,9 +85,11 @@ describe('useRelation', () => { }, }); - await waitFor(() => expect(axiosInstance.get).toBeCalledTimes(1)); + const { get } = useFetchClient(); - expect(axiosInstance.get).toBeCalledWith('/', { + await waitFor(() => expect(get).toBeCalledTimes(1)); + + expect(get).toBeCalledWith('/', { params: { limit: 10, page: 1, @@ -110,7 +112,7 @@ describe('useRelation', () => { title: 'xToOne relation', }; - axiosInstance.get = jest.fn().mockResolvedValueOnce({ + useFetchClient().get = jest.fn().mockResolvedValueOnce({ data: { data: FIXTURE, }, @@ -134,7 +136,9 @@ describe('useRelation', () => { await waitForNextUpdate(); - expect(axiosInstance.get).toBeCalledWith(expect.any(String), { + const { get } = useFetchClient(); + + expect(get).toBeCalledWith(expect.any(String), { params: { limit: 5, page: expect.any(Number), @@ -144,8 +148,9 @@ describe('useRelation', () => { test('does not fetch relations if it was not enabled', async () => { await setup({ relation: { enabled: false } }); + const { get } = useFetchClient(); - expect(axiosInstance.get).not.toBeCalled(); + expect(get).not.toBeCalled(); }); test('fetch relations', async () => { @@ -153,9 +158,11 @@ describe('useRelation', () => { await waitForNextUpdate(); + const { get } = useFetchClient(); + expect(result.current.relations.isSuccess).toBe(true); - expect(axiosInstance.get).toBeCalledTimes(1); - expect(axiosInstance.get).toBeCalledWith('/', { + expect(get).toBeCalledTimes(1); + expect(get).toBeCalledWith('/', { params: { limit: 10, page: 1, @@ -164,7 +171,7 @@ describe('useRelation', () => { }); test('fetch relations next page, if there is one', async () => { - axiosInstance.get = jest.fn().mockResolvedValueOnce({ + useFetchClient().get = jest.fn().mockResolvedValueOnce({ data: { results: [], pagination: { @@ -174,6 +181,8 @@ describe('useRelation', () => { }, }); + const { get } = useFetchClient(); + const { result, waitForNextUpdate } = await setup(); await waitForNextUpdate(); @@ -184,14 +193,14 @@ describe('useRelation', () => { await waitForNextUpdate(); - expect(axiosInstance.get).toBeCalledTimes(2); - expect(axiosInstance.get).toHaveBeenNthCalledWith(1, expect.any(String), { + expect(get).toBeCalledTimes(2); + expect(get).toHaveBeenNthCalledWith(1, expect.any(String), { params: { limit: expect.any(Number), page: 1, }, }); - expect(axiosInstance.get).toHaveBeenNthCalledWith(2, expect.any(String), { + expect(get).toHaveBeenNthCalledWith(2, expect.any(String), { params: { limit: expect.any(Number), page: 2, @@ -200,7 +209,7 @@ describe('useRelation', () => { }); test("does not fetch relations next page, if there isn't one", async () => { - axiosInstance.get = jest.fn().mockResolvedValueOnce({ + useFetchClient().get = jest.fn().mockResolvedValueOnce({ data: { results: [], pagination: { @@ -210,6 +219,8 @@ describe('useRelation', () => { }, }); + const { get } = useFetchClient(); + const { result, waitForNextUpdate } = await setup(); await waitForNextUpdate(); @@ -220,7 +231,7 @@ describe('useRelation', () => { await waitForNextUpdate(); - expect(axiosInstance.get).toBeCalledTimes(1); + expect(get).toBeCalledTimes(1); }); test('does not fetch search by default', async () => { @@ -239,7 +250,7 @@ describe('useRelation', () => { const spy = jest .fn() .mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } }); - axiosInstance.get = spy; + useFetchClient().get = spy; act(() => { result.current.searchFor('something'); @@ -261,7 +272,7 @@ describe('useRelation', () => { const spy = jest .fn() .mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 2 } } }); - axiosInstance.get = spy; + useFetchClient().get = spy; act(() => { result.current.searchFor('something'); @@ -285,7 +296,7 @@ describe('useRelation', () => { const spy = jest .fn() .mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } }); - axiosInstance.get = spy; + useFetchClient().get = spy; act(() => { result.current.searchFor('something'); @@ -322,7 +333,7 @@ describe('useRelation', () => { const spy = jest.fn().mockResolvedValueOnce({ data: { results: [], pagination: { page: 1, pageCount: 1 } }, }); - axiosInstance.get = spy; + useFetchClient().get = spy; act(() => { result.current.searchFor('something'); diff --git a/packages/core/admin/admin/src/content-manager/hooks/useRelation/useRelation.js b/packages/core/admin/admin/src/content-manager/hooks/useRelation/useRelation.js index 55eb0cfe5f..c9450a16c4 100644 --- a/packages/core/admin/admin/src/content-manager/hooks/useRelation/useRelation.js +++ b/packages/core/admin/admin/src/content-manager/hooks/useRelation/useRelation.js @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import { useInfiniteQuery } from 'react-query'; -import { axiosInstance } from '../../../core/utils'; +import { useFetchClient } from '@strapi/helper-plugin'; import { normalizeRelations } from '../../components/RelationInputDataManager/utils'; @@ -10,13 +10,13 @@ import { useCallbackRef } from '../useCallbackRef'; export const useRelation = (cacheKey, { relation, search }) => { const [searchParams, setSearchParams] = useState({}); const [currentPage, setCurrentPage] = useState(0); - + const { get } = useFetchClient(); /** * This runs in `useInfiniteQuery` to actually fetch the data */ const fetchRelations = async ({ pageParam = 1 }) => { try { - const { data } = await axiosInstance.get(relation?.endpoint, { + const { data } = await get(relation?.endpoint, { params: { ...(relation.pageParams ?? {}), page: pageParam, @@ -33,7 +33,7 @@ export const useRelation = (cacheKey, { relation, search }) => { const fetchSearch = async ({ pageParam = 1 }) => { try { - const { data } = await axiosInstance.get(search.endpoint, { + const { data } = await get(search.endpoint, { params: { ...(search.pageParams ?? {}), ...searchParams, diff --git a/packages/core/admin/admin/src/content-manager/pages/App/useModels.js b/packages/core/admin/admin/src/content-manager/pages/App/useModels.js index 382114690a..4b447eddcc 100644 --- a/packages/core/admin/admin/src/content-manager/pages/App/useModels.js +++ b/packages/core/admin/admin/src/content-manager/pages/App/useModels.js @@ -1,10 +1,14 @@ -import { useNotification, useRBACProvider, useStrapiApp } from '@strapi/helper-plugin'; +import { + useNotification, + useRBACProvider, + useStrapiApp, + useFetchClient, +} from '@strapi/helper-plugin'; import { useEffect, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useNotifyAT } from '@strapi/design-system/LiveRegions'; import axios from 'axios'; import { useIntl } from 'react-intl'; -import { axiosInstance } from '../../../core/utils'; import { MUTATE_COLLECTION_TYPES_LINKS, MUTATE_SINGLE_TYPES_LINKS } from '../../../exposedHooks'; import { getRequestUrl, getTrad } from '../../utils'; import { getData, resetProps, setContentTypeLinks } from './actions'; @@ -22,6 +26,7 @@ const useModels = () => { const source = CancelToken.source(); const { notifyStatus } = useNotifyAT(); const { formatMessage } = useIntl(); + const { get } = useFetchClient(); const fetchData = async () => { dispatch(getData()); @@ -36,7 +41,7 @@ const useModels = () => { }, ] = await Promise.all( ['components', 'content-types'].map((endPoint) => - axiosInstance.get(getRequestUrl(endPoint), { cancelToken: source.token }) + get(getRequestUrl(endPoint), { cancelToken: source.token }) ) ); diff --git a/packages/core/admin/admin/src/content-manager/pages/App/utils/getContentTypeLinks.js b/packages/core/admin/admin/src/content-manager/pages/App/utils/getContentTypeLinks.js index 1e7e7c7823..759913657f 100644 --- a/packages/core/admin/admin/src/content-manager/pages/App/utils/getContentTypeLinks.js +++ b/packages/core/admin/admin/src/content-manager/pages/App/utils/getContentTypeLinks.js @@ -1,13 +1,14 @@ -import { axiosInstance } from '../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; import generateModelsLinks from './generateModelsLinks'; import checkPermissions from './checkPermissions'; import { getRequestUrl } from '../../../utils'; const getContentTypeLinks = async (models, userPermissions, toggleNotification) => { + const { get } = getFetchClient(); try { const { data: { data: contentTypeConfigurations }, - } = await axiosInstance.get(getRequestUrl('content-types-settings')); + } = await get(getRequestUrl('content-types-settings')); const { collectionTypesSectionLinks, singleTypesSectionLinks } = generateModelsLinks( models, diff --git a/packages/core/admin/admin/src/content-manager/pages/ComponentSetttingsView/index.js b/packages/core/admin/admin/src/content-manager/pages/ComponentSetttingsView/index.js index 8480c5f2db..0832bea36d 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ComponentSetttingsView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ComponentSetttingsView/index.js @@ -1,9 +1,8 @@ import React, { memo, useEffect, useMemo, useReducer } from 'react'; import { useParams } from 'react-router-dom'; -import { CheckPagePermissions, LoadingIndicatorPage } from '@strapi/helper-plugin'; +import { CheckPagePermissions, LoadingIndicatorPage, useFetchClient } from '@strapi/helper-plugin'; import { useSelector, shallowEqual } from 'react-redux'; import axios from 'axios'; -import { axiosInstance } from '../../../core/utils'; import { getRequestUrl, mergeMetasWithSchema } from '../../utils'; import { makeSelectModelAndComponentSchemas } from '../App/selectors'; import permissions from '../../../permissions'; @@ -18,6 +17,7 @@ const ComponentSettingsView = () => { const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []); const { schemas } = useSelector((state) => schemasSelector(state), shallowEqual); const { uid } = useParams(); + const { get } = useFetchClient(); useEffect(() => { const CancelToken = axios.CancelToken; @@ -29,7 +29,7 @@ const ComponentSettingsView = () => { const { data: { data }, - } = await axiosInstance.get(getRequestUrl(`components/${uid}/configuration`), { + } = await get(getRequestUrl(`components/${uid}/configuration`), { cancelToken: source.token, }); @@ -48,7 +48,7 @@ const ComponentSettingsView = () => { return () => { source.cancel('Operation canceled by the user.'); }; - }, [uid, schemas]); + }, [uid, schemas, get]); if (isLoading) { return ; diff --git a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/utils/api.js b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/utils/api.js index f99179eff2..a91cbd6ecf 100644 --- a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/utils/api.js +++ b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/utils/api.js @@ -1,8 +1,10 @@ -import { axiosInstance } from '../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; import { getRequestUrl } from '../../../utils'; const putCMSettingsEV = (body, slug, isContentTypeView) => { - return axiosInstance.put( + const { put } = getFetchClient(); + + return put( getRequestUrl( isContentTypeView ? `content-types/${slug}/configuration` : `components/${slug}/configuration` ), diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/utils/api.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/utils/api.js index 837871421f..8b6575703f 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/utils/api.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/utils/api.js @@ -1,8 +1,10 @@ -import { axiosInstance } from '../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; import { getRequestUrl } from '../../../utils'; const putCMSettingsLV = (body, slug) => { - return axiosInstance.put(getRequestUrl(`content-types/${slug}/configuration`), body); + const { put } = getFetchClient(); + + return put(getRequestUrl(`content-types/${slug}/configuration`), body); }; export default putCMSettingsLV; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/index.js b/packages/core/admin/admin/src/content-manager/pages/ListView/index.js index 354f1cfb20..8a3925b770 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/index.js @@ -14,6 +14,7 @@ import { NoPermissions, CheckPermissions, SearchURLQuery, + useFetchClient, useFocusWhenNavigate, useQueryParams, useNotification, @@ -33,8 +34,6 @@ import ArrowLeft from '@strapi/icons/ArrowLeft'; import Plus from '@strapi/icons/Plus'; import Cog from '@strapi/icons/Cog'; -import { axiosInstance } from '../../../core/utils'; - import DynamicTable from '../../components/DynamicTable'; import AttributeFilter from '../../components/AttributeFilter'; import { InjectionZone } from '../../../shared/components'; @@ -97,6 +96,8 @@ function ListView({ const { formatMessage } = useIntl(); const contentType = layout.contentType; const hasDraftAndPublish = get(contentType, 'options.draftAndPublish', false); + const fetchClient = useFetchClient(); + const { post, del } = fetchClient; // FIXME // Using a ref to avoid requests being fired multiple times on slug on change @@ -112,7 +113,7 @@ function ListView({ const { data: { results, pagination: paginationResult }, - } = await axiosInstance.get(endPoint, opts); + } = await fetchClient.get(endPoint, opts); notifyStatus( formatMessage( @@ -154,13 +155,13 @@ function ListView({ }); } }, - [formatMessage, getData, getDataSucceeded, notifyStatus, push, toggleNotification] + [formatMessage, getData, getDataSucceeded, notifyStatus, push, toggleNotification, fetchClient] ); const handleConfirmDeleteAllData = useCallback( async (ids) => { try { - await axiosInstance.post(getRequestUrl(`collection-types/${slug}/actions/bulkDelete`), { + await post(getRequestUrl(`collection-types/${slug}/actions/bulkDelete`), { ids, }); @@ -174,13 +175,13 @@ function ListView({ }); } }, - [fetchData, params, slug, toggleNotification] + [fetchData, params, slug, toggleNotification, post] ); const handleConfirmDeleteData = useCallback( async (idToDelete) => { try { - await axiosInstance.delete(getRequestUrl(`collection-types/${slug}/${idToDelete}`)); + await del(getRequestUrl(`collection-types/${slug}/${idToDelete}`)); const requestUrl = getRequestUrl(`collection-types/${slug}${params}`); fetchData(requestUrl); @@ -202,7 +203,7 @@ function ListView({ }); } }, - [slug, params, fetchData, toggleNotification, formatMessage] + [slug, params, fetchData, toggleNotification, formatMessage, del] ); useEffect(() => { @@ -221,7 +222,6 @@ function ListView({ source.cancel('Operation canceled by the user.'); }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [canRead, getData, slug, params, getDataSucceeded, fetchData]); const defaultHeaderLayoutTitle = formatMessage({ diff --git a/packages/core/admin/admin/src/core/utils/axiosInstance.js b/packages/core/admin/admin/src/core/utils/axiosInstance.js deleted file mode 100644 index bd4c3b5e7c..0000000000 --- a/packages/core/admin/admin/src/core/utils/axiosInstance.js +++ /dev/null @@ -1,40 +0,0 @@ -import axios from 'axios'; -import { auth, wrapAxiosInstance } from '@strapi/helper-plugin'; - -const instance = axios.create({ - baseURL: process.env.STRAPI_ADMIN_BACKEND_URL, - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, -}); - -instance.interceptors.request.use( - async (config) => { - config.headers = { - Authorization: `Bearer ${auth.getToken()}`, - }; - - return config; - }, - (error) => { - Promise.reject(error); - } -); - -instance.interceptors.response.use( - (response) => response, - (error) => { - // whatever you want to do with the error - if (error?.response?.status === 401) { - auth.clearAppStorage(); - window.location.reload(); - } - - throw error; - } -); - -const wrapper = wrapAxiosInstance(instance); - -export default wrapper; diff --git a/packages/core/admin/admin/src/core/utils/index.js b/packages/core/admin/admin/src/core/utils/index.js index c5803f9a35..82e9d22c12 100644 --- a/packages/core/admin/admin/src/core/utils/index.js +++ b/packages/core/admin/admin/src/core/utils/index.js @@ -1,3 +1,2 @@ -export { default as axiosInstance } from './axiosInstance'; export { default as basename } from './basename'; export { default as createHook } from './createHook'; diff --git a/packages/core/admin/admin/src/hooks/useFetchEnabledPlugins/utils/api.js b/packages/core/admin/admin/src/hooks/useFetchEnabledPlugins/utils/api.js index dfd1580ff1..2200e50282 100644 --- a/packages/core/admin/admin/src/hooks/useFetchEnabledPlugins/utils/api.js +++ b/packages/core/admin/admin/src/hooks/useFetchEnabledPlugins/utils/api.js @@ -1,7 +1,8 @@ -import { axiosInstance } from '../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; const fetchEnabledPlugins = async () => { - const { data } = await axiosInstance.get('/admin/plugins'); + const { get } = getFetchClient(); + const { data } = await get('/admin/plugins'); return data; }; diff --git a/packages/core/admin/admin/src/hooks/useRegenerate/index.js b/packages/core/admin/admin/src/hooks/useRegenerate/index.js index bec18b673f..0c164519b9 100644 --- a/packages/core/admin/admin/src/hooks/useRegenerate/index.js +++ b/packages/core/admin/admin/src/hooks/useRegenerate/index.js @@ -1,11 +1,11 @@ import { useState } from 'react'; import { get } from 'lodash'; -import { useNotification } from '@strapi/helper-plugin'; -import { axiosInstance } from '../../core/utils'; +import { useFetchClient, useNotification } from '@strapi/helper-plugin'; const useRegenerate = (id, onRegenerate) => { const [isLoadingConfirmation, setIsLoadingConfirmation] = useState(false); const toggleNotification = useNotification(); + const { post } = useFetchClient(); const regenerateData = async () => { try { @@ -13,7 +13,7 @@ const useRegenerate = (id, onRegenerate) => { data: { data: { accessKey }, }, - } = await axiosInstance.post(`/admin/api-tokens/${id}/regenerate`); + } = await post(`/admin/api-tokens/${id}/regenerate`); setIsLoadingConfirmation(false); onRegenerate(accessKey); } catch (error) { diff --git a/packages/core/admin/admin/src/hooks/useRegenerate/tests/index.test.js b/packages/core/admin/admin/src/hooks/useRegenerate/tests/index.test.js index f11b63cd68..e0a67e565a 100644 --- a/packages/core/admin/admin/src/hooks/useRegenerate/tests/index.test.js +++ b/packages/core/admin/admin/src/hooks/useRegenerate/tests/index.test.js @@ -1,18 +1,18 @@ import { renderHook } from '@testing-library/react-hooks'; -import { axiosInstance } from '../../../core/utils'; import useRegenerate from '../index'; -jest.spyOn(axiosInstance, 'get').mockResolvedValue({ - data: { - data: { - accessKey: 'this is my new access key', - }, - }, -}); - jest.mock('@strapi/helper-plugin', () => ({ ...jest.requireActual('@strapi/helper-plugin'), useNotification: jest.fn(), + useFetchClient: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ + data: { + data: { + accessKey: 'this is my new access key', + }, + }, + }), + }), })); describe('useRegenerate', () => { diff --git a/packages/core/admin/admin/src/hooks/useRolesList/index.js b/packages/core/admin/admin/src/hooks/useRolesList/index.js index a96cc913fd..f35c1692de 100644 --- a/packages/core/admin/admin/src/hooks/useRolesList/index.js +++ b/packages/core/admin/admin/src/hooks/useRolesList/index.js @@ -1,7 +1,6 @@ import { useEffect, useReducer, useCallback } from 'react'; -import { useNotification } from '@strapi/helper-plugin'; +import { getFetchClient, useNotification } from '@strapi/helper-plugin'; import get from 'lodash/get'; -import { axiosInstance } from '../../core/utils'; import init from './init'; import reducer, { initialState } from './reducer'; @@ -19,6 +18,9 @@ const useRolesList = (shouldFetchData = true) => { }, [shouldFetchData]); const fetchRolesList = useCallback(async () => { + // TODO: evaluate to replace it with a useFetchClient when we work on the useCallback to remove + const fetchClient = getFetchClient(); + try { dispatch({ type: 'GET_DATA', @@ -26,7 +28,7 @@ const useRolesList = (shouldFetchData = true) => { const { data: { data }, - } = await axiosInstance.get('/admin/roles'); + } = await fetchClient.get('/admin/roles'); dispatch({ type: 'GET_DATA_SUCCEEDED', diff --git a/packages/core/admin/admin/src/index.js b/packages/core/admin/admin/src/index.js index dadfafe3a1..98f1a4e4da 100644 --- a/packages/core/admin/admin/src/index.js +++ b/packages/core/admin/admin/src/index.js @@ -1,7 +1,7 @@ import ReactDOM from 'react-dom'; -import appCustomisations from './app'; +import { getFetchClient } from '@strapi/helper-plugin'; import { Components, Fields, Middlewares, Reducers } from './core/apis'; -import { axiosInstance } from './core/utils'; +import appCustomisations from './app'; // eslint-disable-next-line import/extensions import plugins from './plugins'; import appReducers from './reducers'; @@ -29,12 +29,13 @@ const reducers = Reducers({ appReducers }); const MOUNT_NODE = document.getElementById('app'); const run = async () => { + const { get } = getFetchClient(); try { const { data: { data: { isEE, features }, }, - } = await axiosInstance.get('/admin/project-type'); + } = await get('/admin/project-type'); window.strapi.isEE = isEE; window.strapi.features = { diff --git a/packages/core/admin/admin/src/pages/Admin/tests/useTrackUsage.test.js b/packages/core/admin/admin/src/pages/Admin/tests/useTrackUsage.test.js index 0b64a2ac65..52bbda94f6 100644 --- a/packages/core/admin/admin/src/pages/Admin/tests/useTrackUsage.test.js +++ b/packages/core/admin/admin/src/pages/Admin/tests/useTrackUsage.test.js @@ -11,7 +11,6 @@ jest.mock('react-redux', () => ({ jest.mock('@strapi/helper-plugin', () => ({ useTracking: jest.fn(() => ({ trackUsage: trackUsageMock })), - wrapAxiosInstance: jest.fn(() => {}), })); describe('Admin | pages | AdminĀ | useTrackUsage', () => { diff --git a/packages/core/admin/admin/src/pages/ProfilePage/utils/api.js b/packages/core/admin/admin/src/pages/ProfilePage/utils/api.js index 7d7475eef7..855eeeb70b 100644 --- a/packages/core/admin/admin/src/pages/ProfilePage/utils/api.js +++ b/packages/core/admin/admin/src/pages/ProfilePage/utils/api.js @@ -1,15 +1,17 @@ import omit from 'lodash/omit'; -import { axiosInstance } from '../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; const fetchUser = async () => { - const { data } = await axiosInstance.get('/admin/users/me'); + const { get } = getFetchClient(); + const { data } = await get('/admin/users/me'); return data.data; }; const putUser = async (body) => { const dataToSend = omit(body, ['confirmPassword', 'currentTheme']); - const { data } = await axiosInstance.put('/admin/users/me', dataToSend); + const { put } = getFetchClient(); + const { data } = await put('/admin/users/me', dataToSend); return { ...data.data, currentTheme: body.currentTheme }; }; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js index a128aaa280..d7647a8a53 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js @@ -9,13 +9,13 @@ import { useTracking, useGuidedTour, useRBAC, + useFetchClient, } from '@strapi/helper-plugin'; import { Main } from '@strapi/design-system/Main'; import { Formik } from 'formik'; import { useRouteMatch, useHistory } from 'react-router-dom'; import { useQuery } from 'react-query'; import { formatAPIErrors } from '../../../../../utils'; -import { axiosInstance } from '../../../../../core/utils'; import { schema } from './utils'; import LoadingView from './components/LoadingView'; import FormHead from './components/FormHead'; @@ -50,6 +50,7 @@ const ApiTokenCreateView = () => { const { params: { id }, } = useRouteMatch('/settings/api-tokens/:id'); + const { get, post, put } = useFetchClient(); const isCreating = id === 'create'; @@ -58,7 +59,7 @@ const ApiTokenCreateView = () => { async () => { const [permissions, routes] = await Promise.all( ['/admin/content-api/permissions', '/admin/content-api/routes'].map(async (url) => { - const { data } = await axiosInstance.get(url); + const { data } = await get(url); return data.data; }) @@ -112,7 +113,7 @@ const ApiTokenCreateView = () => { async () => { const { data: { data }, - } = await axiosInstance.get(`/admin/api-tokens/${id}`); + } = await get(`/admin/api-tokens/${id}`); setApiToken({ ...data, @@ -160,12 +161,12 @@ const ApiTokenCreateView = () => { const { data: { data: response }, } = isCreating - ? await axiosInstance.post(`/admin/api-tokens`, { + ? await post(`/admin/api-tokens`, { ...body, lifespan: lifespanVal, permissions: body.type === 'custom' ? state.selectedActions : null, }) - : await axiosInstance.put(`/admin/api-tokens/${id}`, { + : await put(`/admin/api-tokens/${id}`, { name: body.name, description: body.description, type: body.type, diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/tests/index.test.js index 5b971356c9..1c008767d4 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/tests/index.test.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/tests/index.test.js @@ -5,7 +5,6 @@ import { Router, Route } from 'react-router-dom'; import { createMemoryHistory } from 'history'; import { QueryClient, QueryClientProvider } from 'react-query'; import { lightTheme, darkTheme } from '@strapi/design-system'; -import { axiosInstance } from '../../../../../../core/utils'; import Theme from '../../../../../../components/Theme'; import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider'; import EditView from '../index'; @@ -32,26 +31,28 @@ jest.mock('@strapi/helper-plugin', () => ({ lockApp: jest.fn(), unlockApp: jest.fn(), })), + useFetchClient: jest.fn().mockReturnValue({ + get: jest.fn().mockImplementation((path) => { + if (path === '/admin/content-api/permissions') { + return { data }; + } + + return { + data: { + data: { + id: '1', + name: 'My super token', + description: 'This describe my super token', + type: 'read-only', + createdAt: '2021-11-15T00:00:00.000Z', + permissions: [], + }, + }, + }; + }), + }), })); -jest.spyOn(axiosInstance, 'get').mockImplementation((path) => { - if (path === '/admin/content-api/permissions') { - return { data }; - } - - return { - data: { - data: { - id: '1', - name: 'My super token', - description: 'This describe my super token', - type: 'read-only', - createdAt: '2021-11-15T00:00:00.000Z', - permissions: [], - }, - }, - }; -}); jest.spyOn(Date, 'now').mockImplementation(() => new Date('2015-10-01T08:00:00.000Z')); const client = new QueryClient({ diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js index d668a28851..d848b8643a 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js @@ -14,6 +14,7 @@ import { DynamicTable, useTracking, useGuidedTour, + useFetchClient, LinkButton, } from '@strapi/helper-plugin'; import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout'; @@ -21,7 +22,6 @@ import { Main } from '@strapi/design-system/Main'; import { Button } from '@strapi/design-system/Button'; import Plus from '@strapi/icons/Plus'; -import { axiosInstance } from '../../../../../core/utils'; import adminPermissions from '../../../../../permissions'; import tableHeaders from './utils/tableHeaders'; import TableRows from './DynamicTable'; @@ -38,6 +38,7 @@ const ApiTokenListView = () => { const { trackUsage } = useTracking(); const { startSection } = useGuidedTour(); const startSectionRef = useRef(startSection); + const { get, del } = useFetchClient(); useEffect(() => { if (startSectionRef.current) { @@ -67,7 +68,7 @@ const ApiTokenListView = () => { trackUsage('willAccessTokenList'); const { data: { data }, - } = await axiosInstance.get(`/admin/api-tokens`); + } = await get(`/admin/api-tokens`); trackUsage('didAccessTokenList', { number: data.length }); @@ -90,7 +91,7 @@ const ApiTokenListView = () => { const deleteMutation = useMutation( async (id) => { - await axiosInstance.delete(`/admin/api-tokens/${id}`); + await del(`/admin/api-tokens/${id}`); }, { async onSuccess() { diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js index 1af0703476..ffec060ca5 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/tests/index.test.js @@ -6,7 +6,6 @@ import { createMemoryHistory } from 'history'; import { useRBAC, TrackingProvider } from '@strapi/helper-plugin'; import { QueryClient, QueryClientProvider } from 'react-query'; import { lightTheme, darkTheme } from '@strapi/design-system'; -import { axiosInstance } from '../../../../../../core/utils'; import Theme from '../../../../../../components/Theme'; import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider'; import ListView from '../index'; @@ -19,21 +18,22 @@ jest.mock('@strapi/helper-plugin', () => ({ useGuidedTour: jest.fn(() => ({ startSection: jest.fn(), })), -})); - -jest.spyOn(axiosInstance, 'get').mockResolvedValue({ - data: { - data: [ - { - id: 1, - name: 'My super token', - description: 'This describe my super token', - type: 'read-only', - createdAt: '2021-11-15T00:00:00.000Z', + useFetchClient: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ + data: { + data: [ + { + id: 1, + name: 'My super token', + description: 'This describe my super token', + type: 'read-only', + createdAt: '2021-11-15T00:00:00.000Z', + }, + ], }, - ], - }, -}); + }), + }), +})); jest.spyOn(Date, 'now').mockImplementation(() => new Date('2015-10-01T08:00:00.000Z')); diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/tests/index.test.js index 1a556cd65b..1aef9b884a 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/tests/index.test.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/tests/index.test.js @@ -5,7 +5,6 @@ import { IntlProvider } from 'react-intl'; import { ThemeProvider, lightTheme } from '@strapi/design-system'; import { useAppInfos, useRBAC, TrackingProvider } from '@strapi/helper-plugin'; import ApplicationInfosPage from '../index'; -import { axiosInstance } from '../../../../../core/utils'; import server from './server'; const updateProjectSettingsSpy = jest.fn(); @@ -17,6 +16,20 @@ jest.mock('@strapi/helper-plugin', () => ({ useAppInfos: jest.fn(() => ({ shouldUpdateStrapi: false, latestStrapiReleaseTag: 'v3.6.8' })), useNotification: jest.fn(() => jest.fn()), useRBAC: jest.fn(() => ({ allowedActions: { canRead: true, canUpdate: true } })), + useFetchClient: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ + data: { + menuLogo: { + ext: 'png', + height: 256, + name: 'image.png', + size: 27.4, + url: 'uploads/image_fe95c5abb9.png', + width: 246, + }, + }, + }), + }), })); jest.mock('../../../../../hooks', () => ({ useConfigurations: jest.fn(() => ({ @@ -28,19 +41,6 @@ jest.mock('../../../../../hooks', () => ({ })), })); -jest.spyOn(axiosInstance, 'get').mockResolvedValue({ - data: { - menuLogo: { - ext: 'png', - height: 256, - name: 'image.png', - size: 27.4, - url: 'uploads/image_fe95c5abb9.png', - width: 246, - }, - }, -}); - const client = new QueryClient(); const App = ( diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js index 9ab8c257f7..e6ad1ef604 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js @@ -1,14 +1,16 @@ -import { axiosInstance } from '../../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; import prefixAllUrls from './prefixAllUrls'; const fetchProjectSettings = async () => { - const { data } = await axiosInstance.get('/admin/project-settings'); + const { get } = getFetchClient(); + const { data } = await get('/admin/project-settings'); return prefixAllUrls(data); }; const postProjectSettings = async (body) => { - const { data } = await axiosInstance.post('/admin/project-settings', body, { + const { post } = getFetchClient(); + const { data } = await post('/admin/project-settings', body, { headers: { 'Content-Type': 'multipart/form-data', }, diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/EditPage/utils/api.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/EditPage/utils/api.js index 5561f3df3f..b4c6012458 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/EditPage/utils/api.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/EditPage/utils/api.js @@ -1,13 +1,16 @@ -import { axiosInstance } from '../../../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; const fetchUser = async (id) => { - const { data } = await axiosInstance.get(`/admin/users/${id}`); + const { get } = getFetchClient(); + const { data } = await get(`/admin/users/${id}`); return data.data; }; const putUser = async (id, body) => { - const { data } = await axiosInstance.put(`/admin/users/${id}`, body); + const { put } = getFetchClient(); + + const { data } = await put(`/admin/users/${id}`, body); return data.data; }; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js index 8b33a5e0ea..b51f29d66f 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js @@ -15,12 +15,17 @@ import { Stack } from '@strapi/design-system/Stack'; import { Typography } from '@strapi/design-system/Typography'; import { Formik } from 'formik'; -import { Form, GenericInput, useNotification, useOverlayBlocker } from '@strapi/helper-plugin'; +import { + Form, + GenericInput, + useNotification, + useOverlayBlocker, + useFetchClient, +} from '@strapi/helper-plugin'; import { useQueryClient, useMutation } from 'react-query'; import formDataModel from 'ee_else_ce/pages/SettingsPage/pages/Users/ListPage/ModalForm/utils/formDataModel'; import roleSettingsForm from 'ee_else_ce/pages/SettingsPage/pages/Users/ListPage/ModalForm/utils/roleSettingsForm'; import MagicLink from 'ee_else_ce/pages/SettingsPage/pages/Users/components/MagicLink'; -import { axiosInstance } from '../../../../../../core/utils'; import SelectRoles from '../../components/SelectRoles'; import layout from './utils/layout'; import schema from './utils/schema'; @@ -34,9 +39,10 @@ const ModalForm = ({ queryName, onToggle }) => { const { formatMessage } = useIntl(); const toggleNotification = useNotification(); const { lockApp, unlockApp } = useOverlayBlocker(); + const { post } = useFetchClient(); const postMutation = useMutation( (body) => { - return axiosInstance.post('/admin/users', body); + return post('/admin/users', body); }, { async onSuccess({ data }) { diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/utils/api.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/utils/api.js index ca694e07f2..f3daec306d 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/utils/api.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/ListPage/utils/api.js @@ -1,9 +1,10 @@ -import { axiosInstance } from '../../../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; const fetchData = async (search, notify) => { + const { get } = getFetchClient(); const { data: { data }, - } = await axiosInstance.get(`/admin/users${search}`); + } = await get(`/admin/users${search}`); notify(); @@ -11,7 +12,9 @@ const fetchData = async (search, notify) => { }; const deleteData = async (ids) => { - await axiosInstance.post('/admin/users/batch-delete', { ids }); + const { post } = getFetchClient(); + + await post('/admin/users/batch-delete', { ids }); }; export { deleteData, fetchData }; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/components/SelectRoles/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/components/SelectRoles/index.js index 63b9489929..82f005617a 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/components/SelectRoles/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Users/components/SelectRoles/index.js @@ -5,7 +5,7 @@ import { Select, Option } from '@strapi/design-system/Select'; import { useQuery } from 'react-query'; import styled, { keyframes } from 'styled-components'; import LoadingIcon from '@strapi/icons/Loader'; -import { axiosInstance } from '../../../../../../core/utils'; +import { getFetchClient } from '@strapi/helper-plugin'; const rotation = keyframes` from { @@ -27,7 +27,8 @@ const Loader = () => ( ); const fetchData = async () => { - const { data } = await axiosInstance.get('/admin/roles'); + const { get } = getFetchClient(); + const { data } = await get('/admin/roles'); return data.data; }; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js index 8a2951d1b1..4d07c1f003 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js @@ -11,12 +11,12 @@ import { to, useNotification, useOverlayBlocker, + useFetchClient, } from '@strapi/helper-plugin'; import { Main } from '@strapi/design-system/Main'; import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useHistory, useRouteMatch } from 'react-router-dom'; import { useModels } from '../../../../../hooks'; -import { axiosInstance } from '../../../../../core/utils'; import WebhookForm from './components/WebhookForm'; import cleanData from './utils/formatData'; @@ -30,6 +30,7 @@ const EditView = () => { const toggleNotification = useNotification(); const queryClient = useQueryClient(); const { isLoading: isLoadingForModels, collectionTypes } = useModels(); + const { post } = useFetchClient(); const isCreating = id === 'create'; @@ -64,7 +65,7 @@ const EditView = () => { data: triggerResponse, isIdle: isTriggerIdle, mutate, - } = useMutation(() => axiosInstance.post(`/admin/webhooks/${id}/trigger`)); + } = useMutation(() => post(`/admin/webhooks/${id}/trigger`)); const triggerWebhook = () => mutate(null, { diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js index c80404f07d..55e09bd118 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js @@ -4,6 +4,7 @@ import { LoadingIndicatorPage, SearchURLQuery, SettingsPageTitle, + getFetchClient, useNotification, useQueryParams, useRBAC, @@ -23,7 +24,6 @@ import { get } from 'lodash'; import matchSorter from 'match-sorter'; import { useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import { axiosInstance } from '../../../../../../../admin/src/core/utils'; import { useRolesList } from '../../../../../../../admin/src/hooks'; import adminPermissions from '../../../../../../../admin/src/permissions'; import EmptyRole from '../../../../../../../admin/src/pages/SettingsPage/pages/Roles/ListPage/components/EmptyRole'; @@ -72,13 +72,15 @@ const useRoleActions = ({ getData, canCreate, canDelete, canUpdate }) => { initialState ); + const { post } = getFetchClient(); + const handleDeleteData = async () => { try { dispatch({ type: 'ON_REMOVE_ROLES', }); - await axiosInstance.post('/admin/roles/batch-delete', { + await post('/admin/roles/batch-delete', { ids: [roleToDelete], }); diff --git a/packages/core/helper-plugin/lib/src/hooks/useFetchClient/index.js b/packages/core/helper-plugin/lib/src/hooks/useFetchClient/index.js index d9325301ee..ba01844942 100644 --- a/packages/core/helper-plugin/lib/src/hooks/useFetchClient/index.js +++ b/packages/core/helper-plugin/lib/src/hooks/useFetchClient/index.js @@ -3,7 +3,7 @@ * useFetchClient * */ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useMemo } from 'react'; import getFetchClient from '../../utils/getFetchClient'; const useFetchClient = () => { @@ -19,11 +19,13 @@ const useFetchClient = () => { }; }, []); - const defaultOptions = { - signal: controller.current.signal, - }; - - return getFetchClient(defaultOptions); + return useMemo( + () => + getFetchClient({ + signal: controller.current.signal, + }), + [] + ); }; export default useFetchClient; diff --git a/packages/core/helper-plugin/lib/src/hooks/useFetchClient/tests/useFetchClient.test.js b/packages/core/helper-plugin/lib/src/hooks/useFetchClient/tests/useFetchClient.test.js new file mode 100644 index 0000000000..cafe56ef8c --- /dev/null +++ b/packages/core/helper-plugin/lib/src/hooks/useFetchClient/tests/useFetchClient.test.js @@ -0,0 +1,48 @@ +import React, { useEffect } from 'react'; +import { render } from '@testing-library/react'; +import { useFetchClient } from '@strapi/helper-plugin'; + +jest.mock('@strapi/helper-plugin', () => ({ + ...jest.requireActual('@strapi/helper-plugin'), + useFetchClient: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ + data: { + results: [ + { id: 2, name: 'newest', publishedAt: null }, + { id: 1, name: 'oldest', publishedAt: null }, + ], + pagination: { page: 1, pageCount: 10 }, + }, + }), + post: jest.fn(), + put: jest.fn(), + del: jest.fn(), + }), +})); + +const TestComponent = (props) => { + const { get } = useFetchClient(); + useEffect(() => { + get('/foo'); + }, [get]); + + // eslint-disable-next-line react/prop-types + return
{props.children}
; +}; + +describe('useFetchClient', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + it('Should call once the GET method even when we rerender the Component', async () => { + const { rerender } = render(content); + + const { get } = useFetchClient(); + + expect(get).toHaveBeenCalledTimes(1); + + rerender(); + + expect(get).toHaveBeenCalledTimes(1); + }); +});