mirror of
https://github.com/strapi/strapi.git
synced 2025-09-08 16:16:21 +00:00
chore(content-releases): refactor rtk with type contracts (#18867)
This commit is contained in:
parent
1efe5dcdd1
commit
9f5e68ce57
@ -13,8 +13,8 @@ import { useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import { useCreateReleaseMutation } from '../modules/releaseSlice';
|
||||
import { isErrorAxiosError } from '../utils/errors';
|
||||
import { isAxiosError } from '../services/axios';
|
||||
import { useCreateReleaseMutation } from '../services/release';
|
||||
|
||||
const RELEASE_SCHEMA = yup.object({
|
||||
name: yup.string().required(),
|
||||
@ -56,7 +56,7 @@ export const AddReleaseDialog = ({ handleClose }: AddReleaseDialogProps) => {
|
||||
});
|
||||
|
||||
push(`/plugins/content-releases/${response.data.data.id}`);
|
||||
} else if (isErrorAxiosError(response.error)) {
|
||||
} else if (isAxiosError(response.error)) {
|
||||
// When the response returns an object with 'error', handle axios error
|
||||
toggleNotification({
|
||||
type: 'warning',
|
||||
|
@ -2,8 +2,8 @@ import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
||||
import { PaperPlane } from '@strapi/icons';
|
||||
|
||||
import { PERMISSIONS } from './constants';
|
||||
import { releaseApi } from './modules/releaseSlice';
|
||||
import { pluginId } from './pluginId';
|
||||
import { releaseApi } from './services/release';
|
||||
|
||||
import type { Plugin } from '@strapi/types';
|
||||
|
||||
|
87
packages/core/content-releases/admin/src/services/axios.ts
Normal file
87
packages/core/content-releases/admin/src/services/axios.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { getFetchClient } from '@strapi/helper-plugin';
|
||||
|
||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Axios data
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
export interface QueryArguments<TSend> {
|
||||
url: string;
|
||||
method: 'PUT' | 'GET' | 'POST' | 'DELETE';
|
||||
data?: TSend;
|
||||
config?: AxiosRequestConfig<TSend>;
|
||||
}
|
||||
|
||||
const axiosBaseQuery = async <TData = any, TSend = any>({
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
config,
|
||||
}: QueryArguments<TSend>) => {
|
||||
try {
|
||||
const { get, post, del, put } = getFetchClient();
|
||||
|
||||
if (method === 'POST') {
|
||||
const result = await post<TData, AxiosResponse<TData>, TSend>(url, data, config);
|
||||
return { data: result.data };
|
||||
}
|
||||
|
||||
if (method === 'DELETE') {
|
||||
const result = await del<TData, AxiosResponse<TData>, TSend>(url, config);
|
||||
return { data: result.data };
|
||||
}
|
||||
|
||||
if (method === 'PUT') {
|
||||
const result = await put<TData, AxiosResponse<TData>, TSend>(url, data, config);
|
||||
return { data: result.data };
|
||||
}
|
||||
|
||||
/**
|
||||
* Default is GET.
|
||||
*/
|
||||
const result = await get<TData, AxiosResponse<TData>, TSend>(url, config);
|
||||
return { data: result.data };
|
||||
} catch (error) {
|
||||
const err = error as AxiosError;
|
||||
/**
|
||||
* Handle error of type AxiosError
|
||||
*
|
||||
* This format mimics what we want from an AxiosError 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.
|
||||
*/
|
||||
return {
|
||||
error: {
|
||||
status: err.response?.status,
|
||||
code: err.code,
|
||||
response: {
|
||||
data: err.response?.data,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* 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, axiosBaseQuery };
|
@ -1,13 +1,10 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { pluginId } from '../pluginId';
|
||||
import { axiosBaseQuery } from '../utils/data';
|
||||
|
||||
import type {
|
||||
CreateRelease,
|
||||
GetReleases,
|
||||
ReleaseDataResponse,
|
||||
} from '../../../shared/contracts/releases';
|
||||
import { axiosBaseQuery } from './axios';
|
||||
|
||||
import type { CreateRelease, GetReleases } from '../../../shared/contracts/releases';
|
||||
|
||||
const releaseApi = createApi({
|
||||
reducerPath: pluginId,
|
||||
@ -24,7 +21,7 @@ const releaseApi = createApi({
|
||||
},
|
||||
providesTags: ['Releases'],
|
||||
}),
|
||||
createRelease: build.mutation<{ data: ReleaseDataResponse }, CreateRelease.Request['body']>({
|
||||
createRelease: build.mutation<CreateRelease.Response, CreateRelease.Request['body']>({
|
||||
query(data) {
|
||||
return {
|
||||
url: '/content-releases',
|
@ -1,61 +0,0 @@
|
||||
import { getFetchClient } from '@strapi/helper-plugin';
|
||||
|
||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
|
||||
export interface QueryArguments<TSend> {
|
||||
url: string;
|
||||
method: 'PUT' | 'GET' | 'POST' | 'DELETE';
|
||||
data?: TSend;
|
||||
config?: AxiosRequestConfig<TSend>;
|
||||
}
|
||||
|
||||
const axiosBaseQuery = async <TData = any, TSend = any>({
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
config,
|
||||
}: QueryArguments<TSend>) => {
|
||||
try {
|
||||
const { get, post, del, put } = getFetchClient();
|
||||
|
||||
if (method === 'POST') {
|
||||
const res = await post<TData, AxiosResponse<TData>, TSend>(url, data, config);
|
||||
return { data: res.data };
|
||||
}
|
||||
if (method === 'DELETE') {
|
||||
const res = await del<TData, AxiosResponse<TData>, TSend>(url, config);
|
||||
return { data: res.data };
|
||||
}
|
||||
if (method === 'PUT') {
|
||||
const res = await put<TData, AxiosResponse<TData>, TSend>(url, data, config);
|
||||
return { data: res.data };
|
||||
}
|
||||
|
||||
/**
|
||||
* Default is GET.
|
||||
*/
|
||||
const res = await get<TData, AxiosResponse<TData>, TSend>(url, config);
|
||||
return { data: res.data };
|
||||
} catch (error) {
|
||||
const err = error as AxiosError;
|
||||
|
||||
/**
|
||||
* This format mimics what we want from an AxiosError 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.
|
||||
*/
|
||||
return {
|
||||
error: {
|
||||
status: err.response?.status,
|
||||
code: err.code,
|
||||
response: {
|
||||
data: err.response?.data,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export { axiosBaseQuery };
|
@ -1,19 +0,0 @@
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
/**
|
||||
* 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 isErrorAxiosError = (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 { isErrorAxiosError };
|
@ -19,7 +19,7 @@ import { IntlProvider } from 'react-intl';
|
||||
import { Provider } from 'react-redux';
|
||||
import { MemoryRouter, MemoryRouterProps } from 'react-router-dom';
|
||||
|
||||
import { releaseApi } from '../src/modules/releaseSlice';
|
||||
import { releaseApi } from '../src/services/release';
|
||||
|
||||
import { server } from './server';
|
||||
import { initialState } from './store';
|
||||
|
@ -2,7 +2,12 @@ import type Koa from 'koa';
|
||||
import { errors } from '@strapi/utils';
|
||||
import { RELEASE_MODEL_UID } from '../constants';
|
||||
import { validateRelease } from './validation/release';
|
||||
import type { CreateRelease, UpdateRelease, GetRelease, Release } from '../../../shared/contracts/releases';
|
||||
import type {
|
||||
CreateRelease,
|
||||
UpdateRelease,
|
||||
GetRelease,
|
||||
Release,
|
||||
} from '../../../shared/contracts/releases';
|
||||
import type { UserInfo } from '../../../shared/types';
|
||||
import { getService } from '../utils';
|
||||
|
||||
@ -34,7 +39,7 @@ const releaseController = {
|
||||
};
|
||||
});
|
||||
|
||||
ctx.body = { data, pagination };
|
||||
ctx.body = { data, meta: { pagination } };
|
||||
},
|
||||
|
||||
async findOne(ctx: Koa.Context) {
|
||||
@ -100,7 +105,7 @@ const releaseController = {
|
||||
ctx.body = {
|
||||
data: await permissionsManager.sanitizeOutput(release),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default releaseController;
|
||||
|
@ -34,11 +34,7 @@ export declare namespace CreateReleaseAction {
|
||||
}
|
||||
|
||||
export interface Response {
|
||||
data:
|
||||
| ReleaseAction
|
||||
| {
|
||||
data: null;
|
||||
error: errors.ApplicationError | errors.ValidationError | errors.NotFoundError;
|
||||
};
|
||||
data: ReleaseAction;
|
||||
error?: errors.ApplicationError | errors.ValidationError | errors.NotFoundError;
|
||||
}
|
||||
}
|
||||
|
@ -31,15 +31,13 @@ export declare namespace GetReleases {
|
||||
query?: Partial<Pick<Pagination, 'page' | 'pageSize'>>;
|
||||
}
|
||||
|
||||
export type Response =
|
||||
| {
|
||||
data: ReleaseDataResponse[];
|
||||
pagination: Pagination;
|
||||
}
|
||||
| {
|
||||
data: null;
|
||||
error: errors.ApplicationError;
|
||||
};
|
||||
export interface Response {
|
||||
data: ReleaseDataResponse[];
|
||||
meta: {
|
||||
pagination: Pagination;
|
||||
};
|
||||
error?: errors.ApplicationError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,14 +53,10 @@ export declare namespace GetRelease {
|
||||
};
|
||||
}
|
||||
|
||||
export type Response =
|
||||
| {
|
||||
data: ReleaseDataResponse;
|
||||
}
|
||||
| {
|
||||
data: null;
|
||||
error: errors.ApplicationError | errors.NotFoundError;
|
||||
};
|
||||
export interface Response {
|
||||
data: ReleaseDataResponse;
|
||||
error?: errors.ApplicationError | errors.NotFoundError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,9 +72,10 @@ export declare namespace CreateRelease {
|
||||
};
|
||||
}
|
||||
|
||||
export type Response =
|
||||
| { data: ReleaseDataResponse }
|
||||
| { data: null; error: errors.ApplicationError | errors.ValidationError };
|
||||
export interface Response {
|
||||
data: ReleaseDataResponse;
|
||||
error?: errors.ApplicationError | errors.ValidationError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +94,8 @@ export declare namespace UpdateRelease {
|
||||
};
|
||||
}
|
||||
|
||||
export type Response =
|
||||
| { data: ReleaseDataResponse }
|
||||
| { data: null; error: errors.ApplicationError | errors.ValidationError };
|
||||
}
|
||||
export interface Response {
|
||||
data: ReleaseDataResponse;
|
||||
error?: errors.ApplicationError | errors.ValidationError;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user