mirror of
https://github.com/strapi/strapi.git
synced 2025-09-09 08:39:45 +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 { useHistory } from 'react-router-dom';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
|
||||||
import { useCreateReleaseMutation } from '../modules/releaseSlice';
|
import { isAxiosError } from '../services/axios';
|
||||||
import { isErrorAxiosError } from '../utils/errors';
|
import { useCreateReleaseMutation } from '../services/release';
|
||||||
|
|
||||||
const RELEASE_SCHEMA = yup.object({
|
const RELEASE_SCHEMA = yup.object({
|
||||||
name: yup.string().required(),
|
name: yup.string().required(),
|
||||||
@ -56,7 +56,7 @@ export const AddReleaseDialog = ({ handleClose }: AddReleaseDialogProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
push(`/plugins/content-releases/${response.data.data.id}`);
|
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
|
// When the response returns an object with 'error', handle axios error
|
||||||
toggleNotification({
|
toggleNotification({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
|
@ -2,8 +2,8 @@ import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
|||||||
import { PaperPlane } from '@strapi/icons';
|
import { PaperPlane } from '@strapi/icons';
|
||||||
|
|
||||||
import { PERMISSIONS } from './constants';
|
import { PERMISSIONS } from './constants';
|
||||||
import { releaseApi } from './modules/releaseSlice';
|
|
||||||
import { pluginId } from './pluginId';
|
import { pluginId } from './pluginId';
|
||||||
|
import { releaseApi } from './services/release';
|
||||||
|
|
||||||
import type { Plugin } from '@strapi/types';
|
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 { createApi } from '@reduxjs/toolkit/query/react';
|
||||||
|
|
||||||
import { pluginId } from '../pluginId';
|
import { pluginId } from '../pluginId';
|
||||||
import { axiosBaseQuery } from '../utils/data';
|
|
||||||
|
|
||||||
import type {
|
import { axiosBaseQuery } from './axios';
|
||||||
CreateRelease,
|
|
||||||
GetReleases,
|
import type { CreateRelease, GetReleases } from '../../../shared/contracts/releases';
|
||||||
ReleaseDataResponse,
|
|
||||||
} from '../../../shared/contracts/releases';
|
|
||||||
|
|
||||||
const releaseApi = createApi({
|
const releaseApi = createApi({
|
||||||
reducerPath: pluginId,
|
reducerPath: pluginId,
|
||||||
@ -24,7 +21,7 @@ const releaseApi = createApi({
|
|||||||
},
|
},
|
||||||
providesTags: ['Releases'],
|
providesTags: ['Releases'],
|
||||||
}),
|
}),
|
||||||
createRelease: build.mutation<{ data: ReleaseDataResponse }, CreateRelease.Request['body']>({
|
createRelease: build.mutation<CreateRelease.Response, CreateRelease.Request['body']>({
|
||||||
query(data) {
|
query(data) {
|
||||||
return {
|
return {
|
||||||
url: '/content-releases',
|
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 { Provider } from 'react-redux';
|
||||||
import { MemoryRouter, MemoryRouterProps } from 'react-router-dom';
|
import { MemoryRouter, MemoryRouterProps } from 'react-router-dom';
|
||||||
|
|
||||||
import { releaseApi } from '../src/modules/releaseSlice';
|
import { releaseApi } from '../src/services/release';
|
||||||
|
|
||||||
import { server } from './server';
|
import { server } from './server';
|
||||||
import { initialState } from './store';
|
import { initialState } from './store';
|
||||||
|
@ -2,7 +2,12 @@ import type Koa from 'koa';
|
|||||||
import { errors } from '@strapi/utils';
|
import { errors } from '@strapi/utils';
|
||||||
import { RELEASE_MODEL_UID } from '../constants';
|
import { RELEASE_MODEL_UID } from '../constants';
|
||||||
import { validateRelease } from './validation/release';
|
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 type { UserInfo } from '../../../shared/types';
|
||||||
import { getService } from '../utils';
|
import { getService } from '../utils';
|
||||||
|
|
||||||
@ -34,7 +39,7 @@ const releaseController = {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.body = { data, pagination };
|
ctx.body = { data, meta: { pagination } };
|
||||||
},
|
},
|
||||||
|
|
||||||
async findOne(ctx: Koa.Context) {
|
async findOne(ctx: Koa.Context) {
|
||||||
@ -100,7 +105,7 @@ const releaseController = {
|
|||||||
ctx.body = {
|
ctx.body = {
|
||||||
data: await permissionsManager.sanitizeOutput(release),
|
data: await permissionsManager.sanitizeOutput(release),
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default releaseController;
|
export default releaseController;
|
||||||
|
@ -34,11 +34,7 @@ export declare namespace CreateReleaseAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Response {
|
export interface Response {
|
||||||
data:
|
data: ReleaseAction;
|
||||||
| ReleaseAction
|
error?: errors.ApplicationError | errors.ValidationError | errors.NotFoundError;
|
||||||
| {
|
|
||||||
data: null;
|
|
||||||
error: errors.ApplicationError | errors.ValidationError | errors.NotFoundError;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,15 +31,13 @@ export declare namespace GetReleases {
|
|||||||
query?: Partial<Pick<Pagination, 'page' | 'pageSize'>>;
|
query?: Partial<Pick<Pagination, 'page' | 'pageSize'>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response =
|
export interface Response {
|
||||||
| {
|
|
||||||
data: ReleaseDataResponse[];
|
data: ReleaseDataResponse[];
|
||||||
|
meta: {
|
||||||
pagination: Pagination;
|
pagination: Pagination;
|
||||||
}
|
|
||||||
| {
|
|
||||||
data: null;
|
|
||||||
error: errors.ApplicationError;
|
|
||||||
};
|
};
|
||||||
|
error?: errors.ApplicationError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,14 +53,10 @@ export declare namespace GetRelease {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response =
|
export interface Response {
|
||||||
| {
|
|
||||||
data: ReleaseDataResponse;
|
data: ReleaseDataResponse;
|
||||||
|
error?: errors.ApplicationError | errors.NotFoundError;
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
data: null;
|
|
||||||
error: errors.ApplicationError | errors.NotFoundError;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,9 +72,10 @@ export declare namespace CreateRelease {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response =
|
export interface Response {
|
||||||
| { data: ReleaseDataResponse }
|
data: ReleaseDataResponse;
|
||||||
| { data: null; error: errors.ApplicationError | errors.ValidationError };
|
error?: errors.ApplicationError | errors.ValidationError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,7 +94,8 @@ export declare namespace UpdateRelease {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response =
|
export interface Response {
|
||||||
| { data: ReleaseDataResponse }
|
data: ReleaseDataResponse;
|
||||||
| { data: null; error: errors.ApplicationError | errors.ValidationError };
|
error?: errors.ApplicationError | errors.ValidationError;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user