@oneuptime/common 11.7.0 → 11.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/DashboardDomain.ts +1 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Monitor.ts +1 -0
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/NetworkSite.ts +1 -0
- package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
- package/Models/DatabaseModels/Probe.ts +13 -13
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
- package/Models/DatabaseModels/StatusPage.ts +1 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
- package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
- package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
- package/Server/API/BaseAPI.ts +17 -1
- package/Server/API/CommonAPI.ts +32 -0
- package/Server/API/MicrosoftTeamsAPI.ts +99 -2
- package/Server/API/ProbeAPI.ts +2 -0
- package/Server/API/SlackAPI.ts +62 -0
- package/Server/API/StatusPageAPI.ts +157 -192
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/DatabaseService.ts +11 -3
- package/Server/Services/MonitorGroupService.ts +145 -0
- package/Server/Services/MonitorService.ts +231 -30
- package/Server/Services/ProbeService.ts +319 -16
- package/Server/Services/StatusPageService.ts +72 -42
- package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
- package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
- package/Server/Utils/Monitor/MonitorResource.ts +88 -35
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
- package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
- package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
- package/Tests/Server/API/ProbeAPI.test.ts +6 -0
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
- package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
- package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
- package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
- package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
- package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
- package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
- package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
- package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
- package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
- package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
- package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
- package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
- package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
- package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
- package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
- package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
- package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
- package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
- package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
- package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
- package/Tests/Types/DateUserTimezone.test.ts +205 -0
- package/Tests/Types/DateUserTimezoneOverridesBrowser.test.ts +73 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
- package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
- package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
- package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
- package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
- package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
- package/Tests/UI/Components/TimePicker/TimePicker.test.tsx +11 -0
- package/Tests/UI/Utils/Project.test.ts +809 -0
- package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
- package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
- package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
- package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
- package/Types/Date.ts +119 -50
- package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
- package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
- package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
- package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
- package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
- package/UI/Components/Forms/Fields/FormField.tsx +13 -3
- package/UI/Components/Forms/ModelForm.tsx +41 -18
- package/UI/Components/Input/Input.tsx +6 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
- package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +300 -30
- package/UI/Components/ModelTable/Column.ts +12 -0
- package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
- package/UI/Components/Navbar/NavBar.tsx +66 -1
- package/UI/Components/Table/Table.tsx +52 -13
- package/UI/Components/Table/Types/Column.ts +18 -0
- package/UI/Components/TimePicker/TimePicker.tsx +8 -4
- package/UI/Utils/Project.ts +214 -18
- package/UI/Utils/TableColumnsToCsv.ts +86 -6
- package/UI/Utils/User.ts +19 -0
- package/UI/esbuild-config.js +34 -0
- package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Probe.js +13 -13
- package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +12 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +21 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +2 -0
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +44 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +98 -152
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +10 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/MonitorGroupService.js +118 -0
- package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +198 -24
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +238 -9
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +46 -39
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Date.js +106 -35
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +6 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +234 -21
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +31 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/TimePicker/TimePicker.js +4 -4
- package/build/dist/UI/Components/TimePicker/TimePicker.js.map +1 -1
- package/build/dist/UI/Utils/Project.js +161 -15
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
- package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
- package/build/dist/UI/Utils/User.js +19 -0
- package/build/dist/UI/Utils/User.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
- package/package.json +4 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.ts +0 -79
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.ts +0 -41
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +0 -17
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js +0 -34
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js.map +0 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js +0 -22
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js.map +0 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +0 -12
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +0 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import StatusPageResourceUptimeUtil from "../../../Utils/StatusPage/ResourceUptime";
|
|
2
|
+
import { Green, Red, Yellow } from "../../../Types/BrandColors";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
5
|
+
import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
|
|
6
|
+
import MonitorStatusTimeline from "../../../Models/DatabaseModels/MonitorStatusTimeline";
|
|
7
|
+
import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResource";
|
|
8
|
+
import StatusPageGroup from "../../../Models/DatabaseModels/StatusPageGroup";
|
|
9
|
+
|
|
10
|
+
const MONITOR_A: ObjectID = new ObjectID(
|
|
11
|
+
"11111111-1111-4111-8111-111111111111",
|
|
12
|
+
);
|
|
13
|
+
const MONITOR_B: ObjectID = new ObjectID(
|
|
14
|
+
"22222222-2222-4222-8222-222222222222",
|
|
15
|
+
);
|
|
16
|
+
const MONITOR_C: ObjectID = new ObjectID(
|
|
17
|
+
"33333333-3333-4333-8333-333333333333",
|
|
18
|
+
);
|
|
19
|
+
const GROUP_ONE: ObjectID = new ObjectID(
|
|
20
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
21
|
+
);
|
|
22
|
+
const GROUP_TWO: ObjectID = new ObjectID(
|
|
23
|
+
"bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
function makeStatus(data: {
|
|
27
|
+
name: string;
|
|
28
|
+
priority: number;
|
|
29
|
+
color: typeof Green;
|
|
30
|
+
}): MonitorStatus {
|
|
31
|
+
const status: MonitorStatus = new MonitorStatus();
|
|
32
|
+
status.name = data.name;
|
|
33
|
+
status.priority = data.priority;
|
|
34
|
+
status.color = data.color;
|
|
35
|
+
return status;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function makeResourceForMonitor(monitorId: ObjectID): StatusPageResource {
|
|
39
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
40
|
+
resource.monitorId = monitorId;
|
|
41
|
+
return resource;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function makeTimelineForMonitor(monitorId: ObjectID): MonitorStatusTimeline {
|
|
45
|
+
const timeline: MonitorStatusTimeline = new MonitorStatusTimeline();
|
|
46
|
+
timeline.monitorId = monitorId;
|
|
47
|
+
return timeline;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("StatusPageResourceUptimeUtil", () => {
|
|
51
|
+
describe("getWorstMonitorStatus", () => {
|
|
52
|
+
test("defaults to Operational when there are no statuses", () => {
|
|
53
|
+
const worst: MonitorStatus =
|
|
54
|
+
StatusPageResourceUptimeUtil.getWorstMonitorStatus({
|
|
55
|
+
monitorStatuses: [],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(worst.name).toBe("Operational");
|
|
59
|
+
expect(worst.color).toEqual(Green);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("returns the highest priority (worst) status", () => {
|
|
63
|
+
const operational: MonitorStatus = makeStatus({
|
|
64
|
+
name: "Operational",
|
|
65
|
+
priority: 1,
|
|
66
|
+
color: Green,
|
|
67
|
+
});
|
|
68
|
+
const degraded: MonitorStatus = makeStatus({
|
|
69
|
+
name: "Degraded",
|
|
70
|
+
priority: 2,
|
|
71
|
+
color: Yellow,
|
|
72
|
+
});
|
|
73
|
+
const offline: MonitorStatus = makeStatus({
|
|
74
|
+
name: "Offline",
|
|
75
|
+
priority: 3,
|
|
76
|
+
color: Red,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const worst: MonitorStatus =
|
|
80
|
+
StatusPageResourceUptimeUtil.getWorstMonitorStatus({
|
|
81
|
+
monitorStatuses: [operational, offline, degraded],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(worst.name).toBe("Offline");
|
|
85
|
+
expect(worst.priority).toBe(3);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("a single status wins over the operational default", () => {
|
|
89
|
+
const degraded: MonitorStatus = makeStatus({
|
|
90
|
+
name: "Degraded",
|
|
91
|
+
priority: 5,
|
|
92
|
+
color: Yellow,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const worst: MonitorStatus =
|
|
96
|
+
StatusPageResourceUptimeUtil.getWorstMonitorStatus({
|
|
97
|
+
monitorStatuses: [degraded],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(worst.name).toBe("Degraded");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("getResourcesInStatusPageGroup", () => {
|
|
105
|
+
test("returns only resources whose group id matches", () => {
|
|
106
|
+
const group: StatusPageGroup = new StatusPageGroup();
|
|
107
|
+
group._id = GROUP_ONE.toString();
|
|
108
|
+
|
|
109
|
+
const inGroup: StatusPageResource = new StatusPageResource();
|
|
110
|
+
inGroup.statusPageGroupId = GROUP_ONE;
|
|
111
|
+
|
|
112
|
+
const otherGroup: StatusPageResource = new StatusPageResource();
|
|
113
|
+
otherGroup.statusPageGroupId = GROUP_TWO;
|
|
114
|
+
|
|
115
|
+
const noGroup: StatusPageResource = new StatusPageResource();
|
|
116
|
+
|
|
117
|
+
const result: Array<StatusPageResource> =
|
|
118
|
+
StatusPageResourceUptimeUtil.getResourcesInStatusPageGroup({
|
|
119
|
+
statusPageGroup: group,
|
|
120
|
+
statusPageResources: [inGroup, otherGroup, noGroup],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(result).toHaveLength(1);
|
|
124
|
+
expect(result[0]).toBe(inGroup);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("returns an empty array when nothing matches", () => {
|
|
128
|
+
const group: StatusPageGroup = new StatusPageGroup();
|
|
129
|
+
group._id = GROUP_ONE.toString();
|
|
130
|
+
|
|
131
|
+
const otherGroup: StatusPageResource = new StatusPageResource();
|
|
132
|
+
otherGroup.statusPageGroupId = GROUP_TWO;
|
|
133
|
+
|
|
134
|
+
expect(
|
|
135
|
+
StatusPageResourceUptimeUtil.getResourcesInStatusPageGroup({
|
|
136
|
+
statusPageGroup: group,
|
|
137
|
+
statusPageResources: [otherGroup],
|
|
138
|
+
}),
|
|
139
|
+
).toHaveLength(0);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("getResourcesWithoutStatusPageGroup", () => {
|
|
144
|
+
test("returns only ungrouped resources", () => {
|
|
145
|
+
const grouped: StatusPageResource = new StatusPageResource();
|
|
146
|
+
grouped.statusPageGroupId = GROUP_ONE;
|
|
147
|
+
|
|
148
|
+
const ungroupedA: StatusPageResource = new StatusPageResource();
|
|
149
|
+
const ungroupedB: StatusPageResource = new StatusPageResource();
|
|
150
|
+
|
|
151
|
+
const result: Array<StatusPageResource> =
|
|
152
|
+
StatusPageResourceUptimeUtil.getResourcesWithoutStatusPageGroup({
|
|
153
|
+
statusPageResources: [grouped, ungroupedA, ungroupedB],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(result).toHaveLength(2);
|
|
157
|
+
expect(result).toContain(ungroupedA);
|
|
158
|
+
expect(result).toContain(ungroupedB);
|
|
159
|
+
expect(result).not.toContain(grouped);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("grouped and ungrouped partitions are complementary", () => {
|
|
163
|
+
const group: StatusPageGroup = new StatusPageGroup();
|
|
164
|
+
group._id = GROUP_ONE.toString();
|
|
165
|
+
|
|
166
|
+
const grouped: StatusPageResource = new StatusPageResource();
|
|
167
|
+
grouped.statusPageGroupId = GROUP_ONE;
|
|
168
|
+
const ungrouped: StatusPageResource = new StatusPageResource();
|
|
169
|
+
|
|
170
|
+
const all: Array<StatusPageResource> = [grouped, ungrouped];
|
|
171
|
+
|
|
172
|
+
const withoutGroup: Array<StatusPageResource> =
|
|
173
|
+
StatusPageResourceUptimeUtil.getResourcesWithoutStatusPageGroup({
|
|
174
|
+
statusPageResources: all,
|
|
175
|
+
});
|
|
176
|
+
const inGroup: Array<StatusPageResource> =
|
|
177
|
+
StatusPageResourceUptimeUtil.getResourcesInStatusPageGroup({
|
|
178
|
+
statusPageGroup: group,
|
|
179
|
+
statusPageResources: all,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(withoutGroup.length + inGroup.length).toBe(all.length);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("getMonitorStatusTimelineForResource", () => {
|
|
187
|
+
test("filters timelines to the resource's own monitor", () => {
|
|
188
|
+
const resource: StatusPageResource = makeResourceForMonitor(MONITOR_A);
|
|
189
|
+
|
|
190
|
+
const result: Array<MonitorStatusTimeline> =
|
|
191
|
+
StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource({
|
|
192
|
+
statusPageResource: resource,
|
|
193
|
+
monitorStatusTimelines: [
|
|
194
|
+
makeTimelineForMonitor(MONITOR_A),
|
|
195
|
+
makeTimelineForMonitor(MONITOR_B),
|
|
196
|
+
makeTimelineForMonitor(MONITOR_A),
|
|
197
|
+
],
|
|
198
|
+
monitorsInGroup: {},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(result).toHaveLength(2);
|
|
202
|
+
for (const timeline of result) {
|
|
203
|
+
expect(timeline.monitorId?.toString()).toBe(MONITOR_A.toString());
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("resolves a monitor-group resource to all monitors in the group", () => {
|
|
208
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
209
|
+
resource.monitorGroupId = GROUP_ONE;
|
|
210
|
+
|
|
211
|
+
const monitorsInGroup: Dictionary<Array<ObjectID>> = {
|
|
212
|
+
[GROUP_ONE.toString()]: [MONITOR_A, MONITOR_B],
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const result: Array<MonitorStatusTimeline> =
|
|
216
|
+
StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource({
|
|
217
|
+
statusPageResource: resource,
|
|
218
|
+
monitorStatusTimelines: [
|
|
219
|
+
makeTimelineForMonitor(MONITOR_A),
|
|
220
|
+
makeTimelineForMonitor(MONITOR_B),
|
|
221
|
+
makeTimelineForMonitor(MONITOR_C),
|
|
222
|
+
],
|
|
223
|
+
monitorsInGroup,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// MONITOR_C is not part of the group, so it is excluded.
|
|
227
|
+
expect(result).toHaveLength(2);
|
|
228
|
+
const ids: Array<string> = result.map((t: MonitorStatusTimeline) => {
|
|
229
|
+
return t.monitorId!.toString();
|
|
230
|
+
});
|
|
231
|
+
expect(ids).toContain(MONITOR_A.toString());
|
|
232
|
+
expect(ids).toContain(MONITOR_B.toString());
|
|
233
|
+
expect(ids).not.toContain(MONITOR_C.toString());
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("returns nothing when the group has no known monitors", () => {
|
|
237
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
238
|
+
resource.monitorGroupId = GROUP_ONE;
|
|
239
|
+
|
|
240
|
+
const result: Array<MonitorStatusTimeline> =
|
|
241
|
+
StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource({
|
|
242
|
+
statusPageResource: resource,
|
|
243
|
+
monitorStatusTimelines: [makeTimelineForMonitor(MONITOR_A)],
|
|
244
|
+
monitorsInGroup: {},
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
expect(result).toHaveLength(0);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("returns nothing when the resource has neither monitor nor group", () => {
|
|
251
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
252
|
+
|
|
253
|
+
const result: Array<MonitorStatusTimeline> =
|
|
254
|
+
StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource({
|
|
255
|
+
statusPageResource: resource,
|
|
256
|
+
monitorStatusTimelines: [makeTimelineForMonitor(MONITOR_A)],
|
|
257
|
+
monitorsInGroup: {},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(result).toHaveLength(0);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
package/Types/Date.ts
CHANGED
|
@@ -12,6 +12,13 @@ export const Moment: typeof moment = moment;
|
|
|
12
12
|
export default class OneUptimeDate {
|
|
13
13
|
// get date time from unix timestamp
|
|
14
14
|
|
|
15
|
+
/*
|
|
16
|
+
* The timezone the signed-in user configured in User Settings, pushed in by
|
|
17
|
+
* the UI (see UserUtil.initializeUserTimezone). Null on the server and on
|
|
18
|
+
* signed-out pages, where the process / browser zone is used instead.
|
|
19
|
+
*/
|
|
20
|
+
private static userTimezone: Timezone | null = null;
|
|
21
|
+
|
|
15
22
|
private static padDatePart(value: number): string {
|
|
16
23
|
return value.toString().padStart(2, "0");
|
|
17
24
|
}
|
|
@@ -44,6 +51,7 @@ export default class OneUptimeDate {
|
|
|
44
51
|
private static getLocalShortMonthName(date: Date): string {
|
|
45
52
|
return date.toLocaleString("default", {
|
|
46
53
|
month: "short",
|
|
54
|
+
timeZone: this.getCurrentTimezone().toString(),
|
|
47
55
|
});
|
|
48
56
|
}
|
|
49
57
|
|
|
@@ -156,6 +164,19 @@ export default class OneUptimeDate {
|
|
|
156
164
|
return this.getLocalTimeString(date);
|
|
157
165
|
}
|
|
158
166
|
|
|
167
|
+
/**
|
|
168
|
+
* The hour / minute `date` reads at in the current timezone. Time pickers use
|
|
169
|
+
* these instead of Date.getHours()/getMinutes() so the digits they show are
|
|
170
|
+
* the ones the user expects in the zone they configured.
|
|
171
|
+
*/
|
|
172
|
+
public static getLocalHours(date: Date | string): number {
|
|
173
|
+
return this.inCurrentTimezone(date).hours();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public static getLocalMinutes(date: Date | string): number {
|
|
177
|
+
return this.inCurrentTimezone(date).minutes();
|
|
178
|
+
}
|
|
179
|
+
|
|
159
180
|
public static getLocalShortMonthNameFromDate(date: Date | string): string {
|
|
160
181
|
date = this.fromString(date);
|
|
161
182
|
return this.getLocalShortMonthName(date);
|
|
@@ -172,26 +193,27 @@ export default class OneUptimeDate {
|
|
|
172
193
|
|
|
173
194
|
const includeMinutes: boolean = options?.includeMinutes ?? true;
|
|
174
195
|
const includeSeconds: boolean = options?.includeSeconds ?? false;
|
|
175
|
-
const
|
|
196
|
+
const localDate: moment.Moment = this.inCurrentTimezone(date);
|
|
197
|
+
const hours: string = this.padDatePart(localDate.hours());
|
|
176
198
|
|
|
177
199
|
if (!includeMinutes) {
|
|
178
200
|
return hours;
|
|
179
201
|
}
|
|
180
202
|
|
|
181
|
-
const minutes: string = this.padDatePart(
|
|
203
|
+
const minutes: string = this.padDatePart(localDate.minutes());
|
|
182
204
|
|
|
183
205
|
if (!includeSeconds) {
|
|
184
206
|
return `${hours}:${minutes}`;
|
|
185
207
|
}
|
|
186
208
|
|
|
187
|
-
const seconds: string = this.padDatePart(
|
|
209
|
+
const seconds: string = this.padDatePart(localDate.seconds());
|
|
188
210
|
return `${hours}:${minutes}:${seconds}`;
|
|
189
211
|
}
|
|
190
212
|
|
|
191
213
|
public static getDateAsLocalDayMonthString(date: Date | string): string {
|
|
192
214
|
date = this.fromString(date);
|
|
193
215
|
|
|
194
|
-
const day: string = this.padDatePart(date.
|
|
216
|
+
const day: string = this.padDatePart(this.inCurrentTimezone(date).date());
|
|
195
217
|
const month: string = this.getLocalShortMonthName(date);
|
|
196
218
|
|
|
197
219
|
return `${day} ${month}`;
|
|
@@ -207,7 +229,7 @@ export default class OneUptimeDate {
|
|
|
207
229
|
date = this.fromString(date);
|
|
208
230
|
|
|
209
231
|
const month: string = this.getLocalShortMonthName(date);
|
|
210
|
-
const year: string = date.
|
|
232
|
+
const year: string = this.inCurrentTimezone(date).year().toString();
|
|
211
233
|
|
|
212
234
|
return `${month} ${year}`;
|
|
213
235
|
}
|
|
@@ -316,7 +338,7 @@ export default class OneUptimeDate {
|
|
|
316
338
|
: `${datePart}, ${timePart}`;
|
|
317
339
|
|
|
318
340
|
if (!data.timezone) {
|
|
319
|
-
const local: moment.Moment =
|
|
341
|
+
const local: moment.Moment = this.inCurrentTimezone(date);
|
|
320
342
|
return (
|
|
321
343
|
local.format(formatString) +
|
|
322
344
|
(data.onlyShowDate ? "" : " " + this.getCurrentTimezoneString())
|
|
@@ -333,18 +355,18 @@ export default class OneUptimeDate {
|
|
|
333
355
|
}
|
|
334
356
|
|
|
335
357
|
/**
|
|
336
|
-
* Reinterpret the wall-clock components of `date` — read in the
|
|
337
|
-
*
|
|
338
|
-
* instant. Used to store a time the user typed
|
|
339
|
-
* even though the time picker captured it in the
|
|
340
|
-
* F1). Inverse of
|
|
358
|
+
* Reinterpret the wall-clock components of `date` — read in the CURRENT
|
|
359
|
+
* timezone, i.e. the one the pickers render in — as the SAME wall-clock in
|
|
360
|
+
* `timezone`, and return that instant. Used to store a time the user typed
|
|
361
|
+
* for the schedule's timezone even though the time picker captured it in the
|
|
362
|
+
* viewer's own zone (audit F1). Inverse of
|
|
363
|
+
* getLocalDateFromWallClockInTimezone.
|
|
341
364
|
*/
|
|
342
365
|
public static getInstantFromLocalWallClockInTimezone(
|
|
343
366
|
date: Date | string,
|
|
344
367
|
timezone: string,
|
|
345
368
|
): Date {
|
|
346
|
-
|
|
347
|
-
const local: moment.Moment = moment(date);
|
|
369
|
+
const local: moment.Moment = this.inCurrentTimezone(date);
|
|
348
370
|
return moment
|
|
349
371
|
.tz(
|
|
350
372
|
{
|
|
@@ -361,10 +383,10 @@ export default class OneUptimeDate {
|
|
|
361
383
|
}
|
|
362
384
|
|
|
363
385
|
/**
|
|
364
|
-
* Inverse of getInstantFromLocalWallClockInTimezone: return a
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
386
|
+
* Inverse of getInstantFromLocalWallClockInTimezone: return a Date whose
|
|
387
|
+
* wall-clock IN THE CURRENT TIMEZONE equals `date`'s wall-clock as seen in
|
|
388
|
+
* `timezone`. Used to display a stored schedule-timezone time inside a picker
|
|
389
|
+
* that renders in the viewer's own zone (audit F1).
|
|
368
390
|
*/
|
|
369
391
|
public static getLocalDateFromWallClockInTimezone(
|
|
370
392
|
date: Date | string,
|
|
@@ -372,14 +394,19 @@ export default class OneUptimeDate {
|
|
|
372
394
|
): Date {
|
|
373
395
|
date = this.fromString(date);
|
|
374
396
|
const zoned: moment.Moment = moment.tz(date, timezone);
|
|
375
|
-
return moment
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
397
|
+
return moment
|
|
398
|
+
.tz(
|
|
399
|
+
{
|
|
400
|
+
year: zoned.year(),
|
|
401
|
+
month: zoned.month(),
|
|
402
|
+
day: zoned.date(),
|
|
403
|
+
hour: zoned.hour(),
|
|
404
|
+
minute: zoned.minute(),
|
|
405
|
+
second: zoned.second(),
|
|
406
|
+
},
|
|
407
|
+
this.getCurrentTimezone().toString(),
|
|
408
|
+
)
|
|
409
|
+
.toDate();
|
|
383
410
|
}
|
|
384
411
|
|
|
385
412
|
public static isOverlapping(
|
|
@@ -709,22 +736,26 @@ export default class OneUptimeDate {
|
|
|
709
736
|
return this.addRemoveMinutes(date, date.getTimezoneOffset());
|
|
710
737
|
}
|
|
711
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Render an instant as the value of an `<input type="datetime-local">` — a
|
|
741
|
+
* bare wall-clock with no offset — read in the current timezone.
|
|
742
|
+
*/
|
|
712
743
|
public static toDateTimeLocalString(date: Date): string {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
type TenFunction = (i: number) => string;
|
|
716
|
-
|
|
717
|
-
const ten: TenFunction = (i: number): string => {
|
|
718
|
-
return (i < 10 ? "0" : "") + i;
|
|
719
|
-
},
|
|
720
|
-
YYYY: number = date.getFullYear(),
|
|
721
|
-
MM: string = ten(date.getMonth() + 1),
|
|
722
|
-
DD: string = ten(date.getDate()),
|
|
723
|
-
HH: string = ten(date.getHours()),
|
|
724
|
-
II: string = ten(date.getMinutes()),
|
|
725
|
-
SS: string = ten(date.getSeconds());
|
|
744
|
+
return this.inCurrentTimezone(date).format("YYYY-MM-DDTHH:mm:ss");
|
|
745
|
+
}
|
|
726
746
|
|
|
727
|
-
|
|
747
|
+
/**
|
|
748
|
+
* Inverse of toDateTimeLocalString: turn the offset-less wall-clock a user
|
|
749
|
+
* typed into a `<input type="date">` / `<input type="datetime-local">` into
|
|
750
|
+
* the instant it names in the current timezone. Without this the browser
|
|
751
|
+
* zone is used to resolve the wall-clock, so a user whose machine reports a
|
|
752
|
+
* different zone than the one they configured would have their times stored
|
|
753
|
+
* hours off.
|
|
754
|
+
*/
|
|
755
|
+
public static fromDateTimeLocalString(value: string): Date {
|
|
756
|
+
return moment
|
|
757
|
+
.tz(value.trim(), this.getCurrentTimezone().toString())
|
|
758
|
+
.toDate();
|
|
728
759
|
}
|
|
729
760
|
|
|
730
761
|
public static fromJSON(json: JSONObject): Date {
|
|
@@ -1713,7 +1744,7 @@ export default class OneUptimeDate {
|
|
|
1713
1744
|
formatstring = "MMM DD, YYYY";
|
|
1714
1745
|
}
|
|
1715
1746
|
|
|
1716
|
-
const momentDate: moment.Moment =
|
|
1747
|
+
const momentDate: moment.Moment = this.inCurrentTimezone(date);
|
|
1717
1748
|
|
|
1718
1749
|
return (
|
|
1719
1750
|
momentDate.format(formatstring) +
|
|
@@ -1752,8 +1783,41 @@ export default class OneUptimeDate {
|
|
|
1752
1783
|
return zoneAbbr;
|
|
1753
1784
|
}
|
|
1754
1785
|
|
|
1786
|
+
/**
|
|
1787
|
+
* Set the timezone the signed-in user picked in User Settings. Once set,
|
|
1788
|
+
* every "local" helper below resolves wall-clock times in THIS zone instead
|
|
1789
|
+
* of the zone the browser reports, so what the user reads and types matches
|
|
1790
|
+
* the timezone they configured — even when the machine, OS or VPN they are
|
|
1791
|
+
* on reports a different one. Pass null to fall back to the browser zone
|
|
1792
|
+
* (signed-out surfaces, or a user who has not picked a timezone).
|
|
1793
|
+
*/
|
|
1794
|
+
public static setUserTimezone(timezone: Timezone | null | undefined): void {
|
|
1795
|
+
/*
|
|
1796
|
+
* Ignore anything moment does not recognise as an IANA zone. A stale or
|
|
1797
|
+
* corrupt saved value must not take every date in the UI down with it.
|
|
1798
|
+
*/
|
|
1799
|
+
if (!timezone || !moment.tz.zone(timezone.toString())) {
|
|
1800
|
+
this.userTimezone = null;
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
this.userTimezone = timezone;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
public static getUserTimezone(): Timezone | null {
|
|
1808
|
+
return this.userTimezone;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1755
1811
|
public static getCurrentTimezone(): Timezone {
|
|
1756
|
-
return moment.tz.guess() as Timezone;
|
|
1812
|
+
return this.userTimezone || (moment.tz.guess() as Timezone);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* The instant `date` viewed through the current timezone — the single place
|
|
1817
|
+
* that decides which wall clock the "local" helpers below read from.
|
|
1818
|
+
*/
|
|
1819
|
+
private static inCurrentTimezone(date: Date | string): moment.Moment {
|
|
1820
|
+
return moment(this.fromString(date)).tz(this.getCurrentTimezone());
|
|
1757
1821
|
}
|
|
1758
1822
|
|
|
1759
1823
|
public static getDateString(date: Date): string {
|
|
@@ -1810,9 +1874,8 @@ export default class OneUptimeDate {
|
|
|
1810
1874
|
}
|
|
1811
1875
|
|
|
1812
1876
|
public static asDateForDatabaseQuery(date: string | Date): string {
|
|
1813
|
-
date = this.fromString(date);
|
|
1814
1877
|
const formatstring: string = "YYYY-MM-DD";
|
|
1815
|
-
return
|
|
1878
|
+
return this.inCurrentTimezone(date).format(formatstring);
|
|
1816
1879
|
}
|
|
1817
1880
|
|
|
1818
1881
|
public static asFilterDateForDatabaseQuery(
|
|
@@ -1850,13 +1913,19 @@ export default class OneUptimeDate {
|
|
|
1850
1913
|
throw new BadDataException("Invalid seconds");
|
|
1851
1914
|
}
|
|
1852
1915
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1916
|
+
/*
|
|
1917
|
+
* The hour/minute the user picked is a wall-clock reading in THEIR
|
|
1918
|
+
* timezone, so anchor it to today's date in that zone rather than setting
|
|
1919
|
+
* it on a browser-local Date.
|
|
1920
|
+
*/
|
|
1921
|
+
return this.inCurrentTimezone(OneUptimeDate.getCurrentDate())
|
|
1922
|
+
.set({
|
|
1923
|
+
hour: hour,
|
|
1924
|
+
minute: minutes,
|
|
1925
|
+
second: seconds,
|
|
1926
|
+
millisecond: 0,
|
|
1927
|
+
})
|
|
1928
|
+
.toDate();
|
|
1860
1929
|
}
|
|
1861
1930
|
|
|
1862
1931
|
public static toDatabaseDate(date: Date): string {
|
|
@@ -10,4 +10,8 @@ export default interface BaseNotificationRule {
|
|
|
10
10
|
shouldPostToExistingChannel: boolean;
|
|
11
11
|
existingChannelNames: string; // separate by comma
|
|
12
12
|
existingTeam?: string; // team to post to (for MS Teams only)
|
|
13
|
+
|
|
14
|
+
// Post to group / personal chats (for MS Teams only).
|
|
15
|
+
shouldPostToExistingChat?: boolean;
|
|
16
|
+
existingChatIds?: Array<string>; // Teams chat ids the OneUptime app has been added to.
|
|
13
17
|
}
|
|
@@ -9,7 +9,7 @@ import ScheduledMaintenanceState from "../../../Models/DatabaseModels/ScheduledM
|
|
|
9
9
|
import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
|
|
10
10
|
import DropdownUtil from "../../../UI/Utils/Dropdown";
|
|
11
11
|
import MonitorType from "../../Monitor/MonitorType";
|
|
12
|
-
import WorkspaceType from "../WorkspaceType";
|
|
12
|
+
import WorkspaceType, { getWorkspaceTypeDisplayName } from "../WorkspaceType";
|
|
13
13
|
import NotificationRuleEventType from "./EventType";
|
|
14
14
|
import IncidentNotificationRule from "./NotificationRuleTypes/IncidentNotificationRule";
|
|
15
15
|
|
|
@@ -108,32 +108,47 @@ export class NotificationRuleConditionUtil {
|
|
|
108
108
|
eventType === NotificationRuleEventType.IncidentEpisode ||
|
|
109
109
|
eventType === NotificationRuleEventType.ScheduledMaintenance
|
|
110
110
|
) {
|
|
111
|
-
// either create
|
|
111
|
+
// either create a channel, post to an existing channel, or post to a chat.
|
|
112
|
+
|
|
113
|
+
const workspaceDisplayName: string =
|
|
114
|
+
getWorkspaceTypeDisplayName(workspaceType);
|
|
112
115
|
|
|
113
116
|
if (
|
|
114
117
|
!notificationRule.shouldCreateNewChannel &&
|
|
115
|
-
!notificationRule.shouldPostToExistingChannel
|
|
118
|
+
!notificationRule.shouldPostToExistingChannel &&
|
|
119
|
+
!notificationRule.shouldPostToExistingChat
|
|
116
120
|
) {
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
" channel"
|
|
121
|
-
);
|
|
121
|
+
return workspaceType === WorkspaceType.MicrosoftTeams
|
|
122
|
+
? `Please select a destination: create a ${workspaceDisplayName} channel, post to an existing ${workspaceDisplayName} channel, or post to an existing ${workspaceDisplayName} chat`
|
|
123
|
+
: `Please select a destination: create a ${workspaceDisplayName} channel or post to an existing ${workspaceDisplayName} channel`;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
if (notificationRule.shouldPostToExistingChannel) {
|
|
125
127
|
if (!notificationRule.existingChannelNames?.trim()) {
|
|
126
|
-
return
|
|
128
|
+
return (
|
|
129
|
+
"Existing " + workspaceDisplayName + " channel name is required"
|
|
130
|
+
);
|
|
127
131
|
}
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
if (notificationRule.shouldCreateNewChannel) {
|
|
131
135
|
if (!notificationRule.newChannelTemplateName?.trim()) {
|
|
132
|
-
return "New " +
|
|
136
|
+
return "New " + workspaceDisplayName + " channel name is required";
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
if (notificationRule.shouldPostToExistingChat) {
|
|
142
|
+
if (
|
|
143
|
+
!notificationRule.existingChatIds ||
|
|
144
|
+
notificationRule.existingChatIds.length === 0
|
|
145
|
+
) {
|
|
146
|
+
const workspaceDisplayName: string =
|
|
147
|
+
getWorkspaceTypeDisplayName(workspaceType);
|
|
148
|
+
return `Please select at least one ${workspaceDisplayName} chat to post to. If no chats are listed, add the OneUptime app to a chat in ${workspaceDisplayName} first.`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
137
152
|
return null;
|
|
138
153
|
}
|
|
139
154
|
|
|
@@ -106,4 +106,5 @@ export default interface WorkspaceMessagePayload {
|
|
|
106
106
|
messageBlocks: Array<WorkspaceMessageBlock>; // Message to add to blocks.
|
|
107
107
|
workspaceType: WorkspaceType;
|
|
108
108
|
teamId?: string | undefined; // Team ID for Microsoft Teams
|
|
109
|
+
chatIds?: Array<string> | undefined; // Microsoft Teams chat ids (group / personal chats) to send message to.
|
|
109
110
|
}
|