mirror of
https://github.com/strapi/strapi.git
synced 2025-12-26 14:44:31 +00:00
chore: error handling for fetch
This commit is contained in:
parent
65df420943
commit
7ca9df7f9d
@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { AxiosError } from 'axios';
|
||||
import { IntlFormatters, useIntl } from 'react-intl';
|
||||
|
||||
import { FetchError } from '../utils/getFetchClient';
|
||||
@ -121,18 +120,18 @@ export function useAPIErrorHandler(
|
||||
/**
|
||||
* @description This method try to normalize the passed error
|
||||
* and then call formatAPIError to stringify the ResponseObject
|
||||
* into a string. If it fails it will call formatAxiosError and
|
||||
* into a string. If it fails it will call formatFetchError and
|
||||
* return the error message.
|
||||
*/
|
||||
const formatError = React.useCallback(
|
||||
(error: AxiosError<{ error: ApiError }>) => {
|
||||
(error: FetchError) => {
|
||||
// Try to normalize the passed error first. This will fail for e.g. network
|
||||
// errors which are thrown by Axios directly.
|
||||
try {
|
||||
const formattedErr = formatAPIError(error, { intlMessagePrefixCallback, formatMessage });
|
||||
|
||||
if (!formattedErr) {
|
||||
return formatAxiosError(error, { intlMessagePrefixCallback, formatMessage });
|
||||
return formatFetchError(error, { intlMessagePrefixCallback, formatMessage });
|
||||
}
|
||||
|
||||
return formattedErr;
|
||||
@ -192,7 +191,7 @@ export function useAPIErrorHandler(
|
||||
error,
|
||||
},
|
||||
},
|
||||
} as AxiosError<{ error: BaseQueryError }>;
|
||||
} as FetchError;
|
||||
|
||||
/**
|
||||
* There's a chance with SerializedErrors that the message is not set.
|
||||
@ -202,7 +201,6 @@ export function useAPIErrorHandler(
|
||||
return 'Unknown error occured.';
|
||||
}
|
||||
|
||||
// @ts-expect-error – UnknownApiError is in the same shape as ApiError, but we don't want to expose this to users yet.
|
||||
return formatError(err);
|
||||
},
|
||||
[formatError]
|
||||
@ -211,8 +209,8 @@ export function useAPIErrorHandler(
|
||||
};
|
||||
}
|
||||
|
||||
function formatAxiosError(
|
||||
error: AxiosError<unknown>,
|
||||
function formatFetchError(
|
||||
error: FetchError,
|
||||
{ intlMessagePrefixCallback, formatMessage }: FormatAPIErrorOptions
|
||||
) {
|
||||
const { code, message } = error;
|
||||
@ -238,7 +236,7 @@ type FormatAPIErrorOptions = Partial<Pick<NormalizeErrorOptions, 'intlMessagePre
|
||||
* will bo concatenated into a single string.
|
||||
*/
|
||||
function formatAPIError(
|
||||
error: AxiosError<{ error: ApiError }>,
|
||||
error: FetchError,
|
||||
{ formatMessage, intlMessagePrefixCallback }: FormatAPIErrorOptions
|
||||
) {
|
||||
if (!formatMessage) {
|
||||
|
||||
@ -15,7 +15,6 @@ import {
|
||||
VisuallyHidden,
|
||||
} from '@strapi/design-system';
|
||||
import { Duplicate, Pencil, Plus, Trash } from '@strapi/icons';
|
||||
import { AxiosError } from 'axios';
|
||||
import { produce } from 'immer';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -31,6 +30,7 @@ import { useFetchClient } from '../../../../hooks/useFetchClient';
|
||||
import { useQueryParams } from '../../../../hooks/useQueryParams';
|
||||
import { useRBAC } from '../../../../hooks/useRBAC';
|
||||
import { selectAdminPermissions } from '../../../../selectors';
|
||||
import { isFetchError } from '../../../../utils/getFetchClient';
|
||||
|
||||
import { RoleRow, RoleRowProps } from './components/RoleRow';
|
||||
|
||||
@ -74,7 +74,7 @@ const ListPage = () => {
|
||||
type: 'RESET_DATA_TO_DELETE',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
if (isFetchError(error)) {
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
message: formatAPIError(error),
|
||||
|
||||
@ -5,9 +5,6 @@ import { getFetchClient, isFetchError, type FetchConfig } from '../utils/getFetc
|
||||
|
||||
import type { ApiError } from '../hooks/useAPIErrorHandler';
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Axios data
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
export interface QueryArguments {
|
||||
url: string;
|
||||
method?: string;
|
||||
@ -73,16 +70,26 @@ const fetchBaseQuery =
|
||||
// Handle error of type FetchError
|
||||
|
||||
if (isFetchError(err)) {
|
||||
return {
|
||||
data: undefined,
|
||||
error: {
|
||||
name: 'UnknownError',
|
||||
message: err.message,
|
||||
details: err.response,
|
||||
status: err.response?.status,
|
||||
stack: err.stack,
|
||||
} as UnknownApiError,
|
||||
};
|
||||
if (
|
||||
typeof err.response?.data === 'object' &&
|
||||
err.response?.data !== null &&
|
||||
'error' in err.response?.data
|
||||
) {
|
||||
/**
|
||||
* This will most likely be ApiError
|
||||
*/
|
||||
return { data: undefined, error: err.response?.data.error as any };
|
||||
} else {
|
||||
return {
|
||||
data: undefined,
|
||||
error: {
|
||||
name: 'UnknownError',
|
||||
message: err.message,
|
||||
details: err.response,
|
||||
status: err.response?.error.status,
|
||||
} as UnknownApiError,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const error = err as Error;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import qs from 'qs';
|
||||
|
||||
import type { ApiError } from '../hooks/useAPIErrorHandler';
|
||||
|
||||
const STORAGE_KEYS = {
|
||||
TOKEN: 'jwtToken',
|
||||
USER: 'userInfo',
|
||||
@ -21,16 +23,23 @@ export type FetchConfig = {
|
||||
fetchConfig?: FetchParams[1];
|
||||
};
|
||||
|
||||
type ErrorResponse = {
|
||||
data: any;
|
||||
error: ApiError & { status: number };
|
||||
};
|
||||
|
||||
export class FetchError extends Error {
|
||||
public name: string;
|
||||
public message: string;
|
||||
public response?: Response;
|
||||
public response?: ErrorResponse;
|
||||
public code?: number;
|
||||
|
||||
constructor(message: string, response?: Response) {
|
||||
constructor(message: string, response?: ErrorResponse) {
|
||||
super(message);
|
||||
this.name = 'FetchError';
|
||||
this.message = message;
|
||||
this.response = response;
|
||||
this.code = response?.error.status;
|
||||
|
||||
// Ensure correct stack trace in error object
|
||||
if (Error.captureStackTrace) {
|
||||
@ -104,7 +113,9 @@ const getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {
|
||||
response: Response
|
||||
): Promise<FetchResponse<TData>> => {
|
||||
const result = await response.json();
|
||||
|
||||
if (result.error) {
|
||||
throw new FetchError(result.error.message, result);
|
||||
}
|
||||
return {
|
||||
data: result,
|
||||
};
|
||||
@ -129,21 +140,12 @@ const getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {
|
||||
return `${baseURL}${url}`;
|
||||
};
|
||||
|
||||
const fetchHandler = async (url: string, options?: FetchOptions) => {
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
throw new FetchError(`Failed to fetch data: ${response.statusText}`, response);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
const fetchClient: FetchClient = {
|
||||
get: async <TData = unknown, R = FetchResponse<TData>>(
|
||||
url: string,
|
||||
options?: FetchConfig
|
||||
): Promise<R> => {
|
||||
const response = await fetchHandler(
|
||||
const response = await fetch(
|
||||
paramsSerializer(
|
||||
addBaseUrl(normalizeUrl(url), options?.options?.baseURL),
|
||||
options?.options?.params
|
||||
@ -162,7 +164,7 @@ const getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {
|
||||
data?: TSend,
|
||||
options?: FetchConfig
|
||||
): Promise<R> => {
|
||||
const response = await fetchHandler(
|
||||
const response = await fetch(
|
||||
paramsSerializer(
|
||||
addBaseUrl(normalizeUrl(url), options?.options?.baseURL),
|
||||
options?.options?.params
|
||||
@ -182,7 +184,7 @@ const getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {
|
||||
data?: TSend,
|
||||
options?: FetchConfig
|
||||
): Promise<R> => {
|
||||
const response = await fetchHandler(
|
||||
const response = await fetch(
|
||||
paramsSerializer(
|
||||
addBaseUrl(normalizeUrl(url), options?.options?.baseURL),
|
||||
options?.options?.params
|
||||
@ -201,7 +203,7 @@ const getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {
|
||||
url: string,
|
||||
options?: FetchConfig
|
||||
): Promise<R> => {
|
||||
const response = await fetchHandler(
|
||||
const response = await fetch(
|
||||
paramsSerializer(
|
||||
addBaseUrl(normalizeUrl(url), options?.options?.baseURL),
|
||||
options?.options?.params
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { getPrefixedId } from './getPrefixedId';
|
||||
|
||||
import type { ApiError } from '../hooks/useAPIErrorHandler';
|
||||
import type { FetchError } from '../utils/getFetchClient';
|
||||
import type { errors } from '@strapi/utils';
|
||||
import type { AxiosError } from 'axios';
|
||||
|
||||
export interface NormalizeErrorOptions {
|
||||
name?: string;
|
||||
@ -53,7 +53,7 @@ const validateErrorIsYupValidationError = (
|
||||
* (e.g. outside of a React component).
|
||||
*/
|
||||
export function normalizeAPIError(
|
||||
apiError: AxiosError<{ error: ApiError }>,
|
||||
apiError: FetchError,
|
||||
intlMessagePrefixCallback?: NormalizeErrorOptions['intlMessagePrefixCallback']
|
||||
):
|
||||
| NormalizeErrorReturn
|
||||
|
||||
@ -2,7 +2,7 @@ import { server } from '@tests/utils';
|
||||
import { AxiosError } from 'axios';
|
||||
import { rest } from 'msw';
|
||||
|
||||
import { getFetchClient, instance } from '../getFetchClient';
|
||||
import { getFetchClient, FetchError } from '../getFetchClient';
|
||||
|
||||
describe('fetchClient', () => {
|
||||
it('should contain a paramsSerializer that can serialize a params object to a string', async () => {
|
||||
@ -70,7 +70,7 @@ describe('getFetchClient', () => {
|
||||
try {
|
||||
await response.get('test-fetch-client');
|
||||
} catch (err) {
|
||||
const url = (err as AxiosError).config?.url;
|
||||
const url = (err as FetchError).config?.url;
|
||||
expect(url).toBe('/test-fetch-client');
|
||||
}
|
||||
});
|
||||
|
||||
@ -112,9 +112,9 @@ const fetchBaseQuery =
|
||||
}
|
||||
} catch (err) {
|
||||
/**
|
||||
* Handle error of type AxiosError
|
||||
* Handle error of type FetchError
|
||||
*
|
||||
* This format mimics what we want from an AxiosError which is what the
|
||||
* This format mimics what we want from an FetchError which is what the
|
||||
* rest of the app works with, except this format is "serializable" since
|
||||
* it goes into the redux store.
|
||||
*
|
||||
@ -130,7 +130,7 @@ const fetchBaseQuery =
|
||||
/**
|
||||
* This will most likely be ApiError
|
||||
*/
|
||||
return { data: undefined, error: err.response?.data.error };
|
||||
return { data: undefined, error: err.response?.data.error as any };
|
||||
} else {
|
||||
return {
|
||||
data: undefined,
|
||||
@ -138,7 +138,7 @@ const fetchBaseQuery =
|
||||
name: 'UnknownError',
|
||||
message: 'There was an unknown error response from the API',
|
||||
details: err.response,
|
||||
status: err.response?.status,
|
||||
status: err.response?.error.status,
|
||||
} as UnknownApiError,
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ import {
|
||||
import { LinkButton } from '@strapi/design-system/v2';
|
||||
import { EmptyDocuments, Plus } from '@strapi/icons';
|
||||
import { unstable_useDocument } from '@strapi/plugin-content-manager/strapi-admin';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Link as ReactRouterLink, useParams } from 'react-router-dom';
|
||||
@ -144,7 +143,7 @@ const AddActionToReleaseModal = ({
|
||||
}
|
||||
|
||||
if ('error' in response) {
|
||||
if (isAxiosError(response.error)) {
|
||||
if (isFetchError(response.error)) {
|
||||
// Handle axios error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
useNotification,
|
||||
useQueryParams,
|
||||
useRBAC,
|
||||
isFetchError,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
import {
|
||||
Box,
|
||||
@ -17,7 +18,6 @@ import {
|
||||
ModalFooter,
|
||||
} from '@strapi/design-system';
|
||||
import { UID } from '@strapi/types';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
@ -121,7 +121,7 @@ const ReleaseAction: BulkActionComponent = ({ documentIds, model }) => {
|
||||
}
|
||||
|
||||
if ('error' in response) {
|
||||
if (isAxiosError(response.error)) {
|
||||
if (isFetchError(response.error)) {
|
||||
// Handle axios error
|
||||
toggleNotification({
|
||||
type: 'warning',
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { useAPIErrorHandler, useNotification, useAuth, useRBAC } from '@strapi/admin/strapi-admin';
|
||||
import {
|
||||
useAPIErrorHandler,
|
||||
useNotification,
|
||||
useAuth,
|
||||
useRBAC,
|
||||
isFetchError,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
import { Flex, IconButton, Typography, Icon } from '@strapi/design-system';
|
||||
import { Menu, Link } from '@strapi/design-system/v2';
|
||||
import { Cross, More, Pencil } from '@strapi/icons';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
@ -85,7 +90,7 @@ const DeleteReleaseActionItem = ({ releaseId, actionId }: DeleteReleaseActionIte
|
||||
}
|
||||
|
||||
if ('error' in response) {
|
||||
if (isAxiosError(response.error)) {
|
||||
if (isFetchError(response.error)) {
|
||||
// Handle axios error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
useNotification,
|
||||
useQueryParams,
|
||||
useRBAC,
|
||||
isFetchError,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
import {
|
||||
Button,
|
||||
@ -43,7 +44,6 @@ import { ReleaseActionMenu } from '../components/ReleaseActionMenu';
|
||||
import { ReleaseActionOptions } from '../components/ReleaseActionOptions';
|
||||
import { ReleaseModal, FormValues } from '../components/ReleaseModal';
|
||||
import { PERMISSIONS } from '../constants';
|
||||
import { isAxiosError } from '../services/baseQuery';
|
||||
import {
|
||||
GetReleaseActionsQueryParams,
|
||||
useGetReleaseActionsQuery,
|
||||
@ -253,7 +253,7 @@ const ReleaseDetailsLayout = ({
|
||||
totalPublishedEntries,
|
||||
totalUnpublishedEntries,
|
||||
});
|
||||
} else if (isAxiosError(response.error)) {
|
||||
} else if (isFetchError(response.error)) {
|
||||
// When the response returns an object with 'error', handle axios error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
@ -556,7 +556,7 @@ const ReleaseDetailsBody = ({ releaseId }: ReleaseDetailsBodyProps) => {
|
||||
});
|
||||
|
||||
if ('error' in response) {
|
||||
if (isAxiosError(response.error)) {
|
||||
if (isFetchError(response.error)) {
|
||||
// When the response returns an object with 'error', handle axios error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
@ -894,7 +894,7 @@ const ReleaseDetailsPage = () => {
|
||||
}),
|
||||
});
|
||||
toggleEditReleaseModal();
|
||||
} else if (isAxiosError(response.error)) {
|
||||
} else if (isFetchError(response.error)) {
|
||||
// When the response returns an object with 'error', handle axios error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
@ -916,8 +916,8 @@ const ReleaseDetailsPage = () => {
|
||||
|
||||
if ('data' in response) {
|
||||
navigate('..');
|
||||
} else if (isAxiosError(response.error)) {
|
||||
// When the response returns an object with 'error', handle axios error
|
||||
} else if (isFetchError(response.error)) {
|
||||
// When the response returns an object with 'error', handle fetch error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
message: formatAPIError(response.error),
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
useNotification,
|
||||
useQueryParams,
|
||||
useRBAC,
|
||||
isFetchError,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
import { useLicenseLimits } from '@strapi/admin/strapi-admin/ee';
|
||||
import {
|
||||
@ -40,7 +41,6 @@ import { GetReleases, type Release } from '../../../shared/contracts/releases';
|
||||
import { RelativeTime as BaseRelativeTime } from '../components/RelativeTime';
|
||||
import { ReleaseModal, FormValues } from '../components/ReleaseModal';
|
||||
import { PERMISSIONS } from '../constants';
|
||||
import { isAxiosError } from '../services/baseQuery';
|
||||
import {
|
||||
useGetReleasesQuery,
|
||||
GetReleasesQueryParams,
|
||||
@ -274,7 +274,7 @@ const ReleasesPage = () => {
|
||||
trackUsage('didCreateRelease');
|
||||
|
||||
navigate(response.data.data.id.toString());
|
||||
} else if (isAxiosError(response.error)) {
|
||||
} else if (isFetchError(response.error)) {
|
||||
// When the response returns an object with 'error', handle axios error
|
||||
toggleNotification({
|
||||
type: 'danger',
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { getFetchClient, type FetchResponse, type FetchConfig } from '@strapi/admin/strapi-admin';
|
||||
|
||||
import type { AxiosError } from 'axios';
|
||||
import {
|
||||
getFetchClient,
|
||||
type FetchResponse,
|
||||
type FetchConfig,
|
||||
type FetchError,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
|
||||
export interface QueryArguments<TSend> {
|
||||
url: string;
|
||||
@ -39,11 +42,11 @@ const fetchBaseQuery = async <TData = unknown, TSend = unknown>({
|
||||
const result = await get<TData, FetchResponse<TData>>(url, config);
|
||||
return { data: result.data };
|
||||
} catch (error) {
|
||||
const err = error as AxiosError;
|
||||
const err = error as FetchError;
|
||||
/**
|
||||
* Handle error of type AxiosError
|
||||
* Handle error of type FetchError
|
||||
*
|
||||
* This format mimics what we want from an AxiosError which is what the
|
||||
* This format mimics what we want from an FetchError which is what the
|
||||
* rest of the app works with, except this format is "serializable" since
|
||||
* it goes into the redux store.
|
||||
*
|
||||
@ -51,7 +54,7 @@ const fetchBaseQuery = async <TData = unknown, TSend = unknown>({
|
||||
*/
|
||||
return {
|
||||
error: {
|
||||
status: err.response?.status,
|
||||
status: err.response?.error.status,
|
||||
code: err.code,
|
||||
response: {
|
||||
data: err.response?.data,
|
||||
@ -61,24 +64,4 @@ const fetchBaseQuery = async <TData = unknown, TSend = unknown>({
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Axios error
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* This asserts the errors from redux-toolkit-query are
|
||||
* axios errors so we can pass them to our utility functions
|
||||
* to correctly render error messages.
|
||||
*/
|
||||
const isAxiosError = (err: unknown): err is AxiosError<{ error: any }> => {
|
||||
return (
|
||||
typeof err === 'object' &&
|
||||
err !== null &&
|
||||
'response' in err &&
|
||||
typeof err.response === 'object' &&
|
||||
err.response !== null &&
|
||||
'data' in err.response
|
||||
);
|
||||
};
|
||||
|
||||
export { isAxiosError, fetchBaseQuery };
|
||||
export { fetchBaseQuery };
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { axiosBaseQuery, type UnknownApiError } from '../utils/api';
|
||||
import { fetchBaseQuery, type UnknownApiError } from '../utils/api';
|
||||
|
||||
const reviewWorkflowsApi = createApi({
|
||||
reducerPath: 'reviewWorkflowsApi',
|
||||
baseQuery: axiosBaseQuery(),
|
||||
baseQuery: fetchBaseQuery(),
|
||||
tagTypes: ['ReviewWorkflow', 'ReviewWorkflowStage'],
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { SerializedError } from '@reduxjs/toolkit';
|
||||
import { BaseQueryFn } from '@reduxjs/toolkit/query';
|
||||
import { getFetchClient, ApiError, FetchConfig } from '@strapi/admin/strapi-admin';
|
||||
import { isAxiosError, type AxiosRequestConfig } from 'axios';
|
||||
import {
|
||||
getFetchClient,
|
||||
isFetchError,
|
||||
type ApiError,
|
||||
type FetchConfig,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Axios data
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
export interface QueryArguments {
|
||||
url: string;
|
||||
method?: string;
|
||||
@ -22,7 +23,7 @@ export interface UnknownApiError {
|
||||
|
||||
export type BaseQueryError = ApiError | UnknownApiError;
|
||||
|
||||
const axiosBaseQuery =
|
||||
const fetchBaseQuery =
|
||||
(): BaseQueryFn<string | QueryArguments, unknown, BaseQueryError> =>
|
||||
async (query, { signal }) => {
|
||||
try {
|
||||
@ -69,16 +70,16 @@ const axiosBaseQuery =
|
||||
}
|
||||
} catch (err) {
|
||||
/**
|
||||
* Handle error of type AxiosError
|
||||
* Handle error of type FetchError
|
||||
*
|
||||
* This format mimics what we want from an AxiosError which is what the
|
||||
* This format mimics what we want from an FetchError which is what the
|
||||
* rest of the app works with, except this format is "serializable" since
|
||||
* it goes into the redux store.
|
||||
*
|
||||
* NOTE – passing the whole response will highlight this "serializability" issue.
|
||||
*/
|
||||
|
||||
if (isAxiosError(err)) {
|
||||
if (isFetchError(err)) {
|
||||
if (
|
||||
typeof err.response?.data === 'object' &&
|
||||
err.response?.data !== null &&
|
||||
@ -95,7 +96,7 @@ const axiosBaseQuery =
|
||||
name: 'UnknownError',
|
||||
message: 'There was an unknown error response from the API',
|
||||
details: err.response?.data,
|
||||
status: err.response?.status,
|
||||
status: err.response?.error.status,
|
||||
} as UnknownApiError,
|
||||
};
|
||||
}
|
||||
@ -117,4 +118,4 @@ const isBaseQueryError = (error: BaseQueryError | SerializedError): error is Bas
|
||||
return error.name !== undefined;
|
||||
};
|
||||
|
||||
export { axiosBaseQuery, isBaseQueryError };
|
||||
export { fetchBaseQuery, isBaseQueryError };
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { DocumentInfos } from '../types';
|
||||
import { axiosBaseQuery } from '../utils/baseQuery';
|
||||
import { baseQuery } from '../utils/baseQuery';
|
||||
|
||||
type SettingsInput = {
|
||||
restrictedAccess: boolean;
|
||||
@ -10,7 +10,7 @@ type SettingsInput = {
|
||||
|
||||
const api = createApi({
|
||||
reducerPath: 'plugin::documentation',
|
||||
baseQuery: axiosBaseQuery({
|
||||
baseQuery: baseQuery({
|
||||
options: {
|
||||
baseURL: '/documentation',
|
||||
},
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { SerializedError } from '@reduxjs/toolkit';
|
||||
import { BaseQueryFn } from '@reduxjs/toolkit/query';
|
||||
import { getFetchClient, ApiError, FetchConfig } from '@strapi/strapi/admin';
|
||||
import { isAxiosError, type AxiosRequestConfig } from 'axios';
|
||||
import {
|
||||
getFetchClient,
|
||||
isFetchError,
|
||||
type ApiError,
|
||||
type FetchConfig,
|
||||
} from '@strapi/strapi/admin';
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Axios data
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
export interface QueryArguments {
|
||||
url: string;
|
||||
method?: string;
|
||||
@ -22,7 +23,7 @@ export interface UnknownApiError {
|
||||
|
||||
export type BaseQueryError = ApiError | UnknownApiError;
|
||||
|
||||
const axiosBaseQuery =
|
||||
const baseQuery =
|
||||
(config: FetchConfig): BaseQueryFn<string | QueryArguments, unknown, BaseQueryError> =>
|
||||
async (query, { signal }) => {
|
||||
try {
|
||||
@ -69,16 +70,16 @@ const axiosBaseQuery =
|
||||
}
|
||||
} catch (err) {
|
||||
/**
|
||||
* Handle error of type AxiosError
|
||||
* Handle error of type FetchError
|
||||
*
|
||||
* This format mimics what we want from an AxiosError which is what the
|
||||
* This format mimics what we want from an FetchError which is what the
|
||||
* rest of the app works with, except this format is "serializable" since
|
||||
* it goes into the redux store.
|
||||
*
|
||||
* NOTE – passing the whole response will highlight this "serializability" issue.
|
||||
*/
|
||||
|
||||
if (isAxiosError(err)) {
|
||||
if (isFetchError(err)) {
|
||||
if (
|
||||
typeof err.response?.data === 'object' &&
|
||||
err.response?.data !== null &&
|
||||
@ -95,7 +96,7 @@ const axiosBaseQuery =
|
||||
name: 'UnknownError',
|
||||
message: 'There was an unknown error response from the API',
|
||||
details: err.response?.data,
|
||||
status: err.response?.status,
|
||||
status: err.response?.error?.status,
|
||||
} as UnknownApiError,
|
||||
};
|
||||
}
|
||||
@ -117,4 +118,4 @@ const isBaseQueryError = (error: BaseQueryError | SerializedError): error is Bas
|
||||
return error.name !== undefined;
|
||||
};
|
||||
|
||||
export { axiosBaseQuery, isBaseQueryError };
|
||||
export { baseQuery, isBaseQueryError };
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { axiosBaseQuery, type UnknownApiError } from '../utils/baseQuery';
|
||||
import { fetchBaseQuery, type UnknownApiError } from '../utils/baseQuery';
|
||||
|
||||
const i18nApi = createApi({
|
||||
reducerPath: 'i18nApi',
|
||||
baseQuery: axiosBaseQuery(),
|
||||
baseQuery: fetchBaseQuery(),
|
||||
tagTypes: ['Locale'],
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { SerializedError } from '@reduxjs/toolkit';
|
||||
import { BaseQueryFn } from '@reduxjs/toolkit/query';
|
||||
import { getFetchClient, ApiError, type FetchConfig } from '@strapi/admin/strapi-admin';
|
||||
import { isAxiosError } from 'axios';
|
||||
import {
|
||||
getFetchClient,
|
||||
isFetchError,
|
||||
type ApiError,
|
||||
type FetchConfig,
|
||||
} from '@strapi/admin/strapi-admin';
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Axios data
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
export interface QueryArguments {
|
||||
url: string;
|
||||
method?: string;
|
||||
@ -22,7 +23,7 @@ export interface UnknownApiError {
|
||||
|
||||
export type BaseQueryError = ApiError | UnknownApiError;
|
||||
|
||||
const axiosBaseQuery =
|
||||
const fetchBaseQuery =
|
||||
(): BaseQueryFn<string | QueryArguments, unknown, BaseQueryError> =>
|
||||
async (query, { signal }) => {
|
||||
try {
|
||||
@ -69,16 +70,16 @@ const axiosBaseQuery =
|
||||
}
|
||||
} catch (err) {
|
||||
/**
|
||||
* Handle error of type AxiosError
|
||||
* Handle error of type FetchError
|
||||
*
|
||||
* This format mimics what we want from an AxiosError which is what the
|
||||
* This format mimics what we want from an FetchError which is what the
|
||||
* rest of the app works with, except this format is "serializable" since
|
||||
* it goes into the redux store.
|
||||
*
|
||||
* NOTE – passing the whole response will highlight this "serializability" issue.
|
||||
*/
|
||||
|
||||
if (isAxiosError(err)) {
|
||||
if (isFetchError(err)) {
|
||||
if (
|
||||
typeof err.response?.data === 'object' &&
|
||||
err.response?.data !== null &&
|
||||
@ -95,7 +96,7 @@ const axiosBaseQuery =
|
||||
name: 'UnknownError',
|
||||
message: 'There was an unknown error response from the API',
|
||||
details: err.response?.data,
|
||||
status: err.response?.status,
|
||||
status: err.response?.error.status,
|
||||
} as UnknownApiError,
|
||||
};
|
||||
}
|
||||
@ -117,4 +118,4 @@ const isBaseQueryError = (error: BaseQueryError | SerializedError): error is Bas
|
||||
return error.name !== undefined;
|
||||
};
|
||||
|
||||
export { axiosBaseQuery, isBaseQueryError };
|
||||
export { fetchBaseQuery, isBaseQueryError };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user