@oneuptime/common 12.0.18 → 12.0.20
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/EnterpriseLicense.ts +23 -0
- package/Models/DatabaseModels/Index.ts +4 -2
- package/Models/DatabaseModels/Monitor.ts +39 -9
- package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
- package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
- package/Models/DatabaseModels/UserSlack.ts +341 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
- package/Server/API/EnterpriseLicenseAPI.ts +94 -19
- package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
- package/Server/API/UserSlackAPI.ts +125 -0
- package/Server/EnvironmentConfig.ts +29 -107
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Queue.ts +7 -0
- package/Server/Services/EnterpriseLicenseService.ts +60 -0
- package/Server/Services/Index.ts +6 -2
- package/Server/Services/LlmLogService.ts +25 -9
- package/Server/Services/OnCallReadinessService.ts +86 -2
- package/Server/Services/ProjectService.ts +171 -0
- package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
- package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
- package/Server/Services/UserNotificationRuleService.ts +589 -11
- package/Server/Services/UserNotificationSettingService.ts +138 -0
- package/Server/Services/UserService.ts +28 -0
- package/Server/Services/UserSlackService.ts +218 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
- package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
- package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
- package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
- package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
- package/Server/Utils/Response.ts +2 -2
- package/Server/Utils/StartServer.ts +2 -2
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
- package/Server/Views/Partials/AnalyticsConsent.ejs +533 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
- package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +465 -0
- package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +302 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +408 -0
- package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +474 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +368 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +763 -0
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
- package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
- package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +138 -0
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +516 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
- package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
- package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
- package/Tests/Server/Services/UserSlackService.test.ts +407 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
- package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
- package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
- package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
- package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
- package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
- package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +53 -0
- package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
- package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +241 -0
- package/Tests/UI/Components/Charts/ChartTooltipSorted.test.tsx +129 -0
- package/Tests/UI/Components/Charts/TooltipEntries.test.ts +155 -0
- package/Tests/UI/Components/Detail/DetailFieldErrors.test.tsx +347 -0
- package/Tests/UI/Components/Forms/BasicFormStepValidation.test.tsx +392 -0
- package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +549 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
- package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +562 -0
- package/Types/Analytics/RevenueEvent.ts +1 -0
- package/Types/Marketing/MarketingEvent.ts +140 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +8 -1
- package/UI/Components/Charts/Bar/BarChart.tsx +8 -1
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +19 -4
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +20 -2
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +19 -4
- package/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.ts +105 -0
- package/UI/Components/Charts/Line/LineChart.tsx +8 -1
- package/UI/Components/Detail/Detail.tsx +18 -1
- package/UI/Utils/NotificationMethodUtil.ts +41 -1
- package/UI/Utils/PermissionGate.ts +56 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +244 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
- package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +66 -12
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
- package/build/dist/Server/API/UserSlackAPI.js +81 -0
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +25 -70
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +7 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +25 -9
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +144 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +26 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSlackService.js +195 -0
- package/build/dist/Server/Services/UserSlackService.js.map +1 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Response.js +2 -2
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +2 -2
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +1 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -1
- package/build/dist/Types/Marketing/MarketingEvent.js +82 -0
- package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +9 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +9 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +22 -13
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +27 -15
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +27 -18
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js +71 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +9 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Detail/Detail.js +17 -1
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +32 -0
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +174 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +1 -0
- package/package.json +1 -1
- package/Models/DatabaseModels/MarketingConversion.ts +0 -410
- package/Server/Services/MarketingConversionService.ts +0 -10
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
- package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
- package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
- package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
- package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
- package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
- package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
- package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
- package/Types/Marketing/MarketingConversion.ts +0 -53
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
- package/build/dist/Server/Services/MarketingConversionService.js +0 -9
- package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
- package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
- package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import {
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeEach,
|
|
5
|
+
describe,
|
|
6
|
+
expect,
|
|
7
|
+
jest,
|
|
8
|
+
test,
|
|
9
|
+
} from "@jest/globals";
|
|
10
|
+
import {
|
|
11
|
+
cleanup,
|
|
12
|
+
fireEvent,
|
|
13
|
+
render,
|
|
14
|
+
screen,
|
|
15
|
+
waitFor,
|
|
16
|
+
} from "@testing-library/react";
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* The per-series investigate menu is the chart's bridge from "this series
|
|
22
|
+
* is spiking" to the signals that explain it: the chip's magnifier must
|
|
23
|
+
* open a menu whose pivots carry the series' group-by labels as REAL
|
|
24
|
+
* filters (logs/traces URLs), whose "Filter to this series" narrows the
|
|
25
|
+
* live explorer view, and whose resource entry lands on the host page the
|
|
26
|
+
* series is about. Rendering the real MetricCharts (not a stub) pins the
|
|
27
|
+
* whole chain: series naming → chip → menu → scoped URL.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
* ResponsiveContainer measures its parent, which is always 0x0 in jsdom,
|
|
32
|
+
* so the chart renders nothing without a fixed size.
|
|
33
|
+
*/
|
|
34
|
+
jest.mock("recharts", () => {
|
|
35
|
+
const actual: Record<string, any> = jest.requireActual("recharts") as Record<
|
|
36
|
+
string,
|
|
37
|
+
any
|
|
38
|
+
>;
|
|
39
|
+
return {
|
|
40
|
+
...actual,
|
|
41
|
+
ResponsiveContainer: ({ children }: { children: React.ReactElement }) => {
|
|
42
|
+
return React.cloneElement(children, {
|
|
43
|
+
width: 600,
|
|
44
|
+
height: 300,
|
|
45
|
+
} as Record<string, unknown>);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
51
|
+
const fetchExemplarsMock: MockFunction = getJestMockFunction();
|
|
52
|
+
const showToastMock: MockFunction = getJestMockFunction();
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* The arrow wrappers are load bearing: jest.mock is hoisted above the
|
|
56
|
+
* compiled requires, so the mock variables above are still unassigned when
|
|
57
|
+
* the factory runs. Dereferencing them lazily, at call time, is what makes
|
|
58
|
+
* this work.
|
|
59
|
+
*/
|
|
60
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
61
|
+
return {
|
|
62
|
+
__esModule: true,
|
|
63
|
+
default: {
|
|
64
|
+
getList: (...args: Array<any>) => {
|
|
65
|
+
return getListMock(...args);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
jest.mock("../../../UI/Components/Toast/ToastInit", () => {
|
|
72
|
+
return {
|
|
73
|
+
__esModule: true,
|
|
74
|
+
ShowToastNotification: (...args: Array<any>) => {
|
|
75
|
+
return showToastMock(...args);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* Keep the exemplar fetch (fired by MetricCharts whenever the view has a
|
|
82
|
+
* resolvable window) off the network; everything else on the Metrics util
|
|
83
|
+
* surface that MetricCharts touches is pure enough to stub inline.
|
|
84
|
+
*/
|
|
85
|
+
jest.mock(
|
|
86
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Metrics/Utils/Metrics",
|
|
87
|
+
() => {
|
|
88
|
+
return {
|
|
89
|
+
__esModule: true,
|
|
90
|
+
default: {
|
|
91
|
+
fetchExemplars: (...args: Array<any>) => {
|
|
92
|
+
fetchExemplarsMock(...args);
|
|
93
|
+
/*
|
|
94
|
+
* Never resolves: a resolved exemplar set would setState after
|
|
95
|
+
* each test's assertions and trip React's act() warning; no
|
|
96
|
+
* test here cares about exemplar dots.
|
|
97
|
+
*/
|
|
98
|
+
return new Promise(() => {
|
|
99
|
+
// Intentionally never settles.
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
setQueryTopNOverride: () => {
|
|
103
|
+
return undefined;
|
|
104
|
+
},
|
|
105
|
+
getQueryConfigTopNKey: (
|
|
106
|
+
_queryConfig: unknown,
|
|
107
|
+
index: number,
|
|
108
|
+
scope?: string,
|
|
109
|
+
) => {
|
|
110
|
+
return `${scope || ""}:${index}`;
|
|
111
|
+
},
|
|
112
|
+
clearQueryTopNOverridesForScope: () => {
|
|
113
|
+
return undefined;
|
|
114
|
+
},
|
|
115
|
+
serializeAttributeFiltersForKey: (attributes: unknown) => {
|
|
116
|
+
return JSON.stringify(attributes || {});
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
DEFAULT_TOP_N_SERIES: 10,
|
|
120
|
+
SHOW_ALL_SERIES_TOP_N: 10_000,
|
|
121
|
+
sanitizeAttributeFilters: (attributes: unknown) => {
|
|
122
|
+
return attributes;
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
import MetricCharts from "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/MetricCharts";
|
|
129
|
+
import AggregatedResult from "../../../Types/BaseDatabase/AggregatedResult";
|
|
130
|
+
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
131
|
+
import MetricsAggregationType from "../../../Types/Metrics/MetricsAggregationType";
|
|
132
|
+
import MetricQueryConfigData from "../../../Types/Metrics/MetricQueryConfigData";
|
|
133
|
+
import MetricViewData from "../../../Types/Metrics/MetricViewData";
|
|
134
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
135
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
136
|
+
import ProjectUtil from "../../../UI/Utils/Project";
|
|
137
|
+
|
|
138
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
139
|
+
"77777777-1111-4111-8111-777777777777",
|
|
140
|
+
);
|
|
141
|
+
const HOST_MODEL_ID: string = "88888888-2222-4222-8222-888888888888";
|
|
142
|
+
|
|
143
|
+
const WINDOW_START: Date = new Date("2026-08-20T10:00:00.000Z");
|
|
144
|
+
const WINDOW_END: Date = new Date("2026-08-20T11:00:00.000Z");
|
|
145
|
+
|
|
146
|
+
function buildQueryConfig(): MetricQueryConfigData {
|
|
147
|
+
return {
|
|
148
|
+
metricAliasData: { metricVariable: "a" },
|
|
149
|
+
metricQueryData: {
|
|
150
|
+
filterData: {
|
|
151
|
+
metricName: "cpu.usage",
|
|
152
|
+
attributes: {},
|
|
153
|
+
aggegationType: MetricsAggregationType.Avg,
|
|
154
|
+
},
|
|
155
|
+
groupByAttributeKeys: ["host.name"],
|
|
156
|
+
},
|
|
157
|
+
} as unknown as MetricQueryConfigData;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function buildViewData(): MetricViewData {
|
|
161
|
+
return {
|
|
162
|
+
queryConfigs: [buildQueryConfig()],
|
|
163
|
+
formulaConfigs: [],
|
|
164
|
+
startAndEndDate: new InBetween<Date>(WINDOW_START, WINDOW_END),
|
|
165
|
+
} as MetricViewData;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildResults(): Array<AggregatedResult> {
|
|
169
|
+
const timestamps: Array<string> = [
|
|
170
|
+
"2026-08-20T10:10:00.000Z",
|
|
171
|
+
"2026-08-20T10:20:00.000Z",
|
|
172
|
+
];
|
|
173
|
+
const data: Array<Record<string, unknown>> = [];
|
|
174
|
+
for (const host of ["web-01", "web-02"]) {
|
|
175
|
+
for (const [index, timestamp] of timestamps.entries()) {
|
|
176
|
+
data.push({
|
|
177
|
+
timestamp: new Date(timestamp),
|
|
178
|
+
value: host === "web-01" ? 90 + index : 10 + index,
|
|
179
|
+
attributes: { "host.name": host },
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return [{ data, truncated: false } as unknown as AggregatedResult];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let navigateSpy: ReturnType<typeof jest.spyOn>;
|
|
187
|
+
|
|
188
|
+
beforeEach(() => {
|
|
189
|
+
getListMock.mockReset();
|
|
190
|
+
fetchExemplarsMock.mockReset();
|
|
191
|
+
showToastMock.mockReset();
|
|
192
|
+
navigateSpy = jest.spyOn(Navigation, "navigate").mockImplementation(() => {
|
|
193
|
+
return undefined;
|
|
194
|
+
});
|
|
195
|
+
jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
afterEach(() => {
|
|
199
|
+
jest.restoreAllMocks();
|
|
200
|
+
cleanup();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
function getNavigatedUrl(): string {
|
|
204
|
+
const lastCall: Array<unknown> | undefined =
|
|
205
|
+
navigateSpy.mock.calls[navigateSpy.mock.calls.length - 1];
|
|
206
|
+
return decodeURIComponent(String(lastCall?.[0]));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
describe("per-series investigate menu", () => {
|
|
210
|
+
test("each series chip carries an investigate button that opens the menu", () => {
|
|
211
|
+
render(
|
|
212
|
+
<MetricCharts
|
|
213
|
+
metricViewData={buildViewData()}
|
|
214
|
+
metricResults={buildResults()}
|
|
215
|
+
metricTypes={[]}
|
|
216
|
+
/>,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
fireEvent.click(screen.getByLabelText("Investigate host.name=web-01"));
|
|
220
|
+
|
|
221
|
+
const menu: HTMLElement = screen.getByRole("menu", {
|
|
222
|
+
name: "Series investigation actions",
|
|
223
|
+
});
|
|
224
|
+
expect(menu).toBeInTheDocument();
|
|
225
|
+
expect(screen.getByText("View logs")).toBeInTheDocument();
|
|
226
|
+
expect(screen.getByText("View traces")).toBeInTheDocument();
|
|
227
|
+
expect(screen.getByText("View exceptions")).toBeInTheDocument();
|
|
228
|
+
// The menu header names the series and what it is scoped to.
|
|
229
|
+
expect(menu.textContent).toContain("host.name = web-01");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("read-only hosts get an explorer deep link; read-write hosts filter in place", () => {
|
|
233
|
+
const onQueryConfigsChange: MockFunction = getJestMockFunction();
|
|
234
|
+
|
|
235
|
+
const { unmount } = render(
|
|
236
|
+
<MetricCharts
|
|
237
|
+
metricViewData={buildViewData()}
|
|
238
|
+
metricResults={buildResults()}
|
|
239
|
+
metricTypes={[]}
|
|
240
|
+
/>,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
fireEvent.click(screen.getByLabelText("Investigate host.name=web-01"));
|
|
244
|
+
expect(screen.getByText("Open in Metric Explorer")).toBeInTheDocument();
|
|
245
|
+
expect(screen.queryByText("Filter to this series")).toBeNull();
|
|
246
|
+
unmount();
|
|
247
|
+
|
|
248
|
+
render(
|
|
249
|
+
<MetricCharts
|
|
250
|
+
metricViewData={buildViewData()}
|
|
251
|
+
metricResults={buildResults()}
|
|
252
|
+
metricTypes={[]}
|
|
253
|
+
onQueryConfigsChange={(...args: Array<any>) => {
|
|
254
|
+
onQueryConfigsChange(...args);
|
|
255
|
+
}}
|
|
256
|
+
/>,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
fireEvent.click(screen.getByLabelText("Investigate host.name=web-01"));
|
|
260
|
+
expect(screen.queryByText("Open in Metric Explorer")).toBeNull();
|
|
261
|
+
|
|
262
|
+
fireEvent.click(screen.getByText("Filter to this series"));
|
|
263
|
+
|
|
264
|
+
expect(onQueryConfigsChange).toHaveBeenCalledTimes(1);
|
|
265
|
+
const narrowedConfigs: Array<MetricQueryConfigData> = onQueryConfigsChange
|
|
266
|
+
.mock.calls[0]?.[0] as Array<MetricQueryConfigData>;
|
|
267
|
+
expect(
|
|
268
|
+
(
|
|
269
|
+
narrowedConfigs[0]?.metricQueryData.filterData as Record<
|
|
270
|
+
string,
|
|
271
|
+
unknown
|
|
272
|
+
>
|
|
273
|
+
)["attributes"],
|
|
274
|
+
).toEqual({ "host.name": "web-01" });
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("View logs navigates to the logs explorer scoped to the series and window", async () => {
|
|
278
|
+
render(
|
|
279
|
+
<MetricCharts
|
|
280
|
+
metricViewData={buildViewData()}
|
|
281
|
+
metricResults={buildResults()}
|
|
282
|
+
metricTypes={[]}
|
|
283
|
+
/>,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
fireEvent.click(screen.getByLabelText("Investigate host.name=web-02"));
|
|
287
|
+
fireEvent.click(screen.getByText("View logs"));
|
|
288
|
+
|
|
289
|
+
await waitFor(() => {
|
|
290
|
+
expect(navigateSpy).toHaveBeenCalled();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const url: string = getNavigatedUrl();
|
|
294
|
+
expect(url).toContain("/logs");
|
|
295
|
+
expect(url).toContain('["attributes.host.name",["web-02"]]');
|
|
296
|
+
expect(url).toContain("range=Custom");
|
|
297
|
+
expect(url).toContain("start=");
|
|
298
|
+
expect(url).toContain("end=");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("View exceptions reports what the exceptions grammar cannot carry", async () => {
|
|
302
|
+
render(
|
|
303
|
+
<MetricCharts
|
|
304
|
+
metricViewData={buildViewData()}
|
|
305
|
+
metricResults={buildResults()}
|
|
306
|
+
metricTypes={[]}
|
|
307
|
+
/>,
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
fireEvent.click(screen.getByLabelText("Investigate host.name=web-01"));
|
|
311
|
+
fireEvent.click(screen.getByText("View exceptions"));
|
|
312
|
+
|
|
313
|
+
await waitFor(() => {
|
|
314
|
+
expect(navigateSpy).toHaveBeenCalled();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const url: string = getNavigatedUrl();
|
|
318
|
+
expect(url).toContain("/exceptions");
|
|
319
|
+
expect(url).toContain("status=all");
|
|
320
|
+
// The host.name narrowing has no exceptions facet — the user is told.
|
|
321
|
+
expect(showToastMock).toHaveBeenCalled();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("the host series jumps to the matching Host page", async () => {
|
|
325
|
+
getListMock.mockReturnValue(
|
|
326
|
+
Promise.resolve({ data: [{ _id: HOST_MODEL_ID }] }),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
render(
|
|
330
|
+
<MetricCharts
|
|
331
|
+
metricViewData={buildViewData()}
|
|
332
|
+
metricResults={buildResults()}
|
|
333
|
+
metricTypes={[]}
|
|
334
|
+
/>,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
fireEvent.click(screen.getByLabelText("Investigate host.name=web-01"));
|
|
338
|
+
fireEvent.click(screen.getByText('Open host "web-01"'));
|
|
339
|
+
|
|
340
|
+
await waitFor(() => {
|
|
341
|
+
expect(navigateSpy).toHaveBeenCalled();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const listArgs: Record<string, unknown> = getListMock.mock
|
|
345
|
+
.calls[0]?.[0] as Record<string, unknown>;
|
|
346
|
+
expect(listArgs["query"]).toEqual({
|
|
347
|
+
projectId: PROJECT_ID,
|
|
348
|
+
hostIdentifier: "web-01",
|
|
349
|
+
});
|
|
350
|
+
expect(getNavigatedUrl()).toContain(`/host/${HOST_MODEL_ID}`);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("enableSeriesActions={false} renders plain chips with no investigate affordance", () => {
|
|
354
|
+
render(
|
|
355
|
+
<MetricCharts
|
|
356
|
+
metricViewData={buildViewData()}
|
|
357
|
+
metricResults={buildResults()}
|
|
358
|
+
metricTypes={[]}
|
|
359
|
+
enableSeriesActions={false}
|
|
360
|
+
/>,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// The chips themselves still render…
|
|
364
|
+
expect(screen.getByText("host.name=web-01")).toBeInTheDocument();
|
|
365
|
+
// …but nothing offers navigation.
|
|
366
|
+
expect(screen.queryByLabelText("Investigate host.name=web-01")).toBeNull();
|
|
367
|
+
});
|
|
368
|
+
});
|
|
@@ -122,14 +122,18 @@ import CallMethods from "../../../../App/FeatureSet/Dashboard/src/Components/Not
|
|
|
122
122
|
import PushMethods from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/Push";
|
|
123
123
|
import WhatsAppMethods from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/WhatsApp";
|
|
124
124
|
import TelegramMethods from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/Telegram";
|
|
125
|
+
import SlackMethods from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/Slack";
|
|
126
|
+
import MicrosoftTeamsMethods from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/MicrosoftTeams";
|
|
125
127
|
import WebhookMethods from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/Webhook";
|
|
126
128
|
import BaseModel, {
|
|
127
129
|
DatabaseBaseModelType,
|
|
128
130
|
} from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
129
131
|
import UserCall from "../../../Models/DatabaseModels/UserCall";
|
|
130
132
|
import UserEmail from "../../../Models/DatabaseModels/UserEmail";
|
|
133
|
+
import UserMicrosoftTeams from "../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
131
134
|
import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
|
|
132
135
|
import UserPush from "../../../Models/DatabaseModels/UserPush";
|
|
136
|
+
import UserSlack from "../../../Models/DatabaseModels/UserSlack";
|
|
133
137
|
import UserSMS from "../../../Models/DatabaseModels/UserSMS";
|
|
134
138
|
import UserTelegram from "../../../Models/DatabaseModels/UserTelegram";
|
|
135
139
|
import UserWebhook from "../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -1470,7 +1474,7 @@ describe("Escalation rules, wired: the delete confirmation says what it costs",
|
|
|
1470
1474
|
|
|
1471
1475
|
/*
|
|
1472
1476
|
* ---------------------------------------------------------------------------
|
|
1473
|
-
* THE
|
|
1477
|
+
* THE NINE METHOD TABLES, RENDERED FOR REAL.
|
|
1474
1478
|
* ---------------------------------------------------------------------------
|
|
1475
1479
|
*
|
|
1476
1480
|
* This block exists because the first cut of this feature shipped a correct,
|
|
@@ -1532,6 +1536,8 @@ const METHOD_ID_CALL: string = "33333333-9999-4999-8999-999999999999";
|
|
|
1532
1536
|
const METHOD_ID_PUSH: string = "44444444-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
|
|
1533
1537
|
const METHOD_ID_WHATSAPP: string = "55555555-bbbb-4bbb-8bbb-bbbbbbbbbbbb";
|
|
1534
1538
|
const METHOD_ID_TELEGRAM: string = "66666666-cccc-4ccc-8ccc-cccccccccccc";
|
|
1539
|
+
const METHOD_ID_SLACK: string = "88888888-eeee-4eee-8eee-eeeeeeeeeeee";
|
|
1540
|
+
const METHOD_ID_TEAMS: string = "99999999-ffff-4fff-8fff-ffffffffffff";
|
|
1535
1541
|
const METHOD_ID_WEBHOOK: string = "77777777-dddd-4ddd-8ddd-dddddddddddd";
|
|
1536
1542
|
|
|
1537
1543
|
const METHOD_SURFACES: Array<MethodSurface> = [
|
|
@@ -1624,6 +1630,36 @@ const METHOD_SURFACES: Array<MethodSurface> = [
|
|
|
1624
1630
|
modalTitle: "Delete Telegram Account",
|
|
1625
1631
|
methodLabel: "Telegram: @alexchen",
|
|
1626
1632
|
},
|
|
1633
|
+
{
|
|
1634
|
+
name: "Slack",
|
|
1635
|
+
Component: SlackMethods,
|
|
1636
|
+
modelType: UserSlack,
|
|
1637
|
+
relationName: "userSlack",
|
|
1638
|
+
methodId: METHOD_ID_SLACK,
|
|
1639
|
+
buildRow: (): BaseModel => {
|
|
1640
|
+
return buildMethodRow(UserSlack, METHOD_ID_SLACK, {
|
|
1641
|
+
slackUserName: "alexchen",
|
|
1642
|
+
isVerified: true,
|
|
1643
|
+
});
|
|
1644
|
+
},
|
|
1645
|
+
modalTitle: "Delete Slack Account",
|
|
1646
|
+
methodLabel: "Slack: alexchen",
|
|
1647
|
+
},
|
|
1648
|
+
{
|
|
1649
|
+
name: "Microsoft Teams",
|
|
1650
|
+
Component: MicrosoftTeamsMethods,
|
|
1651
|
+
modelType: UserMicrosoftTeams,
|
|
1652
|
+
relationName: "userMicrosoftTeams",
|
|
1653
|
+
methodId: METHOD_ID_TEAMS,
|
|
1654
|
+
buildRow: (): BaseModel => {
|
|
1655
|
+
return buildMethodRow(UserMicrosoftTeams, METHOD_ID_TEAMS, {
|
|
1656
|
+
microsoftTeamsUserName: "Alex Example",
|
|
1657
|
+
isVerified: true,
|
|
1658
|
+
});
|
|
1659
|
+
},
|
|
1660
|
+
modalTitle: "Delete Microsoft Teams Account",
|
|
1661
|
+
methodLabel: "Microsoft Teams: Alex Example",
|
|
1662
|
+
},
|
|
1627
1663
|
{
|
|
1628
1664
|
name: "Webhook",
|
|
1629
1665
|
Component: WebhookMethods,
|
|
@@ -38,7 +38,7 @@ import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
|
38
38
|
*
|
|
39
39
|
* The fourth is where the "Notification Method" dropdown comes from, which is
|
|
40
40
|
* now two different places depending on whose rules are on screen. For the
|
|
41
|
-
* signed-in user it is still
|
|
41
|
+
* signed-in user it is still nine reads of the nine method models, and that
|
|
42
42
|
* path is the four settings pages every existing user relies on. For anybody
|
|
43
43
|
* else those reads are REFUSED - the models are scoped to the person who owns
|
|
44
44
|
* the device, because the columns behind them are the raw phone number, the
|
|
@@ -186,8 +186,10 @@ import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
|
|
|
186
186
|
import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
|
|
187
187
|
import UserCall from "../../../Models/DatabaseModels/UserCall";
|
|
188
188
|
import UserEmail from "../../../Models/DatabaseModels/UserEmail";
|
|
189
|
+
import UserMicrosoftTeams from "../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
189
190
|
import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
|
|
190
191
|
import UserPush from "../../../Models/DatabaseModels/UserPush";
|
|
192
|
+
import UserSlack from "../../../Models/DatabaseModels/UserSlack";
|
|
191
193
|
import UserSMS from "../../../Models/DatabaseModels/UserSMS";
|
|
192
194
|
import UserTelegram from "../../../Models/DatabaseModels/UserTelegram";
|
|
193
195
|
import UserWebhook from "../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -199,7 +201,7 @@ import ObjectID from "../../../Types/ObjectID";
|
|
|
199
201
|
import ProjectUtil from "../../../UI/Utils/Project";
|
|
200
202
|
|
|
201
203
|
/*
|
|
202
|
-
* The
|
|
204
|
+
* The nine models whose rows only their owner may read. They are listed
|
|
203
205
|
* together because the assertion that matters is about the SET: an admin
|
|
204
206
|
* surface that reads any one of them is reading a row it is not entitled to,
|
|
205
207
|
* and which one hardly matters.
|
|
@@ -211,13 +213,15 @@ const NOTIFICATION_METHOD_MODELS: Array<unknown> = [
|
|
|
211
213
|
UserPush,
|
|
212
214
|
UserWhatsApp,
|
|
213
215
|
UserTelegram,
|
|
216
|
+
UserSlack,
|
|
217
|
+
UserMicrosoftTeams,
|
|
214
218
|
UserWebhook,
|
|
215
219
|
];
|
|
216
220
|
|
|
217
221
|
/*
|
|
218
222
|
* What the admin surface hands over in place of those reads: a channel name, the
|
|
219
223
|
* id of the row, and a mask. Telegram is unverified on purpose - the self-serve
|
|
220
|
-
* path filters `isVerified: true` in
|
|
224
|
+
* path filters `isVerified: true` in eight of its nine queries, so a supplied
|
|
221
225
|
* list that offered unverified methods would be the two paths disagreeing about
|
|
222
226
|
* what is pickable.
|
|
223
227
|
*/
|
|
@@ -250,6 +254,8 @@ const METHOD_FOREIGN_KEYS: Array<keyof UserNotificationRule> = [
|
|
|
250
254
|
"userPushId",
|
|
251
255
|
"userWhatsAppId",
|
|
252
256
|
"userTelegramId",
|
|
257
|
+
"userSlackId",
|
|
258
|
+
"userMicrosoftTeamsId",
|
|
253
259
|
"userWebhookId",
|
|
254
260
|
];
|
|
255
261
|
|
|
@@ -343,7 +349,7 @@ const getMethodOptions: GetMethodOptions = (
|
|
|
343
349
|
};
|
|
344
350
|
|
|
345
351
|
/*
|
|
346
|
-
* The
|
|
352
|
+
* The nine RELATION spellings. Selecting any of these on a rule pulls columns
|
|
347
353
|
* out of the method models themselves - the raw address, the raw number, the
|
|
348
354
|
* telegram handle - through a table an administrator is allowed to read.
|
|
349
355
|
*
|
|
@@ -366,6 +372,8 @@ const METHOD_RELATIONS: Array<string> = [
|
|
|
366
372
|
"userPush",
|
|
367
373
|
"userWhatsApp",
|
|
368
374
|
"userTelegram",
|
|
375
|
+
"userSlack",
|
|
376
|
+
"userMicrosoftTeams",
|
|
369
377
|
"userWebhook",
|
|
370
378
|
];
|
|
371
379
|
|
|
@@ -719,7 +727,7 @@ describe("OnCallRulesTable", () => {
|
|
|
719
727
|
|
|
720
728
|
/*
|
|
721
729
|
* The whole point of the redesign, in one assertion. Not "reads them and
|
|
722
|
-
* shows fewer columns" - does not read them. Each of these
|
|
730
|
+
* shows fewer columns" - does not read them. Each of these nine models is
|
|
723
731
|
* scoped to the person who owns the device, and the columns behind them are
|
|
724
732
|
* the raw phone number, the webhook bearer url, the push device token, the
|
|
725
733
|
* telegram chat id and the verification code. A request here is a request
|
|
@@ -748,7 +756,7 @@ describe("OnCallRulesTable", () => {
|
|
|
748
756
|
* The readiness payload the admin page sources these from is fetched
|
|
749
757
|
* asynchronously and can fail outright, so an absent list is a real state
|
|
750
758
|
* rather than a caller mistake. Treating it as "then fetch them yourself"
|
|
751
|
-
* would fire
|
|
759
|
+
* would fire nine refused requests and turn this component's error state on
|
|
752
760
|
* - taking the rule repair away because a DIFFERENT read failed.
|
|
753
761
|
*/
|
|
754
762
|
await renderForOtherUser(undefined);
|
|
@@ -786,7 +794,7 @@ describe("OnCallRulesTable", () => {
|
|
|
786
794
|
|
|
787
795
|
/*
|
|
788
796
|
* The unverified Telegram method is absent, which mirrors the
|
|
789
|
-
* `isVerified: true` filter on
|
|
797
|
+
* `isVerified: true` filter on eight of the nine self-serve queries. A rule
|
|
790
798
|
* pointed at an unverified device is a rule that pages nothing.
|
|
791
799
|
*/
|
|
792
800
|
expect(
|
|
@@ -805,7 +813,7 @@ describe("OnCallRulesTable", () => {
|
|
|
805
813
|
});
|
|
806
814
|
|
|
807
815
|
/*
|
|
808
|
-
* The
|
|
816
|
+
* The nine foreign keys are mutually exclusive and nothing validates that
|
|
809
817
|
* client-side, so this asserts the whole set: an SMS id written to
|
|
810
818
|
* `userEmailId` would page an address rather than a phone, and would not
|
|
811
819
|
* error anywhere on the way.
|
|
@@ -968,7 +976,7 @@ describe("OnCallRulesTable", () => {
|
|
|
968
976
|
test("the signed-in user's own table still reads the method models itself", async () => {
|
|
969
977
|
/*
|
|
970
978
|
* The regression surface. Four settings pages every existing user already
|
|
971
|
-
* relies on read their own
|
|
979
|
+
* relies on read their own nine models to fill this dropdown, and reading
|
|
972
980
|
* your own rows is exactly what those models allow. Nothing about the admin
|
|
973
981
|
* surface is a reason to change it - and an unmasked label is more use to
|
|
974
982
|
* somebody looking at their own phone number than a mask of it.
|
|
@@ -985,7 +993,7 @@ describe("OnCallRulesTable", () => {
|
|
|
985
993
|
getQueriedUserIds().filter((userId: string | undefined) => {
|
|
986
994
|
return userId === CURRENT_USER_ID_STRING;
|
|
987
995
|
}),
|
|
988
|
-
).toHaveLength(
|
|
996
|
+
).toHaveLength(9);
|
|
989
997
|
|
|
990
998
|
expect(getMethodOptions(capturedTables[0]!)).toEqual([
|
|
991
999
|
{ label: `Email: ${OWN_EMAIL_ADDRESS}`, value: OWN_EMAIL_ID },
|
|
@@ -117,6 +117,30 @@ const NOT_READABLE_BY_THEIR_DOMAIN_ROLES: Array<string> = [
|
|
|
117
117
|
"IncomingCallPolicyEscalationRule",
|
|
118
118
|
];
|
|
119
119
|
|
|
120
|
+
/*
|
|
121
|
+
* Columns inside an in-domain table whose read list deliberately stops short of
|
|
122
|
+
* the domain's full tier set, and why. The table-level list above is about
|
|
123
|
+
* whole tables a role holder is kept out of; this is the same idea one level
|
|
124
|
+
* down, for a table they are otherwise welcome in.
|
|
125
|
+
*
|
|
126
|
+
* Entries are the exact `<table>.<column> is missing <tier>` strings the sweep
|
|
127
|
+
* produces, so the list is self-policing: widen one of these read lists again
|
|
128
|
+
* and the entry stops being produced and the assertion fails.
|
|
129
|
+
*
|
|
130
|
+
* https://github.com/OneUptime/oneuptime/issues/3360 - Monitor's three secret
|
|
131
|
+
* keys are bearer credentials, not monitor data. Reading one is gated on being
|
|
132
|
+
* able to ROTATE it, which is what separates MonitorMember (may edit a monitor,
|
|
133
|
+
* so may see and reset its key) from MonitorViewer (read-only, so may not).
|
|
134
|
+
* Viewer used to be on these lists, which made "read-only" mean "can read a
|
|
135
|
+
* live credential for every agent in the project" -- the same reasoning that
|
|
136
|
+
* keeps MonitorSecret out of the table list above.
|
|
137
|
+
*/
|
|
138
|
+
const COLUMNS_NOT_READABLE_BY_THEIR_DOMAIN_ROLES: Array<string> = [
|
|
139
|
+
"Monitor.incomingEmailSecretKey is missing MonitorViewer",
|
|
140
|
+
"Monitor.incomingRequestSecretKey is missing MonitorViewer",
|
|
141
|
+
"Monitor.serverMonitorSecretKey is missing MonitorViewer",
|
|
142
|
+
];
|
|
143
|
+
|
|
120
144
|
type ModelNameFunction = (modelType: ModelType) => string;
|
|
121
145
|
|
|
122
146
|
const modelName: ModelNameFunction = (modelType: ModelType): string => {
|
|
@@ -317,7 +341,9 @@ describe("Domain role tier coverage", () => {
|
|
|
317
341
|
}
|
|
318
342
|
}
|
|
319
343
|
|
|
320
|
-
expect(missing.sort()).toEqual(
|
|
344
|
+
expect(missing.sort()).toEqual(
|
|
345
|
+
COLUMNS_NOT_READABLE_BY_THEIR_DOMAIN_ROLES.slice().sort(),
|
|
346
|
+
);
|
|
321
347
|
expect(checked).toBeGreaterThan(500);
|
|
322
348
|
});
|
|
323
349
|
});
|