mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 23:28:58 +08:00
feat: added generic UI for scenario 1,3,4 (#6287)
* feat: added generic table component for scenario 1,3,4 * feat: added generic logic for mq detail tables and consumed for sc-1,2 * feat: added overview and details table for scenario-3 * feat: added table row clicks func * feat: resolved comments
This commit is contained in:
parent
9d90b8d19c
commit
12377be809
@ -37,4 +37,5 @@ export enum QueryParams {
|
|||||||
partition = 'partition',
|
partition = 'partition',
|
||||||
selectedTimelineQuery = 'selectedTimelineQuery',
|
selectedTimelineQuery = 'selectedTimelineQuery',
|
||||||
ruleType = 'ruleType',
|
ruleType = 'ruleType',
|
||||||
|
configDetail = 'configDetail',
|
||||||
}
|
}
|
||||||
|
@ -5,17 +5,33 @@ import logEvent from 'api/common/logEvent';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||||
import { ListMinus } from 'lucide-react';
|
import { ListMinus } from 'lucide-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { MessagingQueuesViewType } from '../MessagingQueuesUtils';
|
import {
|
||||||
|
MessagingQueuesViewType,
|
||||||
|
MessagingQueuesViewTypeOptions,
|
||||||
|
ProducerLatencyOptions,
|
||||||
|
} from '../MessagingQueuesUtils';
|
||||||
import { SelectLabelWithComingSoon } from '../MQCommon/MQCommon';
|
import { SelectLabelWithComingSoon } from '../MQCommon/MQCommon';
|
||||||
|
import MessagingQueueOverview from '../MQDetails/MessagingQueueOverview';
|
||||||
import MessagingQueuesDetails from '../MQDetails/MQDetails';
|
import MessagingQueuesDetails from '../MQDetails/MQDetails';
|
||||||
import MessagingQueuesConfigOptions from '../MQGraph/MQConfigOptions';
|
import MessagingQueuesConfigOptions from '../MQGraph/MQConfigOptions';
|
||||||
import MessagingQueuesGraph from '../MQGraph/MQGraph';
|
import MessagingQueuesGraph from '../MQGraph/MQGraph';
|
||||||
|
|
||||||
function MQDetailPage(): JSX.Element {
|
function MQDetailPage(): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const [
|
||||||
|
selectedView,
|
||||||
|
setSelectedView,
|
||||||
|
] = useState<MessagingQueuesViewTypeOptions>(
|
||||||
|
MessagingQueuesViewType.consumerLag.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
producerLatencyOption,
|
||||||
|
setproducerLatencyOption,
|
||||||
|
] = useState<ProducerLatencyOptions>(ProducerLatencyOptions.Producers);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logEvent('Messaging Queues: Detail page visited', {});
|
logEvent('Messaging Queues: Detail page visited', {});
|
||||||
@ -39,28 +55,19 @@ function MQDetailPage(): JSX.Element {
|
|||||||
className="messaging-queue-options"
|
className="messaging-queue-options"
|
||||||
defaultValue={MessagingQueuesViewType.consumerLag.value}
|
defaultValue={MessagingQueuesViewType.consumerLag.value}
|
||||||
popupClassName="messaging-queue-options-popup"
|
popupClassName="messaging-queue-options-popup"
|
||||||
|
onChange={(value): void => setSelectedView(value)}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
label: MessagingQueuesViewType.consumerLag.label,
|
label: MessagingQueuesViewType.consumerLag.label,
|
||||||
value: MessagingQueuesViewType.consumerLag.value,
|
value: MessagingQueuesViewType.consumerLag.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: (
|
label: MessagingQueuesViewType.partitionLatency.label,
|
||||||
<SelectLabelWithComingSoon
|
|
||||||
label={MessagingQueuesViewType.partitionLatency.label}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
value: MessagingQueuesViewType.partitionLatency.value,
|
value: MessagingQueuesViewType.partitionLatency.value,
|
||||||
disabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: (
|
label: MessagingQueuesViewType.producerLatency.label,
|
||||||
<SelectLabelWithComingSoon
|
|
||||||
label={MessagingQueuesViewType.producerLatency.label}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
value: MessagingQueuesViewType.producerLatency.value,
|
value: MessagingQueuesViewType.producerLatency.value,
|
||||||
disabled: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
@ -78,10 +85,21 @@ function MQDetailPage(): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
<div className="messaging-queue-main-graph">
|
<div className="messaging-queue-main-graph">
|
||||||
<MessagingQueuesConfigOptions />
|
<MessagingQueuesConfigOptions />
|
||||||
<MessagingQueuesGraph />
|
{selectedView === MessagingQueuesViewType.consumerLag.value ? (
|
||||||
|
<MessagingQueuesGraph />
|
||||||
|
) : (
|
||||||
|
<MessagingQueueOverview
|
||||||
|
selectedView={selectedView}
|
||||||
|
option={producerLatencyOption}
|
||||||
|
setOption={setproducerLatencyOption}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="messaging-queue-details">
|
<div className="messaging-queue-details">
|
||||||
<MessagingQueuesDetails />
|
<MessagingQueuesDetails
|
||||||
|
selectedView={selectedView}
|
||||||
|
producerLatencyOption={producerLatencyOption}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -4,3 +4,42 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mq-overview-container {
|
||||||
|
display: flex;
|
||||||
|
padding: 24px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--bg-slate-500);
|
||||||
|
background: var(--bg-ink-500);
|
||||||
|
|
||||||
|
.mq-overview-title {
|
||||||
|
color: var(--bg-vanilla-200);
|
||||||
|
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mq-details-options {
|
||||||
|
letter-spacing: -0.06px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
border-color: var(--bg-slate-400);
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
}
|
||||||
|
.ant-radio-button-wrapper-checked {
|
||||||
|
background: var(--bg-slate-400);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
}
|
||||||
|
.ant-radio-button-wrapper::before {
|
||||||
|
width: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,65 +1,227 @@
|
|||||||
import './MQDetails.style.scss';
|
import './MQDetails.style.scss';
|
||||||
|
|
||||||
import { Radio } from 'antd';
|
import { Radio } from 'antd';
|
||||||
import { Dispatch, SetStateAction, useState } from 'react';
|
import { QueryParams } from 'constants/query';
|
||||||
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
|
import { isEmpty } from 'lodash-es';
|
||||||
|
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ConsumerLagDetailTitle,
|
ConsumerLagDetailTitle,
|
||||||
ConsumerLagDetailType,
|
getMetaDataAndAPIPerView,
|
||||||
|
MessagingQueueServiceDetailType,
|
||||||
|
MessagingQueuesViewType,
|
||||||
|
MessagingQueuesViewTypeOptions,
|
||||||
|
ProducerLatencyOptions,
|
||||||
|
SelectedTimelineQuery,
|
||||||
} from '../MessagingQueuesUtils';
|
} from '../MessagingQueuesUtils';
|
||||||
import { ComingSoon } from '../MQCommon/MQCommon';
|
import { ComingSoon } from '../MQCommon/MQCommon';
|
||||||
import MessagingQueuesTable from './MQTables/MQTables';
|
import MessagingQueuesTable from './MQTables/MQTables';
|
||||||
|
|
||||||
|
const MQServiceDetailTypePerView = (
|
||||||
|
producerLatencyOption: ProducerLatencyOptions,
|
||||||
|
): Record<string, MessagingQueueServiceDetailType[]> => ({
|
||||||
|
[MessagingQueuesViewType.consumerLag.value]: [
|
||||||
|
MessagingQueueServiceDetailType.ConsumerDetails,
|
||||||
|
MessagingQueueServiceDetailType.ProducerDetails,
|
||||||
|
MessagingQueueServiceDetailType.NetworkLatency,
|
||||||
|
MessagingQueueServiceDetailType.PartitionHostMetrics,
|
||||||
|
],
|
||||||
|
[MessagingQueuesViewType.partitionLatency.value]: [
|
||||||
|
MessagingQueueServiceDetailType.ConsumerDetails,
|
||||||
|
MessagingQueueServiceDetailType.ProducerDetails,
|
||||||
|
],
|
||||||
|
[MessagingQueuesViewType.producerLatency.value]: [
|
||||||
|
producerLatencyOption === ProducerLatencyOptions.Consumers
|
||||||
|
? MessagingQueueServiceDetailType.ConsumerDetails
|
||||||
|
: MessagingQueueServiceDetailType.ProducerDetails,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
interface MessagingQueuesOptionsProps {
|
||||||
|
currentTab: MessagingQueueServiceDetailType;
|
||||||
|
setCurrentTab: Dispatch<SetStateAction<MessagingQueueServiceDetailType>>;
|
||||||
|
selectedView: MessagingQueuesViewTypeOptions;
|
||||||
|
producerLatencyOption: ProducerLatencyOptions;
|
||||||
|
}
|
||||||
|
|
||||||
function MessagingQueuesOptions({
|
function MessagingQueuesOptions({
|
||||||
currentTab,
|
currentTab,
|
||||||
setCurrentTab,
|
setCurrentTab,
|
||||||
}: {
|
selectedView,
|
||||||
currentTab: ConsumerLagDetailType;
|
producerLatencyOption,
|
||||||
setCurrentTab: Dispatch<SetStateAction<ConsumerLagDetailType>>;
|
}: MessagingQueuesOptionsProps): JSX.Element {
|
||||||
}): JSX.Element {
|
const handleChange = (value: MessagingQueueServiceDetailType): void => {
|
||||||
const [option, setOption] = useState<ConsumerLagDetailType>(currentTab);
|
setCurrentTab(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderRadioButtons = (): JSX.Element[] => {
|
||||||
|
const detailTypes =
|
||||||
|
MQServiceDetailTypePerView(producerLatencyOption)[selectedView] || [];
|
||||||
|
return detailTypes.map((detailType) => (
|
||||||
|
<Radio.Button
|
||||||
|
key={detailType}
|
||||||
|
value={detailType}
|
||||||
|
disabled={
|
||||||
|
detailType === MessagingQueueServiceDetailType.PartitionHostMetrics
|
||||||
|
}
|
||||||
|
className={
|
||||||
|
detailType === MessagingQueueServiceDetailType.PartitionHostMetrics
|
||||||
|
? 'disabled-option'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ConsumerLagDetailTitle[detailType]}
|
||||||
|
{detailType === MessagingQueueServiceDetailType.PartitionHostMetrics && (
|
||||||
|
<ComingSoon />
|
||||||
|
)}
|
||||||
|
</Radio.Button>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
onChange={(value): void => {
|
onChange={(e): void => handleChange(e.target.value)}
|
||||||
setOption(value.target.value);
|
value={currentTab}
|
||||||
setCurrentTab(value.target.value);
|
|
||||||
}}
|
|
||||||
value={option}
|
|
||||||
className="mq-details-options"
|
className="mq-details-options"
|
||||||
>
|
>
|
||||||
<Radio.Button value={ConsumerLagDetailType.ConsumerDetails} checked>
|
{renderRadioButtons()}
|
||||||
{ConsumerLagDetailTitle[ConsumerLagDetailType.ConsumerDetails]}
|
|
||||||
</Radio.Button>
|
|
||||||
<Radio.Button value={ConsumerLagDetailType.ProducerDetails}>
|
|
||||||
{ConsumerLagDetailTitle[ConsumerLagDetailType.ProducerDetails]}
|
|
||||||
</Radio.Button>
|
|
||||||
<Radio.Button value={ConsumerLagDetailType.NetworkLatency}>
|
|
||||||
{ConsumerLagDetailTitle[ConsumerLagDetailType.NetworkLatency]}
|
|
||||||
</Radio.Button>
|
|
||||||
<Radio.Button
|
|
||||||
value={ConsumerLagDetailType.PartitionHostMetrics}
|
|
||||||
disabled
|
|
||||||
className="disabled-option"
|
|
||||||
>
|
|
||||||
{ConsumerLagDetailTitle[ConsumerLagDetailType.PartitionHostMetrics]}
|
|
||||||
<ComingSoon />
|
|
||||||
</Radio.Button>
|
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function MessagingQueuesDetails(): JSX.Element {
|
const checkValidityOfDetailConfigs = (
|
||||||
const [currentTab, setCurrentTab] = useState<ConsumerLagDetailType>(
|
selectedTimelineQuery: SelectedTimelineQuery,
|
||||||
ConsumerLagDetailType.ConsumerDetails,
|
selectedView: MessagingQueuesViewTypeOptions,
|
||||||
|
currentTab: MessagingQueueServiceDetailType,
|
||||||
|
configDetails?: {
|
||||||
|
[key: string]: string;
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
): boolean => {
|
||||||
|
if (selectedView === MessagingQueuesViewType.consumerLag.value) {
|
||||||
|
return !(
|
||||||
|
isEmpty(selectedTimelineQuery) ||
|
||||||
|
(!selectedTimelineQuery?.group &&
|
||||||
|
!selectedTimelineQuery?.topic &&
|
||||||
|
!selectedTimelineQuery?.partition)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedView === MessagingQueuesViewType.partitionLatency.value) {
|
||||||
|
if (isEmpty(configDetails)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTab === MessagingQueueServiceDetailType.ConsumerDetails) {
|
||||||
|
return Boolean(configDetails?.topic && configDetails?.partition);
|
||||||
|
}
|
||||||
|
return Boolean(
|
||||||
|
configDetails?.group && configDetails?.topic && configDetails?.partition,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedView === MessagingQueuesViewType.producerLatency.value) {
|
||||||
|
if (isEmpty(configDetails)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTab === MessagingQueueServiceDetailType.ProducerDetails) {
|
||||||
|
return Boolean(
|
||||||
|
configDetails?.topic &&
|
||||||
|
configDetails?.partition &&
|
||||||
|
configDetails?.service_name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Boolean(configDetails?.topic && configDetails?.service_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function MessagingQueuesDetails({
|
||||||
|
selectedView,
|
||||||
|
producerLatencyOption,
|
||||||
|
}: {
|
||||||
|
selectedView: MessagingQueuesViewTypeOptions;
|
||||||
|
producerLatencyOption: ProducerLatencyOptions;
|
||||||
|
}): JSX.Element {
|
||||||
|
const [currentTab, setCurrentTab] = useState<MessagingQueueServiceDetailType>(
|
||||||
|
MessagingQueueServiceDetailType.ConsumerDetails,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
producerLatencyOption &&
|
||||||
|
selectedView === MessagingQueuesViewType.producerLatency.value
|
||||||
|
) {
|
||||||
|
setCurrentTab(
|
||||||
|
producerLatencyOption === ProducerLatencyOptions.Consumers
|
||||||
|
? MessagingQueueServiceDetailType.ConsumerDetails
|
||||||
|
: MessagingQueueServiceDetailType.ProducerDetails,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [selectedView, producerLatencyOption]);
|
||||||
|
|
||||||
|
const urlQuery = useUrlQuery();
|
||||||
|
const timelineQuery = decodeURIComponent(
|
||||||
|
urlQuery.get(QueryParams.selectedTimelineQuery) || '',
|
||||||
|
);
|
||||||
|
|
||||||
|
const timelineQueryData: SelectedTimelineQuery = useMemo(
|
||||||
|
() => (timelineQuery ? JSON.parse(timelineQuery) : {}),
|
||||||
|
[timelineQuery],
|
||||||
|
);
|
||||||
|
|
||||||
|
const configDetails = decodeURIComponent(
|
||||||
|
urlQuery.get(QueryParams.configDetail) || '',
|
||||||
|
);
|
||||||
|
|
||||||
|
const configDetailQueryData: {
|
||||||
|
[key: string]: string;
|
||||||
|
} = useMemo(() => (configDetails ? JSON.parse(configDetails) : {}), [
|
||||||
|
configDetails,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
const serviceConfigDetails = useMemo(
|
||||||
|
() =>
|
||||||
|
getMetaDataAndAPIPerView({
|
||||||
|
detailType: currentTab,
|
||||||
|
minTime,
|
||||||
|
maxTime,
|
||||||
|
selectedTimelineQuery: timelineQueryData,
|
||||||
|
configDetails: configDetailQueryData,
|
||||||
|
}),
|
||||||
|
[configDetailQueryData, currentTab, maxTime, minTime, timelineQueryData],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mq-details">
|
<div className="mq-details">
|
||||||
<MessagingQueuesOptions
|
<MessagingQueuesOptions
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
setCurrentTab={setCurrentTab}
|
setCurrentTab={setCurrentTab}
|
||||||
|
selectedView={selectedView}
|
||||||
|
producerLatencyOption={producerLatencyOption}
|
||||||
|
/>
|
||||||
|
<MessagingQueuesTable
|
||||||
|
currentTab={currentTab}
|
||||||
|
selectedView={selectedView}
|
||||||
|
tableApi={serviceConfigDetails[selectedView].tableApi}
|
||||||
|
validConfigPresent={checkValidityOfDetailConfigs(
|
||||||
|
timelineQueryData,
|
||||||
|
selectedView,
|
||||||
|
currentTab,
|
||||||
|
configDetailQueryData,
|
||||||
|
)}
|
||||||
|
tableApiPayload={serviceConfigDetails[selectedView].tableApiPayload}
|
||||||
/>
|
/>
|
||||||
<MessagingQueuesTable currentTab={currentTab} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
.mq-tables-container {
|
.mq-tables-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.mq-table-title {
|
.mq-table-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -31,9 +34,6 @@
|
|||||||
.ant-table-tbody {
|
.ant-table-tbody {
|
||||||
.ant-table-cell {
|
.ant-table-cell {
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
|
|
||||||
background-color: var(--bg-ink-400);
|
|
||||||
|
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,6 +63,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mq-table {
|
||||||
|
&.mq-overview-row-clickable {
|
||||||
|
.ant-table-row {
|
||||||
|
background-color: var(--bg-ink-400);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--bg-slate-400) !important;
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.mq-tables-container {
|
.mq-tables-container {
|
||||||
.mq-table-title {
|
.mq-table-title {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
/* eslint-disable react/require-default-props */
|
||||||
import './MQTables.styles.scss';
|
import './MQTables.styles.scss';
|
||||||
|
|
||||||
import { Skeleton, Table, Typography } from 'antd';
|
import { Skeleton, Table, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { isNumber } from 'chart.js/helpers';
|
import { isNumber } from 'chart.js/helpers';
|
||||||
|
import cx from 'classnames';
|
||||||
import { ColumnTypeRender } from 'components/Logs/TableView/types';
|
import { ColumnTypeRender } from 'components/Logs/TableView/types';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@ -13,18 +14,21 @@ import useUrlQuery from 'hooks/useUrlQuery';
|
|||||||
import { isEmpty } from 'lodash-es';
|
import { isEmpty } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
ConsumerLagDetailTitle,
|
ConsumerLagDetailTitle,
|
||||||
ConsumerLagDetailType,
|
|
||||||
convertToTitleCase,
|
convertToTitleCase,
|
||||||
|
MessagingQueueServiceDetailType,
|
||||||
|
MessagingQueuesViewType,
|
||||||
|
MessagingQueuesViewTypeOptions,
|
||||||
RowData,
|
RowData,
|
||||||
SelectedTimelineQuery,
|
SelectedTimelineQuery,
|
||||||
|
setConfigDetail,
|
||||||
} from 'pages/MessagingQueues/MessagingQueuesUtils';
|
} from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ConsumerLagPayload,
|
MessagingQueueServicePayload,
|
||||||
getConsumerLagDetails,
|
|
||||||
MessagingQueuesPayloadProps,
|
MessagingQueuesPayloadProps,
|
||||||
} from './getConsumerLagDetails';
|
} from './getConsumerLagDetails';
|
||||||
|
|
||||||
@ -33,7 +37,6 @@ export function getColumns(
|
|||||||
data: MessagingQueuesPayloadProps['payload'],
|
data: MessagingQueuesPayloadProps['payload'],
|
||||||
history: History<unknown>,
|
history: History<unknown>,
|
||||||
): RowData[] {
|
): RowData[] {
|
||||||
console.log(data);
|
|
||||||
if (data?.result?.length === 0) {
|
if (data?.result?.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -105,10 +108,25 @@ const showPaginationItem = (total: number, range: number[]): JSX.Element => (
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function MessagingQueuesTable({
|
function MessagingQueuesTable({
|
||||||
currentTab,
|
currentTab,
|
||||||
|
selectedView,
|
||||||
|
tableApiPayload,
|
||||||
|
tableApi,
|
||||||
|
validConfigPresent = false,
|
||||||
|
type = 'Detail',
|
||||||
}: {
|
}: {
|
||||||
currentTab: ConsumerLagDetailType;
|
currentTab?: MessagingQueueServiceDetailType;
|
||||||
|
selectedView: MessagingQueuesViewTypeOptions;
|
||||||
|
tableApiPayload?: MessagingQueueServicePayload;
|
||||||
|
tableApi: (
|
||||||
|
props: MessagingQueueServicePayload,
|
||||||
|
) => Promise<
|
||||||
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
|
>;
|
||||||
|
validConfigPresent?: boolean;
|
||||||
|
type?: 'Detail' | 'Overview';
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const [columns, setColumns] = useState<any[]>([]);
|
const [columns, setColumns] = useState<any[]>([]);
|
||||||
const [tableData, setTableData] = useState<any[]>([]);
|
const [tableData, setTableData] = useState<any[]>([]);
|
||||||
@ -118,11 +136,22 @@ function MessagingQueuesTable({
|
|||||||
const timelineQuery = decodeURIComponent(
|
const timelineQuery = decodeURIComponent(
|
||||||
urlQuery.get(QueryParams.selectedTimelineQuery) || '',
|
urlQuery.get(QueryParams.selectedTimelineQuery) || '',
|
||||||
);
|
);
|
||||||
|
|
||||||
const timelineQueryData: SelectedTimelineQuery = useMemo(
|
const timelineQueryData: SelectedTimelineQuery = useMemo(
|
||||||
() => (timelineQuery ? JSON.parse(timelineQuery) : {}),
|
() => (timelineQuery ? JSON.parse(timelineQuery) : {}),
|
||||||
[timelineQuery],
|
[timelineQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const configDetails = decodeURIComponent(
|
||||||
|
urlQuery.get(QueryParams.configDetail) || '',
|
||||||
|
);
|
||||||
|
|
||||||
|
const configDetailQueryData: {
|
||||||
|
[key: string]: string;
|
||||||
|
} = useMemo(() => (configDetails ? JSON.parse(configDetails) : {}), [
|
||||||
|
configDetails,
|
||||||
|
]);
|
||||||
|
|
||||||
const paginationConfig = useMemo(
|
const paginationConfig = useMemo(
|
||||||
() =>
|
() =>
|
||||||
tableData?.length > 20 && {
|
tableData?.length > 20 && {
|
||||||
@ -134,90 +163,104 @@ function MessagingQueuesTable({
|
|||||||
[tableData],
|
[tableData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const props: ConsumerLagPayload = useMemo(
|
|
||||||
() => ({
|
|
||||||
start: (timelineQueryData?.start || 0) * 1e9,
|
|
||||||
end: (timelineQueryData?.end || 0) * 1e9,
|
|
||||||
variables: {
|
|
||||||
partition: timelineQueryData?.partition,
|
|
||||||
topic: timelineQueryData?.topic,
|
|
||||||
consumer_group: timelineQueryData?.group,
|
|
||||||
},
|
|
||||||
detailType: currentTab,
|
|
||||||
}),
|
|
||||||
[currentTab, timelineQueryData],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleConsumerDetailsOnError = (error: Error): void => {
|
const handleConsumerDetailsOnError = (error: Error): void => {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: axios.isAxiosError(error) ? error?.message : SOMETHING_WENT_WRONG,
|
message: axios.isAxiosError(error) ? error?.message : SOMETHING_WENT_WRONG,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const { mutate: getConsumerDetails, isLoading } = useMutation(
|
const { mutate: getViewDetails, isLoading } = useMutation(tableApi, {
|
||||||
getConsumerLagDetails,
|
onSuccess: (data) => {
|
||||||
{
|
if (data.payload) {
|
||||||
onSuccess: (data) => {
|
setColumns(getColumns(data?.payload, history));
|
||||||
if (data.payload) {
|
setTableData(getTableData(data?.payload));
|
||||||
setColumns(getColumns(data?.payload, history));
|
}
|
||||||
setTableData(getTableData(data?.payload));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: handleConsumerDetailsOnError,
|
|
||||||
},
|
},
|
||||||
|
onError: handleConsumerDetailsOnError,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if (validConfigPresent && tableApiPayload) {
|
||||||
|
getViewDetails(tableApiPayload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[currentTab, selectedView, tableApiPayload],
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
const [selectedRowKey, setSelectedRowKey] = useState<React.Key>();
|
||||||
useEffect(() => getConsumerDetails(props), [currentTab, props]);
|
const [, setSelectedRows] = useState<any>();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
const isLogEventCalled = useRef<boolean>(false);
|
const onRowClick = (record: { [key: string]: string }): void => {
|
||||||
|
const selectedKey = record.key;
|
||||||
|
|
||||||
const isEmptyDetails = (timelineQueryData: SelectedTimelineQuery): boolean => {
|
if (`${selectedKey}_${selectedView}` === selectedRowKey) {
|
||||||
const isEmptyDetail =
|
setSelectedRowKey(undefined);
|
||||||
isEmpty(timelineQueryData) ||
|
setSelectedRows({});
|
||||||
(!timelineQueryData?.group &&
|
setConfigDetail(urlQuery, location, history, {});
|
||||||
!timelineQueryData?.topic &&
|
} else {
|
||||||
!timelineQueryData?.partition);
|
setSelectedRowKey(`${selectedKey}_${selectedView}`);
|
||||||
|
setSelectedRows(record);
|
||||||
|
|
||||||
if (!isEmptyDetail && !isLogEventCalled.current) {
|
if (!isEmpty(record)) {
|
||||||
logEvent('Messaging Queues: More details viewed', {
|
setConfigDetail(urlQuery, location, history, record);
|
||||||
'tab-option': ConsumerLagDetailTitle[currentTab],
|
}
|
||||||
variables: {
|
|
||||||
group: timelineQueryData?.group,
|
|
||||||
topic: timelineQueryData?.topic,
|
|
||||||
partition: timelineQueryData?.partition,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
isLogEventCalled.current = true;
|
|
||||||
}
|
}
|
||||||
return isEmptyDetail;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const subtitle =
|
||||||
|
selectedView === MessagingQueuesViewType.consumerLag.value
|
||||||
|
? `${timelineQueryData?.group || ''} ${timelineQueryData?.topic || ''} ${
|
||||||
|
timelineQueryData?.partition || ''
|
||||||
|
}`
|
||||||
|
: `${configDetailQueryData?.service_name || ''} ${
|
||||||
|
configDetailQueryData?.topic || ''
|
||||||
|
} ${configDetailQueryData?.partition || ''}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mq-tables-container">
|
<div className="mq-tables-container">
|
||||||
{isEmptyDetails(timelineQueryData) ? (
|
{!validConfigPresent ? (
|
||||||
<div className="no-data-style">
|
<div className="no-data-style">
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
Click on a co-ordinate above to see the details
|
{selectedView === MessagingQueuesViewType.consumerLag.value
|
||||||
|
? 'Click on a co-ordinate above to see the details'
|
||||||
|
: 'Click on a row above to see the details'}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="mq-table-title">
|
{currentTab && (
|
||||||
{ConsumerLagDetailTitle[currentTab]}
|
<div className="mq-table-title">
|
||||||
<div className="mq-table-subtitle">{`${timelineQueryData?.group || ''} ${
|
{ConsumerLagDetailTitle[currentTab]}
|
||||||
timelineQueryData?.topic || ''
|
<div className="mq-table-subtitle">{subtitle}</div>
|
||||||
} ${timelineQueryData?.partition || ''}`}</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<Table
|
<Table
|
||||||
className="mq-table"
|
className={cx(
|
||||||
|
'mq-table',
|
||||||
|
type !== 'Detail' ? 'mq-overview-row-clickable' : '',
|
||||||
|
)}
|
||||||
pagination={paginationConfig}
|
pagination={paginationConfig}
|
||||||
size="middle"
|
size="middle"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={tableData}
|
dataSource={tableData}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
|
onRow={(record): any =>
|
||||||
|
type !== 'Detail'
|
||||||
|
? {
|
||||||
|
onClick: (): void => onRowClick(record),
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
rowClassName={(record): any =>
|
||||||
|
`${record.key}_${selectedView}` === selectedRowKey
|
||||||
|
? 'ant-table-row-selected'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -2,18 +2,19 @@ import axios from 'api';
|
|||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { ConsumerLagDetailType } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
import { MessagingQueueServiceDetailType } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
export interface ConsumerLagPayload {
|
export interface MessagingQueueServicePayload {
|
||||||
start?: number | string;
|
start?: number | string;
|
||||||
end?: number | string;
|
end?: number | string;
|
||||||
variables: {
|
variables: {
|
||||||
partition?: string;
|
partition?: string;
|
||||||
topic?: string;
|
topic?: string;
|
||||||
consumer_group?: string;
|
consumer_group?: string;
|
||||||
|
service_name?: string;
|
||||||
};
|
};
|
||||||
detailType: ConsumerLagDetailType;
|
detailType?: MessagingQueueServiceDetailType | 'producer' | 'consumer';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessagingQueuesPayloadProps {
|
export interface MessagingQueuesPayloadProps {
|
||||||
@ -36,7 +37,7 @@ export interface MessagingQueuesPayloadProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getConsumerLagDetails = async (
|
export const getConsumerLagDetails = async (
|
||||||
props: ConsumerLagPayload,
|
props: MessagingQueueServicePayload,
|
||||||
): Promise<
|
): Promise<
|
||||||
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
> => {
|
> => {
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { MessagingQueueServiceDetailType } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MessagingQueueServicePayload,
|
||||||
|
MessagingQueuesPayloadProps,
|
||||||
|
} from './getConsumerLagDetails';
|
||||||
|
|
||||||
|
export const getPartitionLatencyDetails = async (
|
||||||
|
props: MessagingQueueServicePayload,
|
||||||
|
): Promise<
|
||||||
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
const { detailType, ...rest } = props;
|
||||||
|
let endpoint = '';
|
||||||
|
if (detailType === MessagingQueueServiceDetailType.ConsumerDetails) {
|
||||||
|
endpoint = `/messaging-queues/kafka/partition-latency/consumer`;
|
||||||
|
} else {
|
||||||
|
endpoint = `/messaging-queues/kafka/consumer-lag/producer-details`;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await axios.post(endpoint, {
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler((error as AxiosError) || SOMETHING_WENT_WRONG);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,34 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MessagingQueueServicePayload,
|
||||||
|
MessagingQueuesPayloadProps,
|
||||||
|
} from './getConsumerLagDetails';
|
||||||
|
|
||||||
|
export const getPartitionLatencyOverview = async (
|
||||||
|
props: Omit<MessagingQueueServicePayload, 'detailType' | 'variables'>,
|
||||||
|
): Promise<
|
||||||
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`/messaging-queues/kafka/partition-latency/overview`,
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler((error as AxiosError) || SOMETHING_WENT_WRONG);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,33 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MessagingQueueServicePayload,
|
||||||
|
MessagingQueuesPayloadProps,
|
||||||
|
} from './getConsumerLagDetails';
|
||||||
|
|
||||||
|
export const getTopicThroughputDetails = async (
|
||||||
|
props: MessagingQueueServicePayload,
|
||||||
|
): Promise<
|
||||||
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
const { detailType, ...rest } = props;
|
||||||
|
const endpoint = `/messaging-queues/kafka/topic-throughput/${detailType}`;
|
||||||
|
try {
|
||||||
|
const response = await axios.post(endpoint, {
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler((error as AxiosError) || SOMETHING_WENT_WRONG);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MessagingQueueServicePayload,
|
||||||
|
MessagingQueuesPayloadProps,
|
||||||
|
} from './getConsumerLagDetails';
|
||||||
|
|
||||||
|
export const getTopicThroughputOverview = async (
|
||||||
|
props: Omit<MessagingQueueServicePayload, 'variables'>,
|
||||||
|
): Promise<
|
||||||
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
const { detailType, start, end } = props;
|
||||||
|
console.log(detailType);
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`messaging-queues/kafka/topic-throughput/${detailType}`,
|
||||||
|
{
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler((error as AxiosError) || SOMETHING_WENT_WRONG);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,102 @@
|
|||||||
|
import './MQDetails.style.scss';
|
||||||
|
|
||||||
|
import { Radio } from 'antd';
|
||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MessagingQueuesViewType,
|
||||||
|
MessagingQueuesViewTypeOptions,
|
||||||
|
ProducerLatencyOptions,
|
||||||
|
} from '../MessagingQueuesUtils';
|
||||||
|
import { MessagingQueueServicePayload } from './MQTables/getConsumerLagDetails';
|
||||||
|
import { getPartitionLatencyOverview } from './MQTables/getPartitionLatencyOverview';
|
||||||
|
import { getTopicThroughputOverview } from './MQTables/getTopicThroughputOverview';
|
||||||
|
import MessagingQueuesTable from './MQTables/MQTables';
|
||||||
|
|
||||||
|
type SelectedViewType = keyof typeof MessagingQueuesViewType;
|
||||||
|
|
||||||
|
function PartitionLatencyTabs({
|
||||||
|
option,
|
||||||
|
setOption,
|
||||||
|
}: {
|
||||||
|
option: ProducerLatencyOptions;
|
||||||
|
setOption: Dispatch<SetStateAction<ProducerLatencyOptions>>;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Radio.Group
|
||||||
|
onChange={(e): void => setOption(e.target.value)}
|
||||||
|
value={option}
|
||||||
|
className="mq-details-options"
|
||||||
|
>
|
||||||
|
<Radio.Button
|
||||||
|
value={ProducerLatencyOptions.Producers}
|
||||||
|
key={ProducerLatencyOptions.Producers}
|
||||||
|
>
|
||||||
|
{ProducerLatencyOptions.Producers}
|
||||||
|
</Radio.Button>
|
||||||
|
<Radio.Button
|
||||||
|
value={ProducerLatencyOptions.Consumers}
|
||||||
|
key={ProducerLatencyOptions.Consumers}
|
||||||
|
>
|
||||||
|
{ProducerLatencyOptions.Consumers}
|
||||||
|
</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTableApi = (selectedView: MessagingQueuesViewTypeOptions): any => {
|
||||||
|
if (selectedView === MessagingQueuesViewType.producerLatency.value) {
|
||||||
|
return getTopicThroughputOverview;
|
||||||
|
}
|
||||||
|
return getPartitionLatencyOverview;
|
||||||
|
};
|
||||||
|
|
||||||
|
function MessagingQueueOverview({
|
||||||
|
selectedView,
|
||||||
|
option,
|
||||||
|
setOption,
|
||||||
|
}: {
|
||||||
|
selectedView: MessagingQueuesViewTypeOptions;
|
||||||
|
option: ProducerLatencyOptions;
|
||||||
|
setOption: Dispatch<SetStateAction<ProducerLatencyOptions>>;
|
||||||
|
}): JSX.Element {
|
||||||
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableApiPayload: MessagingQueueServicePayload = {
|
||||||
|
variables: {},
|
||||||
|
start: minTime,
|
||||||
|
end: maxTime,
|
||||||
|
detailType:
|
||||||
|
// eslint-disable-next-line no-nested-ternary
|
||||||
|
selectedView === MessagingQueuesViewType.producerLatency.value
|
||||||
|
? option === ProducerLatencyOptions.Producers
|
||||||
|
? 'producer'
|
||||||
|
: 'consumer'
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mq-overview-container">
|
||||||
|
{selectedView === MessagingQueuesViewType.producerLatency.value ? (
|
||||||
|
<PartitionLatencyTabs option={option} setOption={setOption} />
|
||||||
|
) : (
|
||||||
|
<div className="mq-overview-title">
|
||||||
|
{MessagingQueuesViewType[selectedView as SelectedViewType].label}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<MessagingQueuesTable
|
||||||
|
selectedView={selectedView}
|
||||||
|
tableApiPayload={tableApiPayload}
|
||||||
|
tableApi={getTableApi(selectedView)}
|
||||||
|
validConfigPresent
|
||||||
|
type="Overview"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default MessagingQueueOverview;
|
@ -106,6 +106,8 @@
|
|||||||
|
|
||||||
.mq-details-options {
|
.mq-details-options {
|
||||||
letter-spacing: -0.06px;
|
letter-spacing: -0.06px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
.ant-radio-button-wrapper {
|
.ant-radio-button-wrapper {
|
||||||
border-color: var(--bg-slate-400);
|
border-color: var(--bg-slate-400);
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
|
@ -3,12 +3,21 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
|||||||
import { GetWidgetQueryBuilderProps } from 'container/MetricsApplication/types';
|
import { GetWidgetQueryBuilderProps } from 'container/MetricsApplication/types';
|
||||||
import { History, Location } from 'history';
|
import { History, Location } from 'history';
|
||||||
import { isEmpty } from 'lodash-es';
|
import { isEmpty } from 'lodash-es';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getConsumerLagDetails,
|
||||||
|
MessagingQueueServicePayload,
|
||||||
|
MessagingQueuesPayloadProps,
|
||||||
|
} from './MQDetails/MQTables/getConsumerLagDetails';
|
||||||
|
import { getPartitionLatencyDetails } from './MQDetails/MQTables/getPartitionLatencyDetails';
|
||||||
|
import { getTopicThroughputDetails } from './MQDetails/MQTables/getTopicThroughputDetails';
|
||||||
|
|
||||||
export const KAFKA_SETUP_DOC_LINK =
|
export const KAFKA_SETUP_DOC_LINK =
|
||||||
'https://signoz.io/docs/messaging-queues/kafka?utm_source=product&utm_medium=kafka-get-started';
|
'https://signoz.io/docs/messaging-queues/kafka?utm_source=product&utm_medium=kafka-get-started';
|
||||||
|
|
||||||
@ -24,14 +33,17 @@ export type RowData = {
|
|||||||
[key: string]: string | number;
|
[key: string]: string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum ConsumerLagDetailType {
|
export enum MessagingQueueServiceDetailType {
|
||||||
ConsumerDetails = 'consumer-details',
|
ConsumerDetails = 'consumer-details',
|
||||||
ProducerDetails = 'producer-details',
|
ProducerDetails = 'producer-details',
|
||||||
NetworkLatency = 'network-latency',
|
NetworkLatency = 'network-latency',
|
||||||
PartitionHostMetrics = 'partition-host-metric',
|
PartitionHostMetrics = 'partition-host-metric',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConsumerLagDetailTitle: Record<ConsumerLagDetailType, string> = {
|
export const ConsumerLagDetailTitle: Record<
|
||||||
|
MessagingQueueServiceDetailType,
|
||||||
|
string
|
||||||
|
> = {
|
||||||
'consumer-details': 'Consumer Groups Details',
|
'consumer-details': 'Consumer Groups Details',
|
||||||
'producer-details': 'Producer Details',
|
'producer-details': 'Producer Details',
|
||||||
'network-latency': 'Network Latency',
|
'network-latency': 'Network Latency',
|
||||||
@ -205,21 +217,130 @@ export function setSelectedTimelineQuery(
|
|||||||
history.replace(generatedUrl);
|
history.replace(generatedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum MessagingQueuesViewTypeOptions {
|
||||||
|
ConsumerLag = 'consumerLag',
|
||||||
|
PartitionLatency = 'partitionLatency',
|
||||||
|
ProducerLatency = 'producerLatency',
|
||||||
|
ConsumerLatency = 'consumerLatency',
|
||||||
|
}
|
||||||
|
|
||||||
export const MessagingQueuesViewType = {
|
export const MessagingQueuesViewType = {
|
||||||
consumerLag: {
|
consumerLag: {
|
||||||
label: 'Consumer Lag view',
|
label: 'Consumer Lag view',
|
||||||
value: 'consumerLag',
|
value: MessagingQueuesViewTypeOptions.ConsumerLag,
|
||||||
},
|
},
|
||||||
partitionLatency: {
|
partitionLatency: {
|
||||||
label: 'Partition Latency view',
|
label: 'Partition Latency view',
|
||||||
value: 'partitionLatency',
|
value: MessagingQueuesViewTypeOptions.PartitionLatency,
|
||||||
},
|
},
|
||||||
producerLatency: {
|
producerLatency: {
|
||||||
label: 'Producer Latency view',
|
label: 'Producer Latency view',
|
||||||
value: 'producerLatency',
|
value: MessagingQueuesViewTypeOptions.ProducerLatency,
|
||||||
},
|
},
|
||||||
consumerLatency: {
|
consumerLatency: {
|
||||||
label: 'Consumer latency view',
|
label: 'Consumer latency view',
|
||||||
value: 'consumerLatency',
|
value: MessagingQueuesViewTypeOptions.ConsumerLatency,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function setConfigDetail(
|
||||||
|
urlQuery: URLSearchParams,
|
||||||
|
location: Location<unknown>,
|
||||||
|
history: History<unknown>,
|
||||||
|
paramsToSet?: {
|
||||||
|
[key: string]: string;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
|
// remove "key" and its value from the paramsToSet object
|
||||||
|
const { key, ...restParamsToSet } = paramsToSet || {};
|
||||||
|
|
||||||
|
if (!isEmpty(restParamsToSet)) {
|
||||||
|
const configDetail = {
|
||||||
|
...restParamsToSet,
|
||||||
|
};
|
||||||
|
urlQuery.set(
|
||||||
|
QueryParams.configDetail,
|
||||||
|
encodeURIComponent(JSON.stringify(configDetail)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
urlQuery.delete(QueryParams.configDetail);
|
||||||
|
}
|
||||||
|
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||||
|
history.replace(generatedUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProducerLatencyOptions {
|
||||||
|
Producers = 'Producers',
|
||||||
|
Consumers = 'Consumers',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MetaDataAndAPI {
|
||||||
|
tableApiPayload: MessagingQueueServicePayload;
|
||||||
|
tableApi: (
|
||||||
|
props: MessagingQueueServicePayload,
|
||||||
|
) => Promise<
|
||||||
|
SuccessResponse<MessagingQueuesPayloadProps['payload']> | ErrorResponse
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
interface MetaDataAndAPIPerView {
|
||||||
|
detailType: MessagingQueueServiceDetailType;
|
||||||
|
selectedTimelineQuery: SelectedTimelineQuery;
|
||||||
|
configDetails?: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
minTime: number;
|
||||||
|
maxTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMetaDataAndAPIPerView = (
|
||||||
|
metaDataProps: MetaDataAndAPIPerView,
|
||||||
|
): Record<string, MetaDataAndAPI> => {
|
||||||
|
const {
|
||||||
|
detailType,
|
||||||
|
minTime,
|
||||||
|
maxTime,
|
||||||
|
selectedTimelineQuery,
|
||||||
|
configDetails,
|
||||||
|
} = metaDataProps;
|
||||||
|
return {
|
||||||
|
[MessagingQueuesViewType.consumerLag.value]: {
|
||||||
|
tableApiPayload: {
|
||||||
|
start: (selectedTimelineQuery?.start || 0) * 1e9,
|
||||||
|
end: (selectedTimelineQuery?.end || 0) * 1e9,
|
||||||
|
variables: {
|
||||||
|
partition: selectedTimelineQuery?.partition,
|
||||||
|
topic: selectedTimelineQuery?.topic,
|
||||||
|
consumer_group: selectedTimelineQuery?.group,
|
||||||
|
},
|
||||||
|
detailType,
|
||||||
|
},
|
||||||
|
tableApi: getConsumerLagDetails,
|
||||||
|
},
|
||||||
|
[MessagingQueuesViewType.partitionLatency.value]: {
|
||||||
|
tableApiPayload: {
|
||||||
|
start: minTime,
|
||||||
|
end: maxTime,
|
||||||
|
variables: {
|
||||||
|
partition: configDetails?.partition,
|
||||||
|
topic: configDetails?.topic,
|
||||||
|
consumer_group: configDetails?.group,
|
||||||
|
},
|
||||||
|
detailType,
|
||||||
|
},
|
||||||
|
tableApi: getPartitionLatencyDetails,
|
||||||
|
},
|
||||||
|
[MessagingQueuesViewType.producerLatency.value]: {
|
||||||
|
tableApiPayload: {
|
||||||
|
start: minTime,
|
||||||
|
end: maxTime,
|
||||||
|
variables: {
|
||||||
|
partition: configDetails?.partition,
|
||||||
|
topic: configDetails?.topic,
|
||||||
|
service_name: configDetails?.service_name,
|
||||||
|
},
|
||||||
|
detailType,
|
||||||
|
},
|
||||||
|
tableApi: getTopicThroughputDetails,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user