ability to filter by deployment environment service map (#2506)

This commit is contained in:
Palash Gupta 2023-03-29 18:31:59 +05:30 committed by GitHub
parent 99ed314fc9
commit 3f96325ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 85 deletions

View File

@ -17,6 +17,7 @@ import { Card, GraphContainer, GraphTitle, Row } from '../styles';
import { Button } from './styles'; import { Button } from './styles';
import { import {
dbSystemTags, dbSystemTags,
handleNonInQueryRange,
onGraphClickHandler, onGraphClickHandler,
onViewTracePopupClick, onViewTracePopupClick,
} from './util'; } from './util';
@ -25,10 +26,13 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element {
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<{ servicename?: string }>();
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { queries } = useResourceAttribute(); const { queries } = useResourceAttribute();
const tagFilterItems = useMemo( const tagFilterItems = useMemo(
() => resourceAttributesToTagFilterItems(queries) || [], () =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
[queries], [queries],
); );
const selectedTraceTags: string = useMemo( const selectedTraceTags: string = useMemo(
() => () =>
JSON.stringify( JSON.stringify(

View File

@ -18,7 +18,11 @@ import { Widgets } from 'types/api/dashboard/getAll';
import { Card, GraphContainer, GraphTitle, Row } from '../styles'; import { Card, GraphContainer, GraphTitle, Row } from '../styles';
import { legend } from './constant'; import { legend } from './constant';
import { Button } from './styles'; import { Button } from './styles';
import { onGraphClickHandler, onViewTracePopupClick } from './util'; import {
handleNonInQueryRange,
onGraphClickHandler,
onViewTracePopupClick,
} from './util';
function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
@ -27,7 +31,8 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
const { queries } = useResourceAttribute(); const { queries } = useResourceAttribute();
const tagFilterItems = useMemo( const tagFilterItems = useMemo(
() => resourceAttributesToTagFilterItems(queries) || [], () =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
[queries], [queries],
); );

View File

@ -26,7 +26,11 @@ import {
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles'; import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
import TopOperationsTable from '../TopOperationsTable'; import TopOperationsTable from '../TopOperationsTable';
import { Button } from './styles'; import { Button } from './styles';
import { onGraphClickHandler, onViewTracePopupClick } from './util'; import {
handleNonInQueryRange,
onGraphClickHandler,
onViewTracePopupClick,
} from './util';
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<{ servicename?: string }>();
@ -67,7 +71,8 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
); );
const tagFilterItems = useMemo( const tagFilterItems = useMemo(
() => resourceAttributesToTagFilterItems(queries) || [], () =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
[queries], [queries],
); );

View File

@ -2,6 +2,7 @@ import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import history from 'lib/history'; import history from 'lib/history';
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
import { Tags } from 'types/reducer/trace'; import { Tags } from 'types/reducer/trace';
export const dbSystemTags: Tags[] = [ export const dbSystemTags: Tags[] = [
@ -84,3 +85,16 @@ export function onGraphClickHandler(
} }
}; };
} }
export const handleNonInQueryRange = (
tags: IQueryBuilderTagFilterItems[],
): IQueryBuilderTagFilterItems[] =>
tags.map((tag) => {
if (tag.op === 'Not IN') {
return {
...tag,
op: 'NIN',
};
}
return tag;
});

View File

@ -17,7 +17,6 @@ import {
GetTagKeys, GetTagKeys,
GetTagValues, GetTagValues,
OperatorSchema, OperatorSchema,
resourceAttributesQueryToPromQL,
} from './utils'; } from './utils';
function ResourceProvider({ children }: Props): JSX.Element { function ResourceProvider({ children }: Props): JSX.Element {
@ -29,10 +28,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
getResourceAttributeQueriesFromURL(), getResourceAttributeQueriesFromURL(),
); );
const [promQLQuery, setpromQLQuery] = useState<string>(
resourceAttributesQueryToPromQL(queries),
);
const [optionsData, setOptionsData] = useState<OptionsData>({ const [optionsData, setOptionsData] = useState<OptionsData>({
mode: undefined, mode: undefined,
options: [], options: [],
@ -55,7 +50,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
: '', : '',
}); });
setQueries(queries); setQueries(queries);
setpromQLQuery(resourceAttributesQueryToPromQL(queries));
}, },
[pathname], [pathname],
); );
@ -144,7 +138,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
() => ({ () => ({
queries, queries,
staging, staging,
promQLQuery,
handleClearAll, handleClearAll,
handleClose, handleClose,
handleBlur, handleBlur,
@ -161,7 +154,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
handleClose, handleClose,
handleFocus, handleFocus,
loading, loading,
promQLQuery,
queries, queries,
staging, staging,
selectedQuery, selectedQuery,

View File

@ -20,7 +20,6 @@ export interface OptionsData {
export interface IResourceAttributeProps { export interface IResourceAttributeProps {
queries: IResourceAttribute[]; queries: IResourceAttribute[];
staging: string[]; staging: string[];
promQLQuery: string;
handleClearAll: VoidFunction; handleClearAll: VoidFunction;
handleClose: (id: string) => void; handleClose: (id: string) => void;
handleBlur: VoidFunction; handleBlur: VoidFunction;

View File

@ -57,26 +57,6 @@ export const convertRawQueriesToTraceSelectedTags = (
TagType: tagType, TagType: tagType,
})); }));
/**
* Converts Resource Attribute Queries to PromQL query string
*/
export const resourceAttributesQueryToPromQL = (
queries: IResourceAttribute[],
): string => {
let parsedQueryString = '';
if (Array.isArray(queries))
queries.forEach((query) => {
parsedQueryString += `, ${
query.tagKey
}${convertOperatorLabelToMetricOperator(
query.operator,
)}"${query.tagValue.join('|')}"`;
});
return parsedQueryString;
};
/* Convert resource attributes to tagFilter items for queryBuilder */ /* Convert resource attributes to tagFilter items for queryBuilder */
export const resourceAttributesToTagFilterItems = ( export const resourceAttributesToTagFilterItems = (
queries: IResourceAttribute[], queries: IResourceAttribute[],

View File

@ -0,0 +1,56 @@
/* eslint-disable */
//@ts-nocheck
import { useIsDarkMode } from 'hooks/useDarkMode';
import React, { memo } from 'react';
import { ForceGraph2D } from 'react-force-graph';
import { getGraphData, getTooltip, transformLabel } from './utils';
function Map({ fgRef, serviceMap }: any): JSX.Element {
const isDarkMode = useIsDarkMode();
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
const graphData = { nodes, links };
return (
<ForceGraph2D
ref={fgRef}
cooldownTicks={100}
graphData={graphData}
linkLabel={getTooltip}
linkAutoColorBy={(d) => d.target}
linkDirectionalParticles="value"
linkDirectionalParticleSpeed={(d) => d.value}
nodeCanvasObject={(node, ctx) => {
const label = transformLabel(node.id);
const { fontSize } = node;
ctx.font = `${fontSize}px Roboto`;
const { width } = node;
ctx.fillStyle = node.color;
ctx.beginPath();
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
ctx.fill();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
ctx.fillText(label, node.x, node.y);
}}
onLinkHover={(node) => {
const tooltip = document.querySelector('.graph-tooltip');
if (tooltip && node) {
tooltip.innerHTML = getTooltip(node);
}
}}
nodePointerAreaPaint={(node, color, ctx) => {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
ctx.fill();
}}
/>
);
}
export default memo(Map);

View File

@ -3,9 +3,10 @@
import { Card } from 'antd'; import { Card } from 'antd';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import { useIsDarkMode } from 'hooks/useDarkMode'; import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { ForceGraph2D } from 'react-force-graph';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom'; import { RouteComponentProps, withRouter } from 'react-router-dom';
import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions'; import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions';
@ -13,7 +14,7 @@ import { AppState } from 'store/reducers';
import styled from 'styled-components'; import styled from 'styled-components';
import { GlobalTime } from 'types/actions/globalTime'; import { GlobalTime } from 'types/actions/globalTime';
import { getGraphData, getTooltip, getZoomPx, transformLabel } from './utils'; import Map from './Map';
const Container = styled.div` const Container = styled.div`
.force-graph-container { .force-graph-container {
@ -38,7 +39,10 @@ const Container = styled.div`
interface ServiceMapProps extends RouteComponentProps<any> { interface ServiceMapProps extends RouteComponentProps<any> {
serviceMap: ServiceMapStore; serviceMap: ServiceMapStore;
globalTime: GlobalTime; globalTime: GlobalTime;
getDetailedServiceMapItems: (time: GlobalTime) => void; getDetailedServiceMapItems: (
time: GlobalTime,
queries: IResourceAttribute[],
) => void;
} }
interface graphNode { interface graphNode {
id: string; id: string;
@ -60,17 +64,17 @@ export interface graphDataType {
function ServiceMap(props: ServiceMapProps): JSX.Element { function ServiceMap(props: ServiceMapProps): JSX.Element {
const fgRef = useRef(); const fgRef = useRef();
const isDarkMode = useIsDarkMode();
const { getDetailedServiceMapItems, globalTime, serviceMap } = props; const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
const { queries } = useResourceAttribute();
useEffect(() => { useEffect(() => {
/* /*
Call the apis only when the route is loaded. Call the apis only when the route is loaded.
Check this issue: https://github.com/SigNoz/signoz/issues/110 Check this issue: https://github.com/SigNoz/signoz/issues/110
*/ */
getDetailedServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime, queries);
}, [globalTime, getDetailedServiceMapItems]); }, [globalTime, getDetailedServiceMapItems, queries]);
useEffect(() => { useEffect(() => {
fgRef.current && fgRef.current.d3Force('charge').strength(-400); fgRef.current && fgRef.current.d3Force('charge').strength(-400);
@ -83,51 +87,15 @@ function ServiceMap(props: ServiceMapProps): JSX.Element {
if (!serviceMap.loading && serviceMap.items.length === 0) { if (!serviceMap.loading && serviceMap.items.length === 0) {
return ( return (
<Container> <Container>
<ResourceAttributesFilter />
<Card>No Service Found</Card> <Card>No Service Found</Card>
</Container> </Container>
); );
} }
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
const graphData = { nodes, links };
return ( return (
<Container> <Container>
<ForceGraph2D <ResourceAttributesFilter />
ref={fgRef} <Map fgRef={fgRef} serviceMap={serviceMap} />
cooldownTicks={100}
graphData={graphData}
linkLabel={getTooltip}
linkAutoColorBy={(d) => d.target}
linkDirectionalParticles="value"
linkDirectionalParticleSpeed={(d) => d.value}
nodeCanvasObject={(node, ctx) => {
const label = transformLabel(node.id);
const { fontSize } = node;
ctx.font = `${fontSize}px Roboto`;
const { width } = node;
ctx.fillStyle = node.color;
ctx.beginPath();
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
ctx.fill();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
ctx.fillText(label, node.x, node.y);
}}
onLinkHover={(node) => {
const tooltip = document.querySelector('.graph-tooltip');
if (tooltip && node) {
tooltip.innerHTML = getTooltip(node);
}
}}
nodePointerAreaPaint={(node, color, ctx) => {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
ctx.fill();
}}
/>
</Container> </Container>
); );
} }

View File

@ -1,4 +1,6 @@
import api from 'api'; import api from 'api';
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { GlobalTime } from 'types/actions/globalTime'; import { GlobalTime } from 'types/actions/globalTime';
@ -30,16 +32,17 @@ export interface ServiceMapLoading {
}; };
} }
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => async ( export const getDetailedServiceMapItems = (
dispatch: Dispatch, globalTime: GlobalTime,
): Promise<void> => { queries: IResourceAttribute[],
) => async (dispatch: Dispatch): Promise<void> => {
const start = `${globalTime.minTime}`; const start = `${globalTime.minTime}`;
const end = `${globalTime.maxTime}`; const end = `${globalTime.maxTime}`;
const serviceMapPayload = { const serviceMapPayload = {
start, start,
end, end,
tags: [], tags: convertRawQueriesToTraceSelectedTags(queries),
}; };
const [dependencyGraphResponse] = await Promise.all([ const [dependencyGraphResponse] = await Promise.all([
api.post<ServicesMapItem[]>(`/dependency_graph`, serviceMapPayload), api.post<ServicesMapItem[]>(`/dependency_graph`, serviceMapPayload),