From 87f30fd75b9f567205612e0e4b1c59a45b9cce65 Mon Sep 17 00:00:00 2001 From: WangLiNaruto <122504203+WangLiNaruto@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:48:10 +0800 Subject: [PATCH] Add retry option to deployed services (#1025) --- .../services/details/ServiceDetail.tsx | 4 +- .../myServices/MyServices.tsx | 102 ++++++++- .../common/ScaleOrModifyOrderSubmitResult.tsx | 55 +++++ .../common/ScaleOrModifySubmitStatusAlert.tsx | 4 +- .../content/order/create/OrderSubmit.tsx | 153 ++++++++++--- .../order/create/SelectServiceForm.tsx | 6 +- .../order/orderStatus/OrderSubmitResult.tsx | 40 +++- .../orderStatus/OrderSubmitStatusAlert.tsx | 208 ++++++++++++------ .../order/orderStatus/ProcessingStatus.tsx | 14 +- .../useServiceDetailsPollingQuery.ts | 55 +++++ .../useRedeployFailedDeploymentQuery.ts | 16 ++ .../content/review/ServiceReviewsDetails.tsx | 2 +- 12 files changed, 541 insertions(+), 118 deletions(-) create mode 100644 src/components/content/order/common/ScaleOrModifyOrderSubmitResult.tsx create mode 100644 src/components/content/order/retryDeployment/useRedeployFailedDeploymentQuery.ts diff --git a/src/components/content/catalog/services/details/ServiceDetail.tsx b/src/components/content/catalog/services/details/ServiceDetail.tsx index 152f917a9..f48ba677f 100644 --- a/src/components/content/catalog/services/details/ServiceDetail.tsx +++ b/src/components/content/catalog/services/details/ServiceDetail.tsx @@ -11,12 +11,12 @@ import appStyles from '../../../../../styles/app.module.css'; import catalogStyles from '../../../../../styles/catalog.module.css'; import oclDisplayStyles from '../../../../../styles/ocl-display.module.css'; import { + DeployedService, + ServiceTemplateDetailVo, category, csp, - DeployedService, serviceDeploymentState, serviceRegistrationState, - ServiceTemplateDetailVo, } from '../../../../../xpanse-api/generated'; import { useCurrentUserRoleStore } from '../../../../layouts/header/useCurrentRoleStore'; import { reportsRoute } from '../../../../utils/constants'; diff --git a/src/components/content/deployedServices/myServices/MyServices.tsx b/src/components/content/deployedServices/myServices/MyServices.tsx index 233522b71..62273836b 100644 --- a/src/components/content/deployedServices/myServices/MyServices.tsx +++ b/src/components/content/deployedServices/myServices/MyServices.tsx @@ -23,6 +23,7 @@ import type { ColumnsType } from 'antd/es/table'; import { ColumnFilterItem } from 'antd/es/table/interface'; import React, { useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; +import { v4 } from 'uuid'; import appStyles from '../../../../styles/app.module.css'; import myServicesStyles from '../../../../styles/my-services.module.css'; import tableStyles from '../../../../styles/table.module.css'; @@ -37,6 +38,7 @@ import { serviceDeploymentState, serviceHostingType, serviceState, + taskStatus, } from '../../../../xpanse-api/generated'; import { sortVersionNum } from '../../../utils/Sort'; import { serviceIdQuery, serviceStateQuery } from '../../../utils/constants'; @@ -49,10 +51,16 @@ import { useDestroyRequestSubmitQuery } from '../../order/destroy/useDestroyRequ import { Locks } from '../../order/locks/Locks'; import { Migrate } from '../../order/migrate/Migrate'; import { Modify } from '../../order/modify/Modify'; -import { useServiceDetailsPollingQuery } from '../../order/orderStatus/useServiceDetailsPollingQuery'; +import OrderSubmitStatusAlert from '../../order/orderStatus/OrderSubmitStatusAlert'; +import { + useLatestServiceOrderStatusQuery, + useServiceDetailsByIdQuery, + useServiceDetailsPollingQuery, +} from '../../order/orderStatus/useServiceDetailsPollingQuery'; import { PurgeServiceStatusAlert } from '../../order/purge/PurgeServiceStatusAlert'; import { usePurgeRequestStatusQuery } from '../../order/purge/usePurgeRequestStatusQuery'; import { usePurgeRequestSubmitQuery } from '../../order/purge/usePurgeRequestSubmitQuery'; +import useRedeployFailedDeploymentQuery from '../../order/retryDeployment/useRedeployFailedDeploymentQuery'; import { Scale } from '../../order/scale/Scale'; import RestartServiceStatusAlert from '../../order/serviceState/restart/RestartServiceStatusAlert'; import { useServiceStateRestartQuery } from '../../order/serviceState/restart/useServiceStateRestartQuery'; @@ -95,14 +103,17 @@ function MyServices(): React.JSX.Element { const [isRestartRequestSubmitted, setIsRestartRequestSubmitted] = useState(false); const [isDestroyRequestSubmitted, setIsDestroyRequestSubmitted] = useState(false); const [isPurgeRequestSubmitted, setIsPurgeRequestSubmitted] = useState(false); + const [isRetryDeployRequestSubmitted, setIsRetryDeployRequestSubmitted] = useState(false); const [isMyServiceDetailsModalOpen, setIsMyServiceDetailsModalOpen] = useState(false); const [isMyServiceHistoryModalOpen, setIsMyServiceHistoryModalOpen] = useState(false); const [isMigrateModalOpen, setIsMigrateModalOpen] = useState(false); const [isModifyModalOpen, setIsModifyModalOpen] = useState(false); const [isScaleModalOpen, setIsScaleModalOpen] = useState(false); const [isLocksModalOpen, setIsLocksModalOpen] = useState(false); + const [uniqueRequestId, setUniqueRequestId] = useState(v4()); const serviceDestroyQuery = useDestroyRequestSubmitQuery(); const servicePurgeQuery = usePurgeRequestSubmitQuery(); + const redeployFailedDeploymentQuery = useRedeployFailedDeploymentQuery(); const serviceStateStartQuery = useServiceStateStartQuery(refreshData); const serviceStateStopQuery = useServiceStateStopQuery(refreshData); const serviceStateRestartQuery = useServiceStateRestartQuery(refreshData); @@ -138,6 +149,19 @@ function MyServices(): React.JSX.Element { [serviceState.RUNNING] ); + const getReDeployLatestServiceOrderStatusQuery = useLatestServiceOrderStatusQuery( + redeployFailedDeploymentQuery.data?.orderId ?? '', + redeployFailedDeploymentQuery.isSuccess, + [taskStatus.SUCCESSFUL, taskStatus.FAILED] + ); + + const getServiceDetailsQuery = useServiceDetailsByIdQuery( + activeRecord?.serviceId, + getReDeployLatestServiceOrderStatusQuery.data?.taskStatus.toString() === taskStatus.SUCCESSFUL || + getReDeployLatestServiceOrderStatusQuery.data?.taskStatus.toString() === taskStatus.FAILED, + activeRecord ? (activeRecord.serviceHostingType as serviceHostingType) : serviceHostingType.SELF + ); + useEffect(() => { void listDeployedServicesQuery.refetch(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -145,6 +169,10 @@ function MyServices(): React.JSX.Element { getStartServiceDetailsQuery.data?.serviceState, getStopServiceDetailsQuery.data?.serviceState, getRestartServiceDetailsQuery.data?.serviceState, + redeployFailedDeploymentQuery.isError, + redeployFailedDeploymentQuery.isPending, + getReDeployLatestServiceOrderStatusQuery.isError, + getReDeployLatestServiceOrderStatusQuery.data?.isOrderCompleted, ]); const getPurgeServiceDetailsQuery = usePurgeRequestStatusQuery( @@ -404,6 +432,32 @@ function MyServices(): React.JSX.Element { ), }, + { + key: 'retryDeployment', + label: + record.serviceDeploymentState.toString() === serviceDeploymentState.DEPLOYMENT_FAILED.toString() ? ( + { + retryDeployment(record); + }} + > + + + ) : ( + <> + ), + }, { key: record.serviceDeploymentState.toString() === serviceDeploymentState.DESTROY_SUCCESSFUL.toString() || @@ -682,6 +736,13 @@ function MyServices(): React.JSX.Element { return record.serviceState === serviceState.STOPPED; }; + const isDisableRetryDeploymentBtn = (record: DeployedService) => { + if (record.serviceDeploymentState === serviceDeploymentState.DEPLOYING) { + return true; + } + return false; + }; + const closeDestroyResultAlert = (isClose: boolean) => { if (isClose) { setActiveRecord(undefined); @@ -719,6 +780,13 @@ function MyServices(): React.JSX.Element { } }; + const closeRetryDeployResultAlert = () => { + setActiveRecord(undefined); + refreshData(); + setIsRetryDeployRequestSubmitted(false); + setUniqueRequestId(''); + }; + const columns: ColumnsType = [ { title: 'Id', @@ -1010,6 +1078,17 @@ function MyServices(): React.JSX.Element { setIsLocksModalOpen(true); } + function retryDeployment(record: DeployedService): void { + setIsRetryDeployRequestSubmitted(true); + setActiveRecord( + record.serviceHostingType === serviceHostingType.SELF + ? (record as DeployedServiceDetails) + : (record as VendorHostedDeployedServiceDetails) + ); + redeployFailedDeploymentQuery.mutate(record.serviceId); + record.serviceDeploymentState = serviceDeploymentState.DEPLOYING; + } + function onMonitor(record: DeployedService): void { navigate('/monitor', { state: record, @@ -1241,6 +1320,13 @@ function MyServices(): React.JSX.Element { return undefined; } + const retryRequest = () => { + setUniqueRequestId(v4()); + if (activeRecord && activeRecord.serviceId.length > 0) { + redeployFailedDeploymentQuery.mutate(activeRecord.serviceId); + } + }; + return (
{isDestroyRequestSubmitted && activeRecord ? ( @@ -1289,6 +1375,20 @@ function MyServices(): React.JSX.Element { closePurgeResultAlert={closePurgeResultAlert} /> ) : null} + {isRetryDeployRequestSubmitted && activeRecord ? ( + + ) : null} {activeRecord ? ( { + return ( +
+ {' '} + } + showIcon + closable={true} + type={type} + action={ + <> + {contactServiceDetails !== undefined ? ( + + ) : ( + <> + )} + + + + } + />{' '} +
+ ); +}; diff --git a/src/components/content/order/common/ScaleOrModifySubmitStatusAlert.tsx b/src/components/content/order/common/ScaleOrModifySubmitStatusAlert.tsx index dcd861a40..19675929f 100644 --- a/src/components/content/order/common/ScaleOrModifySubmitStatusAlert.tsx +++ b/src/components/content/order/common/ScaleOrModifySubmitStatusAlert.tsx @@ -15,10 +15,10 @@ import { VendorHostedDeployedServiceDetails, } from '../../../../xpanse-api/generated'; import { convertStringArrayToUnorderedList } from '../../../utils/generateUnorderedList'; -import { OrderSubmitResult } from '../orderStatus/OrderSubmitResult'; import { ProcessingStatus } from '../orderStatus/ProcessingStatus'; import { useServiceDetailsPollingQuery } from '../orderStatus/useServiceDetailsPollingQuery'; import { OperationType } from '../types/OperationType'; +import { ScaleOrModifyOrderSubmitResult } from './ScaleOrModifyOrderSubmitResult'; function ScaleOrModifySubmitStatusAlert({ isSubmitFailed, @@ -144,7 +144,7 @@ function ScaleOrModifySubmitStatusAlert({ } return ( - (false); const uniqueRequestId = useRef(v4()); const submitDeploymentRequest = useDeployRequestSubmitQuery(); - const getServiceDetailsByIdQuery = useServiceDetailsPollingQuery( + const redeployFailedDeploymentQuery = useRedeployFailedDeploymentQuery(); + const getSubmitLatestServiceOrderStatusQuery = useLatestServiceOrderStatusQuery( + redeployFailedDeploymentQuery.isSuccess + ? redeployFailedDeploymentQuery.data.orderId + : (submitDeploymentRequest.data?.orderId ?? ''), + submitDeploymentRequest.isSuccess || redeployFailedDeploymentQuery.isSuccess, + [taskStatus.SUCCESSFUL, taskStatus.FAILED] + ); + + const getServiceDetailsByIdQuery = useServiceDetailsByIdQuery( submitDeploymentRequest.data?.serviceId ?? '', - submitDeploymentRequest.isSuccess, - state.serviceHostingType, - [serviceDeploymentState.DEPLOYMENT_SUCCESSFUL, serviceDeploymentState.DEPLOYMENT_FAILED] + getSubmitLatestServiceOrderStatusQuery.data?.taskStatus.toString() === taskStatus.SUCCESSFUL || + getSubmitLatestServiceOrderStatusQuery.data?.taskStatus.toString() === taskStatus.FAILED, + state.serviceHostingType ); + const [cacheFormVariable] = useOrderFormStore((state) => [state.addDeployVariable]); const navigate = useNavigate(); @@ -77,12 +96,102 @@ function OrderSubmit(state: OrderSubmitProps): React.JSX.Element { submitDeploymentRequest.mutate(createRequest); } + const retryRequest = () => { + uniqueRequestId.current = v4(); + if (submitDeploymentRequest.isSuccess) { + redeployFailedDeploymentQuery.mutate(submitDeploymentRequest.data.serviceId); + } else { + if ( + submitDeploymentRequest.error instanceof ApiError && + submitDeploymentRequest.error.body && + typeof submitDeploymentRequest.error.body === 'object' && + 'serviceId' in submitDeploymentRequest.error.body + ) { + redeployFailedDeploymentQuery.mutate(submitDeploymentRequest.error.body.serviceId as string); + } + } + }; + + const onClose = () => { + navigate(servicesSubPageRoute.concat(state.category)); + }; + const createServicePageUrl: string = createServicePageRoute .concat('?catalog=', state.category) .concat('&serviceName=', state.name) .concat('&latestVersion=', state.version) .concat('&billingMode=', state.billingMode); + const isBackDisabled = () => { + if (submitDeploymentRequest.isPending || redeployFailedDeploymentQuery.isPending) { + return true; + } + if ( + submitDeploymentRequest.isError || + redeployFailedDeploymentQuery.isError || + getSubmitLatestServiceOrderStatusQuery.isError + ) { + return true; + } + if ( + submitDeploymentRequest.isSuccess && + (getSubmitLatestServiceOrderStatusQuery.isPending || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === + taskStatus.IN_PROGRESS.toString() || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.SUCCESSFUL.toString()) + ) { + return true; + } + return false; + }; + + const isDeployDisabled = () => { + if (submitDeploymentRequest.isPending || redeployFailedDeploymentQuery.isPending) { + return true; + } + if ( + submitDeploymentRequest.isError || + redeployFailedDeploymentQuery.isError || + getSubmitLatestServiceOrderStatusQuery.isError + ) { + return true; + } + + if ( + submitDeploymentRequest.isSuccess && + (getSubmitLatestServiceOrderStatusQuery.isPending || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === + taskStatus.IN_PROGRESS.toString() || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === + taskStatus.SUCCESSFUL.toString() || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.FAILED.toString()) + ) { + return true; + } + return false; + }; + + const isDeployLoading = () => { + if (submitDeploymentRequest.isPending || redeployFailedDeploymentQuery.isPending) { + return true; + } + if ( + submitDeploymentRequest.isError || + redeployFailedDeploymentQuery.isError || + getSubmitLatestServiceOrderStatusQuery.isError + ) { + return false; + } + if ( + submitDeploymentRequest.isSuccess && + (getSubmitLatestServiceOrderStatusQuery.isPending || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.IN_PROGRESS.toString()) + ) { + return true; + } + return false; + }; + return ( <>
@@ -104,11 +213,14 @@ function OrderSubmit(state: OrderSubmitProps): React.JSX.Element { ) : null}
@@ -192,17 +299,9 @@ function OrderSubmit(state: OrderSubmitProps): React.JSX.Element {
diff --git a/src/components/content/order/create/SelectServiceForm.tsx b/src/components/content/order/create/SelectServiceForm.tsx index 131171207..dad58dfbd 100644 --- a/src/components/content/order/create/SelectServiceForm.tsx +++ b/src/components/content/order/create/SelectServiceForm.tsx @@ -12,12 +12,12 @@ import serviceOrderStyles from '../../../../styles/service-order.module.css'; import tableStyles from '../../../../styles/table.module.css'; import { AvailabilityZoneConfig, - billingMode, - csp, ServiceFlavor, - serviceHostingType, ServiceProviderContactDetails, UserOrderableServiceVo, + billingMode, + csp, + serviceHostingType, } from '../../../../xpanse-api/generated'; import { orderPageRoute, servicesSubPageRoute } from '../../../utils/constants'; import { ContactDetailsShowType } from '../../common/ocl/ContactDetailsShowType'; diff --git a/src/components/content/order/orderStatus/OrderSubmitResult.tsx b/src/components/content/order/orderStatus/OrderSubmitResult.tsx index 62eb9d040..aebf98d77 100644 --- a/src/components/content/order/orderStatus/OrderSubmitResult.tsx +++ b/src/components/content/order/orderStatus/OrderSubmitResult.tsx @@ -3,9 +3,10 @@ * SPDX-FileCopyrightText: Huawei Inc. */ -import { Alert } from 'antd'; +import { Alert, Button, Row } from 'antd'; import React from 'react'; import { StopwatchResult } from 'react-timer-hook'; +import errorAlertStyles from '../../../../styles/error-alert.module.css'; import submitAlertStyles from '../../../../styles/submit-alert.module.css'; import { ServiceProviderContactDetails } from '../../../../xpanse-api/generated'; import { ContactDetailsShowType } from '../../common/ocl/ContactDetailsShowType'; @@ -18,13 +19,19 @@ export const OrderSubmitResult = ({ uuid, type, stopWatch, + isDeployRequestError, contactServiceDetails, + retryRequest, + onClose, }: { msg: string | React.JSX.Element; uuid: string; type: 'success' | 'error'; stopWatch: StopwatchResult; + isDeployRequestError: boolean; contactServiceDetails: ServiceProviderContactDetails | undefined; + retryRequest: () => void; + onClose: () => void; }): React.JSX.Element => { return (
@@ -34,19 +41,38 @@ export const OrderSubmitResult = ({ description={} showIcon closable={true} + onClose={onClose} type={type} action={ <> + {type === 'error' && !isDeployRequestError ? ( + + + + ) : ( + <> + )} {contactServiceDetails !== undefined ? ( - + + + ) : ( <> )} - - + + + } />{' '} diff --git a/src/components/content/order/orderStatus/OrderSubmitStatusAlert.tsx b/src/components/content/order/orderStatus/OrderSubmitStatusAlert.tsx index 430934add..748b9b629 100644 --- a/src/components/content/order/orderStatus/OrderSubmitStatusAlert.tsx +++ b/src/components/content/order/orderStatus/OrderSubmitStatusAlert.tsx @@ -3,15 +3,19 @@ * SPDX-FileCopyrightText: Huawei Inc. */ +import { UseMutationResult, UseQueryResult } from '@tanstack/react-query'; import React, { useMemo } from 'react'; import { useStopwatch } from 'react-timer-hook'; import { ApiError, DeployedServiceDetails, + DeployRequest, Response, - serviceDeploymentState, serviceHostingType, + ServiceOrder, + ServiceOrderStatusUpdate, ServiceProviderContactDetails, + taskStatus, } from '../../../../xpanse-api/generated'; import { convertStringArrayToUnorderedList } from '../../../utils/generateUnorderedList'; import { OperationType } from '../types/OperationType'; @@ -20,102 +24,124 @@ import { ProcessingStatus } from './ProcessingStatus'; function OrderSubmitStatusAlert({ uuid, - isSubmitFailed, serviceHostType, + submitDeploymentRequest, + redeployFailedDeploymentQuery, + getSubmitLatestServiceOrderStatusQuery, deployedServiceDetails, serviceProviderContactDetails, - isPollingError, + retryRequest, + onClose, }: { - uuid: string | undefined; - isSubmitFailed: Error | null; + uuid: string; serviceHostType: serviceHostingType; + submitDeploymentRequest: + | UseMutationResult + | UseMutationResult; + redeployFailedDeploymentQuery: UseMutationResult; + getSubmitLatestServiceOrderStatusQuery: UseQueryResult; deployedServiceDetails: DeployedServiceDetails | undefined; serviceProviderContactDetails: ServiceProviderContactDetails | undefined; - isPollingError: boolean; + retryRequest: () => void; + onClose: () => void; }): React.JSX.Element { const stopWatch = useStopwatch({ autoStart: true, }); const msg = useMemo(() => { - if (deployedServiceDetails) { + if (submitDeploymentRequest.isPending || redeployFailedDeploymentQuery.isPending) { + return 'Request submission in-progress'; + } else if (redeployFailedDeploymentQuery.isError) { if ( - uuid && - deployedServiceDetails.serviceDeploymentState.toString() === serviceDeploymentState.DEPLOYING.toString() - ) { - return 'Deploying, Please wait...'; - } else if ( - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.DEPLOYMENT_SUCCESSFUL.toString() || - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.DEPLOYMENT_FAILED.toString() || - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.ROLLBACK_FAILED.toString() + redeployFailedDeploymentQuery.error instanceof ApiError && + redeployFailedDeploymentQuery.error.body && + typeof redeployFailedDeploymentQuery.error.body === 'object' && + 'details' in redeployFailedDeploymentQuery.error.body ) { - return ; + const response: Response = redeployFailedDeploymentQuery.error.body as Response; + return getOrderSubmissionFailedDisplay(response.details); + } else { + return getOrderSubmissionFailedDisplay([redeployFailedDeploymentQuery.error.message]); } - } else if (isSubmitFailed) { + } else if (submitDeploymentRequest.isError) { if ( - isSubmitFailed instanceof ApiError && - isSubmitFailed.body && - typeof isSubmitFailed.body === 'object' && - 'details' in isSubmitFailed.body + submitDeploymentRequest.error instanceof ApiError && + submitDeploymentRequest.error.body && + typeof submitDeploymentRequest.error.body === 'object' && + 'details' in submitDeploymentRequest.error.body ) { - const response: Response = isSubmitFailed.body as Response; + const response: Response = submitDeploymentRequest.error.body as Response; return getOrderSubmissionFailedDisplay(response.details); } else { - return getOrderSubmissionFailedDisplay([isSubmitFailed.message]); + return getOrderSubmissionFailedDisplay([submitDeploymentRequest.error.message]); } - } else if (uuid && isPollingError) { - if (serviceHostType === serviceHostingType.SERVICE_VENDOR) { - return 'Deployment status polling failed. Please visit MyServices page to check the status of the request and contact service vendor for error details.'; - } else { - return 'Deployment status polling failed. Please visit MyServices page to check the status of the request'; + } else if (submitDeploymentRequest.isSuccess || redeployFailedDeploymentQuery.isSuccess) { + if ( + getSubmitLatestServiceOrderStatusQuery.isSuccess && + (getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === + taskStatus.SUCCESSFUL.toString() || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.FAILED.toString()) + ) { + return ; + } else if (getSubmitLatestServiceOrderStatusQuery.isError) { + if (serviceHostType === serviceHostingType.SERVICE_VENDOR) { + return 'Deployment status polling failed. Please visit MyServices page to check the status of the request and contact service vendor for error details.'; + } else { + return 'Deployment status polling failed. Please visit MyServices page to check the status of the request'; + } + } else if ( + getSubmitLatestServiceOrderStatusQuery.isPending || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.IN_PROGRESS.toString() + ) { + return 'Deploying, Please wait...'; } - } else if (uuid) { - return 'Request accepted'; - } else if (uuid === undefined) { - return 'Request submission in-progress'; } - return ''; - }, [deployedServiceDetails, uuid, isPollingError, isSubmitFailed, serviceHostType]); + }, [ + serviceHostType, + deployedServiceDetails, + submitDeploymentRequest, + redeployFailedDeploymentQuery, + getSubmitLatestServiceOrderStatusQuery, + ]); const alertType = useMemo(() => { - if (isPollingError || isSubmitFailed) { + if (submitDeploymentRequest.isPending || redeployFailedDeploymentQuery.isPending) { + return 'success'; + } else if ( + redeployFailedDeploymentQuery.isError || + submitDeploymentRequest.isError || + getSubmitLatestServiceOrderStatusQuery.isError + ) { + if (stopWatch.isRunning) { + stopWatch.pause(); + } return 'error'; - } - if (deployedServiceDetails) { + } else if (submitDeploymentRequest.isSuccess || redeployFailedDeploymentQuery.isSuccess) { if ( - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.DEPLOYMENT_FAILED.toString() || - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.ROLLBACK_FAILED.toString() + getSubmitLatestServiceOrderStatusQuery.isSuccess && + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.FAILED.toString() ) { + if (stopWatch.isRunning) { + stopWatch.pause(); + } return 'error'; + } else if ( + getSubmitLatestServiceOrderStatusQuery.isSuccess && + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.SUCCESSFUL.toString() + ) { + if (stopWatch.isRunning) { + stopWatch.pause(); + } + return 'success'; + } else if ( + getSubmitLatestServiceOrderStatusQuery.isPending || + getSubmitLatestServiceOrderStatusQuery.data.taskStatus.toString() === taskStatus.IN_PROGRESS.toString() + ) { + return 'success'; } } return 'success'; - }, [deployedServiceDetails, isPollingError, isSubmitFailed]); - - if (isPollingError || isSubmitFailed) { - if (stopWatch.isRunning) { - stopWatch.pause(); - } - } - - if (deployedServiceDetails) { - if ( - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.DEPLOYMENT_FAILED.toString() || - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.ROLLBACK_FAILED.toString() || - deployedServiceDetails.serviceDeploymentState.toString() === - serviceDeploymentState.DEPLOYMENT_SUCCESSFUL.toString() - ) { - if (stopWatch.isRunning) { - stopWatch.pause(); - } - } - } + }, [stopWatch, submitDeploymentRequest, redeployFailedDeploymentQuery, getSubmitLatestServiceOrderStatusQuery]); function getOrderSubmissionFailedDisplay(reasons: string[]) { return ( @@ -126,13 +152,59 @@ function OrderSubmitStatusAlert({ ); } + const getServiceId = (): string => { + if (submitDeploymentRequest.isSuccess) { + return submitDeploymentRequest.data.serviceId; + } else { + if ( + submitDeploymentRequest.error instanceof ApiError && + submitDeploymentRequest.error.body && + typeof submitDeploymentRequest.error.body === 'object' && + 'serviceId' in submitDeploymentRequest.error.body + ) { + return submitDeploymentRequest.error.body.serviceId as string; + } else { + return uuid; + } + } + }; + + const isDeployDisabled = () => { + if ( + submitDeploymentRequest.isError && + submitDeploymentRequest.error instanceof ApiError && + submitDeploymentRequest.error.body && + typeof submitDeploymentRequest.error.body === 'object' && + !('orderId' in submitDeploymentRequest.error.body && 'serviceId' in submitDeploymentRequest.error.body) + ) { + return true; + } + + if ( + redeployFailedDeploymentQuery.isError && + redeployFailedDeploymentQuery.error instanceof ApiError && + redeployFailedDeploymentQuery.error.body && + typeof redeployFailedDeploymentQuery.error.body === 'object' && + !( + 'orderId' in redeployFailedDeploymentQuery.error.body && + 'serviceId' in redeployFailedDeploymentQuery.error.body + ) + ) { + return true; + } + return false; + }; + return ( ); } diff --git a/src/components/content/order/orderStatus/ProcessingStatus.tsx b/src/components/content/order/orderStatus/ProcessingStatus.tsx index 2a98ca9e8..acf0d4700 100644 --- a/src/components/content/order/orderStatus/ProcessingStatus.tsx +++ b/src/components/content/order/orderStatus/ProcessingStatus.tsx @@ -13,13 +13,13 @@ export const ProcessingStatus = ({ response, operationType, }: { - response: DeployedServiceDetails; + response: DeployedServiceDetails | undefined; operationType: OperationType; }): React.JSX.Element => { const errorMsg: string = 'Please contact service vendor for error details.'; const endPointMap = new Map(); if (operationType === (OperationType.Deploy as OperationType)) { - if (response.serviceDeploymentState === serviceDeploymentState.DEPLOYMENT_SUCCESSFUL) { + if (response?.serviceDeploymentState === serviceDeploymentState.DEPLOYMENT_SUCCESSFUL) { if (response.deployedServiceProperties) { for (const key in response.deployedServiceProperties) { endPointMap.set(key, response.deployedServiceProperties[key]); @@ -37,7 +37,7 @@ export const ProcessingStatus = ({ } else { return {'Deployment Successful'}; } - } else if (response.serviceDeploymentState === serviceDeploymentState.DEPLOYMENT_FAILED) { + } else if (response?.serviceDeploymentState === serviceDeploymentState.DEPLOYMENT_FAILED) { return (
{'Deployment Failed.'} @@ -50,7 +50,7 @@ export const ProcessingStatus = ({ } if (operationType === (OperationType.Modify as OperationType)) { - if (response.serviceDeploymentState === serviceDeploymentState.MODIFICATION_SUCCESSFUL) { + if (response?.serviceDeploymentState === serviceDeploymentState.MODIFICATION_SUCCESSFUL) { if (response.deployedServiceProperties) { for (const key in response.deployedServiceProperties) { endPointMap.set(key, response.deployedServiceProperties[key]); @@ -68,7 +68,7 @@ export const ProcessingStatus = ({ } else { return {'Modification Successful'}; } - } else if (response.serviceDeploymentState === serviceDeploymentState.MODIFICATION_FAILED) { + } else if (response?.serviceDeploymentState === serviceDeploymentState.MODIFICATION_FAILED) { return (
{'Modification Failed.'} @@ -81,13 +81,13 @@ export const ProcessingStatus = ({ } if (operationType === (OperationType.Destroy as OperationType)) { - if (response.serviceDeploymentState === serviceDeploymentState.DESTROY_SUCCESSFUL) { + if (response?.serviceDeploymentState === serviceDeploymentState.DESTROY_SUCCESSFUL) { return (
{'Destroyed Successfully'}
); - } else if (response.serviceDeploymentState === serviceDeploymentState.DESTROY_FAILED) { + } else if (response?.serviceDeploymentState === serviceDeploymentState.DESTROY_FAILED) { return (
{'Destroyed Failed.'} diff --git a/src/components/content/order/orderStatus/useServiceDetailsPollingQuery.ts b/src/components/content/order/orderStatus/useServiceDetailsPollingQuery.ts index a5d977ad8..11a74b8bb 100644 --- a/src/components/content/order/orderStatus/useServiceDetailsPollingQuery.ts +++ b/src/components/content/order/orderStatus/useServiceDetailsPollingQuery.ts @@ -5,12 +5,15 @@ import { useQuery } from '@tanstack/react-query'; import { + getLatestServiceOrderStatus, + GetLatestServiceOrderStatusData, getSelfHostedServiceDetailsById, GetSelfHostedServiceDetailsByIdData, getVendorHostedServiceDetailsById, GetVendorHostedServiceDetailsByIdData, serviceDeploymentState, serviceHostingType, + taskStatus, } from '../../../../xpanse-api/generated'; import { deploymentStatusPollingInterval } from '../../../utils/constants'; @@ -47,3 +50,55 @@ export function useServiceDetailsPollingQuery( gcTime: 0, }); } + +export function useServiceDetailsByIdQuery( + serviceId: string | undefined, + isStart: boolean, + currentHostingType: serviceHostingType +) { + return useQuery({ + queryKey: ['getServiceDetailsById', serviceId, currentHostingType], + queryFn: () => { + if (currentHostingType === serviceHostingType.SELF) { + const data: GetSelfHostedServiceDetailsByIdData = { + serviceId: serviceId ?? '', + }; + return getSelfHostedServiceDetailsById(data); + } else { + const data: GetVendorHostedServiceDetailsByIdData = { + serviceId: serviceId ?? '', + }; + return getVendorHostedServiceDetailsById(data); + } + }, + refetchIntervalInBackground: true, + refetchOnWindowFocus: false, + enabled: serviceId !== undefined && isStart, + gcTime: 0, + }); +} + +export function useLatestServiceOrderStatusQuery( + orderId: string | undefined, + isStartPolling: boolean, + refetchUntilStates: taskStatus[] +) { + return useQuery({ + queryKey: ['getServiceDetailsById', orderId], + queryFn: () => { + const data: GetLatestServiceOrderStatusData = { + lastKnownServiceDeploymentState: undefined, + orderId: orderId ?? '', + }; + return getLatestServiceOrderStatus(data); + }, + refetchInterval: (query) => + query.state.data && refetchUntilStates.includes(query.state.data.taskStatus as taskStatus) + ? false + : deploymentStatusPollingInterval, + refetchIntervalInBackground: true, + refetchOnWindowFocus: false, + enabled: orderId !== undefined && isStartPolling, + gcTime: 0, + }); +} diff --git a/src/components/content/order/retryDeployment/useRedeployFailedDeploymentQuery.ts b/src/components/content/order/retryDeployment/useRedeployFailedDeploymentQuery.ts new file mode 100644 index 000000000..bc2cbb776 --- /dev/null +++ b/src/components/content/order/retryDeployment/useRedeployFailedDeploymentQuery.ts @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +import { useMutation } from '@tanstack/react-query'; +import { redeployFailedDeployment, RedeployFailedDeploymentData } from '../../../../xpanse-api/generated/'; + +export default function useRedeployFailedDeploymentQuery() { + return useMutation({ + mutationFn: (serviceId: string) => { + const data: RedeployFailedDeploymentData = { serviceId: serviceId }; + return redeployFailedDeployment(data); + }, + }); +} diff --git a/src/components/content/review/ServiceReviewsDetails.tsx b/src/components/content/review/ServiceReviewsDetails.tsx index 76a22685c..769c8a22a 100644 --- a/src/components/content/review/ServiceReviewsDetails.tsx +++ b/src/components/content/review/ServiceReviewsDetails.tsx @@ -10,7 +10,7 @@ import React, { useState } from 'react'; import catalogStyles from '../../../styles/catalog.module.css'; import oclDisplayStyles from '../../../styles/ocl-display.module.css'; import serviceReviewStyles from '../../../styles/service-review.module.css'; -import { name, serviceRegistrationState, ServiceTemplateDetailVo } from '../../../xpanse-api/generated'; +import { ServiceTemplateDetailVo, name, serviceRegistrationState } from '../../../xpanse-api/generated'; import { cspMap } from '../common/csp/CspLogo'; import { AgreementText } from '../common/ocl/AgreementText'; import { BillingText } from '../common/ocl/BillingText';