Refactor the Metric Application Layer (#2984)

* refactor: seperate the dependency of top overview query

* refactor: added error handle for api call using usequery

* refactor: update api layar and condition in component

* fix: onDragSelect re-render all graph data

* refactor: removed console

* refactor: corrected names and updated implemented required condition

* fix: the api call issue

* refactor: removed useeffect

* refactor: reverted the unnecessary changes

* refactor: removed the login from service level

* refactor: removed the unwanted code

* refactor: reverted the unwanted changes in getDashboardVariable

* refactor: instead of useQuery used useQueries

* refactor: changed the dependencies of useQuery key

* refactor: linter fixes

* refactor: delete the unrequired files

* fix: generecity of the type

* fix: moved the type to component

* fix: move the logic from container layer to pages layer

* refactor: optimised some part of the code

* refactor: review changes

* refactor: optimised the checks

* refactor: checking if the dependency data loaded in full view

* refactor: resolve the error of props in overview.ts

* refactor: small changes

* refactor: enforced the typecasting of constant variable

* refactor: refactoring in some of the changes are updated

* refactor: refactoring in some of the changes are updated

* refactor: removed the extra parameter from useGetQueryRange

* refactor: revert the changes back for tab

* refactor: metrics application is updated

* chore: loading condition is updated for full view component

* chore: moved the serviceDataProps type to api layer

* chore: message name is updated

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
Rajat Dabade 2023-07-13 15:17:59 +05:30 committed by GitHub
parent 08d496e314
commit 98745fc307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 305 additions and 426 deletions

View File

@ -7,7 +7,7 @@ export const ServicesTablePage = Loadable(
export const ServiceMetricsPage = Loadable( export const ServiceMetricsPage = Loadable(
() => () =>
import( import(
/* webpackChunkName: "ServiceMetricsPage" */ 'pages/MetricApplication' /* webpackChunkName: "ServiceMetricsPage" */ 'pages/MetricsApplication'
), ),
); );

View File

@ -1,30 +1,16 @@
import axios from 'api'; import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/metrics/getServiceOverview'; import { PayloadProps, Props } from 'types/api/metrics/getServiceOverview';
const getServiceOverview = async ( const getServiceOverview = async (props: Props): Promise<PayloadProps> => {
props: Props, const response = await axios.post(`/service/overview`, {
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => { start: `${props.start}`,
try { end: `${props.end}`,
const response = await axios.post(`/service/overview`, { service: props.service,
start: `${props.start}`, step: props.step,
end: `${props.end}`, tags: props.selectedTags,
service: props.service, });
step: props.step,
tags: props.selectedTags,
});
return { return response.data;
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
}; };
export default getServiceOverview; export default getServiceOverview;

View File

@ -1,24 +1,12 @@
import axios from 'api'; import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/metrics/getTopLevelOperations';
const getTopLevelOperations = async ( const getTopLevelOperations = async (): Promise<ServiceDataProps> => {
props: Props, const response = await axios.post(`/service/top_level_operations`);
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => { return response.data;
try { };
const response = await axios.post(`/service/top_level_operations`);
return { export type ServiceDataProps = {
statusCode: 200, [serviceName: string]: string[];
error: null,
message: response.data.status,
payload: response.data[props.service],
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
}; };
export default getTopLevelOperations; export default getTopLevelOperations;

View File

@ -1,29 +1,15 @@
import axios from 'api'; import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/metrics/getTopOperations'; import { PayloadProps, Props } from 'types/api/metrics/getTopOperations';
const getTopOperations = async ( const getTopOperations = async (props: Props): Promise<PayloadProps> => {
props: Props, const response = await axios.post(`/service/top_operations`, {
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => { start: `${props.start}`,
try { end: `${props.end}`,
const response = await axios.post(`/service/top_operations`, { service: props.service,
start: `${props.start}`, tags: props.selectedTags,
end: `${props.end}`, });
service: props.service,
tags: props.selectedTags,
});
return { return response.data;
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
}; };
export default getTopOperations; export default getTopOperations;

View File

@ -1,3 +1,5 @@
const SOMETHING_WENT_WRONG = 'Something went wrong';
const getVersion = 'version'; const getVersion = 'version';
export { getVersion }; export { getVersion, SOMETHING_WENT_WRONG };

View File

@ -26,6 +26,7 @@ function FullView({
name, name,
yAxisUnit, yAxisUnit,
onDragSelect, onDragSelect,
isDependedDataLoaded = false,
}: FullViewProps): JSX.Element { }: FullViewProps): JSX.Element {
const { selectedTime: globalSelectedTime } = useSelector< const { selectedTime: globalSelectedTime } = useSelector<
AppState, AppState,
@ -61,6 +62,7 @@ function FullView({
}, },
{ {
queryKey, queryKey,
enabled: !isDependedDataLoaded,
}, },
); );
@ -76,9 +78,7 @@ function FullView({
[response], [response],
); );
const isLoading = response.isLoading === true; if (response.status === 'idle' || response.status === 'loading') {
if (isLoading) {
return <Spinner height="100%" size="large" tip="Loading..." />; return <Spinner height="100%" size="large" tip="Loading..." />;
} }
@ -123,6 +123,7 @@ interface FullViewProps {
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 = {
@ -130,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,13 @@
import { Typography } from 'antd';
import getServiceOverview from 'api/metrics/getServiceOverview';
import getTopLevelOperations, {
ServiceDataProps,
} from 'api/metrics/getTopLevelOperations';
import getTopOperations from 'api/metrics/getTopOperations';
import axios from 'axios';
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
import Graph from 'components/Graph'; 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 FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
@ -12,16 +20,22 @@ import {
} from 'hooks/useResourceAttribute/utils'; } from 'hooks/useResourceAttribute/utils';
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond'; import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
import { colors } from 'lib/getRandomColor'; 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 { 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 MetricReducer from 'types/reducer/metrics'; import { GlobalReducer } from 'types/reducer/globalTime';
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 { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
import { import {
errorPercentage, errorPercentage,
@ -37,9 +51,17 @@ import {
} from './util'; } from './util';
function Application(): JSX.Element { function Application(): JSX.Element {
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { servicename } = useParams<{ servicename?: string }>(); const { servicename } = useParams<{ servicename?: string }>();
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { search } = useLocation(); const { search } = useLocation();
const { queries } = useResourceAttribute();
const selectedTags = useMemo(
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
[queries],
);
const handleSetTimeStamp = useCallback((selectTime: number) => { const handleSetTimeStamp = useCallback((selectTime: number) => {
setSelectedTimeStamp(selectTime); setSelectedTimeStamp(selectTime);
@ -64,12 +86,53 @@ function Application(): JSX.Element {
[handleSetTimeStamp], [handleSetTimeStamp],
); );
const { topOperations, serviceOverview, topLevelOperations } = useSelector< const queryResult = useQueries<
AppState, [
MetricReducer UseQueryResult<PayloadProps>,
>((state) => state.metrics); UseQueryResult<PayloadPropsTopOpertions>,
UseQueryResult<ServiceDataProps>,
]
>([
{
queryKey: [servicename, selectedTags, minTime, maxTime],
queryFn: (): Promise<PayloadProps> =>
getServiceOverview({
service: servicename || '',
start: minTime,
end: maxTime,
step: getStep({
start: minTime,
end: maxTime,
inputFormat: 'ns',
}),
selectedTags,
}),
},
{
queryKey: [minTime, maxTime, servicename, selectedTags],
queryFn: (): Promise<PayloadPropsTopOpertions> =>
getTopOperations({
service: servicename || '',
start: minTime,
end: maxTime,
selectedTags,
}),
},
{
queryKey: [servicename, minTime, maxTime, selectedTags],
queryFn: (): Promise<ServiceDataProps> => getTopLevelOperations(),
},
]);
const { queries } = useResourceAttribute(); 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) || [],
@ -89,7 +152,9 @@ function Application(): JSX.Element {
builder: operationPerSec({ builder: operationPerSec({
servicename, servicename,
tagFilterItems, tagFilterItems,
topLevelOperations, topLevelOperations: topLevelOperations
? topLevelOperations[servicename || '']
: [],
}), }),
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
@ -105,7 +170,9 @@ function Application(): JSX.Element {
builder: errorPercentage({ builder: errorPercentage({
servicename, servicename,
tagFilterItems, tagFilterItems,
topLevelOperations, topLevelOperations: topLevelOperations
? topLevelOperations[servicename || '']
: [],
}), }),
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
@ -158,8 +225,12 @@ function Application(): JSX.Element {
[], [],
); );
const dataSets = useMemo( const dataSets = useMemo(() => {
() => [ if (!serviceOverview) {
return [];
}
return [
{ {
data: serviceOverview.map((e) => data: serviceOverview.map((e) =>
parseFloat(convertToNanoSecondsToSecond(e.p99)), parseFloat(convertToNanoSecondsToSecond(e.p99)),
@ -178,19 +249,25 @@ function Application(): JSX.Element {
), ),
...generalChartDataProperties('p50 Latency', 2), ...generalChartDataProperties('p50 Latency', 2),
}, },
], ];
[generalChartDataProperties, serviceOverview], }, [generalChartDataProperties, serviceOverview]);
);
const data = useMemo( const data = useMemo(() => {
() => ({ if (!serviceOverview) {
return {
datasets: [],
labels: [],
};
}
return {
datasets: dataSets, datasets: dataSets,
labels: serviceOverview.map( labels: serviceOverview.map(
(e) => new Date(parseFloat(convertToNanoSecondsToSecond(e.timestamp))), (e) => new Date(parseFloat(convertToNanoSecondsToSecond(e.timestamp))),
), ),
}), };
[serviceOverview, dataSets], }, [serviceOverview, dataSets]);
);
return ( return (
<> <>
<Row gutter={24}> <Row gutter={24}>
@ -208,18 +285,33 @@ function Application(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <Card>
<GraphTitle>Latency</GraphTitle> {serviceOverviewIsError ? (
<GraphContainer> <Typography>
<Graph {axios.isAxiosError(serviceOverviewError)
animate={false} ? serviceOverviewError.response?.data
onClickHandler={handleGraphClick('Service')} : SOMETHING_WENT_WRONG}
name="service_latency" </Typography>
type="line" ) : (
data={data} <>
yAxisUnit="ms" <GraphTitle>Latency</GraphTitle>
onDragSelect={onDragSelect} {serviceOverviewIsLoading && (
/> <Spinner size="large" tip="Loading..." height="40vh" />
</GraphContainer> )}
{!serviceOverviewIsLoading && (
<GraphContainer>
<Graph
animate={false}
onClickHandler={handleGraphClick('Service')}
name="service_latency"
type="line"
data={data}
yAxisUnit="ms"
onDragSelect={onDragSelect}
/>
</GraphContainer>
)}
</>
)}
</Card> </Card>
</Col> </Col>
@ -237,17 +329,28 @@ function Application(): JSX.Element {
View Traces View Traces
</Button> </Button>
<Card> <Card>
<GraphTitle>Rate (ops/s)</GraphTitle> {topLevelOperationsIsError ? (
<GraphContainer> <Typography>
<FullView {axios.isAxiosError(topLevelOperationsError)
name="operations_per_sec" ? topLevelOperationsError.response?.data
fullViewOptions={false} : SOMETHING_WENT_WRONG}
onClickHandler={handleGraphClick('Rate')} </Typography>
widget={operationPerSecWidget} ) : (
yAxisUnit="ops" <>
onDragSelect={onDragSelect} <GraphTitle>Rate (ops/s)</GraphTitle>
/> <GraphContainer>
</GraphContainer> <FullView
name="operations_per_sec"
fullViewOptions={false}
onClickHandler={handleGraphClick('Rate')}
widget={operationPerSecWidget}
yAxisUnit="ops"
onDragSelect={onDragSelect}
isDependedDataLoaded={topLevelOperationsIsLoading}
/>
</GraphContainer>
</>
)}
</Card> </Card>
</Col> </Col>
</Row> </Row>
@ -265,23 +368,34 @@ function Application(): JSX.Element {
</Button> </Button>
<Card> <Card>
<GraphTitle>Error Percentage</GraphTitle> {topLevelOperationsIsError ? (
<GraphContainer> <Typography>
<FullView {axios.isAxiosError(topLevelOperationsError)
name="error_percentage_%" ? topLevelOperationsError.response?.data
fullViewOptions={false} : SOMETHING_WENT_WRONG}
onClickHandler={handleGraphClick('Error')} </Typography>
widget={errorPercentageWidget} ) : (
yAxisUnit="%" <>
onDragSelect={onDragSelect} <GraphTitle>Error Percentage</GraphTitle>
/> <GraphContainer>
</GraphContainer> <FullView
name="error_percentage_%"
fullViewOptions={false}
onClickHandler={handleGraphClick('Error')}
widget={errorPercentageWidget}
yAxisUnit="%"
onDragSelect={onDragSelect}
isDependedDataLoaded={topLevelOperationsIsLoading}
/>
</GraphContainer>
</>
)}
</Card> </Card>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Card> <Card>
<TopOperationsTable data={topOperations} /> <TopOperationsTable data={topOperations || []} />
</Card> </Card>
</Col> </Col>
</Row> </Row>

View File

@ -1,90 +0,0 @@
import RouteTab from 'components/RouteTab';
import ROUTES from 'constants/routes';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import history from 'lib/history';
import { memo, useMemo } from 'react';
import { generatePath, useParams } from 'react-router-dom';
import { useLocation } from 'react-use';
import DBCall from './Tabs/DBCall';
import External from './Tabs/External';
import Overview from './Tabs/Overview';
function OverViewTab(): JSX.Element {
return <Overview />;
}
function DbCallTab(): JSX.Element {
return <DBCall />;
}
function ExternalTab(): JSX.Element {
return <External />;
}
function ServiceMetrics(): JSX.Element {
const { search } = useLocation();
const { servicename } = useParams<{ servicename: string }>();
const searchParams = new URLSearchParams(search);
const tab = searchParams.get('tab');
const overMetrics = 'Overview Metrics';
const dbCallMetrics = 'Database Calls';
const externalMetrics = 'External Calls';
const getActiveKey = (): string => {
switch (tab) {
case null: {
return overMetrics;
}
case dbCallMetrics: {
return dbCallMetrics;
}
case externalMetrics: {
return externalMetrics;
}
default: {
return overMetrics;
}
}
};
const activeKey = getActiveKey();
const routes = useMemo(
() => [
{
Component: OverViewTab,
name: overMetrics,
route: `${generatePath(ROUTES.SERVICE_METRICS, {
servicename,
})}?tab=${overMetrics}`,
},
{
Component: DbCallTab,
name: dbCallMetrics,
route: `${generatePath(ROUTES.SERVICE_METRICS, {
servicename,
})}?tab=${dbCallMetrics}`,
},
{
Component: ExternalTab,
name: externalMetrics,
route: `${generatePath(ROUTES.SERVICE_METRICS, {
servicename,
})}?tab=${externalMetrics}`,
},
],
[servicename],
);
return (
<>
<ResourceAttributesFilter />
<RouteTab routes={routes} history={history} activeKey={activeKey} />
</>
);
}
export default memo(ServiceMetrics);

View File

@ -1,76 +0,0 @@
import { Typography } from 'antd';
import Spinner from 'components/Spinner';
import MetricsApplicationContainer from 'container/MetricsApplication';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import { useEffect, useMemo } from 'react';
import { connect, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import {
GetInitialData,
GetInitialDataProps,
} from 'store/actions/metrics/getInitialData';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { GlobalReducer } from 'types/reducer/globalTime';
import MetricReducer from 'types/reducer/metrics';
import { Tags } from 'types/reducer/trace';
function MetricsApplication({ getInitialData }: MetricsProps): JSX.Element {
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { error, errorMessage, metricsApplicationLoading } = useSelector<
AppState,
MetricReducer
>((state) => state.metrics);
const { servicename } = useParams<ServiceProps>();
const { queries } = useResourceAttribute();
const selectedTags = useMemo(
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
[queries],
);
useEffect(() => {
if (servicename !== undefined) {
getInitialData({
serviceName: servicename,
maxTime,
minTime,
selectedTags,
});
}
}, [servicename, getInitialData, maxTime, minTime, selectedTags]);
if (metricsApplicationLoading) {
return <Spinner tip="Loading..." />;
}
if (error) {
return <Typography>{errorMessage}</Typography>;
}
return <MetricsApplicationContainer />;
}
interface DispatchProps {
getInitialData: (props: GetInitialDataProps) => void;
}
interface ServiceProps {
servicename?: string;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
getInitialData: bindActionCreators(GetInitialData, dispatch),
});
type MetricsProps = DispatchProps;
export default connect(null, mapDispatchToProps)(MetricsApplication);

View File

@ -0,0 +1,8 @@
import { MetricsApplicationTab } from './types';
export const TAB_KEYS_VS_METRICS_APPLICATION_KEY = {
[MetricsApplicationTab.DB_CALL_METRICS]: MetricsApplicationTab.DB_CALL_METRICS,
[MetricsApplicationTab.EXTERNAL_METRICS]:
MetricsApplicationTab.EXTERNAL_METRICS,
[MetricsApplicationTab.OVER_METRICS]: MetricsApplicationTab.OVER_METRICS,
};

View File

@ -0,0 +1,54 @@
import RouteTab from 'components/RouteTab';
import ROUTES from 'constants/routes';
import DBCall from 'container/MetricsApplication/Tabs/DBCall';
import External from 'container/MetricsApplication/Tabs/External';
import Overview from 'container/MetricsApplication/Tabs/Overview';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import history from 'lib/history';
import { useMemo } from 'react';
import { generatePath, useParams } from 'react-router-dom';
import { MetricsApplicationTab, TAB_KEY_VS_LABEL } from './types';
import useMetricsApplicationTabKey from './useMetricsApplicationTabKey';
function MetricsApplication(): JSX.Element {
const { servicename } = useParams<{ servicename: string }>();
const activeKey = useMetricsApplicationTabKey();
const routes = useMemo(
() => [
{
Component: Overview,
name: TAB_KEY_VS_LABEL[MetricsApplicationTab.OVER_METRICS],
route: `${generatePath(ROUTES.SERVICE_METRICS, {
servicename,
})}?tab=${MetricsApplicationTab.OVER_METRICS}`,
},
{
Component: DBCall,
name: TAB_KEY_VS_LABEL[MetricsApplicationTab.DB_CALL_METRICS],
route: `${generatePath(ROUTES.SERVICE_METRICS, {
servicename,
})}?tab=${MetricsApplicationTab.DB_CALL_METRICS}`,
},
{
Component: External,
name: TAB_KEY_VS_LABEL[MetricsApplicationTab.EXTERNAL_METRICS],
route: `${generatePath(ROUTES.SERVICE_METRICS, {
servicename,
})}?tab=${MetricsApplicationTab.EXTERNAL_METRICS}`,
},
],
[servicename],
);
return (
<>
<ResourceAttributesFilter />
<RouteTab routes={routes} history={history} activeKey={activeKey} />
</>
);
}
export default MetricsApplication;

View File

@ -0,0 +1,11 @@
export enum MetricsApplicationTab {
OVER_METRICS = 'OVER_METRICS',
DB_CALL_METRICS = 'DB_CALL_METRICS',
EXTERNAL_METRICS = 'EXTERNAL_METRICS',
}
export const TAB_KEY_VS_LABEL = {
[MetricsApplicationTab.OVER_METRICS]: 'Overview',
[MetricsApplicationTab.DB_CALL_METRICS]: 'DB Call Metrics',
[MetricsApplicationTab.EXTERNAL_METRICS]: 'External Metrics',
};

View File

@ -0,0 +1,14 @@
import useUrlQuery from 'hooks/useUrlQuery';
import { TAB_KEY_VS_LABEL } from './types';
import { getMetricsApplicationKey } from './utils';
const useMetricsApplicationTabKey = (): string => {
const urlParams = useUrlQuery();
const tab = urlParams.get('tab');
return TAB_KEY_VS_LABEL[getMetricsApplicationKey(tab)];
};
export default useMetricsApplicationTabKey;

View File

@ -0,0 +1,17 @@
import { TAB_KEYS_VS_METRICS_APPLICATION_KEY } from './config';
import { MetricsApplicationTab } from './types';
export const isMetricsApplicationTab = (
tab: string,
): tab is MetricsApplicationTab =>
Object.values(MetricsApplicationTab).includes(tab as MetricsApplicationTab);
export const getMetricsApplicationKey = (
tab: string | null,
): MetricsApplicationTab => {
if (tab && isMetricsApplicationTab(tab)) {
return TAB_KEYS_VS_METRICS_APPLICATION_KEY[tab];
}
return MetricsApplicationTab.OVER_METRICS;
};

View File

@ -1,137 +0,0 @@
// import getDBOverView from 'api/metrics/getDBOverView';
// import getExternalAverageDuration from 'api/metrics/getExternalAverageDuration';
// import getExternalError from 'api/metrics/getExternalError';
// import getExternalService from 'api/metrics/getExternalService';
import getServiceOverview from 'api/metrics/getServiceOverview';
import getTopLevelOperations from 'api/metrics/getTopLevelOperations';
import getTopOperations from 'api/metrics/getTopOperations';
import { AxiosError } from 'axios';
import GetMinMax from 'lib/getMinMax';
import getStep from 'lib/getStep';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { Props } from 'types/api/metrics/getDBOverview';
import { GlobalReducer } from 'types/reducer/globalTime';
import { Tags } from 'types/reducer/trace';
export const GetInitialData = (
props: GetInitialDataProps,
): ((
dispatch: Dispatch<AppActions>,
getState: () => AppState,
) => void) => async (dispatch, getState): Promise<void> => {
try {
const { globalTime } = getState();
/**
* @description This is because we keeping the store as source of truth
*/
if (
props.maxTime !== globalTime.maxTime &&
props.minTime !== globalTime.minTime
) {
return;
}
dispatch({
type: 'GET_INITIAL_APPLICATION_LOADING',
});
const { maxTime, minTime } = GetMinMax(globalTime.selectedTime, [
globalTime.minTime / 1000000,
globalTime.maxTime / 1000000,
]);
const [
// getDBOverViewResponse,
// getExternalAverageDurationResponse,
// getExternalErrorResponse,
// getExternalServiceResponse,
getServiceOverviewResponse,
getTopOperationsResponse,
getTopLevelOperationsResponse,
] = await Promise.all([
// getDBOverView({
// ...props,
// }),
// getExternalAverageDuration({
// ...props,
// }),
// getExternalError({
// ...props,
// }),
// getExternalService({
// ...props,
// }),
getServiceOverview({
end: maxTime,
service: props.serviceName,
start: minTime,
step: getStep({ start: minTime, end: maxTime, inputFormat: 'ns' }),
selectedTags: props.selectedTags,
}),
getTopOperations({
end: maxTime,
service: props.serviceName,
start: minTime,
selectedTags: props.selectedTags,
}),
getTopLevelOperations({
service: props.serviceName,
}),
]);
if (
// getDBOverViewResponse.statusCode === 200 &&
// getExternalAverageDurationResponse.statusCode === 200 &&
// getExternalErrorResponse.statusCode === 200 &&
// getExternalServiceResponse.statusCode === 200 &&
getServiceOverviewResponse.statusCode === 200 &&
getTopOperationsResponse.statusCode === 200 &&
getTopLevelOperationsResponse.statusCode === 200
) {
dispatch({
type: 'GET_INTIAL_APPLICATION_DATA',
payload: {
// dbOverView: getDBOverViewResponse.payload,
// externalAverageDuration: getExternalAverageDurationResponse.payload,
// externalError: getExternalErrorResponse.payload,
// externalService: getExternalServiceResponse.payload,
serviceOverview: getServiceOverviewResponse.payload,
topOperations: getTopOperationsResponse.payload,
topLevelOperations: getTopLevelOperationsResponse.payload,
},
});
} else {
dispatch({
type: 'GET_INITIAL_APPLICATION_ERROR',
payload: {
errorMessage:
getTopOperationsResponse.error ||
getServiceOverviewResponse.error ||
getTopLevelOperationsResponse.error ||
// getExternalServiceResponse.error ||
// getExternalErrorResponse.error ||
// getExternalAverageDurationResponse.error ||
// getDBOverViewResponse.error ||
'Something went wrong',
},
});
}
} catch (error) {
dispatch({
type: 'GET_INITIAL_APPLICATION_ERROR',
payload: {
errorMessage: (error as AxiosError).toString() || 'Something went wrong',
},
});
}
};
export interface GetInitialDataProps {
serviceName: Props['service'];
maxTime: GlobalReducer['maxTime'];
minTime: GlobalReducer['minTime'];
selectedTags: Tags[];
}

View File

@ -12,7 +12,7 @@ import {
} from 'types/actions/logs'; } from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
const intitalState: GlobalReducer = { const initialState: GlobalReducer = {
maxTime: Date.now() * 1000000, maxTime: Date.now() * 1000000,
minTime: (Date.now() - 15 * 60 * 1000) * 1000000, minTime: (Date.now() - 15 * 60 * 1000) * 1000000,
loading: true, loading: true,
@ -24,7 +24,7 @@ const intitalState: GlobalReducer = {
}; };
const globalTimeReducer = ( const globalTimeReducer = (
state = intitalState, state = initialState,
action: GlobalTimeAction, action: GlobalTimeAction,
): GlobalReducer => { ): GlobalReducer => {
switch (action.type) { switch (action.type) {