diff --git a/frontend/src/api/messagingQueues/onboarding/getOnboardingStatus.ts b/frontend/src/api/messagingQueues/onboarding/getOnboardingStatus.ts index b5da83aa13..da82e70134 100644 --- a/frontend/src/api/messagingQueues/onboarding/getOnboardingStatus.ts +++ b/frontend/src/api/messagingQueues/onboarding/getOnboardingStatus.ts @@ -16,11 +16,13 @@ export interface OnboardingStatusResponse { const getOnboardingStatus = async (props: { start: number; end: number; + endpointService?: string; }): Promise | ErrorResponse> => { + const { endpointService, ...rest } = props; try { const response = await ApiBaseInstance.post( - '/messaging-queues/kafka/onboarding/consumers', - props, + `/messaging-queues/kafka/onboarding/${endpointService || 'consumers'}`, + rest, ); return { diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication-producer.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication-producers.md similarity index 100% rename from frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication-producer.md rename to frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication-producers.md diff --git a/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx b/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx index 97238b6553..0fc81c0533 100644 --- a/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx @@ -14,6 +14,7 @@ import { useQueryService } from 'hooks/useQueryService'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; import useUrlQuery from 'hooks/useUrlQuery'; +import MessagingQueueHealthCheck from 'pages/MessagingQueues/MessagingQueueHealthCheck/MessagingQueueHealthCheck'; import { getAttributeDataFromOnboardingStatus } from 'pages/MessagingQueues/MessagingQueuesUtils'; import { useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; @@ -33,6 +34,9 @@ export default function ConnectionStatus(): JSX.Element { const urlQuery = useUrlQuery(); const getStartedSource = urlQuery.get(QueryParams.getStartedSource); + const getStartedSourceService = urlQuery.get( + QueryParams.getStartedSourceService, + ); const { serviceName, @@ -74,10 +78,14 @@ export default function ConnectionStatus(): JSX.Element { data: onbData, error: onbErr, isFetching: onbFetching, - } = useOnboardingStatus({ - enabled: getStartedSource === 'kafka', - refetchInterval: pollInterval, - }); + } = useOnboardingStatus( + { + enabled: getStartedSource === 'kafka', + refetchInterval: pollInterval, + }, + getStartedSourceService || '', + 'query-key-onboarding-status', + ); const [ shouldRetryOnboardingCall, @@ -326,18 +334,30 @@ export default function ConnectionStatus(): JSX.Element {
{isQueryServiceLoading && } - {!isQueryServiceLoading && isReceivingData && ( - <> - - Success - - )} - {!isQueryServiceLoading && !isReceivingData && ( - <> - - Failed - - )} + {!isQueryServiceLoading && + isReceivingData && + (getStartedSource !== 'kafka' ? ( + <> + + Success + + ) : ( + + ))} + {!isQueryServiceLoading && + !isReceivingData && + (getStartedSource !== 'kafka' ? ( + <> + + Failed + + ) : ( + + ))}
diff --git a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx index f31fad1aa2..0936c4754d 100644 --- a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx @@ -9,7 +9,10 @@ import cx from 'classnames'; import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext'; -import { useCases } from 'container/OnboardingContainer/OnboardingContainer'; +import { + ModulesMap, + useCases, +} from 'container/OnboardingContainer/OnboardingContainer'; import { getDataSources, getSupportedFrameworks, @@ -49,6 +52,9 @@ export default function DataSource(): JSX.Element { updateSelectedFramework, } = useOnboardingContext(); + const isKafkaAPM = + getStartedSource === 'kafka' && selectedModule?.id === ModulesMap.APM; + const [supportedDataSources, setSupportedDataSources] = useState< DataSourceType[] >([]); @@ -155,14 +161,14 @@ export default function DataSource(): JSX.Element { className={cx( 'supported-language', selectedDataSource?.name === dataSource.name ? 'selected' : '', - getStartedSource === 'kafka' && + isKafkaAPM && !messagingQueueKakfaSupportedDataSources.includes(dataSource?.id || '') ? 'disabled' : '', )} key={dataSource.name} onClick={(): void => { - if (getStartedSource !== 'kafka') { + if (!isKafkaAPM) { updateSelectedFramework(null); updateSelectedEnvironment(null); updateSelectedDataSource(dataSource); diff --git a/frontend/src/container/OnboardingContainer/constants/apmDocFilePaths.ts b/frontend/src/container/OnboardingContainer/constants/apmDocFilePaths.ts index df5e296722..1e6909fca7 100644 --- a/frontend/src/container/OnboardingContainer/constants/apmDocFilePaths.ts +++ b/frontend/src/container/OnboardingContainer/constants/apmDocFilePaths.ts @@ -252,7 +252,7 @@ import APM_java_springBoot_docker_recommendedSteps_runApplication from '../Modul import APM_java_springBoot_kubernetes_recommendedSteps_setupOtelCollector from '../Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-installOtelCollector.md'; import APM_java_springBoot_kubernetes_recommendedSteps_instrumentApplication from '../Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-instrumentApplication.md'; import APM_java_springBoot_kubernetes_recommendedSteps_runApplication from '../Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication.md'; -import APM_java_springBoot_kubernetes_recommendedSteps_runApplication_producer from '../Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication-producer.md'; +import APM_java_springBoot_kubernetes_recommendedSteps_runApplication_producers from '../Modules/APM/Java/md-docs/SpringBoot/Kubernetes/springBoot-kubernetes-runApplication-producers.md'; // SpringBoot-LinuxAMD64-quickstart import APM_java_springBoot_linuxAMD64_quickStart_instrumentApplication from '../Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/QuickStart/springBoot-linuxamd64-quickStart-instrumentApplication.md'; import APM_java_springBoot_linuxAMD64_quickStart_runApplication from '../Modules/APM/Java/md-docs/SpringBoot/LinuxAMD64/QuickStart/springBoot-linuxamd64-quickStart-runApplication.md'; @@ -1054,7 +1054,7 @@ export const ApmDocFilePaths = { APM_java_springBoot_kubernetes_recommendedSteps_setupOtelCollector, APM_java_springBoot_kubernetes_recommendedSteps_instrumentApplication, APM_java_springBoot_kubernetes_recommendedSteps_runApplication, - APM_java_springBoot_kubernetes_recommendedSteps_runApplication_producer, + APM_java_springBoot_kubernetes_recommendedSteps_runApplication_producers, // SpringBoot-LinuxAMD64-recommended APM_java_springBoot_linuxAMD64_recommendedSteps_setupOtelCollector, diff --git a/frontend/src/hooks/messagingQueue / onboarding/useOnboardingStatus.tsx b/frontend/src/hooks/messagingQueue / onboarding/useOnboardingStatus.tsx index 897b0d7e33..13ecd15b8b 100644 --- a/frontend/src/hooks/messagingQueue / onboarding/useOnboardingStatus.tsx +++ b/frontend/src/hooks/messagingQueue / onboarding/useOnboardingStatus.tsx @@ -8,15 +8,22 @@ type UseOnboardingStatus = ( options?: UseQueryOptions< SuccessResponse | ErrorResponse >, + endpointService?: string, + queryKey?: string, ) => UseQueryResult | ErrorResponse>; -export const useOnboardingStatus: UseOnboardingStatus = (options) => +export const useOnboardingStatus: UseOnboardingStatus = ( + options, + endpointService, + queryKey, +) => useQuery | ErrorResponse>({ - queryKey: ['onboardingStatus'], + queryKey: [queryKey || `onboardingStatus-${endpointService}`], queryFn: () => getOnboardingStatus({ start: (Date.now() - 15 * 60 * 1000) * 1_000_000, end: Date.now() * 1_000_000, + endpointService, }), ...options, }); diff --git a/frontend/src/pages/MessagingQueues/MessagingQueueHealthCheck/AttributeCheckList.tsx b/frontend/src/pages/MessagingQueues/MessagingQueueHealthCheck/AttributeCheckList.tsx new file mode 100644 index 0000000000..88e2147a0e --- /dev/null +++ b/frontend/src/pages/MessagingQueues/MessagingQueueHealthCheck/AttributeCheckList.tsx @@ -0,0 +1,206 @@ +import './MessagingQueueHealthCheck.styles.scss'; + +import { CaretDownOutlined, LoadingOutlined } from '@ant-design/icons'; +import { + Modal, + Select, + Spin, + Tooltip, + Tree, + TreeDataNode, + Typography, +} from 'antd'; +import { OnboardingStatusResponse } from 'api/messagingQueues/onboarding/getOnboardingStatus'; +import { Bolt, Check, OctagonAlert, X } from 'lucide-react'; +import { ReactNode, useEffect, useState } from 'react'; +import { v4 as uuid } from 'uuid'; + +interface AttributeCheckListProps { + visible: boolean; + onClose: () => void; + onboardingStatusResponses: { + title: string; + data: OnboardingStatusResponse['data']; + errorMsg?: string; + }[]; + loading: boolean; +} + +export enum AttributesFilters { + ALL = 'all', + SUCCESS = 'success', + ERROR = 'error', +} + +function ErrorTitleAndKey({ + title, + errorMsg, + isLeaf, +}: { + title: string; + errorMsg?: string; + isLeaf?: boolean; +}): TreeDataNode { + return { + key: `${title}-key-${uuid()}`, + title: ( +
+ + {title} + + +
+ + Fix +
+
+
+ ), + isLeaf, + }; +} + +function AttributeLabels({ title }: { title: ReactNode }): JSX.Element { + return ( +
+ + {title} +
+ ); +} + +function treeTitleAndKey({ + title, + isLeaf, +}: { + title: string; + isLeaf?: boolean; +}): TreeDataNode { + return { + key: `${title}-key-${uuid()}`, + title: ( +
+ + {title} + + {isLeaf && ( +
+ + + +
+ )} +
+ ), + isLeaf, + }; +} + +function generateTreeDataNodes( + response: OnboardingStatusResponse['data'], +): TreeDataNode[] { + return response + .map((item) => { + if (item.attribute) { + if (item.status === '1') { + return treeTitleAndKey({ title: item.attribute, isLeaf: true }); + } + if (item.status === '0') { + return ErrorTitleAndKey({ + title: item.attribute, + errorMsg: item.error_message || '', + }); + } + } + return null; + }) + .filter(Boolean) as TreeDataNode[]; +} + +function AttributeCheckList({ + visible, + onClose, + onboardingStatusResponses, + loading, +}: AttributeCheckListProps): JSX.Element { + const [filter, setFilter] = useState(AttributesFilters.ALL); + const [treeData, setTreeData] = useState([]); + + const handleFilterChange = (value: AttributesFilters): void => { + setFilter(value); + }; + + useEffect(() => { + const filteredData = onboardingStatusResponses.map((response) => { + if (response.errorMsg) { + return ErrorTitleAndKey({ + title: response.title, + errorMsg: response.errorMsg, + isLeaf: true, + }); + } + let filteredData = response.data; + + if (filter === AttributesFilters.SUCCESS) { + filteredData = response.data.filter((item) => item.status === '1'); + } else if (filter === AttributesFilters.ERROR) { + filteredData = response.data.filter((item) => item.status === '0'); + } + + return { + ...treeTitleAndKey({ title: response.title }), + children: generateTreeDataNodes(filteredData), + }; + }); + + setTreeData(filteredData); + }, [filter, onboardingStatusResponses]); + + return ( + } + > + {loading ? ( +
+ } size="large" /> +
+ ) : ( +
+