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
slug: /hooks/use-fetch-client
description: API reference for the useFetchClient hook in Strapi
tags:
- hooks
- axios
- data
- helper-plugin
---
## Usage

View File

@ -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',
},
],
},
],
},
{

View File

@ -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) {

View File

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

View File

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

View File

@ -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 = (
<Flex gap={1} wrap="nowrap">
@ -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,

View File

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

View File

@ -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(<ComponentFixture />);
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(<ComponentFixture />);
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());
});
});

View File

@ -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() : '',

View File

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

View File

@ -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(() => {

View File

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

View File

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

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 { 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 })
)
);

View File

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

View File

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

View File

@ -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`
),

View File

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

View File

@ -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({

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 createHook } from './createHook';

View File

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

View File

@ -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) {

View File

@ -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', () => {

View File

@ -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',

View File

@ -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 = {

View File

@ -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', () => {

View File

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

View File

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

View File

@ -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({

View File

@ -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() {

View File

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

View File

@ -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 = (

View File

@ -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',
},

View File

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

View File

@ -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 }) {

View File

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

View File

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

View File

@ -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, {

View File

@ -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],
});

View File

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

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);
});
});