diff --git a/packages/core/content-releases/admin/src/components/AddReleaseDialog.tsx b/packages/core/content-releases/admin/src/components/AddReleaseDialog.tsx index dd436c0845..b2c453441a 100644 --- a/packages/core/content-releases/admin/src/components/AddReleaseDialog.tsx +++ b/packages/core/content-releases/admin/src/components/AddReleaseDialog.tsx @@ -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', diff --git a/packages/core/content-releases/admin/src/index.ts b/packages/core/content-releases/admin/src/index.ts index a9378ad4ee..3f60051159 100644 --- a/packages/core/content-releases/admin/src/index.ts +++ b/packages/core/content-releases/admin/src/index.ts @@ -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'; diff --git a/packages/core/content-releases/admin/src/services/axios.ts b/packages/core/content-releases/admin/src/services/axios.ts new file mode 100644 index 0000000000..c6c75874f2 --- /dev/null +++ b/packages/core/content-releases/admin/src/services/axios.ts @@ -0,0 +1,87 @@ +import { getFetchClient } from '@strapi/helper-plugin'; + +import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; + +/* ------------------------------------------------------------------------------------------------- + * Axios data + * -----------------------------------------------------------------------------------------------*/ +export interface QueryArguments { + url: string; + method: 'PUT' | 'GET' | 'POST' | 'DELETE'; + data?: TSend; + config?: AxiosRequestConfig; +} + +const axiosBaseQuery = async ({ + url, + method, + data, + config, +}: QueryArguments) => { + try { + const { get, post, del, put } = getFetchClient(); + + if (method === 'POST') { + const result = await post, TSend>(url, data, config); + return { data: result.data }; + } + + if (method === 'DELETE') { + const result = await del, TSend>(url, config); + return { data: result.data }; + } + + if (method === 'PUT') { + const result = await put, TSend>(url, data, config); + return { data: result.data }; + } + + /** + * Default is GET. + */ + const result = await get, 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 }; diff --git a/packages/core/content-releases/admin/src/modules/releaseSlice.ts b/packages/core/content-releases/admin/src/services/release.ts similarity index 76% rename from packages/core/content-releases/admin/src/modules/releaseSlice.ts rename to packages/core/content-releases/admin/src/services/release.ts index b38cf0242d..15683f88e5 100644 --- a/packages/core/content-releases/admin/src/modules/releaseSlice.ts +++ b/packages/core/content-releases/admin/src/services/release.ts @@ -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({ query(data) { return { url: '/content-releases', diff --git a/packages/core/content-releases/admin/src/utils/data.ts b/packages/core/content-releases/admin/src/utils/data.ts deleted file mode 100644 index 824831616d..0000000000 --- a/packages/core/content-releases/admin/src/utils/data.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { getFetchClient } from '@strapi/helper-plugin'; - -import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; - -export interface QueryArguments { - url: string; - method: 'PUT' | 'GET' | 'POST' | 'DELETE'; - data?: TSend; - config?: AxiosRequestConfig; -} - -const axiosBaseQuery = async ({ - url, - method, - data, - config, -}: QueryArguments) => { - try { - const { get, post, del, put } = getFetchClient(); - - if (method === 'POST') { - const res = await post, TSend>(url, data, config); - return { data: res.data }; - } - if (method === 'DELETE') { - const res = await del, TSend>(url, config); - return { data: res.data }; - } - if (method === 'PUT') { - const res = await put, TSend>(url, data, config); - return { data: res.data }; - } - - /** - * Default is GET. - */ - const res = await get, 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 }; diff --git a/packages/core/content-releases/admin/src/utils/errors.ts b/packages/core/content-releases/admin/src/utils/errors.ts deleted file mode 100644 index 4700e722cb..0000000000 --- a/packages/core/content-releases/admin/src/utils/errors.ts +++ /dev/null @@ -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 }; diff --git a/packages/core/content-releases/admin/tests/utils.tsx b/packages/core/content-releases/admin/tests/utils.tsx index 963f624d55..9fb052fc69 100644 --- a/packages/core/content-releases/admin/tests/utils.tsx +++ b/packages/core/content-releases/admin/tests/utils.tsx @@ -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'; diff --git a/packages/core/content-releases/server/src/controllers/release.ts b/packages/core/content-releases/server/src/controllers/release.ts index bc04cabefb..753c6f9546 100644 --- a/packages/core/content-releases/server/src/controllers/release.ts +++ b/packages/core/content-releases/server/src/controllers/release.ts @@ -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; diff --git a/packages/core/content-releases/shared/contracts/release-actions.ts b/packages/core/content-releases/shared/contracts/release-actions.ts index f6bd956f2c..1e38940fb2 100644 --- a/packages/core/content-releases/shared/contracts/release-actions.ts +++ b/packages/core/content-releases/shared/contracts/release-actions.ts @@ -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; } } diff --git a/packages/core/content-releases/shared/contracts/releases.ts b/packages/core/content-releases/shared/contracts/releases.ts index 89a53916ee..97c8c3c436 100644 --- a/packages/core/content-releases/shared/contracts/releases.ts +++ b/packages/core/content-releases/shared/contracts/releases.ts @@ -31,15 +31,13 @@ export declare namespace GetReleases { query?: Partial>; } - 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 }; -} \ No newline at end of file + export interface Response { + data: ReleaseDataResponse; + error?: errors.ApplicationError | errors.ValidationError; + } +}