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:
Rajat Dabade 2023-07-18 08:55:01 +05:30 committed by GitHub
parent f3817d7335
commit 50142321f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1009 additions and 807 deletions

View File

@ -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;

View File

@ -1,5 +1,4 @@
import { Button, Typography } from 'antd'; import { Button } from 'antd';
import getQueryResult from 'api/widgets/getQuery';
import { GraphOnClickHandler } from 'components/Graph'; import { GraphOnClickHandler } from 'components/Graph';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import TimePreference from 'components/TimePreferenceDropDown'; import TimePreference from 'components/TimePreferenceDropDown';
@ -7,22 +6,18 @@ import GridGraphComponent from 'container/GridGraphComponent';
import { import {
timeItems, timeItems,
timePreferance, timePreferance,
timePreferenceType,
} from 'container/NewWidget/RightContainer/timeItems'; } 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 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 { useCallback, useMemo, useState } from 'react';
import { useQueries } from 'react-query';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; 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 { GlobalReducer } from 'types/reducer/globalTime';
import { NotFoundContainer, TimeContainer } from './styles'; import { TimeContainer } from './styles';
function FullView({ function FullView({
widget, widget,
@ -31,8 +26,9 @@ function FullView({
name, name,
yAxisUnit, yAxisUnit,
onDragSelect, onDragSelect,
isDependedDataLoaded = false,
}: FullViewProps): JSX.Element { }: FullViewProps): JSX.Element {
const { minTime, maxTime, selectedTime: globalSelectedTime } = useSelector< const { selectedTime: globalSelectedTime } = useSelector<
AppState, AppState,
GlobalReducer GlobalReducer
>((state) => state.globalTime); >((state) => state.globalTime);
@ -48,110 +44,55 @@ function FullView({
enum: widget?.timePreferance || 'GLOBAL_TIME', enum: widget?.timePreferance || 'GLOBAL_TIME',
}); });
const maxMinTime = GetMaxMinTime({ const queryKey = useMemo(
graphType: widget.panelTypes, () =>
maxTime, `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
minTime, [selectedTime, globalSelectedTime, widget],
});
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 isError = const updatedQuery = useStepInterval(widget?.query);
response.find((e) => e?.data?.statusCode !== 200) !== undefined ||
response.some((e) => e.isError === true);
const isLoading = response.some((e) => e.isLoading === true); const response = useGetQueryRange(
{
const errorMessage = response.find((e) => e.data?.error !== null)?.data?.error; selectedTime: selectedTime.enum,
graphType: widget.panelTypes,
const data = response.map((responseOfQuery) => query: updatedQuery,
responseOfQuery?.data?.payload?.result.map((e, index) => ({ globalSelectedInterval: globalSelectedTime,
query: queryLength[index]?.query, variables: getDashboardVariables(),
queryData: e, },
legend: queryLength[index]?.legend, {
})), queryKey,
enabled: !isDependedDataLoaded,
},
); );
const chartDataSet = useMemo( const chartDataSet = useMemo(
() => () =>
getChartData({ getChartData({
queryData: data.map((e) => ({ queryData: [
query: e?.map((e) => e.query).join(' ') || '', {
queryData: e?.map((e) => e.queryData) || [], queryData: response?.data?.payload?.data?.result || [],
legend: e?.map((e) => e.legend).join('') || '', },
})), ],
}), }),
[data], [response],
); );
if (isLoading) { if (response.status === 'idle' || response.status === 'loading') {
return <Spinner height="100%" size="large" tip="Loading..." />; return <Spinner height="100%" size="large" tip="Loading..." />;
} }
if (isError || data === undefined || data[0] === undefined) {
return (
<NotFoundContainer>
<Typography>{errorMessage}</Typography>
</NotFoundContainer>
);
}
return ( return (
<> <>
{fullViewOptions && ( {fullViewOptions && (
<TimeContainer> <TimeContainer>
<TimePreference <TimePreference
{...{ selectedTime={selectedTime}
selectedTime, setSelectedTime={setSelectedTime}
setSelectedTime,
}}
/> />
<Button <Button
onClick={(): void => { onClick={(): void => {
response.forEach((e) => e.refetch()); response.refetch();
}} }}
type="primary" type="primary"
> >
@ -176,12 +117,13 @@ function FullView({
} }
interface FullViewProps { interface FullViewProps {
widget: PromQLWidgets; widget: Widgets;
fullViewOptions?: boolean; fullViewOptions?: boolean;
onClickHandler?: GraphOnClickHandler; onClickHandler?: GraphOnClickHandler;
name: string; name: string;
yAxisUnit?: string; yAxisUnit?: string;
onDragSelect?: (start: number, end: number) => void; onDragSelect?: (start: number, end: number) => void;
isDependedDataLoaded?: boolean;
} }
FullView.defaultProps = { FullView.defaultProps = {
@ -189,6 +131,7 @@ FullView.defaultProps = {
onClickHandler: undefined, onClickHandler: undefined,
yAxisUnit: undefined, yAxisUnit: undefined,
onDragSelect: undefined, onDragSelect: undefined,
isDependedDataLoaded: undefined,
}; };
export default FullView; export default FullView;

View File

@ -1,5 +1,6 @@
import { Typography } from 'antd'; import { Typography } from 'antd';
import { ChartData } from 'chart.js'; import { ChartData } from 'chart.js';
import { GraphOnClickHandler } from 'components/Graph';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import GridGraphComponent from 'container/GridGraphComponent'; import GridGraphComponent from 'container/GridGraphComponent';
import { UpdateDashboard } from 'container/GridGraphLayout/utils'; import { UpdateDashboard } from 'container/GridGraphLayout/utils';
@ -35,12 +36,16 @@ import { Widgets } from 'types/api/dashboard/getAll';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import DashboardReducer from 'types/reducer/dashboards'; import DashboardReducer from 'types/reducer/dashboards';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import {
getSelectedDashboard,
getSelectedDashboardVariable,
} from 'utils/dashboard/selectedDashboard';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { LayoutProps } from '..'; import { LayoutProps } from '..';
import EmptyWidget from '../EmptyWidget'; import EmptyWidget from '../EmptyWidget';
import WidgetHeader from '../WidgetHeader'; import WidgetHeader from '../WidgetHeader';
import FullView from './FullView/index.metricsBuilder'; import FullView from './FullView';
import { FullViewContainer, Modal } from './styles'; import { FullViewContainer, Modal } from './styles';
function GridCardGraph({ function GridCardGraph({
@ -51,6 +56,10 @@ function GridCardGraph({
layout = [], layout = [],
setLayout, setLayout,
onDragSelect, onDragSelect,
onClickHandler,
allowDelete,
allowClone,
allowEdit,
}: GridCardGraphProps): JSX.Element { }: GridCardGraphProps): JSX.Element {
const { ref: graphRef, inView: isGraphVisible } = useInView({ const { ref: graphRef, inView: isGraphVisible } = useInView({
threshold: 0, threshold: 0,
@ -77,9 +86,9 @@ function GridCardGraph({
const { dashboards } = useSelector<AppState, DashboardReducer>( const { dashboards } = useSelector<AppState, DashboardReducer>(
(state) => state.dashboards, (state) => state.dashboards,
); );
const [selectedDashboard] = dashboards;
const selectedData = selectedDashboard?.data; const selectedDashboard = getSelectedDashboard(dashboards);
const { variables } = selectedData; const variables = getSelectedDashboardVariable(dashboards);
const updatedQuery = useStepInterval(widget?.query); const updatedQuery = useStepInterval(widget?.query);
@ -172,10 +181,10 @@ function GridCardGraph({
h: 2, h: 2,
y: 0, y: 0,
}, },
...(selectedDashboard.data.layout || []), ...(selectedDashboard?.data.layout || []),
]; ];
if (widget) { if (widget && selectedDashboard) {
await UpdateDashboard( await UpdateDashboard(
{ {
data: selectedDashboard.data, data: selectedDashboard.data,
@ -257,6 +266,9 @@ function GridCardGraph({
onClone={onCloneHandler} onClone={onCloneHandler}
queryResponse={queryResponse} queryResponse={queryResponse}
errorMessage={errorMessage} errorMessage={errorMessage}
allowClone={allowClone}
allowDelete={allowDelete}
allowEdit={allowEdit}
/> />
</div> </div>
<GridGraphComponent <GridGraphComponent
@ -267,6 +279,7 @@ function GridCardGraph({
title={' '} title={' '}
name={name} name={name}
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
onClickHandler={onClickHandler}
/> />
</> </>
)} )}
@ -289,6 +302,9 @@ function GridCardGraph({
onClone={onCloneHandler} onClone={onCloneHandler}
queryResponse={queryResponse} queryResponse={queryResponse}
errorMessage={errorMessage} errorMessage={errorMessage}
allowClone={allowClone}
allowDelete={allowDelete}
allowEdit={allowEdit}
/> />
</div> </div>
<GridGraphComponent <GridGraphComponent
@ -299,6 +315,7 @@ function GridCardGraph({
title={' '} title={' '}
name={name} name={name}
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
onClickHandler={onClickHandler}
/> />
</> </>
) : ( ) : (
@ -335,6 +352,9 @@ function GridCardGraph({
onClone={onCloneHandler} onClone={onCloneHandler}
queryResponse={queryResponse} queryResponse={queryResponse}
errorMessage={errorMessage} errorMessage={errorMessage}
allowClone={allowClone}
allowDelete={allowDelete}
allowEdit={allowEdit}
/> />
</div> </div>
)} )}
@ -351,6 +371,7 @@ function GridCardGraph({
name={name} name={name}
yAxisUnit={yAxisUnit} yAxisUnit={yAxisUnit}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
onClickHandler={onClickHandler}
/> />
)} )}
@ -374,10 +395,18 @@ interface GridCardGraphProps extends DispatchProps {
// eslint-disable-next-line react/require-default-props // eslint-disable-next-line react/require-default-props
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>; setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
onDragSelect?: (start: number, end: number) => void; onDragSelect?: (start: number, end: number) => void;
onClickHandler?: GraphOnClickHandler;
allowDelete?: boolean;
allowClone?: boolean;
allowEdit?: boolean;
} }
GridCardGraph.defaultProps = { GridCardGraph.defaultProps = {
onDragSelect: undefined, onDragSelect: undefined,
onClickHandler: undefined,
allowDelete: true,
allowClone: true,
allowEdit: true,
}; };
const mapDispatchToProps = ( const mapDispatchToProps = (

View File

@ -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',
};

View File

@ -27,24 +27,29 @@ import {
spinnerStyles, spinnerStyles,
tooltipStyles, tooltipStyles,
} from './config'; } from './config';
import { MENUITEM_KEYS_VS_LABELS, MenuItemKeys } from './contants';
import { import {
ArrowContainer, ArrowContainer,
HeaderContainer, HeaderContainer,
HeaderContentContainer, HeaderContentContainer,
} from './styles'; } from './styles';
import { KeyMethodMappingProps, MenuItem, TWidgetOptions } from './types';
import { generateMenuList, isTWidgetOptions } from './utils';
type TWidgetOptions = 'view' | 'edit' | 'delete' | string;
interface IWidgetHeaderProps { interface IWidgetHeaderProps {
title: string; title: string;
widget: Widgets; widget: Widgets;
onView: VoidFunction; onView: VoidFunction;
onDelete: VoidFunction; onDelete?: VoidFunction;
onClone: VoidFunction; onClone?: VoidFunction;
parentHover: boolean; parentHover: boolean;
queryResponse: UseQueryResult< queryResponse: UseQueryResult<
SuccessResponse<MetricRangePayloadProps> | ErrorResponse SuccessResponse<MetricRangePayloadProps> | ErrorResponse
>; >;
errorMessage: string | undefined; errorMessage: string | undefined;
allowDelete?: boolean;
allowClone?: boolean;
allowEdit?: boolean;
} }
function WidgetHeader({ function WidgetHeader({
title, title,
@ -55,6 +60,9 @@ function WidgetHeader({
parentHover, parentHover,
queryResponse, queryResponse,
errorMessage, errorMessage,
allowClone = true,
allowDelete = true,
allowEdit = true,
}: IWidgetHeaderProps): JSX.Element { }: IWidgetHeaderProps): JSX.Element {
const [localHover, setLocalHover] = useState(false); const [localHover, setLocalHover] = useState(false);
const [isOpen, setIsOpen] = useState<boolean>(false); const [isOpen, setIsOpen] = useState<boolean>(false);
@ -70,24 +78,22 @@ function WidgetHeader({
); );
}, [widget.id, widget.panelTypes, widget.query]); }, [widget.id, widget.panelTypes, widget.query]);
const keyMethodMapping: { const keyMethodMapping: KeyMethodMappingProps<TWidgetOptions> = useMemo(
[K in TWidgetOptions]: { key: TWidgetOptions; method: VoidFunction };
} = useMemo(
() => ({ () => ({
view: { view: {
key: 'view', key: MenuItemKeys.View,
method: onView, method: onView,
}, },
edit: { edit: {
key: 'edit', key: MenuItemKeys.Edit,
method: onEditHandler, method: onEditHandler,
}, },
delete: { delete: {
key: 'delete', key: MenuItemKeys.Delete,
method: onDelete, method: onDelete,
}, },
clone: { clone: {
key: 'clone', key: MenuItemKeys.Clone,
method: onClone, method: onClone,
}, },
}), }),
@ -95,11 +101,13 @@ function WidgetHeader({
); );
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback( const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
({ key }: { key: TWidgetOptions }): void => { ({ key }: { key: string }): void => {
const functionToCall = keyMethodMapping[key]?.method; if (isTWidgetOptions(key)) {
if (functionToCall) { const functionToCall = keyMethodMapping[key]?.method;
functionToCall(); if (functionToCall) {
setIsOpen(false); functionToCall();
setIsOpen(false);
}
} }
}, },
[keyMethodMapping], [keyMethodMapping],
@ -111,45 +119,53 @@ function WidgetHeader({
role, role,
); );
const menuList: MenuItemType[] = useMemo( const actions = useMemo(
() => [ (): MenuItem[] => [
{ {
key: keyMethodMapping.view.key, key: MenuItemKeys.View,
icon: <FullscreenOutlined />, icon: <FullscreenOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.View],
isVisible: true,
disabled: queryResponse.isLoading, disabled: queryResponse.isLoading,
label: 'View',
}, },
{ {
key: keyMethodMapping.edit.key, key: MenuItemKeys.Edit,
icon: <EditFilled />, icon: <EditFilled />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Edit],
isVisible: allowEdit,
disabled: !editWidget, disabled: !editWidget,
label: 'Edit',
}, },
{ {
key: keyMethodMapping.clone.key, key: MenuItemKeys.Clone,
icon: <CopyOutlined />, icon: <CopyOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Clone],
isVisible: allowClone,
disabled: !editWidget, disabled: !editWidget,
label: 'Clone',
}, },
{ {
key: keyMethodMapping.delete.key, key: MenuItemKeys.Delete,
icon: <DeleteOutlined />, icon: <DeleteOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Delete],
isVisible: allowDelete,
disabled: !deleteWidget, disabled: !deleteWidget,
danger: true, danger: true,
label: 'Delete',
}, },
], ],
[ [
allowEdit,
allowClone,
allowDelete,
queryResponse.isLoading,
deleteWidget, deleteWidget,
editWidget, 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(() => { const onClickHandler = useCallback(() => {
setIsOpen((open) => !open); setIsOpen((open) => !open);
}, []); }, []);
@ -200,4 +216,12 @@ function WidgetHeader({
); );
} }
WidgetHeader.defaultProps = {
onDelete: undefined,
onClone: undefined,
allowDelete: true,
allowClone: true,
allowEdit: true,
};
export default WidgetHeader; export default WidgetHeader;

View File

@ -9,6 +9,8 @@ export const HeaderContainer = styled.div<{ hover: boolean }>`
font-size: 0.8rem; font-size: 0.8rem;
cursor: all-scroll; cursor: all-scroll;
position: absolute; position: absolute;
top: 0;
left: 0;
`; `;
export const HeaderContentContainer = styled.span` export const HeaderContentContainer = styled.span`

View 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;
};
};

View 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;

View File

@ -2,7 +2,10 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
export const getWidgetQueryBuilder = (query: Widgets['query']): Widgets => ({ export const getWidgetQueryBuilder = (
query: Widgets['query'],
title = '',
): Widgets => ({
description: '', description: '',
id: v4(), id: v4(),
isStacked: false, isStacked: false,
@ -11,5 +14,5 @@ export const getWidgetQueryBuilder = (query: Widgets['query']): Widgets => ({
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
query, query,
timePreferance: 'GLOBAL_TIME', timePreferance: 'GLOBAL_TIME',
title: '', title,
}); });

View File

@ -1,7 +1,10 @@
import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; 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 { import {
getQueryBuilderQueries, getQueryBuilderQueries,
getQueryBuilderQuerieswithFormula, getQueryBuilderQuerieswithFormula,
@ -12,35 +15,42 @@ export const databaseCallsRPS = ({
legend, legend,
tagFilterItems, tagFilterItems,
}: DatabaseCallsRPSProps): QueryBuilderData => { }: DatabaseCallsRPSProps): QueryBuilderData => {
const metricName: BaseAutocompleteData = { const autocompleteData: 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[] = [
{ {
id: '', key: WidgetKeys.SignozDBLatencyCount,
key: { dataType: DataType.FLOAT64,
dataType: 'string', isColumn: true,
isColumn: false, type: null,
key: 'service_name',
type: 'resource',
},
op: 'IN',
value: [`${servicename}`],
}, },
...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({ return getQueryBuilderQueries({
metricName, autocompleteData,
groupBy, groupBy,
legend, legends,
itemsA, filterItems,
dataSource: DataSource.METRICS,
}); });
}; };
@ -48,32 +58,29 @@ export const databaseCallsAvgDuration = ({
servicename, servicename,
tagFilterItems, tagFilterItems,
}: DatabaseCallProps): QueryBuilderData => { }: DatabaseCallProps): QueryBuilderData => {
const metricNameA: BaseAutocompleteData = { const autocompleteDataA: BaseAutocompleteData = {
dataType: 'float64', key: WidgetKeys.SignozDbLatencySum,
dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_db_latency_sum',
type: null, type: null,
}; };
const metricNameB: BaseAutocompleteData = { const autocompleteDataB: BaseAutocompleteData = {
dataType: 'float64', key: WidgetKeys.SignozDBLatencyCount,
dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_db_latency_count',
type: null, type: null,
}; };
const expression = 'A/B';
const legendFormula = 'Average Duration';
const legend = '';
const disabled = true;
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Service_name,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', type: MetricsType.Resource,
type: 'resource',
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
...tagFilterItems, ...tagFilterItems,
@ -81,14 +88,14 @@ export const databaseCallsAvgDuration = ({
const additionalItemsB = additionalItemsA; const additionalItemsB = additionalItemsA;
return getQueryBuilderQuerieswithFormula({ return getQueryBuilderQuerieswithFormula({
metricNameA, autocompleteDataA,
metricNameB, autocompleteDataB,
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, legend: '',
disabled, disabled: true,
expression, expression: FORMULA.DATABASE_CALLS_AVG_DURATION,
legendFormula, legendFormula: 'Average Duration',
}); });
}; };
@ -97,6 +104,6 @@ interface DatabaseCallsRPSProps extends DatabaseCallProps {
} }
interface DatabaseCallProps { interface DatabaseCallProps {
servicename: string | undefined; servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[]; tagFilterItems: TagFilterItem[];
} }

View File

@ -1,14 +1,22 @@
import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; 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 { import {
getQueryBuilderQueries, getQueryBuilderQueries,
getQueryBuilderQuerieswithFormula, getQueryBuilderQuerieswithFormula,
} from './MetricsPageQueriesFactory'; } from './MetricsPageQueriesFactory';
const groupBy: BaseAutocompleteData[] = [ 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 = ({ export const externalCallErrorPercent = ({
@ -16,39 +24,39 @@ export const externalCallErrorPercent = ({
legend, legend,
tagFilterItems, tagFilterItems,
}: ExternalCallDurationByAddressProps): QueryBuilderData => { }: ExternalCallDurationByAddressProps): QueryBuilderData => {
const metricNameA: BaseAutocompleteData = { const autocompleteDataA: BaseAutocompleteData = {
dataType: 'float64', key: WidgetKeys.SignozExternalCallLatencyCount,
dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_external_call_latency_count',
type: null, type: null,
}; };
const metricNameB: BaseAutocompleteData = { const autocompleteDataB: BaseAutocompleteData = {
dataType: 'float64', key: WidgetKeys.SignozExternalCallLatencyCount,
dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_external_call_latency_count',
type: null, type: null,
}; };
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Service_name,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', type: MetricsType.Resource,
type: 'resource',
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
{ {
id: '', id: '',
key: { key: {
dataType: 'int64', key: WidgetKeys.StatusCode,
dataType: DataType.INT64,
isColumn: false, isColumn: false,
key: 'status_code', type: MetricsType.Tag,
type: 'tag',
}, },
op: 'IN', op: OPERATORS.IN,
value: ['STATUS_CODE_ERROR'], value: ['STATUS_CODE_ERROR'],
}, },
...tagFilterItems, ...tagFilterItems,
@ -57,22 +65,22 @@ export const externalCallErrorPercent = ({
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Service_name,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', type: MetricsType.Resource,
type: 'resource',
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
...tagFilterItems, ...tagFilterItems,
]; ];
const legendFormula = legend; const legendFormula = legend;
const expression = 'A*100/B'; const expression = FORMULA.ERROR_PERCENTAGE;
const disabled = true; const disabled = true;
return getQueryBuilderQuerieswithFormula({ return getQueryBuilderQuerieswithFormula({
metricNameA, autocompleteDataA,
metricNameB, autocompleteDataB,
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, legend,
@ -87,19 +95,19 @@ export const externalCallDuration = ({
servicename, servicename,
tagFilterItems, tagFilterItems,
}: ExternalCallProps): QueryBuilderData => { }: ExternalCallProps): QueryBuilderData => {
const metricNameA: BaseAutocompleteData = { const autocompleteDataA: BaseAutocompleteData = {
dataType: 'float64', dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_external_call_latency_sum', key: WidgetKeys.SignozExternalCallLatencySum,
type: null, type: null,
}; };
const metricNameB: BaseAutocompleteData = { const autocompleteDataB: BaseAutocompleteData = {
dataType: 'float64', dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_external_call_latency_count', key: WidgetKeys.SignozExternalCallLatencyCount,
type: null, type: null,
}; };
const expression = 'A/B'; const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
const legendFormula = 'Average Duration'; const legendFormula = 'Average Duration';
const legend = ''; const legend = '';
const disabled = true; const disabled = true;
@ -107,12 +115,12 @@ export const externalCallDuration = ({
{ {
id: '', id: '',
key: { key: {
dataType: 'string', dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', key: WidgetKeys.Service_name,
type: 'resource', type: MetricsType.Resource,
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
...tagFilterItems, ...tagFilterItems,
@ -120,8 +128,8 @@ export const externalCallDuration = ({
const additionalItemsB = additionalItemsA; const additionalItemsB = additionalItemsA;
return getQueryBuilderQuerieswithFormula({ return getQueryBuilderQuerieswithFormula({
metricNameA, autocompleteDataA,
metricNameB, autocompleteDataB,
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, legend,
@ -136,31 +144,38 @@ export const externalCallRpsByAddress = ({
legend, legend,
tagFilterItems, tagFilterItems,
}: ExternalCallDurationByAddressProps): QueryBuilderData => { }: ExternalCallDurationByAddressProps): QueryBuilderData => {
const metricName: BaseAutocompleteData = { const autocompleteData: BaseAutocompleteData[] = [
dataType: 'float64',
isColumn: true,
key: 'signoz_external_call_latency_count',
type: null,
};
const itemsA: TagFilterItem[] = [
{ {
id: '', dataType: DataType.FLOAT64,
key: { isColumn: true,
dataType: 'string', key: WidgetKeys.SignozExternalCallLatencyCount,
isColumn: false, type: null,
key: 'service_name',
type: 'resource',
},
op: 'IN',
value: [`${servicename}`],
}, },
...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({ return getQueryBuilderQueries({
metricName, autocompleteData,
groupBy, groupBy,
legend, legends,
itemsA, filterItems,
dataSource: DataSource.METRICS,
}); });
}; };
@ -169,31 +184,31 @@ export const externalCallDurationByAddress = ({
legend, legend,
tagFilterItems, tagFilterItems,
}: ExternalCallDurationByAddressProps): QueryBuilderData => { }: ExternalCallDurationByAddressProps): QueryBuilderData => {
const metricNameA: BaseAutocompleteData = { const autocompleteDataA: BaseAutocompleteData = {
dataType: 'float64', dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_external_call_latency_sum', key: WidgetKeys.SignozExternalCallLatencySum,
type: null, type: null,
}; };
const metricNameB: BaseAutocompleteData = { const autocompleteDataB: BaseAutocompleteData = {
dataType: 'float64', dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_external_call_latency_count', key: WidgetKeys.SignozExternalCallLatencyCount,
type: null, type: null,
}; };
const expression = 'A/B'; const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
const legendFormula = legend; const legendFormula = legend;
const disabled = true; const disabled = true;
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
key: { key: {
dataType: 'string', dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', key: WidgetKeys.Service_name,
type: 'resource', type: MetricsType.Resource,
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
...tagFilterItems, ...tagFilterItems,
@ -201,8 +216,8 @@ export const externalCallDurationByAddress = ({
const additionalItemsB = additionalItemsA; const additionalItemsB = additionalItemsA;
return getQueryBuilderQuerieswithFormula({ return getQueryBuilderQuerieswithFormula({
metricNameA, autocompleteDataA,
metricNameB, autocompleteDataB,
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, legend,
@ -218,6 +233,6 @@ interface ExternalCallDurationByAddressProps extends ExternalCallProps {
} }
export interface ExternalCallProps { export interface ExternalCallProps {
servicename: string | undefined; servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[]; tagFilterItems: TagFilterItem[];
} }

View File

@ -5,27 +5,39 @@ import {
import getStep from 'lib/getStep'; import getStep from 'lib/getStep';
import store from 'store'; import store from 'store';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { import {
IBuilderQuery,
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import {
DataSource,
MetricAggregateOperator, MetricAggregateOperator,
QueryBuilderData, QueryBuilderData,
} from 'types/common/queryBuilder'; } from 'types/common/queryBuilder';
export const getQueryBuilderQueries = ({ export const getQueryBuilderQueries = ({
metricName, autocompleteData,
groupBy = [], groupBy = [],
legend, legends,
itemsA, filterItems,
aggregateOperator,
dataSource,
queryNameAndExpression,
}: BuilderQueriesProps): QueryBuilderData => ({ }: BuilderQueriesProps): QueryBuilderData => ({
queryFormulas: [], queryFormulas: [],
queryData: [ queryData: autocompleteData.map((item, index) => {
{ const newQueryData: IBuilderQuery = {
...initialQueryBuilderFormValuesMap.metrics, ...initialQueryBuilderFormValuesMap.metrics,
aggregateOperator: MetricAggregateOperator.SUM_RATE, aggregateOperator: ((): string => {
if (aggregateOperator) {
return aggregateOperator[index];
}
return MetricAggregateOperator.SUM_RATE;
})(),
disabled: false, disabled: false,
groupBy, groupBy,
aggregateAttribute: metricName, aggregateAttribute: item,
legend, legend: legends[index],
stepInterval: getStep({ stepInterval: getStep({
end: store.getState().globalTime.maxTime, end: store.getState().globalTime.maxTime,
inputFormat: 'ns', inputFormat: 'ns',
@ -33,16 +45,24 @@ export const getQueryBuilderQueries = ({
}), }),
reduceTo: 'sum', reduceTo: 'sum',
filters: { filters: {
items: itemsA, items: filterItems[index],
op: 'AND', op: 'AND',
}, },
}, dataSource,
], };
if (queryNameAndExpression) {
newQueryData.queryName = queryNameAndExpression[index];
newQueryData.expression = queryNameAndExpression[index];
}
return newQueryData;
}),
}); });
export const getQueryBuilderQuerieswithFormula = ({ export const getQueryBuilderQuerieswithFormula = ({
metricNameA, autocompleteDataA,
metricNameB, autocompleteDataB,
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, legend,
@ -65,7 +85,7 @@ export const getQueryBuilderQuerieswithFormula = ({
disabled, disabled,
groupBy, groupBy,
legend, legend,
aggregateAttribute: metricNameA, aggregateAttribute: autocompleteDataA,
reduceTo: 'sum', reduceTo: 'sum',
filters: { filters: {
items: additionalItemsA, items: additionalItemsA,
@ -83,7 +103,7 @@ export const getQueryBuilderQuerieswithFormula = ({
disabled, disabled,
groupBy, groupBy,
legend, legend,
aggregateAttribute: metricNameB, aggregateAttribute: autocompleteDataB,
queryName: 'B', queryName: 'B',
expression: 'B', expression: 'B',
reduceTo: 'sum', reduceTo: 'sum',
@ -101,15 +121,18 @@ export const getQueryBuilderQuerieswithFormula = ({
}); });
interface BuilderQueriesProps { interface BuilderQueriesProps {
metricName: BaseAutocompleteData; autocompleteData: BaseAutocompleteData[];
groupBy?: BaseAutocompleteData[]; groupBy?: BaseAutocompleteData[];
legend: string; legends: string[];
itemsA: TagFilterItem[]; filterItems: TagFilterItem[][];
aggregateOperator?: string[];
dataSource: DataSource;
queryNameAndExpression?: string[];
} }
interface BuilderQuerieswithFormulaProps { interface BuilderQuerieswithFormulaProps {
metricNameA: BaseAutocompleteData; autocompleteDataA: BaseAutocompleteData;
metricNameB: BaseAutocompleteData; autocompleteDataB: BaseAutocompleteData;
legend: string; legend: string;
disabled: boolean; disabled: boolean;
groupBy?: BaseAutocompleteData[]; groupBy?: BaseAutocompleteData[];

View File

@ -1,55 +1,151 @@
import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; 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 { import {
getQueryBuilderQueries, getQueryBuilderQueries,
getQueryBuilderQuerieswithFormula, getQueryBuilderQuerieswithFormula,
} from './MetricsPageQueriesFactory'; } 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 = ({ export const operationPerSec = ({
servicename, servicename,
tagFilterItems, tagFilterItems,
topLevelOperations, topLevelOperations,
}: OperationPerSecProps): QueryBuilderData => { }: OperationPerSecProps): QueryBuilderData => {
const metricName: BaseAutocompleteData = { const autocompleteData: BaseAutocompleteData[] = [
dataType: 'float64', {
isColumn: true, key: WidgetKeys.SignozLatencyCount,
key: 'signoz_latency_count', dataType: DataType.FLOAT64,
type: null, isColumn: true,
}; type: null,
const legend = 'Operations'; },
];
const itemsA: TagFilterItem[] = [ const filterItems: TagFilterItem[][] = [
{ [
id: '', {
key: { id: '',
dataType: 'string', key: {
isColumn: false, key: WidgetKeys.Service_name,
key: 'service_name', dataType: DataType.STRING,
type: 'resource', isColumn: false,
type: MetricsType.Resource,
},
op: OPERATORS.IN,
value: [`${servicename}`],
}, },
op: 'IN', {
value: [`${servicename}`], id: '',
}, key: {
{ key: WidgetKeys.Operation,
id: '', dataType: DataType.STRING,
key: { isColumn: false,
dataType: 'string', type: MetricsType.Tag,
isColumn: false, },
key: 'operation', op: OPERATORS.IN,
type: 'tag', value: topLevelOperations,
}, },
op: 'IN', ...tagFilterItems,
value: topLevelOperations, ],
},
...tagFilterItems,
]; ];
return getQueryBuilderQueries({ return getQueryBuilderQueries({
metricName, autocompleteData,
legend, legends: OPERATION_LEGENDS,
itemsA, filterItems,
dataSource: DataSource.METRICS,
}); });
}; };
@ -58,50 +154,50 @@ export const errorPercentage = ({
tagFilterItems, tagFilterItems,
topLevelOperations, topLevelOperations,
}: OperationPerSecProps): QueryBuilderData => { }: OperationPerSecProps): QueryBuilderData => {
const metricNameA: BaseAutocompleteData = { const autocompleteDataA: BaseAutocompleteData = {
dataType: 'float64', key: WidgetKeys.SignozCallsTotal,
dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_calls_total',
type: null, type: null,
}; };
const metricNameB: BaseAutocompleteData = { const autocompleteDataB: BaseAutocompleteData = {
dataType: 'float64', key: WidgetKeys.SignozCallsTotal,
dataType: DataType.FLOAT64,
isColumn: true, isColumn: true,
key: 'signoz_calls_total',
type: null, type: null,
}; };
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Service_name,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', type: MetricsType.Resource,
type: 'resource',
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Operation,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'operation', type: MetricsType.Tag,
type: 'tag',
}, },
op: 'IN', op: OPERATORS.IN,
value: topLevelOperations, value: topLevelOperations,
}, },
{ {
id: '', id: '',
key: { key: {
dataType: 'int64', key: WidgetKeys.StatusCode,
dataType: DataType.INT64,
isColumn: false, isColumn: false,
key: 'status_code', type: MetricsType.Tag,
type: 'tag',
}, },
op: 'IN', op: OPERATORS.IN,
value: ['STATUS_CODE_ERROR'], value: ['STATUS_CODE_ERROR'],
}, },
...tagFilterItems, ...tagFilterItems,
@ -111,46 +207,47 @@ export const errorPercentage = ({
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Service_name,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'service_name', type: MetricsType.Resource,
type: 'resource',
}, },
op: 'IN', op: OPERATORS.IN,
value: [`${servicename}`], value: [`${servicename}`],
}, },
{ {
id: '', id: '',
key: { key: {
dataType: 'string', key: WidgetKeys.Operation,
dataType: DataType.STRING,
isColumn: false, isColumn: false,
key: 'operation', type: MetricsType.Tag,
type: 'tag',
}, },
op: 'IN', op: OPERATORS.IN,
value: topLevelOperations, value: topLevelOperations,
}, },
...tagFilterItems, ...tagFilterItems,
]; ];
const legendFormula = 'Error Percentage';
const legend = legendFormula;
const expression = 'A*100/B';
const disabled = true;
return getQueryBuilderQuerieswithFormula({ return getQueryBuilderQuerieswithFormula({
metricNameA, autocompleteDataA,
metricNameB, autocompleteDataB,
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, legend: GraphTitle.ERROR_PERCENTAGE,
disabled, disabled: true,
expression, expression: FORMULA.ERROR_PERCENTAGE,
legendFormula, legendFormula: GraphTitle.ERROR_PERCENTAGE,
}); });
}; };
export interface OperationPerSecProps { export interface OperationPerSecProps {
servicename: string | undefined; servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[]; tagFilterItems: TagFilterItem[];
topLevelOperations: string[]; topLevelOperations: string[];
} }
export interface LatencyProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
}

View File

@ -1,5 +1,5 @@
import { Col } from 'antd'; import { Col } from 'antd';
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder'; import Graph from 'container/GridGraphLayout/Graph/';
import { import {
databaseCallsAvgDuration, databaseCallsAvgDuration,
databaseCallsRPS, databaseCallsRPS,
@ -15,9 +15,11 @@ import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { GraphTitle } from '../constant';
import { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
import { Card, GraphContainer, GraphTitle, Row } from '../styles'; import { Card, GraphContainer, Row } from '../styles';
import { Button } from './styles'; import { Button } from './styles';
import { IServiceName } from './types';
import { import {
dbSystemTags, dbSystemTags,
handleNonInQueryRange, handleNonInQueryRange,
@ -26,7 +28,7 @@ import {
} from './util'; } from './util';
function DBCall(): JSX.Element { function DBCall(): JSX.Element {
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<IServiceName>();
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { queries } = useResourceAttribute(); const { queries } = useResourceAttribute();
@ -48,31 +50,37 @@ function DBCall(): JSX.Element {
const databaseCallsRPSWidget = useMemo( const databaseCallsRPSWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: databaseCallsRPS({ promql: [],
servicename, builder: databaseCallsRPS({
legend, servicename,
tagFilterItems, legend,
}), tagFilterItems,
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.DATABASE_CALLS_RPS,
),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
const databaseCallsAverageDurationWidget = useMemo( const databaseCallsAverageDurationWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: databaseCallsAvgDuration({ promql: [],
servicename, builder: databaseCallsAvgDuration({
tagFilterItems, servicename,
}), tagFilterItems,
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.DATABASE_CALLS_AVG_DURATION,
),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -92,11 +100,9 @@ function DBCall(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <Card>
<GraphTitle>Database Calls RPS</GraphTitle>
<GraphContainer> <GraphContainer>
<FullView <Graph
name="database_call_rps" name="database_call_rps"
fullViewOptions={false}
widget={databaseCallsRPSWidget} widget={databaseCallsRPSWidget}
yAxisUnit="reqps" yAxisUnit="reqps"
onClickHandler={(ChartEvent, activeElements, chart, data): void => { onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -108,6 +114,9 @@ function DBCall(): JSX.Element {
'database_call_rps', 'database_call_rps',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -127,11 +136,9 @@ function DBCall(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <Card>
<GraphTitle>Database Calls Avg Duration</GraphTitle>
<GraphContainer> <GraphContainer>
<FullView <Graph
name="database_call_avg_duration" name="database_call_avg_duration"
fullViewOptions={false}
widget={databaseCallsAverageDurationWidget} widget={databaseCallsAverageDurationWidget}
yAxisUnit="ms" yAxisUnit="ms"
onClickHandler={(ChartEvent, activeElements, chart, data): void => { onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -143,6 +150,9 @@ function DBCall(): JSX.Element {
'database_call_avg_duration', 'database_call_avg_duration',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>

View File

@ -1,5 +1,5 @@
import { Col } from 'antd'; import { Col } from 'antd';
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder'; import Graph from 'container/GridGraphLayout/Graph/';
import { import {
externalCallDuration, externalCallDuration,
externalCallDurationByAddress, externalCallDurationByAddress,
@ -16,10 +16,11 @@ import { useParams } from 'react-router-dom';
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { GraphTitle, legend } from '../constant';
import { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
import { Card, GraphContainer, GraphTitle, Row } from '../styles'; import { Card, GraphContainer, Row } from '../styles';
import { legend } from './constant';
import { Button } from './styles'; import { Button } from './styles';
import { IServiceName } from './types';
import { import {
handleNonInQueryRange, handleNonInQueryRange,
onGraphClickHandler, onGraphClickHandler,
@ -29,7 +30,7 @@ import {
function External(): JSX.Element { function External(): JSX.Element {
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<IServiceName>();
const { queries } = useResourceAttribute(); const { queries } = useResourceAttribute();
const tagFilterItems = useMemo( const tagFilterItems = useMemo(
@ -40,17 +41,20 @@ function External(): JSX.Element {
const externalCallErrorWidget = useMemo( const externalCallErrorWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: externalCallErrorPercent({ promql: [],
servicename, builder: externalCallErrorPercent({
legend: legend.address, servicename,
tagFilterItems, legend: legend.address,
}), tagFilterItems,
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -61,48 +65,57 @@ function External(): JSX.Element {
const externalCallDurationWidget = useMemo( const externalCallDurationWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: externalCallDuration({ promql: [],
servicename, builder: externalCallDuration({
tagFilterItems, servicename,
}), tagFilterItems,
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.EXTERNAL_CALL_DURATION,
),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
const externalCallRPSWidget = useMemo( const externalCallRPSWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: externalCallRpsByAddress({ promql: [],
servicename, builder: externalCallRpsByAddress({
legend: legend.address, servicename,
tagFilterItems, legend: legend.address,
}), tagFilterItems,
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
const externalCallDurationAddressWidget = useMemo( const externalCallDurationAddressWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: externalCallDurationByAddress({ promql: [],
servicename, builder: externalCallDurationByAddress({
legend: legend.address, servicename,
tagFilterItems, legend: legend.address,
}), tagFilterItems,
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -124,11 +137,9 @@ function External(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <Card>
<GraphTitle>External Call Error Percentage</GraphTitle>
<GraphContainer> <GraphContainer>
<FullView <Graph
name="external_call_error_percentage" name="external_call_error_percentage"
fullViewOptions={false}
widget={externalCallErrorWidget} widget={externalCallErrorWidget}
yAxisUnit="%" yAxisUnit="%"
onClickHandler={(ChartEvent, activeElements, chart, data): void => { onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -140,6 +151,9 @@ function External(): JSX.Element {
'external_call_error_percentage', 'external_call_error_percentage',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -161,11 +175,9 @@ function External(): JSX.Element {
</Button> </Button>
<Card> <Card>
<GraphTitle>External Call duration</GraphTitle>
<GraphContainer> <GraphContainer>
<FullView <Graph
name="external_call_duration" name="external_call_duration"
fullViewOptions={false}
widget={externalCallDurationWidget} widget={externalCallDurationWidget}
yAxisUnit="ms" yAxisUnit="ms"
onClickHandler={(ChartEvent, activeElements, chart, data): void => { onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -177,6 +189,9 @@ function External(): JSX.Element {
'external_call_duration', 'external_call_duration',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -199,11 +214,9 @@ function External(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <Card>
<GraphTitle>External Call RPS(by Address)</GraphTitle>
<GraphContainer> <GraphContainer>
<FullView <Graph
name="external_call_rps_by_address" name="external_call_rps_by_address"
fullViewOptions={false}
widget={externalCallRPSWidget} widget={externalCallRPSWidget}
yAxisUnit="reqps" yAxisUnit="reqps"
onClickHandler={(ChartEvent, activeElements, chart, data): void => { onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -215,6 +228,9 @@ function External(): JSX.Element {
'external_call_rps_by_address', 'external_call_rps_by_address',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>
@ -236,11 +252,9 @@ function External(): JSX.Element {
</Button> </Button>
<Card> <Card>
<GraphTitle>External Call duration(by Address)</GraphTitle>
<GraphContainer> <GraphContainer>
<FullView <Graph
name="external_call_duration_by_address" name="external_call_duration_by_address"
fullViewOptions={false}
widget={externalCallDurationAddressWidget} widget={externalCallDurationAddressWidget}
yAxisUnit="ms" yAxisUnit="ms"
onClickHandler={(ChartEvent, activeElements, chart, data): void => { onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -252,6 +266,9 @@ function External(): JSX.Element {
'external_call_duration_by_address', 'external_call_duration_by_address',
); );
}} }}
allowClone={false}
allowDelete={false}
allowEdit={false}
/> />
</GraphContainer> </GraphContainer>
</Card> </Card>

View File

@ -1,16 +1,9 @@
import { Typography } from 'antd';
import getServiceOverview from 'api/metrics/getServiceOverview';
import getTopLevelOperations, { import getTopLevelOperations, {
ServiceDataProps, ServiceDataProps,
} from 'api/metrics/getTopLevelOperations'; } from 'api/metrics/getTopLevelOperations';
import getTopOperations from 'api/metrics/getTopOperations';
import axios from 'axios';
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
import Graph from 'components/Graph';
import Spinner from 'components/Spinner';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
import { routeConfig } from 'container/SideNav/config'; import { routeConfig } from 'container/SideNav/config';
import { getQueryString } from 'container/SideNav/helper'; import { getQueryString } from 'container/SideNav/helper';
import useResourceAttribute from 'hooks/useResourceAttribute'; import useResourceAttribute from 'hooks/useResourceAttribute';
@ -18,32 +11,30 @@ import {
convertRawQueriesToTraceSelectedTags, convertRawQueriesToTraceSelectedTags,
resourceAttributesToTagFilterItems, resourceAttributesToTagFilterItems,
} from 'hooks/useResourceAttribute/utils'; } 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 history from 'lib/history';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useQueries, UseQueryResult } from 'react-query'; import { useQuery } from 'react-query';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions'; import { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers'; 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 { EQueryType } from 'types/common/dashboard';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { Tags } from 'types/reducer/trace'; import { Tags } from 'types/reducer/trace';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { SOMETHING_WENT_WRONG } from '../../../constants/api'; import { GraphTitle } from '../constant';
import { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
import { import {
errorPercentage, errorPercentage,
operationPerSec, operationPerSec,
} from '../MetricsPageQueries/OverviewQueries'; } from '../MetricsPageQueries/OverviewQueries';
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles'; import { Col, Row } from '../styles';
import TopOperationsTable from '../TopOperationsTable'; import ServiceOverview from './Overview/ServiceOverview';
import TopLevelOperation from './Overview/TopLevelOperations';
import TopOperation from './Overview/TopOperation';
import { Button } from './styles'; import { Button } from './styles';
import { IServiceName } from './types';
import { import {
handleNonInQueryRange, handleNonInQueryRange,
onGraphClickHandler, onGraphClickHandler,
@ -54,7 +45,7 @@ function Application(): JSX.Element {
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>( const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime, (state) => state.globalTime,
); );
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<IServiceName>();
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { search } = useLocation(); const { search } = useLocation();
const { queries } = useResourceAttribute(); const { queries } = useResourceAttribute();
@ -86,53 +77,15 @@ function Application(): JSX.Element {
[handleSetTimeStamp], [handleSetTimeStamp],
); );
const queryResult = useQueries< const {
[ data: topLevelOperations,
UseQueryResult<PayloadProps>, isLoading: topLevelOperationsLoading,
UseQueryResult<PayloadPropsTopOpertions>, error: topLevelOperationsError,
UseQueryResult<ServiceDataProps>, isError: topLevelOperationsIsError,
] } = useQuery<ServiceDataProps>({
>([ queryKey: [servicename, minTime, maxTime, selectedTags],
{ queryFn: getTopLevelOperations,
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 selectedTraceTags: string = JSON.stringify( const selectedTraceTags: string = JSON.stringify(
convertRawQueriesToTraceSelectedTags(queries) || [], convertRawQueriesToTraceSelectedTags(queries) || [],
@ -146,37 +99,43 @@ function Application(): JSX.Element {
const operationPerSecWidget = useMemo( const operationPerSecWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: operationPerSec({ promql: [],
servicename, builder: operationPerSec({
tagFilterItems, servicename,
topLevelOperations: topLevelOperations tagFilterItems,
? topLevelOperations[servicename || ''] topLevelOperations: topLevelOperations
: [], ? topLevelOperations[servicename || '']
}), : [],
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.RATE_PER_OPS,
),
[servicename, topLevelOperations, tagFilterItems], [servicename, topLevelOperations, tagFilterItems],
); );
const errorPercentageWidget = useMemo( const errorPercentageWidget = useMemo(
() => () =>
getWidgetQueryBuilder({ getWidgetQueryBuilder(
queryType: EQueryType.QUERY_BUILDER, {
promql: [], queryType: EQueryType.QUERY_BUILDER,
builder: errorPercentage({ promql: [],
servicename, builder: errorPercentage({
tagFilterItems, servicename,
topLevelOperations: topLevelOperations tagFilterItems,
? topLevelOperations[servicename || ''] topLevelOperations: topLevelOperations
: [], ? topLevelOperations[servicename || '']
}), : [],
clickhouse_sql: [], }),
id: uuid(), clickhouse_sql: [],
}), id: uuid(),
},
GraphTitle.ERROR_PERCENTAGE,
),
[servicename, topLevelOperations, tagFilterItems], [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 ( return (
<> <>
<Row gutter={24}> <Row gutter={24}>
<Col span={12}> <Col span={12}>
<Button <ServiceOverview
type="default" onDragSelect={onDragSelect}
size="small" handleGraphClick={handleGraphClick}
id="Service_button" selectedTimeStamp={selectedTimeStamp}
onClick={onViewTracePopupClick({ selectedTraceTags={selectedTraceTags}
servicename, tagFilterItems={tagFilterItems}
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>
</Col> </Col>
<Col span={12}> <Col span={12}>
@ -328,30 +197,17 @@ function Application(): JSX.Element {
> >
View Traces View Traces
</Button> </Button>
<Card> <TopLevelOperation
{topLevelOperationsIsError ? ( handleGraphClick={handleGraphClick}
<Typography> onDragSelect={onDragSelect}
{axios.isAxiosError(topLevelOperationsError) topLevelOperationsError={topLevelOperationsError}
? topLevelOperationsError.response?.data topLevelOperationsLoading={topLevelOperationsLoading}
: SOMETHING_WENT_WRONG} topLevelOperationsIsError={topLevelOperationsIsError}
</Typography> name="operations_per_sec"
) : ( widget={operationPerSecWidget}
<> yAxisUnit="ops"
<GraphTitle>Rate (ops/s)</GraphTitle> opName="Rate"
<GraphContainer> />
<FullView
name="operations_per_sec"
fullViewOptions={false}
onClickHandler={handleGraphClick('Rate')}
widget={operationPerSecWidget}
yAxisUnit="ops"
onDragSelect={onDragSelect}
isDependedDataLoaded={topLevelOperationsIsLoading}
/>
</GraphContainer>
</>
)}
</Card>
</Col> </Col>
</Row> </Row>
<Row gutter={24}> <Row gutter={24}>
@ -367,43 +223,28 @@ function Application(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <TopLevelOperation
{topLevelOperationsIsError ? ( handleGraphClick={handleGraphClick}
<Typography> onDragSelect={onDragSelect}
{axios.isAxiosError(topLevelOperationsError) topLevelOperationsError={topLevelOperationsError}
? topLevelOperationsError.response?.data topLevelOperationsLoading={topLevelOperationsLoading}
: SOMETHING_WENT_WRONG} topLevelOperationsIsError={topLevelOperationsIsError}
</Typography> name="error_percentage_%"
) : ( widget={errorPercentageWidget}
<> yAxisUnit="%"
<GraphTitle>Error Percentage</GraphTitle> opName="Error"
<GraphContainer> />
<FullView
name="error_percentage_%"
fullViewOptions={false}
onClickHandler={handleGraphClick('Error')}
widget={errorPercentageWidget}
yAxisUnit="%"
onDragSelect={onDragSelect}
isDependedDataLoaded={topLevelOperationsIsLoading}
/>
</GraphContainer>
</>
)}
</Card>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Card> <TopOperation />
<TopOperationsTable data={topOperations || []} />
</Card>
</Col> </Col>
</Row> </Row>
</> </>
); );
} }
type ClickHandlerType = ( export type ClickHandlerType = (
ChartEvent: ChartEvent, ChartEvent: ChartEvent,
activeElements: ActiveElement[], activeElements: ActiveElement[],
chart: Chart, chart: Chart,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -1,3 +0,0 @@
export const legend = {
address: '{{address}}',
};

View File

@ -0,0 +1,3 @@
export interface IServiceName {
servicename: string;
}

View 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',
}

View 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 {};
};