mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Add start and end date handling to Dashboard components and implement metric result fetching
This commit is contained in:
@@ -86,6 +86,7 @@ const ArgumentsForm: FunctionComponent<ComponentProps> = (
|
||||
data={value[arg.id] as MetricQueryConfigData}
|
||||
metricNameAndUnits={props.metrics.metricNameAndUnits}
|
||||
telemetryAttributes={props.metrics.telemetryAttributes}
|
||||
hideCard={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,6 +18,8 @@ import { GetReactElementFunction } from "Common/UI/Types/FunctionTypes";
|
||||
import DashboardViewConfig from "Common/Types/Dashboard/DashboardViewConfig";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import DashboardComponentType from "Common/Types/Dashboard/DashboardComponentType";
|
||||
import DashboardStartAndEndDate from "../Types/DashboardStartAndEndDate";
|
||||
import MetricNameAndUnit from "../../Metrics/Types/MetricNameAndUnit";
|
||||
|
||||
export interface DashboardBaseComponentProps {
|
||||
componentId: ObjectID;
|
||||
@@ -31,6 +33,8 @@ export interface DashboardBaseComponentProps {
|
||||
dashboardCanvasWidthInPx: number;
|
||||
dashboardCanvasHeightInPx: number;
|
||||
dashboardViewConfig: DashboardViewConfig;
|
||||
dashboardStartAndEndDate: DashboardStartAndEndDate;
|
||||
metricNameAndUnits: Array<MetricNameAndUnit>;
|
||||
}
|
||||
|
||||
export interface ComponentProps extends DashboardBaseComponentProps {
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
import DashboardChartComponent from "Common/Types/Dashboard/DashboardComponents/DashboardChartComponent";
|
||||
import { DashboardBaseComponentProps } from "./DashboardBaseComponent";
|
||||
import MetricCharts from "../../Metrics/MetricCharts";
|
||||
import AggregatedResult from "Common/Types/BaseDatabase/AggregatedResult";
|
||||
import { DashboardStartAndEndDateUtil } from "../Types/DashboardStartAndEndDate";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import MetricViewData from "../../Metrics/Types/MetricViewData";
|
||||
import MetricUtil from "../../Metrics/Utils/Metrics";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
|
||||
export interface ComponentProps extends DashboardBaseComponentProps {
|
||||
component: DashboardChartComponent;
|
||||
@@ -9,7 +18,65 @@ export interface ComponentProps extends DashboardBaseComponentProps {
|
||||
const DashboardChartComponentElement: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
return <div>Chart Component {props.component.componentId.toString()}</div>;
|
||||
|
||||
const [metricResults, setMetricResults] = React.useState<Array<AggregatedResult>>([]);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||
|
||||
const metricViewData: MetricViewData = {
|
||||
queryConfigs: props.component.arguments.metricQueryConfig ? [props.component.arguments.metricQueryConfig] : [],
|
||||
startAndEndDate: DashboardStartAndEndDateUtil.getStartAndEndDate(props.dashboardStartAndEndDate),
|
||||
formulaConfigs: []
|
||||
}
|
||||
|
||||
const fetchAggregatedResults: PromiseVoidFunction =
|
||||
async (): Promise<void> => {
|
||||
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
if (
|
||||
!metricViewData.startAndEndDate?.startValue ||
|
||||
!metricViewData.startAndEndDate?.endValue
|
||||
) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const results: Array<AggregatedResult> = await MetricUtil.fetchResults({
|
||||
metricViewData: metricViewData,
|
||||
});
|
||||
|
||||
setMetricResults(results);
|
||||
setError("");
|
||||
} catch (err: unknown) {
|
||||
setError(API.getFriendlyErrorMessage(err as Error));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchAggregatedResults();
|
||||
}, [props.dashboardStartAndEndDate, props.component.arguments.metricQueryConfig, props.metricNameAndUnits]);
|
||||
|
||||
|
||||
|
||||
return <div>
|
||||
<MetricCharts
|
||||
metricResults={metricResults}
|
||||
metricNamesAndUnits={props.metricNameAndUnits}
|
||||
metricViewData={metricViewData}
|
||||
/>
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default DashboardChartComponentElement;
|
||||
|
||||
@@ -1,7 +1,64 @@
|
||||
import InBetween from "Common/Types/BaseDatabase/InBetween";
|
||||
import DashboardStartAndEndDateRange from "./DashboardStartAndEndDateRange";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
|
||||
export default interface DashboardStartAndEndDate {
|
||||
startAndEndDate?: InBetween<Date> | undefined;
|
||||
range: DashboardStartAndEndDateRange;
|
||||
}
|
||||
|
||||
export class DashboardStartAndEndDateUtil {
|
||||
public static getStartAndEndDate(dashboardStartAndEndDate: DashboardStartAndEndDate): InBetween<Date> {
|
||||
const currentDate: Date = OneUptimeDate.getCurrentDate();
|
||||
|
||||
// 30 mins.
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_THIRTY_MINS) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveMinutes(currentDate, -30), currentDate);
|
||||
}
|
||||
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_ONE_HOUR) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveHours(currentDate, -1), currentDate);
|
||||
}
|
||||
|
||||
// two hours.
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_TWO_HOURS) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveHours(currentDate, -2), currentDate);
|
||||
}
|
||||
|
||||
// three hours
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_THREE_HOURS) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveHours(currentDate, -3), currentDate);
|
||||
}
|
||||
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_ONE_DAY) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveDays(currentDate, -1), currentDate);
|
||||
}
|
||||
|
||||
// two days .
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_TWO_DAYS) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveDays(currentDate, -2), currentDate);
|
||||
}
|
||||
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_ONE_WEEK) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveDays(currentDate, -7), currentDate);
|
||||
}
|
||||
|
||||
// two weeks.
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_TWO_WEEKS) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveDays(currentDate, -14), currentDate);
|
||||
}
|
||||
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_ONE_MONTH) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveMonths(currentDate, -1), currentDate);
|
||||
}
|
||||
|
||||
// three months.
|
||||
if(dashboardStartAndEndDate.range === DashboardStartAndEndDateRange.PAST_THREE_MONTHS) {
|
||||
return new InBetween<Date>(OneUptimeDate.addRemoveMonths(currentDate, -3), currentDate);
|
||||
}
|
||||
|
||||
// custom
|
||||
return dashboardStartAndEndDate.startAndEndDate || new InBetween<Date>(currentDate, currentDate);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export interface ComponentProps {
|
||||
onFocus?: (() => void) | undefined;
|
||||
onBlur?: (() => void) | undefined;
|
||||
tabIndex?: number | undefined;
|
||||
hideCard?: boolean | undefined;
|
||||
}
|
||||
|
||||
const MetricGraphConfig: FunctionComponent<ComponentProps> = (
|
||||
@@ -35,53 +36,64 @@ const MetricGraphConfig: FunctionComponent<ComponentProps> = (
|
||||
throw new BadDataException("MetricQueryData is required");
|
||||
}
|
||||
|
||||
|
||||
const getContent = (): ReactElement => {
|
||||
return (
|
||||
<div>{props.data.metricAliasData && (
|
||||
<MetricAlias
|
||||
data={props.data.metricAliasData}
|
||||
onDataChanged={(data: MetricAliasData) => {
|
||||
props.onBlur?.();
|
||||
props.onFocus?.();
|
||||
props.onChange &&
|
||||
props.onChange({ ...props.data, metricAliasData: data });
|
||||
}}
|
||||
isFormula={false}
|
||||
/>
|
||||
)}
|
||||
{props.data.metricQueryData && (
|
||||
<MetricQuery
|
||||
data={props.data.metricQueryData}
|
||||
onDataChanged={(data: MetricQueryData) => {
|
||||
props.onBlur?.();
|
||||
props.onFocus?.();
|
||||
props.onChange &&
|
||||
props.onChange({ ...props.data, metricQueryData: data });
|
||||
}}
|
||||
metricNameAndUnits={props.metricNameAndUnits}
|
||||
telemetryAttributes={props.telemetryAttributes}
|
||||
/>
|
||||
)}
|
||||
{props.onRemove && (
|
||||
<div className="-ml-3">
|
||||
<Button
|
||||
title={"Remove"}
|
||||
onClick={() => {
|
||||
props.onBlur?.();
|
||||
props.onFocus?.();
|
||||
return props.onRemove && props.onRemove();
|
||||
}}
|
||||
buttonSize={ButtonSize.Small}
|
||||
buttonStyle={ButtonStyleType.DANGER_OUTLINE}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.error && (
|
||||
<p data-testid="error-message" className="mt-1 text-sm text-red-400">
|
||||
{props.error}
|
||||
</p>
|
||||
)}
|
||||
</div>);
|
||||
}
|
||||
|
||||
if(props.hideCard) {
|
||||
return getContent();
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="-mt-5" tabIndex={props.tabIndex}>
|
||||
{props.data.metricAliasData && (
|
||||
<MetricAlias
|
||||
data={props.data.metricAliasData}
|
||||
onDataChanged={(data: MetricAliasData) => {
|
||||
props.onBlur?.();
|
||||
props.onFocus?.();
|
||||
props.onChange &&
|
||||
props.onChange({ ...props.data, metricAliasData: data });
|
||||
}}
|
||||
isFormula={false}
|
||||
/>
|
||||
)}
|
||||
{props.data.metricQueryData && (
|
||||
<MetricQuery
|
||||
data={props.data.metricQueryData}
|
||||
onDataChanged={(data: MetricQueryData) => {
|
||||
props.onBlur?.();
|
||||
props.onFocus?.();
|
||||
props.onChange &&
|
||||
props.onChange({ ...props.data, metricQueryData: data });
|
||||
}}
|
||||
metricNameAndUnits={props.metricNameAndUnits}
|
||||
telemetryAttributes={props.telemetryAttributes}
|
||||
/>
|
||||
)}
|
||||
{props.onRemove && (
|
||||
<div className="-ml-3">
|
||||
<Button
|
||||
title={"Remove"}
|
||||
onClick={() => {
|
||||
props.onBlur?.();
|
||||
props.onFocus?.();
|
||||
return props.onRemove && props.onRemove();
|
||||
}}
|
||||
buttonSize={ButtonSize.Small}
|
||||
buttonStyle={ButtonStyleType.DANGER_OUTLINE}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.error && (
|
||||
<p data-testid="error-message" className="mt-1 text-sm text-red-400">
|
||||
{props.error}
|
||||
</p>
|
||||
)}
|
||||
{getContent()}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -23,18 +23,11 @@ import Card from "Common/UI/Components/Card/Card";
|
||||
import AggregatedResult from "Common/Types/BaseDatabase/AggregatedResult";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ModelAPI from "Common/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
|
||||
import Metric from "Common/Models/AnalyticsModels/Metric";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
|
||||
import ComponentLoader from "Common/UI/Components/ComponentLoader/ComponentLoader";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import AggregatedModel from "Common/Types/BaseDatabase/AggregatedModel";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import MetricNameAndUnit from "./Types/MetricNameAndUnit";
|
||||
|
||||
import MetricQueryConfigData from "Common/Types/Metrics/MetricQueryConfigData";
|
||||
import MetricFormulaConfigData from "Common/Types/Metrics/MetricFormulaConfigData";
|
||||
import MetricUtil from "./Utils/Metrics";
|
||||
@@ -155,49 +148,10 @@ const MetricView: FunctionComponent<ComponentProps> = (
|
||||
setIsMetricResultsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const results: Array<AggregatedResult> = [];
|
||||
try {
|
||||
for (const queryConfig of metricViewData.queryConfigs) {
|
||||
const result: AggregatedResult = await ModelAPI.aggregate({
|
||||
modelType: Metric,
|
||||
aggregateBy: {
|
||||
query: {
|
||||
time: metricViewData.startAndEndDate!,
|
||||
name: queryConfig.metricQueryData.filterData.metricName!,
|
||||
attributes: queryConfig.metricQueryData.filterData
|
||||
.attributes as Dictionary<string | number | boolean>,
|
||||
},
|
||||
aggregationType:
|
||||
(queryConfig.metricQueryData.filterData
|
||||
.aggegationType as MetricsAggregationType) ||
|
||||
MetricsAggregationType.Avg,
|
||||
aggregateColumnName: "value",
|
||||
aggregationTimestampColumnName: "time",
|
||||
startTimestamp:
|
||||
(metricViewData.startAndEndDate?.startValue as Date) ||
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
endTimestamp:
|
||||
(metricViewData.startAndEndDate?.endValue as Date) ||
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
groupBy: queryConfig.metricQueryData.groupBy,
|
||||
},
|
||||
});
|
||||
|
||||
result.data.map((data: AggregatedModel) => {
|
||||
// convert to int from float
|
||||
|
||||
if (data.value) {
|
||||
data.value = Math.round(data.value);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
const results: Array<AggregatedResult> = await MetricUtil.fetchResults({
|
||||
metricViewData: metricViewData,
|
||||
});
|
||||
|
||||
setMetricResults(results);
|
||||
setMetricResultsError("");
|
||||
@@ -208,18 +162,6 @@ const MetricView: FunctionComponent<ComponentProps> = (
|
||||
setIsMetricResultsLoading(false);
|
||||
};
|
||||
|
||||
// type GetEmptyFormulaConfigFunction = () => MetricFormulaConfigData;
|
||||
|
||||
// const getEmptyFormulaConfigData: GetEmptyFormulaConfigFunction =
|
||||
// (): MetricFormulaConfigData => {
|
||||
// return {
|
||||
// metricAliasData: { metricVariable: "", title: "", description: "" },
|
||||
// metricFormulaData: {
|
||||
// metricFormula: "",
|
||||
// },
|
||||
// };
|
||||
// };
|
||||
|
||||
if (isPageLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,67 @@ import { JSONObject } from "Common/Types/JSON";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { APP_API_URL } from "Common/UI/Config";
|
||||
import AggregatedModel from "Common/Types/BaseDatabase/AggregatedModel";
|
||||
import MetricsAggregationType from "Common/Types/Metrics/MetricsAggregationType";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import AggregatedResult from "Common/Types/BaseDatabase/AggregatedResult";
|
||||
import MetricViewData from "../Types/MetricViewData";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
|
||||
export default class MetricUtil {
|
||||
|
||||
|
||||
public static async fetchResults(data: {
|
||||
metricViewData: MetricViewData;
|
||||
}): Promise<Array<AggregatedResult>> {
|
||||
const results: Array<AggregatedResult> = [];
|
||||
|
||||
const metricViewData: MetricViewData = data.metricViewData;
|
||||
|
||||
for (const queryConfig of metricViewData.queryConfigs) {
|
||||
const result: AggregatedResult = await ModelAPI.aggregate({
|
||||
modelType: Metric,
|
||||
aggregateBy: {
|
||||
query: {
|
||||
time: metricViewData.startAndEndDate!,
|
||||
name: queryConfig.metricQueryData.filterData.metricName!,
|
||||
attributes: queryConfig.metricQueryData.filterData
|
||||
.attributes as Dictionary<string | number | boolean>,
|
||||
},
|
||||
aggregationType:
|
||||
(queryConfig.metricQueryData.filterData
|
||||
.aggegationType as MetricsAggregationType) ||
|
||||
MetricsAggregationType.Avg,
|
||||
aggregateColumnName: "value",
|
||||
aggregationTimestampColumnName: "time",
|
||||
startTimestamp:
|
||||
(metricViewData.startAndEndDate?.startValue as Date) ||
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
endTimestamp:
|
||||
(metricViewData.startAndEndDate?.endValue as Date) ||
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
groupBy: queryConfig.metricQueryData.groupBy,
|
||||
},
|
||||
});
|
||||
|
||||
result.data.map((data: AggregatedModel) => {
|
||||
// convert to int from float
|
||||
|
||||
if (data.value) {
|
||||
data.value = Math.round(data.value);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static async loadAllMetricsTypes(): Promise<{
|
||||
metricNamesAndUnits: Array<MetricNameAndUnit>;
|
||||
telemetryAttributes: Array<string>;
|
||||
@@ -63,14 +122,14 @@ export default class MetricUtil {
|
||||
const metricAttributesResponse:
|
||||
| HTTPResponse<JSONObject>
|
||||
| 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(),
|
||||
},
|
||||
);
|
||||
|
||||
let attributes: Array<string> = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user