diff --git a/libs/ui-lib/lib/common/api/axiosClient.ts b/libs/ui-lib/lib/common/api/axiosClient.ts new file mode 100644 index 0000000000..ee3d4212b7 --- /dev/null +++ b/libs/ui-lib/lib/common/api/axiosClient.ts @@ -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 }; diff --git a/libs/ui-lib/lib/common/api/axiosExtensions.ts b/libs/ui-lib/lib/common/api/axiosExtensions.ts new file mode 100644 index 0000000000..3a0cb65e8a --- /dev/null +++ b/libs/ui-lib/lib/common/api/axiosExtensions.ts @@ -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( + payload: unknown, +): payload is AxiosError { + return ( + isNonNullObject(payload) && hasProp(payload, 'isAxiosError') && payload.isAxiosError === true + ); +} diff --git a/libs/ui-lib/lib/common/api/index.ts b/libs/ui-lib/lib/common/api/index.ts index 6d5a6ef495..b2cb512213 100644 --- a/libs/ui-lib/lib/common/api/index.ts +++ b/libs/ui-lib/lib/common/api/index.ts @@ -1,2 +1,3 @@ +export * from './axiosClient'; export * from './types'; export * from './utils'; diff --git a/libs/ui-lib/lib/common/api/utils.ts b/libs/ui-lib/lib/common/api/utils.ts index 88102d8dea..6bcd35f027 100644 --- a/libs/ui-lib/lib/common/api/utils.ts +++ b/libs/ui-lib/lib/common/api/utils.ts @@ -1,4 +1,116 @@ import camelCase from 'lodash-es/camelCase.js'; +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 '../../common/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 { + 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); +}; + +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; + +export const isApiError = (error: unknown): error is AIAxiosErrorType => { + if (!isAxiosError(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 stringToJSON = (jsonString: string | undefined): T | undefined => { let jsObject: T | undefined; diff --git a/libs/ui-lib/lib/common/apis/assisted-service/ClustersAPI.ts b/libs/ui-lib/lib/common/apis/assisted-service/ClustersAPI.ts new file mode 100644 index 0000000000..efa76c0562 --- /dev/null +++ b/libs/ui-lib/lib/common/apis/assisted-service/ClustersAPI.ts @@ -0,0 +1,253 @@ +import { client } from '../../api/axiosClient'; +import { + Cluster, + ClusterCreateParams, + ClusterDefaultConfig, + V2ClusterUpdateParams, + Credentials, + Host, + ImportClusterParams, + PlatformType, + PreflightHardwareRequirements, + PresignedUrl, + ListManifests, + CreateManifestParams, + UpdateManifestParams, + Manifest, + LogsType, +} from '../../api/types'; +import { AxiosResponse } from 'axios'; + +let _getRequestAbortController = new AbortController(); + +export type ClustersAPIGetPresignedOptions = { + clusterId: string; + fileName: 'logs' | 'kubeconfig' | 'kubeconfig-noingress'; + hostId?: string; + logsType?: LogsType; +}; + +const ClustersAPI = { + abortLastGetRequest() { + _getRequestAbortController.abort(); + /** + * The AbortController.signal can only be aborted once per instance. + * Therefore in order for other requests to be also abortable we need + * to create a new instance when this event occurs + */ + _getRequestAbortController = new AbortController(); + }, + + makeBaseURI(clusterId?: Cluster['id']) { + return `/v2/clusters/${clusterId ? clusterId : ''}`; + }, + + makeDownloadsBaseURI(clusterId: Cluster['id']) { + return `${ClustersAPI.makeBaseURI(clusterId)}/downloads`; + }, + + makeLogsBaseURI(clusterId: Cluster['id']) { + return `${ClustersAPI.makeBaseURI(clusterId)}/logs`; + }, + + makeDownloadsCredentialsBaseURI(clusterId: Cluster['id']) { + return `${ClustersAPI.makeDownloadsBaseURI(clusterId)}/credentials`; + }, + + makeDownloadsCredentialsPresignedBaseURI(clusterId: Cluster['id']) { + return `${ClustersAPI.makeDownloadsBaseURI(clusterId)}/credentials-presigned`; + }, + + makeDownloadsFilesPresignedBaseURI(clusterId: Cluster['id']) { + return `${ClustersAPI.makeDownloadsBaseURI(clusterId)}/files-presigned`; + }, + + makeSupportedPlatformsBaseURI(clusterId: Cluster['id']) { + return `${ClustersAPI.makeBaseURI(clusterId)}/supported-platforms`; + }, + + makeActionsBaseURI(clusterId: string) { + return `${ClustersAPI.makeBaseURI(clusterId)}/actions`; + }, + + downloadClusterCredentials(clusterId: Cluster['id'], fileName: string) { + return client.get( + `${ClustersAPI.makeDownloadsCredentialsBaseURI(clusterId)}?file_name=${fileName}`, + { + responseType: 'blob', + headers: { + Accept: 'application/octet-stream', + }, + }, + ); + }, + + getPresignedForClusterCredentials({ + clusterId, + fileName, + hostId, + logsType, + }: ClustersAPIGetPresignedOptions) { + const queryParams = `${logsType ? `&logs_type=${logsType}` : ''}${ + hostId ? `&host_id=${hostId}` : '' + }`; + return client.get( + `${ClustersAPI.makeDownloadsCredentialsPresignedBaseURI( + clusterId, + )}?file_name=${fileName}${queryParams}`, + ); + }, + + getPresignedForClusterFiles({ + clusterId, + fileName, + hostId, + logsType, + }: ClustersAPIGetPresignedOptions) { + const queryParams = `${logsType ? `&logs_type=${logsType}` : ''}${ + hostId ? `&host_id=${hostId}` : '' + }`; + return client.get( + `${ClustersAPI.makeDownloadsFilesPresignedBaseURI( + clusterId, + )}?file_name=${fileName}${queryParams}`, + ); + }, + + list() { + return client.get(`${ClustersAPI.makeBaseURI()}`, { + signal: _getRequestAbortController.signal, + }); + }, + + get(clusterId: Cluster['id']) { + return client.get(`${ClustersAPI.makeBaseURI(clusterId)}`, { + signal: _getRequestAbortController.signal, + }); + }, + + getSupportedPlatforms(clusterId: Cluster['id']) { + return client.get(`${ClustersAPI.makeSupportedPlatformsBaseURI(clusterId)}`); + }, + + getPreflightRequirements(clusterId: Cluster['id']) { + return client.get( + `${ClustersAPI.makeBaseURI(clusterId)}/preflight-requirements`, + ); + }, + + getDefaultConfig() { + return client.get(`${ClustersAPI.makeBaseURI()}/default-config`); + }, + + deregister(clusterId: Cluster['id']) { + return client.delete(`${ClustersAPI.makeBaseURI(clusterId)}`); + }, + + register(params: ClusterCreateParams) { + return client.post, ClusterCreateParams>( + `${ClustersAPI.makeBaseURI()}`, + params, + ); + }, + + update(clusterId: Cluster['id'], params: V2ClusterUpdateParams) { + return client.patch, V2ClusterUpdateParams>( + `${ClustersAPI.makeBaseURI(clusterId)}`, + params, + ); + }, + + install(clusterId: Cluster['id']) { + return client.post, never>( + `${ClustersAPI.makeActionsBaseURI(clusterId)}/install`, + ); + }, + + cancel(clusterId: Cluster['id']) { + return client.post, never>( + `${ClustersAPI.makeActionsBaseURI(clusterId)}/cancel`, + ); + }, + + reset(clusterId: Cluster['id']) { + return client.post, never>( + `${ClustersAPI.makeActionsBaseURI(clusterId)}/reset`, + ); + }, + + registerAddHosts(params: ImportClusterParams) { + return client.post, ImportClusterParams>( + `${ClustersAPI.makeBaseURI()}import`, + params, + ); + }, + + allowAddHosts(clusterId: Cluster['id']) { + return client.post, never>( + `${ClustersAPI.makeActionsBaseURI(clusterId)}/allow-add-workers`, + ); + }, + + downloadLogs(clusterId: Cluster['id'], hostId?: Host['id']) { + const queryParams = `logs_type=${!hostId ? 'all' : `host&host_id=${hostId}`}`; + return client.get(`${ClustersAPI.makeLogsBaseURI(clusterId)}?${queryParams}`, { + responseType: 'blob', + headers: { + Accept: 'application/octet-stream', + }, + }); + }, + + getCredentials(clusterId: Cluster['id']) { + return client.get(`${ClustersAPI.makeBaseURI(clusterId)}/credentials`); + }, + + listByOpenshiftId(openshiftId: Cluster['openshiftClusterId']) { + return client.get( + `${ClustersAPI.makeBaseURI()}?openshift_cluster_id=${openshiftId || ''}`, + ); + }, + + listBySubscriptionIds(subscriptionIds: Cluster['amsSubscriptionId'][]) { + return client.get( + `${ClustersAPI.makeBaseURI()}?ams_subscription_ids=${subscriptionIds.toString()}`, + ); + }, + + getManifests(clusterId: Cluster['id']) { + return client.get(`${ClustersAPI.makeBaseURI(clusterId)}/manifests`); + }, + createCustomManifest(clusterId: Cluster['id'], params: CreateManifestParams) { + return client.post, CreateManifestParams>( + `${ClustersAPI.makeBaseURI(clusterId)}/manifests`, + params, + ); + }, + removeCustomManifest(clusterId: Cluster['id'], folderName: string, fileName: string) { + return client.delete( + `${ClustersAPI.makeBaseURI(clusterId)}/manifests?folder=${folderName}&file_name=${fileName}`, + ); + }, + getManifestContent(clusterId: Cluster['id'], folderName: string, fileName: string) { + return client.get( + `${ClustersAPI.makeBaseURI( + clusterId, + )}/manifests/files?folder=${folderName}&file_name=${fileName}`, + { + responseType: 'blob', + headers: { + Accept: 'application/octet-stream', + }, + }, + ); + }, + updateCustomManifest(clusterId: Cluster['id'], params: UpdateManifestParams) { + return client.patch, UpdateManifestParams>( + `${ClustersAPI.makeBaseURI(clusterId)}/manifests`, + params, + ); + }, +}; + +export default ClustersAPI; diff --git a/libs/ui-lib/lib/common/apis/assisted-service/ComponentVersionsAPI.ts b/libs/ui-lib/lib/common/apis/assisted-service/ComponentVersionsAPI.ts new file mode 100644 index 0000000000..e4a54d6804 --- /dev/null +++ b/libs/ui-lib/lib/common/apis/assisted-service/ComponentVersionsAPI.ts @@ -0,0 +1,14 @@ +import { client } from '../../api'; +import { ListVersions } from '../../../common'; + +const ComponentVersionsAPI = { + makeBaseURI() { + return `/v2/component-versions`; + }, + + list() { + return client.get(`${ComponentVersionsAPI.makeBaseURI()}`); + }, +}; + +export default ComponentVersionsAPI; diff --git a/libs/ui-lib/lib/common/apis/assisted-service/EventsAPI.ts b/libs/ui-lib/lib/common/apis/assisted-service/EventsAPI.ts new file mode 100644 index 0000000000..b30b80f38e --- /dev/null +++ b/libs/ui-lib/lib/common/apis/assisted-service/EventsAPI.ts @@ -0,0 +1,57 @@ +import { EventList, V2Events } from '../../../common'; +import { client } from '../../api'; + +let _getRequestAbortController = new AbortController(); + +const EventsAPI = { + makeBaseURI() { + return '/v2/events'; + }, + + abort() { + _getRequestAbortController.abort(); + /* + * The AbortController.signal can only be aborted once per instance. + * Therefore in order for other requests to be also abortable we need + * to create a new instance when this event occurs + */ + _getRequestAbortController = new AbortController(); + }, + + createParams({ + clusterId, + hostIds, + infraEnvId, + severities, + deletedHosts = true, + clusterLevel = true, + message, + limit = 10, + offset = 0, + }: V2Events) { + let params = '?'; + params += 'order=descending&'; + + params += clusterId ? `cluster_id=${clusterId}&` : ''; + params += hostIds?.length ? `host_ids=${hostIds.join(',')}&` : ''; + params += infraEnvId ? `infra_env_id=${infraEnvId}&` : ''; + + params += severities?.length ? `severities=${severities?.join(',')}&` : ''; + params += deletedHosts ? `deleted_hosts=${deletedHosts.toString()}&` : ''; + params += clusterLevel ? `cluster_level=${clusterLevel.toString()}&` : ''; + params += message ? `message=${encodeURIComponent(message)}&` : ''; + + params += `limit=${limit}&`; + params += `offset=${offset}`; + + return params; + }, + + list(options: V2Events) { + return client.get(`${EventsAPI.makeBaseURI()}${EventsAPI.createParams(options)}`, { + signal: _getRequestAbortController.signal, + }); + }, +}; + +export default EventsAPI; diff --git a/libs/ui-lib/lib/common/apis/assisted-service/HostsAPI.ts b/libs/ui-lib/lib/common/apis/assisted-service/HostsAPI.ts new file mode 100644 index 0000000000..864531f2aa --- /dev/null +++ b/libs/ui-lib/lib/common/apis/assisted-service/HostsAPI.ts @@ -0,0 +1,63 @@ +import { Host, HostUpdateParams, InfraEnv } from '../../../common'; +import { client } from '../../api'; +import { AxiosResponse } from 'axios'; +import InfraEnvsAPI from './InfraEnvsAPI'; + +let _getRequestAbortController = new AbortController(); + +const HostsAPI = { + abortLastGetRequest() { + _getRequestAbortController.abort(); + /** + * The AbortController.signal can only be aborted once per instance. + * Therefore in order for other requests to be also abortable we need + * to create a new instance when this event occurs + */ + _getRequestAbortController = new AbortController(); + }, + + makeBaseURI(infraEnvId: InfraEnv['id'], hostId?: Host['id']) { + return `${InfraEnvsAPI.makeBaseURI(infraEnvId)}/hosts/${hostId ? hostId : ''}`; + }, + + makeActionsBaseURI(infraEnvId: InfraEnv['id'], hostId: Host['id']) { + return `${HostsAPI.makeBaseURI(infraEnvId, hostId)}/actions`; + }, + + list(infraEnvId: InfraEnv['id']) { + return client.get(`${HostsAPI.makeBaseURI(infraEnvId)}`, { + signal: _getRequestAbortController.signal, + }); + }, + + get(infraEnvId: InfraEnv['id'], hostId: Host['id']) { + return client.get(`${HostsAPI.makeBaseURI(infraEnvId, hostId)}`, { + signal: _getRequestAbortController.signal, + }); + }, + + update(infraEnvId: InfraEnv['id'], hostId: Host['id'], params: HostUpdateParams) { + return client.patch, HostUpdateParams>( + `${HostsAPI.makeBaseURI(infraEnvId, hostId)}`, + params, + ); + }, + + deregister(infraEnvId: InfraEnv['id'], hostId: Host['id']) { + return client.delete(`${HostsAPI.makeBaseURI(infraEnvId, hostId)}`); + }, + + reset(infraEnvId: InfraEnv['id'], hostId: Host['id']) { + return client.post, never>( + `${HostsAPI.makeActionsBaseURI(infraEnvId, hostId)}/reset`, + ); + }, + + installHost(infraEnvId: InfraEnv['id'], hostId: Host['id']) { + return client.post, never>( + `${HostsAPI.makeActionsBaseURI(infraEnvId, hostId)}/install`, + ); + }, +}; + +export default HostsAPI; diff --git a/libs/ui-lib/lib/common/apis/assisted-service/InfraEnvsAPI.ts b/libs/ui-lib/lib/common/apis/assisted-service/InfraEnvsAPI.ts new file mode 100644 index 0000000000..a7370e03ad --- /dev/null +++ b/libs/ui-lib/lib/common/apis/assisted-service/InfraEnvsAPI.ts @@ -0,0 +1,72 @@ +import { client } from '../../api'; +import { + InfraEnv, + InfraEnvCreateParams, + PresignedUrl, + InfraEnvUpdateParams, +} from '../../../common'; +import { AxiosResponse } from 'axios'; + +let _getRequestAbortController = new AbortController(); + +const InfraEnvsAPI = { + abortLastGetRequest() { + _getRequestAbortController.abort(); + /* + * The AbortController.signal can only be aborted once per instance. + * Therefore in order for other requests to be also abortable we need + * to create a new instance when this event occurs + */ + _getRequestAbortController = new AbortController(); + }, + + makeBaseURI(infraEnvId?: InfraEnv['id']) { + return `/v2/infra-envs/${infraEnvId ? infraEnvId : ''}`; + }, + + /** + * Retrieves the list of infra-envs. + * @param clusterId If provided, returns only infra-envs which directly reference the given clusterId. + */ + list(clusterId = '') { + const query = clusterId && `?${new URLSearchParams({ ['cluster_id']: clusterId }).toString()}`; + return client.get(`${InfraEnvsAPI.makeBaseURI()}${query}`, { + signal: _getRequestAbortController.signal, + }); + }, + + get(infraEnvId: InfraEnv['id']) { + return client.get(`${InfraEnvsAPI.makeBaseURI(infraEnvId)}`, { + signal: _getRequestAbortController.signal, + }); + }, + + update(infraEnvId: InfraEnv['id'], params: InfraEnvUpdateParams) { + return client.patch, InfraEnvUpdateParams>( + `${InfraEnvsAPI.makeBaseURI(infraEnvId)}`, + params, + ); + }, + + register(params: InfraEnvCreateParams) { + return client.post, InfraEnvCreateParams>( + `${InfraEnvsAPI.makeBaseURI()}`, + params, + ); + }, + + deregister(infraEnvId: InfraEnv['id']) { + return client.delete(`${InfraEnvsAPI.makeBaseURI(infraEnvId)}`); + }, + + getImageUrl(infraEnvId: InfraEnv['id']) { + return client.get(`${InfraEnvsAPI.makeBaseURI(infraEnvId)}/downloads/image-url`); + }, + getIpxeImageUrl(infraEnvId: InfraEnv['id']) { + return client.get( + `${InfraEnvsAPI.makeBaseURI(infraEnvId)}/downloads/files-presigned?file_name=ipxe-script`, + ); + }, +}; + +export default InfraEnvsAPI; diff --git a/libs/ui-lib/lib/ocm/services/apis/ManagedDomainsAPI.tsx b/libs/ui-lib/lib/common/apis/assisted-service/ManagedDomainsAPI.ts similarity index 100% rename from libs/ui-lib/lib/ocm/services/apis/ManagedDomainsAPI.tsx rename to libs/ui-lib/lib/common/apis/assisted-service/ManagedDomainsAPI.ts diff --git a/libs/ui-lib/lib/common/apis/assisted-service/SupportedOpenshiftVersionsAPI.ts b/libs/ui-lib/lib/common/apis/assisted-service/SupportedOpenshiftVersionsAPI.ts new file mode 100644 index 0000000000..c4f6f9176c --- /dev/null +++ b/libs/ui-lib/lib/common/apis/assisted-service/SupportedOpenshiftVersionsAPI.ts @@ -0,0 +1,14 @@ +import { client } from '../../api/axiosClient'; +import { OpenshiftVersion } from '../../../common'; + +const SupportedOpenshiftVersionsAPI = { + makeBaseURI() { + return `/v2/openshift-versions`; + }, + + list() { + return client.get(`${SupportedOpenshiftVersionsAPI.makeBaseURI()}`); + }, +}; + +export default SupportedOpenshiftVersionsAPI; diff --git a/libs/ui-lib/lib/common/components/clusterDetail/KubeconfigDownload.tsx b/libs/ui-lib/lib/common/components/clusterDetail/KubeconfigDownload.tsx index a8adc7cebc..e85d0e5464 100644 --- a/libs/ui-lib/lib/common/components/clusterDetail/KubeconfigDownload.tsx +++ b/libs/ui-lib/lib/common/components/clusterDetail/KubeconfigDownload.tsx @@ -4,11 +4,9 @@ import { Button, ButtonVariant } from '@patternfly/react-core'; import { canDownloadKubeconfig } from '../hosts/utils'; import { useAlerts } from '../AlertsContextProvider'; import { Cluster } from '../../api/types'; -/* eslint-disable no-restricted-imports */ -import { isInOcm } from '../../../ocm/api/axiosClient'; -import { getApiErrorMessage, handleApiError } from '../../../ocm/api/utils'; -import ClustersAPI from '../../../ocm/services/apis/ClustersAPI'; -/* eslint-enable no-restricted-imports */ +import { isInOcm } from '../../api/axiosClient'; +import { getApiErrorMessage, handleApiError } from '../../api/utils'; +import ClustersAPI from '../../apis/assisted-service/ClustersAPI'; import { AxiosResponseHeaders } from 'axios'; import { useTranslation } from '../../hooks/use-translation-wrapper'; import { TFunction } from 'i18next'; diff --git a/libs/ui-lib/lib/common/components/hosts/MassChangeHostnameModal.tsx b/libs/ui-lib/lib/common/components/hosts/MassChangeHostnameModal.tsx index 1a0b5b693a..46dd61eb29 100644 --- a/libs/ui-lib/lib/common/components/hosts/MassChangeHostnameModal.tsx +++ b/libs/ui-lib/lib/common/components/hosts/MassChangeHostnameModal.tsx @@ -33,7 +33,7 @@ import { Host } from '../../api'; import { getHostname as getHostnameUtils, getInventory } from './utils'; import { ActionCheck } from './types'; import { useTranslation } from '../../hooks/use-translation-wrapper'; -import { getApiErrorMessage } from '../../../ocm/api'; // eslint-disable-line no-restricted-imports +import { getApiErrorMessage } from '../../api'; // eslint-disable-line no-restricted-imports import './MassChangeHostnameModal.css';