@oneuptime/common 12.0.8 → 12.0.9
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/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
- package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
- package/Models/DatabaseModels/AIConversation.ts +32 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
- package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
- package/Models/DatabaseModels/Index.ts +12 -4
- package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +139 -9
- package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
- package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +8 -8
- package/Models/DatabaseModels/NetworkDevice.ts +141 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
- package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
- package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
- package/Models/DatabaseModels/Project.ts +78 -0
- package/Models/DatabaseModels/UserCall.ts +42 -0
- package/Models/DatabaseModels/UserEmail.ts +44 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
- package/Models/DatabaseModels/UserPush.ts +49 -0
- package/Models/DatabaseModels/UserSMS.ts +43 -0
- package/Models/DatabaseModels/UserTelegram.ts +59 -0
- package/Models/DatabaseModels/UserWebhook.ts +52 -0
- package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
- package/Server/API/AIChatAPI.ts +315 -1
- package/Server/API/DashboardAPI.ts +217 -1
- package/Server/API/OnCallReadinessAPI.ts +841 -0
- package/Server/API/TeamComplianceAPI.ts +69 -17
- package/Server/API/TelemetryAPI.ts +220 -12
- package/Server/EnvironmentConfig.ts +52 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
- package/Server/Infrastructure/Queue.ts +78 -13
- package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
- package/Server/Services/AIService.ts +7 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/AlertSeverityService.ts +63 -0
- package/Server/Services/DashboardService.ts +9 -10
- package/Server/Services/DatabaseService.ts +32 -2
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/IncidentSeverityService.ts +76 -0
- package/Server/Services/Index.ts +12 -4
- package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
- package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +4 -4
- package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +89 -20
- package/Server/Services/LogAggregationService.ts +45 -8
- package/Server/Services/MetricAggregationService.ts +121 -0
- package/Server/Services/MetricService.ts +7 -7
- package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
- package/Server/Services/NetworkDeviceLinkService.ts +84 -0
- package/Server/Services/NetworkDeviceService.ts +140 -0
- package/Server/Services/NetworkSiteService.ts +77 -25
- package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
- package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
- package/Server/Services/OnCallReadinessService.ts +2803 -0
- package/Server/Services/OnCallSetupReminderService.ts +955 -0
- package/Server/Services/ProfileAggregationService.ts +123 -0
- package/Server/Services/StatusPageService.ts +9 -10
- package/Server/Services/TeamComplianceService.ts +429 -252
- package/Server/Services/UserCallService.ts +26 -1
- package/Server/Services/UserEmailService.ts +26 -1
- package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
- package/Server/Services/UserNotificationRuleService.ts +3812 -333
- package/Server/Services/UserOnCallLogService.ts +561 -48
- package/Server/Services/UserPushService.ts +29 -0
- package/Server/Services/UserService.ts +11 -0
- package/Server/Services/UserSmsService.ts +26 -1
- package/Server/Services/UserTelegramService.ts +24 -1
- package/Server/Services/UserWebhookService.ts +28 -1
- package/Server/Services/UserWhatsAppService.ts +24 -1
- package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
- package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
- package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
- package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
- package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
- package/Server/Types/Workflow/Components/API/Get.ts +1 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
- package/Server/Types/Workflow/Components/API/Post.ts +1 -1
- package/Server/Types/Workflow/Components/API/Put.ts +1 -1
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
- package/Server/Types/Workflow/Components/Email.ts +25 -7
- package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
- package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
- package/Server/Utils/AI/Toolbox/Index.ts +48 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
- package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
- package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
- package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
- package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
- package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
- package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
- package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
- package/Server/Utils/ClientIp.ts +221 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
- package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
- package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
- package/Server/Utils/Express.ts +12 -17
- package/Server/Utils/LLM/LLMService.ts +85 -8
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
- package/Server/Utils/SSRFProtection.ts +98 -23
- package/Server/Utils/StartServer.ts +12 -3
- package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +25 -25
- package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
- package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
- package/Tests/Models/InventoryItemModel.test.ts +174 -0
- package/Tests/Models/InventoryItemNaming.test.ts +302 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
- package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
- package/Tests/Server/API/Helpers.ts +24 -15
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
- package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
- package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
- package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
- package/Tests/Server/Services/{TelemetryEntityManualCreate.test.ts → InventoryItemManualCreate.test.ts} +27 -27
- package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
- package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
- package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
- package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
- package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
- package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
- package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
- package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
- package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
- package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
- package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
- package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
- package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
- package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
- package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
- package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
- package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
- package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
- package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
- package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
- package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
- package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
- package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
- package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
- package/Tests/Server/Utils/ClientIp.test.ts +438 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
- package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
- package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +15 -15
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
- package/Tests/Types/IP/IP.test.ts +263 -0
- package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
- package/Tests/Types/IP/IPv6.test.ts +12 -1
- package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +6 -6
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
- package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
- package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +112 -0
- package/Tests/Types/Workflow/Templates.test.ts +916 -101
- package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
- package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
- package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
- package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
- package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
- package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
- package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +72 -0
- package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +242 -7
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +15 -7
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +242 -28
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +59 -1
- package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
- package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
- package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
- package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
- package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
- package/Tests/UI/EsbuildConfig.test.ts +607 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
- package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
- package/Tests/UI/Utils/Platform.test.ts +147 -0
- package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +487 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
- package/Tests/__mocks__/bullmq.js +55 -0
- package/Types/AI/AIChatMessageStatus.ts +8 -1
- package/Types/AI/AIChatTypes.ts +12 -0
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
- package/Types/Exception/ExceptionCode.ts +2 -0
- package/Types/Exception/ServiceUnavailableException.ts +8 -0
- package/Types/Exception/TooManyRequestsException.ts +8 -0
- package/Types/IP/IP.ts +93 -47
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +49 -3
- package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
- package/Types/OnCallDutyPolicy/Layer.ts +203 -149
- package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
- package/Types/Permission.ts +193 -0
- package/Types/Telemetry/EntityRelationshipType.ts +1 -1
- package/Types/Telemetry/EntitySource.ts +1 -1
- package/Types/Telemetry/EntityType.ts +1 -1
- package/Types/Telemetry/EntityTypeGroups.ts +1 -1
- package/Types/Workflow/Components/BaseModel.ts +75 -29
- package/Types/Workflow/Components/Discord.ts +1 -0
- package/Types/Workflow/Components/Email.ts +12 -3
- package/Types/Workflow/Components/JavaScript.ts +7 -0
- package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
- package/Types/Workflow/Components/Slack.ts +1 -0
- package/Types/Workflow/Components/Telegram.ts +1 -0
- package/Types/Workflow/TemplateSyntax.ts +44 -0
- package/Types/Workflow/Templates.ts +2097 -45
- package/UI/Components/Calendar/Calendar.css +43 -0
- package/UI/Components/Calendar/Calendar.tsx +8 -0
- package/UI/Components/Card/Card.tsx +2 -2
- package/UI/Components/Checkbox/Checkbox.tsx +16 -0
- package/UI/Components/Dictionary/Dictionary.tsx +48 -9
- package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
- package/UI/Components/Input/Input.tsx +1 -0
- package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
- package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
- package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
- package/UI/Components/LogsViewer/types.ts +23 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
- package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
- package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
- package/UI/Components/Table/Table.tsx +49 -16
- package/UI/Components/Table/TableBody.tsx +53 -28
- package/UI/Components/Table/TableHeader.tsx +13 -0
- package/UI/Components/Table/TableRow.tsx +58 -26
- package/UI/Components/Workflow/ArgumentsForm.tsx +155 -9
- package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +10 -1
- package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
- package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
- package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
- package/UI/Components/Workflow/GraphLint.ts +42 -5
- package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +154 -22
- package/UI/Components/Workflow/ModelSchema.ts +115 -33
- package/UI/Components/Workflow/RunStatusWatcher.ts +1 -1
- package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
- package/UI/Components/Workflow/UseRunWatch.ts +212 -0
- package/UI/Components/Workflow/VariableModal.tsx +6 -2
- package/UI/Components/Workflow/Workflow.tsx +49 -3
- package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
- package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
- package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
- package/UI/Utils/Platform.ts +149 -0
- package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
- package/UI/esbuild-config.js +22 -1
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +888 -164
- package/Utils/Telemetry/CrossSignalScope.ts +502 -0
- package/Utils/Telemetry/EntityKey.ts +5 -5
- package/Utils/Telemetry/EntityRelationship.ts +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +12 -4
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +167 -31
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +27 -27
- package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +80 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
- package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +237 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +165 -13
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
- package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +129 -18
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +45 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +72 -13
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +1 -0
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +54 -0
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/DashboardService.js +10 -9
- package/build/dist/Server/Services/DashboardService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +24 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +12 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +6 -6
- package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +68 -23
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
- package/build/dist/Server/Services/LogAggregationService.js +27 -8
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +80 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +6 -6
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +53 -13
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
- package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
- package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
- package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +11 -10
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceService.js +312 -160
- package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +24 -1
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +24 -1
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +26 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +24 -1
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +22 -1
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +25 -1
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
- package/build/dist/Server/Utils/ClientIp.js +137 -0
- package/build/dist/Server/Utils/ClientIp.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -12
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +82 -21
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +11 -11
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatTypes.js +12 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +2 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
- package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
- package/build/dist/Types/IP/IP.js +87 -43
- package/build/dist/Types/IP/IP.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +174 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +66 -28
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Discord.js +1 -0
- package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Email.js +12 -3
- package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
- package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Slack.js +1 -0
- package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
- package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
- package/build/dist/Types/Workflow/TemplateSyntax.js +12 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -1
- package/build/dist/Types/Workflow/Templates.js +1931 -38
- package/build/dist/Types/Workflow/Templates.js.map +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -11
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -0
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +27 -15
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +24 -18
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +9 -1
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +29 -21
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +130 -12
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +4 -4
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +33 -4
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +116 -21
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelSchema.js +72 -34
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
- package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
- package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +41 -1
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Platform.js +118 -0
- package/build/dist/UI/Utils/Platform.js.map +1 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +642 -136
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +5 -5
- package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
- package/jest.config.json +1 -0
- package/package.json +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
|
@@ -0,0 +1,1119 @@
|
|
|
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 { cleanup, render, waitFor } from "@testing-library/react";
|
|
11
|
+
import React, { FunctionComponent, ReactElement } from "react";
|
|
12
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The four on-call rule pages - incident, alert, incident episode, alert
|
|
16
|
+
* episode - were four ~370-line copies of one another and are now four sets of
|
|
17
|
+
* props on one shared component. Everything a copy-paste page got wrong loudly
|
|
18
|
+
* (a compile error, a missing import) a props object gets wrong silently, and
|
|
19
|
+
* two of those silent failures page real people at the wrong time:
|
|
20
|
+
*
|
|
21
|
+
* - the severity FOREIGN KEY COLUMN. A rule is tied to its severity band by
|
|
22
|
+
* `incidentSeverityId` or `alertSeverityId`. Write one and filter on the
|
|
23
|
+
* other and nothing throws: the table lists rules for EVERY severity, so a
|
|
24
|
+
* Sev 4 pages exactly like a Sev 1, and rules created from that table are
|
|
25
|
+
* stamped with a column nobody reads back.
|
|
26
|
+
*
|
|
27
|
+
* - the severity MODEL. It does NOT follow from the rule type the way the
|
|
28
|
+
* names suggest. The alert episode page enumerates AlertSeverity; the
|
|
29
|
+
* incident episode page enumerates IncidentSeverity. Anything that derives
|
|
30
|
+
* one of the two axes from the other gets one of those pages wrong.
|
|
31
|
+
*
|
|
32
|
+
* The third pin here is the stored-preferences key. It has to carry the
|
|
33
|
+
* severity id because one table is rendered per severity on a single route;
|
|
34
|
+
* drop the id and paging one table repaginates every other table on the page.
|
|
35
|
+
* That is a pure-UX bug, invisible to the type checker, and it was already
|
|
36
|
+
* explained in a comment in the original source - which is exactly why it is
|
|
37
|
+
* asserted rather than trusted to survive the next edit.
|
|
38
|
+
*
|
|
39
|
+
* The fourth is where the "Notification Method" dropdown comes from, which is
|
|
40
|
+
* now two different places depending on whose rules are on screen. For the
|
|
41
|
+
* signed-in user it is still seven reads of the seven method models, and that
|
|
42
|
+
* path is the four settings pages every existing user relies on. For anybody
|
|
43
|
+
* else those reads are REFUSED - the models are scoped to the person who owns
|
|
44
|
+
* the device, because the columns behind them are the raw phone number, the
|
|
45
|
+
* webhook bearer url, the push device token, the telegram chat id and the
|
|
46
|
+
* verification code - so the caller hands the list over instead, masked. Both
|
|
47
|
+
* halves are asserted here: that the self-serve path still reads what it always
|
|
48
|
+
* read, and that pointing the table at somebody else reads nothing at all.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
const PROJECT_ID_STRING: string = "00000000-0000-4000-8000-0000000000a1";
|
|
52
|
+
const CURRENT_USER_ID_STRING: string = "00000000-0000-4000-8000-0000000000b1";
|
|
53
|
+
const OTHER_USER_ID_STRING: string = "00000000-0000-4000-8000-0000000000b2";
|
|
54
|
+
const SEVERITY_ONE_ID: string = "00000000-0000-4000-8000-0000000000c1";
|
|
55
|
+
const SEVERITY_TWO_ID: string = "00000000-0000-4000-8000-0000000000c2";
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* The supplied methods. Each carries the id of its own row - which is what a
|
|
59
|
+
* rule's userSmsId / userEmailId actually reference - alongside a mask in
|
|
60
|
+
* exactly the shape OnCallReadinessService.maskIdentifier emits, and a raw value
|
|
61
|
+
* that must never appear in a dropdown label.
|
|
62
|
+
*/
|
|
63
|
+
const SUPPLIED_SMS_ID: string = "00000000-0000-4000-8000-0000000000d1";
|
|
64
|
+
const SUPPLIED_EMAIL_ID: string = "00000000-0000-4000-8000-0000000000d2";
|
|
65
|
+
const SUPPLIED_TELEGRAM_ID: string = "00000000-0000-4000-8000-0000000000d3";
|
|
66
|
+
const UNKNOWN_METHOD_ID: string = "00000000-0000-4000-8000-0000000000d9";
|
|
67
|
+
|
|
68
|
+
const MASKED_SMS: string = "+1 ••• ••• 4821";
|
|
69
|
+
const MASKED_EMAIL: string = "j•••@example.com";
|
|
70
|
+
const MASKED_TELEGRAM: string = "@ja•••";
|
|
71
|
+
|
|
72
|
+
const RAW_PHONE: string = "+15551234821";
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* Restated rather than imported, so that changing the wording in the component
|
|
76
|
+
* fails here. This string is what distinguishes "deliberately never page me for
|
|
77
|
+
* this" from a row that merely looks broken, and the whole risk it guards
|
|
78
|
+
* against is somebody deleting the row because the cell was blank.
|
|
79
|
+
*/
|
|
80
|
+
const OPT_OUT_LABEL: string = "Muted - notifications turned off for this rule";
|
|
81
|
+
|
|
82
|
+
/* The signed-in user's own email, which their own page IS allowed to read. */
|
|
83
|
+
const OWN_EMAIL_ID: string = "00000000-0000-4000-8000-0000000000e1";
|
|
84
|
+
const OWN_EMAIL_ADDRESS: string = "self.serve@example.com";
|
|
85
|
+
|
|
86
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* The arrow wrappers are load bearing: jest.mock is hoisted above the compiled
|
|
90
|
+
* requires, so the consts above are still in their temporal dead zone when the
|
|
91
|
+
* factory body runs. Dereferencing them lazily, at call time, is what works.
|
|
92
|
+
*/
|
|
93
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
94
|
+
return {
|
|
95
|
+
__esModule: true,
|
|
96
|
+
default: {
|
|
97
|
+
getList: (...args: Array<any>) => {
|
|
98
|
+
return getListMock(...args);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
jest.mock("../../../UI/Utils/User", () => {
|
|
105
|
+
return {
|
|
106
|
+
__esModule: true,
|
|
107
|
+
default: {
|
|
108
|
+
getUserId: () => {
|
|
109
|
+
return currentUserId;
|
|
110
|
+
},
|
|
111
|
+
/*
|
|
112
|
+
* The delete guard asks whether the viewer is a master admin before it
|
|
113
|
+
* offers a Delete control at all. A stub missing this method does not
|
|
114
|
+
* make the guard fail an assertion - it throws
|
|
115
|
+
* "isMasterAdmin is not a function" at render, taking every test in the
|
|
116
|
+
* file with it, which reads as 33 unrelated failures rather than one
|
|
117
|
+
* missing stub.
|
|
118
|
+
*/
|
|
119
|
+
isMasterAdmin: () => {
|
|
120
|
+
return false;
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* A stand-in ModelTable that records the props it was handed. Rendering the
|
|
128
|
+
* real one would drag in the pager, the facet bar and the URL state, none of
|
|
129
|
+
* which this file is about - what matters is the query the table is given, the
|
|
130
|
+
* row it builds on create, and the key it stores preferences under.
|
|
131
|
+
*/
|
|
132
|
+
interface CapturedFormField {
|
|
133
|
+
overrideFieldKey?: string | undefined;
|
|
134
|
+
dropdownOptions?: Array<{ label: string; value: string }> | undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* A declared column. `field` is captured because ModelTable folds it into the
|
|
139
|
+
* projection it sends alongside `selectMoreFields` - so a column is a REQUEST,
|
|
140
|
+
* not merely a renderer, and asserting only on selectMoreFields would miss half
|
|
141
|
+
* of what goes on the wire.
|
|
142
|
+
*/
|
|
143
|
+
interface CapturedColumn {
|
|
144
|
+
title: string;
|
|
145
|
+
field: Record<string, unknown>;
|
|
146
|
+
getElement?: ((item: UserNotificationRule) => ReactElement) | undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface CapturedTableProps {
|
|
150
|
+
userPreferencesKey: string;
|
|
151
|
+
query: Record<string, ObjectID | NotificationRuleType | undefined>;
|
|
152
|
+
onBeforeCreate: (
|
|
153
|
+
model: UserNotificationRule,
|
|
154
|
+
miscDataProps: Record<string, unknown>,
|
|
155
|
+
) => Promise<UserNotificationRule>;
|
|
156
|
+
isCreateable: boolean;
|
|
157
|
+
isDeleteable: boolean;
|
|
158
|
+
isEditable: boolean;
|
|
159
|
+
formFields: Array<CapturedFormField>;
|
|
160
|
+
selectMoreFields: Record<string, unknown>;
|
|
161
|
+
columns: Array<CapturedColumn>;
|
|
162
|
+
cardProps: { title: string; description: string };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let capturedTables: Array<CapturedTableProps> = [];
|
|
166
|
+
|
|
167
|
+
jest.mock("../../../UI/Components/ModelTable/ModelTable", () => {
|
|
168
|
+
return {
|
|
169
|
+
__esModule: true,
|
|
170
|
+
default: (props: CapturedTableProps) => {
|
|
171
|
+
capturedTables.push(props);
|
|
172
|
+
return null;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
import OnCallRulesTable, {
|
|
178
|
+
NotificationMethodChoice,
|
|
179
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationRule/OnCallRulesTable";
|
|
180
|
+
import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
|
|
181
|
+
import AlertOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/AlertOnCallRules";
|
|
182
|
+
import EpisodeOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/EpisodeOnCallRules";
|
|
183
|
+
import IncidentEpisodeOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/IncidentEpisodeOnCallRules";
|
|
184
|
+
import IncidentOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/IncidentOnCallRules";
|
|
185
|
+
import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
|
|
186
|
+
import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
|
|
187
|
+
import UserCall from "../../../Models/DatabaseModels/UserCall";
|
|
188
|
+
import UserEmail from "../../../Models/DatabaseModels/UserEmail";
|
|
189
|
+
import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
|
|
190
|
+
import UserPush from "../../../Models/DatabaseModels/UserPush";
|
|
191
|
+
import UserSMS from "../../../Models/DatabaseModels/UserSMS";
|
|
192
|
+
import UserTelegram from "../../../Models/DatabaseModels/UserTelegram";
|
|
193
|
+
import UserWebhook from "../../../Models/DatabaseModels/UserWebhook";
|
|
194
|
+
import UserWhatsApp from "../../../Models/DatabaseModels/UserWhatsApp";
|
|
195
|
+
import Route from "../../../Types/API/Route";
|
|
196
|
+
import Email from "../../../Types/Email";
|
|
197
|
+
import NotificationRuleType from "../../../Types/NotificationRule/NotificationRuleType";
|
|
198
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
199
|
+
import ProjectUtil from "../../../UI/Utils/Project";
|
|
200
|
+
|
|
201
|
+
/*
|
|
202
|
+
* The seven models whose rows only their owner may read. They are listed
|
|
203
|
+
* together because the assertion that matters is about the SET: an admin
|
|
204
|
+
* surface that reads any one of them is reading a row it is not entitled to,
|
|
205
|
+
* and which one hardly matters.
|
|
206
|
+
*/
|
|
207
|
+
const NOTIFICATION_METHOD_MODELS: Array<unknown> = [
|
|
208
|
+
UserEmail,
|
|
209
|
+
UserSMS,
|
|
210
|
+
UserCall,
|
|
211
|
+
UserPush,
|
|
212
|
+
UserWhatsApp,
|
|
213
|
+
UserTelegram,
|
|
214
|
+
UserWebhook,
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
* What the admin surface hands over in place of those reads: a channel name, the
|
|
219
|
+
* id of the row, and a mask. Telegram is unverified on purpose - the self-serve
|
|
220
|
+
* path filters `isVerified: true` in six of its seven queries, so a supplied
|
|
221
|
+
* list that offered unverified methods would be the two paths disagreeing about
|
|
222
|
+
* what is pickable.
|
|
223
|
+
*/
|
|
224
|
+
const SUPPLIED_METHODS: Array<NotificationMethodChoice> = [
|
|
225
|
+
{
|
|
226
|
+
methodType: "SMS",
|
|
227
|
+
methodId: SUPPLIED_SMS_ID,
|
|
228
|
+
maskedIdentifier: MASKED_SMS,
|
|
229
|
+
isVerified: true,
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
methodType: "Email",
|
|
233
|
+
methodId: SUPPLIED_EMAIL_ID,
|
|
234
|
+
maskedIdentifier: MASKED_EMAIL,
|
|
235
|
+
isVerified: true,
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
methodType: "Telegram",
|
|
239
|
+
methodId: SUPPLIED_TELEGRAM_ID,
|
|
240
|
+
maskedIdentifier: MASKED_TELEGRAM,
|
|
241
|
+
isVerified: false,
|
|
242
|
+
},
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
/* Every foreign key a rule can point a method at. */
|
|
246
|
+
const METHOD_FOREIGN_KEYS: Array<keyof UserNotificationRule> = [
|
|
247
|
+
"userEmailId",
|
|
248
|
+
"userSmsId",
|
|
249
|
+
"userCallId",
|
|
250
|
+
"userPushId",
|
|
251
|
+
"userWhatsAppId",
|
|
252
|
+
"userTelegramId",
|
|
253
|
+
"userWebhookId",
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
const PROJECT_ID: ObjectID = new ObjectID(PROJECT_ID_STRING);
|
|
257
|
+
const currentUserId: ObjectID = new ObjectID(CURRENT_USER_ID_STRING);
|
|
258
|
+
const OTHER_USER_ID: ObjectID = new ObjectID(OTHER_USER_ID_STRING);
|
|
259
|
+
|
|
260
|
+
type SeverityModelType = { new (): IncidentSeverity | AlertSeverity };
|
|
261
|
+
|
|
262
|
+
type BuildSeverities = (
|
|
263
|
+
modelType: SeverityModelType,
|
|
264
|
+
) => Array<IncidentSeverity | AlertSeverity>;
|
|
265
|
+
|
|
266
|
+
const buildSeverities: BuildSeverities = (
|
|
267
|
+
modelType: SeverityModelType,
|
|
268
|
+
): Array<IncidentSeverity | AlertSeverity> => {
|
|
269
|
+
return [
|
|
270
|
+
{ id: SEVERITY_ONE_ID, name: "Sev 1" },
|
|
271
|
+
{ id: SEVERITY_TWO_ID, name: "Sev 2" },
|
|
272
|
+
].map((spec: { id: string; name: string }) => {
|
|
273
|
+
const severity: IncidentSeverity | AlertSeverity = new modelType();
|
|
274
|
+
severity._id = spec.id;
|
|
275
|
+
severity.name = spec.name;
|
|
276
|
+
return severity;
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const pageProps: PageComponentProps = {
|
|
281
|
+
pageRoute: new Route("/user-settings/on-call-rules"),
|
|
282
|
+
currentProject: null,
|
|
283
|
+
hasPaymentMethod: false,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/*
|
|
287
|
+
* Which model each getList call asked for. The severity fetch is the only one
|
|
288
|
+
* that varies between the four pages, so this is how "the right severity model
|
|
289
|
+
* is used" gets asserted without reaching inside the component.
|
|
290
|
+
*/
|
|
291
|
+
type GetRequestedModelTypes = () => Array<unknown>;
|
|
292
|
+
|
|
293
|
+
const getRequestedModelTypes: GetRequestedModelTypes = (): Array<unknown> => {
|
|
294
|
+
return getListMock.mock.calls.map((call: Array<any>) => {
|
|
295
|
+
return call[0].modelType;
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
type GetCapturedTables = () => Array<CapturedTableProps>;
|
|
300
|
+
|
|
301
|
+
/*
|
|
302
|
+
* The captured list is REPLACED between tests rather than emptied in place, so a
|
|
303
|
+
* closure built inside the `for` loop below would otherwise read whichever array
|
|
304
|
+
* happened to be bound when the loop ran rather than the one the test is
|
|
305
|
+
* filling. Going through a stable function is also what stops eslint's
|
|
306
|
+
* no-loop-func from being right about it.
|
|
307
|
+
*/
|
|
308
|
+
const getCapturedTables: GetCapturedTables = (): Array<CapturedTableProps> => {
|
|
309
|
+
return capturedTables;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
type GetQueriedUserIds = () => Array<string | undefined>;
|
|
313
|
+
|
|
314
|
+
const getQueriedUserIds: GetQueriedUserIds = (): Array<string | undefined> => {
|
|
315
|
+
return getListMock.mock.calls.map((call: Array<any>) => {
|
|
316
|
+
return call[0].query.userId?.toString();
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
* The options the create form's "Notification Method" dropdown is offering. It
|
|
322
|
+
* is an override field - its value travels in `miscDataProps` rather than as a
|
|
323
|
+
* column - so it is found by its override key rather than by a field name.
|
|
324
|
+
*/
|
|
325
|
+
type GetMethodOptions = (
|
|
326
|
+
table: CapturedTableProps,
|
|
327
|
+
) => Array<{ label: string; value: string }>;
|
|
328
|
+
|
|
329
|
+
const getMethodOptions: GetMethodOptions = (
|
|
330
|
+
table: CapturedTableProps,
|
|
331
|
+
): Array<{ label: string; value: string }> => {
|
|
332
|
+
const field: CapturedFormField | undefined = table.formFields.find(
|
|
333
|
+
(candidate: CapturedFormField): boolean => {
|
|
334
|
+
return candidate.overrideFieldKey === "notificationMethod";
|
|
335
|
+
},
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
if (!field) {
|
|
339
|
+
throw new Error("no notification method form field");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return field.dropdownOptions || [];
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
* The seven RELATION spellings. Selecting any of these on a rule pulls columns
|
|
347
|
+
* out of the method models themselves - the raw address, the raw number, the
|
|
348
|
+
* telegram handle - through a table an administrator is allowed to read.
|
|
349
|
+
*
|
|
350
|
+
* That route is not refused, and it is worth being precise about why, because
|
|
351
|
+
* "the model is owner-scoped" is the reason everybody assumes covers it and it
|
|
352
|
+
* does not. SelectPermission checks the nested key against UserNotificationRule's
|
|
353
|
+
* own `userEmail` column, whose read list is [Permission.CurrentUser] - and
|
|
354
|
+
* CurrentUser is auto-granted to every authenticated caller. Then
|
|
355
|
+
* QueryPermission.checkRelationQueryPermission sees `canReadOnRelationQuery:
|
|
356
|
+
* true` on UserEmail.email and returns early without consulting UserEmail's own
|
|
357
|
+
* read list at all. And a read of UserNotificationRule is deliberately NOT
|
|
358
|
+
* row-scoped for an administrator, since that is what the admin surface is for.
|
|
359
|
+
* Three permissive steps compose into a leak that no single one of them looks
|
|
360
|
+
* like.
|
|
361
|
+
*/
|
|
362
|
+
const METHOD_RELATIONS: Array<string> = [
|
|
363
|
+
"userEmail",
|
|
364
|
+
"userSms",
|
|
365
|
+
"userCall",
|
|
366
|
+
"userPush",
|
|
367
|
+
"userWhatsApp",
|
|
368
|
+
"userTelegram",
|
|
369
|
+
"userWebhook",
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
/*
|
|
373
|
+
* Everything the table asks the server for about a method, from BOTH of the
|
|
374
|
+
* places ModelTable builds its projection out of. Asserting on the union is the
|
|
375
|
+
* point: the two are unioned on the wire, so a guard applied to one of them is
|
|
376
|
+
* no guard at all.
|
|
377
|
+
*/
|
|
378
|
+
type GetMethodProjectionKeys = (table: CapturedTableProps) => Array<string>;
|
|
379
|
+
|
|
380
|
+
const getMethodProjectionKeys: GetMethodProjectionKeys = (
|
|
381
|
+
table: CapturedTableProps,
|
|
382
|
+
): Array<string> => {
|
|
383
|
+
const keys: Array<string> = Object.keys(table.selectMoreFields || {});
|
|
384
|
+
|
|
385
|
+
for (const column of table.columns || []) {
|
|
386
|
+
keys.push(...Object.keys(column.field || {}));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return keys;
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
/* The rendered "Notification Method" cell for one rule, as plain text. */
|
|
393
|
+
type RenderMethodCell = (
|
|
394
|
+
table: CapturedTableProps,
|
|
395
|
+
rule: UserNotificationRule,
|
|
396
|
+
) => string;
|
|
397
|
+
|
|
398
|
+
const renderMethodCell: RenderMethodCell = (
|
|
399
|
+
table: CapturedTableProps,
|
|
400
|
+
rule: UserNotificationRule,
|
|
401
|
+
): string => {
|
|
402
|
+
const column: CapturedColumn | undefined = (table.columns || []).find(
|
|
403
|
+
(candidate: CapturedColumn): boolean => {
|
|
404
|
+
return candidate.title === "Notification Method";
|
|
405
|
+
},
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
if (!column || !column.getElement) {
|
|
409
|
+
throw new Error("no notification method column");
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const { container }: { container: HTMLElement } = render(
|
|
413
|
+
column.getElement(rule),
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
return container.textContent || "";
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
type GetMethodColumnsSet = (
|
|
420
|
+
rule: UserNotificationRule,
|
|
421
|
+
) => Array<{ column: string; value: string }>;
|
|
422
|
+
|
|
423
|
+
/*
|
|
424
|
+
* Which of the seven method foreign keys a created rule ended up carrying. The
|
|
425
|
+
* assertion is always about the whole set, never about one column: a rule that
|
|
426
|
+
* sets the wrong one pages the wrong device, and a rule that sets two is a shape
|
|
427
|
+
* the create path has no meaning for.
|
|
428
|
+
*/
|
|
429
|
+
const getMethodColumnsSet: GetMethodColumnsSet = (
|
|
430
|
+
rule: UserNotificationRule,
|
|
431
|
+
): Array<{ column: string; value: string }> => {
|
|
432
|
+
const columns: Array<{ column: string; value: string }> = [];
|
|
433
|
+
|
|
434
|
+
for (const foreignKey of METHOD_FOREIGN_KEYS) {
|
|
435
|
+
const value: unknown = rule[foreignKey];
|
|
436
|
+
|
|
437
|
+
if (value) {
|
|
438
|
+
columns.push({
|
|
439
|
+
column: foreignKey as string,
|
|
440
|
+
value: (value as ObjectID).toString(),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return columns;
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
type RenderAndSettle = (element: ReactElement) => Promise<void>;
|
|
449
|
+
|
|
450
|
+
const renderAndSettle: RenderAndSettle = async (
|
|
451
|
+
element: ReactElement,
|
|
452
|
+
): Promise<void> => {
|
|
453
|
+
render(element);
|
|
454
|
+
|
|
455
|
+
await waitFor(() => {
|
|
456
|
+
expect(capturedTables.length).toBeGreaterThan(0);
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
interface RuleTypeCase {
|
|
461
|
+
label: string;
|
|
462
|
+
Page: FunctionComponent<PageComponentProps>;
|
|
463
|
+
severityModelType: SeverityModelType;
|
|
464
|
+
wrongSeverityModelType: SeverityModelType;
|
|
465
|
+
foreignKeyColumn: "incidentSeverityId" | "alertSeverityId";
|
|
466
|
+
wrongForeignKeyColumn: "incidentSeverityId" | "alertSeverityId";
|
|
467
|
+
ruleType: NotificationRuleType;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/*
|
|
471
|
+
* Note the crossed pair in the middle two rows: the ALERT episode page reads
|
|
472
|
+
* AlertSeverity while the INCIDENT episode page reads IncidentSeverity. That
|
|
473
|
+
* crossing is the whole reason the severity model and the foreign key column
|
|
474
|
+
* are two separate props, and a case table is the only shape that makes an
|
|
475
|
+
* accidental re-derivation fail on exactly one row instead of passing on all
|
|
476
|
+
* four.
|
|
477
|
+
*/
|
|
478
|
+
const RULE_TYPE_CASES: Array<RuleTypeCase> = [
|
|
479
|
+
{
|
|
480
|
+
label: "incident",
|
|
481
|
+
Page: IncidentOnCallRules,
|
|
482
|
+
severityModelType: IncidentSeverity,
|
|
483
|
+
wrongSeverityModelType: AlertSeverity,
|
|
484
|
+
foreignKeyColumn: "incidentSeverityId",
|
|
485
|
+
wrongForeignKeyColumn: "alertSeverityId",
|
|
486
|
+
ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
label: "alert",
|
|
490
|
+
Page: AlertOnCallRules,
|
|
491
|
+
severityModelType: AlertSeverity,
|
|
492
|
+
wrongSeverityModelType: IncidentSeverity,
|
|
493
|
+
foreignKeyColumn: "alertSeverityId",
|
|
494
|
+
wrongForeignKeyColumn: "incidentSeverityId",
|
|
495
|
+
ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
label: "alert episode",
|
|
499
|
+
Page: EpisodeOnCallRules,
|
|
500
|
+
severityModelType: AlertSeverity,
|
|
501
|
+
wrongSeverityModelType: IncidentSeverity,
|
|
502
|
+
foreignKeyColumn: "alertSeverityId",
|
|
503
|
+
wrongForeignKeyColumn: "incidentSeverityId",
|
|
504
|
+
ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT_EPISODE,
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
label: "incident episode",
|
|
508
|
+
Page: IncidentEpisodeOnCallRules,
|
|
509
|
+
severityModelType: IncidentSeverity,
|
|
510
|
+
wrongSeverityModelType: AlertSeverity,
|
|
511
|
+
foreignKeyColumn: "incidentSeverityId",
|
|
512
|
+
wrongForeignKeyColumn: "alertSeverityId",
|
|
513
|
+
ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT_EPISODE,
|
|
514
|
+
},
|
|
515
|
+
];
|
|
516
|
+
|
|
517
|
+
describe("OnCallRulesTable", () => {
|
|
518
|
+
beforeEach(() => {
|
|
519
|
+
capturedTables = [];
|
|
520
|
+
getListMock.mockReset();
|
|
521
|
+
|
|
522
|
+
/*
|
|
523
|
+
* Whichever severity model the page asks for is the one it gets back. A
|
|
524
|
+
* page that asked for the wrong one would still render two tables, so the
|
|
525
|
+
* assertions below check the REQUEST rather than inferring it from what
|
|
526
|
+
* came back.
|
|
527
|
+
*/
|
|
528
|
+
getListMock.mockImplementation((data: any) => {
|
|
529
|
+
if (
|
|
530
|
+
data.modelType === IncidentSeverity ||
|
|
531
|
+
data.modelType === AlertSeverity
|
|
532
|
+
) {
|
|
533
|
+
return Promise.resolve({
|
|
534
|
+
data: buildSeverities(data.modelType),
|
|
535
|
+
count: 2,
|
|
536
|
+
skip: 0,
|
|
537
|
+
limit: 2,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/*
|
|
542
|
+
* One real row on the self-serve path, so "the dropdown is built from the
|
|
543
|
+
* models" is asserted against a label rather than against an empty list -
|
|
544
|
+
* which is what every method model returned before, and which a component
|
|
545
|
+
* that had stopped reading them entirely would also produce.
|
|
546
|
+
*/
|
|
547
|
+
if (data.modelType === UserEmail) {
|
|
548
|
+
const userEmail: UserEmail = new UserEmail();
|
|
549
|
+
userEmail._id = OWN_EMAIL_ID;
|
|
550
|
+
userEmail.email = new Email(OWN_EMAIL_ADDRESS);
|
|
551
|
+
|
|
552
|
+
return Promise.resolve({
|
|
553
|
+
data: [userEmail],
|
|
554
|
+
count: 1,
|
|
555
|
+
skip: 0,
|
|
556
|
+
limit: 1,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return Promise.resolve({ data: [], count: 0, skip: 0, limit: 0 });
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
afterEach(() => {
|
|
567
|
+
cleanup();
|
|
568
|
+
jest.restoreAllMocks();
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
for (const testCase of RULE_TYPE_CASES) {
|
|
572
|
+
const Page: FunctionComponent<PageComponentProps> = testCase.Page;
|
|
573
|
+
|
|
574
|
+
describe(`${testCase.label} rules`, () => {
|
|
575
|
+
test("enumerates its own severity model and never the other one", async () => {
|
|
576
|
+
await renderAndSettle(<Page {...pageProps} />);
|
|
577
|
+
|
|
578
|
+
const requested: Array<unknown> = getRequestedModelTypes();
|
|
579
|
+
|
|
580
|
+
expect(requested).toContain(testCase.severityModelType);
|
|
581
|
+
expect(requested).not.toContain(testCase.wrongSeverityModelType);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
test("filters each table on its own severity foreign key column", async () => {
|
|
585
|
+
await renderAndSettle(<Page {...pageProps} />);
|
|
586
|
+
|
|
587
|
+
expect(getCapturedTables()).toHaveLength(2);
|
|
588
|
+
|
|
589
|
+
const severityIds: Array<string> = [SEVERITY_ONE_ID, SEVERITY_TWO_ID];
|
|
590
|
+
|
|
591
|
+
getCapturedTables().forEach((table: CapturedTableProps, i: number) => {
|
|
592
|
+
expect(table.query["ruleType"]).toBe(testCase.ruleType);
|
|
593
|
+
expect(table.query["projectId"]?.toString()).toBe(PROJECT_ID_STRING);
|
|
594
|
+
expect(table.query["userId"]?.toString()).toBe(
|
|
595
|
+
CURRENT_USER_ID_STRING,
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
expect(table.query[testCase.foreignKeyColumn]?.toString()).toBe(
|
|
599
|
+
severityIds[i],
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
/*
|
|
603
|
+
* Absent, not merely undefined. A query that carries the other column
|
|
604
|
+
* at all is the shape that reads as "no severity filter" once it is
|
|
605
|
+
* serialised onto the wire.
|
|
606
|
+
*/
|
|
607
|
+
expect(Object.keys(table.query)).not.toContain(
|
|
608
|
+
testCase.wrongForeignKeyColumn,
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("stamps the same severity foreign key column on a newly created rule", async () => {
|
|
614
|
+
await renderAndSettle(<Page {...pageProps} />);
|
|
615
|
+
|
|
616
|
+
const created: UserNotificationRule =
|
|
617
|
+
await getCapturedTables()[0]!.onBeforeCreate(
|
|
618
|
+
new UserNotificationRule(),
|
|
619
|
+
{
|
|
620
|
+
notificationMethod: undefined,
|
|
621
|
+
},
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
expect(created.ruleType).toBe(testCase.ruleType);
|
|
625
|
+
expect(created.projectId?.toString()).toBe(PROJECT_ID_STRING);
|
|
626
|
+
expect(created.userId?.toString()).toBe(CURRENT_USER_ID_STRING);
|
|
627
|
+
|
|
628
|
+
expect(created[testCase.foreignKeyColumn]?.toString()).toBe(
|
|
629
|
+
SEVERITY_ONE_ID,
|
|
630
|
+
);
|
|
631
|
+
expect(created[testCase.wrongForeignKeyColumn]).toBeUndefined();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test("keeps the severity id in each table's user preferences key", async () => {
|
|
635
|
+
await renderAndSettle(<Page {...pageProps} />);
|
|
636
|
+
|
|
637
|
+
const keys: Array<string> = getCapturedTables().map(
|
|
638
|
+
(table: CapturedTableProps) => {
|
|
639
|
+
return table.userPreferencesKey;
|
|
640
|
+
},
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
expect(keys[0]).toContain(SEVERITY_ONE_ID);
|
|
644
|
+
expect(keys[1]).toContain(SEVERITY_TWO_ID);
|
|
645
|
+
expect(keys[0]).not.toBe(keys[1]);
|
|
646
|
+
|
|
647
|
+
keys.forEach((key: string) => {
|
|
648
|
+
expect(key).toContain(testCase.ruleType);
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
test("gives every table across all four rule types a distinct preferences key", async () => {
|
|
655
|
+
const keys: Array<string> = [];
|
|
656
|
+
|
|
657
|
+
for (const testCase of RULE_TYPE_CASES) {
|
|
658
|
+
const Page: FunctionComponent<PageComponentProps> = testCase.Page;
|
|
659
|
+
|
|
660
|
+
capturedTables = [];
|
|
661
|
+
await renderAndSettle(<Page {...pageProps} />);
|
|
662
|
+
|
|
663
|
+
capturedTables.forEach((table: CapturedTableProps) => {
|
|
664
|
+
keys.push(table.userPreferencesKey);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
cleanup();
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/*
|
|
671
|
+
* Four pages, two severities each. If any two keys collided the distinct
|
|
672
|
+
* count would drop, and the two tables sharing a key would share a stored
|
|
673
|
+
* page size and a slice of URL state.
|
|
674
|
+
*/
|
|
675
|
+
expect(keys).toHaveLength(8);
|
|
676
|
+
expect(new Set(keys).size).toBe(8);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
/*
|
|
680
|
+
* Pointed at somebody else - the admin surface.
|
|
681
|
+
*
|
|
682
|
+
* The rules still move with the user, but the METHODS no longer do: a read of
|
|
683
|
+
* UserSMS for a colleague is refused, because the row behind the label holds
|
|
684
|
+
* their raw number. Widening that scope so this dropdown could be filled was
|
|
685
|
+
* tried and could not be contained, so the list is handed in already masked
|
|
686
|
+
* and the table reads nothing.
|
|
687
|
+
*/
|
|
688
|
+
type RenderForOtherUser = (
|
|
689
|
+
notificationMethods?: Array<NotificationMethodChoice> | undefined,
|
|
690
|
+
) => Promise<void>;
|
|
691
|
+
|
|
692
|
+
const renderForOtherUser: RenderForOtherUser = async (
|
|
693
|
+
notificationMethods?: Array<NotificationMethodChoice> | undefined,
|
|
694
|
+
): Promise<void> => {
|
|
695
|
+
await renderAndSettle(
|
|
696
|
+
<OnCallRulesTable
|
|
697
|
+
severityModelType={IncidentSeverity}
|
|
698
|
+
severityForeignKeyColumn="incidentSeverityId"
|
|
699
|
+
ruleType={NotificationRuleType.ON_CALL_EXECUTED_INCIDENT}
|
|
700
|
+
userPreferencesKeyPrefix="admin-notification-rules-table"
|
|
701
|
+
userId={OTHER_USER_ID}
|
|
702
|
+
notificationMethods={notificationMethods}
|
|
703
|
+
getTitle={(severityName: string): string => {
|
|
704
|
+
return severityName;
|
|
705
|
+
}}
|
|
706
|
+
getDescription={(severityName: string): string => {
|
|
707
|
+
return severityName;
|
|
708
|
+
}}
|
|
709
|
+
/>,
|
|
710
|
+
);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
test("scopes rules to the user it is given, and reads no method model for them", async () => {
|
|
714
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
715
|
+
|
|
716
|
+
expect(capturedTables[0]!.query["userId"]?.toString()).toBe(
|
|
717
|
+
OTHER_USER_ID_STRING,
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
/*
|
|
721
|
+
* The whole point of the redesign, in one assertion. Not "reads them and
|
|
722
|
+
* shows fewer columns" - does not read them. Each of these seven models is
|
|
723
|
+
* scoped to the person who owns the device, and the columns behind them are
|
|
724
|
+
* the raw phone number, the webhook bearer url, the push device token, the
|
|
725
|
+
* telegram chat id and the verification code. A request here is a request
|
|
726
|
+
* the server refuses, and a component that issues it renders an error page
|
|
727
|
+
* where the rules should be.
|
|
728
|
+
*/
|
|
729
|
+
const requested: Array<unknown> = getRequestedModelTypes();
|
|
730
|
+
|
|
731
|
+
for (const methodModel of NOTIFICATION_METHOD_MODELS) {
|
|
732
|
+
expect(requested).not.toContain(methodModel);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
expect(requested).toEqual([IncidentSeverity]);
|
|
736
|
+
|
|
737
|
+
// Nor may it quietly fall back to reading the ADMIN's own methods instead.
|
|
738
|
+
expect(getQueriedUserIds()).not.toContain(CURRENT_USER_ID_STRING);
|
|
739
|
+
|
|
740
|
+
const created: UserNotificationRule =
|
|
741
|
+
await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {});
|
|
742
|
+
|
|
743
|
+
expect(created.userId?.toString()).toBe(OTHER_USER_ID_STRING);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
test("reads no method model even when it is handed no methods at all", async () => {
|
|
747
|
+
/*
|
|
748
|
+
* The readiness payload the admin page sources these from is fetched
|
|
749
|
+
* asynchronously and can fail outright, so an absent list is a real state
|
|
750
|
+
* rather than a caller mistake. Treating it as "then fetch them yourself"
|
|
751
|
+
* would fire seven refused requests and turn this component's error state on
|
|
752
|
+
* - taking the rule repair away because a DIFFERENT read failed.
|
|
753
|
+
*/
|
|
754
|
+
await renderForOtherUser(undefined);
|
|
755
|
+
|
|
756
|
+
const requested: Array<unknown> = getRequestedModelTypes();
|
|
757
|
+
|
|
758
|
+
for (const methodModel of NOTIFICATION_METHOD_MODELS) {
|
|
759
|
+
expect(requested).not.toContain(methodModel);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
expect(capturedTables).toHaveLength(2);
|
|
763
|
+
expect(getMethodOptions(capturedTables[0]!)).toEqual([]);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
test("builds the dropdown from the masked methods it is handed", async () => {
|
|
767
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
768
|
+
|
|
769
|
+
const options: Array<{ label: string; value: string }> = getMethodOptions(
|
|
770
|
+
capturedTables[0]!,
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
/*
|
|
774
|
+
* Label from the mask, value from the method id. That pairing is the entire
|
|
775
|
+
* trick: the viewer picks "SMS: +1 ••• ••• 4821" and the form submits a
|
|
776
|
+
* foreign key, so the number itself is never in the page to begin with.
|
|
777
|
+
*/
|
|
778
|
+
expect(options).toEqual([
|
|
779
|
+
{ label: `SMS: ${MASKED_SMS}`, value: SUPPLIED_SMS_ID },
|
|
780
|
+
{ label: `Email: ${MASKED_EMAIL}`, value: SUPPLIED_EMAIL_ID },
|
|
781
|
+
]);
|
|
782
|
+
|
|
783
|
+
for (const option of options) {
|
|
784
|
+
expect(option.label).not.toContain(RAW_PHONE);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/*
|
|
788
|
+
* The unverified Telegram method is absent, which mirrors the
|
|
789
|
+
* `isVerified: true` filter on six of the seven self-serve queries. A rule
|
|
790
|
+
* pointed at an unverified device is a rule that pages nothing.
|
|
791
|
+
*/
|
|
792
|
+
expect(
|
|
793
|
+
options.map((option: { label: string; value: string }) => {
|
|
794
|
+
return option.value;
|
|
795
|
+
}),
|
|
796
|
+
).not.toContain(SUPPLIED_TELEGRAM_ID);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test("points the created rule at the column the chosen method belongs to", async () => {
|
|
800
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
801
|
+
|
|
802
|
+
const created: UserNotificationRule =
|
|
803
|
+
await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
|
|
804
|
+
notificationMethod: SUPPLIED_SMS_ID,
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
/*
|
|
808
|
+
* The seven foreign keys are mutually exclusive and nothing validates that
|
|
809
|
+
* client-side, so this asserts the whole set: an SMS id written to
|
|
810
|
+
* `userEmailId` would page an address rather than a phone, and would not
|
|
811
|
+
* error anywhere on the way.
|
|
812
|
+
*/
|
|
813
|
+
expect(getMethodColumnsSet(created)).toEqual([
|
|
814
|
+
{ column: "userSmsId", value: SUPPLIED_SMS_ID },
|
|
815
|
+
]);
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
test("sets no method at all for an id it was never handed", async () => {
|
|
819
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
820
|
+
|
|
821
|
+
const created: UserNotificationRule =
|
|
822
|
+
await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
|
|
823
|
+
notificationMethod: UNKNOWN_METHOD_ID,
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
/*
|
|
827
|
+
* A rule with no method is refused by the server, which is the right
|
|
828
|
+
* failure. The wrong one would be guessing a column for an unrecognised id -
|
|
829
|
+
* that produces a rule which pages some other device and never errors.
|
|
830
|
+
*/
|
|
831
|
+
expect(getMethodColumnsSet(created)).toEqual([]);
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
test("sets no method for an id the dropdown deliberately withheld", async () => {
|
|
835
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
836
|
+
|
|
837
|
+
const created: UserNotificationRule =
|
|
838
|
+
await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
|
|
839
|
+
notificationMethod: SUPPLIED_TELEGRAM_ID,
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
/*
|
|
843
|
+
* The unverified method IS in the supplied list - it is shown on the methods
|
|
844
|
+
* card - and is only kept out of the dropdown. Matching on the list alone
|
|
845
|
+
* would let a value that was never offered become a rule anyway, so the
|
|
846
|
+
* offered set and the accepted set are one predicate rather than two.
|
|
847
|
+
*/
|
|
848
|
+
expect(getMethodColumnsSet(created)).toEqual([]);
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
test("asks for no method column of any method model when the table is about somebody else", async () => {
|
|
852
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
853
|
+
|
|
854
|
+
/*
|
|
855
|
+
* The SECOND door, and the one that was open.
|
|
856
|
+
*
|
|
857
|
+
* "Reads no method model" is usually taken to mean "issues no getList over
|
|
858
|
+
* UserEmail", which the test above already pins. But the rules table also
|
|
859
|
+
* asked for `{ userEmail: { email: true }, userSms: { phone: true }, ... }`
|
|
860
|
+
* as a nested select ON THE RULE, to render its "Notification Method" cell.
|
|
861
|
+
* That reaches the same columns through a table an administrator may read,
|
|
862
|
+
* it is not row-scoped for them, and the permission machinery lets it
|
|
863
|
+
* through - see METHOD_RELATIONS above for the three steps. The admin page
|
|
864
|
+
* masked every identifier on its readiness card and then printed the raw
|
|
865
|
+
* ones in a table one card below.
|
|
866
|
+
*/
|
|
867
|
+
const projection: Array<string> = getMethodProjectionKeys(
|
|
868
|
+
capturedTables[0]!,
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
for (const relation of METHOD_RELATIONS) {
|
|
872
|
+
expect(projection).not.toContain(relation);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/*
|
|
876
|
+
* The ids are asked for instead, and are the mechanism rather than a
|
|
877
|
+
* consolation prize: an id carries nothing, it is admin-readable by design,
|
|
878
|
+
* and matching it against the masked payload is what lets the cell name a
|
|
879
|
+
* method whose row was never read.
|
|
880
|
+
*/
|
|
881
|
+
for (const foreignKey of METHOD_FOREIGN_KEYS) {
|
|
882
|
+
expect(projection).toContain(foreignKey as string);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Not vacuous - it still asks for the things the other cells need.
|
|
886
|
+
expect(projection).toContain("isOptOut");
|
|
887
|
+
expect(projection).toContain("notifyAfterMinutes");
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
test("labels somebody else's rule from the mask, never from the row", async () => {
|
|
891
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
892
|
+
|
|
893
|
+
const rule: UserNotificationRule = new UserNotificationRule();
|
|
894
|
+
rule.userSmsId = new ObjectID(SUPPLIED_SMS_ID);
|
|
895
|
+
|
|
896
|
+
const cell: string = renderMethodCell(capturedTables[0]!, rule);
|
|
897
|
+
|
|
898
|
+
expect(cell).toBe(`SMS: ${MASKED_SMS}`);
|
|
899
|
+
expect(cell).not.toContain(RAW_PHONE);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
test("names the method behind a rule even once that method stops being verified", async () => {
|
|
903
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
904
|
+
|
|
905
|
+
const rule: UserNotificationRule = new UserNotificationRule();
|
|
906
|
+
rule.userTelegramId = new ObjectID(SUPPLIED_TELEGRAM_ID);
|
|
907
|
+
|
|
908
|
+
/*
|
|
909
|
+
* The dropdown withholds this method, and the cell must NOT. A rule still
|
|
910
|
+
* pointing at a method that has since become unverified is precisely the
|
|
911
|
+
* misconfiguration an administrator opened this page to find, so hiding it
|
|
912
|
+
* behind the same filter that governs the dropdown would blank out the one
|
|
913
|
+
* row worth looking at.
|
|
914
|
+
*/
|
|
915
|
+
expect(renderMethodCell(capturedTables[0]!, rule)).toBe(
|
|
916
|
+
`Telegram: ${MASKED_TELEGRAM}`,
|
|
917
|
+
);
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
test("a rule whose method is not in the payload is labelled, not left blank", async () => {
|
|
921
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
922
|
+
|
|
923
|
+
const rule: UserNotificationRule = new UserNotificationRule();
|
|
924
|
+
rule.userCallId = new ObjectID(UNKNOWN_METHOD_ID);
|
|
925
|
+
|
|
926
|
+
const cell: string = renderMethodCell(capturedTables[0]!, rule);
|
|
927
|
+
|
|
928
|
+
/*
|
|
929
|
+
* Readiness can be down, or the method deleted, and then the cell cannot
|
|
930
|
+
* name it. Rendering nothing is the one genuinely wrong answer: a blank
|
|
931
|
+
* method cell is exactly what an OPT-OUT row looks like, and the two mean
|
|
932
|
+
* opposite things - "never page me here" against "paged on a device this
|
|
933
|
+
* screen cannot name". An admin who reads the second as the first deletes a
|
|
934
|
+
* working rule and silently stops somebody's pages.
|
|
935
|
+
*/
|
|
936
|
+
expect(cell.length).toBeGreaterThan(0);
|
|
937
|
+
expect(cell).not.toBe(OPT_OUT_LABEL);
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
test("an opt-out row is still labelled as muted for somebody else", async () => {
|
|
941
|
+
await renderForOtherUser(SUPPLIED_METHODS);
|
|
942
|
+
|
|
943
|
+
const rule: UserNotificationRule = new UserNotificationRule();
|
|
944
|
+
(rule as unknown as Record<string, unknown>)["isOptOut"] = true;
|
|
945
|
+
|
|
946
|
+
expect(renderMethodCell(capturedTables[0]!, rule)).toBe(OPT_OUT_LABEL);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
test("the signed-in user's own table still asks for the method relations", async () => {
|
|
950
|
+
/*
|
|
951
|
+
* The other half of the seam, and the regression that would actually be
|
|
952
|
+
* noticed: the four self-serve pages render this same cell from the nested
|
|
953
|
+
* relations, and reading your own address unmasked is both allowed and the
|
|
954
|
+
* more useful thing to show. Narrowing the projection for everybody would
|
|
955
|
+
* blank the method column on every settings page in the product.
|
|
956
|
+
*/
|
|
957
|
+
await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
|
|
958
|
+
|
|
959
|
+
const projection: Array<string> = getMethodProjectionKeys(
|
|
960
|
+
capturedTables[0]!,
|
|
961
|
+
);
|
|
962
|
+
|
|
963
|
+
for (const relation of METHOD_RELATIONS) {
|
|
964
|
+
expect(projection).toContain(relation);
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
test("the signed-in user's own table still reads the method models itself", async () => {
|
|
969
|
+
/*
|
|
970
|
+
* The regression surface. Four settings pages every existing user already
|
|
971
|
+
* relies on read their own seven models to fill this dropdown, and reading
|
|
972
|
+
* your own rows is exactly what those models allow. Nothing about the admin
|
|
973
|
+
* surface is a reason to change it - and an unmasked label is more use to
|
|
974
|
+
* somebody looking at their own phone number than a mask of it.
|
|
975
|
+
*/
|
|
976
|
+
await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
|
|
977
|
+
|
|
978
|
+
const requested: Array<unknown> = getRequestedModelTypes();
|
|
979
|
+
|
|
980
|
+
for (const methodModel of NOTIFICATION_METHOD_MODELS) {
|
|
981
|
+
expect(requested).toContain(methodModel);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
expect(
|
|
985
|
+
getQueriedUserIds().filter((userId: string | undefined) => {
|
|
986
|
+
return userId === CURRENT_USER_ID_STRING;
|
|
987
|
+
}),
|
|
988
|
+
).toHaveLength(7);
|
|
989
|
+
|
|
990
|
+
expect(getMethodOptions(capturedTables[0]!)).toEqual([
|
|
991
|
+
{ label: `Email: ${OWN_EMAIL_ADDRESS}`, value: OWN_EMAIL_ID },
|
|
992
|
+
]);
|
|
993
|
+
|
|
994
|
+
const created: UserNotificationRule =
|
|
995
|
+
await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
|
|
996
|
+
notificationMethod: OWN_EMAIL_ID,
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
expect(getMethodColumnsSet(created)).toEqual([
|
|
1000
|
+
{ column: "userEmailId", value: OWN_EMAIL_ID },
|
|
1001
|
+
]);
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test("a table handed methods for the signed-in user still reads their own rows", async () => {
|
|
1005
|
+
/*
|
|
1006
|
+
* The fourth corner of the seam, stated so it cannot be read the other way:
|
|
1007
|
+
* WHOSE rules these are decides where the methods come from, not whether a
|
|
1008
|
+
* list happened to be passed. The admin page renders this component for the
|
|
1009
|
+
* viewer's own id too, and there the supplied masks are the wrong thing to
|
|
1010
|
+
* show somebody about their own devices.
|
|
1011
|
+
*/
|
|
1012
|
+
await renderAndSettle(
|
|
1013
|
+
<OnCallRulesTable
|
|
1014
|
+
severityModelType={IncidentSeverity}
|
|
1015
|
+
severityForeignKeyColumn="incidentSeverityId"
|
|
1016
|
+
ruleType={NotificationRuleType.ON_CALL_EXECUTED_INCIDENT}
|
|
1017
|
+
userPreferencesKeyPrefix="admin-notification-rules-table"
|
|
1018
|
+
userId={currentUserId}
|
|
1019
|
+
notificationMethods={SUPPLIED_METHODS}
|
|
1020
|
+
getTitle={(severityName: string): string => {
|
|
1021
|
+
return severityName;
|
|
1022
|
+
}}
|
|
1023
|
+
getDescription={(severityName: string): string => {
|
|
1024
|
+
return severityName;
|
|
1025
|
+
}}
|
|
1026
|
+
/>,
|
|
1027
|
+
);
|
|
1028
|
+
|
|
1029
|
+
expect(getMethodOptions(capturedTables[0]!)).toEqual([
|
|
1030
|
+
{ label: `Email: ${OWN_EMAIL_ADDRESS}`, value: OWN_EMAIL_ID },
|
|
1031
|
+
]);
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
test("drops the create and delete affordances when it is not editable", async () => {
|
|
1035
|
+
await renderAndSettle(
|
|
1036
|
+
<OnCallRulesTable
|
|
1037
|
+
severityModelType={AlertSeverity}
|
|
1038
|
+
severityForeignKeyColumn="alertSeverityId"
|
|
1039
|
+
ruleType={NotificationRuleType.ON_CALL_EXECUTED_ALERT}
|
|
1040
|
+
userPreferencesKeyPrefix="admin-notification-rules-table"
|
|
1041
|
+
userId={OTHER_USER_ID}
|
|
1042
|
+
isEditable={false}
|
|
1043
|
+
getTitle={(severityName: string): string => {
|
|
1044
|
+
return severityName;
|
|
1045
|
+
}}
|
|
1046
|
+
getDescription={(severityName: string): string => {
|
|
1047
|
+
return severityName;
|
|
1048
|
+
}}
|
|
1049
|
+
/>,
|
|
1050
|
+
);
|
|
1051
|
+
|
|
1052
|
+
expect(capturedTables).toHaveLength(2);
|
|
1053
|
+
|
|
1054
|
+
capturedTables.forEach((table: CapturedTableProps) => {
|
|
1055
|
+
expect(table.isCreateable).toBe(false);
|
|
1056
|
+
expect(table.isDeleteable).toBe(false);
|
|
1057
|
+
|
|
1058
|
+
/*
|
|
1059
|
+
* Including the row editor. Leaving it on for a viewer who may not create
|
|
1060
|
+
* or delete would draw the one control that rewrites an existing rule for
|
|
1061
|
+
* exactly the person the server will refuse.
|
|
1062
|
+
*/
|
|
1063
|
+
expect(table.isEditable).toBe(false);
|
|
1064
|
+
});
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
test("turns the row editor on for a viewer who may edit, along with add and remove", async () => {
|
|
1068
|
+
/*
|
|
1069
|
+
* All three affordances answer ONE question - may this person rewrite how
|
|
1070
|
+
* somebody gets paged? - and the server answers it once, so they move
|
|
1071
|
+
* together.
|
|
1072
|
+
*
|
|
1073
|
+
* The editor used to be hardcoded off, on the assumption that a notification
|
|
1074
|
+
* rule is only ever added and removed. That made the whole edit capability
|
|
1075
|
+
* inert: no PATCH was ever issued from this table, so opening
|
|
1076
|
+
* `notifyAfterMinutes` to update bought nothing anybody could click. The
|
|
1077
|
+
* delay is the single most common repair on this screen, and doing it by
|
|
1078
|
+
* deleting the rule and adding it back is a window during which the person
|
|
1079
|
+
* is not paged at all.
|
|
1080
|
+
*/
|
|
1081
|
+
await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
|
|
1082
|
+
|
|
1083
|
+
capturedTables.forEach((table: CapturedTableProps) => {
|
|
1084
|
+
expect(table.isCreateable).toBe(true);
|
|
1085
|
+
/*
|
|
1086
|
+
* Off by design, and NOT the same thing as "cannot delete". Deleting a
|
|
1087
|
+
* rule goes through the impact confirmation instead - the guard supplies
|
|
1088
|
+
* its own action button, because ModelTable's built-in dialog cannot say
|
|
1089
|
+
* how many rules disappear or which severity loses its last delivering
|
|
1090
|
+
* rule. "Remove" in this test's name is that button, not this flag.
|
|
1091
|
+
*/
|
|
1092
|
+
expect(table.isDeleteable).toBe(false);
|
|
1093
|
+
expect(table.isEditable).toBe(true);
|
|
1094
|
+
});
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
test("renders the per-severity card copy the self-serve pages had before the extraction", async () => {
|
|
1098
|
+
await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
|
|
1099
|
+
|
|
1100
|
+
expect(capturedTables[0]!.cardProps.title).toBe(
|
|
1101
|
+
"Sev 1 Severity: When I am on call and Sev 1 is assigned to me...",
|
|
1102
|
+
);
|
|
1103
|
+
expect(capturedTables[0]!.cardProps.description).toBe(
|
|
1104
|
+
"Here are the rules when you are on call and Sev 1 is assigned to you.",
|
|
1105
|
+
);
|
|
1106
|
+
|
|
1107
|
+
cleanup();
|
|
1108
|
+
capturedTables = [];
|
|
1109
|
+
|
|
1110
|
+
await renderAndSettle(<EpisodeOnCallRules {...pageProps} />);
|
|
1111
|
+
|
|
1112
|
+
expect(capturedTables[0]!.cardProps.title).toBe(
|
|
1113
|
+
"Sev 1 Severity Episode: When I am on call and Sev 1 severity episode is assigned to me...",
|
|
1114
|
+
);
|
|
1115
|
+
expect(capturedTables[0]!.cardProps.description).toBe(
|
|
1116
|
+
"Here are the rules when you are on call and Sev 1 Severity episode is assigned to you.",
|
|
1117
|
+
);
|
|
1118
|
+
});
|
|
1119
|
+
});
|