From a1519ba737cd73fb087f9442272eb183957028c1 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 2 Jan 2024 13:26:54 +0700 Subject: [PATCH] chore(deps): upgrade axios [EE-6488] (#10885) Co-authored-by: Matt Hook --- app/portainer/services/axios.ts | 54 ++++++++++++++----- app/portainer/services/csrf.ts | 6 +-- app/react/azure/queries/useSubscriptions.ts | 2 +- app/react/azure/services/utils.ts | 17 +++++- .../CreateView/useCreateMutation.tsx | 4 +- .../docker/containers/containers.service.ts | 20 +++---- .../containers/queries/useUpdateContainer.ts | 4 +- .../images/queries/usePullImageMutation.ts | 4 +- .../networks/queries/useConnectContainer.ts | 4 +- .../portainer/gitops/queries/useCheckRepo.ts | 11 ++-- .../licenses/license.service.test.ts | 2 +- .../portainer/licenses/license.service.ts | 13 ++--- package.json | 2 +- yarn.lock | 30 ++++++++--- 14 files changed, 116 insertions(+), 57 deletions(-) diff --git a/app/portainer/services/axios.ts b/app/portainer/services/axios.ts index 1a26de8b3..32b4a981b 100644 --- a/app/portainer/services/axios.ts +++ b/app/portainer/services/axios.ts @@ -1,4 +1,4 @@ -import axiosOrigin, { AxiosError, AxiosRequestConfig } from 'axios'; +import axiosOrigin, { AxiosError, InternalAxiosRequestConfig } from 'axios'; import { setupCache } from 'axios-cache-adapter'; import { loadProgressBar } from 'axios-progress-bar'; @@ -18,14 +18,18 @@ export const cache = setupCache({ exclude: { query: false, // include urls with query params methods: ['put', 'patch', 'delete'], - filter: (req: AxiosRequestConfig) => { + filter: (req: InternalAxiosRequestConfig) => { // exclude caching get requests unless the path contains 'kubernetes' if (!req.url?.includes('kubernetes') && req.method === 'get') { return true; } - // exclude caching get with yaml accept header - if (req.headers?.Accept.includes('application/yaml')) { + const acceptHeader = req.headers?.Accept; + if ( + acceptHeader && + typeof acceptHeader === 'string' && + acceptHeader.includes('application/yaml') + ) { return true; } @@ -59,12 +63,12 @@ export default axios; export const agentTargetHeader = 'X-PortainerAgent-Target'; -export function agentInterceptor(config: AxiosRequestConfig) { +export function agentInterceptor(config: InternalAxiosRequestConfig) { if (!config.url || !config.url.includes('/docker/')) { return config; } - const newConfig = { headers: config.headers || {}, ...config }; + const newConfig = { ...config }; const target = portainerAgentTargetHeader(); if (target) { newConfig.headers[agentTargetHeader] = target; @@ -135,16 +139,40 @@ export function parseAxiosError( return new PortainerError(resultMsg, resultErr); } -export function defaultErrorParser(axiosError: AxiosError) { - const message = axiosError.response?.data.message || ''; - const details = axiosError.response?.data.details || message; - const error = new Error(message); +type DefaultAxiosErrorType = { + message: string; + details?: string; +}; + +export function defaultErrorParser(axiosError: AxiosError) { + if (isDefaultResponse(axiosError.response?.data)) { + const message = axiosError.response?.data.message || ''; + const details = axiosError.response?.data.details || message; + const error = new Error(message); + return { error, details }; + } + + const details = axiosError.response?.data + ? axiosError.response?.data.toString() + : ''; + const error = new Error('Axios error'); return { error, details }; } -export function isAxiosError< - ResponseType = { message: string; details: string }, ->(error: unknown): error is AxiosError { +export function isDefaultResponse( + data: unknown +): data is DefaultAxiosErrorType { + return ( + !!data && + typeof data === 'object' && + 'message' in data && + typeof data.message === 'string' + ); +} + +export function isAxiosError( + error: unknown +): error is AxiosError { return axiosOrigin.isAxiosError(error); } diff --git a/app/portainer/services/csrf.ts b/app/portainer/services/csrf.ts index 4dbb813cd..fcbe06e8c 100644 --- a/app/portainer/services/csrf.ts +++ b/app/portainer/services/csrf.ts @@ -1,4 +1,4 @@ -import { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; import { IHttpResponse } from 'angular'; import axios from './axios'; @@ -26,12 +26,12 @@ export function csrfTokenReaderInterceptorAngular( return config; } -export function csrfInterceptor(config: AxiosRequestConfig) { +export function csrfInterceptor(config: InternalAxiosRequestConfig) { if (!csrfToken) { return config; } - const newConfig = { headers: config.headers || {}, ...config }; + const newConfig = { ...config }; newConfig.headers['X-CSRF-Token'] = csrfToken; return newConfig; } diff --git a/app/react/azure/queries/useSubscriptions.ts b/app/react/azure/queries/useSubscriptions.ts index e5fbb587d..323fe27be 100644 --- a/app/react/azure/queries/useSubscriptions.ts +++ b/app/react/azure/queries/useSubscriptions.ts @@ -29,7 +29,7 @@ async function getSubscriptions(environmentId: EnvironmentId) { return data.value; } catch (e) { throw parseAxiosError( - e as Error, + e, 'Unable to retrieve subscriptions', azureErrorParser ); diff --git a/app/react/azure/services/utils.ts b/app/react/azure/services/utils.ts index e36e72f27..eb2898c43 100644 --- a/app/react/azure/services/utils.ts +++ b/app/react/azure/services/utils.ts @@ -1,9 +1,22 @@ import { AxiosError } from 'axios'; export function azureErrorParser(axiosError: AxiosError) { + if (!axiosError.response) { + const error = new Error('Failed azure request'); + return { + error, + details: axiosError.message, + }; + } + + const responseData = axiosError.response.data; const message = - (axiosError.response?.data?.error?.message as string) || - 'Failed azure request'; + responseData && + typeof responseData === 'object' && + 'error' in responseData && + typeof responseData.error === 'string' + ? responseData.error + : `Failed azure request: ${axiosError.response?.statusText}`; return { error: new Error(message), diff --git a/app/react/docker/containers/CreateView/useCreateMutation.tsx b/app/react/docker/containers/CreateView/useCreateMutation.tsx index 179daa3d7..54c8601c2 100644 --- a/app/react/docker/containers/CreateView/useCreateMutation.tsx +++ b/app/react/docker/containers/CreateView/useCreateMutation.tsx @@ -1,5 +1,5 @@ import { useMutation, useQueryClient } from 'react-query'; -import { AxiosRequestHeaders } from 'axios'; +import { RawAxiosRequestHeaders } from 'axios'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { @@ -282,7 +282,7 @@ async function createContainer( { nodeName }: { nodeName?: string } = {} ) { try { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; diff --git a/app/react/docker/containers/containers.service.ts b/app/react/docker/containers/containers.service.ts index d511a20f9..f9e2e1cda 100644 --- a/app/react/docker/containers/containers.service.ts +++ b/app/react/docker/containers/containers.service.ts @@ -1,4 +1,4 @@ -import { AxiosRequestHeaders } from 'axios'; +import { RawAxiosRequestHeaders } from 'axios'; import { EnvironmentId } from '@/react/portainer/environments/types'; import PortainerError from '@/portainer/error'; @@ -12,7 +12,7 @@ export async function startContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -34,7 +34,7 @@ export async function stopContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -49,7 +49,7 @@ export async function recreateContainer( pullImage: boolean, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -69,7 +69,7 @@ export async function restartContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -87,7 +87,7 @@ export async function killContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -101,7 +101,7 @@ export async function pauseContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -115,7 +115,7 @@ export async function resumeContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -134,7 +134,7 @@ export async function renameContainer( name: string, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; @@ -156,7 +156,7 @@ export async function removeContainer( }: { removeVolumes?: boolean; nodeName?: string } = {} ) { try { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; diff --git a/app/react/docker/containers/queries/useUpdateContainer.ts b/app/react/docker/containers/queries/useUpdateContainer.ts index 9e56d54cf..2d23cd361 100644 --- a/app/react/docker/containers/queries/useUpdateContainer.ts +++ b/app/react/docker/containers/queries/useUpdateContainer.ts @@ -1,5 +1,5 @@ import { Resources, RestartPolicy } from 'docker-types/generated/1.41'; -import { AxiosRequestHeaders } from 'axios'; +import { RawAxiosRequestHeaders } from 'axios'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { EnvironmentId } from '@/react/portainer/environments/types'; @@ -22,7 +22,7 @@ export async function updateContainer( config: UpdateConfig, { nodeName }: { nodeName?: string } = {} ) { - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; diff --git a/app/react/docker/images/queries/usePullImageMutation.ts b/app/react/docker/images/queries/usePullImageMutation.ts index 66271bc46..e1d00fd01 100644 --- a/app/react/docker/images/queries/usePullImageMutation.ts +++ b/app/react/docker/images/queries/usePullImageMutation.ts @@ -1,4 +1,4 @@ -import { AxiosRequestHeaders } from 'axios'; +import { RawAxiosRequestHeaders } from 'axios'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { EnvironmentId } from '@/react/portainer/environments/types'; @@ -31,7 +31,7 @@ export async function pullImage({ const imageURI = buildImageFullURI(image, registry); - const headers: AxiosRequestHeaders = { + const headers: RawAxiosRequestHeaders = { 'X-Registry-Auth': authenticationDetails, }; diff --git a/app/react/docker/networks/queries/useConnectContainer.ts b/app/react/docker/networks/queries/useConnectContainer.ts index b23182f48..f9733effd 100644 --- a/app/react/docker/networks/queries/useConnectContainer.ts +++ b/app/react/docker/networks/queries/useConnectContainer.ts @@ -1,5 +1,5 @@ import { EndpointSettings } from 'docker-types/generated/1.41'; -import { AxiosRequestHeaders } from 'axios'; +import { RawAxiosRequestHeaders } from 'axios'; import { useMutation, useQueryClient } from 'react-query'; import axios, { parseAxiosError } from '@/portainer/services/axios'; @@ -56,7 +56,7 @@ export async function connectContainer({ }; } - const headers: AxiosRequestHeaders = {}; + const headers: RawAxiosRequestHeaders = {}; if (nodeName) { headers['X-PortainerAgent-Target'] = nodeName; diff --git a/app/react/portainer/gitops/queries/useCheckRepo.ts b/app/react/portainer/gitops/queries/useCheckRepo.ts index 3486c03b2..8c1c3d854 100644 --- a/app/react/portainer/gitops/queries/useCheckRepo.ts +++ b/app/react/portainer/gitops/queries/useCheckRepo.ts @@ -1,7 +1,10 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import axios, { parseAxiosError } from '@/portainer/services/axios'; +import axios, { + isDefaultResponse, + parseAxiosError, +} from '@/portainer/services/axios'; interface Creds { username?: string; @@ -45,8 +48,10 @@ export async function checkRepo( ); return true; } catch (error) { - throw parseAxiosError(error as Error, '', (axiosError: AxiosError) => { - let details = axiosError.response?.data.details; + throw parseAxiosError(error, '', (axiosError: AxiosError) => { + let details = isDefaultResponse(axiosError.response?.data) + ? axiosError.response?.data.details || '' + : ''; const { creds = {} } = options; // If no credentials were provided alter error from git to indicate repository is not found or is private diff --git a/app/react/portainer/licenses/license.service.test.ts b/app/react/portainer/licenses/license.service.test.ts index 2bbf2b3f9..68091a4f2 100644 --- a/app/react/portainer/licenses/license.service.test.ts +++ b/app/react/portainer/licenses/license.service.test.ts @@ -37,7 +37,7 @@ describe('getLicenses', () => { const promise = getLicenses(); await promise.then(thenFn, catchFn); - expect(catchFn).toHaveBeenCalledWith(new Error(message)); + expect(catchFn).toHaveBeenCalledWith(new Error(details)); expect(thenFn).not.toHaveBeenCalled(); }); }); diff --git a/app/react/portainer/licenses/license.service.ts b/app/react/portainer/licenses/license.service.ts index 7d7dd250b..dec2d9864 100644 --- a/app/react/portainer/licenses/license.service.ts +++ b/app/react/portainer/licenses/license.service.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import { AxiosError } from 'axios'; -import axios from '@/portainer/services/axios'; +import axios, { parseAxiosError } from '@/portainer/services/axios'; import { License, LicenseInfo } from './types'; @@ -25,8 +25,7 @@ export async function getLicenses() { return data; } catch (e) { - const axiosError = e as AxiosError; - throw new Error(axiosError.response?.data.message); + throw parseAxiosError(e); } } @@ -55,7 +54,7 @@ export async function attachLicense(licenseKeys: string[]) { 'Your session has expired, please refresh the browser and log in again.' ); } - throw new Error(axiosError.response?.data.message); + throw parseAxiosError(e); } } @@ -76,8 +75,7 @@ export async function removeLicense(licenseKeys: string[]) { getLicenseInfo(); return data; } catch (e) { - const axiosError = e as AxiosError; - throw new Error(axiosError.response?.data.message); + throw parseAxiosError(e); } } @@ -105,8 +103,7 @@ export async function getLicenseInfo() { return info; } catch (e) { - const axiosError = e as AxiosError; - throw new Error(axiosError.response?.data.message); + throw parseAxiosError(e); } } diff --git a/package.json b/package.json index 4e7b44325..bbc076fde 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "angularjs-scroll-glue": "^2.2.0", "angularjs-slider": "^6.4.0", "angulartics": "^1.6.0", - "axios": "^0.24.0", + "axios": "^1.6.2", "axios-cache-adapter": "^2.7.3", "axios-progress-bar": "^1.2.0", "babel-plugin-angularjs-annotate": "^0.10.0", diff --git a/yarn.lock b/yarn.lock index 4fc3b701a..eff298427 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6483,12 +6483,14 @@ axios-progress-bar@^1.2.0: resolved "https://registry.yarnpkg.com/axios-progress-bar/-/axios-progress-bar-1.2.0.tgz#f9ee88dc9af977246be1ef07eedfa4c990c639c5" integrity sha512-PEgWb/b2SMyHnKJ/cxA46OdCuNeVlo8eqL0HxXPtz+6G/Jtpyo49icPbW+jpO1wUeDEjbqpseMoCyWxESxf5pA== -axios@^0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== +axios@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" + integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== dependencies: - follow-redirects "^1.14.4" + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" axobject-query@^3.1.1: version "3.1.1" @@ -9698,11 +9700,16 @@ focus-lock@^0.11.2: dependencies: tslib "^2.0.3" -follow-redirects@^1.0.0, follow-redirects@^1.14.4: +follow-redirects@^1.0.0: version "1.15.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== +follow-redirects@^1.15.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -9757,6 +9764,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + formik@^2.2.9: version "2.2.9" resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0" @@ -14021,7 +14037,7 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.0.0: +proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==