diff --git a/package-lock.json b/package-lock.json index 0ee70b664..5fe343913 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,7 @@ "@tiptap/pm": "^2.1.12", "@tiptap/react": "^2.1.12", "@tiptap/starter-kit": "^2.1.12", - "axios": "^0.21.3", + "axios": "^1.6.5", "bluebird": "^3.7.1", "bootstrap": "^4.6.0", "cheerio": "^1.0.0-rc.10", @@ -5129,6 +5129,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@datadog/datadog-ci/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/@datadog/datadog-ci/node_modules/chalk": { "version": "3.0.0", "license": "MIT", @@ -13313,12 +13321,33 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "license": "MIT", + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/axobject-query": { "version": "3.2.1", "dev": true, @@ -19586,14 +19615,15 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, diff --git a/package.json b/package.json index 09be5e442..f8ca47f9d 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@tiptap/pm": "^2.1.12", "@tiptap/react": "^2.1.12", "@tiptap/starter-kit": "^2.1.12", - "axios": "^0.21.3", + "axios": "^1.6.5", "bluebird": "^3.7.1", "bootstrap": "^4.6.0", "cheerio": "^1.0.0-rc.10", diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 0274a1774..8044e62e5 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -97,8 +97,9 @@ const Header = ({ bg="white" h="4rem" w="100%" + spacing="0.5rem" > - + {!showButton ? ( - + {backButtonTextFromParams || backButtonText} @@ -128,11 +129,18 @@ const Header = ({ {/* */} {title ? ( - - {title} + + + {title} + ) : null} - + ( {isShowStagingBuildStatusEnabled && ( diff --git a/src/hooks/directoryHooks/contactUsHooks/useGetContactUsHook.ts b/src/hooks/directoryHooks/contactUsHooks/useGetContactUsHook.ts index 4f8eb70ec..944c94cdb 100644 --- a/src/hooks/directoryHooks/contactUsHooks/useGetContactUsHook.ts +++ b/src/hooks/directoryHooks/contactUsHooks/useGetContactUsHook.ts @@ -3,6 +3,8 @@ import { UseQueryResult, useQuery } from "react-query" import { GET_CONTACT_US_KEY } from "constants/queryKeys" +import { getAxiosErrorMessage } from "utils/axios" + import { ContactUsService } from "services" import { ContactUsDto } from "types/contactUs" import { useErrorToast, DEFAULT_RETRY_MSG } from "utils" @@ -18,7 +20,9 @@ export const useGetContactUsHook = ( onError: (err: AxiosError) => { errorToast({ id: "get-contact-us-error", - description: `Your Contact Us page details could not be retrieved. ${DEFAULT_RETRY_MSG}. Error: ${err.response?.data.error.message}`, + description: `Your Contact Us page details could not be retrieved. ${DEFAULT_RETRY_MSG}. Error: ${getAxiosErrorMessage( + err + )}`, }) }, } diff --git a/src/hooks/directoryHooks/contactUsHooks/useUpdateContactUsHook.ts b/src/hooks/directoryHooks/contactUsHooks/useUpdateContactUsHook.ts index 7c4ad7180..03833d688 100644 --- a/src/hooks/directoryHooks/contactUsHooks/useUpdateContactUsHook.ts +++ b/src/hooks/directoryHooks/contactUsHooks/useUpdateContactUsHook.ts @@ -3,9 +3,10 @@ import { UseMutationResult, useQueryClient, useMutation } from "react-query" import { GET_CONTACT_US_KEY } from "constants/queryKeys" +import { getAxiosErrorMessage } from "utils/axios" + import { ContactUsService } from "services" import { ContactUsFrontMatter } from "types/contactUs" -import { MiddlewareError } from "types/error" import { useSuccessToast, useErrorToast, DEFAULT_RETRY_MSG } from "utils" interface UpdateContactUsParams { @@ -15,11 +16,7 @@ interface UpdateContactUsParams { export const useUpdateContactUsHook = ( siteName: string -): UseMutationResult< - void, - AxiosError, - UpdateContactUsParams -> => { +): UseMutationResult => { const queryClient = useQueryClient() const successToast = useSuccessToast() const errorToast = useErrorToast() @@ -42,10 +39,12 @@ export const useUpdateContactUsHook = ( description: "Contact Us page updated successfully", }) }, - onError: (err: AxiosError) => { + onError: (err) => { errorToast({ id: "update-homepage-error", - description: `Could not update Contact Us page. ${DEFAULT_RETRY_MSG}. Error: ${err.response?.data.error.message}`, + description: `Could not update Contact Us page. ${DEFAULT_RETRY_MSG}. Error: ${getAxiosErrorMessage( + err + )}`, }) }, } diff --git a/src/hooks/homepageHooks/useGetHomepageHook.ts b/src/hooks/homepageHooks/useGetHomepageHook.ts index ac29cd806..d86c9a8a4 100644 --- a/src/hooks/homepageHooks/useGetHomepageHook.ts +++ b/src/hooks/homepageHooks/useGetHomepageHook.ts @@ -3,6 +3,8 @@ import { UseQueryResult, useQuery } from "react-query" import { GET_HOMEPAGE_KEY } from "constants/queryKeys" +import { getAxiosErrorMessage } from "utils/axios" + import { HomepageService } from "services" import { HomepageDto } from "types/homepage" import { useErrorToast, DEFAULT_RETRY_MSG } from "utils" @@ -18,7 +20,9 @@ export const useGetHomepageHook = ( onError: (err: AxiosError) => { errorToast({ id: "get-homepage-error", - description: `Your homepage could not be retrieved. ${DEFAULT_RETRY_MSG}. Error: ${err.response?.data.error.message}`, + description: `Your homepage could not be retrieved. ${DEFAULT_RETRY_MSG}. Error: ${getAxiosErrorMessage( + err + )}`, }) }, } diff --git a/src/hooks/homepageHooks/useUpdateHomepageHook.ts b/src/hooks/homepageHooks/useUpdateHomepageHook.ts index 31b7a2b5e..afe774a28 100644 --- a/src/hooks/homepageHooks/useUpdateHomepageHook.ts +++ b/src/hooks/homepageHooks/useUpdateHomepageHook.ts @@ -8,18 +8,20 @@ import { import { GET_HOMEPAGE_KEY } from "constants/queryKeys" +import { getAxiosErrorMessage } from "utils/axios" + import { HomepageService } from "services" -import { MiddlewareError } from "types/error" +import { MiddlewareErrorDto } from "types/error" import { HomepageDto } from "types/homepage" import { useSuccessToast, useErrorToast, DEFAULT_RETRY_MSG } from "utils" export const useUpdateHomepageHook = ( siteName: string, mutationOptions?: Omit< - UseMutationOptions, HomepageDto>, + UseMutationOptions, HomepageDto>, "mutationFn" | "mutationKey" > -): UseMutationResult, HomepageDto> => { +): UseMutationResult, HomepageDto> => { const queryClient = useQueryClient() const successToast = useSuccessToast() const errorToast = useErrorToast() @@ -44,7 +46,9 @@ export const useUpdateHomepageHook = ( if (err.response?.status !== 409) { errorToast({ id: "update-homepage-error", - description: `Could not update homepage. ${DEFAULT_RETRY_MSG}. Error: ${err.response?.data?.message}`, + description: `Could not update homepage. ${DEFAULT_RETRY_MSG}. Error: ${getAxiosErrorMessage( + err + )}`, }) } if (mutationOptions?.onError) diff --git a/src/hooks/mediaHooks/useDeleteMultipleMediaHook.tsx b/src/hooks/mediaHooks/useDeleteMultipleMediaHook.tsx index f0449866d..a22d68308 100644 --- a/src/hooks/mediaHooks/useDeleteMultipleMediaHook.tsx +++ b/src/hooks/mediaHooks/useDeleteMultipleMediaHook.tsx @@ -24,35 +24,22 @@ const deleteMultipleMedia = async ( ) => { const mediaService = new MediaService({ apiClient: apiService }) - await selectedMediaDto - .map((deleteMedia) => { - const pathTokens = deleteMedia.filePath.split("/") - - const reqParams = { - siteName, - mediaDirectoryName: pathTokens.slice(0, -1).join("%2F"), - fileName: pathTokens.pop(), - sha: deleteMedia.sha, + await mediaService + .deleteMultiple( + { siteName }, + { + items: selectedMediaDto.map(({ filePath, sha }) => { + return { + filePath, + sha, + } + }), } - - return reqParams - }) - .reduce( - (acc, curr) => - acc - .then(() => { - const { sha, ...rest } = curr - mediaService.delete(rest, { sha }).catch((error) => { - // We want to continue even if some of the media files fail to delete - // because there is no turning back now if we fail (the earlier files - // would have been deleted) - return error - }) as Promise - }) - // This wait is necessary to avoid the repo lock - .then(() => new Promise((resolve) => setTimeout(resolve, 500))), - Promise.resolve() ) + // Note: Unfortunately, we have to wait for GitHub to finish deleting the files + // before we can refetch the list of files in the directory. Otherwise, the + // refetch will return the files that were just deleted. + .then(() => new Promise((resolve) => setTimeout(resolve, 1000))) } export const useDeleteMultipleMediaHook = ( @@ -71,7 +58,19 @@ export const useDeleteMultipleMediaHook = ( ...mutationOptions, onSettled: (data, error, variables, context) => { queryClient.invalidateQueries([LIST_MEDIA_DIRECTORY_FILES_KEY]) - queryClient.invalidateQueries([GET_ALL_MEDIA_FILES_KEY]) + // Note: We choose to remove here because once the files are deleted, + // refetching will definitely cause a 404 Not Found error, which is what + // we want to avoid + variables.forEach(({ filePath }) => { + const directoryName = encodeURIComponent( + filePath.split("/").slice(0, -1).join("/") + ) + const fileName = encodeURIComponent(filePath.split("/").pop() || "") + queryClient.removeQueries([ + GET_ALL_MEDIA_FILES_KEY, + { siteName: params.siteName, directoryName, name: fileName }, + ]) + }) if (mutationOptions?.onSettled) mutationOptions.onSettled(data, error, variables, context) }, diff --git a/src/hooks/navHooks/useUpdateNavHook.ts b/src/hooks/navHooks/useUpdateNavHook.ts index 9b92e8927..3d72d8cf5 100644 --- a/src/hooks/navHooks/useUpdateNavHook.ts +++ b/src/hooks/navHooks/useUpdateNavHook.ts @@ -8,8 +8,10 @@ import { RESOURCE_ROOM_NAME_KEY, } from "constants/queryKeys" +import { getAxiosErrorMessage } from "utils/axios" + import { NavService } from "services" -import { MiddlewareError } from "types/error" +import { MiddlewareErrorDto } from "types/error" import { CollectionNav, NavDto, @@ -27,7 +29,7 @@ export interface UpdateNavParams { export const useUpdateNavHook = ( siteName: string -): UseMutationResult, UpdateNavParams> => { +): UseMutationResult, UpdateNavParams> => { const queryClient = useQueryClient() const successToast = useSuccessToast() const errorToast = useErrorToast() @@ -56,10 +58,12 @@ export const useUpdateNavHook = ( description: "Navigation bar updated successfully", }) }, - onError: (err: AxiosError) => { + onError: (err: AxiosError) => { errorToast({ id: "update-nav-error", - description: `Could not update navigation bar. ${DEFAULT_RETRY_MSG}. Error: ${err.response?.data.error.message}`, + description: `Could not update navigation bar. ${DEFAULT_RETRY_MSG}. Error: ${getAxiosErrorMessage( + err + )}`, }) }, } diff --git a/src/hooks/settingsHooks/useUpdateSettings.ts b/src/hooks/settingsHooks/useUpdateSettings.ts index 6e7cbeebc..e185fd130 100644 --- a/src/hooks/settingsHooks/useUpdateSettings.ts +++ b/src/hooks/settingsHooks/useUpdateSettings.ts @@ -2,7 +2,7 @@ import { AxiosError } from "axios" import _ from "lodash" import { useMutation, UseMutationResult, useQueryClient } from "react-query" -import { SETTINGS_CONTENT_KEY } from "constants/queryKeys" +import { SETTINGS_CONTENT_KEY, STAGING_URL_KEY } from "constants/queryKeys" import * as SettingsService from "services/SettingsService" @@ -58,6 +58,7 @@ export const useUpdateSettings = ( { onSettled: () => { queryClient.invalidateQueries([SETTINGS_CONTENT_KEY, siteName]) + queryClient.invalidateQueries([STAGING_URL_KEY, siteName]) }, onSuccess, onError, diff --git a/src/layouts/ReviewRequest/components/RequestOverview/RequestOverview.tsx b/src/layouts/ReviewRequest/components/RequestOverview/RequestOverview.tsx index 24ceb2205..eef9fb5eb 100644 --- a/src/layouts/ReviewRequest/components/RequestOverview/RequestOverview.tsx +++ b/src/layouts/ReviewRequest/components/RequestOverview/RequestOverview.tsx @@ -113,7 +113,12 @@ const ItemName = ({ name, path }: Pick) => { const theme = useTheme() return ( - + {name} = table.getRowModel().rows[index] // NOTE: This is guaranteed to exist because the table will filter // so that the index we're referencing is within the filtered items. - return row - .getVisibleCells() - .map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - )) + return row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + )) }} /> diff --git a/src/services/MediaService.jsx b/src/services/MediaService.jsx index 058bc3d2c..880a46d42 100644 --- a/src/services/MediaService.jsx +++ b/src/services/MediaService.jsx @@ -46,4 +46,14 @@ export class MediaService { .delete(this.getMediaEndpoint(apiParams), { data: body }) .then((res) => res.data) } + + async deleteMultiple(apiParams, { items }) { + const { siteName } = apiParams + const body = { + items, + } + return this.apiClient + .delete(`/sites/${siteName}/media`, { data: body }) + .then((res) => res.data) + } } diff --git a/src/utils/axios.ts b/src/utils/axios.ts index f6d1d9022..43436f408 100644 --- a/src/utils/axios.ts +++ b/src/utils/axios.ts @@ -1,6 +1,6 @@ import { AxiosError } from "axios" -import { ErrorDto, IsomerErrorDto } from "types/error" +import { ErrorDto, IsomerErrorDto, MiddlewareErrorDto } from "types/error" import { DEFAULT_RETRY_MSG } from "utils" export const isAxiosError = (err: unknown): err is AxiosError => { @@ -25,8 +25,18 @@ const isIsomerExternalError = ( ) } +const isIsomerBackendMiddlewareError = ( + err: AxiosError +): err is AxiosError => { + return !!(err as AxiosError).response?.data.error.message +} + export const getAxiosErrorMessage = ( - error: null | AxiosError | AxiosError, + error: + | null + | AxiosError + | AxiosError + | AxiosError, defaultErrorMessage = DEFAULT_RETRY_MSG ): string => { if (!error) return "" @@ -35,9 +45,12 @@ export const getAxiosErrorMessage = ( return `${error.response?.data.error?.code}: ${error.response?.data.error?.message}` } + if (isIsomerBackendMiddlewareError(error)) { + return error.response?.data?.error?.message || defaultErrorMessage + } if (isBackendError(error)) { return error.response?.data.message || defaultErrorMessage } - return error.response?.data?.error?.message || defaultErrorMessage + return defaultErrorMessage }