@oneuptime/common 12.0.7 → 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} +184 -9
- package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
- package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +33 -7
- 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/WorkflowLog.ts +38 -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/API/UserAPI.ts +16 -1
- 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/1786551733814-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
- 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 +24 -0
- package/Server/Infrastructure/Queue.ts +78 -13
- package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
- 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} +7 -4
- package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +203 -21
- 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/API/Utils.ts +44 -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/Types/Workflow/TriggerCode.ts +12 -0
- package/Server/Types/Workflow/Workflow.ts +5 -0
- 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 +689 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
- package/Server/Utils/VM/VMAPI.ts +56 -8
- 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/MonitorCriteriaAttributeFilter.test.tsx +574 -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/API/UserProjectsAPI.test.ts +478 -4
- 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/InventoryItemManualCreate.test.ts +248 -0
- 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/ApiComponentHeaders.test.ts +192 -0
- 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/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -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 +322 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
- package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
- 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/SerializableObjectDictionaryImportCycle.test.ts +197 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
- 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/StepTrace.test.ts +235 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +491 -0
- package/Tests/Types/Workflow/Templates.test.ts +1218 -0
- package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
- package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
- package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
- package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
- package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
- package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
- package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
- package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -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/TelemetryTimeRangePicker.test.tsx +49 -7
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +893 -0
- package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +522 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +193 -0
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +388 -0
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +210 -0
- 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/Utils.test.ts +192 -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/Monitor/MonitorStepCriteriaView.test.tsx +432 -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/NetworkDeviceRoleUtil.test.ts +514 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +727 -0
- package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
- package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
- package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -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 +80 -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/SerializableObjectDictionary.ts +133 -39
- package/Types/Telemetry/EntityRelationshipType.ts +1 -1
- package/Types/Telemetry/EntitySource.ts +40 -0
- package/Types/Telemetry/EntityType.ts +28 -1
- package/Types/Telemetry/EntityTypeGroups.ts +65 -0
- package/Types/Workflow/Component.ts +29 -0
- package/Types/Workflow/Components/API.ts +35 -0
- package/Types/Workflow/Components/BaseModel.ts +77 -27
- 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/StepTrace.ts +181 -0
- package/Types/Workflow/TemplateSyntax.ts +543 -0
- package/Types/Workflow/Templates.ts +2368 -0
- 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/Date/CustomTimeRangeModal.tsx +278 -0
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +71 -15
- package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
- package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
- package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
- package/UI/Components/Forms/Types/Field.ts +7 -0
- package/UI/Components/Forms/Validation.ts +63 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
- package/UI/Components/Input/Input.tsx +32 -3
- 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/LogTimeRangePicker.tsx +11 -216
- 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/Modal/Modal.tsx +37 -4
- 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/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
- package/UI/Components/Workflow/ArgumentsForm.tsx +341 -8
- package/UI/Components/Workflow/Component.tsx +28 -26
- package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +79 -9
- 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 +628 -0
- package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +528 -0
- package/UI/Components/Workflow/ModelSchema.ts +278 -0
- package/UI/Components/Workflow/RunForm.tsx +41 -7
- package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
- package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
- package/UI/Components/Workflow/UseRunWatch.ts +212 -0
- package/UI/Components/Workflow/Utils.ts +93 -1
- package/UI/Components/Workflow/VariableModal.tsx +6 -2
- package/UI/Components/Workflow/Workflow.tsx +126 -7
- 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/Types/LayeredDismissal.ts +31 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +99 -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/NetworkDeviceRoleUtil.ts +533 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +917 -155
- package/Utils/Telemetry/CrossSignalScope.ts +502 -0
- package/Utils/Telemetry/EntityKey.ts +71 -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} +212 -29
- 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} +52 -25
- 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/WorkflowLog.js +39 -0
- package/build/dist/Models/DatabaseModels/WorkflowLog.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/API/UserAPI.js +16 -1
- package/build/dist/Server/API/UserAPI.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/1786551733814-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
- 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 +24 -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/MasterAdminAuthorization.js +11 -6
- package/build/dist/Server/Middleware/MasterAdminAuthorization.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} +9 -6
- package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +158 -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/API/Utils.js +28 -0
- package/build/dist/Server/Types/Workflow/Components/API/Utils.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/Types/Workflow/TriggerCode.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 +507 -0
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
- 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/VM/VMAPI.js +51 -4
- package/build/dist/Server/Utils/VM/VMAPI.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/SerializableObjectDictionary.js +133 -39
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +39 -0
- package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +28 -1
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
- package/build/dist/Types/Workflow/Component.js +21 -0
- package/build/dist/Types/Workflow/Component.js.map +1 -1
- package/build/dist/Types/Workflow/Components/API.js +35 -0
- package/build/dist/Types/Workflow/Components/API.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +68 -26
- 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/StepTrace.js +104 -0
- package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js +338 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +2111 -0
- package/build/dist/Types/Workflow/Templates.js.map +1 -0
- 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/Date/CustomTimeRangeModal.js +126 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +47 -17
- 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/Forms/Fields/ColorPicker.js +52 -13
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +44 -0
- package/build/dist/UI/Components/Forms/Validation.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 +13 -4
- 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/LogTimeRangePicker.js +5 -108
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.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/Modal/Modal.js +31 -5
- package/build/dist/UI/Components/Modal/Modal.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/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +261 -14
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Component.js +20 -19
- package/build/dist/UI/Components/Workflow/Component.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 +35 -7
- 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 +425 -0
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
- 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 +320 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js +156 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
- package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
- package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
- 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/Utils.js +73 -1
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- 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 +92 -7
- 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/Types/LayeredDismissal.js +21 -0
- package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
- 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/NetworkDeviceRoleUtil.js +386 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +661 -126
- 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 +57 -5
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- 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,1468 @@
|
|
|
1
|
+
import UserNotificationRuleService from "../../../Server/Services/UserNotificationRuleService";
|
|
2
|
+
import UserOnCallLogService from "../../../Server/Services/UserOnCallLogService";
|
|
3
|
+
import UserOnCallLogTimelineService from "../../../Server/Services/UserOnCallLogTimelineService";
|
|
4
|
+
import ProjectCallSMSConfigService from "../../../Server/Services/ProjectCallSMSConfigService";
|
|
5
|
+
import IncidentService from "../../../Server/Services/IncidentService";
|
|
6
|
+
import AlertService from "../../../Server/Services/AlertService";
|
|
7
|
+
import AlertEpisodeService from "../../../Server/Services/AlertEpisodeService";
|
|
8
|
+
import IncidentEpisodeService from "../../../Server/Services/IncidentEpisodeService";
|
|
9
|
+
import MailService from "../../../Server/Services/MailService";
|
|
10
|
+
import SmsService from "../../../Server/Services/SmsService";
|
|
11
|
+
import CallService from "../../../Server/Services/CallService";
|
|
12
|
+
import WhatsAppService from "../../../Server/Services/WhatsAppService";
|
|
13
|
+
import TelegramService from "../../../Server/Services/TelegramService";
|
|
14
|
+
import WebhookService from "../../../Server/Services/WebhookService";
|
|
15
|
+
import PushNotificationService from "../../../Server/Services/PushNotificationService";
|
|
16
|
+
import WorkspaceNotificationRuleService from "../../../Server/Services/WorkspaceNotificationRuleService";
|
|
17
|
+
import logger from "../../../Server/Utils/Logger";
|
|
18
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
19
|
+
import Alert from "../../../Models/DatabaseModels/Alert";
|
|
20
|
+
import UserOnCallLog from "../../../Models/DatabaseModels/UserOnCallLog";
|
|
21
|
+
import UserOnCallLogTimeline from "../../../Models/DatabaseModels/UserOnCallLogTimeline";
|
|
22
|
+
import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
|
|
23
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
24
|
+
import Email from "../../../Types/Email";
|
|
25
|
+
import Phone from "../../../Types/Phone";
|
|
26
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
27
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
28
|
+
import EmailMessage from "../../../Types/Email/EmailMessage";
|
|
29
|
+
import UserNotificationEventType from "../../../Types/UserNotification/UserNotificationEventType";
|
|
30
|
+
import UserNotificationExecutionStatus from "../../../Types/UserNotification/UserNotificationExecutionStatus";
|
|
31
|
+
import UserNotificationStatus from "../../../Types/UserNotification/UserNotificationStatus";
|
|
32
|
+
import NotificationRuleEventType from "../../../Types/Workspace/NotificationRules/EventType";
|
|
33
|
+
import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* `executeNotificationRuleItem` is the last mile of the on-call system: by the
|
|
37
|
+
* time control reaches it, an incident has fired, a policy has escalated, a
|
|
38
|
+
* responder has been chosen, and a delay has elapsed. Everything that happens
|
|
39
|
+
* inside decides whether a human's phone actually rings. Three things in it are
|
|
40
|
+
* load-bearing and easy to break by accident, so they are pinned here:
|
|
41
|
+
*
|
|
42
|
+
* 1. THE CLAIM GATE. The very first statement is an atomic claim
|
|
43
|
+
* (`UserOnCallLogService.claimNotificationRuleExecution`, audit F7). When
|
|
44
|
+
* the claim is lost, the function must return *before* it reads anything
|
|
45
|
+
* and before any send happens — that is what stops two overlapping cron
|
|
46
|
+
* ticks from double-paging one responder for one escalation. A refactor
|
|
47
|
+
* that moved the rule lookup above the gate, or that treated `false` as
|
|
48
|
+
* "keep going", would silently reintroduce duplicate pages, so we assert
|
|
49
|
+
* not just "nothing was sent" but "findOneById was never even called" and
|
|
50
|
+
* that the claim strictly precedes the read.
|
|
51
|
+
*
|
|
52
|
+
* 2. THE SELECT CONTRACT. Every channel is verification-gated at send time
|
|
53
|
+
* (`userEmail?.email && userEmail?.isVerified`). Those gates are only as
|
|
54
|
+
* good as the `select` that hydrates them: an unselected `isVerified`
|
|
55
|
+
* arrives as `undefined`, which is falsy, so dropping it would not page
|
|
56
|
+
* unverified methods — it would *silence every verified one* and write a
|
|
57
|
+
* "not verified" error row for a phone that is perfectly verified. The
|
|
58
|
+
* exact select shape is therefore asserted field by field, together with
|
|
59
|
+
* the verified/unverified behaviour it drives.
|
|
60
|
+
*
|
|
61
|
+
* 3. THE SCHEDULING ENTRYPOINT. `startUserNotificationRulesExecution` writes
|
|
62
|
+
* the `UserOnCallLog` row that the ExecutePendingExecutions worker later
|
|
63
|
+
* picks up. Every option it is handed has to land on that row (a dropped
|
|
64
|
+
* `onCallDutyPolicyExecutionLogTimelineId` means the timeline never
|
|
65
|
+
* reconciles), the row must be `Scheduled`/"Scheduled" so the worker picks
|
|
66
|
+
* it up at all, and its fire-and-forget workspace-invite call must never
|
|
67
|
+
* be able to take the scheduling down with it.
|
|
68
|
+
*
|
|
69
|
+
* Nothing here touches a database and no template rendering is exercised — the
|
|
70
|
+
* senders and generators are stubbed. This file pins CONTROL FLOW and CONTRACTS,
|
|
71
|
+
* which is precisely what a later refactor of this 1700-line method will break.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
75
|
+
"11111111-1111-4111-8111-111111111111",
|
|
76
|
+
);
|
|
77
|
+
const RULE_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
|
|
78
|
+
const LOG_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
|
|
79
|
+
const USER_ID: ObjectID = new ObjectID("44444444-4444-4444-8444-444444444444");
|
|
80
|
+
const INCIDENT_ID: ObjectID = new ObjectID(
|
|
81
|
+
"55555555-5555-4555-8555-555555555555",
|
|
82
|
+
);
|
|
83
|
+
const ALERT_ID: ObjectID = new ObjectID("66666666-6666-4666-8666-666666666666");
|
|
84
|
+
const TEAM_ID: ObjectID = new ObjectID("77777777-7777-4777-8777-777777777777");
|
|
85
|
+
const POLICY_ID: ObjectID = new ObjectID(
|
|
86
|
+
"88888888-8888-4888-8888-888888888888",
|
|
87
|
+
);
|
|
88
|
+
const ESCALATION_RULE_ID: ObjectID = new ObjectID(
|
|
89
|
+
"99999999-9999-4999-8999-999999999999",
|
|
90
|
+
);
|
|
91
|
+
const EXECUTION_LOG_ID: ObjectID = new ObjectID(
|
|
92
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
93
|
+
);
|
|
94
|
+
const EXECUTION_LOG_TIMELINE_ID: ObjectID = new ObjectID(
|
|
95
|
+
"bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
|
96
|
+
);
|
|
97
|
+
const SCHEDULE_ID: ObjectID = new ObjectID(
|
|
98
|
+
"cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
|
99
|
+
);
|
|
100
|
+
const TIMELINE_ID: ObjectID = new ObjectID(
|
|
101
|
+
"dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
102
|
+
);
|
|
103
|
+
const ALERT_EPISODE_ID: ObjectID = new ObjectID(
|
|
104
|
+
"eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
|
|
105
|
+
);
|
|
106
|
+
const INCIDENT_EPISODE_ID: ObjectID = new ObjectID(
|
|
107
|
+
"ffffffff-ffff-4fff-8fff-ffffffffffff",
|
|
108
|
+
);
|
|
109
|
+
const OVERRIDE_USER_ID: ObjectID = new ObjectID(
|
|
110
|
+
"12121212-1212-4212-8212-121212121212",
|
|
111
|
+
);
|
|
112
|
+
const METHOD_ID: ObjectID = new ObjectID(
|
|
113
|
+
"13131313-1313-4313-8313-131313131313",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
type ExecuteOptions = Parameters<
|
|
117
|
+
typeof UserNotificationRuleService.executeNotificationRuleItem
|
|
118
|
+
>[1];
|
|
119
|
+
|
|
120
|
+
type StartOptions = Parameters<
|
|
121
|
+
typeof UserNotificationRuleService.startUserNotificationRulesExecution
|
|
122
|
+
>[1];
|
|
123
|
+
|
|
124
|
+
/* The `select` / `props` bundle handed to findOneById. */
|
|
125
|
+
interface FindOneByIdArg {
|
|
126
|
+
id: ObjectID;
|
|
127
|
+
select: JSONObject;
|
|
128
|
+
props: { isRoot: boolean };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* The service mutates ONE UserOnCallLogTimeline instance and passes that same
|
|
133
|
+
* instance to create() over and over, so mock.calls all alias the final state.
|
|
134
|
+
* We snapshot the fields we care about at call time instead.
|
|
135
|
+
*/
|
|
136
|
+
interface TimelineRow {
|
|
137
|
+
status: UserNotificationStatus | undefined;
|
|
138
|
+
statusMessage: string | undefined;
|
|
139
|
+
userId: ObjectID | undefined;
|
|
140
|
+
userNotificationRuleId: ObjectID | undefined;
|
|
141
|
+
userNotificationLogId: ObjectID | undefined;
|
|
142
|
+
projectId: ObjectID | undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function flushMicrotasks(): Promise<void> {
|
|
146
|
+
return Promise.resolve()
|
|
147
|
+
.then((): Promise<void> => {
|
|
148
|
+
return Promise.resolve();
|
|
149
|
+
})
|
|
150
|
+
.then((): Promise<void> => {
|
|
151
|
+
return Promise.resolve();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function executeOptions(
|
|
156
|
+
overrides: Partial<ExecuteOptions> = {},
|
|
157
|
+
): ExecuteOptions {
|
|
158
|
+
return {
|
|
159
|
+
projectId: PROJECT_ID,
|
|
160
|
+
userNotificationEventType: UserNotificationEventType.IncidentCreated,
|
|
161
|
+
triggeredByIncidentId: INCIDENT_ID,
|
|
162
|
+
onCallPolicyId: POLICY_ID,
|
|
163
|
+
userNotificationLogId: LOG_ID,
|
|
164
|
+
...overrides,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function ruleItem(channels: JSONObject = {}): UserNotificationRule {
|
|
169
|
+
return {
|
|
170
|
+
id: RULE_ID,
|
|
171
|
+
_id: RULE_ID.toString(),
|
|
172
|
+
userId: USER_ID,
|
|
173
|
+
...channels,
|
|
174
|
+
} as unknown as UserNotificationRule;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function fakeIncident(): Incident {
|
|
178
|
+
return {
|
|
179
|
+
id: INCIDENT_ID,
|
|
180
|
+
projectId: PROJECT_ID,
|
|
181
|
+
title: "Checkout is down",
|
|
182
|
+
description: "Checkout returns 500 for every request.",
|
|
183
|
+
incidentNumber: 42,
|
|
184
|
+
incidentNumberWithPrefix: "INC-42",
|
|
185
|
+
} as unknown as Incident;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function fakeAlert(): Alert {
|
|
189
|
+
return {
|
|
190
|
+
id: ALERT_ID,
|
|
191
|
+
projectId: PROJECT_ID,
|
|
192
|
+
title: "Disk almost full",
|
|
193
|
+
description: "94% used on node-3.",
|
|
194
|
+
alertNumber: 7,
|
|
195
|
+
alertNumberWithPrefix: "ALR-7",
|
|
196
|
+
} as unknown as Alert;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Every stub the delivery path touches, so no test can reach a real service. */
|
|
200
|
+
interface DeliverySpies {
|
|
201
|
+
claim: jest.SpyInstance;
|
|
202
|
+
findRule: jest.SpyInstance;
|
|
203
|
+
twilio: jest.SpyInstance;
|
|
204
|
+
timelineCreate: jest.SpyInstance;
|
|
205
|
+
timelineUpdate: jest.SpyInstance;
|
|
206
|
+
incidentFind: jest.SpyInstance;
|
|
207
|
+
alertFind: jest.SpyInstance;
|
|
208
|
+
alertEpisodeFind: jest.SpyInstance;
|
|
209
|
+
incidentEpisodeFind: jest.SpyInstance;
|
|
210
|
+
mail: jest.SpyInstance;
|
|
211
|
+
sms: jest.SpyInstance;
|
|
212
|
+
call: jest.SpyInstance;
|
|
213
|
+
whatsApp: jest.SpyInstance;
|
|
214
|
+
telegram: jest.SpyInstance;
|
|
215
|
+
webhook: jest.SpyInstance;
|
|
216
|
+
push: jest.SpyInstance;
|
|
217
|
+
emailTemplateForIncident: jest.SpyInstance;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
describe("UserNotificationRuleService.executeNotificationRuleItem", () => {
|
|
221
|
+
let spies: DeliverySpies;
|
|
222
|
+
let timelineRows: Array<TimelineRow>;
|
|
223
|
+
|
|
224
|
+
beforeEach(() => {
|
|
225
|
+
timelineRows = [];
|
|
226
|
+
|
|
227
|
+
jest.spyOn(logger, "error").mockImplementation((): void => {
|
|
228
|
+
return undefined;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const timelineCreate: jest.SpyInstance = jest.spyOn(
|
|
232
|
+
UserOnCallLogTimelineService,
|
|
233
|
+
"create",
|
|
234
|
+
);
|
|
235
|
+
timelineCreate.mockImplementation(
|
|
236
|
+
(createBy: unknown): Promise<UserOnCallLogTimeline> => {
|
|
237
|
+
const data: UserOnCallLogTimeline = (
|
|
238
|
+
createBy as { data: UserOnCallLogTimeline }
|
|
239
|
+
).data;
|
|
240
|
+
|
|
241
|
+
timelineRows.push({
|
|
242
|
+
status: data.status,
|
|
243
|
+
statusMessage: data.statusMessage,
|
|
244
|
+
userId: data.userId,
|
|
245
|
+
userNotificationRuleId: data.userNotificationRuleId,
|
|
246
|
+
userNotificationLogId: data.userNotificationLogId,
|
|
247
|
+
projectId: data.projectId,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return Promise.resolve({
|
|
251
|
+
id: TIMELINE_ID,
|
|
252
|
+
} as unknown as UserOnCallLogTimeline);
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
spies = {
|
|
257
|
+
claim: jest
|
|
258
|
+
.spyOn(UserOnCallLogService, "claimNotificationRuleExecution")
|
|
259
|
+
.mockResolvedValue(true as never),
|
|
260
|
+
findRule: jest
|
|
261
|
+
.spyOn(UserNotificationRuleService, "findOneById")
|
|
262
|
+
.mockResolvedValue(ruleItem() as never),
|
|
263
|
+
twilio: jest
|
|
264
|
+
.spyOn(ProjectCallSMSConfigService, "getProjectDefaultTwilioConfig")
|
|
265
|
+
.mockResolvedValue(undefined as never),
|
|
266
|
+
timelineCreate: timelineCreate,
|
|
267
|
+
timelineUpdate: jest
|
|
268
|
+
.spyOn(UserOnCallLogTimelineService, "updateOneById")
|
|
269
|
+
.mockResolvedValue(undefined as never),
|
|
270
|
+
incidentFind: jest
|
|
271
|
+
.spyOn(IncidentService, "findOneById")
|
|
272
|
+
.mockResolvedValue(fakeIncident() as never),
|
|
273
|
+
alertFind: jest
|
|
274
|
+
.spyOn(AlertService, "findOneById")
|
|
275
|
+
.mockResolvedValue(fakeAlert() as never),
|
|
276
|
+
alertEpisodeFind: jest
|
|
277
|
+
.spyOn(AlertEpisodeService, "findOneById")
|
|
278
|
+
.mockResolvedValue(null as never),
|
|
279
|
+
incidentEpisodeFind: jest
|
|
280
|
+
.spyOn(IncidentEpisodeService, "findOneById")
|
|
281
|
+
.mockResolvedValue(null as never),
|
|
282
|
+
mail: jest
|
|
283
|
+
.spyOn(MailService, "sendMail")
|
|
284
|
+
.mockResolvedValue(undefined as never),
|
|
285
|
+
sms: jest
|
|
286
|
+
.spyOn(SmsService, "sendSms")
|
|
287
|
+
.mockResolvedValue(undefined as never),
|
|
288
|
+
call: jest
|
|
289
|
+
.spyOn(CallService, "makeCall")
|
|
290
|
+
.mockResolvedValue(undefined as never),
|
|
291
|
+
whatsApp: jest
|
|
292
|
+
.spyOn(WhatsAppService, "sendWhatsAppMessage")
|
|
293
|
+
.mockResolvedValue(undefined as never),
|
|
294
|
+
telegram: jest
|
|
295
|
+
.spyOn(TelegramService, "sendTelegramMessage")
|
|
296
|
+
.mockResolvedValue(undefined as never),
|
|
297
|
+
webhook: jest
|
|
298
|
+
.spyOn(WebhookService, "sendWebhook")
|
|
299
|
+
.mockResolvedValue(undefined as never),
|
|
300
|
+
push: jest
|
|
301
|
+
.spyOn(PushNotificationService, "sendPushNotification")
|
|
302
|
+
.mockResolvedValue(undefined as never),
|
|
303
|
+
emailTemplateForIncident: jest
|
|
304
|
+
.spyOn(
|
|
305
|
+
UserNotificationRuleService,
|
|
306
|
+
"generateEmailTemplateForIncidentCreated",
|
|
307
|
+
)
|
|
308
|
+
.mockResolvedValue({} as EmailMessage as never),
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
afterEach(() => {
|
|
313
|
+
jest.restoreAllMocks();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
function everySender(): Array<jest.SpyInstance> {
|
|
317
|
+
return [
|
|
318
|
+
spies.mail,
|
|
319
|
+
spies.sms,
|
|
320
|
+
spies.call,
|
|
321
|
+
spies.whatsApp,
|
|
322
|
+
spies.telegram,
|
|
323
|
+
spies.webhook,
|
|
324
|
+
spies.push,
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/*
|
|
329
|
+
* ----------------------------------------------------------------------- *
|
|
330
|
+
* (A) The atomic claim gate.
|
|
331
|
+
* -----------------------------------------------------------------------
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
describe("the atomic claim gate runs first and is decisive", () => {
|
|
335
|
+
test("claim resolving FALSE returns immediately - findOneById is never called", async () => {
|
|
336
|
+
spies.claim.mockResolvedValue(false as never);
|
|
337
|
+
|
|
338
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
339
|
+
RULE_ID,
|
|
340
|
+
executeOptions(),
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
expect(spies.claim).toHaveBeenCalledTimes(1);
|
|
344
|
+
/*
|
|
345
|
+
* The whole point of F7: a lost claim must short-circuit BEFORE the read,
|
|
346
|
+
* not merely before the send.
|
|
347
|
+
*/
|
|
348
|
+
expect(spies.findRule).not.toHaveBeenCalled();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("claim resolving FALSE sends nothing on any channel and writes no timeline row", async () => {
|
|
352
|
+
spies.claim.mockResolvedValue(false as never);
|
|
353
|
+
|
|
354
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
355
|
+
RULE_ID,
|
|
356
|
+
executeOptions(),
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
for (const sender of everySender()) {
|
|
360
|
+
expect(sender).not.toHaveBeenCalled();
|
|
361
|
+
}
|
|
362
|
+
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
363
|
+
expect(spies.timelineUpdate).not.toHaveBeenCalled();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test("claim resolving FALSE also skips the incident/alert lookups and the Twilio config read", async () => {
|
|
367
|
+
spies.claim.mockResolvedValue(false as never);
|
|
368
|
+
|
|
369
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
370
|
+
RULE_ID,
|
|
371
|
+
executeOptions(),
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(spies.twilio).not.toHaveBeenCalled();
|
|
375
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
376
|
+
expect(spies.alertFind).not.toHaveBeenCalled();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("a lost claim resolves (does NOT throw) so the worker does not mark the log Error", async () => {
|
|
380
|
+
spies.claim.mockResolvedValue(false as never);
|
|
381
|
+
|
|
382
|
+
/*
|
|
383
|
+
* ExecutePendingExecutions marks a log `Error` on BadDataException. A
|
|
384
|
+
* duplicate-suppressed rule is a normal outcome, not bad data, so this
|
|
385
|
+
* path must resolve quietly.
|
|
386
|
+
*/
|
|
387
|
+
await expect(
|
|
388
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
389
|
+
RULE_ID,
|
|
390
|
+
executeOptions(),
|
|
391
|
+
),
|
|
392
|
+
).resolves.toBeUndefined();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("the claim is made for exactly this on-call log and this rule", async () => {
|
|
396
|
+
spies.claim.mockResolvedValue(false as never);
|
|
397
|
+
|
|
398
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
399
|
+
RULE_ID,
|
|
400
|
+
executeOptions({ userNotificationLogId: LOG_ID }),
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
const claimArg: {
|
|
404
|
+
userOnCallLogId: ObjectID;
|
|
405
|
+
userNotificationRuleId: ObjectID;
|
|
406
|
+
} = spies.claim.mock.calls[0][0] as {
|
|
407
|
+
userOnCallLogId: ObjectID;
|
|
408
|
+
userNotificationRuleId: ObjectID;
|
|
409
|
+
};
|
|
410
|
+
expect(claimArg.userOnCallLogId.toString()).toBe(LOG_ID.toString());
|
|
411
|
+
expect(claimArg.userNotificationRuleId.toString()).toBe(
|
|
412
|
+
RULE_ID.toString(),
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test("claim resolving TRUE lets execution proceed to the rule lookup", async () => {
|
|
417
|
+
spies.claim.mockResolvedValue(true as never);
|
|
418
|
+
|
|
419
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
420
|
+
RULE_ID,
|
|
421
|
+
executeOptions(),
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
expect(spies.findRule).toHaveBeenCalledTimes(1);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("the claim strictly PRECEDES the rule lookup (ordering, not just presence)", async () => {
|
|
428
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
429
|
+
RULE_ID,
|
|
430
|
+
executeOptions(),
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(spies.claim.mock.invocationCallOrder[0]).toBeLessThan(
|
|
434
|
+
spies.findRule.mock.invocationCallOrder[0] as number,
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("a claim that rejects propagates - the page is NOT attempted on an unknown claim state", async () => {
|
|
439
|
+
spies.claim.mockRejectedValue(new Error("db connection reset") as never);
|
|
440
|
+
|
|
441
|
+
await expect(
|
|
442
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
443
|
+
RULE_ID,
|
|
444
|
+
executeOptions(),
|
|
445
|
+
),
|
|
446
|
+
).rejects.toThrow("db connection reset");
|
|
447
|
+
|
|
448
|
+
expect(spies.findRule).not.toHaveBeenCalled();
|
|
449
|
+
for (const sender of everySender()) {
|
|
450
|
+
expect(sender).not.toHaveBeenCalled();
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
/*
|
|
456
|
+
* ----------------------------------------------------------------------- *
|
|
457
|
+
* (B) A rule id that resolves to nothing.
|
|
458
|
+
* -----------------------------------------------------------------------
|
|
459
|
+
*/
|
|
460
|
+
|
|
461
|
+
describe("a rule id that resolves to nothing", () => {
|
|
462
|
+
test('throws BadDataException("Notification rule item not found.")', async () => {
|
|
463
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
464
|
+
|
|
465
|
+
await expect(
|
|
466
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
467
|
+
RULE_ID,
|
|
468
|
+
executeOptions(),
|
|
469
|
+
),
|
|
470
|
+
).rejects.toThrow(BadDataException);
|
|
471
|
+
|
|
472
|
+
await expect(
|
|
473
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
474
|
+
RULE_ID,
|
|
475
|
+
executeOptions(),
|
|
476
|
+
),
|
|
477
|
+
).rejects.toThrow("Notification rule item not found.");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test("the throw happens before the Twilio config and incident lookups", async () => {
|
|
481
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
482
|
+
|
|
483
|
+
await expect(
|
|
484
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
485
|
+
RULE_ID,
|
|
486
|
+
executeOptions(),
|
|
487
|
+
),
|
|
488
|
+
).rejects.toThrow(BadDataException);
|
|
489
|
+
|
|
490
|
+
expect(spies.twilio).not.toHaveBeenCalled();
|
|
491
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
492
|
+
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test("nothing is sent when the rule is missing", async () => {
|
|
496
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
497
|
+
|
|
498
|
+
await expect(
|
|
499
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
500
|
+
RULE_ID,
|
|
501
|
+
executeOptions(),
|
|
502
|
+
),
|
|
503
|
+
).rejects.toThrow(BadDataException);
|
|
504
|
+
|
|
505
|
+
for (const sender of everySender()) {
|
|
506
|
+
expect(sender).not.toHaveBeenCalled();
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test("the claim has ALREADY been consumed when the rule turns out to be missing", async () => {
|
|
511
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
512
|
+
|
|
513
|
+
await expect(
|
|
514
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
515
|
+
RULE_ID,
|
|
516
|
+
executeOptions(),
|
|
517
|
+
),
|
|
518
|
+
).rejects.toThrow(BadDataException);
|
|
519
|
+
|
|
520
|
+
/*
|
|
521
|
+
* Current behaviour, worth knowing: the claim is written before the rule
|
|
522
|
+
* is read, so a rule deleted between scheduling and execution burns its
|
|
523
|
+
* claim and is never retried by a later tick. That is deliberate (a
|
|
524
|
+
* missing rule is permanent), but it means the claim is not released on
|
|
525
|
+
* failure.
|
|
526
|
+
*/
|
|
527
|
+
expect(spies.claim).toHaveBeenCalledTimes(1);
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
/*
|
|
532
|
+
* ----------------------------------------------------------------------- *
|
|
533
|
+
* (C) The select contract that the verification gates depend on.
|
|
534
|
+
* -----------------------------------------------------------------------
|
|
535
|
+
*/
|
|
536
|
+
|
|
537
|
+
describe("the select handed to findOneById", () => {
|
|
538
|
+
async function captureFindArg(): Promise<FindOneByIdArg> {
|
|
539
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
540
|
+
RULE_ID,
|
|
541
|
+
executeOptions(),
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
return spies.findRule.mock.calls[0][0] as FindOneByIdArg;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
test("targets the requested rule id, as root", async () => {
|
|
548
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
549
|
+
|
|
550
|
+
expect(arg.id.toString()).toBe(RULE_ID.toString());
|
|
551
|
+
expect(arg.props.isRoot).toBe(true);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test("is EXACTLY this shape (a dropped field silently changes who gets paged)", async () => {
|
|
555
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
556
|
+
|
|
557
|
+
/*
|
|
558
|
+
* WHY EACH RELATION ALSO ASKS FOR ITS OWN userId, WHICH NO SENDER READS.
|
|
559
|
+
*
|
|
560
|
+
* A rule is a pair of columns that nothing in the ORM ever compares: its
|
|
561
|
+
* `userId` decides WHOSE page this is, and the method relation decides
|
|
562
|
+
* WHERE that page is delivered. A row where those two name different
|
|
563
|
+
* people routes one person's pages to another person's address, and it is
|
|
564
|
+
* invisible from both ends - the rules page still lists a rule and the
|
|
565
|
+
* on-call log still records a delivered notification.
|
|
566
|
+
*
|
|
567
|
+
* The write-side guards refuse to SAVE such a row. This select is what
|
|
568
|
+
* lets executeNotificationRuleItem refuse to ACT ON one that exists
|
|
569
|
+
* anyway: written before those guards landed, written by internal code
|
|
570
|
+
* running as root, or written down a path a future change forgets to
|
|
571
|
+
* route through them. getNotificationMethodsNotOwnedByRuleOwner compares
|
|
572
|
+
* the loaded relation's userId against the rule's, and it can only report
|
|
573
|
+
* a mismatch it can SEE - an unselected column arrives as `undefined` and
|
|
574
|
+
* is deliberately read as "no evidence", so dropping any one of these
|
|
575
|
+
* seven silently disables the backstop for that channel.
|
|
576
|
+
*
|
|
577
|
+
* Kept as an exact-shape assertion on purpose. This test caught the
|
|
578
|
+
* column being added, which is the whole argument for not relaxing it
|
|
579
|
+
* into a partial match: the select is a contract about who gets paged,
|
|
580
|
+
* and every future edit to it should have to come through here.
|
|
581
|
+
*/
|
|
582
|
+
expect(arg.select).toEqual({
|
|
583
|
+
_id: true,
|
|
584
|
+
userId: true,
|
|
585
|
+
userCall: {
|
|
586
|
+
phone: true,
|
|
587
|
+
isVerified: true,
|
|
588
|
+
userId: true,
|
|
589
|
+
},
|
|
590
|
+
userSms: {
|
|
591
|
+
phone: true,
|
|
592
|
+
isVerified: true,
|
|
593
|
+
userId: true,
|
|
594
|
+
},
|
|
595
|
+
userWhatsApp: {
|
|
596
|
+
phone: true,
|
|
597
|
+
isVerified: true,
|
|
598
|
+
userId: true,
|
|
599
|
+
},
|
|
600
|
+
userTelegram: {
|
|
601
|
+
telegramChatId: true,
|
|
602
|
+
telegramUserHandle: true,
|
|
603
|
+
isVerified: true,
|
|
604
|
+
userId: true,
|
|
605
|
+
},
|
|
606
|
+
userWebhook: {
|
|
607
|
+
webhookUrl: true,
|
|
608
|
+
name: true,
|
|
609
|
+
secret: true,
|
|
610
|
+
userId: true,
|
|
611
|
+
},
|
|
612
|
+
userEmail: {
|
|
613
|
+
email: true,
|
|
614
|
+
isVerified: true,
|
|
615
|
+
userId: true,
|
|
616
|
+
},
|
|
617
|
+
userPush: {
|
|
618
|
+
deviceToken: true,
|
|
619
|
+
deviceType: true,
|
|
620
|
+
isVerified: true,
|
|
621
|
+
userId: true,
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test.each<[string]>([
|
|
627
|
+
["userCall"],
|
|
628
|
+
["userSms"],
|
|
629
|
+
["userWhatsApp"],
|
|
630
|
+
["userTelegram"],
|
|
631
|
+
["userEmail"],
|
|
632
|
+
["userPush"],
|
|
633
|
+
])(
|
|
634
|
+
"%s requests isVerified - without it every verified method reads as unverified",
|
|
635
|
+
async (channel: string) => {
|
|
636
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
637
|
+
const selected: JSONObject = arg.select[channel] as JSONObject;
|
|
638
|
+
|
|
639
|
+
expect(selected).toBeDefined();
|
|
640
|
+
expect(selected["isVerified"]).toBe(true);
|
|
641
|
+
},
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
test.each<[string, string]>([
|
|
645
|
+
["userCall", "phone"],
|
|
646
|
+
["userSms", "phone"],
|
|
647
|
+
["userWhatsApp", "phone"],
|
|
648
|
+
["userTelegram", "telegramChatId"],
|
|
649
|
+
["userEmail", "email"],
|
|
650
|
+
["userPush", "deviceToken"],
|
|
651
|
+
])(
|
|
652
|
+
"%s also requests its address column (%s), which the send gate reads",
|
|
653
|
+
async (channel: string, addressColumn: string) => {
|
|
654
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
655
|
+
const selected: JSONObject = arg.select[channel] as JSONObject;
|
|
656
|
+
|
|
657
|
+
expect(selected[addressColumn]).toBe(true);
|
|
658
|
+
},
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
test("userWebhook requests webhookUrl, name, secret - and its owner", async () => {
|
|
662
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
663
|
+
const webhookSelect: JSONObject = arg.select["userWebhook"] as JSONObject;
|
|
664
|
+
|
|
665
|
+
/*
|
|
666
|
+
* `userId` is read by no part of the webhook send. It is the
|
|
667
|
+
* defence-in-depth column described on the exact-shape assertion above -
|
|
668
|
+
* the one the pre-delivery ownership check compares against the rule's
|
|
669
|
+
* own userId - and webhooks are the channel where a hijacked method
|
|
670
|
+
* matters most, because the attacker chooses the endpoint outright.
|
|
671
|
+
*/
|
|
672
|
+
expect(webhookSelect).toEqual({
|
|
673
|
+
webhookUrl: true,
|
|
674
|
+
name: true,
|
|
675
|
+
secret: true,
|
|
676
|
+
userId: true,
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
test("userWebhook requests NO isVerified - the UserWebhook model has no such column", async () => {
|
|
681
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
682
|
+
const webhookSelect: JSONObject = arg.select["userWebhook"] as JSONObject;
|
|
683
|
+
|
|
684
|
+
/*
|
|
685
|
+
* Pinned as-is: webhooks are the one user-contactable channel with no
|
|
686
|
+
* verification concept at all (UserWebhook has no isVerified column), so
|
|
687
|
+
* the delivery gate below is `webhookUrl` alone. If webhook verification
|
|
688
|
+
* is ever introduced, this select AND the gate both have to change.
|
|
689
|
+
*/
|
|
690
|
+
expect(webhookSelect["isVerified"]).toBeUndefined();
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
test("userPush does NOT request deviceName even though the sender reads it", async () => {
|
|
694
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
695
|
+
const pushSelect: JSONObject = arg.select["userPush"] as JSONObject;
|
|
696
|
+
|
|
697
|
+
/*
|
|
698
|
+
* KNOWN DEFECT (pinned as-is): the push send path reads
|
|
699
|
+
* `notificationRuleItem.userPush.deviceName` to label the device, but
|
|
700
|
+
* deviceName is not selected here, so it is always undefined and the
|
|
701
|
+
* `name` key is never spread onto the device payload. Harmless today
|
|
702
|
+
* (cosmetic label only) but it is a live select/consumer mismatch.
|
|
703
|
+
*/
|
|
704
|
+
expect(pushSelect["deviceName"]).toBeUndefined();
|
|
705
|
+
expect(pushSelect["deviceToken"]).toBe(true);
|
|
706
|
+
expect(pushSelect["deviceType"]).toBe(true);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
test("the rule is read exactly once per execution", async () => {
|
|
710
|
+
await captureFindArg();
|
|
711
|
+
|
|
712
|
+
expect(spies.findRule).toHaveBeenCalledTimes(1);
|
|
713
|
+
});
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
/*
|
|
717
|
+
* ----------------------------------------------------------------------- *
|
|
718
|
+
* (C continued) What the selected isVerified actually gates.
|
|
719
|
+
* -----------------------------------------------------------------------
|
|
720
|
+
*/
|
|
721
|
+
|
|
722
|
+
describe("verification gating (what isVerified in the select buys)", () => {
|
|
723
|
+
test("a VERIFIED email is delivered and its timeline row is Sending", async () => {
|
|
724
|
+
spies.findRule.mockResolvedValue(
|
|
725
|
+
ruleItem({
|
|
726
|
+
userEmail: {
|
|
727
|
+
id: METHOD_ID,
|
|
728
|
+
email: new Email("responder@company.com"),
|
|
729
|
+
isVerified: true,
|
|
730
|
+
},
|
|
731
|
+
} as unknown as JSONObject) as never,
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
735
|
+
RULE_ID,
|
|
736
|
+
executeOptions(),
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
expect(spies.mail).toHaveBeenCalledTimes(1);
|
|
740
|
+
expect(timelineRows).toHaveLength(1);
|
|
741
|
+
expect(timelineRows[0]?.status).toBe(UserNotificationStatus.Sending);
|
|
742
|
+
expect(timelineRows[0]?.statusMessage).toContain("responder@company.com");
|
|
743
|
+
// The timeline row is stamped with the identifiers a reader needs.
|
|
744
|
+
expect(timelineRows[0]?.userId?.toString()).toBe(USER_ID.toString());
|
|
745
|
+
expect(timelineRows[0]?.userNotificationRuleId?.toString()).toBe(
|
|
746
|
+
RULE_ID.toString(),
|
|
747
|
+
);
|
|
748
|
+
expect(timelineRows[0]?.userNotificationLogId?.toString()).toBe(
|
|
749
|
+
LOG_ID.toString(),
|
|
750
|
+
);
|
|
751
|
+
expect(timelineRows[0]?.projectId?.toString()).toBe(
|
|
752
|
+
PROJECT_ID.toString(),
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
test("no unverified channel is ever contacted, and each writes one Error row", async () => {
|
|
757
|
+
spies.findRule.mockResolvedValue(
|
|
758
|
+
ruleItem({
|
|
759
|
+
userEmail: {
|
|
760
|
+
id: METHOD_ID,
|
|
761
|
+
email: new Email("responder@company.com"),
|
|
762
|
+
isVerified: false,
|
|
763
|
+
},
|
|
764
|
+
userSms: {
|
|
765
|
+
id: METHOD_ID,
|
|
766
|
+
phone: new Phone("+11234567890"),
|
|
767
|
+
isVerified: false,
|
|
768
|
+
},
|
|
769
|
+
userWhatsApp: {
|
|
770
|
+
id: METHOD_ID,
|
|
771
|
+
phone: new Phone("+11234567891"),
|
|
772
|
+
isVerified: false,
|
|
773
|
+
},
|
|
774
|
+
userTelegram: {
|
|
775
|
+
id: METHOD_ID,
|
|
776
|
+
telegramChatId: "123456",
|
|
777
|
+
isVerified: false,
|
|
778
|
+
},
|
|
779
|
+
userCall: {
|
|
780
|
+
id: METHOD_ID,
|
|
781
|
+
phone: new Phone("+11234567892"),
|
|
782
|
+
isVerified: false,
|
|
783
|
+
},
|
|
784
|
+
userPush: {
|
|
785
|
+
id: METHOD_ID,
|
|
786
|
+
deviceToken: "device-token",
|
|
787
|
+
deviceType: "iOS",
|
|
788
|
+
isVerified: false,
|
|
789
|
+
},
|
|
790
|
+
} as unknown as JSONObject) as never,
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
794
|
+
RULE_ID,
|
|
795
|
+
executeOptions(),
|
|
796
|
+
);
|
|
797
|
+
|
|
798
|
+
expect(spies.mail).not.toHaveBeenCalled();
|
|
799
|
+
expect(spies.sms).not.toHaveBeenCalled();
|
|
800
|
+
expect(spies.call).not.toHaveBeenCalled();
|
|
801
|
+
expect(spies.whatsApp).not.toHaveBeenCalled();
|
|
802
|
+
expect(spies.telegram).not.toHaveBeenCalled();
|
|
803
|
+
expect(spies.push).not.toHaveBeenCalled();
|
|
804
|
+
|
|
805
|
+
// One "not verified" Error row per unverified channel: 6 in total.
|
|
806
|
+
expect(timelineRows).toHaveLength(6);
|
|
807
|
+
for (const row of timelineRows) {
|
|
808
|
+
expect(row.status).toBe(UserNotificationStatus.Error);
|
|
809
|
+
expect(row.statusMessage).toContain("not verified");
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
test("the Telegram unverified branch keys off the RELATION, not the chat id", async () => {
|
|
814
|
+
spies.findRule.mockResolvedValue(
|
|
815
|
+
ruleItem({
|
|
816
|
+
userTelegram: {
|
|
817
|
+
id: METHOD_ID,
|
|
818
|
+
isVerified: false,
|
|
819
|
+
},
|
|
820
|
+
} as unknown as JSONObject) as never,
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
824
|
+
RULE_ID,
|
|
825
|
+
executeOptions(),
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
/*
|
|
829
|
+
* Every other channel guards its "not verified" log on the address being
|
|
830
|
+
* present (`userSms?.phone && !isVerified`); Telegram alone guards on the
|
|
831
|
+
* relation existing, so a Telegram row with no chat id still produces an
|
|
832
|
+
* Error row. Pinned as current behaviour.
|
|
833
|
+
*/
|
|
834
|
+
expect(spies.telegram).not.toHaveBeenCalled();
|
|
835
|
+
expect(timelineRows).toHaveLength(1);
|
|
836
|
+
expect(timelineRows[0]?.statusMessage).toContain("not verified");
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
test("a webhook is dispatched on webhookUrl alone - there is no verification gate", async () => {
|
|
840
|
+
spies.findRule.mockResolvedValue(
|
|
841
|
+
ruleItem({
|
|
842
|
+
userWebhook: {
|
|
843
|
+
id: METHOD_ID,
|
|
844
|
+
webhookUrl: "https://hooks.example.com/on-call",
|
|
845
|
+
name: "Pager bridge",
|
|
846
|
+
secret: "s3cr3t",
|
|
847
|
+
},
|
|
848
|
+
} as unknown as JSONObject) as never,
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
852
|
+
RULE_ID,
|
|
853
|
+
executeOptions(),
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
expect(spies.webhook).toHaveBeenCalledTimes(1);
|
|
857
|
+
const payload: {
|
|
858
|
+
url: string;
|
|
859
|
+
eventType: string;
|
|
860
|
+
secret: string | undefined;
|
|
861
|
+
} = spies.webhook.mock.calls[0][0] as {
|
|
862
|
+
url: string;
|
|
863
|
+
eventType: string;
|
|
864
|
+
secret: string | undefined;
|
|
865
|
+
};
|
|
866
|
+
expect(payload.url).toBe("https://hooks.example.com/on-call");
|
|
867
|
+
expect(payload.eventType).toBe("on-call.incident.created");
|
|
868
|
+
// The selected `secret` is what signs the request - it must be selected.
|
|
869
|
+
expect(payload.secret).toBe("s3cr3t");
|
|
870
|
+
expect(timelineRows).toHaveLength(1);
|
|
871
|
+
expect(timelineRows[0]?.status).toBe(UserNotificationStatus.Sending);
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
test("a rule with no contactable channel at all sends nothing and logs nothing", async () => {
|
|
875
|
+
spies.findRule.mockResolvedValue(ruleItem() as never);
|
|
876
|
+
|
|
877
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
878
|
+
RULE_ID,
|
|
879
|
+
executeOptions(),
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
for (const sender of everySender()) {
|
|
883
|
+
expect(sender).not.toHaveBeenCalled();
|
|
884
|
+
}
|
|
885
|
+
/*
|
|
886
|
+
* KNOWN GAP (pinned as-is): a hydrated rule pointing at a method that was
|
|
887
|
+
* cascade-deleted produces a completely silent execution - no send and no
|
|
888
|
+
* timeline row saying why.
|
|
889
|
+
*/
|
|
890
|
+
expect(timelineRows).toHaveLength(0);
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
/*
|
|
895
|
+
* ----------------------------------------------------------------------- *
|
|
896
|
+
* (C continued) The trigger entity must resolve before anything is sent.
|
|
897
|
+
* -----------------------------------------------------------------------
|
|
898
|
+
*/
|
|
899
|
+
|
|
900
|
+
describe("resolving the triggering entity", () => {
|
|
901
|
+
test("throws when the incident behind an IncidentCreated event is gone", async () => {
|
|
902
|
+
spies.incidentFind.mockResolvedValue(null as never);
|
|
903
|
+
|
|
904
|
+
await expect(
|
|
905
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
906
|
+
RULE_ID,
|
|
907
|
+
executeOptions(),
|
|
908
|
+
),
|
|
909
|
+
).rejects.toThrow(
|
|
910
|
+
"Incident, Alert, Alert Episode, or Incident Episode not found.",
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
for (const sender of everySender()) {
|
|
914
|
+
expect(sender).not.toHaveBeenCalled();
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
test("an IncidentCreated event with no triggeredByIncidentId never even queries the incident", async () => {
|
|
919
|
+
await expect(
|
|
920
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
921
|
+
RULE_ID,
|
|
922
|
+
executeOptions({ triggeredByIncidentId: undefined }),
|
|
923
|
+
),
|
|
924
|
+
).rejects.toThrow(BadDataException);
|
|
925
|
+
|
|
926
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
test("the event type - not the supplied ids - picks which entity is loaded", async () => {
|
|
930
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
931
|
+
RULE_ID,
|
|
932
|
+
executeOptions({
|
|
933
|
+
userNotificationEventType: UserNotificationEventType.AlertCreated,
|
|
934
|
+
triggeredByAlertId: ALERT_ID,
|
|
935
|
+
triggeredByIncidentId: INCIDENT_ID,
|
|
936
|
+
}),
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
expect(spies.alertFind).toHaveBeenCalledTimes(1);
|
|
940
|
+
// IncidentCreated is not the event type, so the incident is never read.
|
|
941
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
942
|
+
});
|
|
943
|
+
});
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
/*
|
|
947
|
+
* ------------------------------------------------------------------------- *
|
|
948
|
+
* (D) startUserNotificationRulesExecution - the scheduling entrypoint.
|
|
949
|
+
* -------------------------------------------------------------------------
|
|
950
|
+
*/
|
|
951
|
+
|
|
952
|
+
describe("UserNotificationRuleService.startUserNotificationRulesExecution", () => {
|
|
953
|
+
let createLog: jest.SpyInstance;
|
|
954
|
+
let workspaceRules: jest.SpyInstance;
|
|
955
|
+
let loggerError: jest.SpyInstance;
|
|
956
|
+
|
|
957
|
+
function startOptions(overrides: Partial<StartOptions> = {}): StartOptions {
|
|
958
|
+
return {
|
|
959
|
+
projectId: PROJECT_ID,
|
|
960
|
+
userNotificationEventType: UserNotificationEventType.IncidentCreated,
|
|
961
|
+
onCallPolicyId: POLICY_ID,
|
|
962
|
+
...overrides,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
function fullStartOptions(): StartOptions {
|
|
967
|
+
return {
|
|
968
|
+
projectId: PROJECT_ID,
|
|
969
|
+
triggeredByIncidentId: INCIDENT_ID,
|
|
970
|
+
triggeredByAlertId: ALERT_ID,
|
|
971
|
+
triggeredByAlertEpisodeId: ALERT_EPISODE_ID,
|
|
972
|
+
triggeredByIncidentEpisodeId: INCIDENT_EPISODE_ID,
|
|
973
|
+
userNotificationEventType: UserNotificationEventType.IncidentCreated,
|
|
974
|
+
onCallPolicyExecutionLogId: EXECUTION_LOG_ID,
|
|
975
|
+
onCallPolicyId: POLICY_ID,
|
|
976
|
+
onCallPolicyEscalationRuleId: ESCALATION_RULE_ID,
|
|
977
|
+
userBelongsToTeamId: TEAM_ID,
|
|
978
|
+
onCallDutyPolicyExecutionLogTimelineId: EXECUTION_LOG_TIMELINE_ID,
|
|
979
|
+
onCallScheduleId: SCHEDULE_ID,
|
|
980
|
+
overridedByUserId: OVERRIDE_USER_ID,
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
function createdLog(): UserOnCallLog {
|
|
985
|
+
return (createLog.mock.calls[0][0] as { data: UserOnCallLog }).data;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
beforeEach(() => {
|
|
989
|
+
loggerError = jest.spyOn(logger, "error").mockImplementation((): void => {
|
|
990
|
+
return undefined;
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
createLog = jest
|
|
994
|
+
.spyOn(UserOnCallLogService, "create")
|
|
995
|
+
.mockResolvedValue({ id: LOG_ID } as unknown as UserOnCallLog as never);
|
|
996
|
+
|
|
997
|
+
workspaceRules = jest
|
|
998
|
+
.spyOn(
|
|
999
|
+
UserNotificationRuleService,
|
|
1000
|
+
"runWorkspaceRulesForOnCallNotification",
|
|
1001
|
+
)
|
|
1002
|
+
.mockResolvedValue(undefined as never);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
afterEach(() => {
|
|
1006
|
+
jest.restoreAllMocks();
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
test("creates exactly one UserOnCallLog, as root", async () => {
|
|
1010
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1011
|
+
USER_ID,
|
|
1012
|
+
startOptions(),
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
expect(createLog).toHaveBeenCalledTimes(1);
|
|
1016
|
+
const arg: { props: { isRoot: boolean } } = createLog.mock.calls[0][0] as {
|
|
1017
|
+
props: { isRoot: boolean };
|
|
1018
|
+
};
|
|
1019
|
+
expect(arg.props.isRoot).toBe(true);
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
test("the log is Scheduled with statusMessage 'Scheduled' so the worker picks it up", async () => {
|
|
1023
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1024
|
+
USER_ID,
|
|
1025
|
+
startOptions(),
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
const log: UserOnCallLog = createdLog();
|
|
1029
|
+
expect(log.status).toBe(UserNotificationExecutionStatus.Scheduled);
|
|
1030
|
+
expect(log.statusMessage).toBe("Scheduled");
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
test("the user and project always land on the log", async () => {
|
|
1034
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1035
|
+
USER_ID,
|
|
1036
|
+
startOptions(),
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
const log: UserOnCallLog = createdLog();
|
|
1040
|
+
expect(log.userId?.toString()).toBe(USER_ID.toString());
|
|
1041
|
+
expect(log.projectId?.toString()).toBe(PROJECT_ID.toString());
|
|
1042
|
+
expect(log.userNotificationEventType).toBe(
|
|
1043
|
+
UserNotificationEventType.IncidentCreated,
|
|
1044
|
+
);
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
test("EVERY option field is forwarded onto the log when all of them are supplied", async () => {
|
|
1048
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1049
|
+
USER_ID,
|
|
1050
|
+
fullStartOptions(),
|
|
1051
|
+
);
|
|
1052
|
+
|
|
1053
|
+
const log: UserOnCallLog = createdLog();
|
|
1054
|
+
|
|
1055
|
+
expect(log.triggeredByIncidentId?.toString()).toBe(INCIDENT_ID.toString());
|
|
1056
|
+
expect(log.triggeredByAlertId?.toString()).toBe(ALERT_ID.toString());
|
|
1057
|
+
expect(log.triggeredByAlertEpisodeId?.toString()).toBe(
|
|
1058
|
+
ALERT_EPISODE_ID.toString(),
|
|
1059
|
+
);
|
|
1060
|
+
expect(log.triggeredByIncidentEpisodeId?.toString()).toBe(
|
|
1061
|
+
INCIDENT_EPISODE_ID.toString(),
|
|
1062
|
+
);
|
|
1063
|
+
// Renamed on the way in: onCallPolicy* -> onCallDutyPolicy*.
|
|
1064
|
+
expect(log.onCallDutyPolicyExecutionLogId?.toString()).toBe(
|
|
1065
|
+
EXECUTION_LOG_ID.toString(),
|
|
1066
|
+
);
|
|
1067
|
+
expect(log.onCallDutyPolicyId?.toString()).toBe(POLICY_ID.toString());
|
|
1068
|
+
expect(log.onCallDutyPolicyEscalationRuleId?.toString()).toBe(
|
|
1069
|
+
ESCALATION_RULE_ID.toString(),
|
|
1070
|
+
);
|
|
1071
|
+
expect(log.onCallDutyPolicyExecutionLogTimelineId?.toString()).toBe(
|
|
1072
|
+
EXECUTION_LOG_TIMELINE_ID.toString(),
|
|
1073
|
+
);
|
|
1074
|
+
expect(log.userBelongsToTeamId?.toString()).toBe(TEAM_ID.toString());
|
|
1075
|
+
// onCallScheduleId -> onCallDutyScheduleId.
|
|
1076
|
+
expect(log.onCallDutyScheduleId?.toString()).toBe(SCHEDULE_ID.toString());
|
|
1077
|
+
expect(log.overridedByUserId?.toString()).toBe(OVERRIDE_USER_ID.toString());
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
test("optional fields left out stay unset on the log (no undefined-id writes)", async () => {
|
|
1081
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1082
|
+
USER_ID,
|
|
1083
|
+
startOptions({ onCallPolicyId: undefined }),
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
const log: UserOnCallLog = createdLog();
|
|
1087
|
+
expect(log.triggeredByIncidentId).toBeUndefined();
|
|
1088
|
+
expect(log.triggeredByAlertId).toBeUndefined();
|
|
1089
|
+
expect(log.triggeredByAlertEpisodeId).toBeUndefined();
|
|
1090
|
+
expect(log.triggeredByIncidentEpisodeId).toBeUndefined();
|
|
1091
|
+
expect(log.onCallDutyPolicyId).toBeUndefined();
|
|
1092
|
+
expect(log.onCallDutyPolicyExecutionLogId).toBeUndefined();
|
|
1093
|
+
expect(log.onCallDutyPolicyEscalationRuleId).toBeUndefined();
|
|
1094
|
+
expect(log.onCallDutyPolicyExecutionLogTimelineId).toBeUndefined();
|
|
1095
|
+
expect(log.userBelongsToTeamId).toBeUndefined();
|
|
1096
|
+
expect(log.onCallDutyScheduleId).toBeUndefined();
|
|
1097
|
+
expect(log.overridedByUserId).toBeUndefined();
|
|
1098
|
+
// ...but the status fields are unconditional.
|
|
1099
|
+
expect(log.status).toBe(UserNotificationExecutionStatus.Scheduled);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
test("the workspace-invite call is handed projectId, alertId, incidentId and userId", async () => {
|
|
1103
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1104
|
+
USER_ID,
|
|
1105
|
+
startOptions({ triggeredByIncidentId: INCIDENT_ID }),
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1108
|
+
expect(workspaceRules).toHaveBeenCalledTimes(1);
|
|
1109
|
+
const arg: {
|
|
1110
|
+
projectId: ObjectID;
|
|
1111
|
+
alertId: ObjectID | undefined;
|
|
1112
|
+
incidentId: ObjectID | undefined;
|
|
1113
|
+
userId: ObjectID;
|
|
1114
|
+
} = workspaceRules.mock.calls[0][0] as {
|
|
1115
|
+
projectId: ObjectID;
|
|
1116
|
+
alertId: ObjectID | undefined;
|
|
1117
|
+
incidentId: ObjectID | undefined;
|
|
1118
|
+
userId: ObjectID;
|
|
1119
|
+
};
|
|
1120
|
+
expect(arg.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
1121
|
+
expect(arg.incidentId?.toString()).toBe(INCIDENT_ID.toString());
|
|
1122
|
+
expect(arg.alertId).toBeUndefined();
|
|
1123
|
+
expect(arg.userId.toString()).toBe(USER_ID.toString());
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
test("the log is created BEFORE the workspace-invite call is fired", async () => {
|
|
1127
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1128
|
+
USER_ID,
|
|
1129
|
+
startOptions({ triggeredByIncidentId: INCIDENT_ID }),
|
|
1130
|
+
);
|
|
1131
|
+
|
|
1132
|
+
expect(createLog.mock.invocationCallOrder[0]).toBeLessThan(
|
|
1133
|
+
workspaceRules.mock.invocationCallOrder[0] as number,
|
|
1134
|
+
);
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
test("a failure creating the log propagates, and no workspace invite is attempted", async () => {
|
|
1138
|
+
createLog.mockRejectedValue(new Error("pool timeout") as never);
|
|
1139
|
+
|
|
1140
|
+
await expect(
|
|
1141
|
+
UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1142
|
+
USER_ID,
|
|
1143
|
+
startOptions(),
|
|
1144
|
+
),
|
|
1145
|
+
).rejects.toThrow("pool timeout");
|
|
1146
|
+
|
|
1147
|
+
expect(workspaceRules).not.toHaveBeenCalled();
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
test("a REJECTION from the workspace-invite call does NOT reject the scheduling", async () => {
|
|
1151
|
+
workspaceRules.mockRejectedValue(
|
|
1152
|
+
new Error("slack workspace unreachable") as never,
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
/*
|
|
1156
|
+
* This is the whole reason the call is `.catch`-ed rather than awaited: an
|
|
1157
|
+
* unreachable Slack must never stop a page from being scheduled. If this
|
|
1158
|
+
* ever starts rejecting, every on-call notification in a project with a
|
|
1159
|
+
* broken workspace integration disappears.
|
|
1160
|
+
*/
|
|
1161
|
+
await expect(
|
|
1162
|
+
UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1163
|
+
USER_ID,
|
|
1164
|
+
startOptions({ triggeredByIncidentId: INCIDENT_ID }),
|
|
1165
|
+
),
|
|
1166
|
+
).resolves.toBeUndefined();
|
|
1167
|
+
|
|
1168
|
+
await flushMicrotasks();
|
|
1169
|
+
|
|
1170
|
+
// It is swallowed into the log, not lost silently.
|
|
1171
|
+
expect(loggerError).toHaveBeenCalled();
|
|
1172
|
+
expect(createLog).toHaveBeenCalledTimes(1);
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
test("the swallowed workspace error is logged with the project and user attributes", async () => {
|
|
1176
|
+
workspaceRules.mockRejectedValue(new Error("slack down") as never);
|
|
1177
|
+
|
|
1178
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1179
|
+
USER_ID,
|
|
1180
|
+
startOptions({ triggeredByIncidentId: INCIDENT_ID }),
|
|
1181
|
+
);
|
|
1182
|
+
await flushMicrotasks();
|
|
1183
|
+
|
|
1184
|
+
const attributes: { projectId: string; userId: string } = loggerError.mock
|
|
1185
|
+
.calls[0][1] as { projectId: string; userId: string };
|
|
1186
|
+
expect(attributes.projectId).toBe(PROJECT_ID.toString());
|
|
1187
|
+
expect(attributes.userId).toBe(USER_ID.toString());
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
test("the workspace-invite call is NOT awaited (scheduling returns while it is still pending)", async () => {
|
|
1191
|
+
let release: () => void = (): void => {
|
|
1192
|
+
return undefined;
|
|
1193
|
+
};
|
|
1194
|
+
const pending: Promise<void> = new Promise<void>(
|
|
1195
|
+
(resolve: () => void): void => {
|
|
1196
|
+
release = resolve;
|
|
1197
|
+
},
|
|
1198
|
+
);
|
|
1199
|
+
workspaceRules.mockReturnValue(pending);
|
|
1200
|
+
|
|
1201
|
+
/*
|
|
1202
|
+
* If startUserNotificationRulesExecution awaited the workspace call, this
|
|
1203
|
+
* await would never settle and the test would time out. Reaching the next
|
|
1204
|
+
* line IS the assertion.
|
|
1205
|
+
*/
|
|
1206
|
+
await UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1207
|
+
USER_ID,
|
|
1208
|
+
startOptions({ triggeredByIncidentId: INCIDENT_ID }),
|
|
1209
|
+
);
|
|
1210
|
+
|
|
1211
|
+
expect(workspaceRules).toHaveBeenCalledTimes(1);
|
|
1212
|
+
|
|
1213
|
+
release();
|
|
1214
|
+
await pending;
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
test("an episode-triggered execution has no incident or alert id, so the workspace call always errors", async () => {
|
|
1218
|
+
/*
|
|
1219
|
+
* Discovered behaviour, pinned as-is: startUserNotificationRulesExecution
|
|
1220
|
+
* forwards `alertId`/`incidentId` verbatim, and
|
|
1221
|
+
* runWorkspaceRulesForOnCallNotification throws when BOTH are missing. For
|
|
1222
|
+
* AlertEpisodeCreated / IncidentEpisodeCreated executions that is always
|
|
1223
|
+
* the case, so every episode page logs a BadDataException on this path.
|
|
1224
|
+
* Harmless to delivery (it is caught), but it is permanent log noise.
|
|
1225
|
+
*/
|
|
1226
|
+
workspaceRules.mockRestore();
|
|
1227
|
+
|
|
1228
|
+
const getRules: jest.SpyInstance = jest.spyOn(
|
|
1229
|
+
WorkspaceNotificationRuleService,
|
|
1230
|
+
"getNotificationRulesWhereInviteOwnersIsTrue",
|
|
1231
|
+
);
|
|
1232
|
+
|
|
1233
|
+
await expect(
|
|
1234
|
+
UserNotificationRuleService.startUserNotificationRulesExecution(
|
|
1235
|
+
USER_ID,
|
|
1236
|
+
startOptions({
|
|
1237
|
+
userNotificationEventType:
|
|
1238
|
+
UserNotificationEventType.AlertEpisodeCreated,
|
|
1239
|
+
triggeredByAlertEpisodeId: ALERT_EPISODE_ID,
|
|
1240
|
+
}),
|
|
1241
|
+
),
|
|
1242
|
+
).resolves.toBeUndefined();
|
|
1243
|
+
|
|
1244
|
+
await flushMicrotasks();
|
|
1245
|
+
|
|
1246
|
+
expect(createLog).toHaveBeenCalledTimes(1);
|
|
1247
|
+
expect(getRules).not.toHaveBeenCalled();
|
|
1248
|
+
expect(loggerError).toHaveBeenCalled();
|
|
1249
|
+
expect(loggerError.mock.calls[0][0]).toBeInstanceOf(BadDataException);
|
|
1250
|
+
});
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
/*
|
|
1254
|
+
* ------------------------------------------------------------------------- *
|
|
1255
|
+
* (E) runWorkspaceRulesForOnCallNotification.
|
|
1256
|
+
* -------------------------------------------------------------------------
|
|
1257
|
+
*/
|
|
1258
|
+
|
|
1259
|
+
describe("UserNotificationRuleService.runWorkspaceRulesForOnCallNotification", () => {
|
|
1260
|
+
let getRules: jest.SpyInstance;
|
|
1261
|
+
let inviteUsers: jest.SpyInstance;
|
|
1262
|
+
let incidentChannels: jest.SpyInstance;
|
|
1263
|
+
let alertChannels: jest.SpyInstance;
|
|
1264
|
+
let loggerError: jest.SpyInstance;
|
|
1265
|
+
|
|
1266
|
+
beforeEach(() => {
|
|
1267
|
+
loggerError = jest.spyOn(logger, "error").mockImplementation((): void => {
|
|
1268
|
+
return undefined;
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
getRules = jest
|
|
1272
|
+
.spyOn(
|
|
1273
|
+
WorkspaceNotificationRuleService,
|
|
1274
|
+
"getNotificationRulesWhereInviteOwnersIsTrue",
|
|
1275
|
+
)
|
|
1276
|
+
.mockResolvedValue([] as never);
|
|
1277
|
+
|
|
1278
|
+
inviteUsers = jest
|
|
1279
|
+
.spyOn(
|
|
1280
|
+
WorkspaceNotificationRuleService,
|
|
1281
|
+
"inviteUsersBasedOnRulesAndWorkspaceChannels",
|
|
1282
|
+
)
|
|
1283
|
+
.mockResolvedValue(undefined as never);
|
|
1284
|
+
|
|
1285
|
+
incidentChannels = jest
|
|
1286
|
+
.spyOn(IncidentService, "getWorkspaceChannelForIncident")
|
|
1287
|
+
.mockResolvedValue([] as never);
|
|
1288
|
+
|
|
1289
|
+
alertChannels = jest
|
|
1290
|
+
.spyOn(AlertService, "getWorkspaceChannelForAlert")
|
|
1291
|
+
.mockResolvedValue([] as never);
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
afterEach(() => {
|
|
1295
|
+
jest.restoreAllMocks();
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
test("throws BadDataException when BOTH incidentId and alertId are supplied", async () => {
|
|
1299
|
+
await expect(
|
|
1300
|
+
UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1301
|
+
projectId: PROJECT_ID,
|
|
1302
|
+
incidentId: INCIDENT_ID,
|
|
1303
|
+
alertId: ALERT_ID,
|
|
1304
|
+
userId: USER_ID,
|
|
1305
|
+
}),
|
|
1306
|
+
).rejects.toThrow(BadDataException);
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
test("the both-supplied message is 'Either incidentId or alertId is required.'", async () => {
|
|
1310
|
+
/*
|
|
1311
|
+
* Pinned as-is even though it reads wrongly for this branch: the ambiguous
|
|
1312
|
+
* case ("you gave me both") reuses the missing-case message verbatim.
|
|
1313
|
+
*/
|
|
1314
|
+
await expect(
|
|
1315
|
+
UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1316
|
+
projectId: PROJECT_ID,
|
|
1317
|
+
incidentId: INCIDENT_ID,
|
|
1318
|
+
alertId: ALERT_ID,
|
|
1319
|
+
userId: USER_ID,
|
|
1320
|
+
}),
|
|
1321
|
+
).rejects.toThrow("Either incidentId or alertId is required.");
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
test("supplying both short-circuits before any workspace query", async () => {
|
|
1325
|
+
await expect(
|
|
1326
|
+
UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1327
|
+
projectId: PROJECT_ID,
|
|
1328
|
+
incidentId: INCIDENT_ID,
|
|
1329
|
+
alertId: ALERT_ID,
|
|
1330
|
+
userId: USER_ID,
|
|
1331
|
+
}),
|
|
1332
|
+
).rejects.toThrow(BadDataException);
|
|
1333
|
+
|
|
1334
|
+
expect(getRules).not.toHaveBeenCalled();
|
|
1335
|
+
expect(incidentChannels).not.toHaveBeenCalled();
|
|
1336
|
+
expect(alertChannels).not.toHaveBeenCalled();
|
|
1337
|
+
expect(inviteUsers).not.toHaveBeenCalled();
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
test("throws the same BadDataException when NEITHER is supplied", async () => {
|
|
1341
|
+
await expect(
|
|
1342
|
+
UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1343
|
+
projectId: PROJECT_ID,
|
|
1344
|
+
userId: USER_ID,
|
|
1345
|
+
}),
|
|
1346
|
+
).rejects.toThrow("Either incidentId or alertId is required.");
|
|
1347
|
+
|
|
1348
|
+
expect(getRules).not.toHaveBeenCalled();
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
test("the incident path queries incident rules and incident channels only", async () => {
|
|
1352
|
+
await UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1353
|
+
projectId: PROJECT_ID,
|
|
1354
|
+
incidentId: INCIDENT_ID,
|
|
1355
|
+
userId: USER_ID,
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
const rulesArg: {
|
|
1359
|
+
projectId: ObjectID;
|
|
1360
|
+
notificationFor: { incidentId?: ObjectID; alertId?: ObjectID };
|
|
1361
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
1362
|
+
} = getRules.mock.calls[0][0] as {
|
|
1363
|
+
projectId: ObjectID;
|
|
1364
|
+
notificationFor: { incidentId?: ObjectID; alertId?: ObjectID };
|
|
1365
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
1366
|
+
};
|
|
1367
|
+
expect(rulesArg.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
1368
|
+
expect(rulesArg.notificationFor.incidentId?.toString()).toBe(
|
|
1369
|
+
INCIDENT_ID.toString(),
|
|
1370
|
+
);
|
|
1371
|
+
expect(rulesArg.notificationFor.alertId).toBeUndefined();
|
|
1372
|
+
expect(rulesArg.notificationRuleEventType).toBe(
|
|
1373
|
+
NotificationRuleEventType.Incident,
|
|
1374
|
+
);
|
|
1375
|
+
|
|
1376
|
+
expect(incidentChannels).toHaveBeenCalledTimes(1);
|
|
1377
|
+
expect(alertChannels).not.toHaveBeenCalled();
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
test("the alert path queries alert rules and alert channels only", async () => {
|
|
1381
|
+
await UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1382
|
+
projectId: PROJECT_ID,
|
|
1383
|
+
alertId: ALERT_ID,
|
|
1384
|
+
userId: USER_ID,
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
const rulesArg: {
|
|
1388
|
+
notificationFor: { incidentId?: ObjectID; alertId?: ObjectID };
|
|
1389
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
1390
|
+
} = getRules.mock.calls[0][0] as {
|
|
1391
|
+
notificationFor: { incidentId?: ObjectID; alertId?: ObjectID };
|
|
1392
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
1393
|
+
};
|
|
1394
|
+
expect(rulesArg.notificationFor.alertId?.toString()).toBe(
|
|
1395
|
+
ALERT_ID.toString(),
|
|
1396
|
+
);
|
|
1397
|
+
expect(rulesArg.notificationFor.incidentId).toBeUndefined();
|
|
1398
|
+
expect(rulesArg.notificationRuleEventType).toBe(
|
|
1399
|
+
NotificationRuleEventType.Alert,
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
expect(alertChannels).toHaveBeenCalledTimes(1);
|
|
1403
|
+
expect(incidentChannels).not.toHaveBeenCalled();
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
test("only the notified user is invited, with the resolved rules and channels", async () => {
|
|
1407
|
+
const rules: Array<unknown> = [{ _id: "rule-1" }];
|
|
1408
|
+
const channels: Array<unknown> = [{ id: "channel-1", name: "#incidents" }];
|
|
1409
|
+
getRules.mockResolvedValue(rules as never);
|
|
1410
|
+
incidentChannels.mockResolvedValue(channels as never);
|
|
1411
|
+
|
|
1412
|
+
await UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1413
|
+
projectId: PROJECT_ID,
|
|
1414
|
+
incidentId: INCIDENT_ID,
|
|
1415
|
+
userId: USER_ID,
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
expect(inviteUsers).toHaveBeenCalledTimes(1);
|
|
1419
|
+
const inviteArg: {
|
|
1420
|
+
notificationRules: Array<unknown>;
|
|
1421
|
+
projectId: ObjectID;
|
|
1422
|
+
workspaceChannels: Array<unknown>;
|
|
1423
|
+
userIds: Array<ObjectID>;
|
|
1424
|
+
} = inviteUsers.mock.calls[0][0] as {
|
|
1425
|
+
notificationRules: Array<unknown>;
|
|
1426
|
+
projectId: ObjectID;
|
|
1427
|
+
workspaceChannels: Array<unknown>;
|
|
1428
|
+
userIds: Array<ObjectID>;
|
|
1429
|
+
};
|
|
1430
|
+
expect(inviteArg.notificationRules).toBe(rules);
|
|
1431
|
+
expect(inviteArg.workspaceChannels).toBe(channels);
|
|
1432
|
+
expect(inviteArg.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
1433
|
+
expect(inviteArg.userIds).toHaveLength(1);
|
|
1434
|
+
expect(inviteArg.userIds[0]?.toString()).toBe(USER_ID.toString());
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1437
|
+
test("a failing invite is swallowed and logged - it never rejects the caller", async () => {
|
|
1438
|
+
inviteUsers.mockRejectedValue(new Error("slack api 500") as never);
|
|
1439
|
+
|
|
1440
|
+
await expect(
|
|
1441
|
+
UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1442
|
+
projectId: PROJECT_ID,
|
|
1443
|
+
incidentId: INCIDENT_ID,
|
|
1444
|
+
userId: USER_ID,
|
|
1445
|
+
}),
|
|
1446
|
+
).resolves.toBeUndefined();
|
|
1447
|
+
|
|
1448
|
+
await flushMicrotasks();
|
|
1449
|
+
|
|
1450
|
+
expect(loggerError).toHaveBeenCalled();
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
test("a failing channel lookup DOES reject (it is awaited, unlike the invite)", async () => {
|
|
1454
|
+
incidentChannels.mockRejectedValue(
|
|
1455
|
+
new Error("workspace api down") as never,
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
await expect(
|
|
1459
|
+
UserNotificationRuleService.runWorkspaceRulesForOnCallNotification({
|
|
1460
|
+
projectId: PROJECT_ID,
|
|
1461
|
+
incidentId: INCIDENT_ID,
|
|
1462
|
+
userId: USER_ID,
|
|
1463
|
+
}),
|
|
1464
|
+
).rejects.toThrow("workspace api down");
|
|
1465
|
+
|
|
1466
|
+
expect(inviteUsers).not.toHaveBeenCalled();
|
|
1467
|
+
});
|
|
1468
|
+
});
|