Merge branch 'main' into fix/json-input-validation

This commit is contained in:
markkaylor 2023-02-06 09:38:15 +01:00 committed by GitHub
commit 18dec12617
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 396 additions and 283 deletions

View File

@ -1,11 +1,11 @@
--- ---
title: useFetchClient title: useFetchClient
slug: /hooks/use-fetch-client
description: API reference for the useFetchClient hook in Strapi description: API reference for the useFetchClient hook in Strapi
tags: tags:
- hooks - hooks
- axios - axios
- data - data
- helper-plugin
--- ---
## Usage ## Usage

View File

@ -16,6 +16,17 @@ const sidebars = {
// By default, Docusaurus generates a sidebar from the docs folder structure // By default, Docusaurus generates a sidebar from the docs folder structure
docs: [ docs: [
'index', 'index',
{
type: 'category',
label: 'Admin',
items: [
{
type: 'doc',
label: 'Link Strapi Design System',
id: 'core/admin/link-strapi-design-system',
},
],
},
{ {
type: 'category', type: 'category',
label: 'Core', 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', type: 'category',
label: 'Content Type Builder', label: 'Content Type Builder',
@ -86,6 +75,34 @@ const sidebars = {
}, },
items: ['example'], 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',
},
],
},
], ],
}, },
{ {

View File

@ -1,10 +1,11 @@
import axios from 'axios'; import axios from 'axios';
import { getFetchClient } from '@strapi/helper-plugin';
import checkLatestStrapiVersion from './checkLatestStrapiVersion'; import checkLatestStrapiVersion from './checkLatestStrapiVersion';
import { axiosInstance } from '../../../core/utils';
import packageJSON from '../../../../../package.json'; import packageJSON from '../../../../../package.json';
const strapiVersion = packageJSON.version; const strapiVersion = packageJSON.version;
const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF')); const showUpdateNotif = !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF'));
const { get } = getFetchClient();
const fetchStrapiLatestRelease = async (toggleNotification) => { const fetchStrapiLatestRelease = async (toggleNotification) => {
try { try {
@ -38,7 +39,7 @@ const fetchStrapiLatestRelease = async (toggleNotification) => {
const fetchAppInfo = async () => { const fetchAppInfo = async () => {
try { try {
const { data, headers } = await axiosInstance.get('/admin/information'); const { data, headers } = await get('/admin/information');
if (!headers['content-type'].includes('application/json')) { if (!headers['content-type'].includes('application/json')) {
throw new Error('Not found'); throw new Error('Not found');
@ -52,7 +53,7 @@ const fetchAppInfo = async () => {
const fetchCurrentUserPermissions = async () => { const fetchCurrentUserPermissions = async () => {
try { 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')) { if (!headers['content-type'].includes('application/json')) {
throw new Error('Not found'); throw new Error('Not found');
@ -70,7 +71,7 @@ const fetchUserRoles = async () => {
data: { data: {
data: { roles }, data: { roles },
}, },
} = await axiosInstance.get('/admin/users/me'); } = await get('/admin/users/me');
return roles; return roles;
} catch (err) { } catch (err) {

View File

@ -20,9 +20,14 @@ import { Typography } from '@strapi/design-system/Typography';
import { Stack } from '@strapi/design-system/Stack'; import { Stack } from '@strapi/design-system/Stack';
import Write from '@strapi/icons/Write'; import Write from '@strapi/icons/Write';
import Exit from '@strapi/icons/Exit'; 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 { useConfigurations } from '../../hooks';
import { axiosInstance } from '../../core/utils';
const LinkUserWrapper = styled(Box)` const LinkUserWrapper = styled(Box)`
width: ${150 / 16}rem; width: ${150 / 16}rem;
@ -64,6 +69,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
const { trackUsage } = useTracking(); const { trackUsage } = useTracking();
const { pathname } = useLocation(); const { pathname } = useLocation();
const history = useHistory(); const history = useHistory();
const { post } = getFetchClient();
const initials = userDisplayName const initials = userDisplayName
.split(' ') .split(' ')
@ -74,7 +80,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
const handleToggleUserLinks = () => setUserLinksVisible((prev) => !prev); const handleToggleUserLinks = () => setUserLinksVisible((prev) => !prev);
const handleLogout = async () => { const handleLogout = async () => {
await axiosInstance.post('/admin/logout'); await post('/admin/logout');
auth.clearAppStorage(); auth.clearAppStorage();
handleToggleUserLinks(); handleToggleUserLinks();
history.push('/auth/login'); history.push('/auth/login');

View File

@ -10,11 +10,11 @@ import {
formatContentTypeData, formatContentTypeData,
contentManagementUtilRemoveFieldsFromData, contentManagementUtilRemoveFieldsFromData,
useGuidedTour, useGuidedTour,
useFetchClient,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
import { axiosInstance } from '../../../core/utils';
import { import {
createDefaultForm, createDefaultForm,
getTrad, getTrad,
@ -51,6 +51,9 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
const allLayoutDataRef = useRef(allLayoutData); const allLayoutDataRef = useRef(allLayoutData);
const fetchClient = useFetchClient();
const { put, post, del } = fetchClient;
const isCreatingEntry = id === null; const isCreatingEntry = id === null;
const requestURL = useMemo(() => { const requestURL = useMemo(() => {
@ -137,7 +140,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
dispatch(getData()); dispatch(getData());
try { try {
const { data } = await axiosInstance.get(requestURL, { cancelToken: source.token }); const { data } = await fetchClient.get(requestURL, { cancelToken: source.token });
dispatch(getDataSucceeded(cleanReceivedData(cleanClonedData(data)))); dispatch(getDataSucceeded(cleanReceivedData(cleanClonedData(data))));
} catch (err) { } catch (err) {
@ -187,6 +190,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
source.cancel('Operation canceled by the user.'); source.cancel('Operation canceled by the user.');
}; };
}, [ }, [
fetchClient,
cleanClonedData, cleanClonedData,
cleanReceivedData, cleanReceivedData,
push, push,
@ -216,12 +220,12 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
const onDelete = useCallback( const onDelete = useCallback(
async (trackerProperty) => { async (trackerProperty) => {
console.log('onDelete');
try { try {
trackUsageRef.current('willDeleteEntry', trackerProperty); trackUsageRef.current('willDeleteEntry', trackerProperty);
const { data } = await axiosInstance.delete( const { data } = await del(getRequestUrl(`collection-types/${slug}/${id}`));
getRequestUrl(`collection-types/${slug}/${id}`)
);
toggleNotification({ toggleNotification({
type: 'success', type: 'success',
@ -237,7 +241,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
return Promise.reject(err); return Promise.reject(err);
} }
}, },
[id, slug, toggleNotification] [id, slug, toggleNotification, del]
); );
const onDeleteSucceeded = useCallback(() => { const onDeleteSucceeded = useCallback(() => {
@ -247,12 +251,11 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
const onPost = useCallback( const onPost = useCallback(
async (body, trackerProperty) => { async (body, trackerProperty) => {
const endPoint = `${getRequestUrl(`collection-types/${slug}`)}${rawQuery}`; const endPoint = `${getRequestUrl(`collection-types/${slug}`)}${rawQuery}`;
try { try {
// Show a loading button in the EditView/Header.js && lock the app => no navigation // Show a loading button in the EditView/Header.js && lock the app => no navigation
dispatch(setStatus('submit-pending')); dispatch(setStatus('submit-pending'));
const { data } = await axiosInstance.post(endPoint, body); const { data } = await post(endPoint, body);
trackUsageRef.current('didCreateEntry', trackerProperty); trackUsageRef.current('didCreateEntry', trackerProperty);
toggleNotification({ toggleNotification({
@ -291,6 +294,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
toggleNotification, toggleNotification,
setCurrentStep, setCurrentStep,
queryClient, queryClient,
post,
] ]
); );
@ -303,7 +307,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
); );
dispatch(setStatus('draft-relation-check-pending')); dispatch(setStatus('draft-relation-check-pending'));
const numberOfDraftRelations = await axiosInstance.get(endPoint); const numberOfDraftRelations = await fetchClient.get(endPoint);
trackUsageRef.current('didCheckDraftRelations'); trackUsageRef.current('didCheckDraftRelations');
dispatch(setStatus('resolved')); dispatch(setStatus('resolved'));
@ -315,7 +319,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
return Promise.reject(err); return Promise.reject(err);
} }
}, [displayErrors, id, slug, dispatch]); }, [displayErrors, id, slug, dispatch, fetchClient]);
const onPublish = useCallback(async () => { const onPublish = useCallback(async () => {
try { try {
@ -324,7 +328,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
dispatch(setStatus('publish-pending')); dispatch(setStatus('publish-pending'));
const { data } = await axiosInstance.post(endPoint); const { data } = await post(endPoint);
trackUsageRef.current('didPublishEntry'); trackUsageRef.current('didPublishEntry');
@ -343,7 +347,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
return Promise.reject(err); return Promise.reject(err);
} }
}, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification]); }, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification, post]);
const onPut = useCallback( const onPut = useCallback(
async (body, trackerProperty) => { async (body, trackerProperty) => {
@ -354,7 +358,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
dispatch(setStatus('submit-pending')); dispatch(setStatus('submit-pending'));
const { data } = await axiosInstance.put(endPoint, body); const { data } = await put(endPoint, body);
trackUsageRef.current('didEditEntry', { trackerProperty }); trackUsageRef.current('didEditEntry', { trackerProperty });
toggleNotification({ toggleNotification({
@ -379,7 +383,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
return Promise.reject(err); return Promise.reject(err);
} }
}, },
[cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification, queryClient] [cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification, queryClient, put]
); );
const onUnpublish = useCallback(async () => { const onUnpublish = useCallback(async () => {
@ -390,7 +394,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
try { try {
trackUsageRef.current('willUnpublishEntry'); trackUsageRef.current('willUnpublishEntry');
const { data } = await axiosInstance.post(endPoint); const { data } = await post(endPoint);
trackUsageRef.current('didUnpublishEntry'); trackUsageRef.current('didUnpublishEntry');
toggleNotification({ toggleNotification({
@ -408,7 +412,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
return Promise.reject(err); return Promise.reject(err);
} }
}, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification]); }, [cleanReceivedData, displayErrors, id, slug, dispatch, toggleNotification, post]);
return children({ return children({
componentsDataStructure, componentsDataStructure,

View File

@ -10,19 +10,18 @@ import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
import { Loader } from '@strapi/design-system/Loader'; import { Loader } from '@strapi/design-system/Loader';
import styled from 'styled-components'; import styled from 'styled-components';
import { useNotifyAT } from '@strapi/design-system/LiveRegions'; 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 CellValue from '../CellValue';
import { axiosInstance } from '../../../../../core/utils';
import { getRequestUrl, getTrad } from '../../../../utils'; import { getRequestUrl, getTrad } from '../../../../utils';
const TypographyMaxWidth = styled(Typography)` const TypographyMaxWidth = styled(Typography)`
max-width: 500px; max-width: 500px;
`; `;
const fetchRelation = async (endPoint, notifyStatus) => { const fetchRelation = async (endPoint, notifyStatus, get) => {
const { const {
data: { results, pagination }, data: { results, pagination },
} = await axiosInstance.get(endPoint); } = await get(endPoint);
notifyStatus(); notifyStatus();
@ -37,6 +36,7 @@ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, conte
[entityId, name, contentType] [entityId, name, contentType]
); );
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const { get } = useFetchClient();
const Label = ( const Label = (
<Flex gap={1} wrap="nowrap"> <Flex gap={1} wrap="nowrap">
@ -61,7 +61,7 @@ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, conte
const { data, status } = useQuery( const { data, status } = useQuery(
[fieldSchema.targetModel, entityId], [fieldSchema.targetModel, entityId],
() => fetchRelation(relationFetchEndpoint, notify), () => fetchRelation(relationFetchEndpoint, notify, get),
{ {
enabled: isOpen, enabled: isOpen,
staleTime: 0, staleTime: 0,

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // 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 { .c13 {
border: 0; border: 0;
-webkit-clip: rect(0 0 0 0); -webkit-clip: rect(0 0 0 0);

View File

@ -4,10 +4,13 @@ import { ThemeProvider, lightTheme } from '@strapi/design-system';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import { QueryClientProvider, QueryClient } from 'react-query'; import { QueryClientProvider, QueryClient } from 'react-query';
import { axiosInstance } from '../../../../../../core/utils'; import { useFetchClient } from '@strapi/helper-plugin';
import RelationMultiple from '../index'; import RelationMultiple from '../index';
jest.spyOn(axiosInstance, 'get').mockResolvedValue({ jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useFetchClient: jest.fn().mockReturnValue({
get: jest.fn().mockResolvedValue({
data: { data: {
results: [ results: [
{ {
@ -20,7 +23,9 @@ jest.spyOn(axiosInstance, 'get').mockResolvedValue({
total: 1, total: 1,
}, },
}, },
}); }),
}),
}));
const DEFAULT_PROPS_FIXTURE = { const DEFAULT_PROPS_FIXTURE = {
contentType: { contentType: {
@ -61,21 +66,23 @@ const ComponentFixture = () => {
); );
}; };
describe('DynamicTabe / Cellcontent / RelationMultiple', () => { describe('DynamicTable / Cellcontent / RelationMultiple', () => {
it('renders and matches the snapshot', async () => { it('renders and matches the snapshot', async () => {
const { container } = render(<ComponentFixture />); const { container } = render(<ComponentFixture />);
expect(container).toMatchSnapshot(); 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 () => { it('fetches relation entities once the menu is opened', async () => {
const { container } = render(<ComponentFixture />); const { container } = render(<ComponentFixture />);
const { get } = useFetchClient();
const button = container.querySelector('[type=button]'); const button = container.querySelector('[type=button]');
fireEvent(button, new MouseEvent('mousedown', { bubbles: true })); fireEvent(button, new MouseEvent('mousedown', { bubbles: true }));
expect(screen.getByText('Relations are loading')).toBeInTheDocument(); 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()); await waitFor(() => expect(screen.getByText('Relation entity 1')).toBeInTheDocument());
}); });
}); });

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useCMEditViewDataManager } from '@strapi/helper-plugin'; import { useCMEditViewDataManager, useFetchClient } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import get from 'lodash/get'; import get from 'lodash/get';
import { TextInput } from '@strapi/design-system/TextInput'; import { TextInput } from '@strapi/design-system/TextInput';
@ -9,7 +9,6 @@ import Refresh from '@strapi/icons/Refresh';
import CheckCircle from '@strapi/icons/CheckCircle'; import CheckCircle from '@strapi/icons/CheckCircle';
import ExclamationMarkCircle from '@strapi/icons/ExclamationMarkCircle'; import ExclamationMarkCircle from '@strapi/icons/ExclamationMarkCircle';
import Loader from '@strapi/icons/Loader'; import Loader from '@strapi/icons/Loader';
import { axiosInstance } from '../../../core/utils';
import { getRequestUrl } from '../../utils'; import { getRequestUrl } from '../../utils';
import useDebounce from './useDebounce'; import useDebounce from './useDebounce';
import UID_REGEX from './regex'; import UID_REGEX from './regex';
@ -46,6 +45,7 @@ const InputUID = ({
const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300); const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300);
const [isCustomized, setIsCustomized] = useState(false); const [isCustomized, setIsCustomized] = useState(false);
const [regenerateLabel, setRegenerateLabel] = useState(null); const [regenerateLabel, setRegenerateLabel] = useState(null);
const { post } = useFetchClient();
const label = intlLabel.id const label = intlLabel.id
? formatMessage( ? formatMessage(
@ -67,7 +67,7 @@ const InputUID = ({
try { try {
const { const {
data: { data }, data: { data },
} = await axiosInstance.post(requestURL, { } = await post(requestURL, {
contentTypeUID, contentTypeUID,
field: name, field: name,
data: modifiedData, data: modifiedData,
@ -89,7 +89,7 @@ const InputUID = ({
} }
try { try {
const { data } = await axiosInstance.post(requestURL, { const { data } = await post(requestURL, {
contentTypeUID, contentTypeUID,
field: name, field: name,
value: value ? value.trim() : '', value: value ? value.trim() : '',

View File

@ -8,11 +8,11 @@ import {
useQueryParams, useQueryParams,
useNotification, useNotification,
useGuidedTour, useGuidedTour,
useFetchClient,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import axios from 'axios'; import axios from 'axios';
import { axiosInstance } from '../../../core/utils';
import { createDefaultForm, getTrad, removePasswordFieldsFromData } from '../../utils'; import { createDefaultForm, getTrad, removePasswordFieldsFromData } from '../../utils';
import { import {
getData, getData,
@ -39,6 +39,8 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
const searchToSend = buildQueryString(query); const searchToSend = buildQueryString(query);
const toggleNotification = useNotification(); const toggleNotification = useNotification();
const dispatch = useDispatch(); const dispatch = useDispatch();
const fetchClient = useFetchClient();
const { post, put, del } = fetchClient;
const { componentsDataStructure, contentTypeDataStructure, data, isLoading, status } = const { componentsDataStructure, contentTypeDataStructure, data, isLoading, status } =
useSelector(selectCrudReducer); useSelector(selectCrudReducer);
@ -103,7 +105,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
setIsCreatingEntry(true); setIsCreatingEntry(true);
try { try {
const { data } = await axiosInstance.get(getRequestUrl(`${slug}${searchToSend}`), { const { data } = await fetchClient.get(getRequestUrl(`${slug}${searchToSend}`), {
cancelToken: source.token, cancelToken: source.token,
}); });
@ -136,7 +138,16 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
fetchData(source); fetchData(source);
return () => source.cancel('Operation canceled by the user.'); 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( const displayErrors = useCallback(
(err) => { (err) => {
@ -160,7 +171,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
try { try {
trackUsageRef.current('willDeleteEntry', trackerProperty); trackUsageRef.current('willDeleteEntry', trackerProperty);
const { data } = await axiosInstance.delete(getRequestUrl(`${slug}${searchToSend}`)); const { data } = await del(getRequestUrl(`${slug}${searchToSend}`));
toggleNotification({ toggleNotification({
type: 'success', type: 'success',
@ -178,7 +189,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
return Promise.reject(err); return Promise.reject(err);
} }
}, },
[slug, displayErrors, toggleNotification, searchToSend] [del, slug, displayErrors, toggleNotification, searchToSend]
); );
const onDeleteSucceeded = useCallback(() => { const onDeleteSucceeded = useCallback(() => {
@ -194,7 +205,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
try { try {
dispatch(setStatus('submit-pending')); dispatch(setStatus('submit-pending'));
const { data } = await axiosInstance.put(endPoint, body); const { data } = await put(endPoint, body);
trackUsageRef.current('didCreateEntry', trackerProperty); trackUsageRef.current('didCreateEntry', trackerProperty);
toggleNotification({ toggleNotification({
@ -224,6 +235,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
} }
}, },
[ [
put,
cleanReceivedData, cleanReceivedData,
displayErrors, displayErrors,
slug, slug,
@ -242,7 +254,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
const endPoint = getRequestUrl(`${slug}/actions/numberOfDraftRelations`); const endPoint = getRequestUrl(`${slug}/actions/numberOfDraftRelations`);
dispatch(setStatus('draft-relation-check-pending')); dispatch(setStatus('draft-relation-check-pending'));
const numberOfDraftRelations = await axiosInstance.get(endPoint); const numberOfDraftRelations = await fetchClient.get(endPoint);
trackUsageRef.current('didCheckDraftRelations'); trackUsageRef.current('didCheckDraftRelations');
dispatch(setStatus('resolved')); dispatch(setStatus('resolved'));
@ -254,7 +266,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
return Promise.reject(err); return Promise.reject(err);
} }
}, [displayErrors, slug, dispatch]); }, [fetchClient, displayErrors, slug, dispatch]);
const onPublish = useCallback(async () => { const onPublish = useCallback(async () => {
try { try {
@ -263,7 +275,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
dispatch(setStatus('publish-pending')); dispatch(setStatus('publish-pending'));
const { data } = await axiosInstance.post(endPoint); const { data } = await post(endPoint);
trackUsageRef.current('didPublishEntry'); trackUsageRef.current('didPublishEntry');
toggleNotification({ toggleNotification({
@ -283,7 +295,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
return Promise.reject(err); return Promise.reject(err);
} }
}, [cleanReceivedData, displayErrors, slug, searchToSend, dispatch, toggleNotification]); }, [post, cleanReceivedData, displayErrors, slug, searchToSend, dispatch, toggleNotification]);
const onPut = useCallback( const onPut = useCallback(
async (body, trackerProperty) => { async (body, trackerProperty) => {
@ -294,7 +306,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
dispatch(setStatus('submit-pending')); dispatch(setStatus('submit-pending'));
const { data } = await axiosInstance.put(endPoint, body); const { data } = await put(endPoint, body);
toggleNotification({ toggleNotification({
type: 'success', type: 'success',
@ -321,7 +333,16 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
return Promise.reject(err); 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 // 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 { try {
trackUsageRef.current('willUnpublishEntry'); trackUsageRef.current('willUnpublishEntry');
const { data } = await axiosInstance.post(endPoint); const { data } = await post(endPoint);
trackUsageRef.current('didUnpublishEntry'); trackUsageRef.current('didUnpublishEntry');
toggleNotification({ toggleNotification({
@ -348,7 +369,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
dispatch(setStatus('resolved')); dispatch(setStatus('resolved'));
displayErrors(err); displayErrors(err);
} }
}, [cleanReceivedData, toggleNotification, displayErrors, slug, dispatch, searchToSend]); }, [post, cleanReceivedData, toggleNotification, displayErrors, slug, dispatch, searchToSend]);
return children({ return children({
componentsDataStructure, componentsDataStructure,

View File

@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'; import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
import { useSelector, shallowEqual } from 'react-redux'; import { useSelector, shallowEqual } from 'react-redux';
import axios from 'axios'; import axios from 'axios';
import { axiosInstance } from '../../../core/utils'; import { useFetchClient } from '@strapi/helper-plugin';
import formatLayouts from './utils/formatLayouts'; import formatLayouts from './utils/formatLayouts';
import reducer, { initialState } from './reducer'; import reducer, { initialState } from './reducer';
import { makeSelectModelAndComponentSchemas } from '../../pages/App/selectors'; import { makeSelectModelAndComponentSchemas } from '../../pages/App/selectors';
@ -12,6 +12,7 @@ const useFetchContentTypeLayout = (contentTypeUID) => {
const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []); const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []);
const { schemas } = useSelector((state) => schemasSelector(state), shallowEqual); const { schemas } = useSelector((state) => schemasSelector(state), shallowEqual);
const isMounted = useRef(true); const isMounted = useRef(true);
const { get } = useFetchClient();
const getData = useCallback( const getData = useCallback(
async (uid, source) => { async (uid, source) => {
@ -27,7 +28,7 @@ const useFetchContentTypeLayout = (contentTypeUID) => {
const { const {
data: { data }, data: { data },
} = await axiosInstance.get(endPoint, { cancelToken: source.token }); } = await get(endPoint, { cancelToken: source.token });
dispatch({ dispatch({
type: 'GET_DATA_SUCCEEDED', type: 'GET_DATA_SUCCEEDED',
@ -47,7 +48,7 @@ const useFetchContentTypeLayout = (contentTypeUID) => {
} }
} }
}, },
[layouts, schemas] [layouts, schemas, get]
); );
useEffect(() => { useEffect(() => {

View File

@ -2,12 +2,12 @@ import React from 'react';
import { QueryClientProvider, QueryClient } from 'react-query'; import { QueryClientProvider, QueryClient } from 'react-query';
import { renderHook, act } from '@testing-library/react-hooks'; import { renderHook, act } from '@testing-library/react-hooks';
import { axiosInstance } from '../../../../core/utils'; import { useFetchClient } from '@strapi/helper-plugin';
import { useRelation } from '../useRelation'; import { useRelation } from '../useRelation';
jest.mock('../../../../core/utils', () => ({ jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('../../../../core/utils'), ...jest.requireActual('@strapi/helper-plugin'),
axiosInstance: { useFetchClient: jest.fn().mockReturnValue({
get: jest.fn().mockResolvedValue({ get: jest.fn().mockResolvedValue({
data: { data: {
results: [ results: [
@ -17,7 +17,7 @@ jest.mock('../../../../core/utils', () => ({
pagination: { page: 1, pageCount: 10 }, pagination: { page: 1, pageCount: 10 },
}, },
}), }),
}, }),
})); }));
const client = new QueryClient({ 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: { params: {
limit: 10, limit: 10,
page: 1, page: 1,
@ -110,7 +112,7 @@ describe('useRelation', () => {
title: 'xToOne relation', title: 'xToOne relation',
}; };
axiosInstance.get = jest.fn().mockResolvedValueOnce({ useFetchClient().get = jest.fn().mockResolvedValueOnce({
data: { data: {
data: FIXTURE, data: FIXTURE,
}, },
@ -134,7 +136,9 @@ describe('useRelation', () => {
await waitForNextUpdate(); await waitForNextUpdate();
expect(axiosInstance.get).toBeCalledWith(expect.any(String), { const { get } = useFetchClient();
expect(get).toBeCalledWith(expect.any(String), {
params: { params: {
limit: 5, limit: 5,
page: expect.any(Number), page: expect.any(Number),
@ -144,8 +148,9 @@ describe('useRelation', () => {
test('does not fetch relations if it was not enabled', async () => { test('does not fetch relations if it was not enabled', async () => {
await setup({ relation: { enabled: false } }); await setup({ relation: { enabled: false } });
const { get } = useFetchClient();
expect(axiosInstance.get).not.toBeCalled(); expect(get).not.toBeCalled();
}); });
test('fetch relations', async () => { test('fetch relations', async () => {
@ -153,9 +158,11 @@ describe('useRelation', () => {
await waitForNextUpdate(); await waitForNextUpdate();
const { get } = useFetchClient();
expect(result.current.relations.isSuccess).toBe(true); expect(result.current.relations.isSuccess).toBe(true);
expect(axiosInstance.get).toBeCalledTimes(1); expect(get).toBeCalledTimes(1);
expect(axiosInstance.get).toBeCalledWith('/', { expect(get).toBeCalledWith('/', {
params: { params: {
limit: 10, limit: 10,
page: 1, page: 1,
@ -164,7 +171,7 @@ describe('useRelation', () => {
}); });
test('fetch relations next page, if there is one', async () => { test('fetch relations next page, if there is one', async () => {
axiosInstance.get = jest.fn().mockResolvedValueOnce({ useFetchClient().get = jest.fn().mockResolvedValueOnce({
data: { data: {
results: [], results: [],
pagination: { pagination: {
@ -174,6 +181,8 @@ describe('useRelation', () => {
}, },
}); });
const { get } = useFetchClient();
const { result, waitForNextUpdate } = await setup(); const { result, waitForNextUpdate } = await setup();
await waitForNextUpdate(); await waitForNextUpdate();
@ -184,14 +193,14 @@ describe('useRelation', () => {
await waitForNextUpdate(); await waitForNextUpdate();
expect(axiosInstance.get).toBeCalledTimes(2); expect(get).toBeCalledTimes(2);
expect(axiosInstance.get).toHaveBeenNthCalledWith(1, expect.any(String), { expect(get).toHaveBeenNthCalledWith(1, expect.any(String), {
params: { params: {
limit: expect.any(Number), limit: expect.any(Number),
page: 1, page: 1,
}, },
}); });
expect(axiosInstance.get).toHaveBeenNthCalledWith(2, expect.any(String), { expect(get).toHaveBeenNthCalledWith(2, expect.any(String), {
params: { params: {
limit: expect.any(Number), limit: expect.any(Number),
page: 2, page: 2,
@ -200,7 +209,7 @@ describe('useRelation', () => {
}); });
test("does not fetch relations next page, if there isn't one", async () => { test("does not fetch relations next page, if there isn't one", async () => {
axiosInstance.get = jest.fn().mockResolvedValueOnce({ useFetchClient().get = jest.fn().mockResolvedValueOnce({
data: { data: {
results: [], results: [],
pagination: { pagination: {
@ -210,6 +219,8 @@ describe('useRelation', () => {
}, },
}); });
const { get } = useFetchClient();
const { result, waitForNextUpdate } = await setup(); const { result, waitForNextUpdate } = await setup();
await waitForNextUpdate(); await waitForNextUpdate();
@ -220,7 +231,7 @@ describe('useRelation', () => {
await waitForNextUpdate(); await waitForNextUpdate();
expect(axiosInstance.get).toBeCalledTimes(1); expect(get).toBeCalledTimes(1);
}); });
test('does not fetch search by default', async () => { test('does not fetch search by default', async () => {
@ -239,7 +250,7 @@ describe('useRelation', () => {
const spy = jest const spy = jest
.fn() .fn()
.mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } }); .mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } });
axiosInstance.get = spy; useFetchClient().get = spy;
act(() => { act(() => {
result.current.searchFor('something'); result.current.searchFor('something');
@ -261,7 +272,7 @@ describe('useRelation', () => {
const spy = jest const spy = jest
.fn() .fn()
.mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 2 } } }); .mockResolvedValue({ data: { values: [], pagination: { page: 1, pageCount: 2 } } });
axiosInstance.get = spy; useFetchClient().get = spy;
act(() => { act(() => {
result.current.searchFor('something'); result.current.searchFor('something');
@ -285,7 +296,7 @@ describe('useRelation', () => {
const spy = jest const spy = jest
.fn() .fn()
.mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } }); .mockResolvedValue({ data: { results: [], pagination: { page: 1, pageCount: 2 } } });
axiosInstance.get = spy; useFetchClient().get = spy;
act(() => { act(() => {
result.current.searchFor('something'); result.current.searchFor('something');
@ -322,7 +333,7 @@ describe('useRelation', () => {
const spy = jest.fn().mockResolvedValueOnce({ const spy = jest.fn().mockResolvedValueOnce({
data: { results: [], pagination: { page: 1, pageCount: 1 } }, data: { results: [], pagination: { page: 1, pageCount: 1 } },
}); });
axiosInstance.get = spy; useFetchClient().get = spy;
act(() => { act(() => {
result.current.searchFor('something'); result.current.searchFor('something');

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useInfiniteQuery } from 'react-query'; import { useInfiniteQuery } from 'react-query';
import { axiosInstance } from '../../../core/utils'; import { useFetchClient } from '@strapi/helper-plugin';
import { normalizeRelations } from '../../components/RelationInputDataManager/utils'; import { normalizeRelations } from '../../components/RelationInputDataManager/utils';
@ -10,13 +10,13 @@ import { useCallbackRef } from '../useCallbackRef';
export const useRelation = (cacheKey, { relation, search }) => { export const useRelation = (cacheKey, { relation, search }) => {
const [searchParams, setSearchParams] = useState({}); const [searchParams, setSearchParams] = useState({});
const [currentPage, setCurrentPage] = useState(0); const [currentPage, setCurrentPage] = useState(0);
const { get } = useFetchClient();
/** /**
* This runs in `useInfiniteQuery` to actually fetch the data * This runs in `useInfiniteQuery` to actually fetch the data
*/ */
const fetchRelations = async ({ pageParam = 1 }) => { const fetchRelations = async ({ pageParam = 1 }) => {
try { try {
const { data } = await axiosInstance.get(relation?.endpoint, { const { data } = await get(relation?.endpoint, {
params: { params: {
...(relation.pageParams ?? {}), ...(relation.pageParams ?? {}),
page: pageParam, page: pageParam,
@ -33,7 +33,7 @@ export const useRelation = (cacheKey, { relation, search }) => {
const fetchSearch = async ({ pageParam = 1 }) => { const fetchSearch = async ({ pageParam = 1 }) => {
try { try {
const { data } = await axiosInstance.get(search.endpoint, { const { data } = await get(search.endpoint, {
params: { params: {
...(search.pageParams ?? {}), ...(search.pageParams ?? {}),
...searchParams, ...searchParams,

View File

@ -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 { useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useNotifyAT } from '@strapi/design-system/LiveRegions'; import { useNotifyAT } from '@strapi/design-system/LiveRegions';
import axios from 'axios'; import axios from 'axios';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { axiosInstance } from '../../../core/utils';
import { MUTATE_COLLECTION_TYPES_LINKS, MUTATE_SINGLE_TYPES_LINKS } from '../../../exposedHooks'; import { MUTATE_COLLECTION_TYPES_LINKS, MUTATE_SINGLE_TYPES_LINKS } from '../../../exposedHooks';
import { getRequestUrl, getTrad } from '../../utils'; import { getRequestUrl, getTrad } from '../../utils';
import { getData, resetProps, setContentTypeLinks } from './actions'; import { getData, resetProps, setContentTypeLinks } from './actions';
@ -22,6 +26,7 @@ const useModels = () => {
const source = CancelToken.source(); const source = CancelToken.source();
const { notifyStatus } = useNotifyAT(); const { notifyStatus } = useNotifyAT();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { get } = useFetchClient();
const fetchData = async () => { const fetchData = async () => {
dispatch(getData()); dispatch(getData());
@ -36,7 +41,7 @@ const useModels = () => {
}, },
] = await Promise.all( ] = await Promise.all(
['components', 'content-types'].map((endPoint) => ['components', 'content-types'].map((endPoint) =>
axiosInstance.get(getRequestUrl(endPoint), { cancelToken: source.token }) get(getRequestUrl(endPoint), { cancelToken: source.token })
) )
); );

View File

@ -1,13 +1,14 @@
import { axiosInstance } from '../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
import generateModelsLinks from './generateModelsLinks'; import generateModelsLinks from './generateModelsLinks';
import checkPermissions from './checkPermissions'; import checkPermissions from './checkPermissions';
import { getRequestUrl } from '../../../utils'; import { getRequestUrl } from '../../../utils';
const getContentTypeLinks = async (models, userPermissions, toggleNotification) => { const getContentTypeLinks = async (models, userPermissions, toggleNotification) => {
const { get } = getFetchClient();
try { try {
const { const {
data: { data: contentTypeConfigurations }, data: { data: contentTypeConfigurations },
} = await axiosInstance.get(getRequestUrl('content-types-settings')); } = await get(getRequestUrl('content-types-settings'));
const { collectionTypesSectionLinks, singleTypesSectionLinks } = generateModelsLinks( const { collectionTypesSectionLinks, singleTypesSectionLinks } = generateModelsLinks(
models, models,

View File

@ -1,9 +1,8 @@
import React, { memo, useEffect, useMemo, useReducer } from 'react'; import React, { memo, useEffect, useMemo, useReducer } from 'react';
import { useParams } from 'react-router-dom'; 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 { useSelector, shallowEqual } from 'react-redux';
import axios from 'axios'; import axios from 'axios';
import { axiosInstance } from '../../../core/utils';
import { getRequestUrl, mergeMetasWithSchema } from '../../utils'; import { getRequestUrl, mergeMetasWithSchema } from '../../utils';
import { makeSelectModelAndComponentSchemas } from '../App/selectors'; import { makeSelectModelAndComponentSchemas } from '../App/selectors';
import permissions from '../../../permissions'; import permissions from '../../../permissions';
@ -18,6 +17,7 @@ const ComponentSettingsView = () => {
const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []); const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []);
const { schemas } = useSelector((state) => schemasSelector(state), shallowEqual); const { schemas } = useSelector((state) => schemasSelector(state), shallowEqual);
const { uid } = useParams(); const { uid } = useParams();
const { get } = useFetchClient();
useEffect(() => { useEffect(() => {
const CancelToken = axios.CancelToken; const CancelToken = axios.CancelToken;
@ -29,7 +29,7 @@ const ComponentSettingsView = () => {
const { const {
data: { data }, data: { data },
} = await axiosInstance.get(getRequestUrl(`components/${uid}/configuration`), { } = await get(getRequestUrl(`components/${uid}/configuration`), {
cancelToken: source.token, cancelToken: source.token,
}); });
@ -48,7 +48,7 @@ const ComponentSettingsView = () => {
return () => { return () => {
source.cancel('Operation canceled by the user.'); source.cancel('Operation canceled by the user.');
}; };
}, [uid, schemas]); }, [uid, schemas, get]);
if (isLoading) { if (isLoading) {
return <LoadingIndicatorPage />; return <LoadingIndicatorPage />;

View File

@ -1,8 +1,10 @@
import { axiosInstance } from '../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
import { getRequestUrl } from '../../../utils'; import { getRequestUrl } from '../../../utils';
const putCMSettingsEV = (body, slug, isContentTypeView) => { const putCMSettingsEV = (body, slug, isContentTypeView) => {
return axiosInstance.put( const { put } = getFetchClient();
return put(
getRequestUrl( getRequestUrl(
isContentTypeView ? `content-types/${slug}/configuration` : `components/${slug}/configuration` isContentTypeView ? `content-types/${slug}/configuration` : `components/${slug}/configuration`
), ),

View File

@ -1,8 +1,10 @@
import { axiosInstance } from '../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
import { getRequestUrl } from '../../../utils'; import { getRequestUrl } from '../../../utils';
const putCMSettingsLV = (body, slug) => { 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; export default putCMSettingsLV;

View File

@ -14,6 +14,7 @@ import {
NoPermissions, NoPermissions,
CheckPermissions, CheckPermissions,
SearchURLQuery, SearchURLQuery,
useFetchClient,
useFocusWhenNavigate, useFocusWhenNavigate,
useQueryParams, useQueryParams,
useNotification, useNotification,
@ -33,8 +34,6 @@ import ArrowLeft from '@strapi/icons/ArrowLeft';
import Plus from '@strapi/icons/Plus'; import Plus from '@strapi/icons/Plus';
import Cog from '@strapi/icons/Cog'; import Cog from '@strapi/icons/Cog';
import { axiosInstance } from '../../../core/utils';
import DynamicTable from '../../components/DynamicTable'; import DynamicTable from '../../components/DynamicTable';
import AttributeFilter from '../../components/AttributeFilter'; import AttributeFilter from '../../components/AttributeFilter';
import { InjectionZone } from '../../../shared/components'; import { InjectionZone } from '../../../shared/components';
@ -97,6 +96,8 @@ function ListView({
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const contentType = layout.contentType; const contentType = layout.contentType;
const hasDraftAndPublish = get(contentType, 'options.draftAndPublish', false); const hasDraftAndPublish = get(contentType, 'options.draftAndPublish', false);
const fetchClient = useFetchClient();
const { post, del } = fetchClient;
// FIXME // FIXME
// Using a ref to avoid requests being fired multiple times on slug on change // Using a ref to avoid requests being fired multiple times on slug on change
@ -112,7 +113,7 @@ function ListView({
const { const {
data: { results, pagination: paginationResult }, data: { results, pagination: paginationResult },
} = await axiosInstance.get(endPoint, opts); } = await fetchClient.get(endPoint, opts);
notifyStatus( notifyStatus(
formatMessage( formatMessage(
@ -154,13 +155,13 @@ function ListView({
}); });
} }
}, },
[formatMessage, getData, getDataSucceeded, notifyStatus, push, toggleNotification] [formatMessage, getData, getDataSucceeded, notifyStatus, push, toggleNotification, fetchClient]
); );
const handleConfirmDeleteAllData = useCallback( const handleConfirmDeleteAllData = useCallback(
async (ids) => { async (ids) => {
try { try {
await axiosInstance.post(getRequestUrl(`collection-types/${slug}/actions/bulkDelete`), { await post(getRequestUrl(`collection-types/${slug}/actions/bulkDelete`), {
ids, ids,
}); });
@ -174,13 +175,13 @@ function ListView({
}); });
} }
}, },
[fetchData, params, slug, toggleNotification] [fetchData, params, slug, toggleNotification, post]
); );
const handleConfirmDeleteData = useCallback( const handleConfirmDeleteData = useCallback(
async (idToDelete) => { async (idToDelete) => {
try { try {
await axiosInstance.delete(getRequestUrl(`collection-types/${slug}/${idToDelete}`)); await del(getRequestUrl(`collection-types/${slug}/${idToDelete}`));
const requestUrl = getRequestUrl(`collection-types/${slug}${params}`); const requestUrl = getRequestUrl(`collection-types/${slug}${params}`);
fetchData(requestUrl); fetchData(requestUrl);
@ -202,7 +203,7 @@ function ListView({
}); });
} }
}, },
[slug, params, fetchData, toggleNotification, formatMessage] [slug, params, fetchData, toggleNotification, formatMessage, del]
); );
useEffect(() => { useEffect(() => {
@ -221,7 +222,6 @@ function ListView({
source.cancel('Operation canceled by the user.'); source.cancel('Operation canceled by the user.');
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [canRead, getData, slug, params, getDataSucceeded, fetchData]); }, [canRead, getData, slug, params, getDataSucceeded, fetchData]);
const defaultHeaderLayoutTitle = formatMessage({ const defaultHeaderLayoutTitle = formatMessage({

View File

@ -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;

View File

@ -1,3 +1,2 @@
export { default as axiosInstance } from './axiosInstance';
export { default as basename } from './basename'; export { default as basename } from './basename';
export { default as createHook } from './createHook'; export { default as createHook } from './createHook';

View File

@ -1,7 +1,8 @@
import { axiosInstance } from '../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
const fetchEnabledPlugins = async () => { const fetchEnabledPlugins = async () => {
const { data } = await axiosInstance.get('/admin/plugins'); const { get } = getFetchClient();
const { data } = await get('/admin/plugins');
return data; return data;
}; };

View File

@ -1,11 +1,11 @@
import { useState } from 'react'; import { useState } from 'react';
import { get } from 'lodash'; import { get } from 'lodash';
import { useNotification } from '@strapi/helper-plugin'; import { useFetchClient, useNotification } from '@strapi/helper-plugin';
import { axiosInstance } from '../../core/utils';
const useRegenerate = (id, onRegenerate) => { const useRegenerate = (id, onRegenerate) => {
const [isLoadingConfirmation, setIsLoadingConfirmation] = useState(false); const [isLoadingConfirmation, setIsLoadingConfirmation] = useState(false);
const toggleNotification = useNotification(); const toggleNotification = useNotification();
const { post } = useFetchClient();
const regenerateData = async () => { const regenerateData = async () => {
try { try {
@ -13,7 +13,7 @@ const useRegenerate = (id, onRegenerate) => {
data: { data: {
data: { accessKey }, data: { accessKey },
}, },
} = await axiosInstance.post(`/admin/api-tokens/${id}/regenerate`); } = await post(`/admin/api-tokens/${id}/regenerate`);
setIsLoadingConfirmation(false); setIsLoadingConfirmation(false);
onRegenerate(accessKey); onRegenerate(accessKey);
} catch (error) { } catch (error) {

View File

@ -1,18 +1,18 @@
import { renderHook } from '@testing-library/react-hooks'; import { renderHook } from '@testing-library/react-hooks';
import { axiosInstance } from '../../../core/utils';
import useRegenerate from '../index'; import useRegenerate from '../index';
jest.spyOn(axiosInstance, 'get').mockResolvedValue({ jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useNotification: jest.fn(),
useFetchClient: jest.fn().mockReturnValue({
get: jest.fn().mockResolvedValue({
data: { data: {
data: { data: {
accessKey: 'this is my new access key', accessKey: 'this is my new access key',
}, },
}, },
}); }),
}),
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useNotification: jest.fn(),
})); }));
describe('useRegenerate', () => { describe('useRegenerate', () => {

View File

@ -1,7 +1,6 @@
import { useEffect, useReducer, useCallback } from 'react'; 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 get from 'lodash/get';
import { axiosInstance } from '../../core/utils';
import init from './init'; import init from './init';
import reducer, { initialState } from './reducer'; import reducer, { initialState } from './reducer';
@ -19,6 +18,9 @@ const useRolesList = (shouldFetchData = true) => {
}, [shouldFetchData]); }, [shouldFetchData]);
const fetchRolesList = useCallback(async () => { const fetchRolesList = useCallback(async () => {
// TODO: evaluate to replace it with a useFetchClient when we work on the useCallback to remove
const fetchClient = getFetchClient();
try { try {
dispatch({ dispatch({
type: 'GET_DATA', type: 'GET_DATA',
@ -26,7 +28,7 @@ const useRolesList = (shouldFetchData = true) => {
const { const {
data: { data }, data: { data },
} = await axiosInstance.get('/admin/roles'); } = await fetchClient.get('/admin/roles');
dispatch({ dispatch({
type: 'GET_DATA_SUCCEEDED', type: 'GET_DATA_SUCCEEDED',

View File

@ -1,7 +1,7 @@
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import appCustomisations from './app'; import { getFetchClient } from '@strapi/helper-plugin';
import { Components, Fields, Middlewares, Reducers } from './core/apis'; import { Components, Fields, Middlewares, Reducers } from './core/apis';
import { axiosInstance } from './core/utils'; import appCustomisations from './app';
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import plugins from './plugins'; import plugins from './plugins';
import appReducers from './reducers'; import appReducers from './reducers';
@ -29,12 +29,13 @@ const reducers = Reducers({ appReducers });
const MOUNT_NODE = document.getElementById('app'); const MOUNT_NODE = document.getElementById('app');
const run = async () => { const run = async () => {
const { get } = getFetchClient();
try { try {
const { const {
data: { data: {
data: { isEE, features }, data: { isEE, features },
}, },
} = await axiosInstance.get('/admin/project-type'); } = await get('/admin/project-type');
window.strapi.isEE = isEE; window.strapi.isEE = isEE;
window.strapi.features = { window.strapi.features = {

View File

@ -11,7 +11,6 @@ jest.mock('react-redux', () => ({
jest.mock('@strapi/helper-plugin', () => ({ jest.mock('@strapi/helper-plugin', () => ({
useTracking: jest.fn(() => ({ trackUsage: trackUsageMock })), useTracking: jest.fn(() => ({ trackUsage: trackUsageMock })),
wrapAxiosInstance: jest.fn(() => {}),
})); }));
describe('Admin | pages | Admin | useTrackUsage', () => { describe('Admin | pages | Admin | useTrackUsage', () => {

View File

@ -1,15 +1,17 @@
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { axiosInstance } from '../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
const fetchUser = async () => { const fetchUser = async () => {
const { data } = await axiosInstance.get('/admin/users/me'); const { get } = getFetchClient();
const { data } = await get('/admin/users/me');
return data.data; return data.data;
}; };
const putUser = async (body) => { const putUser = async (body) => {
const dataToSend = omit(body, ['confirmPassword', 'currentTheme']); 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 }; return { ...data.data, currentTheme: body.currentTheme };
}; };

View File

@ -9,13 +9,13 @@ import {
useTracking, useTracking,
useGuidedTour, useGuidedTour,
useRBAC, useRBAC,
useFetchClient,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import { Main } from '@strapi/design-system/Main'; import { Main } from '@strapi/design-system/Main';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { useRouteMatch, useHistory } from 'react-router-dom'; import { useRouteMatch, useHistory } from 'react-router-dom';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { formatAPIErrors } from '../../../../../utils'; import { formatAPIErrors } from '../../../../../utils';
import { axiosInstance } from '../../../../../core/utils';
import { schema } from './utils'; import { schema } from './utils';
import LoadingView from './components/LoadingView'; import LoadingView from './components/LoadingView';
import FormHead from './components/FormHead'; import FormHead from './components/FormHead';
@ -50,6 +50,7 @@ const ApiTokenCreateView = () => {
const { const {
params: { id }, params: { id },
} = useRouteMatch('/settings/api-tokens/:id'); } = useRouteMatch('/settings/api-tokens/:id');
const { get, post, put } = useFetchClient();
const isCreating = id === 'create'; const isCreating = id === 'create';
@ -58,7 +59,7 @@ const ApiTokenCreateView = () => {
async () => { async () => {
const [permissions, routes] = await Promise.all( const [permissions, routes] = await Promise.all(
['/admin/content-api/permissions', '/admin/content-api/routes'].map(async (url) => { ['/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; return data.data;
}) })
@ -112,7 +113,7 @@ const ApiTokenCreateView = () => {
async () => { async () => {
const { const {
data: { data }, data: { data },
} = await axiosInstance.get(`/admin/api-tokens/${id}`); } = await get(`/admin/api-tokens/${id}`);
setApiToken({ setApiToken({
...data, ...data,
@ -160,12 +161,12 @@ const ApiTokenCreateView = () => {
const { const {
data: { data: response }, data: { data: response },
} = isCreating } = isCreating
? await axiosInstance.post(`/admin/api-tokens`, { ? await post(`/admin/api-tokens`, {
...body, ...body,
lifespan: lifespanVal, lifespan: lifespanVal,
permissions: body.type === 'custom' ? state.selectedActions : null, permissions: body.type === 'custom' ? state.selectedActions : null,
}) })
: await axiosInstance.put(`/admin/api-tokens/${id}`, { : await put(`/admin/api-tokens/${id}`, {
name: body.name, name: body.name,
description: body.description, description: body.description,
type: body.type, type: body.type,

View File

@ -5,7 +5,6 @@ import { Router, Route } from 'react-router-dom';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { QueryClient, QueryClientProvider } from 'react-query'; import { QueryClient, QueryClientProvider } from 'react-query';
import { lightTheme, darkTheme } from '@strapi/design-system'; import { lightTheme, darkTheme } from '@strapi/design-system';
import { axiosInstance } from '../../../../../../core/utils';
import Theme from '../../../../../../components/Theme'; import Theme from '../../../../../../components/Theme';
import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider'; import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider';
import EditView from '../index'; import EditView from '../index';
@ -32,9 +31,8 @@ jest.mock('@strapi/helper-plugin', () => ({
lockApp: jest.fn(), lockApp: jest.fn(),
unlockApp: jest.fn(), unlockApp: jest.fn(),
})), })),
})); useFetchClient: jest.fn().mockReturnValue({
get: jest.fn().mockImplementation((path) => {
jest.spyOn(axiosInstance, 'get').mockImplementation((path) => {
if (path === '/admin/content-api/permissions') { if (path === '/admin/content-api/permissions') {
return { data }; return { data };
} }
@ -51,7 +49,10 @@ jest.spyOn(axiosInstance, 'get').mockImplementation((path) => {
}, },
}, },
}; };
}); }),
}),
}));
jest.spyOn(Date, 'now').mockImplementation(() => new Date('2015-10-01T08:00:00.000Z')); jest.spyOn(Date, 'now').mockImplementation(() => new Date('2015-10-01T08:00:00.000Z'));
const client = new QueryClient({ const client = new QueryClient({

View File

@ -14,6 +14,7 @@ import {
DynamicTable, DynamicTable,
useTracking, useTracking,
useGuidedTour, useGuidedTour,
useFetchClient,
LinkButton, LinkButton,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout'; 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 { Button } from '@strapi/design-system/Button';
import Plus from '@strapi/icons/Plus'; import Plus from '@strapi/icons/Plus';
import { axiosInstance } from '../../../../../core/utils';
import adminPermissions from '../../../../../permissions'; import adminPermissions from '../../../../../permissions';
import tableHeaders from './utils/tableHeaders'; import tableHeaders from './utils/tableHeaders';
import TableRows from './DynamicTable'; import TableRows from './DynamicTable';
@ -38,6 +38,7 @@ const ApiTokenListView = () => {
const { trackUsage } = useTracking(); const { trackUsage } = useTracking();
const { startSection } = useGuidedTour(); const { startSection } = useGuidedTour();
const startSectionRef = useRef(startSection); const startSectionRef = useRef(startSection);
const { get, del } = useFetchClient();
useEffect(() => { useEffect(() => {
if (startSectionRef.current) { if (startSectionRef.current) {
@ -67,7 +68,7 @@ const ApiTokenListView = () => {
trackUsage('willAccessTokenList'); trackUsage('willAccessTokenList');
const { const {
data: { data }, data: { data },
} = await axiosInstance.get(`/admin/api-tokens`); } = await get(`/admin/api-tokens`);
trackUsage('didAccessTokenList', { number: data.length }); trackUsage('didAccessTokenList', { number: data.length });
@ -90,7 +91,7 @@ const ApiTokenListView = () => {
const deleteMutation = useMutation( const deleteMutation = useMutation(
async (id) => { async (id) => {
await axiosInstance.delete(`/admin/api-tokens/${id}`); await del(`/admin/api-tokens/${id}`);
}, },
{ {
async onSuccess() { async onSuccess() {

View File

@ -6,7 +6,6 @@ import { createMemoryHistory } from 'history';
import { useRBAC, TrackingProvider } from '@strapi/helper-plugin'; import { useRBAC, TrackingProvider } from '@strapi/helper-plugin';
import { QueryClient, QueryClientProvider } from 'react-query'; import { QueryClient, QueryClientProvider } from 'react-query';
import { lightTheme, darkTheme } from '@strapi/design-system'; import { lightTheme, darkTheme } from '@strapi/design-system';
import { axiosInstance } from '../../../../../../core/utils';
import Theme from '../../../../../../components/Theme'; import Theme from '../../../../../../components/Theme';
import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider'; import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider';
import ListView from '../index'; import ListView from '../index';
@ -19,9 +18,8 @@ jest.mock('@strapi/helper-plugin', () => ({
useGuidedTour: jest.fn(() => ({ useGuidedTour: jest.fn(() => ({
startSection: jest.fn(), startSection: jest.fn(),
})), })),
})); useFetchClient: jest.fn().mockReturnValue({
get: jest.fn().mockResolvedValue({
jest.spyOn(axiosInstance, 'get').mockResolvedValue({
data: { data: {
data: [ data: [
{ {
@ -33,7 +31,9 @@ jest.spyOn(axiosInstance, 'get').mockResolvedValue({
}, },
], ],
}, },
}); }),
}),
}));
jest.spyOn(Date, 'now').mockImplementation(() => new Date('2015-10-01T08:00:00.000Z')); jest.spyOn(Date, 'now').mockImplementation(() => new Date('2015-10-01T08:00:00.000Z'));

View File

@ -5,7 +5,6 @@ import { IntlProvider } from 'react-intl';
import { ThemeProvider, lightTheme } from '@strapi/design-system'; import { ThemeProvider, lightTheme } from '@strapi/design-system';
import { useAppInfos, useRBAC, TrackingProvider } from '@strapi/helper-plugin'; import { useAppInfos, useRBAC, TrackingProvider } from '@strapi/helper-plugin';
import ApplicationInfosPage from '../index'; import ApplicationInfosPage from '../index';
import { axiosInstance } from '../../../../../core/utils';
import server from './server'; import server from './server';
const updateProjectSettingsSpy = jest.fn(); const updateProjectSettingsSpy = jest.fn();
@ -17,18 +16,8 @@ jest.mock('@strapi/helper-plugin', () => ({
useAppInfos: jest.fn(() => ({ shouldUpdateStrapi: false, latestStrapiReleaseTag: 'v3.6.8' })), useAppInfos: jest.fn(() => ({ shouldUpdateStrapi: false, latestStrapiReleaseTag: 'v3.6.8' })),
useNotification: jest.fn(() => jest.fn()), useNotification: jest.fn(() => jest.fn()),
useRBAC: jest.fn(() => ({ allowedActions: { canRead: true, canUpdate: true } })), useRBAC: jest.fn(() => ({ allowedActions: { canRead: true, canUpdate: true } })),
})); useFetchClient: jest.fn().mockReturnValue({
jest.mock('../../../../../hooks', () => ({ get: jest.fn().mockResolvedValue({
useConfigurations: jest.fn(() => ({
logos: {
menu: { custom: 'customMenuLogo.png', default: 'defaultMenuLogo.png' },
auth: { custom: 'customAuthLogo.png', default: 'defaultAuthLogo.png' },
},
updateProjectSettings: updateProjectSettingsSpy,
})),
}));
jest.spyOn(axiosInstance, 'get').mockResolvedValue({
data: { data: {
menuLogo: { menuLogo: {
ext: 'png', ext: 'png',
@ -39,7 +28,18 @@ jest.spyOn(axiosInstance, 'get').mockResolvedValue({
width: 246, width: 246,
}, },
}, },
}); }),
}),
}));
jest.mock('../../../../../hooks', () => ({
useConfigurations: jest.fn(() => ({
logos: {
menu: { custom: 'customMenuLogo.png', default: 'defaultMenuLogo.png' },
auth: { custom: 'customAuthLogo.png', default: 'defaultAuthLogo.png' },
},
updateProjectSettings: updateProjectSettingsSpy,
})),
}));
const client = new QueryClient(); const client = new QueryClient();

View File

@ -1,14 +1,16 @@
import { axiosInstance } from '../../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
import prefixAllUrls from './prefixAllUrls'; import prefixAllUrls from './prefixAllUrls';
const fetchProjectSettings = async () => { const fetchProjectSettings = async () => {
const { data } = await axiosInstance.get('/admin/project-settings'); const { get } = getFetchClient();
const { data } = await get('/admin/project-settings');
return prefixAllUrls(data); return prefixAllUrls(data);
}; };
const postProjectSettings = async (body) => { 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: { headers: {
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data',
}, },

View File

@ -1,13 +1,16 @@
import { axiosInstance } from '../../../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
const fetchUser = async (id) => { 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; return data.data;
}; };
const putUser = async (id, body) => { 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; return data.data;
}; };

View File

@ -15,12 +15,17 @@ import { Stack } from '@strapi/design-system/Stack';
import { Typography } from '@strapi/design-system/Typography'; import { Typography } from '@strapi/design-system/Typography';
import { Formik } from 'formik'; 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 { useQueryClient, useMutation } from 'react-query';
import formDataModel from 'ee_else_ce/pages/SettingsPage/pages/Users/ListPage/ModalForm/utils/formDataModel'; 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 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 MagicLink from 'ee_else_ce/pages/SettingsPage/pages/Users/components/MagicLink';
import { axiosInstance } from '../../../../../../core/utils';
import SelectRoles from '../../components/SelectRoles'; import SelectRoles from '../../components/SelectRoles';
import layout from './utils/layout'; import layout from './utils/layout';
import schema from './utils/schema'; import schema from './utils/schema';
@ -34,9 +39,10 @@ const ModalForm = ({ queryName, onToggle }) => {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const toggleNotification = useNotification(); const toggleNotification = useNotification();
const { lockApp, unlockApp } = useOverlayBlocker(); const { lockApp, unlockApp } = useOverlayBlocker();
const { post } = useFetchClient();
const postMutation = useMutation( const postMutation = useMutation(
(body) => { (body) => {
return axiosInstance.post('/admin/users', body); return post('/admin/users', body);
}, },
{ {
async onSuccess({ data }) { async onSuccess({ data }) {

View File

@ -1,9 +1,10 @@
import { axiosInstance } from '../../../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
const fetchData = async (search, notify) => { const fetchData = async (search, notify) => {
const { get } = getFetchClient();
const { const {
data: { data }, data: { data },
} = await axiosInstance.get(`/admin/users${search}`); } = await get(`/admin/users${search}`);
notify(); notify();
@ -11,7 +12,9 @@ const fetchData = async (search, notify) => {
}; };
const deleteData = async (ids) => { 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 }; export { deleteData, fetchData };

View File

@ -5,7 +5,7 @@ import { Select, Option } from '@strapi/design-system/Select';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import styled, { keyframes } from 'styled-components'; import styled, { keyframes } from 'styled-components';
import LoadingIcon from '@strapi/icons/Loader'; import LoadingIcon from '@strapi/icons/Loader';
import { axiosInstance } from '../../../../../../core/utils'; import { getFetchClient } from '@strapi/helper-plugin';
const rotation = keyframes` const rotation = keyframes`
from { from {
@ -27,7 +27,8 @@ const Loader = () => (
); );
const fetchData = async () => { const fetchData = async () => {
const { data } = await axiosInstance.get('/admin/roles'); const { get } = getFetchClient();
const { data } = await get('/admin/roles');
return data.data; return data.data;
}; };

View File

@ -11,12 +11,12 @@ import {
to, to,
useNotification, useNotification,
useOverlayBlocker, useOverlayBlocker,
useFetchClient,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import { Main } from '@strapi/design-system/Main'; import { Main } from '@strapi/design-system/Main';
import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useMutation, useQuery, useQueryClient } from 'react-query';
import { useHistory, useRouteMatch } from 'react-router-dom'; import { useHistory, useRouteMatch } from 'react-router-dom';
import { useModels } from '../../../../../hooks'; import { useModels } from '../../../../../hooks';
import { axiosInstance } from '../../../../../core/utils';
import WebhookForm from './components/WebhookForm'; import WebhookForm from './components/WebhookForm';
import cleanData from './utils/formatData'; import cleanData from './utils/formatData';
@ -30,6 +30,7 @@ const EditView = () => {
const toggleNotification = useNotification(); const toggleNotification = useNotification();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { isLoading: isLoadingForModels, collectionTypes } = useModels(); const { isLoading: isLoadingForModels, collectionTypes } = useModels();
const { post } = useFetchClient();
const isCreating = id === 'create'; const isCreating = id === 'create';
@ -64,7 +65,7 @@ const EditView = () => {
data: triggerResponse, data: triggerResponse,
isIdle: isTriggerIdle, isIdle: isTriggerIdle,
mutate, mutate,
} = useMutation(() => axiosInstance.post(`/admin/webhooks/${id}/trigger`)); } = useMutation(() => post(`/admin/webhooks/${id}/trigger`));
const triggerWebhook = () => const triggerWebhook = () =>
mutate(null, { mutate(null, {

View File

@ -4,6 +4,7 @@ import {
LoadingIndicatorPage, LoadingIndicatorPage,
SearchURLQuery, SearchURLQuery,
SettingsPageTitle, SettingsPageTitle,
getFetchClient,
useNotification, useNotification,
useQueryParams, useQueryParams,
useRBAC, useRBAC,
@ -23,7 +24,6 @@ import { get } from 'lodash';
import matchSorter from 'match-sorter'; import matchSorter from 'match-sorter';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { axiosInstance } from '../../../../../../../admin/src/core/utils';
import { useRolesList } from '../../../../../../../admin/src/hooks'; import { useRolesList } from '../../../../../../../admin/src/hooks';
import adminPermissions from '../../../../../../../admin/src/permissions'; import adminPermissions from '../../../../../../../admin/src/permissions';
import EmptyRole from '../../../../../../../admin/src/pages/SettingsPage/pages/Roles/ListPage/components/EmptyRole'; import EmptyRole from '../../../../../../../admin/src/pages/SettingsPage/pages/Roles/ListPage/components/EmptyRole';
@ -72,13 +72,15 @@ const useRoleActions = ({ getData, canCreate, canDelete, canUpdate }) => {
initialState initialState
); );
const { post } = getFetchClient();
const handleDeleteData = async () => { const handleDeleteData = async () => {
try { try {
dispatch({ dispatch({
type: 'ON_REMOVE_ROLES', type: 'ON_REMOVE_ROLES',
}); });
await axiosInstance.post('/admin/roles/batch-delete', { await post('/admin/roles/batch-delete', {
ids: [roleToDelete], ids: [roleToDelete],
}); });

View File

@ -3,7 +3,7 @@
* useFetchClient * useFetchClient
* *
*/ */
import { useEffect, useRef } from 'react'; import { useEffect, useRef, useMemo } from 'react';
import getFetchClient from '../../utils/getFetchClient'; import getFetchClient from '../../utils/getFetchClient';
const useFetchClient = () => { const useFetchClient = () => {
@ -19,11 +19,13 @@ const useFetchClient = () => {
}; };
}, []); }, []);
const defaultOptions = { return useMemo(
() =>
getFetchClient({
signal: controller.current.signal, signal: controller.current.signal,
}; }),
[]
return getFetchClient(defaultOptions); );
}; };
export default useFetchClient; export default useFetchClient;

View File

@ -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 <div {...props}>{props.children}</div>;
};
describe('useFetchClient', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('Should call once the GET method even when we rerender the Component', async () => {
const { rerender } = render(<TestComponent>content</TestComponent>);
const { get } = useFetchClient();
expect(get).toHaveBeenCalledTimes(1);
rerender();
expect(get).toHaveBeenCalledTimes(1);
});
});