mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 13:25:54 +08:00
Shifting of graph from Dashboard to Service layer (#3107)
* refactor: initial setup for full view done * feat: done with shifting of chart to services * refactor: removed the dependency dashboard action * refactor: make ondelete and onclone optional in widgetheader * refactor: optimised the allowdelete, allowclone and allowEdit * fix: build pipline error * refactor: moved contant to contant.js * refactor: create a utils and types and seperated it from component * refactor: merge the latest overview from develop * refactor: review comments changes * refactor: magic string to constants * refactor: handle the isloading for topLevelOperations * refactor: apply loading check for other api's * refactor: seperated the component * refactor: removed the graphwithoutdashboard component * fix: the type of variable from dashboard * refactor: created utils and updated types * refactor: changed the name of variable and fixed typos * fix: the menu option dropdown for services widget header * chore: ts config is updated for the isTwidgetoptions * chore: removed the unwanted file * fix: css on hover of widget header and default value * refactor: renamed the file to index in fullView * refactor: disable the edit delete clone option * fix: typos * chore: types are updated in metrics application * chore: type is updated * fix: build is fixed * refactor: changes the yaxisunit to ns of serviceoverview --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com> Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
This commit is contained in:
parent
f3817d7335
commit
50142321f7
@ -1,137 +0,0 @@
|
||||
import { Button } from 'antd';
|
||||
import { GraphOnClickHandler } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import TimePreference from 'components/TimePreferenceDropDown';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import {
|
||||
timeItems,
|
||||
timePreferance,
|
||||
} from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import getChartData from 'lib/getChartData';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { TimeContainer } from './styles';
|
||||
|
||||
function FullView({
|
||||
widget,
|
||||
fullViewOptions = true,
|
||||
onClickHandler,
|
||||
name,
|
||||
yAxisUnit,
|
||||
onDragSelect,
|
||||
isDependedDataLoaded = false,
|
||||
}: FullViewProps): JSX.Element {
|
||||
const { selectedTime: globalSelectedTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
|
||||
const getSelectedTime = useCallback(
|
||||
() =>
|
||||
timeItems.find((e) => e.enum === (widget?.timePreferance || 'GLOBAL_TIME')),
|
||||
[widget],
|
||||
);
|
||||
|
||||
const [selectedTime, setSelectedTime] = useState<timePreferance>({
|
||||
name: getSelectedTime()?.name || '',
|
||||
enum: widget?.timePreferance || 'GLOBAL_TIME',
|
||||
});
|
||||
|
||||
const queryKey = useMemo(
|
||||
() =>
|
||||
`FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
|
||||
[selectedTime, globalSelectedTime, widget],
|
||||
);
|
||||
|
||||
const updatedQuery = useStepInterval(widget?.query);
|
||||
|
||||
const response = useGetQueryRange(
|
||||
{
|
||||
selectedTime: selectedTime.enum,
|
||||
graphType: widget.panelTypes,
|
||||
query: updatedQuery,
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(),
|
||||
},
|
||||
{
|
||||
queryKey,
|
||||
enabled: !isDependedDataLoaded,
|
||||
},
|
||||
);
|
||||
|
||||
const chartDataSet = useMemo(
|
||||
() =>
|
||||
getChartData({
|
||||
queryData: [
|
||||
{
|
||||
queryData: response?.data?.payload?.data?.result || [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[response],
|
||||
);
|
||||
|
||||
if (response.status === 'idle' || response.status === 'loading') {
|
||||
return <Spinner height="100%" size="large" tip="Loading..." />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{fullViewOptions && (
|
||||
<TimeContainer>
|
||||
<TimePreference
|
||||
selectedTime={selectedTime}
|
||||
setSelectedTime={setSelectedTime}
|
||||
/>
|
||||
<Button
|
||||
onClick={(): void => {
|
||||
response.refetch();
|
||||
}}
|
||||
type="primary"
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</TimeContainer>
|
||||
)}
|
||||
|
||||
<GridGraphComponent
|
||||
GRAPH_TYPES={widget.panelTypes}
|
||||
data={chartDataSet}
|
||||
isStacked={widget.isStacked}
|
||||
opacity={widget.opacity}
|
||||
title={widget.title}
|
||||
onClickHandler={onClickHandler}
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface FullViewProps {
|
||||
widget: Widgets;
|
||||
fullViewOptions?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
name: string;
|
||||
yAxisUnit?: string;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
isDependedDataLoaded?: boolean;
|
||||
}
|
||||
|
||||
FullView.defaultProps = {
|
||||
fullViewOptions: undefined,
|
||||
onClickHandler: undefined,
|
||||
yAxisUnit: undefined,
|
||||
onDragSelect: undefined,
|
||||
isDependedDataLoaded: undefined,
|
||||
};
|
||||
|
||||
export default FullView;
|
@ -1,5 +1,4 @@
|
||||
import { Button, Typography } from 'antd';
|
||||
import getQueryResult from 'api/widgets/getQuery';
|
||||
import { Button } from 'antd';
|
||||
import { GraphOnClickHandler } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import TimePreference from 'components/TimePreferenceDropDown';
|
||||
@ -7,22 +6,18 @@ import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import {
|
||||
timeItems,
|
||||
timePreferance,
|
||||
timePreferenceType,
|
||||
} from 'container/NewWidget/RightContainer/timeItems';
|
||||
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import getChartData from 'lib/getChartData';
|
||||
import GetMaxMinTime from 'lib/getMaxMinTime';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import getStartAndEndTime from 'lib/getStartAndEndTime';
|
||||
import getStep from 'lib/getStep';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { PromQLWidgets } from 'types/api/dashboard/getAll';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { NotFoundContainer, TimeContainer } from './styles';
|
||||
import { TimeContainer } from './styles';
|
||||
|
||||
function FullView({
|
||||
widget,
|
||||
@ -31,8 +26,9 @@ function FullView({
|
||||
name,
|
||||
yAxisUnit,
|
||||
onDragSelect,
|
||||
isDependedDataLoaded = false,
|
||||
}: FullViewProps): JSX.Element {
|
||||
const { minTime, maxTime, selectedTime: globalSelectedTime } = useSelector<
|
||||
const { selectedTime: globalSelectedTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
@ -48,110 +44,55 @@ function FullView({
|
||||
enum: widget?.timePreferance || 'GLOBAL_TIME',
|
||||
});
|
||||
|
||||
const maxMinTime = GetMaxMinTime({
|
||||
graphType: widget.panelTypes,
|
||||
maxTime,
|
||||
minTime,
|
||||
});
|
||||
|
||||
const getMinMax = (
|
||||
time: timePreferenceType,
|
||||
): { min: string | number; max: string | number } => {
|
||||
if (time === 'GLOBAL_TIME') {
|
||||
const minMax = GetMinMax(globalSelectedTime, [
|
||||
minTime / 1000000,
|
||||
maxTime / 1000000,
|
||||
]);
|
||||
return {
|
||||
min: convertToNanoSecondsToSecond(minMax.minTime / 1000),
|
||||
max: convertToNanoSecondsToSecond(minMax.maxTime / 1000),
|
||||
};
|
||||
}
|
||||
|
||||
const minMax = getStartAndEndTime({
|
||||
type: selectedTime.enum,
|
||||
maxTime: maxMinTime.maxTime,
|
||||
minTime: maxMinTime.minTime,
|
||||
});
|
||||
return { min: parseInt(minMax.start, 10), max: parseInt(minMax.end, 10) };
|
||||
};
|
||||
|
||||
const queryMinMax = getMinMax(selectedTime.enum);
|
||||
|
||||
const queryLength = widget.query.filter((e) => e.query.length !== 0);
|
||||
|
||||
const response = useQueries(
|
||||
queryLength.map((query) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
queryFn: () =>
|
||||
getQueryResult({
|
||||
end: queryMinMax.max.toString(),
|
||||
query: query.query,
|
||||
start: queryMinMax.min.toString(),
|
||||
step: `${getStep({
|
||||
start: queryMinMax.min,
|
||||
end: queryMinMax.max,
|
||||
inputFormat: 's',
|
||||
})}`,
|
||||
}),
|
||||
queryHash: `${query.query}-${query.legend}-${selectedTime.enum}`,
|
||||
retryOnMount: false,
|
||||
})),
|
||||
const queryKey = useMemo(
|
||||
() =>
|
||||
`FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
|
||||
[selectedTime, globalSelectedTime, widget],
|
||||
);
|
||||
|
||||
const isError =
|
||||
response.find((e) => e?.data?.statusCode !== 200) !== undefined ||
|
||||
response.some((e) => e.isError === true);
|
||||
const updatedQuery = useStepInterval(widget?.query);
|
||||
|
||||
const isLoading = response.some((e) => e.isLoading === true);
|
||||
|
||||
const errorMessage = response.find((e) => e.data?.error !== null)?.data?.error;
|
||||
|
||||
const data = response.map((responseOfQuery) =>
|
||||
responseOfQuery?.data?.payload?.result.map((e, index) => ({
|
||||
query: queryLength[index]?.query,
|
||||
queryData: e,
|
||||
legend: queryLength[index]?.legend,
|
||||
})),
|
||||
const response = useGetQueryRange(
|
||||
{
|
||||
selectedTime: selectedTime.enum,
|
||||
graphType: widget.panelTypes,
|
||||
query: updatedQuery,
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(),
|
||||
},
|
||||
{
|
||||
queryKey,
|
||||
enabled: !isDependedDataLoaded,
|
||||
},
|
||||
);
|
||||
|
||||
const chartDataSet = useMemo(
|
||||
() =>
|
||||
getChartData({
|
||||
queryData: data.map((e) => ({
|
||||
query: e?.map((e) => e.query).join(' ') || '',
|
||||
queryData: e?.map((e) => e.queryData) || [],
|
||||
legend: e?.map((e) => e.legend).join('') || '',
|
||||
})),
|
||||
queryData: [
|
||||
{
|
||||
queryData: response?.data?.payload?.data?.result || [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[data],
|
||||
[response],
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
if (response.status === 'idle' || response.status === 'loading') {
|
||||
return <Spinner height="100%" size="large" tip="Loading..." />;
|
||||
}
|
||||
|
||||
if (isError || data === undefined || data[0] === undefined) {
|
||||
return (
|
||||
<NotFoundContainer>
|
||||
<Typography>{errorMessage}</Typography>
|
||||
</NotFoundContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{fullViewOptions && (
|
||||
<TimeContainer>
|
||||
<TimePreference
|
||||
{...{
|
||||
selectedTime,
|
||||
setSelectedTime,
|
||||
}}
|
||||
selectedTime={selectedTime}
|
||||
setSelectedTime={setSelectedTime}
|
||||
/>
|
||||
<Button
|
||||
onClick={(): void => {
|
||||
response.forEach((e) => e.refetch());
|
||||
response.refetch();
|
||||
}}
|
||||
type="primary"
|
||||
>
|
||||
@ -176,12 +117,13 @@ function FullView({
|
||||
}
|
||||
|
||||
interface FullViewProps {
|
||||
widget: PromQLWidgets;
|
||||
widget: Widgets;
|
||||
fullViewOptions?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
name: string;
|
||||
yAxisUnit?: string;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
isDependedDataLoaded?: boolean;
|
||||
}
|
||||
|
||||
FullView.defaultProps = {
|
||||
@ -189,6 +131,7 @@ FullView.defaultProps = {
|
||||
onClickHandler: undefined,
|
||||
yAxisUnit: undefined,
|
||||
onDragSelect: undefined,
|
||||
isDependedDataLoaded: undefined,
|
||||
};
|
||||
|
||||
export default FullView;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Typography } from 'antd';
|
||||
import { ChartData } from 'chart.js';
|
||||
import { GraphOnClickHandler } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import { UpdateDashboard } from 'container/GridGraphLayout/utils';
|
||||
@ -35,12 +36,16 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import DashboardReducer from 'types/reducer/dashboards';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import {
|
||||
getSelectedDashboard,
|
||||
getSelectedDashboardVariable,
|
||||
} from 'utils/dashboard/selectedDashboard';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { LayoutProps } from '..';
|
||||
import EmptyWidget from '../EmptyWidget';
|
||||
import WidgetHeader from '../WidgetHeader';
|
||||
import FullView from './FullView/index.metricsBuilder';
|
||||
import FullView from './FullView';
|
||||
import { FullViewContainer, Modal } from './styles';
|
||||
|
||||
function GridCardGraph({
|
||||
@ -51,6 +56,10 @@ function GridCardGraph({
|
||||
layout = [],
|
||||
setLayout,
|
||||
onDragSelect,
|
||||
onClickHandler,
|
||||
allowDelete,
|
||||
allowClone,
|
||||
allowEdit,
|
||||
}: GridCardGraphProps): JSX.Element {
|
||||
const { ref: graphRef, inView: isGraphVisible } = useInView({
|
||||
threshold: 0,
|
||||
@ -77,9 +86,9 @@ function GridCardGraph({
|
||||
const { dashboards } = useSelector<AppState, DashboardReducer>(
|
||||
(state) => state.dashboards,
|
||||
);
|
||||
const [selectedDashboard] = dashboards;
|
||||
const selectedData = selectedDashboard?.data;
|
||||
const { variables } = selectedData;
|
||||
|
||||
const selectedDashboard = getSelectedDashboard(dashboards);
|
||||
const variables = getSelectedDashboardVariable(dashboards);
|
||||
|
||||
const updatedQuery = useStepInterval(widget?.query);
|
||||
|
||||
@ -172,10 +181,10 @@ function GridCardGraph({
|
||||
h: 2,
|
||||
y: 0,
|
||||
},
|
||||
...(selectedDashboard.data.layout || []),
|
||||
...(selectedDashboard?.data.layout || []),
|
||||
];
|
||||
|
||||
if (widget) {
|
||||
if (widget && selectedDashboard) {
|
||||
await UpdateDashboard(
|
||||
{
|
||||
data: selectedDashboard.data,
|
||||
@ -257,6 +266,9 @@ function GridCardGraph({
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
/>
|
||||
</div>
|
||||
<GridGraphComponent
|
||||
@ -267,6 +279,7 @@ function GridCardGraph({
|
||||
title={' '}
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onClickHandler={onClickHandler}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@ -289,6 +302,9 @@ function GridCardGraph({
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
/>
|
||||
</div>
|
||||
<GridGraphComponent
|
||||
@ -299,6 +315,7 @@ function GridCardGraph({
|
||||
title={' '}
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onClickHandler={onClickHandler}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@ -335,6 +352,9 @@ function GridCardGraph({
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
allowClone={allowClone}
|
||||
allowDelete={allowDelete}
|
||||
allowEdit={allowEdit}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -351,6 +371,7 @@ function GridCardGraph({
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={onClickHandler}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -374,10 +395,18 @@ interface GridCardGraphProps extends DispatchProps {
|
||||
// eslint-disable-next-line react/require-default-props
|
||||
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
allowDelete?: boolean;
|
||||
allowClone?: boolean;
|
||||
allowEdit?: boolean;
|
||||
}
|
||||
|
||||
GridCardGraph.defaultProps = {
|
||||
onDragSelect: undefined,
|
||||
onClickHandler: undefined,
|
||||
allowDelete: true,
|
||||
allowClone: true,
|
||||
allowEdit: true,
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (
|
||||
|
@ -0,0 +1,13 @@
|
||||
export enum MenuItemKeys {
|
||||
View = 'view',
|
||||
Edit = 'edit',
|
||||
Delete = 'delete',
|
||||
Clone = 'clone',
|
||||
}
|
||||
|
||||
export const MENUITEM_KEYS_VS_LABELS = {
|
||||
[MenuItemKeys.View]: 'View',
|
||||
[MenuItemKeys.Edit]: 'Edit',
|
||||
[MenuItemKeys.Delete]: 'Delete',
|
||||
[MenuItemKeys.Clone]: 'Clone',
|
||||
};
|
@ -27,24 +27,29 @@ import {
|
||||
spinnerStyles,
|
||||
tooltipStyles,
|
||||
} from './config';
|
||||
import { MENUITEM_KEYS_VS_LABELS, MenuItemKeys } from './contants';
|
||||
import {
|
||||
ArrowContainer,
|
||||
HeaderContainer,
|
||||
HeaderContentContainer,
|
||||
} from './styles';
|
||||
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types';
|
||||
import { generateMenuList, isTWidgetOptions } from './utils';
|
||||
|
||||
type TWidgetOptions = 'view' | 'edit' | 'delete' | string;
|
||||
interface IWidgetHeaderProps {
|
||||
title: string;
|
||||
widget: Widgets;
|
||||
onView: VoidFunction;
|
||||
onDelete: VoidFunction;
|
||||
onClone: VoidFunction;
|
||||
onDelete?: VoidFunction;
|
||||
onClone?: VoidFunction;
|
||||
parentHover: boolean;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
||||
>;
|
||||
errorMessage: string | undefined;
|
||||
allowDelete?: boolean;
|
||||
allowClone?: boolean;
|
||||
allowEdit?: boolean;
|
||||
}
|
||||
function WidgetHeader({
|
||||
title,
|
||||
@ -55,6 +60,9 @@ function WidgetHeader({
|
||||
parentHover,
|
||||
queryResponse,
|
||||
errorMessage,
|
||||
allowClone = true,
|
||||
allowDelete = true,
|
||||
allowEdit = true,
|
||||
}: IWidgetHeaderProps): JSX.Element {
|
||||
const [localHover, setLocalHover] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
@ -70,24 +78,22 @@ function WidgetHeader({
|
||||
);
|
||||
}, [widget.id, widget.panelTypes, widget.query]);
|
||||
|
||||
const keyMethodMapping: {
|
||||
[K in TWidgetOptions]: { key: TWidgetOptions; method: VoidFunction };
|
||||
} = useMemo(
|
||||
const keyMethodMapping: KeyMethodMappingProps<TWidgetOptions> = useMemo(
|
||||
() => ({
|
||||
view: {
|
||||
key: 'view',
|
||||
key: MenuItemKeys.View,
|
||||
method: onView,
|
||||
},
|
||||
edit: {
|
||||
key: 'edit',
|
||||
key: MenuItemKeys.Edit,
|
||||
method: onEditHandler,
|
||||
},
|
||||
delete: {
|
||||
key: 'delete',
|
||||
key: MenuItemKeys.Delete,
|
||||
method: onDelete,
|
||||
},
|
||||
clone: {
|
||||
key: 'clone',
|
||||
key: MenuItemKeys.Clone,
|
||||
method: onClone,
|
||||
},
|
||||
}),
|
||||
@ -95,11 +101,13 @@ function WidgetHeader({
|
||||
);
|
||||
|
||||
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
|
||||
({ key }: { key: TWidgetOptions }): void => {
|
||||
const functionToCall = keyMethodMapping[key]?.method;
|
||||
if (functionToCall) {
|
||||
functionToCall();
|
||||
setIsOpen(false);
|
||||
({ key }: { key: string }): void => {
|
||||
if (isTWidgetOptions(key)) {
|
||||
const functionToCall = keyMethodMapping[key]?.method;
|
||||
if (functionToCall) {
|
||||
functionToCall();
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
[keyMethodMapping],
|
||||
@ -111,45 +119,53 @@ function WidgetHeader({
|
||||
role,
|
||||
);
|
||||
|
||||
const menuList: MenuItemType[] = useMemo(
|
||||
() => [
|
||||
const actions = useMemo(
|
||||
(): MenuItem[] => [
|
||||
{
|
||||
key: keyMethodMapping.view.key,
|
||||
key: MenuItemKeys.View,
|
||||
icon: <FullscreenOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
|
||||
isVisible: true,
|
||||
disabled: queryResponse.isLoading,
|
||||
label: 'View',
|
||||
},
|
||||
{
|
||||
key: keyMethodMapping.edit.key,
|
||||
key: MenuItemKeys.Edit,
|
||||
icon: <EditFilled />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
|
||||
isVisible: allowEdit,
|
||||
disabled: !editWidget,
|
||||
label: 'Edit',
|
||||
},
|
||||
{
|
||||
key: keyMethodMapping.clone.key,
|
||||
key: MenuItemKeys.Clone,
|
||||
icon: <CopyOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
|
||||
isVisible: allowClone,
|
||||
disabled: !editWidget,
|
||||
label: 'Clone',
|
||||
},
|
||||
{
|
||||
key: keyMethodMapping.delete.key,
|
||||
key: MenuItemKeys.Delete,
|
||||
icon: <DeleteOutlined />,
|
||||
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
|
||||
isVisible: allowDelete,
|
||||
disabled: !deleteWidget,
|
||||
danger: true,
|
||||
label: 'Delete',
|
||||
},
|
||||
],
|
||||
[
|
||||
allowEdit,
|
||||
allowClone,
|
||||
allowDelete,
|
||||
queryResponse.isLoading,
|
||||
deleteWidget,
|
||||
editWidget,
|
||||
keyMethodMapping.delete.key,
|
||||
keyMethodMapping.edit.key,
|
||||
keyMethodMapping.view.key,
|
||||
keyMethodMapping.clone.key,
|
||||
queryResponse.isLoading,
|
||||
],
|
||||
);
|
||||
|
||||
const menuList: MenuItemType[] = useMemo(
|
||||
(): MenuItemType[] => generateMenuList(actions, keyMethodMapping),
|
||||
[actions, keyMethodMapping],
|
||||
);
|
||||
|
||||
const onClickHandler = useCallback(() => {
|
||||
setIsOpen((open) => !open);
|
||||
}, []);
|
||||
@ -200,4 +216,12 @@ function WidgetHeader({
|
||||
);
|
||||
}
|
||||
|
||||
WidgetHeader.defaultProps = {
|
||||
onDelete: undefined,
|
||||
onClone: undefined,
|
||||
allowDelete: true,
|
||||
allowClone: true,
|
||||
allowEdit: true,
|
||||
};
|
||||
|
||||
export default WidgetHeader;
|
||||
|
@ -9,6 +9,8 @@ export const HeaderContainer = styled.div<{ hover: boolean }>`
|
||||
font-size: 0.8rem;
|
||||
cursor: all-scroll;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
`;
|
||||
|
||||
export const HeaderContentContainer = styled.span`
|
||||
|
25
frontend/src/container/GridGraphLayout/WidgetHeader/types.ts
Normal file
25
frontend/src/container/GridGraphLayout/WidgetHeader/types.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { MenuItemKeys } from './contants';
|
||||
|
||||
export interface MenuItem {
|
||||
key: TWidgetOptions;
|
||||
icon: ReactNode;
|
||||
label: string;
|
||||
isVisible: boolean;
|
||||
disabled: boolean;
|
||||
danger?: boolean;
|
||||
}
|
||||
|
||||
export type TWidgetOptions =
|
||||
| MenuItemKeys.View
|
||||
| MenuItemKeys.Edit
|
||||
| MenuItemKeys.Delete
|
||||
| MenuItemKeys.Clone;
|
||||
|
||||
export type KeyMethodMappingProps<T extends TWidgetOptions> = {
|
||||
[K in T]: {
|
||||
key: TWidgetOptions;
|
||||
method?: VoidFunction;
|
||||
};
|
||||
};
|
24
frontend/src/container/GridGraphLayout/WidgetHeader/utils.ts
Normal file
24
frontend/src/container/GridGraphLayout/WidgetHeader/utils.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { MenuItemType } from 'antd/es/menu/hooks/useItems';
|
||||
|
||||
import { MenuItemKeys } from './contants';
|
||||
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types';
|
||||
|
||||
export const generateMenuList = (
|
||||
actions: MenuItem[],
|
||||
keyMethodMapping: KeyMethodMappingProps<TWidgetOptions>,
|
||||
): MenuItemType[] =>
|
||||
actions
|
||||
.filter((action: MenuItem) => action.isVisible)
|
||||
.map(({ key, icon: Icon, label, disabled, ...rest }) => ({
|
||||
key: keyMethodMapping[key].key,
|
||||
icon: Icon,
|
||||
label,
|
||||
disabled,
|
||||
...rest,
|
||||
}));
|
||||
|
||||
export const isTWidgetOptions = (value: string): value is TWidgetOptions =>
|
||||
value === MenuItemKeys.View ||
|
||||
value === MenuItemKeys.Edit ||
|
||||
value === MenuItemKeys.Delete ||
|
||||
value === MenuItemKeys.Clone;
|
@ -2,7 +2,10 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const getWidgetQueryBuilder = (query: Widgets['query']): Widgets => ({
|
||||
export const getWidgetQueryBuilder = (
|
||||
query: Widgets['query'],
|
||||
title = '',
|
||||
): Widgets => ({
|
||||
description: '',
|
||||
id: v4(),
|
||||
isStacked: false,
|
||||
@ -11,5 +14,5 @@ export const getWidgetQueryBuilder = (query: Widgets['query']): Widgets => ({
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
query,
|
||||
timePreferance: 'GLOBAL_TIME',
|
||||
title: '',
|
||||
title,
|
||||
});
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryBuilderData } from 'types/common/queryBuilder';
|
||||
import { DataSource, QueryBuilderData } from 'types/common/queryBuilder';
|
||||
|
||||
import { DataType, FORMULA, MetricsType, WidgetKeys } from '../constant';
|
||||
import { IServiceName } from '../Tabs/types';
|
||||
import {
|
||||
getQueryBuilderQueries,
|
||||
getQueryBuilderQuerieswithFormula,
|
||||
@ -12,35 +15,42 @@ export const databaseCallsRPS = ({
|
||||
legend,
|
||||
tagFilterItems,
|
||||
}: DatabaseCallsRPSProps): QueryBuilderData => {
|
||||
const metricName: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
isColumn: true,
|
||||
key: 'signoz_db_latency_count',
|
||||
type: null,
|
||||
};
|
||||
const groupBy: BaseAutocompleteData[] = [
|
||||
{ dataType: 'string', isColumn: false, key: 'db_system', type: 'tag' },
|
||||
];
|
||||
const itemsA: TagFilterItem[] = [
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
},
|
||||
op: 'IN',
|
||||
value: [`${servicename}`],
|
||||
key: WidgetKeys.SignozDBLatencyCount,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: null,
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
const groupBy: BaseAutocompleteData[] = [
|
||||
{ dataType: DataType.STRING, isColumn: false, key: 'db_system', type: 'tag' },
|
||||
];
|
||||
const filterItems: TagFilterItem[][] = [
|
||||
[
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
...tagFilterItems,
|
||||
],
|
||||
];
|
||||
|
||||
const legends = [legend];
|
||||
|
||||
return getQueryBuilderQueries({
|
||||
metricName,
|
||||
autocompleteData,
|
||||
groupBy,
|
||||
legend,
|
||||
itemsA,
|
||||
legends,
|
||||
filterItems,
|
||||
dataSource: DataSource.METRICS,
|
||||
});
|
||||
};
|
||||
|
||||
@ -48,32 +58,29 @@ export const databaseCallsAvgDuration = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}: DatabaseCallProps): QueryBuilderData => {
|
||||
const metricNameA: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozDbLatencySum,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_db_latency_sum',
|
||||
type: null,
|
||||
};
|
||||
const metricNameB: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataB: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozDBLatencyCount,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_db_latency_count',
|
||||
type: null,
|
||||
};
|
||||
const expression = 'A/B';
|
||||
const legendFormula = 'Average Duration';
|
||||
const legend = '';
|
||||
const disabled = true;
|
||||
|
||||
const additionalItemsA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
...tagFilterItems,
|
||||
@ -81,14 +88,14 @@ export const databaseCallsAvgDuration = ({
|
||||
const additionalItemsB = additionalItemsA;
|
||||
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
metricNameA,
|
||||
metricNameB,
|
||||
autocompleteDataA,
|
||||
autocompleteDataB,
|
||||
additionalItemsA,
|
||||
additionalItemsB,
|
||||
legend,
|
||||
disabled,
|
||||
expression,
|
||||
legendFormula,
|
||||
legend: '',
|
||||
disabled: true,
|
||||
expression: FORMULA.DATABASE_CALLS_AVG_DURATION,
|
||||
legendFormula: 'Average Duration',
|
||||
});
|
||||
};
|
||||
|
||||
@ -97,6 +104,6 @@ interface DatabaseCallsRPSProps extends DatabaseCallProps {
|
||||
}
|
||||
|
||||
interface DatabaseCallProps {
|
||||
servicename: string | undefined;
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryBuilderData } from 'types/common/queryBuilder';
|
||||
import { DataSource, QueryBuilderData } from 'types/common/queryBuilder';
|
||||
|
||||
import { DataType, FORMULA, MetricsType, WidgetKeys } from '../constant';
|
||||
import { IServiceName } from '../Tabs/types';
|
||||
import {
|
||||
getQueryBuilderQueries,
|
||||
getQueryBuilderQuerieswithFormula,
|
||||
} from './MetricsPageQueriesFactory';
|
||||
|
||||
const groupBy: BaseAutocompleteData[] = [
|
||||
{ dataType: 'string', isColumn: false, key: 'address', type: 'tag' },
|
||||
{
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: WidgetKeys.Address,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
];
|
||||
|
||||
export const externalCallErrorPercent = ({
|
||||
@ -16,39 +24,39 @@ export const externalCallErrorPercent = ({
|
||||
legend,
|
||||
tagFilterItems,
|
||||
}: ExternalCallDurationByAddressProps): QueryBuilderData => {
|
||||
const metricNameA: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozExternalCallLatencyCount,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_count',
|
||||
type: null,
|
||||
};
|
||||
const metricNameB: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataB: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozExternalCallLatencyCount,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_count',
|
||||
type: null,
|
||||
};
|
||||
const additionalItemsA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'int64',
|
||||
key: WidgetKeys.StatusCode,
|
||||
dataType: DataType.INT64,
|
||||
isColumn: false,
|
||||
key: 'status_code',
|
||||
type: 'tag',
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: ['STATUS_CODE_ERROR'],
|
||||
},
|
||||
...tagFilterItems,
|
||||
@ -57,22 +65,22 @@ export const externalCallErrorPercent = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
const legendFormula = legend;
|
||||
const expression = 'A*100/B';
|
||||
const expression = FORMULA.ERROR_PERCENTAGE;
|
||||
const disabled = true;
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
metricNameA,
|
||||
metricNameB,
|
||||
autocompleteDataA,
|
||||
autocompleteDataB,
|
||||
additionalItemsA,
|
||||
additionalItemsB,
|
||||
legend,
|
||||
@ -87,19 +95,19 @@ export const externalCallDuration = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}: ExternalCallProps): QueryBuilderData => {
|
||||
const metricNameA: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_sum',
|
||||
key: WidgetKeys.SignozExternalCallLatencySum,
|
||||
type: null,
|
||||
};
|
||||
const metricNameB: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataB: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_count',
|
||||
key: WidgetKeys.SignozExternalCallLatencyCount,
|
||||
type: null,
|
||||
};
|
||||
const expression = 'A/B';
|
||||
const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
|
||||
const legendFormula = 'Average Duration';
|
||||
const legend = '';
|
||||
const disabled = true;
|
||||
@ -107,12 +115,12 @@ export const externalCallDuration = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
key: WidgetKeys.Service_name,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
...tagFilterItems,
|
||||
@ -120,8 +128,8 @@ export const externalCallDuration = ({
|
||||
const additionalItemsB = additionalItemsA;
|
||||
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
metricNameA,
|
||||
metricNameB,
|
||||
autocompleteDataA,
|
||||
autocompleteDataB,
|
||||
additionalItemsA,
|
||||
additionalItemsB,
|
||||
legend,
|
||||
@ -136,31 +144,38 @@ export const externalCallRpsByAddress = ({
|
||||
legend,
|
||||
tagFilterItems,
|
||||
}: ExternalCallDurationByAddressProps): QueryBuilderData => {
|
||||
const metricName: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_count',
|
||||
type: null,
|
||||
};
|
||||
const itemsA: TagFilterItem[] = [
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
},
|
||||
op: 'IN',
|
||||
value: [`${servicename}`],
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: WidgetKeys.SignozExternalCallLatencyCount,
|
||||
type: null,
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
const filterItems: TagFilterItem[][] = [
|
||||
[
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: WidgetKeys.Service_name,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
...tagFilterItems,
|
||||
],
|
||||
];
|
||||
|
||||
const legends: string[] = [legend];
|
||||
return getQueryBuilderQueries({
|
||||
metricName,
|
||||
autocompleteData,
|
||||
groupBy,
|
||||
legend,
|
||||
itemsA,
|
||||
legends,
|
||||
filterItems,
|
||||
dataSource: DataSource.METRICS,
|
||||
});
|
||||
};
|
||||
|
||||
@ -169,31 +184,31 @@ export const externalCallDurationByAddress = ({
|
||||
legend,
|
||||
tagFilterItems,
|
||||
}: ExternalCallDurationByAddressProps): QueryBuilderData => {
|
||||
const metricNameA: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_sum',
|
||||
key: WidgetKeys.SignozExternalCallLatencySum,
|
||||
type: null,
|
||||
};
|
||||
const metricNameB: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataB: BaseAutocompleteData = {
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_external_call_latency_count',
|
||||
key: WidgetKeys.SignozExternalCallLatencyCount,
|
||||
type: null,
|
||||
};
|
||||
const expression = 'A/B';
|
||||
const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
|
||||
const legendFormula = legend;
|
||||
const disabled = true;
|
||||
const additionalItemsA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
key: WidgetKeys.Service_name,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
...tagFilterItems,
|
||||
@ -201,8 +216,8 @@ export const externalCallDurationByAddress = ({
|
||||
const additionalItemsB = additionalItemsA;
|
||||
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
metricNameA,
|
||||
metricNameB,
|
||||
autocompleteDataA,
|
||||
autocompleteDataB,
|
||||
additionalItemsA,
|
||||
additionalItemsB,
|
||||
legend,
|
||||
@ -218,6 +233,6 @@ interface ExternalCallDurationByAddressProps extends ExternalCallProps {
|
||||
}
|
||||
|
||||
export interface ExternalCallProps {
|
||||
servicename: string | undefined;
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
}
|
||||
|
@ -5,27 +5,39 @@ import {
|
||||
import getStep from 'lib/getStep';
|
||||
import store from 'store';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
IBuilderQuery,
|
||||
TagFilterItem,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
DataSource,
|
||||
MetricAggregateOperator,
|
||||
QueryBuilderData,
|
||||
} from 'types/common/queryBuilder';
|
||||
|
||||
export const getQueryBuilderQueries = ({
|
||||
metricName,
|
||||
autocompleteData,
|
||||
groupBy = [],
|
||||
legend,
|
||||
itemsA,
|
||||
legends,
|
||||
filterItems,
|
||||
aggregateOperator,
|
||||
dataSource,
|
||||
queryNameAndExpression,
|
||||
}: BuilderQueriesProps): QueryBuilderData => ({
|
||||
queryFormulas: [],
|
||||
queryData: [
|
||||
{
|
||||
queryData: autocompleteData.map((item, index) => {
|
||||
const newQueryData: IBuilderQuery = {
|
||||
...initialQueryBuilderFormValuesMap.metrics,
|
||||
aggregateOperator: MetricAggregateOperator.SUM_RATE,
|
||||
aggregateOperator: ((): string => {
|
||||
if (aggregateOperator) {
|
||||
return aggregateOperator[index];
|
||||
}
|
||||
return MetricAggregateOperator.SUM_RATE;
|
||||
})(),
|
||||
disabled: false,
|
||||
groupBy,
|
||||
aggregateAttribute: metricName,
|
||||
legend,
|
||||
aggregateAttribute: item,
|
||||
legend: legends[index],
|
||||
stepInterval: getStep({
|
||||
end: store.getState().globalTime.maxTime,
|
||||
inputFormat: 'ns',
|
||||
@ -33,16 +45,24 @@ export const getQueryBuilderQueries = ({
|
||||
}),
|
||||
reduceTo: 'sum',
|
||||
filters: {
|
||||
items: itemsA,
|
||||
items: filterItems[index],
|
||||
op: 'AND',
|
||||
},
|
||||
},
|
||||
],
|
||||
dataSource,
|
||||
};
|
||||
|
||||
if (queryNameAndExpression) {
|
||||
newQueryData.queryName = queryNameAndExpression[index];
|
||||
newQueryData.expression = queryNameAndExpression[index];
|
||||
}
|
||||
|
||||
return newQueryData;
|
||||
}),
|
||||
});
|
||||
|
||||
export const getQueryBuilderQuerieswithFormula = ({
|
||||
metricNameA,
|
||||
metricNameB,
|
||||
autocompleteDataA,
|
||||
autocompleteDataB,
|
||||
additionalItemsA,
|
||||
additionalItemsB,
|
||||
legend,
|
||||
@ -65,7 +85,7 @@ export const getQueryBuilderQuerieswithFormula = ({
|
||||
disabled,
|
||||
groupBy,
|
||||
legend,
|
||||
aggregateAttribute: metricNameA,
|
||||
aggregateAttribute: autocompleteDataA,
|
||||
reduceTo: 'sum',
|
||||
filters: {
|
||||
items: additionalItemsA,
|
||||
@ -83,7 +103,7 @@ export const getQueryBuilderQuerieswithFormula = ({
|
||||
disabled,
|
||||
groupBy,
|
||||
legend,
|
||||
aggregateAttribute: metricNameB,
|
||||
aggregateAttribute: autocompleteDataB,
|
||||
queryName: 'B',
|
||||
expression: 'B',
|
||||
reduceTo: 'sum',
|
||||
@ -101,15 +121,18 @@ export const getQueryBuilderQuerieswithFormula = ({
|
||||
});
|
||||
|
||||
interface BuilderQueriesProps {
|
||||
metricName: BaseAutocompleteData;
|
||||
autocompleteData: BaseAutocompleteData[];
|
||||
groupBy?: BaseAutocompleteData[];
|
||||
legend: string;
|
||||
itemsA: TagFilterItem[];
|
||||
legends: string[];
|
||||
filterItems: TagFilterItem[][];
|
||||
aggregateOperator?: string[];
|
||||
dataSource: DataSource;
|
||||
queryNameAndExpression?: string[];
|
||||
}
|
||||
|
||||
interface BuilderQuerieswithFormulaProps {
|
||||
metricNameA: BaseAutocompleteData;
|
||||
metricNameB: BaseAutocompleteData;
|
||||
autocompleteDataA: BaseAutocompleteData;
|
||||
autocompleteDataB: BaseAutocompleteData;
|
||||
legend: string;
|
||||
disabled: boolean;
|
||||
groupBy?: BaseAutocompleteData[];
|
||||
|
@ -1,55 +1,151 @@
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryBuilderData } from 'types/common/queryBuilder';
|
||||
import { DataSource, QueryBuilderData } from 'types/common/queryBuilder';
|
||||
|
||||
import {
|
||||
DataType,
|
||||
FORMULA,
|
||||
GraphTitle,
|
||||
LETENCY_LEGENDS_AGGREGATEOPERATOR,
|
||||
MetricsType,
|
||||
OPERATION_LEGENDS,
|
||||
QUERYNAME_AND_EXPRESSION,
|
||||
WidgetKeys,
|
||||
} from '../constant';
|
||||
import { IServiceName } from '../Tabs/types';
|
||||
import {
|
||||
getQueryBuilderQueries,
|
||||
getQueryBuilderQuerieswithFormula,
|
||||
} from './MetricsPageQueriesFactory';
|
||||
|
||||
export const latency = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}: LatencyProps): QueryBuilderData => {
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: WidgetKeys.DurationNano,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
{
|
||||
key: WidgetKeys.DurationNano,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
{
|
||||
key: WidgetKeys.DurationNano,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
];
|
||||
|
||||
const filterItems: TagFilterItem[][] = [
|
||||
[
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.ServiceName,
|
||||
dataType: DataType.STRING,
|
||||
type: MetricsType.Tag,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: `${servicename}`,
|
||||
},
|
||||
...tagFilterItems,
|
||||
],
|
||||
[
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.ServiceName,
|
||||
dataType: DataType.STRING,
|
||||
type: MetricsType.Tag,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: `${servicename}`,
|
||||
},
|
||||
...tagFilterItems,
|
||||
],
|
||||
[
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.ServiceName,
|
||||
dataType: DataType.STRING,
|
||||
type: MetricsType.Tag,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: `${servicename}`,
|
||||
},
|
||||
...tagFilterItems,
|
||||
],
|
||||
];
|
||||
|
||||
return getQueryBuilderQueries({
|
||||
autocompleteData,
|
||||
legends: LETENCY_LEGENDS_AGGREGATEOPERATOR,
|
||||
filterItems,
|
||||
aggregateOperator: LETENCY_LEGENDS_AGGREGATEOPERATOR,
|
||||
dataSource: DataSource.TRACES,
|
||||
queryNameAndExpression: QUERYNAME_AND_EXPRESSION,
|
||||
});
|
||||
};
|
||||
|
||||
export const operationPerSec = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations,
|
||||
}: OperationPerSecProps): QueryBuilderData => {
|
||||
const metricName: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
isColumn: true,
|
||||
key: 'signoz_latency_count',
|
||||
type: null,
|
||||
};
|
||||
const legend = 'Operations';
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: WidgetKeys.SignozLatencyCount,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
type: null,
|
||||
},
|
||||
];
|
||||
|
||||
const itemsA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
const filterItems: TagFilterItem[][] = [
|
||||
[
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
op: 'IN',
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
isColumn: false,
|
||||
key: 'operation',
|
||||
type: 'tag',
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.Operation,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
value: topLevelOperations,
|
||||
},
|
||||
op: 'IN',
|
||||
value: topLevelOperations,
|
||||
},
|
||||
...tagFilterItems,
|
||||
...tagFilterItems,
|
||||
],
|
||||
];
|
||||
|
||||
return getQueryBuilderQueries({
|
||||
metricName,
|
||||
legend,
|
||||
itemsA,
|
||||
autocompleteData,
|
||||
legends: OPERATION_LEGENDS,
|
||||
filterItems,
|
||||
dataSource: DataSource.METRICS,
|
||||
});
|
||||
};
|
||||
|
||||
@ -58,50 +154,50 @@ export const errorPercentage = ({
|
||||
tagFilterItems,
|
||||
topLevelOperations,
|
||||
}: OperationPerSecProps): QueryBuilderData => {
|
||||
const metricNameA: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozCallsTotal,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_calls_total',
|
||||
type: null,
|
||||
};
|
||||
const metricNameB: BaseAutocompleteData = {
|
||||
dataType: 'float64',
|
||||
const autocompleteDataB: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozCallsTotal,
|
||||
dataType: DataType.FLOAT64,
|
||||
isColumn: true,
|
||||
key: 'signoz_calls_total',
|
||||
type: null,
|
||||
};
|
||||
const additionalItemsA: TagFilterItem[] = [
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Operation,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'operation',
|
||||
type: 'tag',
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: topLevelOperations,
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'int64',
|
||||
key: WidgetKeys.StatusCode,
|
||||
dataType: DataType.INT64,
|
||||
isColumn: false,
|
||||
key: 'status_code',
|
||||
type: 'tag',
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: ['STATUS_CODE_ERROR'],
|
||||
},
|
||||
...tagFilterItems,
|
||||
@ -111,46 +207,47 @@ export const errorPercentage = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Service_name,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'service_name',
|
||||
type: 'resource',
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: [`${servicename}`],
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
dataType: 'string',
|
||||
key: WidgetKeys.Operation,
|
||||
dataType: DataType.STRING,
|
||||
isColumn: false,
|
||||
key: 'operation',
|
||||
type: 'tag',
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: 'IN',
|
||||
op: OPERATORS.IN,
|
||||
value: topLevelOperations,
|
||||
},
|
||||
...tagFilterItems,
|
||||
];
|
||||
|
||||
const legendFormula = 'Error Percentage';
|
||||
const legend = legendFormula;
|
||||
const expression = 'A*100/B';
|
||||
const disabled = true;
|
||||
return getQueryBuilderQuerieswithFormula({
|
||||
metricNameA,
|
||||
metricNameB,
|
||||
autocompleteDataA,
|
||||
autocompleteDataB,
|
||||
additionalItemsA,
|
||||
additionalItemsB,
|
||||
legend,
|
||||
disabled,
|
||||
expression,
|
||||
legendFormula,
|
||||
legend: GraphTitle.ERROR_PERCENTAGE,
|
||||
disabled: true,
|
||||
expression: FORMULA.ERROR_PERCENTAGE,
|
||||
legendFormula: GraphTitle.ERROR_PERCENTAGE,
|
||||
});
|
||||
};
|
||||
|
||||
export interface OperationPerSecProps {
|
||||
servicename: string | undefined;
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
topLevelOperations: string[];
|
||||
}
|
||||
|
||||
export interface LatencyProps {
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Col } from 'antd';
|
||||
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
|
||||
import Graph from 'container/GridGraphLayout/Graph/';
|
||||
import {
|
||||
databaseCallsAvgDuration,
|
||||
databaseCallsRPS,
|
||||
@ -15,9 +15,11 @@ import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { GraphTitle } from '../constant';
|
||||
import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
|
||||
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
||||
import { Card, GraphContainer, Row } from '../styles';
|
||||
import { Button } from './styles';
|
||||
import { IServiceName } from './types';
|
||||
import {
|
||||
dbSystemTags,
|
||||
handleNonInQueryRange,
|
||||
@ -26,7 +28,7 @@ import {
|
||||
} from './util';
|
||||
|
||||
function DBCall(): JSX.Element {
|
||||
const { servicename } = useParams<{ servicename?: string }>();
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
@ -48,31 +50,37 @@ function DBCall(): JSX.Element {
|
||||
|
||||
const databaseCallsRPSWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: databaseCallsRPS({
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: databaseCallsRPS({
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.DATABASE_CALLS_RPS,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
const databaseCallsAverageDurationWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: databaseCallsAvgDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: databaseCallsAvgDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.DATABASE_CALLS_AVG_DURATION,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
@ -92,11 +100,9 @@ function DBCall(): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphTitle>Database Calls RPS</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
<Graph
|
||||
name="database_call_rps"
|
||||
fullViewOptions={false}
|
||||
widget={databaseCallsRPSWidget}
|
||||
yAxisUnit="reqps"
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -108,6 +114,9 @@ function DBCall(): JSX.Element {
|
||||
'database_call_rps',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@ -127,11 +136,9 @@ function DBCall(): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphTitle>Database Calls Avg Duration</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
<Graph
|
||||
name="database_call_avg_duration"
|
||||
fullViewOptions={false}
|
||||
widget={databaseCallsAverageDurationWidget}
|
||||
yAxisUnit="ms"
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -143,6 +150,9 @@ function DBCall(): JSX.Element {
|
||||
'database_call_avg_duration',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Col } from 'antd';
|
||||
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
|
||||
import Graph from 'container/GridGraphLayout/Graph/';
|
||||
import {
|
||||
externalCallDuration,
|
||||
externalCallDurationByAddress,
|
||||
@ -16,10 +16,11 @@ import { useParams } from 'react-router-dom';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { GraphTitle, legend } from '../constant';
|
||||
import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
|
||||
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
||||
import { legend } from './constant';
|
||||
import { Card, GraphContainer, Row } from '../styles';
|
||||
import { Button } from './styles';
|
||||
import { IServiceName } from './types';
|
||||
import {
|
||||
handleNonInQueryRange,
|
||||
onGraphClickHandler,
|
||||
@ -29,7 +30,7 @@ import {
|
||||
function External(): JSX.Element {
|
||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||
|
||||
const { servicename } = useParams<{ servicename?: string }>();
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
const tagFilterItems = useMemo(
|
||||
@ -40,17 +41,20 @@ function External(): JSX.Element {
|
||||
|
||||
const externalCallErrorWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallErrorPercent({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallErrorPercent({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
@ -61,48 +65,57 @@ function External(): JSX.Element {
|
||||
|
||||
const externalCallDurationWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.EXTERNAL_CALL_DURATION,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
const externalCallRPSWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallRpsByAddress({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallRpsByAddress({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
const externalCallDurationAddressWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallDurationByAddress({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: externalCallDurationByAddress({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
@ -124,11 +137,9 @@ function External(): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphTitle>External Call Error Percentage</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
<Graph
|
||||
name="external_call_error_percentage"
|
||||
fullViewOptions={false}
|
||||
widget={externalCallErrorWidget}
|
||||
yAxisUnit="%"
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -140,6 +151,9 @@ function External(): JSX.Element {
|
||||
'external_call_error_percentage',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@ -161,11 +175,9 @@ function External(): JSX.Element {
|
||||
</Button>
|
||||
|
||||
<Card>
|
||||
<GraphTitle>External Call duration</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
<Graph
|
||||
name="external_call_duration"
|
||||
fullViewOptions={false}
|
||||
widget={externalCallDurationWidget}
|
||||
yAxisUnit="ms"
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -177,6 +189,9 @@ function External(): JSX.Element {
|
||||
'external_call_duration',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@ -199,11 +214,9 @@ function External(): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphTitle>External Call RPS(by Address)</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
<Graph
|
||||
name="external_call_rps_by_address"
|
||||
fullViewOptions={false}
|
||||
widget={externalCallRPSWidget}
|
||||
yAxisUnit="reqps"
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -215,6 +228,9 @@ function External(): JSX.Element {
|
||||
'external_call_rps_by_address',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@ -236,11 +252,9 @@ function External(): JSX.Element {
|
||||
</Button>
|
||||
|
||||
<Card>
|
||||
<GraphTitle>External Call duration(by Address)</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
<Graph
|
||||
name="external_call_duration_by_address"
|
||||
fullViewOptions={false}
|
||||
widget={externalCallDurationAddressWidget}
|
||||
yAxisUnit="ms"
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -252,6 +266,9 @@ function External(): JSX.Element {
|
||||
'external_call_duration_by_address',
|
||||
);
|
||||
}}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
@ -1,16 +1,9 @@
|
||||
import { Typography } from 'antd';
|
||||
import getServiceOverview from 'api/metrics/getServiceOverview';
|
||||
import getTopLevelOperations, {
|
||||
ServiceDataProps,
|
||||
} from 'api/metrics/getTopLevelOperations';
|
||||
import getTopOperations from 'api/metrics/getTopOperations';
|
||||
import axios from 'axios';
|
||||
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
|
||||
import Graph from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
|
||||
import { routeConfig } from 'container/SideNav/config';
|
||||
import { getQueryString } from 'container/SideNav/helper';
|
||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||
@ -18,32 +11,30 @@ import {
|
||||
convertRawQueriesToTraceSelectedTags,
|
||||
resourceAttributesToTagFilterItems,
|
||||
} from 'hooks/useResourceAttribute/utils';
|
||||
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
|
||||
import { colors } from 'lib/getRandomColor';
|
||||
import getStep from 'lib/getStep';
|
||||
import history from 'lib/history';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useQueries, UseQueryResult } from 'react-query';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { PayloadProps } from 'types/api/metrics/getServiceOverview';
|
||||
import { PayloadProps as PayloadPropsTopOpertions } from 'types/api/metrics/getTopOperations';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { Tags } from 'types/reducer/trace';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { SOMETHING_WENT_WRONG } from '../../../constants/api';
|
||||
import { GraphTitle } from '../constant';
|
||||
import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
|
||||
import {
|
||||
errorPercentage,
|
||||
operationPerSec,
|
||||
} from '../MetricsPageQueries/OverviewQueries';
|
||||
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
||||
import TopOperationsTable from '../TopOperationsTable';
|
||||
import { Col, Row } from '../styles';
|
||||
import ServiceOverview from './Overview/ServiceOverview';
|
||||
import TopLevelOperation from './Overview/TopLevelOperations';
|
||||
import TopOperation from './Overview/TopOperation';
|
||||
import { Button } from './styles';
|
||||
import { IServiceName } from './types';
|
||||
import {
|
||||
handleNonInQueryRange,
|
||||
onGraphClickHandler,
|
||||
@ -54,7 +45,7 @@ function Application(): JSX.Element {
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
const { servicename } = useParams<{ servicename?: string }>();
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||
const { search } = useLocation();
|
||||
const { queries } = useResourceAttribute();
|
||||
@ -86,53 +77,15 @@ function Application(): JSX.Element {
|
||||
[handleSetTimeStamp],
|
||||
);
|
||||
|
||||
const queryResult = useQueries<
|
||||
[
|
||||
UseQueryResult<PayloadProps>,
|
||||
UseQueryResult<PayloadPropsTopOpertions>,
|
||||
UseQueryResult<ServiceDataProps>,
|
||||
]
|
||||
>([
|
||||
{
|
||||
queryKey: [servicename, selectedTags, minTime, maxTime],
|
||||
queryFn: (): Promise<PayloadProps> =>
|
||||
getServiceOverview({
|
||||
service: servicename || '',
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
step: getStep({
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
inputFormat: 'ns',
|
||||
}),
|
||||
selectedTags,
|
||||
}),
|
||||
},
|
||||
{
|
||||
queryKey: [minTime, maxTime, servicename, selectedTags],
|
||||
queryFn: (): Promise<PayloadPropsTopOpertions> =>
|
||||
getTopOperations({
|
||||
service: servicename || '',
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
selectedTags,
|
||||
}),
|
||||
},
|
||||
{
|
||||
queryKey: [servicename, minTime, maxTime, selectedTags],
|
||||
queryFn: (): Promise<ServiceDataProps> => getTopLevelOperations(),
|
||||
},
|
||||
]);
|
||||
|
||||
const serviceOverview = queryResult[0].data;
|
||||
const serviceOverviewError = queryResult[0].error;
|
||||
const serviceOverviewIsError = queryResult[0].isError;
|
||||
const serviceOverviewIsLoading = queryResult[0].isLoading;
|
||||
const topOperations = queryResult[1].data;
|
||||
const topLevelOperations = queryResult[2].data;
|
||||
const topLevelOperationsError = queryResult[2].error;
|
||||
const topLevelOperationsIsError = queryResult[2].isError;
|
||||
const topLevelOperationsIsLoading = queryResult[2].isLoading;
|
||||
const {
|
||||
data: topLevelOperations,
|
||||
isLoading: topLevelOperationsLoading,
|
||||
error: topLevelOperationsError,
|
||||
isError: topLevelOperationsIsError,
|
||||
} = useQuery<ServiceDataProps>({
|
||||
queryKey: [servicename, minTime, maxTime, selectedTags],
|
||||
queryFn: getTopLevelOperations,
|
||||
});
|
||||
|
||||
const selectedTraceTags: string = JSON.stringify(
|
||||
convertRawQueriesToTraceSelectedTags(queries) || [],
|
||||
@ -146,37 +99,43 @@ function Application(): JSX.Element {
|
||||
|
||||
const operationPerSecWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: operationPerSec({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperations
|
||||
? topLevelOperations[servicename || '']
|
||||
: [],
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: operationPerSec({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperations
|
||||
? topLevelOperations[servicename || '']
|
||||
: [],
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.RATE_PER_OPS,
|
||||
),
|
||||
[servicename, topLevelOperations, tagFilterItems],
|
||||
);
|
||||
|
||||
const errorPercentageWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: errorPercentage({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperations
|
||||
? topLevelOperations[servicename || '']
|
||||
: [],
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
}),
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: errorPercentage({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperations
|
||||
? topLevelOperations[servicename || '']
|
||||
: [],
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.ERROR_PERCENTAGE,
|
||||
),
|
||||
[servicename, topLevelOperations, tagFilterItems],
|
||||
);
|
||||
|
||||
@ -212,107 +171,17 @@ function Application(): JSX.Element {
|
||||
);
|
||||
};
|
||||
|
||||
const generalChartDataProperties = useCallback(
|
||||
(title: string, colorIndex: number) => ({
|
||||
borderColor: colors[colorIndex],
|
||||
label: title,
|
||||
showLine: true,
|
||||
borderWidth: 1.5,
|
||||
spanGaps: true,
|
||||
pointRadius: 2,
|
||||
pointHoverRadius: 4,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const dataSets = useMemo(() => {
|
||||
if (!serviceOverview) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
data: serviceOverview.map((e) =>
|
||||
parseFloat(convertToNanoSecondsToSecond(e.p99)),
|
||||
),
|
||||
...generalChartDataProperties('p99 Latency', 0),
|
||||
},
|
||||
{
|
||||
data: serviceOverview.map((e) =>
|
||||
parseFloat(convertToNanoSecondsToSecond(e.p95)),
|
||||
),
|
||||
...generalChartDataProperties('p95 Latency', 1),
|
||||
},
|
||||
{
|
||||
data: serviceOverview.map((e) =>
|
||||
parseFloat(convertToNanoSecondsToSecond(e.p50)),
|
||||
),
|
||||
...generalChartDataProperties('p50 Latency', 2),
|
||||
},
|
||||
];
|
||||
}, [generalChartDataProperties, serviceOverview]);
|
||||
|
||||
const data = useMemo(() => {
|
||||
if (!serviceOverview) {
|
||||
return {
|
||||
datasets: [],
|
||||
labels: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
datasets: dataSets,
|
||||
labels: serviceOverview.map(
|
||||
(e) => new Date(parseFloat(convertToNanoSecondsToSecond(e.timestamp))),
|
||||
),
|
||||
};
|
||||
}, [serviceOverview, dataSets]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
id="Service_button"
|
||||
onClick={onViewTracePopupClick({
|
||||
servicename,
|
||||
selectedTraceTags,
|
||||
timestamp: selectedTimeStamp,
|
||||
})}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
{serviceOverviewIsError ? (
|
||||
<Typography>
|
||||
{axios.isAxiosError(serviceOverviewError)
|
||||
? serviceOverviewError.response?.data
|
||||
: SOMETHING_WENT_WRONG}
|
||||
</Typography>
|
||||
) : (
|
||||
<>
|
||||
<GraphTitle>Latency</GraphTitle>
|
||||
{serviceOverviewIsLoading && (
|
||||
<Spinner size="large" tip="Loading..." height="40vh" />
|
||||
)}
|
||||
{!serviceOverviewIsLoading && (
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
animate={false}
|
||||
onClickHandler={handleGraphClick('Service')}
|
||||
name="service_latency"
|
||||
type="line"
|
||||
data={data}
|
||||
yAxisUnit="ms"
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</GraphContainer>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
<ServiceOverview
|
||||
onDragSelect={onDragSelect}
|
||||
handleGraphClick={handleGraphClick}
|
||||
selectedTimeStamp={selectedTimeStamp}
|
||||
selectedTraceTags={selectedTraceTags}
|
||||
tagFilterItems={tagFilterItems}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
@ -328,30 +197,17 @@ function Application(): JSX.Element {
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
{topLevelOperationsIsError ? (
|
||||
<Typography>
|
||||
{axios.isAxiosError(topLevelOperationsError)
|
||||
? topLevelOperationsError.response?.data
|
||||
: SOMETHING_WENT_WRONG}
|
||||
</Typography>
|
||||
) : (
|
||||
<>
|
||||
<GraphTitle>Rate (ops/s)</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="operations_per_sec"
|
||||
fullViewOptions={false}
|
||||
onClickHandler={handleGraphClick('Rate')}
|
||||
widget={operationPerSecWidget}
|
||||
yAxisUnit="ops"
|
||||
onDragSelect={onDragSelect}
|
||||
isDependedDataLoaded={topLevelOperationsIsLoading}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
<TopLevelOperation
|
||||
handleGraphClick={handleGraphClick}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsError={topLevelOperationsError}
|
||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||
name="operations_per_sec"
|
||||
widget={operationPerSecWidget}
|
||||
yAxisUnit="ops"
|
||||
opName="Rate"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
@ -367,43 +223,28 @@ function Application(): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
|
||||
<Card>
|
||||
{topLevelOperationsIsError ? (
|
||||
<Typography>
|
||||
{axios.isAxiosError(topLevelOperationsError)
|
||||
? topLevelOperationsError.response?.data
|
||||
: SOMETHING_WENT_WRONG}
|
||||
</Typography>
|
||||
) : (
|
||||
<>
|
||||
<GraphTitle>Error Percentage</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="error_percentage_%"
|
||||
fullViewOptions={false}
|
||||
onClickHandler={handleGraphClick('Error')}
|
||||
widget={errorPercentageWidget}
|
||||
yAxisUnit="%"
|
||||
onDragSelect={onDragSelect}
|
||||
isDependedDataLoaded={topLevelOperationsIsLoading}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
<TopLevelOperation
|
||||
handleGraphClick={handleGraphClick}
|
||||
onDragSelect={onDragSelect}
|
||||
topLevelOperationsError={topLevelOperationsError}
|
||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||
name="error_percentage_%"
|
||||
widget={errorPercentageWidget}
|
||||
yAxisUnit="%"
|
||||
opName="Error"
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Card>
|
||||
<TopOperationsTable data={topOperations || []} />
|
||||
</Card>
|
||||
<TopOperation />
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type ClickHandlerType = (
|
||||
export type ClickHandlerType = (
|
||||
ChartEvent: ChartEvent,
|
||||
activeElements: ActiveElement[],
|
||||
chart: Chart,
|
||||
|
@ -0,0 +1,84 @@
|
||||
import Graph from 'container/GridGraphLayout/Graph/';
|
||||
import { GraphTitle } from 'container/MetricsApplication/constant';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { latency } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||
import { useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { ClickHandlerType } from '../Overview';
|
||||
import { Button } from '../styles';
|
||||
import { IServiceName } from '../types';
|
||||
import { onViewTracePopupClick } from '../util';
|
||||
|
||||
function ServiceOverview({
|
||||
onDragSelect,
|
||||
handleGraphClick,
|
||||
selectedTraceTags,
|
||||
selectedTimeStamp,
|
||||
tagFilterItems,
|
||||
}: ServiceOverviewProps): JSX.Element {
|
||||
const { servicename } = useParams<IServiceName>();
|
||||
|
||||
const latencyWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder(
|
||||
{
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: latency({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
GraphTitle.LATENCY,
|
||||
),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
id="Service_button"
|
||||
onClick={onViewTracePopupClick({
|
||||
servicename,
|
||||
selectedTraceTags,
|
||||
timestamp: selectedTimeStamp,
|
||||
})}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
name="service_latency"
|
||||
onDragSelect={onDragSelect}
|
||||
widget={latencyWidget}
|
||||
yAxisUnit="ns"
|
||||
onClickHandler={handleGraphClick('Service')}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface ServiceOverviewProps {
|
||||
selectedTimeStamp: number;
|
||||
selectedTraceTags: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
handleGraphClick: (type: string) => ClickHandlerType;
|
||||
tagFilterItems: TagFilterItem[];
|
||||
}
|
||||
|
||||
export default ServiceOverview;
|
@ -0,0 +1,65 @@
|
||||
import { Typography } from 'antd';
|
||||
import axios from 'axios';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import Graph from 'container/GridGraphLayout/Graph/';
|
||||
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { ClickHandlerType } from '../Overview';
|
||||
|
||||
function TopLevelOperation({
|
||||
name,
|
||||
opName,
|
||||
topLevelOperationsIsError,
|
||||
topLevelOperationsError,
|
||||
topLevelOperationsLoading,
|
||||
onDragSelect,
|
||||
handleGraphClick,
|
||||
widget,
|
||||
yAxisUnit,
|
||||
}: TopLevelOperationProps): JSX.Element {
|
||||
return (
|
||||
<Card>
|
||||
{topLevelOperationsIsError ? (
|
||||
<Typography>
|
||||
{axios.isAxiosError(topLevelOperationsError)
|
||||
? topLevelOperationsError.response?.data
|
||||
: SOMETHING_WENT_WRONG}
|
||||
</Typography>
|
||||
) : (
|
||||
<GraphContainer>
|
||||
{topLevelOperationsLoading && (
|
||||
<Spinner size="large" tip="Loading..." height="40vh" />
|
||||
)}
|
||||
{!topLevelOperationsLoading && (
|
||||
<Graph
|
||||
name={name}
|
||||
widget={widget}
|
||||
onClickHandler={handleGraphClick(opName)}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
allowClone={false}
|
||||
allowDelete={false}
|
||||
allowEdit={false}
|
||||
/>
|
||||
)}
|
||||
</GraphContainer>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
interface TopLevelOperationProps {
|
||||
name: string;
|
||||
opName: string;
|
||||
topLevelOperationsIsError: boolean;
|
||||
topLevelOperationsError: unknown;
|
||||
topLevelOperationsLoading: boolean;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
handleGraphClick: (type: string) => ClickHandlerType;
|
||||
widget: Widgets;
|
||||
yAxisUnit: string;
|
||||
}
|
||||
|
||||
export default TopLevelOperation;
|
@ -0,0 +1,46 @@
|
||||
import getTopOperations from 'api/metrics/getTopOperations';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { Card } from 'container/MetricsApplication/styles';
|
||||
import TopOperationsTable from 'container/MetricsApplication/TopOperationsTable';
|
||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { PayloadProps } from 'types/api/metrics/getTopOperations';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { Tags } from 'types/reducer/trace';
|
||||
|
||||
function TopOperation(): JSX.Element {
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
const { servicename } = useParams<{ servicename?: string }>();
|
||||
const { queries } = useResourceAttribute();
|
||||
const selectedTags = useMemo(
|
||||
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
|
||||
[queries],
|
||||
);
|
||||
|
||||
const { data, isLoading } = useQuery<PayloadProps>({
|
||||
queryKey: [minTime, maxTime, servicename, selectedTags],
|
||||
queryFn: (): Promise<PayloadProps> =>
|
||||
getTopOperations({
|
||||
service: servicename || '',
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
selectedTags,
|
||||
}),
|
||||
});
|
||||
|
||||
return (
|
||||
<Card>
|
||||
{isLoading && <Spinner size="large" tip="Loading..." height="40vh" />}
|
||||
{!isLoading && <TopOperationsTable data={data || []} />}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default TopOperation;
|
@ -1,3 +0,0 @@
|
||||
export const legend = {
|
||||
address: '{{address}}',
|
||||
};
|
3
frontend/src/container/MetricsApplication/Tabs/types.ts
Normal file
3
frontend/src/container/MetricsApplication/Tabs/types.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface IServiceName {
|
||||
servicename: string;
|
||||
}
|
53
frontend/src/container/MetricsApplication/constant.ts
Normal file
53
frontend/src/container/MetricsApplication/constant.ts
Normal file
@ -0,0 +1,53 @@
|
||||
export const legend = {
|
||||
address: '{{address}}',
|
||||
};
|
||||
|
||||
export const QUERYNAME_AND_EXPRESSION = ['A', 'B', 'C'];
|
||||
export const LETENCY_LEGENDS_AGGREGATEOPERATOR = ['p50', 'p90', 'p99'];
|
||||
export const OPERATION_LEGENDS = ['Operations'];
|
||||
|
||||
export enum FORMULA {
|
||||
ERROR_PERCENTAGE = 'A*100/B',
|
||||
DATABASE_CALLS_AVG_DURATION = 'A/B',
|
||||
}
|
||||
|
||||
export enum GraphTitle {
|
||||
LATENCY = 'Latency',
|
||||
RATE_PER_OPS = 'Rate (ops/s)',
|
||||
ERROR_PERCENTAGE = 'Error Percentage',
|
||||
DATABASE_CALLS_RPS = 'Database Calls RPS',
|
||||
DATABASE_CALLS_AVG_DURATION = 'Database Calls Avg Duration',
|
||||
EXTERNAL_CALL_ERROR_PERCENTAGE = 'External Call Error Percentage',
|
||||
EXTERNAL_CALL_DURATION = 'External Call duration',
|
||||
EXTERNAL_CALL_RPS_BY_ADDRESS = 'External Call RPS(by Address)',
|
||||
EXTERNAL_CALL_DURATION_BY_ADDRESS = 'External Call duration(by Address)',
|
||||
}
|
||||
|
||||
export enum DataType {
|
||||
STRING = 'string',
|
||||
FLOAT64 = 'float64',
|
||||
INT64 = 'int64',
|
||||
}
|
||||
|
||||
export enum MetricsType {
|
||||
Tag = 'tag',
|
||||
Resource = 'resource',
|
||||
}
|
||||
|
||||
export enum WidgetKeys {
|
||||
Address = 'address',
|
||||
DurationNano = 'durationNano',
|
||||
StatusCode = 'status_code',
|
||||
Operation = 'operation',
|
||||
OperationName = 'operationName',
|
||||
Service_name = 'service_name',
|
||||
ServiceName = 'serviceName',
|
||||
SignozLatencyCount = 'signoz_latency_count',
|
||||
SignozDBLatencyCount = 'signoz_db_latency_count',
|
||||
DatabaseCallCount = 'signoz_database_call_count',
|
||||
DatabaseCallLatencySum = 'signoz_database_call_latency_sum',
|
||||
SignozDbLatencySum = 'signoz_db_latency_sum',
|
||||
SignozCallsTotal = 'signoz_calls_total',
|
||||
SignozExternalCallLatencyCount = 'signoz_external_call_latency_count',
|
||||
SignozExternalCallLatencySum = 'signoz_external_call_latency_sum',
|
||||
}
|
18
frontend/src/utils/dashboard/selectedDashboard.ts
Normal file
18
frontend/src/utils/dashboard/selectedDashboard.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
export const getSelectedDashboard = (dashboard: Dashboard[]): Dashboard => {
|
||||
if (dashboard.length > 0) {
|
||||
return dashboard[0];
|
||||
}
|
||||
return {} as Dashboard;
|
||||
};
|
||||
|
||||
export const getSelectedDashboardVariable = (
|
||||
dashboard: Dashboard[],
|
||||
): Record<string, IDashboardVariable> => {
|
||||
if (dashboard.length > 0) {
|
||||
const { variables } = dashboard[0].data;
|
||||
return variables;
|
||||
}
|
||||
return {};
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user