forked from openshift-assisted/assisted-installer-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MGMT-15374: Unties common-to-ocm dependencies (openshift-assisted#2279)
* Breaks common-to-ocm dependencies * Breaks circular dependencies Moves fileSize and stringToJson to lib/common/utils.ts in order to break circular dependencies
- Loading branch information
Showing
43 changed files
with
743 additions
and
78 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
libs/ui-lib/lib/cim/components/ClusterDeployment/ShortCapacitySummary.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import axios, { AxiosInstance } from 'axios'; | ||
import applyCaseMiddleware from 'axios-case-converter'; | ||
import { camelCase } from 'camel-case'; | ||
|
||
// conforms basePath in swagger.json | ||
export const BASE_PATH = '/api/assisted-install'; | ||
|
||
// Prevent axios converter to change object keys from '4.7-fc2' to '4_7Fc2' | ||
const axiosCaseConverterOptions = { | ||
caseFunctions: { | ||
camel: (input: string) => | ||
camelCase(input, { | ||
stripRegexp: /[^A-Z0-9.-]+/gi, | ||
}), | ||
}, | ||
}; | ||
|
||
const getDefaultClient = (withoutConverter = false) => { | ||
const client = axios.create(); | ||
client.interceptors.request.use((cfg) => ({ | ||
...cfg, | ||
url: `${process.env.AIUI_APP_API_ROOT || ''}${cfg.url || ''}`, | ||
})); | ||
if (withoutConverter) { | ||
return client; | ||
} else { | ||
return applyCaseMiddleware(client, axiosCaseConverterOptions); | ||
} | ||
}; | ||
|
||
let client: AxiosInstance = getDefaultClient(); | ||
let clientWithoutConverter: AxiosInstance = getDefaultClient(true); | ||
let ocmClient: AxiosInstance | null; | ||
let isInOcm = false; | ||
|
||
const aiInterceptor = (client: AxiosInstance) => { | ||
client.interceptors.request.use((cfg) => ({ | ||
...cfg, | ||
url: `${BASE_PATH}${cfg.url || ''}`, | ||
})); | ||
return client; | ||
}; | ||
|
||
const getOcmClient = () => ocmClient; | ||
|
||
export const setAuthInterceptor = (authInterceptor: (client: AxiosInstance) => AxiosInstance) => { | ||
ocmClient = authInterceptor(axios.create()); | ||
isInOcm = true; | ||
client = applyCaseMiddleware( | ||
aiInterceptor(authInterceptor(axios.create())), | ||
axiosCaseConverterOptions, | ||
); | ||
clientWithoutConverter = aiInterceptor(authInterceptor(axios.create())); | ||
}; | ||
|
||
export { client, getOcmClient, isInOcm, clientWithoutConverter }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { AxiosError } from 'axios'; | ||
import { hasProp, isNonNullObject } from '../types/typescriptExtensions'; | ||
|
||
// Implementation from Axios.isAxiosError v0.29.2, which is not available in OCM's version 0.17.x | ||
export function isAxiosError<T = unknown, D = unknown>( | ||
payload: unknown, | ||
): payload is AxiosError<T, D> { | ||
return ( | ||
isNonNullObject(payload) && hasProp(payload, 'isAxiosError') && payload.isAxiosError === true | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './axiosClient'; | ||
export * from './types'; | ||
export * from './utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,114 @@ | ||
import camelCase from 'lodash-es/camelCase.js'; | ||
|
||
export const stringToJSON = <T>(jsonString: string | undefined): T | undefined => { | ||
let jsObject: T | undefined; | ||
if (jsonString) { | ||
try { | ||
const camelCased = jsonString.replace( | ||
/"([\w-]+)":/g, | ||
(_match, offset: string) => `"${camelCase(offset)}":`, | ||
); | ||
jsObject = JSON.parse(camelCased) as T; | ||
} catch (e) { | ||
// console.error('Failed to parse api string', e, jsonString); | ||
import Axios, { AxiosError } from 'axios'; | ||
import * as Sentry from '@sentry/browser'; | ||
import pick from 'lodash-es/pick.js'; | ||
import { Error as APIError, InfraError } from './types'; | ||
import { getErrorMessage } from '../utils'; | ||
import { isAxiosError } from './axiosExtensions'; | ||
import { isInOcm } from './axiosClient'; | ||
|
||
export const FETCH_ABORTED_ERROR_CODE = 'ERR_CANCELED'; | ||
export const FETCH_CONNECTIVITY_ERROR_CODE = 'CONNECTIVITY_ERROR'; | ||
export const SERVER_ERROR_CODE = 'SERVER_ERROR'; | ||
export const PAGE_RELOAD_ERROR = 'ECONNABORTED'; | ||
|
||
type OnError = (arg0: unknown) => void; | ||
|
||
export const handleApiError = (error: unknown, onError?: OnError): void => { | ||
if (Axios.isCancel(error)) { | ||
captureException(error, 'Request canceled', Sentry.Severity.Info); | ||
} else if (isApiError(error)) { | ||
const config = error.config || { url: '', method: '' }; | ||
let message = `URL: ${JSON.stringify(config.url, null, 1)}\n`; | ||
message += `Method: ${JSON.stringify(config.method, null, 1)}\n`; | ||
if (error.response) { | ||
// The request was made and the server responded with a status code | ||
// that falls out of the range of 2xx | ||
message += `Status: ${error.response.status}\n`; | ||
message += `Response: ${JSON.stringify( | ||
pick(error.response.data, ['code', 'message', 'reason']), | ||
null, | ||
1, | ||
)}\n`; | ||
} else if (error.request) { | ||
// The request was made but no response was received | ||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of | ||
// http.ClientRequest in node.js | ||
message += `Status Code: ${JSON.stringify( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
error.request.__sentry_xhr__.status_code, | ||
null, | ||
1, | ||
)}`; | ||
} | ||
captureException(error, message); | ||
} else { | ||
// console.info('Empty api string received.'); | ||
captureException(error); | ||
} | ||
if (onError) return onError(error); | ||
}; | ||
|
||
export const getApiErrorMessage = (error: unknown): string => { | ||
if (isApiError(error)) { | ||
return error.response?.data?.message || error.response?.data.reason || error.message; | ||
} | ||
return getErrorMessage(error); | ||
}; | ||
|
||
return jsObject; | ||
export const isUnknownServerError = (error: Error): boolean => { | ||
return getApiErrorCode(error) === SERVER_ERROR_CODE; | ||
}; | ||
|
||
export const getApiErrorCode = (error: Error | AxiosError): string | number => { | ||
if (!isAxiosError(error)) { | ||
return FETCH_CONNECTIVITY_ERROR_CODE; | ||
} | ||
// Aborted request | ||
if (error.code === FETCH_ABORTED_ERROR_CODE) { | ||
return FETCH_ABORTED_ERROR_CODE; | ||
} | ||
|
||
// Page is reloading and some request has been interrupted | ||
if (error.code === PAGE_RELOAD_ERROR) { | ||
return ''; | ||
} | ||
|
||
const responseStatus = error.response?.status || 0; | ||
|
||
// Error status | ||
if (responseStatus >= 500 && responseStatus < 600) { | ||
return SERVER_ERROR_CODE; | ||
} | ||
if (responseStatus >= 400 && responseStatus < 500) { | ||
return responseStatus; | ||
} | ||
// A generic connectivity issue | ||
return FETCH_CONNECTIVITY_ERROR_CODE; | ||
}; | ||
|
||
export type APIErrorMixin = InfraError & APIError; | ||
export type AIAxiosErrorType = AxiosError<APIErrorMixin, APIErrorMixin>; | ||
|
||
export const isApiError = (error: unknown): error is AIAxiosErrorType => { | ||
if (!isAxiosError<AIAxiosErrorType, AIAxiosErrorType>(error)) { | ||
return false; | ||
} | ||
return typeof error.response?.data === 'object'; | ||
}; | ||
|
||
export const captureException = ( | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
error: any, | ||
message?: string, | ||
severity: Sentry.Severity = Sentry.Severity.Error, | ||
) => { | ||
if (isInOcm) { | ||
message && Sentry.captureMessage(message, severity); | ||
Sentry.captureException(error); | ||
} else { | ||
// severity === Sentry.Severity.Error | ||
// ? console.error(message, error) | ||
// : console.warn(message, error); | ||
} | ||
}; | ||
|
||
export const removeProtocolFromURL = (url = '') => url.replace(/^(http|https):\/\//, ''); |
Oops, something went wrong.