From 5221c20ee27b2f7190919bd17ae254b01df8956e Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Mon, 5 Aug 2024 15:11:54 -0600 Subject: [PATCH 01/10] refactor: Remove commented out code and unused monitor types in MonitorTypeHelper --- Common/Types/Monitor/MonitorType.ts | 27 ++++++++++++++------------- Common/Types/ObjectID.ts | 5 +++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Common/Types/Monitor/MonitorType.ts b/Common/Types/Monitor/MonitorType.ts index 54b12b061f..242601e029 100644 --- a/Common/Types/Monitor/MonitorType.ts +++ b/Common/Types/Monitor/MonitorType.ts @@ -108,19 +108,20 @@ export class MonitorTypeHelper { monitorType: MonitorType.Logs, title: "Logs", description: "This monitor type lets you monitor logs from any source.", - }, - { - monitorType: MonitorType.Metrics, - title: "Metrics", - description: - "This monitor type lets you monitor metrics from any source.", - }, - { - monitorType: MonitorType.Traces, - title: "Traces", - description: - "This monitor type lets you monitor traces from any source.", - }, + } + // , + // { + // monitorType: MonitorType.Metrics, + // title: "Metrics", + // description: + // "This monitor type lets you monitor metrics from any source.", + // }, + // { + // monitorType: MonitorType.Traces, + // title: "Traces", + // description: + // "This monitor type lets you monitor traces from any source.", + // }, ]; return monitorTypeProps; diff --git a/Common/Types/ObjectID.ts b/Common/Types/ObjectID.ts index 95192d53ee..f70c66d0b4 100644 --- a/Common/Types/ObjectID.ts +++ b/Common/Types/ObjectID.ts @@ -48,6 +48,11 @@ export default class ObjectID extends DatabaseProperty { } public static toJSONArray(ids: Array): Array { + + if(!ids || ids.length === 0) { + return []; + } + return ids.map((id: ObjectID) => { if (typeof id === "string") { id = new ObjectID(id); From 02f91d26ff8ee95580dc6bc715ecb932eb166111 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Mon, 5 Aug 2024 15:40:20 -0600 Subject: [PATCH 02/10] refactor: Update import statements for ProbeMonitor and ServerMonitor to use ProbeMonitorResponse and ServerMonitorResponse --- App/FeatureSet/Workers/Index.ts | 3 ++ Common/Types/Monitor/MonitorType.ts | 2 +- Common/Types/ObjectID.ts | 3 +- .../1722892318363-MigrationName.ts | 41 +++++++++++++++++ .../Postgres/SchemaMigrations/Index.ts | 2 + .../Monitor/LogMonitor/LogMonitorStepFrom.tsx | 2 +- .../Monitor/SummaryView/LogMonitorView.tsx | 44 +++++++++++++++++++ .../Monitor/SummaryView/Summary.tsx | 3 ++ .../Monitor/SummaryView/SummaryInfo.tsx | 17 ++++++- .../Types/TelemetryMonitorSummary.ts | 4 ++ Dashboard/src/Pages/Monitor/View/Index.tsx | 10 ++++- .../src/Utils/Form/Monitor/CriteriaFilter.ts | 3 +- 12 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 CommonServer/Infrastructure/Postgres/SchemaMigrations/1722892318363-MigrationName.ts create mode 100644 Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx create mode 100644 Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts diff --git a/App/FeatureSet/Workers/Index.ts b/App/FeatureSet/Workers/Index.ts index 50154aac31..6199371fa4 100644 --- a/App/FeatureSet/Workers/Index.ts +++ b/App/FeatureSet/Workers/Index.ts @@ -67,6 +67,9 @@ import logger from "CommonServer/Utils/Logger"; import "./Jobs/Probe/SendOwnerAddedNotification"; import "./Jobs/Probe/UpdateConnectionStatus"; +// Telemetry Monitors. +import "./Jobs/TelemetryMonitor/MonitorTelemetryMonitor"; + const WorkersFeatureSet: FeatureSet = { init: async (): Promise => { try { diff --git a/Common/Types/Monitor/MonitorType.ts b/Common/Types/Monitor/MonitorType.ts index 242601e029..63d601841d 100644 --- a/Common/Types/Monitor/MonitorType.ts +++ b/Common/Types/Monitor/MonitorType.ts @@ -108,7 +108,7 @@ export class MonitorTypeHelper { monitorType: MonitorType.Logs, title: "Logs", description: "This monitor type lets you monitor logs from any source.", - } + }, // , // { // monitorType: MonitorType.Metrics, diff --git a/Common/Types/ObjectID.ts b/Common/Types/ObjectID.ts index f70c66d0b4..7a1a01c3c3 100644 --- a/Common/Types/ObjectID.ts +++ b/Common/Types/ObjectID.ts @@ -48,8 +48,7 @@ export default class ObjectID extends DatabaseProperty { } public static toJSONArray(ids: Array): Array { - - if(!ids || ids.length === 0) { + if (!ids || ids.length === 0) { return []; } diff --git a/CommonServer/Infrastructure/Postgres/SchemaMigrations/1722892318363-MigrationName.ts b/CommonServer/Infrastructure/Postgres/SchemaMigrations/1722892318363-MigrationName.ts new file mode 100644 index 0000000000..56f5361239 --- /dev/null +++ b/CommonServer/Infrastructure/Postgres/SchemaMigrations/1722892318363-MigrationName.ts @@ -0,0 +1,41 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MigrationName1722892318363 implements MigrationInterface { + public name = "MigrationName1722892318363"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "Monitor" ADD "telemetryMonitorNextMonitorAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "Monitor" ADD "telemetryMonitorLastMonitorAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "Incident" ADD "telemetryQuery" jsonb`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_540a4857bf04eabc59775ca210" ON "Monitor" ("telemetryMonitorNextMonitorAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_02bae6cdc6d5b3092ac393fbb3" ON "Monitor" ("telemetryMonitorLastMonitorAt") `, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX "public"."IDX_02bae6cdc6d5b3092ac393fbb3"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_540a4857bf04eabc59775ca210"`, + ); + await queryRunner.query( + `ALTER TABLE "Incident" DROP COLUMN "telemetryQuery"`, + ); + await queryRunner.query( + `ALTER TABLE "Monitor" DROP COLUMN "telemetryMonitorLastMonitorAt"`, + ); + await queryRunner.query( + `ALTER TABLE "Monitor" DROP COLUMN "telemetryMonitorNextMonitorAt"`, + ); + } +} diff --git a/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts b/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts index 39bc401aac..a38ceb8cc6 100644 --- a/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts +++ b/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts @@ -37,6 +37,7 @@ import { MigrationName1721754545771 } from "./1721754545771-MigrationName"; import { MigrationName1721779190475 } from "./1721779190475-MigrationName"; import { MigrationName1722031205897 } from "./1722031205897-MigrationName"; import { MigrationName1722543640526 } from "./1722543640526-MigrationName"; +import { MigrationName1722892318363 } from "./1722892318363-MigrationName"; export default [ InitialMigration, @@ -78,4 +79,5 @@ export default [ MigrationName1721779190475, MigrationName1722031205897, MigrationName1722543640526, + MigrationName1722892318363, ]; diff --git a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx index a4bd244b0e..c56296769d 100644 --- a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx +++ b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx @@ -84,7 +84,7 @@ const LogMonitorStepForm: FunctionComponent = ( value: 86400, }, ], - title: "Monitor Logs for Last", + title: "Monitor Logs for", isAdvancedFilter: true, }, { diff --git a/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx b/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx new file mode 100644 index 0000000000..d93a49947c --- /dev/null +++ b/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx @@ -0,0 +1,44 @@ +import OneUptimeDate from "Common/Types/Date"; +import InfoCard from "CommonUI/src/Components/InfoCard/InfoCard"; +import React, { FunctionComponent, ReactElement } from "react"; +import TelemetryMonitorSummary from "./Types/TelemetryMonitorSummary"; + +export interface ComponentProps { + telemetryMonitorSummary?: TelemetryMonitorSummary | undefined; +} + +const WebsiteMonitorSummaryView: FunctionComponent = ( + props: ComponentProps, +): ReactElement => { + + return ( +
+
+ + +
+
+ ); +}; + +export default WebsiteMonitorSummaryView; diff --git a/Dashboard/src/Components/Monitor/SummaryView/Summary.tsx b/Dashboard/src/Components/Monitor/SummaryView/Summary.tsx index 79e971eca4..16d8122f14 100644 --- a/Dashboard/src/Components/Monitor/SummaryView/Summary.tsx +++ b/Dashboard/src/Components/Monitor/SummaryView/Summary.tsx @@ -10,6 +10,7 @@ import Card from "CommonUI/src/Components/Card/Card"; import { MonitorStepProbeResponse } from "Common/Models/DatabaseModels/MonitorProbe"; import Probe from "Common/Models/DatabaseModels/Probe"; import React, { FunctionComponent, ReactElement, useEffect } from "react"; +import TelemetryMonitorSummary from "./Types/TelemetryMonitorSummary"; export interface ComponentProps { probeMonitorResponses?: Array | undefined; @@ -17,6 +18,7 @@ export interface ComponentProps { serverMonitorResponse?: ServerMonitorResponse | undefined; probes?: Array; monitorType: MonitorType; + telemetryMonitorSummary?: TelemetryMonitorSummary | undefined; } const Summary: FunctionComponent = ( @@ -81,6 +83,7 @@ const Summary: FunctionComponent = ( probeMonitorResponses={probeResponses} incomingMonitorRequest={props.incomingMonitorRequest} serverMonitorResponse={props.serverMonitorResponse} + telemetryMonitorSummary={props.telemetryMonitorSummary} /> diff --git a/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx b/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx index 271cd3877b..e0e3d17ad2 100644 --- a/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx +++ b/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx @@ -12,12 +12,16 @@ import MonitorType, { import ProbeMonitorResponse from "Common/Types/Probe/ProbeMonitorResponse"; import ErrorMessage from "CommonUI/src/Components/ErrorMessage/ErrorMessage"; import React, { FunctionComponent, ReactElement } from "react"; +import LogMonitorSummaryView from "./LogMonitorView"; +import TelemetryMonitorSummary from "./Types/TelemetryMonitorSummary"; + export interface ComponentProps { monitorType: MonitorType; probeMonitorResponses?: Array | undefined; // this is an array because of multiple monitor steps. incomingMonitorRequest?: IncomingMonitorRequest | undefined; serverMonitorResponse?: ServerMonitorResponse | undefined; + telemetryMonitorSummary?: TelemetryMonitorSummary | undefined; } const SummaryInfo: FunctionComponent = ( @@ -85,6 +89,15 @@ const SummaryInfo: FunctionComponent = ( ); } + if (props.monitorType === MonitorType.Logs) { + return ( + + ); + } + return <>; }; @@ -124,7 +137,7 @@ const SummaryInfo: FunctionComponent = ( )} {props.incomingMonitorRequest && - props.monitorType === MonitorType.IncomingRequest ? ( + props.monitorType === MonitorType.IncomingRequest ? ( @@ -133,7 +146,7 @@ const SummaryInfo: FunctionComponent = ( )} {props.monitorType === MonitorType.Server && - props.serverMonitorResponse ? ( + props.serverMonitorResponse ? ( diff --git a/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts b/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts new file mode 100644 index 0000000000..48bda08370 --- /dev/null +++ b/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts @@ -0,0 +1,4 @@ +export default interface TelemetryMonitorSummary { + lastCheckedAt?: Date | undefined; + nextCheckAt?: Date | undefined; +} diff --git a/Dashboard/src/Pages/Monitor/View/Index.tsx b/Dashboard/src/Pages/Monitor/View/Index.tsx index d4d3b74459..37a7cabeea 100644 --- a/Dashboard/src/Pages/Monitor/View/Index.tsx +++ b/Dashboard/src/Pages/Monitor/View/Index.tsx @@ -3,7 +3,7 @@ import DisabledWarning from "../../../Components/Monitor/DisabledWarning"; import IncomingMonitorLink from "../../../Components/Monitor/IncomingRequestMonitor/IncomingMonitorLink"; import { MonitorCharts } from "../../../Components/Monitor/MonitorCharts/MonitorChart"; import ServerMonitorDocumentation from "../../../Components/Monitor/ServerMonitor/Documentation"; -import Metrics from "../../../Components/Monitor/SummaryView/Summary"; +import Summary from "../../../Components/Monitor/SummaryView/Summary"; import ProbeUtil from "../../../Utils/Probe"; import PageComponentProps from "../../PageComponentProps"; import InBetween from "Common/Types/BaseDatabase/InBetween"; @@ -184,6 +184,8 @@ const MonitorView: FunctionComponent = (): ReactElement => { serverMonitorResponse: true, isNoProbeEnabledOnThisMonitor: true, isAllProbesDisconnectedFromThisMonitor: true, + telemetryMonitorLastMonitorAt: true, + telemetryMonitorNextMonitorAt: true, }, }); @@ -600,12 +602,16 @@ const MonitorView: FunctionComponent = (): ReactElement => { /> - {shouldFetchMonitorMetrics && getMonitorMetricsChartGroup()} diff --git a/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts b/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts index 70d00cf6d5..910e1868fd 100644 --- a/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts +++ b/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts @@ -261,7 +261,8 @@ export default class CriteriaFilterUtil { i.value === FilterType.GreaterThan || i.value === FilterType.LessThan || i.value === FilterType.LessThanOrEqualTo || - i.value === FilterType.GreaterThanOrEqualTo + i.value === FilterType.GreaterThanOrEqualTo || + i.value === FilterType.EqualTo ); }); } From bca44b5faf3359c9ca8c3707948b98ad0c875e67 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Mon, 5 Aug 2024 15:41:32 -0600 Subject: [PATCH 03/10] refactor: Update import statements for ProbeMonitor and ServerMonitor to use ProbeMonitorResponse and ServerMonitorResponse --- App/FeatureSet/Workers/Index.ts | 2 +- .../Monitor/SummaryView/LogMonitorView.tsx | 19 +++++++++---------- .../Monitor/SummaryView/SummaryInfo.tsx | 5 ++--- .../Types/TelemetryMonitorSummary.ts | 4 ++-- Dashboard/src/Pages/Monitor/View/Index.tsx | 2 +- .../src/Utils/Form/Monitor/CriteriaFilter.ts | 2 +- 6 files changed, 16 insertions(+), 18 deletions(-) diff --git a/App/FeatureSet/Workers/Index.ts b/App/FeatureSet/Workers/Index.ts index 6199371fa4..8118b51507 100644 --- a/App/FeatureSet/Workers/Index.ts +++ b/App/FeatureSet/Workers/Index.ts @@ -67,7 +67,7 @@ import logger from "CommonServer/Utils/Logger"; import "./Jobs/Probe/SendOwnerAddedNotification"; import "./Jobs/Probe/UpdateConnectionStatus"; -// Telemetry Monitors. +// Telemetry Monitors. import "./Jobs/TelemetryMonitor/MonitorTelemetryMonitor"; const WorkersFeatureSet: FeatureSet = { diff --git a/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx b/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx index d93a49947c..e7b5761b89 100644 --- a/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx +++ b/Dashboard/src/Components/Monitor/SummaryView/LogMonitorView.tsx @@ -10,7 +10,6 @@ export interface ComponentProps { const WebsiteMonitorSummaryView: FunctionComponent = ( props: ComponentProps, ): ReactElement => { - return (
@@ -19,21 +18,21 @@ const WebsiteMonitorSummaryView: FunctionComponent = ( title="Monitored At" value={ props.telemetryMonitorSummary?.lastCheckedAt - ? OneUptimeDate.getDateAsLocalFormattedString( - props.telemetryMonitorSummary?.lastCheckedAt, - ) - : "-" + ? OneUptimeDate.getDateAsLocalFormattedString( + props.telemetryMonitorSummary?.lastCheckedAt, + ) + : "-" } /> -
diff --git a/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx b/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx index e0e3d17ad2..0f82aa543f 100644 --- a/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx +++ b/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx @@ -15,7 +15,6 @@ import React, { FunctionComponent, ReactElement } from "react"; import LogMonitorSummaryView from "./LogMonitorView"; import TelemetryMonitorSummary from "./Types/TelemetryMonitorSummary"; - export interface ComponentProps { monitorType: MonitorType; probeMonitorResponses?: Array | undefined; // this is an array because of multiple monitor steps. @@ -137,7 +136,7 @@ const SummaryInfo: FunctionComponent = ( )} {props.incomingMonitorRequest && - props.monitorType === MonitorType.IncomingRequest ? ( + props.monitorType === MonitorType.IncomingRequest ? ( @@ -146,7 +145,7 @@ const SummaryInfo: FunctionComponent = ( )} {props.monitorType === MonitorType.Server && - props.serverMonitorResponse ? ( + props.serverMonitorResponse ? ( diff --git a/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts b/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts index 48bda08370..ad2e8bc78c 100644 --- a/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts +++ b/Dashboard/src/Components/Monitor/SummaryView/Types/TelemetryMonitorSummary.ts @@ -1,4 +1,4 @@ export default interface TelemetryMonitorSummary { - lastCheckedAt?: Date | undefined; - nextCheckAt?: Date | undefined; + lastCheckedAt?: Date | undefined; + nextCheckAt?: Date | undefined; } diff --git a/Dashboard/src/Pages/Monitor/View/Index.tsx b/Dashboard/src/Pages/Monitor/View/Index.tsx index 37a7cabeea..04adc36b18 100644 --- a/Dashboard/src/Pages/Monitor/View/Index.tsx +++ b/Dashboard/src/Pages/Monitor/View/Index.tsx @@ -610,7 +610,7 @@ const MonitorView: FunctionComponent = (): ReactElement => { serverMonitorResponse={serverMonitorResponse} telemetryMonitorSummary={{ lastCheckedAt: monitor?.telemetryMonitorLastMonitorAt, - nextCheckAt: monitor?.telemetryMonitorNextMonitorAt + nextCheckAt: monitor?.telemetryMonitorNextMonitorAt, }} /> diff --git a/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts b/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts index 910e1868fd..150a2c0444 100644 --- a/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts +++ b/Dashboard/src/Utils/Form/Monitor/CriteriaFilter.ts @@ -261,7 +261,7 @@ export default class CriteriaFilterUtil { i.value === FilterType.GreaterThan || i.value === FilterType.LessThan || i.value === FilterType.LessThanOrEqualTo || - i.value === FilterType.GreaterThanOrEqualTo || + i.value === FilterType.GreaterThanOrEqualTo || i.value === FilterType.EqualTo ); }); From 3f7cfa3110713acb3e9084bfa3fde9382bccb00c Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 6 Aug 2024 09:20:10 -0600 Subject: [PATCH 04/10] refactor: Update import statements for ProbeMonitor and ServerMonitor to use ProbeMonitorResponse and ServerMonitorResponse --- App/FeatureSet/Home/Utils/OSSFriends.ts | 6 ++++++ App/FeatureSet/Home/Utils/Reviews.ts | 6 +++--- .../Monitor/SummaryView/SummaryInfo.tsx | 15 ++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/App/FeatureSet/Home/Utils/OSSFriends.ts b/App/FeatureSet/Home/Utils/OSSFriends.ts index 7efb1ae906..d81d5aacbf 100644 --- a/App/FeatureSet/Home/Utils/OSSFriends.ts +++ b/App/FeatureSet/Home/Utils/OSSFriends.ts @@ -37,6 +37,12 @@ const OSSFriends: OSSFriend[] = [ "PostHog is open-source product analytics, built for developers.", repositoryUrl: URL.fromString("https://github.com/PostHog/posthog"), }, + { + name: "Twenty", + description: + "Twenty is building an open-source modern CRM. It's alternative to Salesforce and HubSpot.", + repositoryUrl: URL.fromString("https://github.com/twentyhq/twenty"), + }, ]; export default OSSFriends; diff --git a/App/FeatureSet/Home/Utils/Reviews.ts b/App/FeatureSet/Home/Utils/Reviews.ts index 17ce032749..f56d26e852 100644 --- a/App/FeatureSet/Home/Utils/Reviews.ts +++ b/App/FeatureSet/Home/Utils/Reviews.ts @@ -6,9 +6,9 @@ export interface Review { const reviews: Review[] = [ { - name: "Alice", - text: "OneUptime has been a game-changer for our team. The alerts are timely and the status page keeps our customers informed.", - title: "Game-changer!", + name: "Anderson, GK2 Cloud", + text: "Thanks for building OneUptime, it really is fantastic. We are getting more excited every day!", + title: "OneUptime is fantastic!", }, { name: "Bob", diff --git a/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx b/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx index 0f82aa543f..dd9c43f6bc 100644 --- a/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx +++ b/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo.tsx @@ -88,15 +88,6 @@ const SummaryInfo: FunctionComponent = ( ); } - if (props.monitorType === MonitorType.Logs) { - return ( - - ); - } - return <>; }; @@ -152,6 +143,12 @@ const SummaryInfo: FunctionComponent = ( ) : ( <> )} + + {props.monitorType === MonitorType.Logs && ( + + )}
); }; From 5fc95eb02e5c2607d8919ae3ca7b3ddd0ef17c61 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 6 Aug 2024 11:26:13 -0600 Subject: [PATCH 05/10] refactor: Update import statements for ProbeMonitor and ServerMonitor to use ProbeMonitorResponse and ServerMonitorResponse --- App/FeatureSet/Home/Utils/Reviews.ts | 6 +- .../MonitorTelemetryMonitor.ts | 6 +- Common/Types/Monitor/MonitorStepLogMonitor.ts | 8 +- CommonUI/src/Components/Forms/BasicForm.tsx | 2 +- .../Monitor/LogMonitor/LogMonitorStepFrom.tsx | 53 +-- .../Monitor/MonitorSteps/MonitorStep.tsx | 406 ++++++++++++------ 6 files changed, 320 insertions(+), 161 deletions(-) diff --git a/App/FeatureSet/Home/Utils/Reviews.ts b/App/FeatureSet/Home/Utils/Reviews.ts index f56d26e852..ddae1e6f4c 100644 --- a/App/FeatureSet/Home/Utils/Reviews.ts +++ b/App/FeatureSet/Home/Utils/Reviews.ts @@ -11,9 +11,9 @@ const reviews: Review[] = [ title: "OneUptime is fantastic!", }, { - name: "Bob", - text: "The incident management feature is top-notch. It has streamlined our response process significantly.", - title: "Top-notch incident management", + name: "Reg, Skillable", + text: "We use OneUptime to reliably monitor endpoint availability globally, and it delivers.", + title: "OneUptime delivers!", }, { name: "Charlie", diff --git a/App/FeatureSet/Workers/Jobs/TelemetryMonitor/MonitorTelemetryMonitor.ts b/App/FeatureSet/Workers/Jobs/TelemetryMonitor/MonitorTelemetryMonitor.ts index 7663c5d6c9..28d5b14a46 100644 --- a/App/FeatureSet/Workers/Jobs/TelemetryMonitor/MonitorTelemetryMonitor.ts +++ b/App/FeatureSet/Workers/Jobs/TelemetryMonitor/MonitorTelemetryMonitor.ts @@ -186,8 +186,10 @@ const monitorLogs: MonitorLogsFunction = async (data: { query.body = new Search(logQuery.body); } - if (logQuery.severityText) { - query.severityText = logQuery.severityText; + if (logQuery.severityTexts && logQuery.severityTexts.length > 0) { + query.severityText = QueryHelper.any( + logQuery.severityTexts as Array, + ); } if (logQuery.telemetryServiceIds && logQuery.telemetryServiceIds.length > 0) { diff --git a/Common/Types/Monitor/MonitorStepLogMonitor.ts b/Common/Types/Monitor/MonitorStepLogMonitor.ts index 92901ade82..2c55a50c0c 100644 --- a/Common/Types/Monitor/MonitorStepLogMonitor.ts +++ b/Common/Types/Monitor/MonitorStepLogMonitor.ts @@ -6,7 +6,7 @@ import ObjectID from "../ObjectID"; export default interface MonitorStepLogMonitor { attributes: Dictionary; body: string; - severityText: Array; + severityTexts: Array; telemetryServiceIds: Array; lastXSecondsOfLogs: number; } @@ -16,7 +16,7 @@ export class MonitorStepLogMonitorUtil { return { attributes: {}, body: "", - severityText: [], + severityTexts: [], telemetryServiceIds: [], lastXSecondsOfLogs: 60, }; @@ -27,7 +27,7 @@ export class MonitorStepLogMonitorUtil { attributes: (json["attributes"] as Dictionary) || {}, body: json["body"] as string, - severityText: json["severityText"] as Array, + severityTexts: json["severityTexts"] as Array, telemetryServiceIds: ObjectID.fromJSONArray( json["telemetryServiceIds"] as Array, ), @@ -39,7 +39,7 @@ export class MonitorStepLogMonitorUtil { return { attributes: monitor.attributes, body: monitor.body, - severityText: monitor.severityText, + severityTexts: monitor.severityTexts, telemetryServiceId: ObjectID.toJSONArray(monitor.telemetryServiceIds), lastXSecondsOfLogs: monitor.lastXSecondsOfLogs, }; diff --git a/CommonUI/src/Components/Forms/BasicForm.tsx b/CommonUI/src/Components/Forms/BasicForm.tsx index 366e3f5595..491ea1eb9c 100644 --- a/CommonUI/src/Components/Forms/BasicForm.tsx +++ b/CommonUI/src/Components/Forms/BasicForm.tsx @@ -48,7 +48,7 @@ export const DefaultValidateFunction: DefaultValidateFunctionType = ( return {}; }; -export interface BaseComponentProps { +export interface BaseComponentProps { submitButtonStyleType?: ButtonStyleType | undefined; initialValues?: FormValues | undefined; onValidate?: undefined | ((values: FormValues) => JSONObject); diff --git a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx index c56296769d..00a5b80aeb 100644 --- a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx +++ b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx @@ -1,11 +1,10 @@ -import LogSeverity from "Common/Types/Log/LogSeverity"; import MonitorStepLogMonitor from "Common/Types/Monitor/MonitorStepLogMonitor"; -import FiltersForm from "CommonUI/src/Components/Filters/FiltersForm"; -import FieldType from "CommonUI/src/Components/Types/FieldType"; -import Query from "CommonUI/src/Utils/BaseDatabase/Query"; -import DropdownUtil from "CommonUI/src/Utils/Dropdown"; import TelemetryService from "Common/Models/DatabaseModels/TelemetryService"; import React, { FunctionComponent, ReactElement } from "react"; +import BasicForm from "CommonUI/src/Components/Forms/BasicForm"; +import LogSeverity from "Common/Types/Log/LogSeverity"; +import DropdownUtil from "CommonUI/src/Utils/Dropdown"; +import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType"; export interface ComponentProps { monitorStepLogMonitor: MonitorStepLogMonitor; @@ -20,24 +19,24 @@ const LogMonitorStepForm: FunctionComponent = ( props: ComponentProps, ): ReactElement => { return ( - + ) => { - props.onMonitorStepLogMonitorChanged( - filterData as MonitorStepLogMonitor, - ); - }} - filters={[ + hideSubmitButton={true} + initialValue={props.monitorStepLogMonitor} + onChange={props.onMonitorStepLogMonitorChanged} + fields={[ { - key: "body", - type: FieldType.Text, + field: { + body: true, + }, + type: FormFieldSchemaType.Text, title: "Search Log Body", }, { - key: "lastXSecondsOfLogs", - type: FieldType.Dropdown, + field: { + lastXSecondsOfLogs: true, + }, + type: FormFieldSchemaType.Dropdown, filterDropdownOptions: [ { label: "Last 5 seconds", @@ -88,16 +87,20 @@ const LogMonitorStepForm: FunctionComponent = ( isAdvancedFilter: true, }, { - key: "severityText", + field: { + severityTexts: true, + }, filterDropdownOptions: DropdownUtil.getDropdownOptionsFromEnum(LogSeverity), - type: FieldType.MultiSelectDropdown, + type: FormFieldSchemaType.MultiSelectDropdown, title: "Log Severity", isAdvancedFilter: true, }, { - key: "telemetryServiceIds", - type: FieldType.MultiSelectDropdown, + field: { + telemetryServiceIds: true, + }, + type: FormFieldSchemaType.MultiSelectDropdown, filterDropdownOptions: props.telemetryServices.map( (telemetryService: TelemetryService) => { return { @@ -110,8 +113,10 @@ const LogMonitorStepForm: FunctionComponent = ( isAdvancedFilter: true, }, { - key: "attributes", - type: FieldType.JSON, + field: { + attributes: true, + }, + type: FormFieldSchemaType.JSON, title: "Filter by Attributes", jsonKeys: props.attributeKeys, isAdvancedFilter: true, diff --git a/Dashboard/src/Components/Monitor/MonitorSteps/MonitorStep.tsx b/Dashboard/src/Components/Monitor/MonitorSteps/MonitorStep.tsx index 7f85ed4c2a..329a7c734f 100644 --- a/Dashboard/src/Components/Monitor/MonitorSteps/MonitorStep.tsx +++ b/Dashboard/src/Components/Monitor/MonitorSteps/MonitorStep.tsx @@ -16,6 +16,19 @@ import React, { useEffect, useState, } from "react"; +import ComponentLoader from "CommonUI/src/Components/ComponentLoader/ComponentLoader"; +import ErrorMessage from "CommonUI/src/Components/ErrorMessage/ErrorMessage"; +import TelemetryService from "Common/Models/DatabaseModels/TelemetryService"; +import { JSONObject } from "Common/Types/JSON"; +import { PromiseVoidFunction } from "Common/Types/FunctionTypes"; +import ListResult from "CommonUI/src/Utils/BaseDatabase/ListResult"; +import ModelAPI from "CommonUI/src/Utils/ModelAPI/ModelAPI"; +import DashboardNavigation from "../../../Utils/Navigation"; +import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax"; +import SortOrder from "Common/Types/BaseDatabase/SortOrder"; +import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse"; +import API from "CommonUI/src/Utils/API/API"; +import Includes from "Common/Types/BaseDatabase/Includes"; export interface ComponentProps { monitorStatusOptions: Array; @@ -25,138 +38,268 @@ export interface ComponentProps { onCallPolicyOptions: Array; } +export interface LogMonitorStepView { + body: string | undefined; + severityTexts: Array | undefined; + attributes: JSONObject | undefined; + telemetryServices: Array | undefined; +} + const MonitorStepElement: FunctionComponent = ( props: ComponentProps, ): ReactElement => { - const [requestDetailsFields, setRequestDetailsFields] = useState< - Array> - >([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(undefined); + const [telemetryServices, setTelemetryServices] = useState< + Array | undefined + >(undefined); + + // this field is used for most monitor types + let fields: Array> = []; + let logFields: Array> = []; + + const logMonitorDetailView: LogMonitorStepView = { + body: undefined, + severityTexts: undefined, + attributes: undefined, + telemetryServices: undefined, + }; + + const fetchTelemetryServices: PromiseVoidFunction = + async (): Promise => { + const telemetryServicesResult: ListResult = + await ModelAPI.getList({ + modelType: TelemetryService, + query: { + projectId: DashboardNavigation.getProjectId(), + _id: new Includes( + props.monitorStep.data?.logMonitor?.telemetryServiceIds || [], + ), + }, + limit: LIMIT_PER_PROJECT, + skip: 0, + select: { + _id: true, + name: true, + serviceColor: true, + }, + sort: { + name: SortOrder.Ascending, + }, + }); + + if (telemetryServicesResult instanceof HTTPErrorResponse) { + throw telemetryServicesResult; + } + + setTelemetryServices(telemetryServicesResult.data); + }; + + const loadComponent: PromiseVoidFunction = async (): Promise => { + setIsLoading(true); + try { + if (props.monitorType === MonitorType.Logs) { + await fetchTelemetryServices(); + } + } catch (err) { + setError(API.getFriendlyErrorMessage(err as Error)); + } + + setIsLoading(false); + }; useEffect(() => { - let fields: Array> = []; - - if (props.monitorType === MonitorType.API) { - fields = [ - { - key: "monitorDestination", - title: "API URL", - description: "URL of the API you want to monitor.", - fieldType: FieldType.Text, - placeholder: "No data entered", - }, - { - key: "requestType", - title: "Request Type", - description: "Whats the type of the API request?", - fieldType: FieldType.Text, - placeholder: "No data entered", - }, - { - key: "requestBody", - title: "Request Body", - description: "Request Body to send, if any.", - fieldType: FieldType.JSON, - placeholder: "No data entered", - }, - { - key: "requestHeaders", - title: "Request Headers", - description: "Request Headers to send, if any.", - fieldType: FieldType.DictionaryOfStrings, - placeholder: "No data entered", - }, - ]; - } else if (props.monitorType === MonitorType.Website) { - fields = [ - { - key: "monitorDestination", - title: "Website URL", - description: "URL of the website you want to monitor.", - fieldType: FieldType.Text, - placeholder: "No data entered", - }, - ]; - } else if (props.monitorType === MonitorType.Ping) { - fields = [ - { - key: "monitorDestination", - title: "Ping Hostname or IP Address", - description: - "Hostname or IP Address of the resource you would like us to ping.", - fieldType: FieldType.Text, - placeholder: "No data entered", - }, - ]; - } else if (props.monitorType === MonitorType.Port) { - fields = [ - { - key: "monitorDestination", - title: "Ping Hostname or IP Address", - description: - "Hostname or IP Address of the resource you would like us to ping.", - fieldType: FieldType.Text, - placeholder: "No data entered", - }, - { - key: "monitorDestinationPort", - title: "Port", - description: "Port of the resource you would like us to ping.", - fieldType: FieldType.Port, - placeholder: "No port entered", - }, - ]; - } else if (props.monitorType === MonitorType.IP) { - fields = [ - { - key: "monitorDestination", - title: "IP Address", - description: "IP Address of the resource you would like us to ping.", - fieldType: FieldType.Text, - placeholder: "No data entered", - }, - ]; - } else if (props.monitorType === MonitorType.CustomJavaScriptCode) { - fields = [ - { - key: "customCode", - title: "JavaScript Code", - description: "JavaScript code to run.", - fieldType: FieldType.JavaScript, - placeholder: "No data entered", - }, - ]; - } else if (props.monitorType === MonitorType.SyntheticMonitor) { - fields = [ - { - key: "customCode", - title: "JavaScript Code", - description: "JavaScript code to run.", - fieldType: FieldType.JavaScript, - placeholder: "No data entered", - }, - { - key: "browserTypes", - title: "Browser Types", - description: "Browser types to run the synthetic monitor on.", - fieldType: FieldType.ArrayOfText, - placeholder: "No data entered", - }, - { - key: "screenSizeTypes", - title: "Screen Size Types", - description: "Screen size types to run the synthetic monitor on.", - fieldType: FieldType.ArrayOfText, - placeholder: "No data entered", - }, - ]; - } - setRequestDetailsFields(fields); + loadComponent(); }, [props.monitorType]); + if (isLoading) { + return ; + } + + if (error) { + return ; + } + + if (props.monitorType === MonitorType.API) { + fields = [ + { + key: "monitorDestination", + title: "API URL", + description: "URL of the API you want to monitor.", + fieldType: FieldType.Text, + placeholder: "No data entered", + }, + { + key: "requestType", + title: "Request Type", + description: "Whats the type of the API request?", + fieldType: FieldType.Text, + placeholder: "No data entered", + }, + { + key: "requestBody", + title: "Request Body", + description: "Request Body to send, if any.", + fieldType: FieldType.JSON, + placeholder: "No data entered", + }, + { + key: "requestHeaders", + title: "Request Headers", + description: "Request Headers to send, if any.", + fieldType: FieldType.DictionaryOfStrings, + placeholder: "No data entered", + }, + ]; + } else if (props.monitorType === MonitorType.Website) { + fields = [ + { + key: "monitorDestination", + title: "Website URL", + description: "URL of the website you want to monitor.", + fieldType: FieldType.Text, + placeholder: "No data entered", + }, + ]; + } else if (props.monitorType === MonitorType.Ping) { + fields = [ + { + key: "monitorDestination", + title: "Ping Hostname or IP Address", + description: + "Hostname or IP Address of the resource you would like us to ping.", + fieldType: FieldType.Text, + placeholder: "No data entered", + }, + ]; + } else if (props.monitorType === MonitorType.Port) { + fields = [ + { + key: "monitorDestination", + title: "Ping Hostname or IP Address", + description: + "Hostname or IP Address of the resource you would like us to ping.", + fieldType: FieldType.Text, + placeholder: "No data entered", + }, + { + key: "monitorDestinationPort", + title: "Port", + description: "Port of the resource you would like us to ping.", + fieldType: FieldType.Port, + placeholder: "No port entered", + }, + ]; + } else if (props.monitorType === MonitorType.IP) { + fields = [ + { + key: "monitorDestination", + title: "IP Address", + description: "IP Address of the resource you would like us to ping.", + fieldType: FieldType.Text, + placeholder: "No data entered", + }, + ]; + } else if (props.monitorType === MonitorType.CustomJavaScriptCode) { + fields = [ + { + key: "customCode", + title: "JavaScript Code", + description: "JavaScript code to run.", + fieldType: FieldType.JavaScript, + placeholder: "No data entered", + }, + ]; + } else if (props.monitorType === MonitorType.SyntheticMonitor) { + fields = [ + { + key: "customCode", + title: "JavaScript Code", + description: "JavaScript code to run.", + fieldType: FieldType.JavaScript, + placeholder: "No data entered", + }, + { + key: "browserTypes", + title: "Browser Types", + description: "Browser types to run the synthetic monitor on.", + fieldType: FieldType.ArrayOfText, + placeholder: "No data entered", + }, + { + key: "screenSizeTypes", + title: "Screen Size Types", + description: "Screen size types to run the synthetic monitor on.", + fieldType: FieldType.ArrayOfText, + placeholder: "No data entered", + }, + ]; + } else if (props.monitorType === MonitorType.Logs) { + logFields = []; + + if (props.monitorStep.data?.logMonitor?.body) { + logMonitorDetailView.body = props.monitorStep.data?.logMonitor?.body; + + logFields.push({ + key: "body", + title: "Filter Log Message", + description: "Filter by log message with this text:", + fieldType: FieldType.Text, + placeholder: "No log message entered", + }); + } + + if (props.monitorStep.data?.logMonitor?.severityTexts) { + logMonitorDetailView.severityTexts = + props.monitorStep.data?.logMonitor?.severityTexts; + + logFields.push({ + key: "severityTexts", + title: "Log Severity", + description: "Severity of the logs to monitor.", + fieldType: FieldType.ArrayOfText, + placeholder: "No severity entered", + }); + } + + if ( + props.monitorStep.data?.logMonitor?.attributes && + Object.keys(props.monitorStep.data?.logMonitor?.attributes).length > 0 + ) { + logMonitorDetailView.attributes = + props.monitorStep.data?.logMonitor?.attributes; + + logFields.push({ + key: "attributes", + title: "Log Attributes", + description: "Attributes of the logs to monitor.", + fieldType: FieldType.JSON, + placeholder: "No attributes entered", + }); + } + + if ( + props.monitorStep.data?.logMonitor?.telemetryServiceIds && + props.monitorStep.data?.logMonitor?.telemetryServiceIds.length > 0 && + telemetryServices && + telemetryServices.length > 0 + ) { + logMonitorDetailView.telemetryServices = telemetryServices; // set the telemetry services + + logFields.push({ + key: "telemetryServices", + title: "Telemetry Services", + description: "Telemetry services to monitor.", + fieldType: FieldType.Element, + placeholder: "No telemetry services entered", + }); + } + } + return (
= ( isHeading={true} />
- + {fields && fields.length && ( + + id={"monitor-step"} + item={props.monitorStep.data!} + fields={fields} + /> + )} + {logFields && logFields.length && ( + + id={"monitor-logs"} + item={logMonitorDetailView} + fields={logFields} + /> + )}
From 9bf3856c95f2b53bb4b1cf07aab072513875d573 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 6 Aug 2024 14:02:28 -0600 Subject: [PATCH 06/10] refactor: Update import statements for ProbeMonitor and ServerMonitor to use ProbeMonitorResponse and ServerMonitorResponse --- App/FeatureSet/Home/Utils/Reviews.ts | 20 +- Common/Types/Date.ts | 43 ++++ .../src/Components/Dictionary/Dictionary.tsx | 2 +- .../Components/Forms/Fields/FieldLabel.tsx | 3 +- .../src/Components/Forms/Fields/FormField.tsx | 23 ++ CommonUI/src/Components/Forms/Types/Field.ts | 5 + .../Forms/Types/FormFieldSchemaType.ts | 1 + .../Monitor/LogMonitor/LogMonitorStepFrom.tsx | 225 ++++++++++-------- .../Components/Form/Monitor/MonitorStep.tsx | 7 - .../Monitor/MonitorSteps/MonitorStep.tsx | 35 ++- .../TelemetryServiceElements.tsx | 31 +++ 11 files changed, 277 insertions(+), 118 deletions(-) create mode 100644 Dashboard/src/Components/TelemetryService/TelemetryServiceElements.tsx diff --git a/App/FeatureSet/Home/Utils/Reviews.ts b/App/FeatureSet/Home/Utils/Reviews.ts index ddae1e6f4c..bf00257756 100644 --- a/App/FeatureSet/Home/Utils/Reviews.ts +++ b/App/FeatureSet/Home/Utils/Reviews.ts @@ -5,16 +5,6 @@ export interface Review { } const reviews: Review[] = [ - { - name: "Anderson, GK2 Cloud", - text: "Thanks for building OneUptime, it really is fantastic. We are getting more excited every day!", - title: "OneUptime is fantastic!", - }, - { - name: "Reg, Skillable", - text: "We use OneUptime to reliably monitor endpoint availability globally, and it delivers.", - title: "OneUptime delivers!", - }, { name: "Charlie", text: "I love the on-call rotation setup. It has made scheduling so much easier.", @@ -75,6 +65,16 @@ const reviews: Review[] = [ text: "The log analysis feature is very detailed and insightful.", title: "Detailed log analysis", }, + { + name: "Anderson, GK2 Cloud", + text: "Thanks for building OneUptime, it really is fantastic. We are getting more excited every day!", + title: "OneUptime is fantastic!", + }, + { + name: "Reg, Skillable", + text: "We use OneUptime to reliably monitor endpoint availability globally, and it delivers.", + title: "OneUptime delivers!", + }, { name: "Oscar", text: "OneUptime has made our monitoring process much more efficient.", diff --git a/Common/Types/Date.ts b/Common/Types/Date.ts index 523c6192cb..48f5f0377f 100644 --- a/Common/Types/Date.ts +++ b/Common/Types/Date.ts @@ -849,6 +849,49 @@ export default class OneUptimeDate { return months; } + public static convertSecondsToDaysHoursMinutesAndSeconds( + seconds: number, + ): string { + // should output 2 days, 3 hours, 4 minutes and 5 seconds. If the days are 0, it should not show the days. If the hours are 0, it should not show the hours. If the minutes are 0, it should not show the minutes. If the seconds are 0, it should not show the seconds. + + const days: number = Math.floor(seconds / (24 * 60 * 60)); + const hours: number = Math.floor((seconds % (24 * 60 * 60)) / (60 * 60)); + const minutes: number = Math.floor((seconds % (60 * 60)) / 60); + const secs: number = seconds % 60; + + let formattedString: string = ""; + + if (days > 0) { + formattedString += days + " days"; + } + + if (hours > 0) { + if (formattedString.length > 0) { + formattedString += ", "; + } + + formattedString += hours + " hours"; + } + + if (minutes > 0) { + if (formattedString.length > 0) { + formattedString += ", "; + } + + formattedString += minutes + " minutes"; + } + + if (secs > 0) { + if (formattedString.length > 0) { + formattedString += ", "; + } + + formattedString += secs + " seconds"; + } + + return formattedString; + } + public static convertMinutesToDaysHoursAndMinutes(minutes: number): string { // should output 2 days, 3 hours and 4 minutes. If the days are 0, it should not show the days. If the hours are 0, it should not show the hours. If the minutes are 0, it should not show the minutes. diff --git a/CommonUI/src/Components/Dictionary/Dictionary.tsx b/CommonUI/src/Components/Dictionary/Dictionary.tsx index 9fa8feba5d..f467e60952 100644 --- a/CommonUI/src/Components/Dictionary/Dictionary.tsx +++ b/CommonUI/src/Components/Dictionary/Dictionary.tsx @@ -26,7 +26,7 @@ export interface ComponentProps { initialValue?: Dictionary; keyPlaceholder?: string; valuePlaceholder?: string; - addButtonSuffix?: string; + addButtonSuffix?: string | undefined; valueTypes?: Array; // by default it'll be Text autoConvertValueTypes?: boolean | undefined; keys?: Array | undefined; diff --git a/CommonUI/src/Components/Forms/Fields/FieldLabel.tsx b/CommonUI/src/Components/Forms/Fields/FieldLabel.tsx index b0b18c4e96..637873e38c 100644 --- a/CommonUI/src/Components/Forms/Fields/FieldLabel.tsx +++ b/CommonUI/src/Components/Forms/Fields/FieldLabel.tsx @@ -8,6 +8,7 @@ export interface ComponentProps { sideLink?: FormFieldSideLink | undefined; description?: string | ReactElement | undefined; isHeading?: boolean | undefined; + hideOptionalLabel?: boolean | undefined; } const FieldLabelElement: FunctionComponent = ( @@ -23,7 +24,7 @@ const FieldLabelElement: FunctionComponent = ( {props.title}{" "} - {props.required ? "" : "(Optional)"} + {props.required || props.hideOptionalLabel ? "" : "(Optional)"} {props.sideLink && props.sideLink?.text && props.sideLink?.url && ( diff --git a/CommonUI/src/Components/Forms/Fields/FormField.tsx b/CommonUI/src/Components/Forms/Fields/FormField.tsx index 37096901a2..f4b645383c 100644 --- a/CommonUI/src/Components/Forms/Fields/FormField.tsx +++ b/CommonUI/src/Components/Forms/Fields/FormField.tsx @@ -1,9 +1,11 @@ +import Dictionary from "Common/Types/Dictionary"; import { GetReactElementFunction } from "../../../Types/FunctionTypes"; import CategoryCheckbox from "../../CategoryCheckbox/Index"; import CheckboxElement, { CategoryCheckboxValue, } from "../../Checkbox/Checkbox"; import CodeEditor from "../../CodeEditor/CodeEditor"; +import DictionaryForm from "../../Dictionary/Dictionary"; import Dropdown, { DropdownValue } from "../../Dropdown/Dropdown"; import FilePicker from "../../FilePicker/FilePicker"; import Input, { InputType } from "../../Input/Input"; @@ -238,6 +240,7 @@ const FormField: ( description={getFieldDescription()} sideLink={props.field.sideLink} required={required} + hideOptionalLabel={props.field.hideOptionalLabel} isHeading={props.field.styleType === FormFieldStyleType.Heading} /> )} @@ -317,6 +320,26 @@ const FormField: ( /> )} + {props.field.fieldType === FormFieldSchemaType.Dictionary && ( + ) => { + props.field.onChange && props.field.onChange(value); + props.setFieldValue(props.fieldName, value); + }} + /> + )} + {props.field.fieldType === FormFieldSchemaType.RadioButton && ( { // set this to true if you want to show this field in the form even when the form is in edit mode. doNotShowWhenEditing?: boolean | undefined; doNotShowWhenCreating?: boolean | undefined; + + // + jsonKeysForDictionary?: Array | undefined; + + hideOptionalLabel?: boolean | undefined; } diff --git a/CommonUI/src/Components/Forms/Types/FormFieldSchemaType.ts b/CommonUI/src/Components/Forms/Types/FormFieldSchemaType.ts index 057d5afbfc..0dd1e585aa 100644 --- a/CommonUI/src/Components/Forms/Types/FormFieldSchemaType.ts +++ b/CommonUI/src/Components/Forms/Types/FormFieldSchemaType.ts @@ -34,6 +34,7 @@ enum FormFieldSchemaType { CustomComponent = "CustomComponent", Checkbox = "Checkbox", CategoryCheckbox = "CategoryCheckbox", + Dictionary = "Dictionary", } export default FormFieldSchemaType; diff --git a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx index 00a5b80aeb..28d0a0ab44 100644 --- a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx +++ b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx @@ -5,6 +5,7 @@ import BasicForm from "CommonUI/src/Components/Forms/BasicForm"; import LogSeverity from "Common/Types/Log/LogSeverity"; import DropdownUtil from "CommonUI/src/Utils/Dropdown"; import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType"; +import Button, { ButtonStyleType } from "CommonUI/src/Components/Button/Button"; export interface ComponentProps { monitorStepLogMonitor: MonitorStepLogMonitor; @@ -18,111 +19,141 @@ export interface ComponentProps { const LogMonitorStepForm: FunctionComponent = ( props: ComponentProps, ): ReactElement => { + const [showAdvancedOptions, setShowAdvancedOptions] = React.useState(false); + return ( - + { + return showAdvancedOptions; }, - { - label: "Last 30 seconds", - value: 30, - }, - { - label: "Last 1 minute", - value: 60, - }, - { - label: "Last 5 minutes", - value: 300, - }, - { - label: "Last 15 minutes", - value: 900, - }, - { - label: "Last 30 minutes", - value: 1800, - }, - { - label: "Last 1 hour", - value: 3600, - }, - { - label: "Last 6 hours", - value: 21600, - }, - { - label: "Last 12 hours", - value: 43200, - }, - { - label: "Last 24 hours", - value: 86400, - }, - ], - title: "Monitor Logs for", - isAdvancedFilter: true, - }, - { - field: { - severityTexts: true, }, - filterDropdownOptions: - DropdownUtil.getDropdownOptionsFromEnum(LogSeverity), - type: FormFieldSchemaType.MultiSelectDropdown, - title: "Log Severity", - isAdvancedFilter: true, - }, - { - field: { - telemetryServiceIds: true, - }, - type: FormFieldSchemaType.MultiSelectDropdown, - filterDropdownOptions: props.telemetryServices.map( - (telemetryService: TelemetryService) => { - return { - label: telemetryService.name!, - value: telemetryService.id?.toString() || "", - }; + { + field: { + telemetryServiceIds: true, + }, + fieldType: FormFieldSchemaType.MultiSelectDropdown, + dropdownOptions: props.telemetryServices.map( + (telemetryService: TelemetryService) => { + return { + label: telemetryService.name!, + value: telemetryService.id?.toString() || "", + }; + }, + ), + title: "Filter by Telemetry Service", + hideOptionalLabel: true, + showIf: () => { + return showAdvancedOptions; }, - ), - title: "Filter by Telemetry Service", - isAdvancedFilter: true, - }, - { - field: { - attributes: true, }, - type: FormFieldSchemaType.JSON, - title: "Filter by Attributes", - jsonKeys: props.attributeKeys, - isAdvancedFilter: true, - }, - ]} - /> + { + field: { + attributes: true, + }, + fieldType: FormFieldSchemaType.Dictionary, + title: "Filter by Attributes", + jsonKeysForDictionary: props.attributeKeys, + hideOptionalLabel: true, + showIf: () => { + return showAdvancedOptions; + }, + }, + ]} + /> + +
); }; diff --git a/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx b/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx index 300578f064..6eb6904916 100644 --- a/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx +++ b/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx @@ -506,13 +506,6 @@ const MonitorStepElement: FunctionComponent = ( {props.monitorType === MonitorType.Logs && (
- ; @@ -43,6 +45,7 @@ export interface LogMonitorStepView { severityTexts: Array | undefined; attributes: JSONObject | undefined; telemetryServices: Array | undefined; + lastXSecondsOfLogs: number | undefined; } const MonitorStepElement: FunctionComponent = ( @@ -63,6 +66,7 @@ const MonitorStepElement: FunctionComponent = ( severityTexts: undefined, attributes: undefined, telemetryServices: undefined, + lastXSecondsOfLogs: undefined, }; const fetchTelemetryServices: PromiseVoidFunction = @@ -249,6 +253,28 @@ const MonitorStepElement: FunctionComponent = ( }); } + if (props.monitorStep.data?.logMonitor?.lastXSecondsOfLogs) { + logMonitorDetailView.lastXSecondsOfLogs = + props.monitorStep.data?.logMonitor?.lastXSecondsOfLogs; + + logFields.push({ + key: "lastXSecondsOfLogs", + title: "Monitor logs for the last (time)", + description: "How many seconds of logs to monitor.", + fieldType: FieldType.Element, + placeholder: "1 minute", + getElement: (item: LogMonitorStepView): ReactElement => { + return ( +

+ {OneUptimeDate.convertSecondsToDaysHoursMinutesAndSeconds( + item.lastXSecondsOfLogs || 0, + )} +

+ ); + }, + }); + } + if (props.monitorStep.data?.logMonitor?.severityTexts) { logMonitorDetailView.severityTexts = props.monitorStep.data?.logMonitor?.severityTexts; @@ -292,6 +318,11 @@ const MonitorStepElement: FunctionComponent = ( description: "Telemetry services to monitor.", fieldType: FieldType.Element, placeholder: "No telemetry services entered", + getElement: (): ReactElement => { + return ( + + ); + }, }); } } @@ -307,14 +338,14 @@ const MonitorStepElement: FunctionComponent = ( isHeading={true} />
- {fields && fields.length && ( + {fields && fields.length > 0 && ( id={"monitor-step"} item={props.monitorStep.data!} fields={fields} /> )} - {logFields && logFields.length && ( + {logFields && logFields.length > 0 && ( id={"monitor-logs"} item={logMonitorDetailView} diff --git a/Dashboard/src/Components/TelemetryService/TelemetryServiceElements.tsx b/Dashboard/src/Components/TelemetryService/TelemetryServiceElements.tsx new file mode 100644 index 0000000000..6f2343817b --- /dev/null +++ b/Dashboard/src/Components/TelemetryService/TelemetryServiceElements.tsx @@ -0,0 +1,31 @@ +import TelemetryServiceElement from "./TelemetryServiceElement"; +import TableColumnListComponent from "CommonUI/src/Components/TableColumnList/TableColumnListComponent"; +import TelemetryService from "Common/Models/DatabaseModels/TelemetryService"; +import React, { FunctionComponent, ReactElement } from "react"; + +export interface ComponentProps { + telemetryServices: Array; + onNavigateComplete?: (() => void) | undefined; +} + +const TelemetryServicesElement: FunctionComponent = ( + props: ComponentProps, +): ReactElement => { + return ( + { + return ( + + ); + }} + noItemsMessage="No services." + /> + ); +}; + +export default TelemetryServicesElement; From 725a58d5fb9045b0646664299830c4b9472e8a7c Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 6 Aug 2024 17:47:14 -0600 Subject: [PATCH 07/10] refactor: Update import statements for ProbeMonitor and ServerMonitor to use ProbeMonitorResponse and ServerMonitorResponse --- Common/Types/Monitor/MonitorStepLogMonitor.ts | 50 +++++++++- CommonUI/src/Utils/File.ts | 9 +- .../Monitor/LogMonitor/LogMonitorStepFrom.tsx | 95 +++++++++++++++---- .../Components/Form/Monitor/MonitorStep.tsx | 19 ++-- Dashboard/src/Components/Logs/LogsViewer.tsx | 47 ++++++--- Dashboard/src/Pages/Monitor/SideMenu.tsx | 32 +++---- 6 files changed, 192 insertions(+), 60 deletions(-) diff --git a/Common/Types/Monitor/MonitorStepLogMonitor.ts b/Common/Types/Monitor/MonitorStepLogMonitor.ts index 2c55a50c0c..c60bcb36bd 100644 --- a/Common/Types/Monitor/MonitorStepLogMonitor.ts +++ b/Common/Types/Monitor/MonitorStepLogMonitor.ts @@ -1,3 +1,9 @@ +import Log from "../../Models/AnalyticsModels/Log"; +import InBetween from "../BaseDatabase/InBetween"; +import Includes from "../BaseDatabase/Includes"; +import Query from "../BaseDatabase/Query"; +import Search from "../BaseDatabase/Search"; +import OneUptimeDate from "../Date"; import Dictionary from "../Dictionary"; import { JSONObject } from "../JSON"; import LogSeverity from "../Log/LogSeverity"; @@ -12,6 +18,48 @@ export default interface MonitorStepLogMonitor { } export class MonitorStepLogMonitorUtil { + + public static toQuery(monitorStepLogMonitor: MonitorStepLogMonitor): Query { + + let query: Query = {}; + + if ( + monitorStepLogMonitor.telemetryServiceIds && + monitorStepLogMonitor.telemetryServiceIds.length > 0 + ) { + query.serviceId = new Includes(monitorStepLogMonitor.telemetryServiceIds); + } + + if ( + monitorStepLogMonitor.attributes && + Object.keys(monitorStepLogMonitor.attributes).length > 0 + ) { + query.attributes = monitorStepLogMonitor.attributes; + } + + if ( + monitorStepLogMonitor.severityTexts && + monitorStepLogMonitor.severityTexts.length > 0 + ) { + query.severityText = new Includes(monitorStepLogMonitor.severityTexts); + } + + if (monitorStepLogMonitor.body) { + query.body = new Search(monitorStepLogMonitor.body); + } + + if (monitorStepLogMonitor.lastXSecondsOfLogs) { + const endDate: Date = OneUptimeDate.getCurrentDate(); + const startDate: Date = OneUptimeDate.addRemoveSeconds( + endDate, + monitorStepLogMonitor.lastXSecondsOfLogs * -1, + ); + query.time = new InBetween(startDate, endDate); + } + + return query; + } + public static getDefault(): MonitorStepLogMonitor { return { attributes: {}, @@ -40,7 +88,7 @@ export class MonitorStepLogMonitorUtil { attributes: monitor.attributes, body: monitor.body, severityTexts: monitor.severityTexts, - telemetryServiceId: ObjectID.toJSONArray(monitor.telemetryServiceIds), + telemetryServiceIds: ObjectID.toJSONArray(monitor.telemetryServiceIds), lastXSecondsOfLogs: monitor.lastXSecondsOfLogs, }; } diff --git a/CommonUI/src/Utils/File.ts b/CommonUI/src/Utils/File.ts index 577b85f0b3..67ec7cda4d 100644 --- a/CommonUI/src/Utils/File.ts +++ b/CommonUI/src/Utils/File.ts @@ -1,10 +1,11 @@ -import { FILE_URL } from "../Config"; -import URL from "Common/Types/API/URL"; +import Route from "Common/Types/API/Route"; import ObjectID from "Common/Types/ObjectID"; +import { FileRoute } from "Common/ServiceRoute"; export default class FileUtil { - public static getFileURL(fileId: ObjectID): URL { - return URL.fromString(FILE_URL.toString()) + public static getFileRoute(fileId: ObjectID): Route { + + return Route.fromString(FileRoute.toString()) .addRoute("/image") .addRoute(`/${fileId.toString()}`); } diff --git a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx index 28d0a0ab44..77f6179e1f 100644 --- a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx +++ b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx @@ -1,11 +1,16 @@ -import MonitorStepLogMonitor from "Common/Types/Monitor/MonitorStepLogMonitor"; +import MonitorStepLogMonitor, { MonitorStepLogMonitorUtil } from "Common/Types/Monitor/MonitorStepLogMonitor"; import TelemetryService from "Common/Models/DatabaseModels/TelemetryService"; -import React, { FunctionComponent, ReactElement } from "react"; +import React, { FunctionComponent, ReactElement, useEffect } from "react"; import BasicForm from "CommonUI/src/Components/Forms/BasicForm"; import LogSeverity from "Common/Types/Log/LogSeverity"; import DropdownUtil from "CommonUI/src/Utils/Dropdown"; import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType"; import Button, { ButtonStyleType } from "CommonUI/src/Components/Button/Button"; +import FieldLabelElement from "CommonUI/src/Components/Forms/Fields/FieldLabel"; +import DashboardLogsViewer from "../../../Logs/LogsViewer"; +import Query from "Common/Types/BaseDatabase/Query"; +import Log from "Common/Models/AnalyticsModels/Log"; +import HorizontalRule from "CommonUI/src/Components/HorizontalRule/HorizontalRule"; export interface ComponentProps { monitorStepLogMonitor: MonitorStepLogMonitor; @@ -19,15 +24,43 @@ export interface ComponentProps { const LogMonitorStepForm: FunctionComponent = ( props: ComponentProps, ): ReactElement => { - const [showAdvancedOptions, setShowAdvancedOptions] = React.useState(false); + const [monitorStepLogMonitor, setMonitorStepLogMonitor] = + React.useState(props.monitorStepLogMonitor); + + let showAdvancedOptionsByDefault = false; + + if ( + monitorStepLogMonitor.attributes || + monitorStepLogMonitor.severityTexts || + monitorStepLogMonitor.telemetryServiceIds + ) { + showAdvancedOptionsByDefault = true; + } + + const [showAdvancedOptions, setShowAdvancedOptions] = React.useState( + showAdvancedOptionsByDefault, + ); + + const refreshQuery = (): Query => { + return MonitorStepLogMonitorUtil.toQuery(monitorStepLogMonitor); + }; + + const [logQuery, setLogQuery] = React.useState>(refreshQuery()); + + useEffect(() => { + setLogQuery(refreshQuery()); + }, [monitorStepLogMonitor]); return (
{ + setMonitorStepLogMonitor(values); + props.onMonitorStepLogMonitorChanged(values); + }} fields={[ { field: { @@ -35,6 +68,8 @@ const LogMonitorStepForm: FunctionComponent = ( }, fieldType: FormFieldSchemaType.Text, title: "Monitor Logs that include this text", + description: + "This monitor will filter all the logs that include this text.", hideOptionalLabel: true, }, { @@ -91,7 +126,7 @@ const LogMonitorStepForm: FunctionComponent = ( ], title: "Monitor Logs for (time)", description: - "Select the time interval for which you want to monitor logs.", + "We will fetch all the logs that were generated in the last X time.", hideOptionalLabel: true, }, { @@ -102,6 +137,7 @@ const LogMonitorStepForm: FunctionComponent = ( DropdownUtil.getDropdownOptionsFromEnum(LogSeverity), fieldType: FormFieldSchemaType.MultiSelectDropdown, title: "Log Severity", + description: "Select the severity of the logs you want to monitor.", hideOptionalLabel: true, showIf: () => { return showAdvancedOptions; @@ -121,6 +157,7 @@ const LogMonitorStepForm: FunctionComponent = ( }, ), title: "Filter by Telemetry Service", + description: "Select the telemetry services you want to monitor.", hideOptionalLabel: true, showIf: () => { return showAdvancedOptions; @@ -133,6 +170,8 @@ const LogMonitorStepForm: FunctionComponent = ( fieldType: FormFieldSchemaType.Dictionary, title: "Filter by Attributes", jsonKeysForDictionary: props.attributeKeys, + description: + "You can filter the logs based on the attributes that are attached to the logs.", hideOptionalLabel: true, showIf: () => { return showAdvancedOptions; @@ -140,19 +179,37 @@ const LogMonitorStepForm: FunctionComponent = ( }, ]} /> - -
+
+ + +
+ +
+
); }; diff --git a/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx b/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx index 6eb6904916..84192f20b1 100644 --- a/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx +++ b/Dashboard/src/Components/Form/Monitor/MonitorStep.tsx @@ -618,14 +618,17 @@ const MonitorStepElement: FunctionComponent = (
{props.monitorType !== MonitorType.IncomingRequest && ( - + <> + + + )} | undefined; showFilters?: boolean | undefined; noLogsMessage?: string | undefined; + logQuery?: Query | undefined; + limit?: number | undefined; } const DashboardLogsViewer: FunctionComponent = ( props: ComponentProps, ): ReactElement => { - const query: Query = {}; + const refreshQuery = (): Query => { + let query: Query = {}; - if (props.telemetryServiceIds && props.telemetryServiceIds.length > 0) { - query.serviceId = new Includes(props.telemetryServiceIds); - } + if (props.telemetryServiceIds && props.telemetryServiceIds.length > 0) { + query.serviceId = new Includes(props.telemetryServiceIds); + } - if (props.traceIds && props.traceIds.length > 0) { - query.traceId = new Includes(props.traceIds); - } + if (props.traceIds && props.traceIds.length > 0) { + query.traceId = new Includes(props.traceIds); + } - if (props.spanIds && props.spanIds.length > 0) { - query.spanId = new Includes(props.spanIds); - } + if (props.spanIds && props.spanIds.length > 0) { + query.spanId = new Includes(props.spanIds); + } + + if (props.logQuery) { + query = { + ...query, + ...props.logQuery, + }; + } + + return query; + }; const [logs, setLogs] = React.useState>([]); const [error, setError] = React.useState(""); const [isLoading, setIsLoading] = React.useState(false); - const [filterOptions, setFilterOptions] = React.useState>(query); + const [filterOptions, setFilterOptions] = + React.useState>(refreshQuery()); const select: Select = { body: true, @@ -72,6 +86,15 @@ const DashboardLogsViewer: FunctionComponent = ( }); }, [filterOptions]); + useEffect(() => { + setFilterOptions(refreshQuery()); + }, [ + props.telemetryServiceIds, + props.traceIds, + props.spanIds, + props.logQuery, + ]); + const fetchItems: PromiseVoidFunction = async (): Promise => { setError(""); setIsLoading(true); @@ -80,7 +103,7 @@ const DashboardLogsViewer: FunctionComponent = ( const listResult: ListResult = await AnalyticsModelAPI.getList({ modelType: Log, query: getQuery(), - limit: LIMIT_PER_PROJECT, + limit: props.limit || LIMIT_PER_PROJECT, skip: 0, select: select, sort: { diff --git a/Dashboard/src/Pages/Monitor/SideMenu.tsx b/Dashboard/src/Pages/Monitor/SideMenu.tsx index e0fb5b043d..59d6ff79b6 100644 --- a/Dashboard/src/Pages/Monitor/SideMenu.tsx +++ b/Dashboard/src/Pages/Monitor/SideMenu.tsx @@ -48,24 +48,24 @@ const DashboardSideMenu: FunctionComponent = ( }, }} /> - - {props.project?.isFeatureFlagMonitorGroupsEnabled ? ( - - - - ) : ( - <> - )} + {props.project?.isFeatureFlagMonitorGroupsEnabled ? ( + + + + ) : ( + <> + )} + link={{ From 6fd43622a321cf01610b501411cab74979e5df3c Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 6 Aug 2024 17:47:18 -0600 Subject: [PATCH 08/10] refactor: Update import statements for UserProfile component to use FileUtil.getFileRoute instead of FileUtil.getFileURL --- Dashboard/src/Components/Header/UserProfile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dashboard/src/Components/Header/UserProfile.tsx b/Dashboard/src/Components/Header/UserProfile.tsx index 64c66a2222..18a2ed60e3 100644 --- a/Dashboard/src/Components/Header/UserProfile.tsx +++ b/Dashboard/src/Components/Header/UserProfile.tsx @@ -62,7 +62,7 @@ const DashboardUserProfile: FunctionComponent = ( Date: Tue, 6 Aug 2024 17:57:27 -0600 Subject: [PATCH 09/10] refactor: Update import statements for UserProfile component to use FileUtil.getFileRoute instead of FileUtil.getFileURL --- Common/Types/Monitor/MonitorStepLogMonitor.ts | 8 ++++---- CommonUI/src/Utils/File.ts | 1 - .../Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx | 12 ++++++++---- Dashboard/src/Components/Logs/LogsViewer.tsx | 4 +++- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Common/Types/Monitor/MonitorStepLogMonitor.ts b/Common/Types/Monitor/MonitorStepLogMonitor.ts index c60bcb36bd..d6ce80860e 100644 --- a/Common/Types/Monitor/MonitorStepLogMonitor.ts +++ b/Common/Types/Monitor/MonitorStepLogMonitor.ts @@ -18,10 +18,10 @@ export default interface MonitorStepLogMonitor { } export class MonitorStepLogMonitorUtil { - - public static toQuery(monitorStepLogMonitor: MonitorStepLogMonitor): Query { - - let query: Query = {}; + public static toQuery( + monitorStepLogMonitor: MonitorStepLogMonitor, + ): Query { + const query: Query = {}; if ( monitorStepLogMonitor.telemetryServiceIds && diff --git a/CommonUI/src/Utils/File.ts b/CommonUI/src/Utils/File.ts index 67ec7cda4d..95839a633e 100644 --- a/CommonUI/src/Utils/File.ts +++ b/CommonUI/src/Utils/File.ts @@ -4,7 +4,6 @@ import { FileRoute } from "Common/ServiceRoute"; export default class FileUtil { public static getFileRoute(fileId: ObjectID): Route { - return Route.fromString(FileRoute.toString()) .addRoute("/image") .addRoute(`/${fileId.toString()}`); diff --git a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx index 77f6179e1f..7188ff8331 100644 --- a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx +++ b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx @@ -1,4 +1,6 @@ -import MonitorStepLogMonitor, { MonitorStepLogMonitorUtil } from "Common/Types/Monitor/MonitorStepLogMonitor"; +import MonitorStepLogMonitor, { + MonitorStepLogMonitorUtil, +} from "Common/Types/Monitor/MonitorStepLogMonitor"; import TelemetryService from "Common/Models/DatabaseModels/TelemetryService"; import React, { FunctionComponent, ReactElement, useEffect } from "react"; import BasicForm from "CommonUI/src/Components/Forms/BasicForm"; @@ -27,7 +29,7 @@ const LogMonitorStepForm: FunctionComponent = ( const [monitorStepLogMonitor, setMonitorStepLogMonitor] = React.useState(props.monitorStepLogMonitor); - let showAdvancedOptionsByDefault = false; + let showAdvancedOptionsByDefault: boolean = false; if ( monitorStepLogMonitor.attributes || @@ -41,8 +43,10 @@ const LogMonitorStepForm: FunctionComponent = ( showAdvancedOptionsByDefault, ); - const refreshQuery = (): Query => { - return MonitorStepLogMonitorUtil.toQuery(monitorStepLogMonitor); + type RefreshQueryFunction = () => Query; + + const refreshQuery: RefreshQueryFunction = (): Query => { + return MonitorStepLogMonitorUtil.toQuery(monitorStepLogMonitor); }; const [logQuery, setLogQuery] = React.useState>(refreshQuery()); diff --git a/Dashboard/src/Components/Logs/LogsViewer.tsx b/Dashboard/src/Components/Logs/LogsViewer.tsx index 9d858996a1..de57806907 100644 --- a/Dashboard/src/Components/Logs/LogsViewer.tsx +++ b/Dashboard/src/Components/Logs/LogsViewer.tsx @@ -32,7 +32,9 @@ export interface ComponentProps { const DashboardLogsViewer: FunctionComponent = ( props: ComponentProps, ): ReactElement => { - const refreshQuery = (): Query => { + type RefreshQueryFunction = () => Query; + + const refreshQuery: RefreshQueryFunction = (): Query => { let query: Query = {}; if (props.telemetryServiceIds && props.telemetryServiceIds.length > 0) { From 524863dff394f33ef6929df9e383f67802cb9142 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 6 Aug 2024 19:38:47 -0600 Subject: [PATCH 10/10] refactor: Update import statements for UserProfile component to use FileUtil.getFileRoute instead of FileUtil.getFileURL --- .../Home/Views/Partials/copilot.ejs | 2 +- Common/Types/Monitor/MonitorStep.ts | 18 ++++---- .../Monitor/LogMonitor/LogMonitorStepFrom.tsx | 28 ++----------- .../Monitor/LogMonitor/LogMonitorPreview.tsx | 41 +++++++++++++++++++ Dashboard/src/Pages/Monitor/View/Index.tsx | 23 +++++++++++ 5 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 Dashboard/src/Components/Monitor/LogMonitor/LogMonitorPreview.tsx diff --git a/App/FeatureSet/Home/Views/Partials/copilot.ejs b/App/FeatureSet/Home/Views/Partials/copilot.ejs index b86d998c80..eb2f82583f 100644 --- a/App/FeatureSet/Home/Views/Partials/copilot.ejs +++ b/App/FeatureSet/Home/Views/Partials/copilot.ejs @@ -111,7 +111,7 @@ - We don't store or train on your code. + We don't see, store or train on your code. Regardless whether you are on the free tier or enterprise tier, we don't store or train on your code. No part of your code is sent to us. diff --git a/Common/Types/Monitor/MonitorStep.ts b/Common/Types/Monitor/MonitorStep.ts index 86358f4472..5b803a1526 100644 --- a/Common/Types/Monitor/MonitorStep.ts +++ b/Common/Types/Monitor/MonitorStep.ts @@ -196,16 +196,6 @@ export default class MonitorStep extends DatabaseProperty { return "Monitor Destination is required."; } - if (monitorType === MonitorType.Logs) { - if (!value.data.logMonitor) { - return "Log Monitor is required"; - } - - if (!value.data.logMonitor.lastXSecondsOfLogs) { - return "Monitor Last Minutes of Logs is required."; - } - } - if ( !value.data.customCode && (monitorType === MonitorType.CustomJavaScriptCode || @@ -265,7 +255,9 @@ export default class MonitorStep extends DatabaseProperty { screenSizeTypes: this.data.screenSizeTypes || undefined, browserTypes: this.data.browserTypes || undefined, logMonitor: this.data.logMonitor - ? MonitorStepLogMonitorUtil.toJSON(this.data.logMonitor) + ? MonitorStepLogMonitorUtil.toJSON( + this.data.logMonitor || MonitorStepLogMonitorUtil.getDefault(), + ) : undefined, }, }); @@ -360,6 +352,10 @@ export default class MonitorStep extends DatabaseProperty { : undefined, }) as any; + if (monitorStep.data && !monitorStep.data?.logMonitor) { + monitorStep.data.logMonitor = MonitorStepLogMonitorUtil.getDefault(); + } + return monitorStep; } diff --git a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx index 7188ff8331..dc9572002f 100644 --- a/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx +++ b/Dashboard/src/Components/Form/Monitor/LogMonitor/LogMonitorStepFrom.tsx @@ -1,18 +1,14 @@ -import MonitorStepLogMonitor, { - MonitorStepLogMonitorUtil, -} from "Common/Types/Monitor/MonitorStepLogMonitor"; +import MonitorStepLogMonitor from "Common/Types/Monitor/MonitorStepLogMonitor"; import TelemetryService from "Common/Models/DatabaseModels/TelemetryService"; -import React, { FunctionComponent, ReactElement, useEffect } from "react"; +import React, { FunctionComponent, ReactElement } from "react"; import BasicForm from "CommonUI/src/Components/Forms/BasicForm"; import LogSeverity from "Common/Types/Log/LogSeverity"; import DropdownUtil from "CommonUI/src/Utils/Dropdown"; import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType"; import Button, { ButtonStyleType } from "CommonUI/src/Components/Button/Button"; import FieldLabelElement from "CommonUI/src/Components/Forms/Fields/FieldLabel"; -import DashboardLogsViewer from "../../../Logs/LogsViewer"; -import Query from "Common/Types/BaseDatabase/Query"; -import Log from "Common/Models/AnalyticsModels/Log"; import HorizontalRule from "CommonUI/src/Components/HorizontalRule/HorizontalRule"; +import LogMonitorPreview from "../../../Monitor/LogMonitor/LogMonitorPreview"; export interface ComponentProps { monitorStepLogMonitor: MonitorStepLogMonitor; @@ -43,18 +39,6 @@ const LogMonitorStepForm: FunctionComponent = ( showAdvancedOptionsByDefault, ); - type RefreshQueryFunction = () => Query; - - const refreshQuery: RefreshQueryFunction = (): Query => { - return MonitorStepLogMonitorUtil.toQuery(monitorStepLogMonitor); - }; - - const [logQuery, setLogQuery] = React.useState>(refreshQuery()); - - useEffect(() => { - setLogQuery(refreshQuery()); - }, [monitorStepLogMonitor]); - return (
= ( isHeading={true} />
- +
diff --git a/Dashboard/src/Components/Monitor/LogMonitor/LogMonitorPreview.tsx b/Dashboard/src/Components/Monitor/LogMonitor/LogMonitorPreview.tsx new file mode 100644 index 0000000000..09e7f45d9a --- /dev/null +++ b/Dashboard/src/Components/Monitor/LogMonitor/LogMonitorPreview.tsx @@ -0,0 +1,41 @@ +import React, { FunctionComponent, ReactElement, useEffect } from "react"; +import MonitorStepLogMonitor, { + MonitorStepLogMonitorUtil, +} from "Common/Types/Monitor/MonitorStepLogMonitor"; +import DashboardLogsViewer from "../../Logs/LogsViewer"; +import Query from "Common/Types/BaseDatabase/Query"; +import Log from "Common/Models/AnalyticsModels/Log"; + +export interface ComponentProps { + monitorStepLogMonitor: MonitorStepLogMonitor | undefined; +} + +const LogMonitorPreview: FunctionComponent = ( + props: ComponentProps, +): ReactElement => { + type RefreshQueryFunction = () => Query; + + const refreshQuery: RefreshQueryFunction = (): Query => { + if (!props.monitorStepLogMonitor) { + return {}; + } + return MonitorStepLogMonitorUtil.toQuery(props.monitorStepLogMonitor); + }; + + const [logQuery, setLogQuery] = React.useState>(refreshQuery()); + + useEffect(() => { + setLogQuery(refreshQuery()); + }, [props.monitorStepLogMonitor]); + + return ( + + ); +}; + +export default LogMonitorPreview; diff --git a/Dashboard/src/Pages/Monitor/View/Index.tsx b/Dashboard/src/Pages/Monitor/View/Index.tsx index 04adc36b18..ccbdeeb6d4 100644 --- a/Dashboard/src/Pages/Monitor/View/Index.tsx +++ b/Dashboard/src/Pages/Monitor/View/Index.tsx @@ -63,6 +63,7 @@ import React, { import useAsyncEffect from "use-async-effect"; import RouteMap, { RouteUtil } from "../../../Utils/RouteMap"; import PageMap from "../../../Utils/PageMap"; +import LogMonitorPreview from "../../../Components/Monitor/LogMonitor/LogMonitorPreview"; const MonitorView: FunctionComponent = (): ReactElement => { const modelId: ObjectID = Navigation.getLastParamAsObjectID(); @@ -186,6 +187,7 @@ const MonitorView: FunctionComponent = (): ReactElement => { isAllProbesDisconnectedFromThisMonitor: true, telemetryMonitorLastMonitorAt: true, telemetryMonitorNextMonitorAt: true, + monitorSteps: true, }, }); @@ -614,6 +616,27 @@ const MonitorView: FunctionComponent = (): ReactElement => { }} /> + {monitor?.monitorType === MonitorType.Logs && + monitor.monitorSteps && + monitor.monitorSteps.data?.monitorStepsInstanceArray && + monitor.monitorSteps.data?.monitorStepsInstanceArray.length > 0 && ( +
+ + + +
+ )} + {shouldFetchMonitorMetrics && getMonitorMetricsChartGroup()} );