@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,1393 @@
|
|
|
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 logger from "../../../Server/Utils/Logger";
|
|
17
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
18
|
+
import UserOnCallLogTimeline from "../../../Models/DatabaseModels/UserOnCallLogTimeline";
|
|
19
|
+
import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
|
|
20
|
+
import UserEmail from "../../../Models/DatabaseModels/UserEmail";
|
|
21
|
+
import UserPush from "../../../Models/DatabaseModels/UserPush";
|
|
22
|
+
import UserSMS from "../../../Models/DatabaseModels/UserSMS";
|
|
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 URL from "../../../Types/API/URL";
|
|
28
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
29
|
+
import EmailMessage from "../../../Types/Email/EmailMessage";
|
|
30
|
+
import SMS from "../../../Types/SMS/SMS";
|
|
31
|
+
import TwilioConfig from "../../../Types/CallAndSMS/TwilioConfig";
|
|
32
|
+
import NotificationRuleType from "../../../Types/NotificationRule/NotificationRuleType";
|
|
33
|
+
import PushDeviceType from "../../../Types/PushNotification/PushDeviceType";
|
|
34
|
+
import UserNotificationEventType from "../../../Types/UserNotification/UserNotificationEventType";
|
|
35
|
+
import UserNotificationStatus from "../../../Types/UserNotification/UserNotificationStatus";
|
|
36
|
+
import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* Phase 1 §2.1 split `executeNotificationRuleItem` in two. The public method
|
|
40
|
+
* kept its signature and now does only what a rule ID implies — claim the
|
|
41
|
+
* on-call log, load the rule, hand both to the delivery half — and everything
|
|
42
|
+
* from the Twilio-config lookup downwards moved into a private
|
|
43
|
+
* `deliverNotificationForRule(rule, options)`.
|
|
44
|
+
*
|
|
45
|
+
* The extraction exists so the on-call FALLBACK can reuse the delivery half
|
|
46
|
+
* with a UserNotificationRule it assembled in memory and never saved: there is
|
|
47
|
+
* no rule row to claim and no id to look up, so the first half is meaningless
|
|
48
|
+
* to it while the second half is exactly what it needs. That makes the SEAM
|
|
49
|
+
* between the two halves — not the seven channel blocks, which
|
|
50
|
+
* NotificationChannelEventCoverage.test.ts owns — the thing worth pinning, and
|
|
51
|
+
* this file pins five properties of it:
|
|
52
|
+
*
|
|
53
|
+
* 1. THE CLAIM IS STILL FIRST. The atomic claim (audit F7) is what stops two
|
|
54
|
+
* overlapping cron ticks double-paging one responder. A refactor that let
|
|
55
|
+
* the rule load drift above it, or that read `false` as "carry on", brings
|
|
56
|
+
* duplicate pages straight back — so a lost claim is asserted to stop the
|
|
57
|
+
* method before `findOneById` is even called, not merely before the send.
|
|
58
|
+
*
|
|
59
|
+
* 2. A MISSING RULE STILL THROWS. `BadDataException("Notification rule item
|
|
60
|
+
* not found.")` is what ExecutePendingExecutions turns into an `Error`
|
|
61
|
+
* log. Swallowing it during the split would leave the log stuck Executing
|
|
62
|
+
* forever.
|
|
63
|
+
*
|
|
64
|
+
* 3. THE SELECT SURVIVED INTACT. Every channel gate reads
|
|
65
|
+
* `<method>?.isVerified`, and an unselected column arrives as `undefined`.
|
|
66
|
+
* Dropping `isVerified` from this select therefore does NOT page
|
|
67
|
+
* unverified methods — it silences every VERIFIED one and writes "not
|
|
68
|
+
* verified" rows about perfectly verified phones. `userWebhook`'s
|
|
69
|
+
* `webhookUrl`/`name`/`secret` matter for the opposite reason: `secret`
|
|
70
|
+
* signs the outgoing request. The shape is asserted field by field.
|
|
71
|
+
*
|
|
72
|
+
* 4. THE RULE CROSSES THE SEAM UNCHANGED. The object `findOneById` returned
|
|
73
|
+
* and the caller's own options bag are handed to the delivery half by
|
|
74
|
+
* reference — no copy, no reshaping, nothing dropped on the way.
|
|
75
|
+
*
|
|
76
|
+
* 5. THE DELIVERY HALF STANDS ALONE. It can be driven directly with an
|
|
77
|
+
* UNSAVED, id-less rule and still writes a timeline row and dispatches.
|
|
78
|
+
* This is the regression guard for blocker B1: while
|
|
79
|
+
* `UserOnCallLogTimeline.userNotificationRuleId` was NOT NULL, every
|
|
80
|
+
* fallback delivery threw at the first `create()` — before a single page
|
|
81
|
+
* left the process — because an unsaved rule has no id to put there.
|
|
82
|
+
*
|
|
83
|
+
* Plus the one value the split had to keep hoisted: `projectTwilioConfig` is
|
|
84
|
+
* resolved ONCE, inside the delivery half, from `options.projectId`, and handed
|
|
85
|
+
* to the paid senders. Resolving it per channel would multiply a project-config
|
|
86
|
+
* read by the number of channels; resolving it in the public half would leave
|
|
87
|
+
* the fallback sending on the GLOBAL Twilio account.
|
|
88
|
+
*
|
|
89
|
+
* No database is touched. Every sender, every template generator and every
|
|
90
|
+
* persistence call is a `jest.spyOn` stub, and the private delivery half is
|
|
91
|
+
* reached through a cast, exactly as the neighbouring characterisation tests
|
|
92
|
+
* reach protected hooks.
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
96
|
+
"11111111-1111-4111-8111-111111111111",
|
|
97
|
+
);
|
|
98
|
+
const RULE_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
|
|
99
|
+
const LOG_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
|
|
100
|
+
const USER_ID: ObjectID = new ObjectID("44444444-4444-4444-8444-444444444444");
|
|
101
|
+
const INCIDENT_ID: ObjectID = new ObjectID(
|
|
102
|
+
"55555555-5555-4555-8555-555555555555",
|
|
103
|
+
);
|
|
104
|
+
const TEAM_ID: ObjectID = new ObjectID("66666666-6666-4666-8666-666666666666");
|
|
105
|
+
const POLICY_ID: ObjectID = new ObjectID(
|
|
106
|
+
"77777777-7777-4777-8777-777777777777",
|
|
107
|
+
);
|
|
108
|
+
const ESCALATION_RULE_ID: ObjectID = new ObjectID(
|
|
109
|
+
"88888888-8888-4888-8888-888888888888",
|
|
110
|
+
);
|
|
111
|
+
const EXECUTION_LOG_TIMELINE_ID: ObjectID = new ObjectID(
|
|
112
|
+
"99999999-9999-4999-8999-999999999999",
|
|
113
|
+
);
|
|
114
|
+
const TIMELINE_ID: ObjectID = new ObjectID(
|
|
115
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
116
|
+
);
|
|
117
|
+
const EMAIL_METHOD_ID: ObjectID = new ObjectID(
|
|
118
|
+
"bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
|
119
|
+
);
|
|
120
|
+
const SMS_METHOD_ID: ObjectID = new ObjectID(
|
|
121
|
+
"cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
|
122
|
+
);
|
|
123
|
+
const PUSH_METHOD_ID: ObjectID = new ObjectID(
|
|
124
|
+
"dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const RESPONDER_EMAIL: string = "responder@company.com";
|
|
128
|
+
const RESPONDER_PHONE: string = "+11234567890";
|
|
129
|
+
|
|
130
|
+
/* The options bag both halves share, taken from the public signature. */
|
|
131
|
+
type ExecuteOptions = Parameters<
|
|
132
|
+
typeof UserNotificationRuleService.executeNotificationRuleItem
|
|
133
|
+
>[1];
|
|
134
|
+
|
|
135
|
+
/* The `select` / `props` bundle handed to findOneById. */
|
|
136
|
+
interface FindOneByIdArg {
|
|
137
|
+
id: ObjectID;
|
|
138
|
+
select: JSONObject;
|
|
139
|
+
props: { isRoot: boolean };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/*
|
|
143
|
+
* `deliverNotificationForRule` is private, so TypeScript will not let a test
|
|
144
|
+
* name it on the service. It is a perfectly ordinary prototype method at
|
|
145
|
+
* runtime, and the fallback's whole design rests on it being callable with a
|
|
146
|
+
* rule that was never persisted — so it is reached here through a structural
|
|
147
|
+
* cast, the same trick the neighbouring files use for protected hooks.
|
|
148
|
+
*
|
|
149
|
+
* It returns whether a page was actually handed to a sender. That boolean is
|
|
150
|
+
* NOT decoration: executeFallbackNotification reads it to decide which channel
|
|
151
|
+
* names to tell the operator the responder was reached on.
|
|
152
|
+
*/
|
|
153
|
+
interface DeliveryHalf {
|
|
154
|
+
deliverNotificationForRule: (
|
|
155
|
+
notificationRuleItem: UserNotificationRule,
|
|
156
|
+
options: ExecuteOptions,
|
|
157
|
+
) => Promise<boolean>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function deliveryHalf(): DeliveryHalf {
|
|
161
|
+
return UserNotificationRuleService as unknown as DeliveryHalf;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/*
|
|
165
|
+
* The service mutates ONE UserOnCallLogTimeline instance per delivery call and
|
|
166
|
+
* hands that same instance to create(), so mock.calls all alias its final
|
|
167
|
+
* state. Snapshot the fields at call time instead; the raw instances are kept
|
|
168
|
+
* separately, because "did two delivery calls build two distinct rows" is a
|
|
169
|
+
* question only identity can answer.
|
|
170
|
+
*/
|
|
171
|
+
interface TimelineRow {
|
|
172
|
+
status: UserNotificationStatus | undefined;
|
|
173
|
+
statusMessage: string | undefined;
|
|
174
|
+
userId: ObjectID | undefined;
|
|
175
|
+
userNotificationRuleId: ObjectID | undefined;
|
|
176
|
+
userNotificationLogId: ObjectID | undefined;
|
|
177
|
+
projectId: ObjectID | undefined;
|
|
178
|
+
userNotificationEventType: UserNotificationEventType | undefined;
|
|
179
|
+
userEmailId: ObjectID | undefined;
|
|
180
|
+
userSmsId: ObjectID | undefined;
|
|
181
|
+
userPushId: ObjectID | undefined;
|
|
182
|
+
triggeredByIncidentId: ObjectID | undefined;
|
|
183
|
+
onCallDutyPolicyId: ObjectID | undefined;
|
|
184
|
+
onCallDutyPolicyEscalationRuleId: ObjectID | undefined;
|
|
185
|
+
userBelongsToTeamId: ObjectID | undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function executeOptions(
|
|
189
|
+
overrides: Partial<ExecuteOptions> = {},
|
|
190
|
+
): ExecuteOptions {
|
|
191
|
+
return {
|
|
192
|
+
projectId: PROJECT_ID,
|
|
193
|
+
userNotificationEventType: UserNotificationEventType.IncidentCreated,
|
|
194
|
+
triggeredByIncidentId: INCIDENT_ID,
|
|
195
|
+
onCallPolicyId: POLICY_ID,
|
|
196
|
+
onCallPolicyEscalationRuleId: ESCALATION_RULE_ID,
|
|
197
|
+
userBelongsToTeamId: TEAM_ID,
|
|
198
|
+
onCallDutyPolicyExecutionLogTimelineId: EXECUTION_LOG_TIMELINE_ID,
|
|
199
|
+
userNotificationLogId: LOG_ID,
|
|
200
|
+
...overrides,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/*
|
|
205
|
+
* A rule as findOneById hands it back: hydrated, and carrying the `_id` of the
|
|
206
|
+
* row it was read from.
|
|
207
|
+
*/
|
|
208
|
+
function loadedRule(channels: JSONObject = {}): UserNotificationRule {
|
|
209
|
+
return {
|
|
210
|
+
id: RULE_ID,
|
|
211
|
+
_id: RULE_ID.toString(),
|
|
212
|
+
userId: USER_ID,
|
|
213
|
+
...channels,
|
|
214
|
+
} as unknown as UserNotificationRule;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
* A rule as the FALLBACK builds it: a real model instance with no `_id`, so
|
|
219
|
+
* `.id` is genuinely null, assembled from the user's verified methods and
|
|
220
|
+
* never saved. Real model classes are used rather than object literals
|
|
221
|
+
* precisely because `id` is a getter over `_id` — a literal would fake the one
|
|
222
|
+
* property this half of the file is about.
|
|
223
|
+
*/
|
|
224
|
+
function unsavedFallbackRule(): UserNotificationRule {
|
|
225
|
+
const rule: UserNotificationRule = new UserNotificationRule();
|
|
226
|
+
rule.projectId = PROJECT_ID;
|
|
227
|
+
rule.userId = USER_ID;
|
|
228
|
+
rule.ruleType = NotificationRuleType.ON_CALL_EXECUTED_INCIDENT;
|
|
229
|
+
rule.notifyAfterMinutes = 0;
|
|
230
|
+
|
|
231
|
+
return rule;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function verifiedUserEmail(): UserEmail {
|
|
235
|
+
const userEmail: UserEmail = new UserEmail();
|
|
236
|
+
userEmail._id = EMAIL_METHOD_ID.toString();
|
|
237
|
+
userEmail.email = new Email(RESPONDER_EMAIL);
|
|
238
|
+
userEmail.isVerified = true;
|
|
239
|
+
|
|
240
|
+
return userEmail;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function verifiedUserSms(): UserSMS {
|
|
244
|
+
const userSms: UserSMS = new UserSMS();
|
|
245
|
+
userSms._id = SMS_METHOD_ID.toString();
|
|
246
|
+
userSms.phone = new Phone(RESPONDER_PHONE);
|
|
247
|
+
userSms.isVerified = true;
|
|
248
|
+
|
|
249
|
+
return userSms;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function verifiedUserPush(): UserPush {
|
|
253
|
+
const userPush: UserPush = new UserPush();
|
|
254
|
+
userPush._id = PUSH_METHOD_ID.toString();
|
|
255
|
+
userPush.deviceToken = "device-token";
|
|
256
|
+
userPush.deviceType = PushDeviceType.iOS;
|
|
257
|
+
userPush.isVerified = true;
|
|
258
|
+
|
|
259
|
+
return userPush;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function fakeIncident(): Incident {
|
|
263
|
+
return {
|
|
264
|
+
id: INCIDENT_ID,
|
|
265
|
+
projectId: PROJECT_ID,
|
|
266
|
+
title: "Checkout is down",
|
|
267
|
+
description: "Checkout returns 500 for every request.",
|
|
268
|
+
incidentNumber: 42,
|
|
269
|
+
incidentNumberWithPrefix: "INC-42",
|
|
270
|
+
} as unknown as Incident;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function fakeTwilioConfig(): TwilioConfig {
|
|
274
|
+
return {
|
|
275
|
+
accountSid: "AC-project-account",
|
|
276
|
+
authToken: "project-auth-token",
|
|
277
|
+
primaryPhoneNumber: new Phone("+15550001111"),
|
|
278
|
+
secondaryPhoneNumbers: [],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Every stub the two halves touch, so no test can reach a real service. */
|
|
283
|
+
interface Spies {
|
|
284
|
+
claim: jest.SpyInstance;
|
|
285
|
+
findRule: jest.SpyInstance;
|
|
286
|
+
createRule: jest.SpyInstance;
|
|
287
|
+
twilio: jest.SpyInstance;
|
|
288
|
+
timelineCreate: jest.SpyInstance;
|
|
289
|
+
timelineUpdate: jest.SpyInstance;
|
|
290
|
+
incidentFind: jest.SpyInstance;
|
|
291
|
+
incidentLink: jest.SpyInstance;
|
|
292
|
+
alertFind: jest.SpyInstance;
|
|
293
|
+
alertEpisodeFind: jest.SpyInstance;
|
|
294
|
+
incidentEpisodeFind: jest.SpyInstance;
|
|
295
|
+
mail: jest.SpyInstance;
|
|
296
|
+
sms: jest.SpyInstance;
|
|
297
|
+
call: jest.SpyInstance;
|
|
298
|
+
whatsApp: jest.SpyInstance;
|
|
299
|
+
telegram: jest.SpyInstance;
|
|
300
|
+
webhook: jest.SpyInstance;
|
|
301
|
+
push: jest.SpyInstance;
|
|
302
|
+
emailTemplate: jest.SpyInstance;
|
|
303
|
+
smsTemplate: jest.SpyInstance;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
describe("UserNotificationRuleService - the deliverNotificationForRule extraction", () => {
|
|
307
|
+
let spies: Spies;
|
|
308
|
+
let timelineRows: Array<TimelineRow>;
|
|
309
|
+
let timelineInstances: Array<UserOnCallLogTimeline>;
|
|
310
|
+
|
|
311
|
+
beforeEach(() => {
|
|
312
|
+
timelineRows = [];
|
|
313
|
+
timelineInstances = [];
|
|
314
|
+
|
|
315
|
+
jest.spyOn(logger, "error").mockImplementation((): void => {
|
|
316
|
+
return undefined;
|
|
317
|
+
});
|
|
318
|
+
jest.spyOn(logger, "warn").mockImplementation((): void => {
|
|
319
|
+
return undefined;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const timelineCreate: jest.SpyInstance = jest.spyOn(
|
|
323
|
+
UserOnCallLogTimelineService,
|
|
324
|
+
"create",
|
|
325
|
+
);
|
|
326
|
+
timelineCreate.mockImplementation(
|
|
327
|
+
(createBy: unknown): Promise<UserOnCallLogTimeline> => {
|
|
328
|
+
const data: UserOnCallLogTimeline = (
|
|
329
|
+
createBy as { data: UserOnCallLogTimeline }
|
|
330
|
+
).data;
|
|
331
|
+
|
|
332
|
+
timelineInstances.push(data);
|
|
333
|
+
timelineRows.push({
|
|
334
|
+
status: data.status,
|
|
335
|
+
statusMessage: data.statusMessage,
|
|
336
|
+
userId: data.userId,
|
|
337
|
+
userNotificationRuleId: data.userNotificationRuleId,
|
|
338
|
+
userNotificationLogId: data.userNotificationLogId,
|
|
339
|
+
projectId: data.projectId,
|
|
340
|
+
userNotificationEventType: data.userNotificationEventType,
|
|
341
|
+
userEmailId: data.userEmailId,
|
|
342
|
+
userSmsId: data.userSmsId,
|
|
343
|
+
userPushId: data.userPushId,
|
|
344
|
+
triggeredByIncidentId: data.triggeredByIncidentId,
|
|
345
|
+
onCallDutyPolicyId: data.onCallDutyPolicyId,
|
|
346
|
+
onCallDutyPolicyEscalationRuleId:
|
|
347
|
+
data.onCallDutyPolicyEscalationRuleId,
|
|
348
|
+
userBelongsToTeamId: data.userBelongsToTeamId,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
return Promise.resolve({
|
|
352
|
+
id: TIMELINE_ID,
|
|
353
|
+
} as unknown as UserOnCallLogTimeline);
|
|
354
|
+
},
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
spies = {
|
|
358
|
+
claim: jest
|
|
359
|
+
.spyOn(UserOnCallLogService, "claimNotificationRuleExecution")
|
|
360
|
+
.mockResolvedValue(true as never),
|
|
361
|
+
findRule: jest
|
|
362
|
+
.spyOn(UserNotificationRuleService, "findOneById")
|
|
363
|
+
.mockResolvedValue(loadedRule() as never),
|
|
364
|
+
createRule: jest
|
|
365
|
+
.spyOn(UserNotificationRuleService, "create")
|
|
366
|
+
.mockResolvedValue(loadedRule() as never),
|
|
367
|
+
twilio: jest
|
|
368
|
+
.spyOn(ProjectCallSMSConfigService, "getProjectDefaultTwilioConfig")
|
|
369
|
+
.mockResolvedValue(undefined as never),
|
|
370
|
+
timelineCreate: timelineCreate,
|
|
371
|
+
timelineUpdate: jest
|
|
372
|
+
.spyOn(UserOnCallLogTimelineService, "updateOneById")
|
|
373
|
+
.mockResolvedValue(undefined as never),
|
|
374
|
+
incidentFind: jest
|
|
375
|
+
.spyOn(IncidentService, "findOneById")
|
|
376
|
+
.mockResolvedValue(fakeIncident() as never),
|
|
377
|
+
incidentLink: jest
|
|
378
|
+
.spyOn(IncidentService, "getIncidentLinkInDashboard")
|
|
379
|
+
.mockResolvedValue(
|
|
380
|
+
URL.fromString("https://dashboard.example.com/incident") as never,
|
|
381
|
+
),
|
|
382
|
+
alertFind: jest
|
|
383
|
+
.spyOn(AlertService, "findOneById")
|
|
384
|
+
.mockResolvedValue(null as never),
|
|
385
|
+
alertEpisodeFind: jest
|
|
386
|
+
.spyOn(AlertEpisodeService, "findOneById")
|
|
387
|
+
.mockResolvedValue(null as never),
|
|
388
|
+
incidentEpisodeFind: jest
|
|
389
|
+
.spyOn(IncidentEpisodeService, "findOneById")
|
|
390
|
+
.mockResolvedValue(null as never),
|
|
391
|
+
mail: jest
|
|
392
|
+
.spyOn(MailService, "sendMail")
|
|
393
|
+
.mockResolvedValue(undefined as never),
|
|
394
|
+
sms: jest
|
|
395
|
+
.spyOn(SmsService, "sendSms")
|
|
396
|
+
.mockResolvedValue(undefined as never),
|
|
397
|
+
call: jest
|
|
398
|
+
.spyOn(CallService, "makeCall")
|
|
399
|
+
.mockResolvedValue(undefined as never),
|
|
400
|
+
whatsApp: jest
|
|
401
|
+
.spyOn(WhatsAppService, "sendWhatsAppMessage")
|
|
402
|
+
.mockResolvedValue(undefined as never),
|
|
403
|
+
telegram: jest
|
|
404
|
+
.spyOn(TelegramService, "sendTelegramMessage")
|
|
405
|
+
.mockResolvedValue(undefined as never),
|
|
406
|
+
webhook: jest
|
|
407
|
+
.spyOn(WebhookService, "sendWebhook")
|
|
408
|
+
.mockResolvedValue(undefined as never),
|
|
409
|
+
push: jest
|
|
410
|
+
.spyOn(PushNotificationService, "sendPushNotification")
|
|
411
|
+
.mockResolvedValue(undefined as never),
|
|
412
|
+
emailTemplate: jest
|
|
413
|
+
.spyOn(
|
|
414
|
+
UserNotificationRuleService,
|
|
415
|
+
"generateEmailTemplateForIncidentCreated",
|
|
416
|
+
)
|
|
417
|
+
.mockResolvedValue({} as EmailMessage as never),
|
|
418
|
+
smsTemplate: jest
|
|
419
|
+
.spyOn(
|
|
420
|
+
UserNotificationRuleService,
|
|
421
|
+
"generateSmsTemplateForIncidentCreated",
|
|
422
|
+
)
|
|
423
|
+
.mockResolvedValue({} as SMS as never),
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
afterEach(() => {
|
|
428
|
+
jest.restoreAllMocks();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
function everySender(): Array<jest.SpyInstance> {
|
|
432
|
+
return [
|
|
433
|
+
spies.mail,
|
|
434
|
+
spies.sms,
|
|
435
|
+
spies.call,
|
|
436
|
+
spies.whatsApp,
|
|
437
|
+
spies.telegram,
|
|
438
|
+
spies.webhook,
|
|
439
|
+
spies.push,
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/*
|
|
444
|
+
* Replace the delivery half so the public half can be observed on its own.
|
|
445
|
+
* Installed per test rather than in beforeEach, because the sections below
|
|
446
|
+
* that drive the delivery half directly need the REAL implementation.
|
|
447
|
+
*/
|
|
448
|
+
function stubDeliveryHalf(): jest.SpyInstance {
|
|
449
|
+
return jest
|
|
450
|
+
.spyOn(deliveryHalf(), "deliverNotificationForRule")
|
|
451
|
+
.mockResolvedValue(true as never);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/* The second argument SmsService.sendSms was handed. */
|
|
455
|
+
function sentSmsOptions(): {
|
|
456
|
+
customTwilioConfig: TwilioConfig | undefined;
|
|
457
|
+
} {
|
|
458
|
+
return spies.sms.mock.calls[0]![1] as {
|
|
459
|
+
customTwilioConfig: TwilioConfig | undefined;
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* ----------------------------------------------------------------------- *
|
|
465
|
+
* (A) The claim still runs, and still runs FIRST.
|
|
466
|
+
* -----------------------------------------------------------------------
|
|
467
|
+
*/
|
|
468
|
+
|
|
469
|
+
describe("the public half still claims before it loads anything", () => {
|
|
470
|
+
test("a lost claim returns before findOneById is called at all", async () => {
|
|
471
|
+
spies.claim.mockResolvedValue(false as never);
|
|
472
|
+
|
|
473
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
474
|
+
RULE_ID,
|
|
475
|
+
executeOptions(),
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
expect(spies.claim).toHaveBeenCalledTimes(1);
|
|
479
|
+
/*
|
|
480
|
+
* The point of F7. Short-circuiting merely before the SEND would still
|
|
481
|
+
* let two ticks race on the read; the gate has to be above the read.
|
|
482
|
+
*/
|
|
483
|
+
expect(spies.findRule).not.toHaveBeenCalled();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("a lost claim never reaches the delivery half", async () => {
|
|
487
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
488
|
+
spies.claim.mockResolvedValue(false as never);
|
|
489
|
+
|
|
490
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
491
|
+
RULE_ID,
|
|
492
|
+
executeOptions(),
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
expect(deliver).not.toHaveBeenCalled();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("a lost claim sends nothing and writes no timeline row", async () => {
|
|
499
|
+
spies.claim.mockResolvedValue(false as never);
|
|
500
|
+
spies.findRule.mockResolvedValue(
|
|
501
|
+
loadedRule({
|
|
502
|
+
userEmail: verifiedUserEmail(),
|
|
503
|
+
} as unknown as JSONObject) as never,
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
507
|
+
RULE_ID,
|
|
508
|
+
executeOptions(),
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
for (const sender of everySender()) {
|
|
512
|
+
expect(sender).not.toHaveBeenCalled();
|
|
513
|
+
}
|
|
514
|
+
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
515
|
+
expect(spies.timelineUpdate).not.toHaveBeenCalled();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("a lost claim skips the Twilio config and the incident lookup too", async () => {
|
|
519
|
+
spies.claim.mockResolvedValue(false as never);
|
|
520
|
+
|
|
521
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
522
|
+
RULE_ID,
|
|
523
|
+
executeOptions(),
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
expect(spies.twilio).not.toHaveBeenCalled();
|
|
527
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
test("a lost claim RESOLVES - it is a normal outcome, not bad data", async () => {
|
|
531
|
+
spies.claim.mockResolvedValue(false as never);
|
|
532
|
+
|
|
533
|
+
/*
|
|
534
|
+
* ExecutePendingExecutions marks a log Error on BadDataException. A
|
|
535
|
+
* duplicate suppressed by the claim is not an error, so this path must
|
|
536
|
+
* resolve quietly or every suppressed duplicate burns its on-call log.
|
|
537
|
+
*/
|
|
538
|
+
await expect(
|
|
539
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
540
|
+
RULE_ID,
|
|
541
|
+
executeOptions(),
|
|
542
|
+
),
|
|
543
|
+
).resolves.toBeUndefined();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
test("the claim names this on-call log and this rule", async () => {
|
|
547
|
+
spies.claim.mockResolvedValue(false as never);
|
|
548
|
+
|
|
549
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
550
|
+
RULE_ID,
|
|
551
|
+
executeOptions({ userNotificationLogId: LOG_ID }),
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
const claimArg: {
|
|
555
|
+
userOnCallLogId: ObjectID;
|
|
556
|
+
userNotificationRuleId: ObjectID;
|
|
557
|
+
} = spies.claim.mock.calls[0]![0] as {
|
|
558
|
+
userOnCallLogId: ObjectID;
|
|
559
|
+
userNotificationRuleId: ObjectID;
|
|
560
|
+
};
|
|
561
|
+
expect(claimArg.userOnCallLogId.toString()).toBe(LOG_ID.toString());
|
|
562
|
+
expect(claimArg.userNotificationRuleId.toString()).toBe(
|
|
563
|
+
RULE_ID.toString(),
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test("the claim strictly PRECEDES the rule load, which precedes the delivery half", async () => {
|
|
568
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
569
|
+
|
|
570
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
571
|
+
RULE_ID,
|
|
572
|
+
executeOptions(),
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
expect(spies.claim.mock.invocationCallOrder[0]).toBeLessThan(
|
|
576
|
+
spies.findRule.mock.invocationCallOrder[0] as number,
|
|
577
|
+
);
|
|
578
|
+
expect(spies.findRule.mock.invocationCallOrder[0]).toBeLessThan(
|
|
579
|
+
deliver.mock.invocationCallOrder[0] as number,
|
|
580
|
+
);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test("a claim that rejects propagates and nothing is loaded or delivered", async () => {
|
|
584
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
585
|
+
spies.claim.mockRejectedValue(new Error("db connection reset") as never);
|
|
586
|
+
|
|
587
|
+
await expect(
|
|
588
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
589
|
+
RULE_ID,
|
|
590
|
+
executeOptions(),
|
|
591
|
+
),
|
|
592
|
+
).rejects.toThrow("db connection reset");
|
|
593
|
+
|
|
594
|
+
expect(spies.findRule).not.toHaveBeenCalled();
|
|
595
|
+
expect(deliver).not.toHaveBeenCalled();
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test("a held claim lets execution proceed, and the rule is read exactly once", async () => {
|
|
599
|
+
stubDeliveryHalf();
|
|
600
|
+
|
|
601
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
602
|
+
RULE_ID,
|
|
603
|
+
executeOptions(),
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
expect(spies.claim).toHaveBeenCalledTimes(1);
|
|
607
|
+
expect(spies.findRule).toHaveBeenCalledTimes(1);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
/*
|
|
612
|
+
* ----------------------------------------------------------------------- *
|
|
613
|
+
* (B) A rule id that resolves to nothing.
|
|
614
|
+
* -----------------------------------------------------------------------
|
|
615
|
+
*/
|
|
616
|
+
|
|
617
|
+
describe("a rule id that resolves to nothing", () => {
|
|
618
|
+
test('still throws BadDataException("Notification rule item not found.")', async () => {
|
|
619
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
620
|
+
|
|
621
|
+
await expect(
|
|
622
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
623
|
+
RULE_ID,
|
|
624
|
+
executeOptions(),
|
|
625
|
+
),
|
|
626
|
+
).rejects.toThrow(BadDataException);
|
|
627
|
+
|
|
628
|
+
await expect(
|
|
629
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
630
|
+
RULE_ID,
|
|
631
|
+
executeOptions(),
|
|
632
|
+
),
|
|
633
|
+
).rejects.toThrow("Notification rule item not found.");
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
test("the delivery half is never entered on a missing rule", async () => {
|
|
637
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
638
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
639
|
+
|
|
640
|
+
await expect(
|
|
641
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
642
|
+
RULE_ID,
|
|
643
|
+
executeOptions(),
|
|
644
|
+
),
|
|
645
|
+
).rejects.toThrow(BadDataException);
|
|
646
|
+
|
|
647
|
+
expect(deliver).not.toHaveBeenCalled();
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test("the throw beats the Twilio config, the incident lookup and any timeline row", async () => {
|
|
651
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
652
|
+
|
|
653
|
+
await expect(
|
|
654
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
655
|
+
RULE_ID,
|
|
656
|
+
executeOptions(),
|
|
657
|
+
),
|
|
658
|
+
).rejects.toThrow(BadDataException);
|
|
659
|
+
|
|
660
|
+
expect(spies.twilio).not.toHaveBeenCalled();
|
|
661
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
662
|
+
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
663
|
+
for (const sender of everySender()) {
|
|
664
|
+
expect(sender).not.toHaveBeenCalled();
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
test("the claim has ALREADY been consumed when the rule turns out to be missing", async () => {
|
|
669
|
+
spies.findRule.mockResolvedValue(null as never);
|
|
670
|
+
|
|
671
|
+
await expect(
|
|
672
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
673
|
+
RULE_ID,
|
|
674
|
+
executeOptions(),
|
|
675
|
+
),
|
|
676
|
+
).rejects.toThrow(BadDataException);
|
|
677
|
+
|
|
678
|
+
/*
|
|
679
|
+
* Pinned as current behaviour, unchanged by the split: the claim is
|
|
680
|
+
* written before the rule is read, so a rule deleted between scheduling
|
|
681
|
+
* and execution burns its claim and no later tick retries it. Deliberate
|
|
682
|
+
* (a missing rule is permanent), but it means the claim is not released.
|
|
683
|
+
*/
|
|
684
|
+
expect(spies.claim).toHaveBeenCalledTimes(1);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
/*
|
|
689
|
+
* ----------------------------------------------------------------------- *
|
|
690
|
+
* (C) The select the public half still passes.
|
|
691
|
+
* -----------------------------------------------------------------------
|
|
692
|
+
*/
|
|
693
|
+
|
|
694
|
+
describe("the select handed to findOneById", () => {
|
|
695
|
+
async function captureFindArg(): Promise<FindOneByIdArg> {
|
|
696
|
+
stubDeliveryHalf();
|
|
697
|
+
|
|
698
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
699
|
+
RULE_ID,
|
|
700
|
+
executeOptions(),
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
return spies.findRule.mock.calls[0]![0] as FindOneByIdArg;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
test("targets the requested rule id, as root", async () => {
|
|
707
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
708
|
+
|
|
709
|
+
expect(arg.id.toString()).toBe(RULE_ID.toString());
|
|
710
|
+
expect(arg.props.isRoot).toBe(true);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test("is EXACTLY this shape - a field lost in the split changes who gets paged", async () => {
|
|
714
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
715
|
+
|
|
716
|
+
/*
|
|
717
|
+
* WHY EACH RELATION ALSO ASKS FOR ITS OWN userId, WHICH NO SENDER READS.
|
|
718
|
+
*
|
|
719
|
+
* A rule is a pair of columns that nothing in the ORM ever compares: its
|
|
720
|
+
* `userId` decides WHOSE page this is, and the method relation decides
|
|
721
|
+
* WHERE that page is delivered. A row where those two name different
|
|
722
|
+
* people routes one person's pages to another person's address, and it is
|
|
723
|
+
* invisible from both ends - the rules page still lists a rule and the
|
|
724
|
+
* on-call log still records a delivered notification.
|
|
725
|
+
*
|
|
726
|
+
* The write-side guards refuse to SAVE such a row. This select is what
|
|
727
|
+
* lets executeNotificationRuleItem refuse to ACT ON one that exists
|
|
728
|
+
* anyway: written before those guards landed, written by internal code
|
|
729
|
+
* running as root, or written down a path a future change forgets to
|
|
730
|
+
* route through them. getNotificationMethodsNotOwnedByRuleOwner compares
|
|
731
|
+
* the loaded relation's userId against the rule's, and it can only report
|
|
732
|
+
* a mismatch it can SEE - an unselected column arrives as `undefined` and
|
|
733
|
+
* is deliberately read as "no evidence", so dropping any one of these
|
|
734
|
+
* seven silently disables the backstop for that channel.
|
|
735
|
+
*
|
|
736
|
+
* Kept as an exact-shape assertion on purpose. This test caught the
|
|
737
|
+
* column being added, which is the whole argument for not relaxing it
|
|
738
|
+
* into a partial match: the select is a contract about who gets paged,
|
|
739
|
+
* and every future edit to it should have to come through here.
|
|
740
|
+
*/
|
|
741
|
+
expect(arg.select).toEqual({
|
|
742
|
+
_id: true,
|
|
743
|
+
userId: true,
|
|
744
|
+
userCall: {
|
|
745
|
+
phone: true,
|
|
746
|
+
isVerified: true,
|
|
747
|
+
userId: true,
|
|
748
|
+
},
|
|
749
|
+
userSms: {
|
|
750
|
+
phone: true,
|
|
751
|
+
isVerified: true,
|
|
752
|
+
userId: true,
|
|
753
|
+
},
|
|
754
|
+
userWhatsApp: {
|
|
755
|
+
phone: true,
|
|
756
|
+
isVerified: true,
|
|
757
|
+
userId: true,
|
|
758
|
+
},
|
|
759
|
+
userTelegram: {
|
|
760
|
+
telegramChatId: true,
|
|
761
|
+
telegramUserHandle: true,
|
|
762
|
+
isVerified: true,
|
|
763
|
+
userId: true,
|
|
764
|
+
},
|
|
765
|
+
userWebhook: {
|
|
766
|
+
webhookUrl: true,
|
|
767
|
+
name: true,
|
|
768
|
+
secret: true,
|
|
769
|
+
userId: true,
|
|
770
|
+
},
|
|
771
|
+
userEmail: {
|
|
772
|
+
email: true,
|
|
773
|
+
isVerified: true,
|
|
774
|
+
userId: true,
|
|
775
|
+
},
|
|
776
|
+
userPush: {
|
|
777
|
+
deviceToken: true,
|
|
778
|
+
deviceType: true,
|
|
779
|
+
isVerified: true,
|
|
780
|
+
userId: true,
|
|
781
|
+
},
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test.each<[string]>([
|
|
786
|
+
["userCall"],
|
|
787
|
+
["userSms"],
|
|
788
|
+
["userWhatsApp"],
|
|
789
|
+
["userTelegram"],
|
|
790
|
+
["userEmail"],
|
|
791
|
+
["userPush"],
|
|
792
|
+
])(
|
|
793
|
+
"%s requests isVerified - every user-contactable channel is verification-gated",
|
|
794
|
+
async (channel: string) => {
|
|
795
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
796
|
+
const selected: JSONObject = arg.select[channel] as JSONObject;
|
|
797
|
+
|
|
798
|
+
expect(selected).toBeDefined();
|
|
799
|
+
expect(selected["isVerified"]).toBe(true);
|
|
800
|
+
},
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
test.each<[string, string]>([
|
|
804
|
+
["userCall", "phone"],
|
|
805
|
+
["userSms", "phone"],
|
|
806
|
+
["userWhatsApp", "phone"],
|
|
807
|
+
["userTelegram", "telegramChatId"],
|
|
808
|
+
["userEmail", "email"],
|
|
809
|
+
["userPush", "deviceToken"],
|
|
810
|
+
])(
|
|
811
|
+
"%s also requests its address column (%s), which the send gate reads alongside isVerified",
|
|
812
|
+
async (channel: string, addressColumn: string) => {
|
|
813
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
814
|
+
const selected: JSONObject = arg.select[channel] as JSONObject;
|
|
815
|
+
|
|
816
|
+
expect(selected[addressColumn]).toBe(true);
|
|
817
|
+
},
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
test("userWebhook requests webhookUrl, name and secret - and no isVerified", async () => {
|
|
821
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
822
|
+
const webhookSelect: JSONObject = arg.select["userWebhook"] as JSONObject;
|
|
823
|
+
|
|
824
|
+
/*
|
|
825
|
+
* Webhooks are the one user-contactable channel with no verification
|
|
826
|
+
* concept at all (UserWebhook has no isVerified column), so the delivery
|
|
827
|
+
* gate is `webhookUrl` alone. `secret` is not cosmetic: it signs the
|
|
828
|
+
* outgoing request, so an unselected secret ships UNSIGNED webhooks.
|
|
829
|
+
*
|
|
830
|
+
* `userId` is not read by the webhook sender either. It is the
|
|
831
|
+
* defence-in-depth column described on the exact-shape assertion above -
|
|
832
|
+
* the one the pre-delivery ownership check compares against the rule's
|
|
833
|
+
* own userId - and webhooks are the channel where a hijacked method
|
|
834
|
+
* matters most, because the attacker chooses the endpoint outright.
|
|
835
|
+
*/
|
|
836
|
+
expect(webhookSelect).toEqual({
|
|
837
|
+
webhookUrl: true,
|
|
838
|
+
name: true,
|
|
839
|
+
secret: true,
|
|
840
|
+
userId: true,
|
|
841
|
+
});
|
|
842
|
+
expect(webhookSelect["isVerified"]).toBeUndefined();
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
test("userId is selected - the timeline row and every sender stamp it", async () => {
|
|
846
|
+
const arg: FindOneByIdArg = await captureFindArg();
|
|
847
|
+
|
|
848
|
+
expect(arg.select["userId"]).toBe(true);
|
|
849
|
+
expect(arg.select["_id"]).toBe(true);
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
/*
|
|
854
|
+
* ----------------------------------------------------------------------- *
|
|
855
|
+
* (D) The seam itself: what crosses from one half to the other.
|
|
856
|
+
* -----------------------------------------------------------------------
|
|
857
|
+
*/
|
|
858
|
+
|
|
859
|
+
describe("the seam between the two halves", () => {
|
|
860
|
+
test("the delivery half is entered exactly once per execution", async () => {
|
|
861
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
862
|
+
|
|
863
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
864
|
+
RULE_ID,
|
|
865
|
+
executeOptions(),
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
expect(deliver).toHaveBeenCalledTimes(1);
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
test("the rule object findOneById returned is handed over BY REFERENCE, unchanged", async () => {
|
|
872
|
+
const rule: UserNotificationRule = loadedRule({
|
|
873
|
+
userEmail: verifiedUserEmail(),
|
|
874
|
+
} as unknown as JSONObject);
|
|
875
|
+
spies.findRule.mockResolvedValue(rule as never);
|
|
876
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
877
|
+
|
|
878
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
879
|
+
RULE_ID,
|
|
880
|
+
executeOptions(),
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
/*
|
|
884
|
+
* Identity, not shape. A copy would be a real regression: the channel
|
|
885
|
+
* blocks read hydrated RELATIONS (userEmail.email, userEmail.isVerified),
|
|
886
|
+
* and any reshaping on the way across is exactly how a relation turns
|
|
887
|
+
* into a bare FK and a verified responder stops being contactable.
|
|
888
|
+
*/
|
|
889
|
+
expect(deliver.mock.calls[0]![0]).toBe(rule);
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
test("the caller's options bag is handed over BY REFERENCE, unchanged", async () => {
|
|
893
|
+
const options: ExecuteOptions = executeOptions();
|
|
894
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
895
|
+
|
|
896
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
897
|
+
RULE_ID,
|
|
898
|
+
options,
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
expect(deliver.mock.calls[0]![1]).toBe(options);
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
test("the public half itself does no delivery work - it is all beyond the seam", async () => {
|
|
905
|
+
stubDeliveryHalf();
|
|
906
|
+
|
|
907
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
908
|
+
RULE_ID,
|
|
909
|
+
executeOptions(),
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
/*
|
|
913
|
+
* With the delivery half stubbed out, NOTHING should be left running in
|
|
914
|
+
* the public method: no Twilio config, no trigger-entity lookup, no
|
|
915
|
+
* timeline row, no send. If any of these fire, part of the body was left
|
|
916
|
+
* behind on the claim-and-load side, where the fallback cannot reuse it.
|
|
917
|
+
*/
|
|
918
|
+
expect(spies.twilio).not.toHaveBeenCalled();
|
|
919
|
+
expect(spies.incidentFind).not.toHaveBeenCalled();
|
|
920
|
+
expect(spies.alertFind).not.toHaveBeenCalled();
|
|
921
|
+
expect(spies.alertEpisodeFind).not.toHaveBeenCalled();
|
|
922
|
+
expect(spies.incidentEpisodeFind).not.toHaveBeenCalled();
|
|
923
|
+
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
924
|
+
for (const sender of everySender()) {
|
|
925
|
+
expect(sender).not.toHaveBeenCalled();
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
test("the public method still resolves to undefined - the delivery boolean is swallowed", async () => {
|
|
930
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
931
|
+
deliver.mockResolvedValue(false as never);
|
|
932
|
+
|
|
933
|
+
/*
|
|
934
|
+
* The delivery half reports whether anything was dispatched, and the
|
|
935
|
+
* fallback branches on that. The rule-driven path deliberately does not:
|
|
936
|
+
* its signature is Promise<void> and callers await it for sequencing
|
|
937
|
+
* only. Returning the boolean here would be a public-API change.
|
|
938
|
+
*/
|
|
939
|
+
await expect(
|
|
940
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
941
|
+
RULE_ID,
|
|
942
|
+
executeOptions(),
|
|
943
|
+
),
|
|
944
|
+
).resolves.toBeUndefined();
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
test("a throwing delivery half propagates out of the public method", async () => {
|
|
948
|
+
const deliver: jest.SpyInstance = stubDeliveryHalf();
|
|
949
|
+
deliver.mockRejectedValue(
|
|
950
|
+
new BadDataException(
|
|
951
|
+
"Incident, Alert, Alert Episode, or Incident Episode not found.",
|
|
952
|
+
) as never,
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
/*
|
|
956
|
+
* The delivery half is AWAITED, not fired and forgotten. That is what
|
|
957
|
+
* lets ExecutePendingExecutions turn a missing trigger entity into an
|
|
958
|
+
* Error on the log instead of an unhandled rejection.
|
|
959
|
+
*/
|
|
960
|
+
await expect(
|
|
961
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
962
|
+
RULE_ID,
|
|
963
|
+
executeOptions(),
|
|
964
|
+
),
|
|
965
|
+
).rejects.toThrow(
|
|
966
|
+
"Incident, Alert, Alert Episode, or Incident Episode not found.",
|
|
967
|
+
);
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
test("end to end, unstubbed, the rule loaded by id still reaches a sender", async () => {
|
|
971
|
+
spies.findRule.mockResolvedValue(
|
|
972
|
+
loadedRule({
|
|
973
|
+
userEmail: verifiedUserEmail(),
|
|
974
|
+
} as unknown as JSONObject) as never,
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
978
|
+
RULE_ID,
|
|
979
|
+
executeOptions(),
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
expect(spies.mail).toHaveBeenCalledTimes(1);
|
|
983
|
+
expect(timelineRows).toHaveLength(1);
|
|
984
|
+
expect(timelineRows[0]?.status).toBe(UserNotificationStatus.Sending);
|
|
985
|
+
expect(timelineRows[0]?.statusMessage).toContain(RESPONDER_EMAIL);
|
|
986
|
+
// A rule that DOES have an id still stamps it on the timeline row.
|
|
987
|
+
expect(timelineRows[0]?.userNotificationRuleId?.toString()).toBe(
|
|
988
|
+
RULE_ID.toString(),
|
|
989
|
+
);
|
|
990
|
+
expect(timelineRows[0]?.userEmailId?.toString()).toBe(
|
|
991
|
+
EMAIL_METHOD_ID.toString(),
|
|
992
|
+
);
|
|
993
|
+
});
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
/*
|
|
997
|
+
* ----------------------------------------------------------------------- *
|
|
998
|
+
* (E) The delivery half driven directly with an UNSAVED rule.
|
|
999
|
+
*
|
|
1000
|
+
* This is what the fallback does, and it is the regression guard for
|
|
1001
|
+
* blocker B1.
|
|
1002
|
+
* -----------------------------------------------------------------------
|
|
1003
|
+
*/
|
|
1004
|
+
|
|
1005
|
+
describe("the delivery half accepts an unsaved, id-less rule", () => {
|
|
1006
|
+
test("the rule the fallback builds really has no id", () => {
|
|
1007
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1008
|
+
|
|
1009
|
+
/*
|
|
1010
|
+
* Guarding the guard. `id` is a getter over `_id`, so a rule that was
|
|
1011
|
+
* never saved reports null - and every assertion below about the
|
|
1012
|
+
* timeline row depends on that actually being the case.
|
|
1013
|
+
*/
|
|
1014
|
+
expect(rule.id).toBeNull();
|
|
1015
|
+
expect(rule._id).toBeUndefined();
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
test("an id-less email rule still writes a timeline row and dispatches", async () => {
|
|
1019
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1020
|
+
rule.userEmail = verifiedUserEmail();
|
|
1021
|
+
rule.userEmailId = EMAIL_METHOD_ID;
|
|
1022
|
+
|
|
1023
|
+
const dispatched: boolean =
|
|
1024
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1025
|
+
|
|
1026
|
+
/*
|
|
1027
|
+
* BLOCKER B1. UserOnCallLogTimeline.userNotificationRuleId used to be
|
|
1028
|
+
* NOT NULL, so this create() threw for every fallback delivery - before
|
|
1029
|
+
* a single page left the process. The column is nullable now and
|
|
1030
|
+
* buildLogTimelineItem only sets the id when the rule has one.
|
|
1031
|
+
*/
|
|
1032
|
+
expect(dispatched).toBe(true);
|
|
1033
|
+
expect(timelineRows).toHaveLength(1);
|
|
1034
|
+
expect(timelineRows[0]?.userNotificationRuleId).toBeUndefined();
|
|
1035
|
+
expect(timelineRows[0]?.status).toBe(UserNotificationStatus.Sending);
|
|
1036
|
+
expect(timelineRows[0]?.statusMessage).toContain(RESPONDER_EMAIL);
|
|
1037
|
+
expect(spies.mail).toHaveBeenCalledTimes(1);
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
test("the id-less row still carries everything a reader needs", async () => {
|
|
1041
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1042
|
+
rule.userEmail = verifiedUserEmail();
|
|
1043
|
+
rule.userEmailId = EMAIL_METHOD_ID;
|
|
1044
|
+
|
|
1045
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1046
|
+
|
|
1047
|
+
/*
|
|
1048
|
+
* Losing the rule id must not quietly cost the row its other anchors -
|
|
1049
|
+
* an operator opening the on-call timeline has to see which log, which
|
|
1050
|
+
* user, which escalation and which incident this page belonged to.
|
|
1051
|
+
*/
|
|
1052
|
+
expect(timelineRows[0]?.userId?.toString()).toBe(USER_ID.toString());
|
|
1053
|
+
expect(timelineRows[0]?.projectId?.toString()).toBe(
|
|
1054
|
+
PROJECT_ID.toString(),
|
|
1055
|
+
);
|
|
1056
|
+
expect(timelineRows[0]?.userNotificationLogId?.toString()).toBe(
|
|
1057
|
+
LOG_ID.toString(),
|
|
1058
|
+
);
|
|
1059
|
+
expect(timelineRows[0]?.userNotificationEventType).toBe(
|
|
1060
|
+
UserNotificationEventType.IncidentCreated,
|
|
1061
|
+
);
|
|
1062
|
+
expect(timelineRows[0]?.triggeredByIncidentId?.toString()).toBe(
|
|
1063
|
+
INCIDENT_ID.toString(),
|
|
1064
|
+
);
|
|
1065
|
+
expect(timelineRows[0]?.onCallDutyPolicyId?.toString()).toBe(
|
|
1066
|
+
POLICY_ID.toString(),
|
|
1067
|
+
);
|
|
1068
|
+
expect(
|
|
1069
|
+
timelineRows[0]?.onCallDutyPolicyEscalationRuleId?.toString(),
|
|
1070
|
+
).toBe(ESCALATION_RULE_ID.toString());
|
|
1071
|
+
expect(timelineRows[0]?.userBelongsToTeamId?.toString()).toBe(
|
|
1072
|
+
TEAM_ID.toString(),
|
|
1073
|
+
);
|
|
1074
|
+
// The method FK is read off the RELATION, which is all the fallback sets.
|
|
1075
|
+
expect(timelineRows[0]?.userEmailId?.toString()).toBe(
|
|
1076
|
+
EMAIL_METHOD_ID.toString(),
|
|
1077
|
+
);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
test("an id-less push rule dispatches too - push is the fallback's first choice", async () => {
|
|
1081
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1082
|
+
rule.userPush = verifiedUserPush();
|
|
1083
|
+
rule.userPushId = PUSH_METHOD_ID;
|
|
1084
|
+
|
|
1085
|
+
const dispatched: boolean =
|
|
1086
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1087
|
+
|
|
1088
|
+
expect(dispatched).toBe(true);
|
|
1089
|
+
expect(spies.push).toHaveBeenCalledTimes(1);
|
|
1090
|
+
expect(timelineRows).toHaveLength(1);
|
|
1091
|
+
expect(timelineRows[0]?.userNotificationRuleId).toBeUndefined();
|
|
1092
|
+
expect(timelineRows[0]?.userPushId?.toString()).toBe(
|
|
1093
|
+
PUSH_METHOD_ID.toString(),
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
test("driving the delivery half directly claims nothing and loads no rule", async () => {
|
|
1098
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1099
|
+
rule.userEmail = verifiedUserEmail();
|
|
1100
|
+
rule.userEmailId = EMAIL_METHOD_ID;
|
|
1101
|
+
|
|
1102
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1103
|
+
|
|
1104
|
+
/*
|
|
1105
|
+
* The two things the public half does are exactly the two things a
|
|
1106
|
+
* rule with no database row cannot do. If either had been left inside
|
|
1107
|
+
* the delivery half, the fallback would try to claim a rule id it does
|
|
1108
|
+
* not have and read a row that does not exist.
|
|
1109
|
+
*/
|
|
1110
|
+
expect(spies.claim).not.toHaveBeenCalled();
|
|
1111
|
+
expect(spies.findRule).not.toHaveBeenCalled();
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
test("the unsaved rule is never persisted on the way through", async () => {
|
|
1115
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1116
|
+
rule.userEmail = verifiedUserEmail();
|
|
1117
|
+
rule.userEmailId = EMAIL_METHOD_ID;
|
|
1118
|
+
|
|
1119
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1120
|
+
|
|
1121
|
+
/*
|
|
1122
|
+
* The user never asked for this rule. Saving it would silently rewrite
|
|
1123
|
+
* their notification configuration behind their back, and the next real
|
|
1124
|
+
* page would match a rule they did not create.
|
|
1125
|
+
*/
|
|
1126
|
+
expect(spies.createRule).not.toHaveBeenCalled();
|
|
1127
|
+
expect(rule.id).toBeNull();
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
test("two delivery calls build two DISTINCT timeline instances", async () => {
|
|
1131
|
+
const pushRule: UserNotificationRule = unsavedFallbackRule();
|
|
1132
|
+
pushRule.userPush = verifiedUserPush();
|
|
1133
|
+
pushRule.userPushId = PUSH_METHOD_ID;
|
|
1134
|
+
|
|
1135
|
+
const emailRule: UserNotificationRule = unsavedFallbackRule();
|
|
1136
|
+
emailRule.userEmail = verifiedUserEmail();
|
|
1137
|
+
emailRule.userEmailId = EMAIL_METHOD_ID;
|
|
1138
|
+
|
|
1139
|
+
await deliveryHalf().deliverNotificationForRule(
|
|
1140
|
+
pushRule,
|
|
1141
|
+
executeOptions(),
|
|
1142
|
+
);
|
|
1143
|
+
await deliveryHalf().deliverNotificationForRule(
|
|
1144
|
+
emailRule,
|
|
1145
|
+
executeOptions(),
|
|
1146
|
+
);
|
|
1147
|
+
|
|
1148
|
+
/*
|
|
1149
|
+
* Why the fallback calls this once PER CHANNEL with a freshly built
|
|
1150
|
+
* rule instead of looping channels inside one call: the timeline item is
|
|
1151
|
+
* a single mutable instance per call, and after the first create() it
|
|
1152
|
+
* carries an _id - so a second create() with the SAME instance UPDATEs
|
|
1153
|
+
* the row the first channel wrote instead of inserting a second one. Two
|
|
1154
|
+
* calls means two instances, which is what keeps the second page
|
|
1155
|
+
* visible.
|
|
1156
|
+
*/
|
|
1157
|
+
expect(timelineInstances).toHaveLength(2);
|
|
1158
|
+
expect(timelineInstances[0]).not.toBe(timelineInstances[1]);
|
|
1159
|
+
expect(spies.push).toHaveBeenCalledTimes(1);
|
|
1160
|
+
expect(spies.mail).toHaveBeenCalledTimes(1);
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
test("an id-less rule with no contactable method dispatches nothing and returns false", async () => {
|
|
1164
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1165
|
+
|
|
1166
|
+
const dispatched: boolean =
|
|
1167
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1168
|
+
|
|
1169
|
+
/*
|
|
1170
|
+
* The return value is the fallback's only evidence. "Resolved without
|
|
1171
|
+
* throwing" is NOT "paged": a false here is what stops the operator
|
|
1172
|
+
* being told the responder was reached on a channel that sent nothing.
|
|
1173
|
+
*/
|
|
1174
|
+
expect(dispatched).toBe(false);
|
|
1175
|
+
for (const sender of everySender()) {
|
|
1176
|
+
expect(sender).not.toHaveBeenCalled();
|
|
1177
|
+
}
|
|
1178
|
+
expect(timelineRows).toHaveLength(0);
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
test("an id-less rule whose method is UNVERIFIED is not contacted", async () => {
|
|
1182
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1183
|
+
const userEmail: UserEmail = verifiedUserEmail();
|
|
1184
|
+
userEmail.isVerified = false;
|
|
1185
|
+
rule.userEmail = userEmail;
|
|
1186
|
+
rule.userEmailId = EMAIL_METHOD_ID;
|
|
1187
|
+
|
|
1188
|
+
const dispatched: boolean =
|
|
1189
|
+
await deliveryHalf().deliverNotificationForRule(rule, executeOptions());
|
|
1190
|
+
|
|
1191
|
+
/*
|
|
1192
|
+
* The verification gates are inside the delivery half, so they apply to
|
|
1193
|
+
* the fallback exactly as they apply to a configured rule. A fallback
|
|
1194
|
+
* that could page unverified addresses would be a way to send mail to an
|
|
1195
|
+
* address nobody proved they own.
|
|
1196
|
+
*/
|
|
1197
|
+
expect(dispatched).toBe(false);
|
|
1198
|
+
expect(spies.mail).not.toHaveBeenCalled();
|
|
1199
|
+
expect(timelineRows).toHaveLength(1);
|
|
1200
|
+
expect(timelineRows[0]?.status).toBe(UserNotificationStatus.Error);
|
|
1201
|
+
expect(timelineRows[0]?.statusMessage).toContain("not verified");
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
test("a missing trigger entity still throws, even for an id-less rule", async () => {
|
|
1205
|
+
spies.incidentFind.mockResolvedValue(null as never);
|
|
1206
|
+
|
|
1207
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1208
|
+
rule.userEmail = verifiedUserEmail();
|
|
1209
|
+
rule.userEmailId = EMAIL_METHOD_ID;
|
|
1210
|
+
|
|
1211
|
+
await expect(
|
|
1212
|
+
deliveryHalf().deliverNotificationForRule(rule, executeOptions()),
|
|
1213
|
+
).rejects.toThrow(
|
|
1214
|
+
"Incident, Alert, Alert Episode, or Incident Episode not found.",
|
|
1215
|
+
);
|
|
1216
|
+
|
|
1217
|
+
expect(spies.mail).not.toHaveBeenCalled();
|
|
1218
|
+
});
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
/*
|
|
1222
|
+
* ----------------------------------------------------------------------- *
|
|
1223
|
+
* (F) projectTwilioConfig: resolved once, in the delivery half.
|
|
1224
|
+
* -----------------------------------------------------------------------
|
|
1225
|
+
*/
|
|
1226
|
+
|
|
1227
|
+
describe("the project Twilio config", () => {
|
|
1228
|
+
test("is resolved exactly once per delivery, from options.projectId", async () => {
|
|
1229
|
+
const options: ExecuteOptions = executeOptions();
|
|
1230
|
+
spies.findRule.mockResolvedValue(
|
|
1231
|
+
loadedRule({
|
|
1232
|
+
userSms: verifiedUserSms(),
|
|
1233
|
+
} as unknown as JSONObject) as never,
|
|
1234
|
+
);
|
|
1235
|
+
|
|
1236
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
1237
|
+
RULE_ID,
|
|
1238
|
+
options,
|
|
1239
|
+
);
|
|
1240
|
+
|
|
1241
|
+
expect(spies.twilio).toHaveBeenCalledTimes(1);
|
|
1242
|
+
/*
|
|
1243
|
+
* Identity against the caller's own projectId. The channel blocks below
|
|
1244
|
+
* pass the TRIGGER entity's projectId to the senders, so reading the
|
|
1245
|
+
* config off the incident instead of the options would look identical in
|
|
1246
|
+
* every normal case and diverge only in the odd one.
|
|
1247
|
+
*/
|
|
1248
|
+
expect(spies.twilio.mock.calls[0]![0]).toBe(options.projectId);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
test("is resolved AFTER the rule is loaded and BEFORE any timeline row or send", async () => {
|
|
1252
|
+
spies.findRule.mockResolvedValue(
|
|
1253
|
+
loadedRule({
|
|
1254
|
+
userSms: verifiedUserSms(),
|
|
1255
|
+
} as unknown as JSONObject) as never,
|
|
1256
|
+
);
|
|
1257
|
+
|
|
1258
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
1259
|
+
RULE_ID,
|
|
1260
|
+
executeOptions(),
|
|
1261
|
+
);
|
|
1262
|
+
|
|
1263
|
+
/*
|
|
1264
|
+
* The ordering IS the extraction boundary: the config lookup was the
|
|
1265
|
+
* first statement of the old body's second half, and it has to stay on
|
|
1266
|
+
* the delivery side or the fallback never sees it.
|
|
1267
|
+
*/
|
|
1268
|
+
expect(spies.findRule.mock.invocationCallOrder[0]).toBeLessThan(
|
|
1269
|
+
spies.twilio.mock.invocationCallOrder[0] as number,
|
|
1270
|
+
);
|
|
1271
|
+
expect(spies.twilio.mock.invocationCallOrder[0]).toBeLessThan(
|
|
1272
|
+
spies.timelineCreate.mock.invocationCallOrder[0] as number,
|
|
1273
|
+
);
|
|
1274
|
+
expect(spies.twilio.mock.invocationCallOrder[0]).toBeLessThan(
|
|
1275
|
+
spies.sms.mock.invocationCallOrder[0] as number,
|
|
1276
|
+
);
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
test("the resolved config object reaches SmsService.sendSms by reference", async () => {
|
|
1280
|
+
const twilioConfig: TwilioConfig = fakeTwilioConfig();
|
|
1281
|
+
spies.twilio.mockResolvedValue(twilioConfig as never);
|
|
1282
|
+
spies.findRule.mockResolvedValue(
|
|
1283
|
+
loadedRule({
|
|
1284
|
+
userSms: verifiedUserSms(),
|
|
1285
|
+
} as unknown as JSONObject) as never,
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
1289
|
+
RULE_ID,
|
|
1290
|
+
executeOptions(),
|
|
1291
|
+
);
|
|
1292
|
+
|
|
1293
|
+
expect(spies.sms).toHaveBeenCalledTimes(1);
|
|
1294
|
+
/*
|
|
1295
|
+
* A project with its own Twilio account must be billed on that account.
|
|
1296
|
+
* Dropping this key does not fail - it quietly sends every on-call SMS
|
|
1297
|
+
* from the global OneUptime number.
|
|
1298
|
+
*/
|
|
1299
|
+
expect(sentSmsOptions().customTwilioConfig).toBe(twilioConfig);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
test("no project config resolves to undefined and is still passed through", async () => {
|
|
1303
|
+
spies.twilio.mockResolvedValue(undefined as never);
|
|
1304
|
+
spies.findRule.mockResolvedValue(
|
|
1305
|
+
loadedRule({
|
|
1306
|
+
userSms: verifiedUserSms(),
|
|
1307
|
+
} as unknown as JSONObject) as never,
|
|
1308
|
+
);
|
|
1309
|
+
|
|
1310
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
1311
|
+
RULE_ID,
|
|
1312
|
+
executeOptions(),
|
|
1313
|
+
);
|
|
1314
|
+
|
|
1315
|
+
// undefined is the signal for "use the global config", not a bug.
|
|
1316
|
+
expect(sentSmsOptions().customTwilioConfig).toBeUndefined();
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
test("a fallback-style delivery resolves the PROJECT's config too", async () => {
|
|
1320
|
+
const twilioConfig: TwilioConfig = fakeTwilioConfig();
|
|
1321
|
+
spies.twilio.mockResolvedValue(twilioConfig as never);
|
|
1322
|
+
|
|
1323
|
+
const rule: UserNotificationRule = unsavedFallbackRule();
|
|
1324
|
+
rule.userSms = verifiedUserSms();
|
|
1325
|
+
rule.userSmsId = SMS_METHOD_ID;
|
|
1326
|
+
|
|
1327
|
+
const options: ExecuteOptions = executeOptions();
|
|
1328
|
+
await deliveryHalf().deliverNotificationForRule(rule, options);
|
|
1329
|
+
|
|
1330
|
+
/*
|
|
1331
|
+
* The fallback picks a paid channel only when the responder has no
|
|
1332
|
+
* zero-cost method, so when it does reach SMS the project's own Twilio
|
|
1333
|
+
* account is the one that must be charged.
|
|
1334
|
+
*/
|
|
1335
|
+
expect(spies.twilio).toHaveBeenCalledTimes(1);
|
|
1336
|
+
expect(spies.twilio.mock.calls[0]![0]).toBe(options.projectId);
|
|
1337
|
+
expect(sentSmsOptions().customTwilioConfig).toBe(twilioConfig);
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
test("it is hoisted above the channel blocks - two channels, still one lookup", async () => {
|
|
1341
|
+
/*
|
|
1342
|
+
* A rule with two methods is not something the product creates (a rule
|
|
1343
|
+
* carries exactly one), and the fallback deliberately never builds one -
|
|
1344
|
+
* see the distinct-instances test above for why. It is used HERE only to
|
|
1345
|
+
* prove the config read sits above the channel blocks rather than inside
|
|
1346
|
+
* them: were it per-block, this would be two reads.
|
|
1347
|
+
*/
|
|
1348
|
+
spies.findRule.mockResolvedValue(
|
|
1349
|
+
loadedRule({
|
|
1350
|
+
userEmail: verifiedUserEmail(),
|
|
1351
|
+
userSms: verifiedUserSms(),
|
|
1352
|
+
} as unknown as JSONObject) as never,
|
|
1353
|
+
);
|
|
1354
|
+
|
|
1355
|
+
await UserNotificationRuleService.executeNotificationRuleItem(
|
|
1356
|
+
RULE_ID,
|
|
1357
|
+
executeOptions(),
|
|
1358
|
+
);
|
|
1359
|
+
|
|
1360
|
+
expect(spies.mail).toHaveBeenCalledTimes(1);
|
|
1361
|
+
expect(spies.sms).toHaveBeenCalledTimes(1);
|
|
1362
|
+
expect(spies.twilio).toHaveBeenCalledTimes(1);
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
test("a Twilio config read that rejects takes the whole delivery down", async () => {
|
|
1366
|
+
spies.twilio.mockRejectedValue(
|
|
1367
|
+
new Error("project config read failed") as never,
|
|
1368
|
+
);
|
|
1369
|
+
spies.findRule.mockResolvedValue(
|
|
1370
|
+
loadedRule({
|
|
1371
|
+
userSms: verifiedUserSms(),
|
|
1372
|
+
} as unknown as JSONObject) as never,
|
|
1373
|
+
);
|
|
1374
|
+
|
|
1375
|
+
/*
|
|
1376
|
+
* Pinned as current behaviour: the lookup is the first awaited statement
|
|
1377
|
+
* of the delivery half and is not guarded, so a failing project-config
|
|
1378
|
+
* read stops the page rather than falling back to the global config.
|
|
1379
|
+
* Loud, and the log ends up Error - but it is a real single point of
|
|
1380
|
+
* failure for channels that do not need Twilio at all.
|
|
1381
|
+
*/
|
|
1382
|
+
await expect(
|
|
1383
|
+
UserNotificationRuleService.executeNotificationRuleItem(
|
|
1384
|
+
RULE_ID,
|
|
1385
|
+
executeOptions(),
|
|
1386
|
+
),
|
|
1387
|
+
).rejects.toThrow("project config read failed");
|
|
1388
|
+
|
|
1389
|
+
expect(spies.sms).not.toHaveBeenCalled();
|
|
1390
|
+
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
1391
|
+
});
|
|
1392
|
+
});
|
|
1393
|
+
});
|