@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
|
@@ -26,9 +26,11 @@ import TeamMemberService from "../../../Server/Services/TeamMemberService";
|
|
|
26
26
|
import TeamService from "../../../Server/Services/TeamService";
|
|
27
27
|
import UserCallService from "../../../Server/Services/UserCallService";
|
|
28
28
|
import UserEmailService from "../../../Server/Services/UserEmailService";
|
|
29
|
+
import UserMicrosoftTeamsService from "../../../Server/Services/UserMicrosoftTeamsService";
|
|
29
30
|
import UserNotificationRuleService from "../../../Server/Services/UserNotificationRuleService";
|
|
30
31
|
import UserPushService from "../../../Server/Services/UserPushService";
|
|
31
32
|
import UserService from "../../../Server/Services/UserService";
|
|
33
|
+
import UserSlackService from "../../../Server/Services/UserSlackService";
|
|
32
34
|
import UserSmsService from "../../../Server/Services/UserSmsService";
|
|
33
35
|
import UserTelegramService from "../../../Server/Services/UserTelegramService";
|
|
34
36
|
import UserWebhookService from "../../../Server/Services/UserWebhookService";
|
|
@@ -41,7 +43,9 @@ import {
|
|
|
41
43
|
import Response from "../../../Server/Utils/Response";
|
|
42
44
|
import UserCall from "../../../Models/DatabaseModels/UserCall";
|
|
43
45
|
import UserEmail from "../../../Models/DatabaseModels/UserEmail";
|
|
46
|
+
import UserMicrosoftTeams from "../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
44
47
|
import UserPush from "../../../Models/DatabaseModels/UserPush";
|
|
48
|
+
import UserSlack from "../../../Models/DatabaseModels/UserSlack";
|
|
45
49
|
import UserSMS from "../../../Models/DatabaseModels/UserSMS";
|
|
46
50
|
import UserTelegram from "../../../Models/DatabaseModels/UserTelegram";
|
|
47
51
|
import UserWebhook from "../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -316,6 +320,8 @@ let userSmsFindBy: jest.SpyInstance;
|
|
|
316
320
|
let userCallFindBy: jest.SpyInstance;
|
|
317
321
|
let userWhatsAppFindBy: jest.SpyInstance;
|
|
318
322
|
let userTelegramFindBy: jest.SpyInstance;
|
|
323
|
+
let userSlackFindBy: jest.SpyInstance;
|
|
324
|
+
let userMicrosoftTeamsFindBy: jest.SpyInstance;
|
|
319
325
|
let userWebhookFindBy: jest.SpyInstance;
|
|
320
326
|
let notificationRuleFindBy: jest.SpyInstance;
|
|
321
327
|
let incidentSeverityFindBy: jest.SpyInstance;
|
|
@@ -340,6 +346,8 @@ function everyFindBySpy(): Array<jest.SpyInstance> {
|
|
|
340
346
|
userCallFindBy,
|
|
341
347
|
userWhatsAppFindBy,
|
|
342
348
|
userTelegramFindBy,
|
|
349
|
+
userSlackFindBy,
|
|
350
|
+
userMicrosoftTeamsFindBy,
|
|
343
351
|
userWebhookFindBy,
|
|
344
352
|
notificationRuleFindBy,
|
|
345
353
|
incidentSeverityFindBy,
|
|
@@ -445,6 +453,12 @@ beforeEach(() => {
|
|
|
445
453
|
userTelegramFindBy = jest
|
|
446
454
|
.spyOn(UserTelegramService, "findBy")
|
|
447
455
|
.mockResolvedValue([] as never);
|
|
456
|
+
userSlackFindBy = jest
|
|
457
|
+
.spyOn(UserSlackService, "findBy")
|
|
458
|
+
.mockResolvedValue([] as never);
|
|
459
|
+
userMicrosoftTeamsFindBy = jest
|
|
460
|
+
.spyOn(UserMicrosoftTeamsService, "findBy")
|
|
461
|
+
.mockResolvedValue([] as never);
|
|
448
462
|
userWebhookFindBy = jest
|
|
449
463
|
.spyOn(UserWebhookService, "findBy")
|
|
450
464
|
.mockResolvedValue([] as never);
|
|
@@ -759,13 +773,14 @@ describe("GET /on-call-readiness/policy/:policyId", () => {
|
|
|
759
773
|
|
|
760
774
|
/*
|
|
761
775
|
* Four fields and no fifth. `methods` is the part of this payload that
|
|
762
|
-
* describes rows an administrator is NOT permitted to read - the
|
|
776
|
+
* describes rows an administrator is NOT permitted to read - the nine
|
|
763
777
|
* method models are owner-scoped precisely because their columns are the raw
|
|
764
|
-
* phone number, the webhook bearer url, the push token, the telegram chat
|
|
765
|
-
*
|
|
766
|
-
* rather than field by field. A
|
|
767
|
-
*
|
|
768
|
-
* pass through here on
|
|
778
|
+
* phone number, the webhook bearer url, the push token, the telegram chat
|
|
779
|
+
* id, the slack member id, the teams object id and the verification code -
|
|
780
|
+
* so the wire shape is asserted exhaustively rather than field by field. A
|
|
781
|
+
* field added to ReadinessMethod is a field that ships to every
|
|
782
|
+
* administrator of the project, and it should have to pass through here on
|
|
783
|
+
* the way.
|
|
769
784
|
*/
|
|
770
785
|
expect(user["methods"]).toEqual([
|
|
771
786
|
{
|
|
@@ -1437,6 +1452,15 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1437
1452
|
const RAW_WHATSAPP: string = "+14155559999";
|
|
1438
1453
|
const RAW_HANDLE: string = "@jamesbond";
|
|
1439
1454
|
const RAW_DEVICE: string = "Jane's iPhone 15 Pro";
|
|
1455
|
+
const RAW_SLACK_USERNAME: string = "jane.oncall.slack";
|
|
1456
|
+
const RAW_TEAMS_USERNAME: string = "Jane Doe [Overnight]";
|
|
1457
|
+
/*
|
|
1458
|
+
* The addressable targets behind the two workspace channels - the Slack
|
|
1459
|
+
* member id the bot DMs and the Entra object id the Teams bot resolves. On
|
|
1460
|
+
* the rows, like the webhook's bearer url, and never selected.
|
|
1461
|
+
*/
|
|
1462
|
+
const RAW_SLACK_USER_ID: string = "U0MEMBERIDLEAK";
|
|
1463
|
+
const RAW_TEAMS_USER_ID: string = "aad-ENTRALEAK-7788";
|
|
1440
1464
|
const RAW_WEBHOOK_NAME: string = "Payments Slack Hook";
|
|
1441
1465
|
const RAW_WEBHOOK_URL: string =
|
|
1442
1466
|
"https://hooks.slack.com/services/T000/B000/XXXXsecretXXXX";
|
|
@@ -1445,7 +1469,7 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1445
1469
|
* One fixed id per channel, and fixed rather than generated so that a failure
|
|
1446
1470
|
* prints a value a reader can match against the fixture that produced it.
|
|
1447
1471
|
*
|
|
1448
|
-
* These are the ids UserNotificationRule's
|
|
1472
|
+
* These are the ids UserNotificationRule's nine foreign keys reference -
|
|
1449
1473
|
* userEmailId points at a UserEmail row, userSmsId at a UserSMS row - and they
|
|
1450
1474
|
* are the reason `methodId` exists at all. An administrator building a rule
|
|
1451
1475
|
* for somebody else may not READ any of these rows; what they get instead is
|
|
@@ -1475,10 +1499,16 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1475
1499
|
const WEBHOOK_METHOD_ID: ObjectID = new ObjectID(
|
|
1476
1500
|
"1a000000-0000-4000-8000-000000000007",
|
|
1477
1501
|
);
|
|
1502
|
+
const SLACK_METHOD_ID: ObjectID = new ObjectID(
|
|
1503
|
+
"1a000000-0000-4000-8000-000000000008",
|
|
1504
|
+
);
|
|
1505
|
+
const MICROSOFT_TEAMS_METHOD_ID: ObjectID = new ObjectID(
|
|
1506
|
+
"1a000000-0000-4000-8000-000000000009",
|
|
1507
|
+
);
|
|
1478
1508
|
|
|
1479
1509
|
/*
|
|
1480
1510
|
* The expected id per channel, in one place, so the sweep below is a sweep
|
|
1481
|
-
* over all
|
|
1511
|
+
* over all nine rather than nine assertions that can each be forgotten
|
|
1482
1512
|
* individually. Keyed by the wire's `methodType` string.
|
|
1483
1513
|
*/
|
|
1484
1514
|
const METHOD_ID_BY_TYPE: Dictionary<string> = {
|
|
@@ -1488,6 +1518,8 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1488
1518
|
[ReadinessMethodType.Call]: CALL_METHOD_ID.toString(),
|
|
1489
1519
|
[ReadinessMethodType.WhatsApp]: WHATSAPP_METHOD_ID.toString(),
|
|
1490
1520
|
[ReadinessMethodType.Telegram]: TELEGRAM_METHOD_ID.toString(),
|
|
1521
|
+
[ReadinessMethodType.Slack]: SLACK_METHOD_ID.toString(),
|
|
1522
|
+
[ReadinessMethodType.MicrosoftTeams]: MICROSOFT_TEAMS_METHOD_ID.toString(),
|
|
1491
1523
|
[ReadinessMethodType.Webhook]: WEBHOOK_METHOD_ID.toString(),
|
|
1492
1524
|
};
|
|
1493
1525
|
|
|
@@ -1548,6 +1580,24 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1548
1580
|
telegram.isVerified = true;
|
|
1549
1581
|
userTelegramFindBy.mockResolvedValue([telegram] as never);
|
|
1550
1582
|
|
|
1583
|
+
const slack: UserSlack = new UserSlack();
|
|
1584
|
+
slack.id = SLACK_METHOD_ID;
|
|
1585
|
+
slack.userId = subjectUserId;
|
|
1586
|
+
slack.slackUserName = RAW_SLACK_USERNAME;
|
|
1587
|
+
// On the row, and never selected: the member id is the addressable target.
|
|
1588
|
+
slack.slackUserId = RAW_SLACK_USER_ID;
|
|
1589
|
+
slack.isVerified = true;
|
|
1590
|
+
userSlackFindBy.mockResolvedValue([slack] as never);
|
|
1591
|
+
|
|
1592
|
+
const microsoftTeams: UserMicrosoftTeams = new UserMicrosoftTeams();
|
|
1593
|
+
microsoftTeams.id = MICROSOFT_TEAMS_METHOD_ID;
|
|
1594
|
+
microsoftTeams.userId = subjectUserId;
|
|
1595
|
+
microsoftTeams.microsoftTeamsUserName = RAW_TEAMS_USERNAME;
|
|
1596
|
+
// On the row, and never selected: the Entra id is the addressable target.
|
|
1597
|
+
microsoftTeams.microsoftTeamsUserId = RAW_TEAMS_USER_ID;
|
|
1598
|
+
microsoftTeams.isVerified = true;
|
|
1599
|
+
userMicrosoftTeamsFindBy.mockResolvedValue([microsoftTeams] as never);
|
|
1600
|
+
|
|
1551
1601
|
const push: UserPush = new UserPush();
|
|
1552
1602
|
push.id = PUSH_METHOD_ID;
|
|
1553
1603
|
push.userId = subjectUserId;
|
|
@@ -1597,6 +1647,24 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1597
1647
|
maskedIdentifier: `j${IDENTIFIER_MASK}@personal.example.com`,
|
|
1598
1648
|
isVerified: true,
|
|
1599
1649
|
},
|
|
1650
|
+
/*
|
|
1651
|
+
* The two workspace channels sit in the zero-cost tier beside Push and
|
|
1652
|
+
* Email, ahead of every paid channel. Their masked identifier is the
|
|
1653
|
+
* human-facing NAME column under the handle rule; the addressable ids
|
|
1654
|
+
* are never selected at all - see the select tests below.
|
|
1655
|
+
*/
|
|
1656
|
+
{
|
|
1657
|
+
methodId: SLACK_METHOD_ID.toString(),
|
|
1658
|
+
methodType: ReadinessMethodType.Slack,
|
|
1659
|
+
maskedIdentifier: `ja${IDENTIFIER_MASK}`,
|
|
1660
|
+
isVerified: true,
|
|
1661
|
+
},
|
|
1662
|
+
{
|
|
1663
|
+
methodId: MICROSOFT_TEAMS_METHOD_ID.toString(),
|
|
1664
|
+
methodType: ReadinessMethodType.MicrosoftTeams,
|
|
1665
|
+
maskedIdentifier: `Ja${IDENTIFIER_MASK}`,
|
|
1666
|
+
isVerified: true,
|
|
1667
|
+
},
|
|
1600
1668
|
{
|
|
1601
1669
|
methodId: SMS_METHOD_ID.toString(),
|
|
1602
1670
|
methodType: ReadinessMethodType.SMS,
|
|
@@ -1634,10 +1702,10 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1634
1702
|
]);
|
|
1635
1703
|
});
|
|
1636
1704
|
|
|
1637
|
-
test("all
|
|
1705
|
+
test("all nine channels reach the wire carrying the id of their OWN row", async () => {
|
|
1638
1706
|
/*
|
|
1639
1707
|
* The dropdown's entire premise, swept across every channel rather than
|
|
1640
|
-
* spot-checked on one, because the
|
|
1708
|
+
* spot-checked on one, because the nine are nine separate code paths that
|
|
1641
1709
|
* each build their own row and each have their own opportunity to hand over
|
|
1642
1710
|
* somebody else's id - or none at all.
|
|
1643
1711
|
*
|
|
@@ -1650,7 +1718,7 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1650
1718
|
const payload: Record<string, unknown> = await readUser();
|
|
1651
1719
|
const methods: Array<Record<string, unknown>> = methodsOf(payload);
|
|
1652
1720
|
|
|
1653
|
-
expect(methods).toHaveLength(
|
|
1721
|
+
expect(methods).toHaveLength(9);
|
|
1654
1722
|
|
|
1655
1723
|
for (const method of methods) {
|
|
1656
1724
|
const methodType: string = method["methodType"] as string;
|
|
@@ -1682,7 +1750,7 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1682
1750
|
const payload: Record<string, unknown> = await readUser();
|
|
1683
1751
|
const methods: Array<Record<string, unknown>> = methodsOf(payload);
|
|
1684
1752
|
|
|
1685
|
-
expect(methods).toHaveLength(
|
|
1753
|
+
expect(methods).toHaveLength(9);
|
|
1686
1754
|
|
|
1687
1755
|
for (const method of methods) {
|
|
1688
1756
|
expect(method["methodId"]).not.toBe(subjectUserId.toString());
|
|
@@ -1691,23 +1759,23 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1691
1759
|
expect(method["methodId"]).not.toBe(projectId.toString());
|
|
1692
1760
|
}
|
|
1693
1761
|
|
|
1694
|
-
// And all
|
|
1762
|
+
// And all nine are distinct - one id copied across channels is the same bug.
|
|
1695
1763
|
const ids: Array<unknown> = methods.map(
|
|
1696
1764
|
(method: Record<string, unknown>): unknown => {
|
|
1697
1765
|
return method["methodId"];
|
|
1698
1766
|
},
|
|
1699
1767
|
);
|
|
1700
|
-
expect(new Set(ids).size).toBe(
|
|
1768
|
+
expect(new Set(ids).size).toBe(9);
|
|
1701
1769
|
});
|
|
1702
1770
|
|
|
1703
1771
|
test("a method carries its id, its type, its mask and its verification - and nothing else", async () => {
|
|
1704
1772
|
/*
|
|
1705
1773
|
* The containment assertion. Every field on this object ships to every
|
|
1706
1774
|
* administrator of the project, about a row that administrator is not
|
|
1707
|
-
* allowed to read, so the key set is pinned exhaustively on all
|
|
1775
|
+
* allowed to read, so the key set is pinned exhaustively on all nine rather
|
|
1708
1776
|
* than left to whatever the serialiser happens to copy. A `phone`, a
|
|
1709
|
-
* `webhookUrl` or a `
|
|
1710
|
-
* change; it is the exposure this design was built to avoid.
|
|
1777
|
+
* `webhookUrl`, a `telegramChatId` or a `slackUserId` appearing here is not
|
|
1778
|
+
* a formatting change; it is the exposure this design was built to avoid.
|
|
1711
1779
|
*/
|
|
1712
1780
|
const payload: Record<string, unknown> = await readUser();
|
|
1713
1781
|
|
|
@@ -1729,10 +1797,10 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1729
1797
|
* Guards the guard, and it is not a formality here: the service drops any
|
|
1730
1798
|
* method row it cannot find an id on, so a fixture regression that emptied
|
|
1731
1799
|
* `methods` would satisfy every "not present" assertion below for the one
|
|
1732
|
-
* reason that proves nothing at all. The
|
|
1800
|
+
* reason that proves nothing at all. The nine masks have to be in the body
|
|
1733
1801
|
* before their absence of raw values means anything.
|
|
1734
1802
|
*/
|
|
1735
|
-
expect(methodsOf(payload)).toHaveLength(
|
|
1803
|
+
expect(methodsOf(payload)).toHaveLength(9);
|
|
1736
1804
|
expect(body).toContain(IDENTIFIER_MASK);
|
|
1737
1805
|
|
|
1738
1806
|
/*
|
|
@@ -1747,6 +1815,8 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1747
1815
|
RAW_WHATSAPP,
|
|
1748
1816
|
RAW_HANDLE,
|
|
1749
1817
|
RAW_DEVICE,
|
|
1818
|
+
RAW_SLACK_USERNAME,
|
|
1819
|
+
RAW_TEAMS_USERNAME,
|
|
1750
1820
|
RAW_WEBHOOK_NAME,
|
|
1751
1821
|
RAW_WEBHOOK_URL,
|
|
1752
1822
|
// The notification address's local part on its own, and the hook's host.
|
|
@@ -1754,6 +1824,14 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1754
1824
|
"hooks.slack.com",
|
|
1755
1825
|
// The telegram chat id - the addressable target, never the label.
|
|
1756
1826
|
"998877",
|
|
1827
|
+
// The workspace targets - the Slack member id and the Entra object id.
|
|
1828
|
+
RAW_SLACK_USER_ID,
|
|
1829
|
+
RAW_TEAMS_USER_ID,
|
|
1830
|
+
"MEMBERIDLEAK",
|
|
1831
|
+
"ENTRALEAK",
|
|
1832
|
+
// The workspace names' revealing middles, past what a handle mask keeps.
|
|
1833
|
+
"oncall.slack",
|
|
1834
|
+
"[Overnight]",
|
|
1757
1835
|
/*
|
|
1758
1836
|
* The unmasked tails of the four phone-shaped identifiers. maskIdentifier
|
|
1759
1837
|
* keeps the last four digits on purpose, so the numbers themselves are
|
|
@@ -1795,7 +1873,7 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1795
1873
|
const payload: Record<string, unknown> = await readUser();
|
|
1796
1874
|
const methods: Array<Record<string, unknown>> = methodsOf(payload);
|
|
1797
1875
|
|
|
1798
|
-
expect(methods).toHaveLength(
|
|
1876
|
+
expect(methods).toHaveLength(9);
|
|
1799
1877
|
|
|
1800
1878
|
for (const method of methods) {
|
|
1801
1879
|
expect(method["methodId"]).not.toBe(ruleId.toString());
|
|
@@ -1870,6 +1948,24 @@ describe("GET /on-call-readiness/user/:userId - identifier masking", () => {
|
|
|
1870
1948
|
expect(call.select?.["telegramUserHandle"]).toBe(true);
|
|
1871
1949
|
expect(call.select?.["telegramChatId"]).toBeUndefined();
|
|
1872
1950
|
});
|
|
1951
|
+
|
|
1952
|
+
test("the slack read takes the username, never the addressable member id", async () => {
|
|
1953
|
+
await readUser();
|
|
1954
|
+
|
|
1955
|
+
const call: CapturedFindBy = firstCall(userSlackFindBy);
|
|
1956
|
+
|
|
1957
|
+
expect(call.select?.["slackUserName"]).toBe(true);
|
|
1958
|
+
expect(call.select?.["slackUserId"]).toBeUndefined();
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
test("the microsoft teams read takes the display name, never the addressable entra id", async () => {
|
|
1962
|
+
await readUser();
|
|
1963
|
+
|
|
1964
|
+
const call: CapturedFindBy = firstCall(userMicrosoftTeamsFindBy);
|
|
1965
|
+
|
|
1966
|
+
expect(call.select?.["microsoftTeamsUserName"]).toBe(true);
|
|
1967
|
+
expect(call.select?.["microsoftTeamsUserId"]).toBeUndefined();
|
|
1968
|
+
});
|
|
1873
1969
|
});
|
|
1874
1970
|
|
|
1875
1971
|
/*
|
|
@@ -1898,6 +1994,8 @@ interface StubRule {
|
|
|
1898
1994
|
userEmailId?: ObjectID | undefined;
|
|
1899
1995
|
userPushId?: ObjectID | undefined;
|
|
1900
1996
|
userTelegramId?: ObjectID | undefined;
|
|
1997
|
+
userSlackId?: ObjectID | undefined;
|
|
1998
|
+
userMicrosoftTeamsId?: ObjectID | undefined;
|
|
1901
1999
|
userWhatsAppId?: ObjectID | undefined;
|
|
1902
2000
|
userWebhookId?: ObjectID | undefined;
|
|
1903
2001
|
}
|
|
@@ -2037,17 +2135,22 @@ describe("GET /team/compliance-status/:teamId - the rebuilt service", () => {
|
|
|
2037
2135
|
]);
|
|
2038
2136
|
});
|
|
2039
2137
|
|
|
2040
|
-
test("DEFECT closed: a rule on Telegram, WhatsApp or a webhook now counts", async () => {
|
|
2138
|
+
test("DEFECT closed: a rule on Telegram, WhatsApp, Slack, Teams or a webhook now counts", async () => {
|
|
2041
2139
|
/*
|
|
2042
2140
|
* The old check read userCallId/userSmsId/userEmailId/userPushId off the
|
|
2043
2141
|
* rule row and treated a row carrying none of them as no rule at all, so a
|
|
2044
2142
|
* responder reachable only on Telegram, WhatsApp or a webhook was reported
|
|
2045
2143
|
* non-compliant while the runtime was quite happily paging them. A false RED
|
|
2046
|
-
* teaches admins to ignore the table, which is worse than no table.
|
|
2144
|
+
* teaches admins to ignore the table, which is worse than no table. Slack
|
|
2145
|
+
* and Microsoft Teams arrived after the fix and are staged alongside so the
|
|
2146
|
+
* defect cannot return for the channels that never lived through it.
|
|
2047
2147
|
*
|
|
2048
|
-
* All
|
|
2148
|
+
* All five channels are staged onto one user at once: whichever column the
|
|
2049
2149
|
* rule carries, the row is a rule.
|
|
2050
2150
|
*/
|
|
2151
|
+
const minor: StubSeverity = { id: ObjectID.generate(), name: "Minor" };
|
|
2152
|
+
const warn: StubSeverity = { id: ObjectID.generate(), name: "Warn" };
|
|
2153
|
+
|
|
2051
2154
|
stage({
|
|
2052
2155
|
settings: [
|
|
2053
2156
|
{ ruleType: ComplianceRuleType.HasIncidentOnCallRules, enabled: true },
|
|
@@ -2069,6 +2172,13 @@ describe("GET /team/compliance-status/:teamId - the rebuilt service", () => {
|
|
|
2069
2172
|
incidentSeverityId: major.id,
|
|
2070
2173
|
userWhatsAppId: ObjectID.generate(),
|
|
2071
2174
|
},
|
|
2175
|
+
{
|
|
2176
|
+
_id: "rule-slack",
|
|
2177
|
+
userId: ada.id,
|
|
2178
|
+
ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
|
|
2179
|
+
incidentSeverityId: minor.id,
|
|
2180
|
+
userSlackId: ObjectID.generate(),
|
|
2181
|
+
},
|
|
2072
2182
|
{
|
|
2073
2183
|
_id: "rule-webhook",
|
|
2074
2184
|
userId: ada.id,
|
|
@@ -2076,9 +2186,16 @@ describe("GET /team/compliance-status/:teamId - the rebuilt service", () => {
|
|
|
2076
2186
|
alertSeverityId: page.id,
|
|
2077
2187
|
userWebhookId: ObjectID.generate(),
|
|
2078
2188
|
},
|
|
2189
|
+
{
|
|
2190
|
+
_id: "rule-microsoft-teams",
|
|
2191
|
+
userId: ada.id,
|
|
2192
|
+
ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
|
|
2193
|
+
alertSeverityId: warn.id,
|
|
2194
|
+
userMicrosoftTeamsId: ObjectID.generate(),
|
|
2195
|
+
},
|
|
2079
2196
|
],
|
|
2080
|
-
incidentSeverities: [critical, major],
|
|
2081
|
-
alertSeverities: [page],
|
|
2197
|
+
incidentSeverities: [critical, major, minor],
|
|
2198
|
+
alertSeverities: [page, warn],
|
|
2082
2199
|
});
|
|
2083
2200
|
|
|
2084
2201
|
const statuses: Array<Record<string, unknown>> = statusesOf(
|
|
@@ -2093,7 +2210,7 @@ describe("GET /team/compliance-status/:teamId - the rebuilt service", () => {
|
|
|
2093
2210
|
/*
|
|
2094
2211
|
* The structural half of the fix. The three formerly-invisible channels were
|
|
2095
2212
|
* invisible because they were never SELECTed; asserting that NONE of the
|
|
2096
|
-
*
|
|
2213
|
+
* nine is selected means no future edit can reintroduce a partial column
|
|
2097
2214
|
* list and quietly start under-counting again.
|
|
2098
2215
|
*/
|
|
2099
2216
|
stage({
|
|
@@ -2116,6 +2233,8 @@ describe("GET /team/compliance-status/:teamId - the rebuilt service", () => {
|
|
|
2116
2233
|
"userEmailId",
|
|
2117
2234
|
"userPushId",
|
|
2118
2235
|
"userTelegramId",
|
|
2236
|
+
"userSlackId",
|
|
2237
|
+
"userMicrosoftTeamsId",
|
|
2119
2238
|
"userWhatsAppId",
|
|
2120
2239
|
"userWebhookId",
|
|
2121
2240
|
]) {
|
|
@@ -30,7 +30,7 @@ import { beforeEach, describe, expect, test } from "@jest/globals";
|
|
|
30
30
|
* methods".
|
|
31
31
|
*
|
|
32
32
|
* This router is the ONLY gate under these routes, and that is not a stylistic
|
|
33
|
-
* observation. The
|
|
33
|
+
* observation. The nine notification method models are scoped to the person
|
|
34
34
|
* who owns the device — the essay at the top of UserEmail.ts is the record of
|
|
35
35
|
* what happened the one time that scope was widened — so the service behind
|
|
36
36
|
* these handlers does every read and write with `isRoot: true`, which means the
|
|
@@ -362,7 +362,7 @@ describe("the routes it registers", () => {
|
|
|
362
362
|
* Extending BaseAPI to inherit `this.router` would register a whole model's
|
|
363
363
|
* CRUD routes as a side effect — dead code that is shadowed by the real
|
|
364
364
|
* registration until the day the mount order changes. There is no model to
|
|
365
|
-
* inherit here in any case: the
|
|
365
|
+
* inherit here in any case: the nine method models stay owner-scoped and
|
|
366
366
|
* this router deliberately never exposes them.
|
|
367
367
|
*/
|
|
368
368
|
expect(UserNotificationMethodAdminAPI).toBe(mockRouter);
|
|
@@ -459,7 +459,17 @@ describe("LlmLogService autonomous-token lane isolation", () => {
|
|
|
459
459
|
expect(sql).toContain('FROM "AIRun" AS "run"');
|
|
460
460
|
expect(sql).toContain('"run"."triggeredByIncidentId" IS NOT NULL');
|
|
461
461
|
expect(params[3]).toEqual(legacyIncidentFeatures);
|
|
462
|
-
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* The incident lane reads only its own legacy list, so it must bind
|
|
465
|
+
* exactly four parameters. Binding the unread alert list as $5 is what
|
|
466
|
+
* PostgreSQL rejected with "bind message supplies 5 parameters, but
|
|
467
|
+
* prepared statement "" requires 4", which silently disabled every capped
|
|
468
|
+
* incident investigation. LlmLogServiceTokenAggregateParams.test.ts holds
|
|
469
|
+
* the exhaustive placeholder/binding contract.
|
|
470
|
+
*/
|
|
471
|
+
expect(params).toHaveLength(4);
|
|
472
|
+
expect(sql).not.toContain("$5");
|
|
463
473
|
});
|
|
464
474
|
|
|
465
475
|
test("subjectless totals exclude identifiable legacy incident and alert rows", async () => {
|
|
@@ -11,8 +11,10 @@ import UserNotificationRuleService, {
|
|
|
11
11
|
} from "../../../Server/Services/UserNotificationRuleService";
|
|
12
12
|
import UserOnCallLogService from "../../../Server/Services/UserOnCallLogService";
|
|
13
13
|
import UserOnCallLogTimelineService from "../../../Server/Services/UserOnCallLogTimelineService";
|
|
14
|
+
import UserMicrosoftTeamsService from "../../../Server/Services/UserMicrosoftTeamsService";
|
|
14
15
|
import UserPushService from "../../../Server/Services/UserPushService";
|
|
15
16
|
import UserService from "../../../Server/Services/UserService";
|
|
17
|
+
import UserSlackService from "../../../Server/Services/UserSlackService";
|
|
16
18
|
import UserSmsService from "../../../Server/Services/UserSmsService";
|
|
17
19
|
import UserTelegramService from "../../../Server/Services/UserTelegramService";
|
|
18
20
|
import UserWebhookService from "../../../Server/Services/UserWebhookService";
|
|
@@ -74,7 +76,7 @@ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
|
74
76
|
* a user id is global.
|
|
75
77
|
* R3 a rule's method FK must point at a method row owned by the rule's own
|
|
76
78
|
* user - on create, on update, through the FK column and through the
|
|
77
|
-
* relation slot, on every one of the
|
|
79
|
+
* relation slot, on every one of the nine channels, and on update with
|
|
78
80
|
* the owner re-read from the DATABASE because the body is written by the
|
|
79
81
|
* party under suspicion.
|
|
80
82
|
* R6 the audit line and the owner's warning email are keyed on the actor the
|
|
@@ -135,14 +137,14 @@ const ADMIN_EMAIL: string = "ada.admin@example.test";
|
|
|
135
137
|
const VICTIM_EMAIL: string = "vic.responder@example.test";
|
|
136
138
|
|
|
137
139
|
/*
|
|
138
|
-
* The
|
|
140
|
+
* The nine channels, each named by the FK column that carries it, the relation
|
|
139
141
|
* slot that is the same write spelled differently, the service the guard has to
|
|
140
142
|
* consult for that channel's owner, and the word that must appear in the
|
|
141
143
|
* refusal so an operator can tell WHICH method was wrong.
|
|
142
144
|
*
|
|
143
145
|
* Everything about method ownership is driven off this table on purpose. The
|
|
144
|
-
* one realistic way for the guard to be wrong is to cover
|
|
145
|
-
* forget the
|
|
146
|
+
* one realistic way for the guard to be wrong is to cover eight channels and
|
|
147
|
+
* forget the ninth, and an attacker choosing between nine doors will always
|
|
146
148
|
* choose the unlocked one - so a per-channel loop is the only shape of test
|
|
147
149
|
* that means anything here.
|
|
148
150
|
*/
|
|
@@ -188,6 +190,18 @@ const CHANNELS: Array<ChannelFixture> = [
|
|
|
188
190
|
label: "Telegram",
|
|
189
191
|
service: UserTelegramService as unknown as MethodServiceLike,
|
|
190
192
|
},
|
|
193
|
+
{
|
|
194
|
+
idColumn: "userSlackId",
|
|
195
|
+
relationColumn: "userSlack",
|
|
196
|
+
label: "Slack",
|
|
197
|
+
service: UserSlackService as unknown as MethodServiceLike,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
idColumn: "userMicrosoftTeamsId",
|
|
201
|
+
relationColumn: "userMicrosoftTeams",
|
|
202
|
+
label: "Microsoft Teams",
|
|
203
|
+
service: UserMicrosoftTeamsService as unknown as MethodServiceLike,
|
|
204
|
+
},
|
|
191
205
|
{
|
|
192
206
|
idColumn: "userPushId",
|
|
193
207
|
relationColumn: "userPush",
|
|
@@ -860,7 +874,7 @@ describe("Administrative notification rule edit guards", () => {
|
|
|
860
874
|
describe("R3 on create: a rule's method must belong to the rule's user", () => {
|
|
861
875
|
test("the guard covers exactly the notification method columns the model declares", () => {
|
|
862
876
|
/*
|
|
863
|
-
* Derived from the model rather than restated, so that
|
|
877
|
+
* Derived from the model rather than restated, so that a tenth channel
|
|
864
878
|
* added to UserNotificationRule and forgotten in the guard fails HERE,
|
|
865
879
|
* loudly, instead of quietly becoming the one unlocked door.
|
|
866
880
|
*/
|
|
@@ -873,7 +887,7 @@ describe("Administrative notification rule edit guards", () => {
|
|
|
873
887
|
});
|
|
874
888
|
|
|
875
889
|
// If the derivation itself ever breaks, fail rather than pass vacuously.
|
|
876
|
-
expect(columnsOnModel).toHaveLength(
|
|
890
|
+
expect(columnsOnModel).toHaveLength(9);
|
|
877
891
|
|
|
878
892
|
expect(
|
|
879
893
|
[
|
|
@@ -1783,7 +1797,7 @@ describe("Administrative notification rule edit guards", () => {
|
|
|
1783
1797
|
}),
|
|
1784
1798
|
),
|
|
1785
1799
|
).rejects.toThrow(
|
|
1786
|
-
"Call, SMS, WhatsApp, Telegram, Webhook, Email, or Push notification is required",
|
|
1800
|
+
"Call, SMS, WhatsApp, Telegram, Slack, Microsoft Teams, Webhook, Email, or Push notification is required",
|
|
1787
1801
|
);
|
|
1788
1802
|
});
|
|
1789
1803
|
|
|
@@ -1995,7 +2009,7 @@ describe("Administrative notification rule edit guards", () => {
|
|
|
1995
2009
|
updatePayload({ patch: { userEmailId: null } }),
|
|
1996
2010
|
),
|
|
1997
2011
|
).rejects.toThrow(
|
|
1998
|
-
"Call, SMS, WhatsApp, Telegram, Webhook, Email, or Push notification is required",
|
|
2012
|
+
"Call, SMS, WhatsApp, Telegram, Slack, Microsoft Teams, Webhook, Email, or Push notification is required",
|
|
1999
2013
|
);
|
|
2000
2014
|
});
|
|
2001
2015
|
|
|
@@ -46,7 +46,7 @@ import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
|
|
|
46
46
|
* with a UserNotificationRule it assembled in memory and never saved: there is
|
|
47
47
|
* no rule row to claim and no id to look up, so the first half is meaningless
|
|
48
48
|
* to it while the second half is exactly what it needs. That makes the SEAM
|
|
49
|
-
* between the two halves — not the
|
|
49
|
+
* between the two halves — not the nine channel blocks, which
|
|
50
50
|
* NotificationChannelEventCoverage.test.ts owns — the thing worth pinning, and
|
|
51
51
|
* this file pins five properties of it:
|
|
52
52
|
*
|
|
@@ -731,7 +731,7 @@ describe("UserNotificationRuleService - the deliverNotificationForRule extractio
|
|
|
731
731
|
* the loaded relation's userId against the rule's, and it can only report
|
|
732
732
|
* a mismatch it can SEE - an unselected column arrives as `undefined` and
|
|
733
733
|
* is deliberately read as "no evidence", so dropping any one of these
|
|
734
|
-
*
|
|
734
|
+
* nine silently disables the backstop for that channel.
|
|
735
735
|
*
|
|
736
736
|
* Kept as an exact-shape assertion on purpose. This test caught the
|
|
737
737
|
* column being added, which is the whole argument for not relaxing it
|
|
@@ -762,6 +762,18 @@ describe("UserNotificationRuleService - the deliverNotificationForRule extractio
|
|
|
762
762
|
isVerified: true,
|
|
763
763
|
userId: true,
|
|
764
764
|
},
|
|
765
|
+
userSlack: {
|
|
766
|
+
slackUserId: true,
|
|
767
|
+
slackUserName: true,
|
|
768
|
+
isVerified: true,
|
|
769
|
+
userId: true,
|
|
770
|
+
},
|
|
771
|
+
userMicrosoftTeams: {
|
|
772
|
+
microsoftTeamsUserId: true,
|
|
773
|
+
microsoftTeamsUserName: true,
|
|
774
|
+
isVerified: true,
|
|
775
|
+
userId: true,
|
|
776
|
+
},
|
|
765
777
|
userWebhook: {
|
|
766
778
|
webhookUrl: true,
|
|
767
779
|
name: true,
|
|
@@ -788,6 +800,8 @@ describe("UserNotificationRuleService - the deliverNotificationForRule extractio
|
|
|
788
800
|
["userSms"],
|
|
789
801
|
["userWhatsApp"],
|
|
790
802
|
["userTelegram"],
|
|
803
|
+
["userSlack"],
|
|
804
|
+
["userMicrosoftTeams"],
|
|
791
805
|
["userEmail"],
|
|
792
806
|
["userPush"],
|
|
793
807
|
])(
|
|
@@ -806,6 +820,8 @@ describe("UserNotificationRuleService - the deliverNotificationForRule extractio
|
|
|
806
820
|
["userSms", "phone"],
|
|
807
821
|
["userWhatsApp", "phone"],
|
|
808
822
|
["userTelegram", "telegramChatId"],
|
|
823
|
+
["userSlack", "slackUserId"],
|
|
824
|
+
["userMicrosoftTeams", "microsoftTeamsUserId"],
|
|
809
825
|
["userEmail", "email"],
|
|
810
826
|
["userPush", "deviceToken"],
|
|
811
827
|
])(
|
|
@@ -161,13 +161,15 @@ const TELEGRAM_METHOD_ID: ObjectID = new ObjectID(
|
|
|
161
161
|
"ccccccc2-cccc-4ccc-8ccc-cccccccccccc",
|
|
162
162
|
);
|
|
163
163
|
|
|
164
|
-
/* The
|
|
164
|
+
/* The nine FK columns a rule can name a notification method through. */
|
|
165
165
|
type MethodColumn =
|
|
166
166
|
| "userEmailId"
|
|
167
167
|
| "userSmsId"
|
|
168
168
|
| "userCallId"
|
|
169
169
|
| "userWhatsAppId"
|
|
170
170
|
| "userTelegramId"
|
|
171
|
+
| "userSlackId"
|
|
172
|
+
| "userMicrosoftTeamsId"
|
|
171
173
|
| "userWebhookId"
|
|
172
174
|
| "userPushId";
|
|
173
175
|
|
|
@@ -177,6 +179,8 @@ const ALL_METHOD_COLUMNS: Array<MethodColumn> = [
|
|
|
177
179
|
"userCallId",
|
|
178
180
|
"userWhatsAppId",
|
|
179
181
|
"userTelegramId",
|
|
182
|
+
"userSlackId",
|
|
183
|
+
"userMicrosoftTeamsId",
|
|
180
184
|
"userWebhookId",
|
|
181
185
|
"userPushId",
|
|
182
186
|
];
|