diff --git a/frontend/src/constants/env.ts b/frontend/src/constants/env.ts index 96256e7f4c..971e21c877 100644 --- a/frontend/src/constants/env.ts +++ b/frontend/src/constants/env.ts @@ -1,3 +1,3 @@ export const ENVIRONMENT = { - baseURL: "", + baseURL: "http://ec2-3-135-219-130.us-east-2.compute.amazonaws.com:8080", }; diff --git a/frontend/src/modules/Metrics/ExternalApi/ExternalApiGraph.tsx b/frontend/src/modules/Metrics/ExternalApi/ExternalApiGraph.tsx new file mode 100644 index 0000000000..b26aa5783d --- /dev/null +++ b/frontend/src/modules/Metrics/ExternalApi/ExternalApiGraph.tsx @@ -0,0 +1,97 @@ +import React from "react"; +import { Line as ChartJSLine } from "react-chartjs-2"; +import { ChartOptions } from "chart.js"; +import { withRouter } from "react-router"; +import { RouteComponentProps } from "react-router-dom"; +import styled from "styled-components"; +import { getOptions, borderColors } from "./graphConfig"; +import { externalMetricsItem } from "../../store/actions/metrics"; +import { uniqBy, filter } from "lodash"; + +const theme = "dark"; + +interface ExternalApiGraphProps extends RouteComponentProps { + data: externalMetricsItem[]; + keyIdentifier: string; + title: string; + dataIdentifier: string; + fnDataIdentifier?: (s: number | string) => number | string; +} + +interface ExternalApiGraph { + chartRef: any; +} + +class ExternalApiGraph extends React.Component { + constructor(props: ExternalApiGraphProps) { + super(props); + this.chartRef = React.createRef(); + } + + state = { + xcoordinate: 0, + ycoordinate: 0, + showpopUp: false, + firstpoint_ts: 0, + // graphInfo:{} + }; + + render() { + const { + title, + data, + dataIdentifier, + keyIdentifier, + fnDataIdentifier, + } = this.props; + const getDataSets = () => { + const uniq = uniqBy(data, keyIdentifier); + return uniq.map((obj: externalMetricsItem, i: number) => { + const _data = filter( + data, + (s: externalMetricsItem) => s[keyIdentifier] === obj[keyIdentifier], + ); + return { + label: obj[keyIdentifier], + data: _data.map((s: externalMetricsItem) => + fnDataIdentifier + ? fnDataIdentifier(s[dataIdentifier]) + : s[dataIdentifier], + ), + pointRadius: 0.5, + borderColor: borderColors[i] || borderColors[0], // Can also add transparency in border color + borderWidth: 2, + }; + }); + }; + const data_chartJS = (canvas: any) => { + const ctx = canvas.getContext("2d"); + const gradient = ctx.createLinearGradient(0, 0, 0, 100); + gradient.addColorStop(0, "rgba(250,174,50,1)"); + gradient.addColorStop(1, "rgba(250,174,50,1)"); + const uniqTimestamp = uniqBy(data, "timestamp"); + + return { + labels: uniqTimestamp.map( + (s: externalMetricsItem) => new Date(s.timestamp / 1000000), + ), + datasets: getDataSets(), + }; + }; + + return ( +
+
+
{title}
+ +
+
+ ); + } +} + +export default withRouter(ExternalApiGraph); diff --git a/frontend/src/modules/Metrics/ExternalApi/graphConfig.ts b/frontend/src/modules/Metrics/ExternalApi/graphConfig.ts new file mode 100644 index 0000000000..d04ef3d0cb --- /dev/null +++ b/frontend/src/modules/Metrics/ExternalApi/graphConfig.ts @@ -0,0 +1,97 @@ +import { ChartOptions } from "chart.js"; + +export const getOptions = (theme: string): ChartOptions => { + return { + maintainAspectRatio: true, + responsive: true, + + title: { + display: true, + text: "", + fontSize: 20, + position: "top", + padding: 8, + fontFamily: "Arial", + fontStyle: "regular", + fontColor: theme === "dark" ? "rgb(200, 200, 200)" : "rgb(20, 20, 20)", + }, + + legend: { + display: true, + position: "bottom", + align: "center", + + labels: { + fontColor: theme === "dark" ? "rgb(200, 200, 200)" : "rgb(20, 20, 20)", + fontSize: 10, + boxWidth: 10, + usePointStyle: true, + }, + }, + + tooltips: { + mode: "label", + bodyFontSize: 12, + titleFontSize: 12, + + callbacks: { + label: function (tooltipItem, data) { + if (typeof tooltipItem.yLabel === "number") { + return ( + data.datasets![tooltipItem.datasetIndex!].label + + " : " + + tooltipItem.yLabel.toFixed(2) + ); + } else { + return ""; + } + }, + }, + }, + + scales: { + yAxes: [ + { + stacked: false, + ticks: { + beginAtZero: false, + fontSize: 10, + autoSkip: true, + maxTicksLimit: 6, + }, + + gridLines: { + // You can change the color, the dash effect, the main axe color, etc. + borderDash: [1, 4], + color: "#D3D3D3", + lineWidth: 0.25, + }, + }, + ], + xAxes: [ + { + type: "time", + // time: { + // unit: 'second' + // }, + distribution: "linear", + //'linear': data are spread according to their time (distances can vary) + // From https://www.chartjs.org/docs/latest/axes/cartesian/time.html + ticks: { + beginAtZero: false, + fontSize: 10, + autoSkip: true, + maxTicksLimit: 10, + }, + // gridLines: false, --> not a valid option + }, + ], + }, + }; +}; + +export const borderColors = [ + "rgba(250,174,50,1)", + "rgba(227, 74, 51, 1.0)", + "rgba(57, 255, 20, 1.0)", +]; diff --git a/frontend/src/modules/Metrics/ExternalApi/index.tsx b/frontend/src/modules/Metrics/ExternalApi/index.tsx new file mode 100644 index 0000000000..7333a56db0 --- /dev/null +++ b/frontend/src/modules/Metrics/ExternalApi/index.tsx @@ -0,0 +1 @@ +export { default } from "./ExternalApiGraph"; diff --git a/frontend/src/modules/Metrics/ServiceMetrics.tsx b/frontend/src/modules/Metrics/ServiceMetrics.tsx index 51dee6cf23..87dbfa83fe 100644 --- a/frontend/src/modules/Metrics/ServiceMetrics.tsx +++ b/frontend/src/modules/Metrics/ServiceMetrics.tsx @@ -8,22 +8,26 @@ import { getServicesMetrics, metricItem, getTopEndpoints, + getExternalMetrics, + externalMetricsItem, topEndpointListItem, GlobalTime, updateTimeInterval, -} from "../../store/actions"; +} from "Src/store/actions"; import { StoreState } from "../../store/reducers"; import LatencyLineChart from "./LatencyLineChart"; import RequestRateChart from "./RequestRateChart"; import ErrorRateChart from "./ErrorRateChart"; import TopEndpointsTable from "./TopEndpointsTable"; import { METRICS_PAGE_QUERY_PARAM } from "Src/constants/query"; - +import ExternalApiGraph from "./ExternalApi"; const { TabPane } = Tabs; interface ServicesMetricsProps extends RouteComponentProps { serviceMetrics: metricItem[]; getServicesMetrics: Function; + getExternalMetrics: Function;g + externalMetrics: externalMetricsItem[]; topEndpointsList: topEndpointListItem[]; getTopEndpoints: Function; globalTime: GlobalTime; @@ -35,6 +39,7 @@ const _ServiceMetrics = (props: ServicesMetricsProps) => { useEffect(() => { props.getServicesMetrics(servicename, props.globalTime); props.getTopEndpoints(servicename, props.globalTime); + props.getExternalMetrics(servicename, props.globalTime); }, [props.globalTime, servicename]); const onTracePopupClick = (timestamp: number) => { @@ -89,17 +94,31 @@ const _ServiceMetrics = (props: ServicesMetricsProps) => { -
Coming Soon..
- {/* */} + + +
- {/* */} + + Number(s) / 1000000} + data={props.externalMetrics} + /> +
@@ -113,11 +132,13 @@ const mapStateToProps = ( ): { serviceMetrics: metricItem[]; topEndpointsList: topEndpointListItem[]; + externalMetrics: externalMetricsItem[]; globalTime: GlobalTime; } => { return { serviceMetrics: state.serviceMetrics, topEndpointsList: state.topEndpointsList, + externalMetrics: state.externalMetrics, globalTime: state.globalTime, }; }; @@ -125,6 +146,7 @@ const mapStateToProps = ( export const ServiceMetrics = withRouter( connect(mapStateToProps, { getServicesMetrics: getServicesMetrics, + getExternalMetrics: getExternalMetrics, getTopEndpoints: getTopEndpoints, updateTimeInterval: updateTimeInterval, })(_ServiceMetrics), diff --git a/frontend/src/store/actions/metrics.ts b/frontend/src/store/actions/metrics.ts index 68bc0edc72..2ee1e29166 100644 --- a/frontend/src/store/actions/metrics.ts +++ b/frontend/src/store/actions/metrics.ts @@ -35,6 +35,14 @@ export interface topEndpointListItem { name: string; } +export interface externalMetricsItem { + avgDuration: number; + callRate: number; + externalHttpUrl: string; + numCalls: number; + timestamp: number; +} + export interface customMetricsItem { timestamp: number; value: number; @@ -49,6 +57,10 @@ export interface getServiceMetricsAction { type: ActionTypes.getServiceMetrics; payload: metricItem[]; } +export interface getExternalMetricsAction { + type: ActionTypes.getExternalMetrics; + payload: externalMetricsItem[]; +} export interface getTopEndpointsAction { type: ActionTypes.getTopEndpoints; @@ -75,6 +87,28 @@ export const getServicesList = (globalTime: GlobalTime) => { }; }; +export const getExternalMetrics = ( + serviceName: string, + globalTime: GlobalTime, +) => { + return async (dispatch: Dispatch) => { + let request_string = + "/service/external?service=" + + serviceName + + "&start=" + + globalTime.minTime + + "&end=" + + globalTime.maxTime + + "&step=60"; + const response = await api.get(apiV1 + request_string); + console.log("response", response); + dispatch({ + type: ActionTypes.getExternalMetrics, + payload: response.data, + }); + }; +}; + export const getServicesMetrics = ( serviceName: string, globalTime: GlobalTime, diff --git a/frontend/src/store/actions/types.ts b/frontend/src/store/actions/types.ts index 12a1ca15f1..60c76da551 100644 --- a/frontend/src/store/actions/types.ts +++ b/frontend/src/store/actions/types.ts @@ -3,6 +3,7 @@ import { updateTraceFiltersAction, updateInputTagAction } from "./traceFilters"; import { getServicesListAction, getServiceMetricsAction, + getExternalMetricsAction, getTopEndpointsAction, getFilteredTraceMetricsAction, } from "./metrics"; @@ -16,6 +17,7 @@ export enum ActionTypes { fetchTraceItem = "FETCH_TRACE_ITEM", getServicesList = "GET_SERVICE_LIST", getServiceMetrics = "GET_SERVICE_METRICS", + getExternalMetrics = "GET_EXTERNAL_METRICS", getTopEndpoints = "GET_TOP_ENDPOINTS", getUsageData = "GET_USAGE_DATE", updateTimeInterval = "UPDATE_TIME_INTERVAL", @@ -32,4 +34,5 @@ export type Action = | getTopEndpointsAction | getUsageDataAction | updateTimeIntervalAction - | getFilteredTraceMetricsAction; + | getFilteredTraceMetricsAction + | getExternalMetricsAction; diff --git a/frontend/src/store/reducers/index.ts b/frontend/src/store/reducers/index.ts index 565b8c1d7f..7ddd4bd29c 100644 --- a/frontend/src/store/reducers/index.ts +++ b/frontend/src/store/reducers/index.ts @@ -5,6 +5,7 @@ import { servicesListItem, metricItem, topEndpointListItem, + externalMetricsItem, usageDataItem, GlobalTime, customMetricsItem, @@ -16,6 +17,7 @@ import { serviceMetricsReducer, serviceTableReducer, topEndpointsReducer, + externalMetricsReducer, } from "./metrics"; import { traceFiltersReducer, inputsReducer } from "./traceFilters"; import { traceItemReducer, tracesReducer } from "./traces"; @@ -29,6 +31,7 @@ export interface StoreState { servicesList: servicesListItem[]; serviceMetrics: metricItem[]; topEndpointsList: topEndpointListItem[]; + externalMetrics: externalMetricsItem[]; usageDate: usageDataItem[]; globalTime: GlobalTime; filteredTraceMetrics: customMetricsItem[]; @@ -42,6 +45,7 @@ const reducers = combineReducers({ servicesList: serviceTableReducer, serviceMetrics: serviceMetricsReducer, topEndpointsList: topEndpointsReducer, + externalMetrics: externalMetricsReducer, usageDate: usageDataReducer, globalTime: updateGlobalTimeReducer, filteredTraceMetrics: filteredTraceMetricsReducer, diff --git a/frontend/src/store/reducers/metrics.ts b/frontend/src/store/reducers/metrics.ts index 1bf0be0c53..2926a3d8c6 100644 --- a/frontend/src/store/reducers/metrics.ts +++ b/frontend/src/store/reducers/metrics.ts @@ -5,6 +5,7 @@ import { metricItem, topEndpointListItem, customMetricsItem, + externalMetricsItem, } from "../actions"; export const serviceTableReducer = ( @@ -66,6 +67,26 @@ export const topEndpointsReducer = ( } }; +export const externalMetricsReducer = ( + state: externalMetricsItem[] = [ + { + avgDuration: 0, + callRate: 0, + externalHttpUrl: "", + numCalls: 0, + timestamp: 0, + }, + ], + action: Action, +) => { + switch (action.type) { + case ActionTypes.getExternalMetrics: + return action.payload; + default: + return state; + } +}; + export const filteredTraceMetricsReducer = ( state: customMetricsItem[] = [{ timestamp: 0, value: 0 }], action: Action,