diff --git a/Common/Server/API/BaseAnalyticsAPI.ts b/Common/Server/API/BaseAnalyticsAPI.ts index 2f7e81d66aa..eba73a4a740 100644 --- a/Common/Server/API/BaseAnalyticsAPI.ts +++ b/Common/Server/API/BaseAnalyticsAPI.ts @@ -291,15 +291,14 @@ export default class BaseAnalyticsAPI< ) as any; } - let groupBy: GroupBy | null = req.body["groupBy"] || null; + let groupBy: GroupBy | null = + req.body["groupBy"] || null; - if(groupBy && Object.keys(groupBy).length > 0) { - groupBy = JSONFunctions.deserialize( - groupBy as JSONObject, - ) as any; + if (groupBy && Object.keys(groupBy).length > 0) { + groupBy = JSONFunctions.deserialize(groupBy as JSONObject) as any; } - if(groupBy && Object.keys(groupBy).length === 0) { + if (groupBy && Object.keys(groupBy).length === 0) { groupBy = null; } diff --git a/Common/Server/Services/AnalyticsDatabaseService.ts b/Common/Server/Services/AnalyticsDatabaseService.ts index 19065bef9fa..9f0e38d8721 100644 --- a/Common/Server/Services/AnalyticsDatabaseService.ts +++ b/Common/Server/Services/AnalyticsDatabaseService.ts @@ -57,14 +57,14 @@ import ModelEventType from "../../Types/Realtime/ModelEventType"; export default class AnalyticsDatabaseService< TBaseModel extends AnalyticsBaseModel, > extends BaseService { - public modelType!: { new(): TBaseModel }; + public modelType!: { new (): TBaseModel }; public database!: ClickhouseDatabase; public model!: TBaseModel; public databaseClient!: ClickhouseClient; public statementGenerator!: StatementGenerator; public constructor(data: { - modelType: { new(): TBaseModel }; + modelType: { new (): TBaseModel }; database?: ClickhouseDatabase | undefined; }) { super(); @@ -240,8 +240,6 @@ export default class AnalyticsDatabaseService< columns: Array; } = this.toAggregateStatement(aggregateBy); - debugger; - const dbResult: ExecResult = await this.execute( findStatement.statement, ); @@ -261,22 +259,24 @@ export default class AnalyticsDatabaseService< // convert date column from string to date. - const groupByColumnName: keyof TBaseModel | undefined = aggregateBy.groupBy && Object.keys(aggregateBy.groupBy).length > 0 ? Object.keys(aggregateBy.groupBy)[0] as keyof TBaseModel : undefined; + const groupByColumnName: keyof TBaseModel | undefined = + aggregateBy.groupBy && Object.keys(aggregateBy.groupBy).length > 0 + ? (Object.keys(aggregateBy.groupBy)[0] as keyof TBaseModel) + : undefined; for (const item of items) { if ( !(item as JSONObject)[ - aggregateBy.aggregationTimestampColumnName as string + aggregateBy.aggregationTimestampColumnName as string ] ) { continue; } - const aggregatedModel: AggregatedModel = { timestamp: OneUptimeDate.fromString( (item as JSONObject)[ - aggregateBy.aggregationTimestampColumnName as string + aggregateBy.aggregationTimestampColumnName as string ] as string, ), value: (item as JSONObject)[ diff --git a/Common/Server/Utils/AnalyticsDatabase/StatementGenerator.ts b/Common/Server/Utils/AnalyticsDatabase/StatementGenerator.ts index 1fb2ae8c9b1..15fb12b6d8d 100644 --- a/Common/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +++ b/Common/Server/Utils/AnalyticsDatabase/StatementGenerator.ts @@ -548,17 +548,18 @@ export default class StatementGenerator { `${aggregationMethod}(${aggregateBy.aggregateColumnName.toString()}) as ${aggregateBy.aggregateColumnName.toString()}, date_trunc('${aggregationInterval.toLowerCase()}', toStartOfInterval(${aggregateBy.aggregationTimestampColumnName.toString()}, INTERVAL 1 ${aggregationInterval.toLowerCase()})) as ${aggregateBy.aggregationTimestampColumnName.toString()}`, ); - const columns: Array = [ aggregateBy.aggregateColumnName.toString(), aggregateBy.aggregationTimestampColumnName.toString(), ]; - if(aggregateBy.groupBy && Object.keys(aggregateBy.groupBy).length > 0) { - const groupByStatement: Statement = this.toGroupByStatement(aggregateBy.groupBy); + if (aggregateBy.groupBy && Object.keys(aggregateBy.groupBy).length > 0) { + const groupByStatement: Statement = this.toGroupByStatement( + aggregateBy.groupBy, + ); selectStatement.append(SQL`, `).append(groupByStatement); - // add to columns. + // add to columns. for (const key in aggregateBy.groupBy) { columns.push(key); } diff --git a/Common/Types/Metrics/MetricsQuery.ts b/Common/Types/Metrics/MetricsQuery.ts index 1a4d4c0ff79..b9a0f865758 100644 --- a/Common/Types/Metrics/MetricsQuery.ts +++ b/Common/Types/Metrics/MetricsQuery.ts @@ -7,12 +7,11 @@ export default interface MetricsQuery { aggegationType: MetricsAggregationType; aggregateBy: Dictionary; - - // This is used for example for probes. - // To display US probe and EU probe in chart for example. + // This is used for example for probes. + // To display US probe and EU probe in chart for example. // In this case groupByAttribute is "probeId" // and attributeValueToLegendMap is { "xx-xx-xx-xx": "US Probe", "yy-yyy-yyy-yy-yy": "EU Probe" } - + groupByAttribute?: string | undefined; attributeValueToLegendMap?: Dictionary; } diff --git a/Common/UI/Components/Charts/ChartGroup/ChartGroup.tsx b/Common/UI/Components/Charts/ChartGroup/ChartGroup.tsx index 1f7dcd63693..60defa14cf0 100644 --- a/Common/UI/Components/Charts/ChartGroup/ChartGroup.tsx +++ b/Common/UI/Components/Charts/ChartGroup/ChartGroup.tsx @@ -47,7 +47,7 @@ const ChartGroup: FunctionComponent = ( {chart.description}

)} - + ); default: diff --git a/Common/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx b/Common/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx index 434d481a32e..8fe14d79188 100644 --- a/Common/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +++ b/Common/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx @@ -571,7 +571,7 @@ interface LineChartProps extends React.HTMLAttributes { legendPosition?: "left" | "center" | "right"; tooltipCallback?: (tooltipCallbackContent: TooltipProps) => void; customTooltip?: React.ComponentType; - syncId?: string | undefined; + syncid?: string | undefined; } const LineChart: React.ForwardRefExoticComponent< @@ -692,7 +692,7 @@ const LineChart: React.ForwardRefExoticComponent< { diff --git a/Common/UI/Components/Charts/Line/LineChart.tsx b/Common/UI/Components/Charts/Line/LineChart.tsx index ac0d5f4e911..f301e39ade8 100644 --- a/Common/UI/Components/Charts/Line/LineChart.tsx +++ b/Common/UI/Components/Charts/Line/LineChart.tsx @@ -16,7 +16,7 @@ export interface ComponentProps { } export interface LineInternalProps extends ComponentProps { - syncId: string; + syncid: string; } const LineChartElement: FunctionComponent = ( @@ -64,7 +64,7 @@ const LineChartElement: FunctionComponent = ( showTooltip={true} connectNulls={true} curve={props.curve} - syncId={props.sync ? props.syncId : undefined} + syncid={props.sync ? props.syncid : undefined} yAxisWidth={60} /> ); diff --git a/Dashboard/src/Components/Metrics/MetricQuery.tsx b/Dashboard/src/Components/Metrics/MetricQuery.tsx index d4f7704eebc..3024b9af6ad 100644 --- a/Dashboard/src/Components/Metrics/MetricQuery.tsx +++ b/Dashboard/src/Components/Metrics/MetricQuery.tsx @@ -12,7 +12,7 @@ import Metric from "Common/Models/AnalyticsModels/Metric"; export interface MetricQueryData { filterData: FilterData; - groupBy?: GroupBy | undefined; + groupBy?: GroupBy | undefined; } export interface ComponentProps { diff --git a/Dashboard/src/Components/Metrics/MetricQueryConfig.tsx b/Dashboard/src/Components/Metrics/MetricQueryConfig.tsx index ecdf1e6cc50..ec1290f161a 100644 --- a/Dashboard/src/Components/Metrics/MetricQueryConfig.tsx +++ b/Dashboard/src/Components/Metrics/MetricQueryConfig.tsx @@ -10,7 +10,7 @@ import Button, { import MetricNameAndUnit from "./Types/MetricNameAndUnit"; import AggregatedModel from "Common/Types/BaseDatabase/AggregatedModel"; -export interface ChartSeries { +export interface ChartSeries { title: string; } diff --git a/Dashboard/src/Components/Metrics/MetricView.tsx b/Dashboard/src/Components/Metrics/MetricView.tsx index fa6c84e128b..362e4636c67 100644 --- a/Dashboard/src/Components/Metrics/MetricView.tsx +++ b/Dashboard/src/Components/Metrics/MetricView.tsx @@ -5,7 +5,10 @@ import React, { useEffect, useState, } from "react"; -import MetricQueryConfig, { ChartSeries, MetricQueryConfigData } from "./MetricQueryConfig"; +import MetricQueryConfig, { + ChartSeries, + MetricQueryConfigData, +} from "./MetricQueryConfig"; import MetricGraphConfig, { MetricFormulaConfigData, } from "./MetricFormulaConfig"; @@ -199,12 +202,9 @@ const MetricView: FunctionComponent = ( xAxisAggregationType = XAxisAggregateType.Average; } - let chartSeries: Array = [ - - ]; + const chartSeries: Array = []; if (queryConfig.getSeries) { - for (const item of metricResults[index]!.data) { const series: ChartSeries = queryConfig.getSeries(item); const seriesName: string = series.title; @@ -213,27 +213,30 @@ const MetricView: FunctionComponent = ( // if it does not exist then create a new series and add the data to it - const existingSeries: SeriesPoint | undefined = chartSeries.find((s: SeriesPoint) => { - return s.seriesName === seriesName; - }); + const existingSeries: SeriesPoint | undefined = chartSeries.find( + (s: SeriesPoint) => { + return s.seriesName === seriesName; + }, + ); if (existingSeries) { existingSeries.data.push({ x: OneUptimeDate.fromString(item.timestamp), - y: item.value + y: item.value, }); } else { const newSeries: SeriesPoint = { seriesName: seriesName, - data: [{ - x: OneUptimeDate.fromString(item.timestamp), - y: item.value - }] + data: [ + { + x: OneUptimeDate.fromString(item.timestamp), + y: item.value, + }, + ], }; chartSeries.push(newSeries); } - } } else { chartSeries.push({ @@ -241,14 +244,12 @@ const MetricView: FunctionComponent = ( queryConfig.metricAliasData.title || queryConfig.metricQueryData.filterData.metricName?.toString() || "", - data: metricResults[index]!.data.map( - (result: AggregatedModel) => { - return { - x: OneUptimeDate.fromString(result.timestamp), - y: result.value, - }; - }, - ), + data: metricResults[index]!.data.map((result: AggregatedModel) => { + return { + x: OneUptimeDate.fromString(result.timestamp), + y: result.value, + }; + }), }); } @@ -365,14 +366,14 @@ const MetricView: FunctionComponent = ( const metricAttributesResponse: | HTTPResponse | HTTPErrorResponse = await API.post( - URL.fromString(APP_API_URL.toString()).addRoute( - "/telemetry/metrics/get-attributes", - ), - {}, - { - ...ModelAPI.getCommonHeaders(), - }, - ); + URL.fromString(APP_API_URL.toString()).addRoute( + "/telemetry/metrics/get-attributes", + ), + {}, + { + ...ModelAPI.getCommonHeaders(), + }, + ); if (metricAttributesResponse instanceof HTTPErrorResponse) { throw metricAttributesResponse; @@ -421,7 +422,7 @@ const MetricView: FunctionComponent = ( OneUptimeDate.getCurrentDate(), limit: LIMIT_PER_PROJECT, skip: 0, - groupBy: queryConfig.metricQueryData.groupBy + groupBy: queryConfig.metricQueryData.groupBy, }, }); diff --git a/Dashboard/src/Components/Monitor/MonitorMetrics.tsx b/Dashboard/src/Components/Monitor/MonitorMetrics.tsx index 20e4ef96631..ee513090f17 100644 --- a/Dashboard/src/Components/Monitor/MonitorMetrics.tsx +++ b/Dashboard/src/Components/Monitor/MonitorMetrics.tsx @@ -9,16 +9,26 @@ import MonitorMetricTypeUtil from "Common/Utils/Monitor/MonitorMetricType"; import OneUptimeDate from "Common/Types/Date"; import InBetween from "Common/Types/BaseDatabase/InBetween"; import MetricView from "../Metrics/MetricView"; -import { MetricQueryConfigData } from "../Metrics/MetricQueryConfig"; +import { + ChartSeries, + MetricQueryConfigData, +} from "../Metrics/MetricQueryConfig"; import DashboardNavigation from "../../Utils/Navigation"; import MonitorMetricType from "Common/Types/Monitor/MonitorMetricType"; -import MonitorType from "Common/Types/Monitor/MonitorType"; +import MonitorType, { + MonitorTypeHelper, +} from "Common/Types/Monitor/MonitorType"; import API from "Common/UI/Utils/API/API"; import Monitor from "Common/Models/DatabaseModels/Monitor"; import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI"; import PageLoader from "Common/UI/Components/Loader/PageLoader"; import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage"; import { PromiseVoidFunction } from "Common/Types/FunctionTypes"; +import ProbeUtil from "../../Utils/Probe"; +import Probe from "Common/Models/DatabaseModels/Probe"; +import AggregateModel from "Common/Types/BaseDatabase/AggregatedModel"; +import { JSONObject } from "Common/Types/JSON"; +import JSONFunctions from "Common/Types/JSONFunctions"; export interface ComponentProps { monitorId: ObjectID; @@ -35,6 +45,8 @@ const MonitorMetricsElement: FunctionComponent = ( const [error, setError] = useState(""); + const [probes, setProbes] = useState>([]); + const fetchMonitor: PromiseVoidFunction = async (): Promise => { setIsLoading(true); @@ -47,7 +59,16 @@ const MonitorMetricsElement: FunctionComponent = ( }, }); - setMonitorType(item?.monitorType || MonitorType.Manual); + const monitorType: MonitorType = item?.monitorType || MonitorType.Manual; + + setMonitorType(monitorType); + + const isProbeableMonitor: boolean = + MonitorTypeHelper.isProbableMonitor(monitorType); + + if (isProbeableMonitor) { + setProbes(await ProbeUtil.getAllProbes()); + } } catch (err) { setError(API.getFriendlyMessage(err)); } @@ -89,6 +110,10 @@ const MonitorMetricsElement: FunctionComponent = ( (): Array => { const queries: Array = []; + if (!monitorType) { + return []; + } + for (const monitorMetricType of monitorMetricTypesByMonitor) { queries.push({ metricAliasData: { @@ -113,18 +138,134 @@ const MonitorMetricsElement: FunctionComponent = ( MonitorMetricTypeUtil.getAggregationTypeByMonitorMetricType( monitorMetricType, ), - }, groupBy: { - attributes: true - } - + attributes: true, + }, }, - getSeries: (data) => { + getSeries: (data: AggregateModel): ChartSeries => { + const isProbeableMonitor: boolean = + MonitorTypeHelper.isProbableMonitor(monitorType); + + if (!data) { + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + + if (isProbeableMonitor) { + let attributes: JSONObject = data["attributes"] as JSONObject; + + if (!attributes) { + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + + // if attributes is typeof string then parse it to JSON + + if (typeof attributes === "string") { + try { + attributes = JSONFunctions.parseJSONObject(attributes); + } catch (err) { + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + } + + const probeId: ObjectID = new ObjectID( + ((attributes as JSONObject)["probeId"] as string)?.toString(), + ); + + if (!probeId) { + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + + const probe: Probe | undefined = probes.find((probe: Probe) => { + return probe.id?.toString() === probeId.toString(); + }); + + if (probe) { + return { + title: + probe.name?.toString() || + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + + if (monitorType === MonitorType.Server) { + let attributes: JSONObject = data["attributes"] as JSONObject; + + if (!attributes) { + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + + // if attributes is typeof string then parse it to JSON + + if (typeof attributes === "string") { + try { + attributes = JSONFunctions.parseJSONObject(attributes); + } catch (err) { + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + } + + if (attributes["diskPath"]) { + return { + title: attributes["diskPath"].toString(), + }; + } + + return { + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), + }; + } + return { - title: data.attributes.monitorId, + title: + MonitorMetricTypeUtil.getTitleByMonitorMetricType( + monitorMetricType, + ), }; - } + }, }); }