@oneuptime/common 12.0.8 → 12.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
- package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
- package/Models/DatabaseModels/AIConversation.ts +32 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
- package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
- package/Models/DatabaseModels/Index.ts +12 -4
- package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +139 -9
- package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
- package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +8 -8
- package/Models/DatabaseModels/NetworkDevice.ts +141 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
- package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
- package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
- package/Models/DatabaseModels/Project.ts +78 -0
- package/Models/DatabaseModels/UserCall.ts +42 -0
- package/Models/DatabaseModels/UserEmail.ts +44 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
- package/Models/DatabaseModels/UserPush.ts +49 -0
- package/Models/DatabaseModels/UserSMS.ts +43 -0
- package/Models/DatabaseModels/UserTelegram.ts +59 -0
- package/Models/DatabaseModels/UserWebhook.ts +52 -0
- package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
- package/Server/API/AIChatAPI.ts +315 -1
- package/Server/API/DashboardAPI.ts +217 -1
- package/Server/API/OnCallReadinessAPI.ts +841 -0
- package/Server/API/TeamComplianceAPI.ts +69 -17
- package/Server/API/TelemetryAPI.ts +220 -12
- package/Server/EnvironmentConfig.ts +52 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
- package/Server/Infrastructure/Queue.ts +78 -13
- package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
- package/Server/Services/AIService.ts +7 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/AlertSeverityService.ts +63 -0
- package/Server/Services/DashboardService.ts +9 -10
- package/Server/Services/DatabaseService.ts +32 -2
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/IncidentSeverityService.ts +76 -0
- package/Server/Services/Index.ts +12 -4
- package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
- package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +4 -4
- package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +89 -20
- package/Server/Services/LogAggregationService.ts +45 -8
- package/Server/Services/MetricAggregationService.ts +121 -0
- package/Server/Services/MetricService.ts +7 -7
- package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
- package/Server/Services/NetworkDeviceLinkService.ts +84 -0
- package/Server/Services/NetworkDeviceService.ts +140 -0
- package/Server/Services/NetworkSiteService.ts +77 -25
- package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
- package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
- package/Server/Services/OnCallReadinessService.ts +2803 -0
- package/Server/Services/OnCallSetupReminderService.ts +955 -0
- package/Server/Services/ProfileAggregationService.ts +123 -0
- package/Server/Services/StatusPageService.ts +9 -10
- package/Server/Services/TeamComplianceService.ts +429 -252
- package/Server/Services/UserCallService.ts +26 -1
- package/Server/Services/UserEmailService.ts +26 -1
- package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
- package/Server/Services/UserNotificationRuleService.ts +3812 -333
- package/Server/Services/UserOnCallLogService.ts +561 -48
- package/Server/Services/UserPushService.ts +29 -0
- package/Server/Services/UserService.ts +11 -0
- package/Server/Services/UserSmsService.ts +26 -1
- package/Server/Services/UserTelegramService.ts +24 -1
- package/Server/Services/UserWebhookService.ts +28 -1
- package/Server/Services/UserWhatsAppService.ts +24 -1
- package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
- package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
- package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
- package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
- package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
- package/Server/Types/Workflow/Components/API/Get.ts +1 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
- package/Server/Types/Workflow/Components/API/Post.ts +1 -1
- package/Server/Types/Workflow/Components/API/Put.ts +1 -1
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
- package/Server/Types/Workflow/Components/Email.ts +25 -7
- package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
- package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
- package/Server/Utils/AI/Toolbox/Index.ts +48 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
- package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
- package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
- package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
- package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
- package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
- package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
- package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
- package/Server/Utils/ClientIp.ts +221 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
- package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
- package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
- package/Server/Utils/Express.ts +12 -17
- package/Server/Utils/LLM/LLMService.ts +85 -8
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
- package/Server/Utils/SSRFProtection.ts +98 -23
- package/Server/Utils/StartServer.ts +12 -3
- package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +25 -25
- package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
- package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
- package/Tests/Models/InventoryItemModel.test.ts +174 -0
- package/Tests/Models/InventoryItemNaming.test.ts +302 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
- package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
- package/Tests/Server/API/Helpers.ts +24 -15
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
- package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
- package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
- package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
- package/Tests/Server/Services/{TelemetryEntityManualCreate.test.ts → InventoryItemManualCreate.test.ts} +27 -27
- package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
- package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
- package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
- package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
- package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
- package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
- package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
- package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
- package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
- package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
- package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
- package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
- package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
- package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
- package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
- package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
- package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
- package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
- package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
- package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
- package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
- package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
- package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
- package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
- package/Tests/Server/Utils/ClientIp.test.ts +438 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
- package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
- package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +15 -15
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
- package/Tests/Types/IP/IP.test.ts +263 -0
- package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
- package/Tests/Types/IP/IPv6.test.ts +12 -1
- package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +6 -6
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
- package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
- package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +112 -0
- package/Tests/Types/Workflow/Templates.test.ts +916 -101
- package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
- package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
- package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
- package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
- package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
- package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
- package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +72 -0
- package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +242 -7
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +15 -7
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +242 -28
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +59 -1
- package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
- package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
- package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
- package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
- package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
- package/Tests/UI/EsbuildConfig.test.ts +607 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
- package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
- package/Tests/UI/Utils/Platform.test.ts +147 -0
- package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +487 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
- package/Tests/__mocks__/bullmq.js +55 -0
- package/Types/AI/AIChatMessageStatus.ts +8 -1
- package/Types/AI/AIChatTypes.ts +12 -0
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
- package/Types/Exception/ExceptionCode.ts +2 -0
- package/Types/Exception/ServiceUnavailableException.ts +8 -0
- package/Types/Exception/TooManyRequestsException.ts +8 -0
- package/Types/IP/IP.ts +93 -47
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +49 -3
- package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
- package/Types/OnCallDutyPolicy/Layer.ts +203 -149
- package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
- package/Types/Permission.ts +193 -0
- package/Types/Telemetry/EntityRelationshipType.ts +1 -1
- package/Types/Telemetry/EntitySource.ts +1 -1
- package/Types/Telemetry/EntityType.ts +1 -1
- package/Types/Telemetry/EntityTypeGroups.ts +1 -1
- package/Types/Workflow/Components/BaseModel.ts +75 -29
- package/Types/Workflow/Components/Discord.ts +1 -0
- package/Types/Workflow/Components/Email.ts +12 -3
- package/Types/Workflow/Components/JavaScript.ts +7 -0
- package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
- package/Types/Workflow/Components/Slack.ts +1 -0
- package/Types/Workflow/Components/Telegram.ts +1 -0
- package/Types/Workflow/TemplateSyntax.ts +44 -0
- package/Types/Workflow/Templates.ts +2097 -45
- package/UI/Components/Calendar/Calendar.css +43 -0
- package/UI/Components/Calendar/Calendar.tsx +8 -0
- package/UI/Components/Card/Card.tsx +2 -2
- package/UI/Components/Checkbox/Checkbox.tsx +16 -0
- package/UI/Components/Dictionary/Dictionary.tsx +48 -9
- package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
- package/UI/Components/Input/Input.tsx +1 -0
- package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
- package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
- package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
- package/UI/Components/LogsViewer/types.ts +23 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
- package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
- package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
- package/UI/Components/Table/Table.tsx +49 -16
- package/UI/Components/Table/TableBody.tsx +53 -28
- package/UI/Components/Table/TableHeader.tsx +13 -0
- package/UI/Components/Table/TableRow.tsx +58 -26
- package/UI/Components/Workflow/ArgumentsForm.tsx +155 -9
- package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +10 -1
- package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
- package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
- package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
- package/UI/Components/Workflow/GraphLint.ts +42 -5
- package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +154 -22
- package/UI/Components/Workflow/ModelSchema.ts +115 -33
- package/UI/Components/Workflow/RunStatusWatcher.ts +1 -1
- package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
- package/UI/Components/Workflow/UseRunWatch.ts +212 -0
- package/UI/Components/Workflow/VariableModal.tsx +6 -2
- package/UI/Components/Workflow/Workflow.tsx +49 -3
- package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
- package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
- package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
- package/UI/Utils/Platform.ts +149 -0
- package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
- package/UI/esbuild-config.js +22 -1
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +888 -164
- package/Utils/Telemetry/CrossSignalScope.ts +502 -0
- package/Utils/Telemetry/EntityKey.ts +5 -5
- package/Utils/Telemetry/EntityRelationship.ts +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +12 -4
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +167 -31
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +27 -27
- package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +80 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
- package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +237 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +165 -13
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
- package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +129 -18
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +45 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +72 -13
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +1 -0
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +54 -0
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/DashboardService.js +10 -9
- package/build/dist/Server/Services/DashboardService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +24 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +12 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +6 -6
- package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +68 -23
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
- package/build/dist/Server/Services/LogAggregationService.js +27 -8
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +80 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +6 -6
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +53 -13
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
- package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
- package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
- package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +11 -10
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceService.js +312 -160
- package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +24 -1
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +24 -1
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +26 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +24 -1
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +22 -1
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +25 -1
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
- package/build/dist/Server/Utils/ClientIp.js +137 -0
- package/build/dist/Server/Utils/ClientIp.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -12
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +82 -21
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +11 -11
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatTypes.js +12 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +2 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
- package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
- package/build/dist/Types/IP/IP.js +87 -43
- package/build/dist/Types/IP/IP.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +174 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +66 -28
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Discord.js +1 -0
- package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Email.js +12 -3
- package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
- package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Slack.js +1 -0
- package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
- package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
- package/build/dist/Types/Workflow/TemplateSyntax.js +12 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -1
- package/build/dist/Types/Workflow/Templates.js +1931 -38
- package/build/dist/Types/Workflow/Templates.js.map +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -11
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -0
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +27 -15
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +24 -18
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +9 -1
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +29 -21
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +130 -12
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +4 -4
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +33 -4
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +116 -21
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelSchema.js +72 -34
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
- package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
- package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +41 -1
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Platform.js +118 -0
- package/build/dist/UI/Utils/Platform.js.map +1 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +642 -136
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +5 -5
- package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
- package/jest.config.json +1 -0
- package/package.json +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
import { mockRouter } from "./Helpers";
|
|
2
|
+
import CommonAPI from "../../../Server/API/CommonAPI";
|
|
3
|
+
import OnCallReadinessAPI from "../../../Server/API/OnCallReadinessAPI";
|
|
4
|
+
import OnCallReadinessService from "../../../Server/Services/OnCallReadinessService";
|
|
5
|
+
import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
|
|
6
|
+
import OnCallSetupReminderService, {
|
|
7
|
+
SetupReminderOutcome,
|
|
8
|
+
SetupReminderResult,
|
|
9
|
+
SetupReminderUserResult,
|
|
10
|
+
} from "../../../Server/Services/OnCallSetupReminderService";
|
|
11
|
+
import {
|
|
12
|
+
ExpressRequest,
|
|
13
|
+
ExpressResponse,
|
|
14
|
+
NextFunction,
|
|
15
|
+
} from "../../../Server/Utils/Express";
|
|
16
|
+
import Response from "../../../Server/Utils/Response";
|
|
17
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
18
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
19
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
20
|
+
import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
|
|
21
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
22
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
23
|
+
import Permission, {
|
|
24
|
+
UserPermission,
|
|
25
|
+
UserTenantAccessPermission,
|
|
26
|
+
} from "../../../Types/Permission";
|
|
27
|
+
import { beforeEach, describe, expect, test } from "@jest/globals";
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* POST /on-call-readiness/send-setup-reminder - the only WRITE on the readiness
|
|
31
|
+
* router, and the only endpoint in this feature that sends email.
|
|
32
|
+
*
|
|
33
|
+
* The route itself is thin by design: OnCallSetupReminderService owns who gets
|
|
34
|
+
* mailed and what the mail says. What is left worth testing here is exactly the
|
|
35
|
+
* part the service cannot do for itself, and it is all about the boundary:
|
|
36
|
+
*
|
|
37
|
+
* WHO. The route is mounted with UserMiddleware.getUserMiddleware, which
|
|
38
|
+
* admits anonymous callers as UserType.Public and takes the project from a
|
|
39
|
+
* caller-supplied `tenantid` header. So the handler is the only gate, and it
|
|
40
|
+
* has to refuse BEFORE any mail-sending work starts - a route that refuses
|
|
41
|
+
* after the send has not refused anything.
|
|
42
|
+
*
|
|
43
|
+
* WITH WHAT STANDING. Membership is the bar for the READS on this router and
|
|
44
|
+
* was, wrongly, the bar for this write too. Every read-only Viewer holds an
|
|
45
|
+
* access entry for the project, so the original gate let anyone who could LOOK
|
|
46
|
+
* at the readiness table make the product mail their colleagues in the
|
|
47
|
+
* project's name. Reading who is unreachable and mailing them about it are
|
|
48
|
+
* different acts, and the tests below pin the second to on-call management.
|
|
49
|
+
*
|
|
50
|
+
* WHICH PROJECT. The project comes from the authenticated caller's tenant,
|
|
51
|
+
* never from the body. A body that carries its own projectId must be ignored
|
|
52
|
+
* entirely, or a member of project A mails project B's responders about
|
|
53
|
+
* project B by sending one extra field.
|
|
54
|
+
*
|
|
55
|
+
* WHAT THE BODY IS ALLOWED TO BE. ObjectID's constructor accepts any string,
|
|
56
|
+
* so a body of arbitrary text would otherwise travel into the service and out
|
|
57
|
+
* the far side as an ordinary-looking "not a member of this project" result.
|
|
58
|
+
*
|
|
59
|
+
* WHAT COMES BACK. Ids flattened to strings, outcomes preserved per user, and
|
|
60
|
+
* the counts carried alongside - because the UI's whole job is to say "5 sent,
|
|
61
|
+
* 2 skipped, 1 failed" instead of showing a tick.
|
|
62
|
+
*
|
|
63
|
+
* The service is stubbed throughout. OnCallSetupReminder.test.ts covers its
|
|
64
|
+
* behaviour with the same rigour; duplicating that here would test the stub.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
68
|
+
return {
|
|
69
|
+
getRouter: () => {
|
|
70
|
+
return mockRouter;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
76
|
+
return {
|
|
77
|
+
sendJsonObjectResponse: jest.fn().mockImplementation((...args: []) => {
|
|
78
|
+
return args;
|
|
79
|
+
}),
|
|
80
|
+
sendJsonArrayResponse: jest.fn().mockImplementation((...args: []) => {
|
|
81
|
+
return args;
|
|
82
|
+
}),
|
|
83
|
+
sendEntityArrayResponse: jest.fn().mockImplementation((...args: []) => {
|
|
84
|
+
return args;
|
|
85
|
+
}),
|
|
86
|
+
sendEntityResponse: jest.fn().mockImplementation((...args: []) => {
|
|
87
|
+
return args;
|
|
88
|
+
}),
|
|
89
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
90
|
+
sendErrorResponse: jest.fn().mockImplementation((...args: []) => {
|
|
91
|
+
return args;
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const REMINDER_ROUTE: string = "/on-call-readiness/send-setup-reminder";
|
|
97
|
+
|
|
98
|
+
/*
|
|
99
|
+
* The message every authorisation refusal on this router carries. Asserted on
|
|
100
|
+
* the literal so "you are in the wrong project" cannot drift apart from "you are
|
|
101
|
+
* not logged in" - a caller must not be able to tell them apart.
|
|
102
|
+
*/
|
|
103
|
+
const REFUSAL: string = "You are not authorized to access this project's data.";
|
|
104
|
+
|
|
105
|
+
interface RegisteredRoute {
|
|
106
|
+
method: string;
|
|
107
|
+
uri: string;
|
|
108
|
+
middleware: unknown;
|
|
109
|
+
handlerFunction: (
|
|
110
|
+
req: ExpressRequest,
|
|
111
|
+
res: ExpressResponse,
|
|
112
|
+
next: NextFunction,
|
|
113
|
+
) => void | Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface RouteCallResult {
|
|
117
|
+
thrownToNext: unknown;
|
|
118
|
+
nextCallCount: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let propsSpy: jest.SpyInstance;
|
|
122
|
+
let sendRemindersSpy: jest.SpyInstance;
|
|
123
|
+
|
|
124
|
+
let projectId: ObjectID;
|
|
125
|
+
let otherProjectId: ObjectID;
|
|
126
|
+
let callerUserId: ObjectID;
|
|
127
|
+
let userA: ObjectID;
|
|
128
|
+
let userB: ObjectID;
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* A logged-in caller holding exactly the permissions named, in the project
|
|
132
|
+
* named.
|
|
133
|
+
*
|
|
134
|
+
* `isBlockPermission` is set explicitly on every entry because the dictionary
|
|
135
|
+
* these live in holds GRANTS AND DENIALS together and the route's permission
|
|
136
|
+
* read filters on that flag. A fixture that left it undefined would be a grant
|
|
137
|
+
* by accident rather than on purpose, and the denial test below would then be
|
|
138
|
+
* testing nothing.
|
|
139
|
+
*/
|
|
140
|
+
function buildProps(data: {
|
|
141
|
+
projectId: ObjectID;
|
|
142
|
+
userId: ObjectID;
|
|
143
|
+
permissions: Array<Permission>;
|
|
144
|
+
blockedPermissions?: Array<Permission>;
|
|
145
|
+
}): DatabaseCommonInteractionProps {
|
|
146
|
+
const granted: Array<UserPermission> = data.permissions.map(
|
|
147
|
+
(permission: Permission): UserPermission => {
|
|
148
|
+
return {
|
|
149
|
+
_type: "UserPermission",
|
|
150
|
+
permission: permission,
|
|
151
|
+
labelIds: [],
|
|
152
|
+
isBlockPermission: false,
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const blocked: Array<UserPermission> = (data.blockedPermissions || []).map(
|
|
158
|
+
(permission: Permission): UserPermission => {
|
|
159
|
+
return {
|
|
160
|
+
_type: "UserPermission",
|
|
161
|
+
permission: permission,
|
|
162
|
+
labelIds: [],
|
|
163
|
+
isBlockPermission: true,
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const tenantPermission: UserTenantAccessPermission = {
|
|
169
|
+
_type: "UserTenantAccessPermission",
|
|
170
|
+
projectId: data.projectId,
|
|
171
|
+
permissions: [...granted, ...blocked],
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const permissionMap: Dictionary<UserTenantAccessPermission> = {};
|
|
175
|
+
permissionMap[data.projectId.toString()] = tenantPermission;
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
tenantId: data.projectId,
|
|
179
|
+
userId: data.userId,
|
|
180
|
+
userTenantAccessPermission: permissionMap,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
* The caller the rest of this file assumes: somebody who administers on-call in
|
|
186
|
+
* this project, which is what the route now requires. Everything about the body
|
|
187
|
+
* and the response shape is tested through this caller, so a regression in the
|
|
188
|
+
* permission gate shows up as those tests failing loudly rather than as one
|
|
189
|
+
* skipped assertion.
|
|
190
|
+
*/
|
|
191
|
+
function buildOnCallAdminProps(data: {
|
|
192
|
+
projectId: ObjectID;
|
|
193
|
+
userId: ObjectID;
|
|
194
|
+
}): DatabaseCommonInteractionProps {
|
|
195
|
+
return buildProps({
|
|
196
|
+
projectId: data.projectId,
|
|
197
|
+
userId: data.userId,
|
|
198
|
+
permissions: [Permission.ProjectMember, Permission.OnCallAdmin],
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* A member with no on-call standing - the caller the original gate admitted.
|
|
204
|
+
* ProjectMember and Viewer are what an ordinary teammate and a read-only
|
|
205
|
+
* teammate carry, and neither is a licence to send mail as the project.
|
|
206
|
+
*/
|
|
207
|
+
function buildMemberProps(data: {
|
|
208
|
+
projectId: ObjectID;
|
|
209
|
+
userId: ObjectID;
|
|
210
|
+
}): DatabaseCommonInteractionProps {
|
|
211
|
+
return buildProps({
|
|
212
|
+
projectId: data.projectId,
|
|
213
|
+
userId: data.userId,
|
|
214
|
+
permissions: [Permission.ProjectMember],
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function routeFor(uri: string): RegisteredRoute {
|
|
219
|
+
const routes: Array<RegisteredRoute> =
|
|
220
|
+
mockRouter.routes as unknown as Array<RegisteredRoute>;
|
|
221
|
+
|
|
222
|
+
const route: RegisteredRoute | undefined = routes.find(
|
|
223
|
+
(candidate: RegisteredRoute): boolean => {
|
|
224
|
+
return candidate.method === "POST" && candidate.uri === uri;
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (!route) {
|
|
229
|
+
throw new Error(`No POST route registered for ${uri}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return route;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function callRoute(
|
|
236
|
+
body: JSONObject | undefined,
|
|
237
|
+
): Promise<RouteCallResult> {
|
|
238
|
+
const req: ExpressRequest = {
|
|
239
|
+
params: {},
|
|
240
|
+
query: {},
|
|
241
|
+
body: body,
|
|
242
|
+
headers: {},
|
|
243
|
+
} as unknown as ExpressRequest;
|
|
244
|
+
|
|
245
|
+
const res: ExpressResponse = {
|
|
246
|
+
send: jest.fn(),
|
|
247
|
+
json: jest.fn(),
|
|
248
|
+
status: jest.fn().mockReturnThis(),
|
|
249
|
+
} as unknown as ExpressResponse;
|
|
250
|
+
|
|
251
|
+
const next: jest.Mock = jest.fn();
|
|
252
|
+
|
|
253
|
+
await routeFor(REMINDER_ROUTE).handlerFunction(
|
|
254
|
+
req,
|
|
255
|
+
res,
|
|
256
|
+
next as unknown as NextFunction,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
thrownToNext: next.mock.calls[0] ? next.mock.calls[0][0] : undefined,
|
|
261
|
+
nextCallCount: next.mock.calls.length,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* The body the route handed to Response.sendJsonObjectResponse, typed as a bag
|
|
267
|
+
* of unknowns rather than as the wire interface: the point of these assertions
|
|
268
|
+
* is what actually crosses the wire, and typing it as the thing it is supposed
|
|
269
|
+
* to be would let a missing field type-check its way past the test.
|
|
270
|
+
*/
|
|
271
|
+
function jsonPayload(): Record<string, unknown> {
|
|
272
|
+
const sender: jest.Mock =
|
|
273
|
+
Response.sendJsonObjectResponse as unknown as jest.Mock;
|
|
274
|
+
const calls: Array<Array<unknown>> = sender.mock.calls;
|
|
275
|
+
const last: Array<unknown> | undefined = calls[calls.length - 1];
|
|
276
|
+
|
|
277
|
+
if (!last) {
|
|
278
|
+
throw new Error("The route sent no JSON response");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return last[2] as Record<string, unknown>;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function serviceCall(): { projectId: ObjectID; userIds: Array<ObjectID> } {
|
|
285
|
+
const call: Array<unknown> | undefined = sendRemindersSpy.mock.calls[0];
|
|
286
|
+
|
|
287
|
+
if (!call) {
|
|
288
|
+
throw new Error("The route did not call the reminder service");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return call[0] as { projectId: ObjectID; userIds: Array<ObjectID> };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
beforeEach(() => {
|
|
295
|
+
jest.clearAllMocks();
|
|
296
|
+
|
|
297
|
+
projectId = ObjectID.generate();
|
|
298
|
+
otherProjectId = ObjectID.generate();
|
|
299
|
+
callerUserId = ObjectID.generate();
|
|
300
|
+
userA = ObjectID.generate();
|
|
301
|
+
userB = ObjectID.generate();
|
|
302
|
+
|
|
303
|
+
propsSpy = jest
|
|
304
|
+
.spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
|
|
305
|
+
.mockResolvedValue(
|
|
306
|
+
buildOnCallAdminProps({ projectId: projectId, userId: callerUserId }),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
/*
|
|
310
|
+
* The default stub echoes the request back as an all-sent result, so the
|
|
311
|
+
* assertions about WHAT THE ROUTE PASSED IN can be made from the payload it
|
|
312
|
+
* sent out. Tests about the shape of a mixed answer override it.
|
|
313
|
+
*/
|
|
314
|
+
const echoAllSent: (data: {
|
|
315
|
+
projectId: ObjectID;
|
|
316
|
+
userIds: Array<ObjectID>;
|
|
317
|
+
}) => Promise<SetupReminderResult> = async (data: {
|
|
318
|
+
projectId: ObjectID;
|
|
319
|
+
userIds: Array<ObjectID>;
|
|
320
|
+
}): Promise<SetupReminderResult> => {
|
|
321
|
+
return {
|
|
322
|
+
projectId: data.projectId,
|
|
323
|
+
requestedCount: data.userIds.length,
|
|
324
|
+
sentCount: data.userIds.length,
|
|
325
|
+
skippedCount: 0,
|
|
326
|
+
failedCount: 0,
|
|
327
|
+
results: data.userIds.map((userId: ObjectID): SetupReminderUserResult => {
|
|
328
|
+
return {
|
|
329
|
+
userId: userId,
|
|
330
|
+
outcome: SetupReminderOutcome.Sent,
|
|
331
|
+
message: "Reminder sent to their account email address.",
|
|
332
|
+
};
|
|
333
|
+
}),
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
sendRemindersSpy = jest
|
|
338
|
+
.spyOn(OnCallSetupReminderService, "sendSetupReminders")
|
|
339
|
+
.mockImplementation(echoAllSent as never);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe("OnCallReadinessAPI POST /send-setup-reminder - registration", () => {
|
|
343
|
+
test("the route is registered as a POST behind the user middleware", () => {
|
|
344
|
+
const route: RegisteredRoute = routeFor(REMINDER_ROUTE);
|
|
345
|
+
|
|
346
|
+
expect(route.method).toBe("POST");
|
|
347
|
+
expect(route.middleware).toBe(UserMiddleware.getUserMiddleware);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("the readiness router still exposes it alongside its three reads", () => {
|
|
351
|
+
/*
|
|
352
|
+
* A guard against the write being registered on some other router by
|
|
353
|
+
* accident: it has to be reachable at the same prefix as the reads it
|
|
354
|
+
* belongs with, or the dashboard's one base URL cannot reach both.
|
|
355
|
+
*/
|
|
356
|
+
const uris: Array<string> = (
|
|
357
|
+
mockRouter.routes as unknown as Array<RegisteredRoute>
|
|
358
|
+
).map((route: RegisteredRoute): string => {
|
|
359
|
+
return route.uri;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(uris).toContain("/on-call-readiness/project");
|
|
363
|
+
expect(uris).toContain(REMINDER_ROUTE);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe("OnCallReadinessAPI POST /send-setup-reminder - who may call it", () => {
|
|
368
|
+
test("an anonymous caller is refused and nothing is sent", async () => {
|
|
369
|
+
propsSpy.mockResolvedValue({
|
|
370
|
+
tenantId: projectId,
|
|
371
|
+
userTenantAccessPermission: {},
|
|
372
|
+
} as never);
|
|
373
|
+
|
|
374
|
+
const result: RouteCallResult = await callRoute({
|
|
375
|
+
userIds: [userA.toString()],
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
379
|
+
expect((result.thrownToNext as NotAuthorizedException).message).toBe(
|
|
380
|
+
REFUSAL,
|
|
381
|
+
);
|
|
382
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test("a member of a different project is refused and nothing is sent", async () => {
|
|
386
|
+
// The header names THIS project; the caller's permissions name another.
|
|
387
|
+
propsSpy.mockResolvedValue({
|
|
388
|
+
...buildOnCallAdminProps({
|
|
389
|
+
projectId: otherProjectId,
|
|
390
|
+
userId: callerUserId,
|
|
391
|
+
}),
|
|
392
|
+
tenantId: projectId,
|
|
393
|
+
} as never);
|
|
394
|
+
|
|
395
|
+
const result: RouteCallResult = await callRoute({
|
|
396
|
+
userIds: [userA.toString()],
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
400
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("a request with no tenant header is refused before the body is even parsed", async () => {
|
|
404
|
+
propsSpy.mockResolvedValue({} as never);
|
|
405
|
+
|
|
406
|
+
const result: RouteCallResult = await callRoute({
|
|
407
|
+
userIds: ["obviously-not-a-uuid"],
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
/*
|
|
411
|
+
* The refusal must be the AUTHORISATION one, not the body-validation one:
|
|
412
|
+
* if the body were parsed first, a caller could probe validation behaviour
|
|
413
|
+
* without being authorised at all.
|
|
414
|
+
*/
|
|
415
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
416
|
+
expect((result.thrownToNext as BadDataException).message).toContain(
|
|
417
|
+
"Project ID is required",
|
|
418
|
+
);
|
|
419
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
/*
|
|
424
|
+
* MEMBERSHIP IS NOT ENOUGH.
|
|
425
|
+
*
|
|
426
|
+
* This block is the whole reason the route's authorisation is not a copy of its
|
|
427
|
+
* read siblings'. Every caller below is a genuine, logged-in member of the right
|
|
428
|
+
* project - assertAuthenticatedProjectMember admits all of them - and the
|
|
429
|
+
* question each test asks is whether they may make the product send branded mail
|
|
430
|
+
* to other people in that project's name.
|
|
431
|
+
*
|
|
432
|
+
* The refusal message is asserted to be the router's single refusal sentence, so
|
|
433
|
+
* "you lack the permission" cannot be told apart from "you are not in this
|
|
434
|
+
* project": a caller who could distinguish them could map out other projects'
|
|
435
|
+
* membership from the outside.
|
|
436
|
+
*/
|
|
437
|
+
describe("OnCallReadinessAPI POST /send-setup-reminder - membership is not enough", () => {
|
|
438
|
+
test("an ordinary project member cannot make the product mail their colleagues", async () => {
|
|
439
|
+
propsSpy.mockResolvedValue(
|
|
440
|
+
buildMemberProps({ projectId: projectId, userId: callerUserId }) as never,
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const result: RouteCallResult = await callRoute({
|
|
444
|
+
userIds: [userA.toString()],
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
448
|
+
expect((result.thrownToNext as NotAuthorizedException).message).toBe(
|
|
449
|
+
REFUSAL,
|
|
450
|
+
);
|
|
451
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
test("a read-only viewer is refused", async () => {
|
|
455
|
+
/*
|
|
456
|
+
* The case that made this change necessary. A Viewer can open the readiness
|
|
457
|
+
* page and read every gap on it; that is a disclosure decision already made.
|
|
458
|
+
* It is not a decision to let them mail anybody.
|
|
459
|
+
*/
|
|
460
|
+
propsSpy.mockResolvedValue(
|
|
461
|
+
buildProps({
|
|
462
|
+
projectId: projectId,
|
|
463
|
+
userId: callerUserId,
|
|
464
|
+
permissions: [Permission.Viewer],
|
|
465
|
+
}) as never,
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
const result: RouteCallResult = await callRoute({
|
|
469
|
+
userIds: [userA.toString()],
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
473
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
test("the on-call tiers below Admin are refused", async () => {
|
|
477
|
+
/*
|
|
478
|
+
* OnCallMember and OnCallViewer are held by people who are ON call, which is
|
|
479
|
+
* the population this endpoint mails. Being a recipient of the reminder is
|
|
480
|
+
* not standing to send it.
|
|
481
|
+
*/
|
|
482
|
+
for (const permission of [
|
|
483
|
+
Permission.OnCallMember,
|
|
484
|
+
Permission.OnCallViewer,
|
|
485
|
+
]) {
|
|
486
|
+
jest.clearAllMocks();
|
|
487
|
+
|
|
488
|
+
propsSpy.mockResolvedValue(
|
|
489
|
+
buildProps({
|
|
490
|
+
projectId: projectId,
|
|
491
|
+
userId: callerUserId,
|
|
492
|
+
permissions: [Permission.ProjectMember, permission],
|
|
493
|
+
}) as never,
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
const result: RouteCallResult = await callRoute({
|
|
497
|
+
userIds: [userA.toString()],
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
501
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test("each permission that means 'manages on-call here' is admitted", async () => {
|
|
506
|
+
/*
|
|
507
|
+
* All four are listed rather than just the granular one, and the reason is
|
|
508
|
+
* migration rather than generosity: OneUptime's teams carry ROLES, so a
|
|
509
|
+
* project that predates this feature has an owner holding ProjectOwner and
|
|
510
|
+
* no granular permission at all. A gate that named only
|
|
511
|
+
* EditProjectOnCallDutyPolicy would lock every existing project's owner out
|
|
512
|
+
* of the button on their own readiness page.
|
|
513
|
+
*/
|
|
514
|
+
const admitted: Array<Permission> = [
|
|
515
|
+
Permission.ProjectOwner,
|
|
516
|
+
Permission.ProjectAdmin,
|
|
517
|
+
Permission.OnCallAdmin,
|
|
518
|
+
Permission.EditProjectOnCallDutyPolicy,
|
|
519
|
+
];
|
|
520
|
+
|
|
521
|
+
for (const permission of admitted) {
|
|
522
|
+
jest.clearAllMocks();
|
|
523
|
+
|
|
524
|
+
propsSpy.mockResolvedValue(
|
|
525
|
+
buildProps({
|
|
526
|
+
projectId: projectId,
|
|
527
|
+
userId: callerUserId,
|
|
528
|
+
permissions: [permission],
|
|
529
|
+
}) as never,
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
const result: RouteCallResult = await callRoute({
|
|
533
|
+
userIds: [userA.toString()],
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
expect(result.thrownToNext).toBeUndefined();
|
|
537
|
+
expect(sendRemindersSpy).toHaveBeenCalledTimes(1);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test("a BLOCKED permission is not read as a granted one", async () => {
|
|
542
|
+
/*
|
|
543
|
+
* The dictionary these permissions live in holds grants and denials in one
|
|
544
|
+
* array, told apart only by isBlockPermission. Reading it raw would count a
|
|
545
|
+
* team's explicit "block ProjectAdmin" as a grant of ProjectAdmin - so the
|
|
546
|
+
* admin action that RESTRICTS a team would be the thing that handed it a
|
|
547
|
+
* mail-sending endpoint.
|
|
548
|
+
*/
|
|
549
|
+
propsSpy.mockResolvedValue(
|
|
550
|
+
buildProps({
|
|
551
|
+
projectId: projectId,
|
|
552
|
+
userId: callerUserId,
|
|
553
|
+
permissions: [Permission.ProjectMember],
|
|
554
|
+
blockedPermissions: [
|
|
555
|
+
Permission.ProjectAdmin,
|
|
556
|
+
Permission.OnCallAdmin,
|
|
557
|
+
Permission.EditProjectOnCallDutyPolicy,
|
|
558
|
+
],
|
|
559
|
+
}) as never,
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
const result: RouteCallResult = await callRoute({
|
|
563
|
+
userIds: [userA.toString()],
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
567
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
test("the permission held must be held IN THIS PROJECT", async () => {
|
|
571
|
+
/*
|
|
572
|
+
* An on-call admin of project B, sending project A's tenantid header. The
|
|
573
|
+
* membership gate refuses this first; the assertion that matters is that
|
|
574
|
+
* nothing was sent, because the permission read is keyed on the tenant and
|
|
575
|
+
* would otherwise be reading somebody else's project's grants.
|
|
576
|
+
*/
|
|
577
|
+
const props: DatabaseCommonInteractionProps = buildOnCallAdminProps({
|
|
578
|
+
projectId: otherProjectId,
|
|
579
|
+
userId: callerUserId,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
propsSpy.mockResolvedValue({
|
|
583
|
+
...props,
|
|
584
|
+
tenantId: projectId,
|
|
585
|
+
} as never);
|
|
586
|
+
|
|
587
|
+
const result: RouteCallResult = await callRoute({
|
|
588
|
+
userIds: [userA.toString()],
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
592
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
test("the refusal happens before the body is validated", async () => {
|
|
596
|
+
/*
|
|
597
|
+
* An unauthorised caller must not be able to learn what this endpoint
|
|
598
|
+
* accepts by watching which complaint comes back. A body that is nonsense
|
|
599
|
+
* AND a caller who may not send has to fail as a permission problem.
|
|
600
|
+
*/
|
|
601
|
+
propsSpy.mockResolvedValue(
|
|
602
|
+
buildMemberProps({ projectId: projectId, userId: callerUserId }) as never,
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
const result: RouteCallResult = await callRoute({
|
|
606
|
+
userIds: "not-even-an-array",
|
|
607
|
+
} as unknown as JSONObject);
|
|
608
|
+
|
|
609
|
+
expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
|
|
610
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("the GET routes are deliberately NOT tightened", async () => {
|
|
614
|
+
/*
|
|
615
|
+
* Only the write moved. The reads exist so that somebody inside the project
|
|
616
|
+
* can see who cannot be paged, and requiring on-call administration to LOOK
|
|
617
|
+
* would take the feature away from the people it was built to inform.
|
|
618
|
+
*/
|
|
619
|
+
const readRoutes: Array<RegisteredRoute> = (
|
|
620
|
+
mockRouter.routes as unknown as Array<RegisteredRoute>
|
|
621
|
+
).filter((route: RegisteredRoute): boolean => {
|
|
622
|
+
return route.method === "GET";
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
expect(readRoutes.length).toBe(3);
|
|
626
|
+
|
|
627
|
+
propsSpy.mockResolvedValue(
|
|
628
|
+
buildMemberProps({ projectId: projectId, userId: callerUserId }) as never,
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
const summarySpy: jest.SpyInstance = jest
|
|
632
|
+
.spyOn(OnCallReadinessService, "getReadinessForProject")
|
|
633
|
+
.mockResolvedValue({
|
|
634
|
+
projectId: projectId,
|
|
635
|
+
readyCount: 0,
|
|
636
|
+
partiallyReadyCount: 0,
|
|
637
|
+
notReachableCount: 0,
|
|
638
|
+
isFallbackEnabled: true,
|
|
639
|
+
isTruncated: false,
|
|
640
|
+
users: [],
|
|
641
|
+
} as never);
|
|
642
|
+
|
|
643
|
+
const projectRoute: RegisteredRoute | undefined = readRoutes.find(
|
|
644
|
+
(route: RegisteredRoute): boolean => {
|
|
645
|
+
return route.uri === "/on-call-readiness/project";
|
|
646
|
+
},
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
const req: ExpressRequest = {
|
|
650
|
+
params: {},
|
|
651
|
+
query: {},
|
|
652
|
+
body: {},
|
|
653
|
+
headers: {},
|
|
654
|
+
} as unknown as ExpressRequest;
|
|
655
|
+
|
|
656
|
+
const res: ExpressResponse = {
|
|
657
|
+
send: jest.fn(),
|
|
658
|
+
json: jest.fn(),
|
|
659
|
+
status: jest.fn().mockReturnThis(),
|
|
660
|
+
} as unknown as ExpressResponse;
|
|
661
|
+
|
|
662
|
+
const next: jest.Mock = jest.fn();
|
|
663
|
+
|
|
664
|
+
await projectRoute!.handlerFunction(
|
|
665
|
+
req,
|
|
666
|
+
res,
|
|
667
|
+
next as unknown as NextFunction,
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
// The bare member got their answer. Nothing was refused.
|
|
671
|
+
expect(next).not.toHaveBeenCalled();
|
|
672
|
+
expect(summarySpy).toHaveBeenCalledTimes(1);
|
|
673
|
+
|
|
674
|
+
summarySpy.mockRestore();
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
describe("OnCallReadinessAPI POST /send-setup-reminder - the body is never trusted", () => {
|
|
679
|
+
test("the project comes from the authenticated tenant, not from the body", async () => {
|
|
680
|
+
await callRoute({
|
|
681
|
+
userIds: [userA.toString()],
|
|
682
|
+
// A caller trying to redirect the send at somebody else's project.
|
|
683
|
+
projectId: otherProjectId.toString(),
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
expect(serviceCall().projectId.toString()).toBe(projectId.toString());
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
test("a missing userIds field is refused", async () => {
|
|
690
|
+
const result: RouteCallResult = await callRoute({});
|
|
691
|
+
|
|
692
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
693
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test("an absent body is refused rather than crashing the handler", async () => {
|
|
697
|
+
const result: RouteCallResult = await callRoute(undefined);
|
|
698
|
+
|
|
699
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
700
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
test("a userIds that is not an array is refused", async () => {
|
|
704
|
+
const result: RouteCallResult = await callRoute({
|
|
705
|
+
userIds: userA.toString(),
|
|
706
|
+
} as unknown as JSONObject);
|
|
707
|
+
|
|
708
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
709
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
test("a non-string entry is refused", async () => {
|
|
713
|
+
const result: RouteCallResult = await callRoute({
|
|
714
|
+
userIds: [userA.toString(), 42],
|
|
715
|
+
} as unknown as JSONObject);
|
|
716
|
+
|
|
717
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
718
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
test("an entry that is not a UUID is refused rather than passed through as an id", async () => {
|
|
722
|
+
/*
|
|
723
|
+
* ObjectID's constructor accepts any string. Without this check, arbitrary
|
|
724
|
+
* caller text reaches the mail-sending service and comes back looking like
|
|
725
|
+
* an ordinary stale id.
|
|
726
|
+
*/
|
|
727
|
+
const result: RouteCallResult = await callRoute({
|
|
728
|
+
userIds: ["'; DROP TABLE users; --"],
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
732
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
test("one bad id refuses the whole request rather than silently dropping it", async () => {
|
|
736
|
+
/*
|
|
737
|
+
* Sending to "everyone except the one we could not parse" would be a
|
|
738
|
+
* partial action reported as a complete one.
|
|
739
|
+
*/
|
|
740
|
+
const result: RouteCallResult = await callRoute({
|
|
741
|
+
userIds: [userA.toString(), "not-a-uuid", userB.toString()],
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
745
|
+
expect(sendRemindersSpy).not.toHaveBeenCalled();
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
test("well-formed ids reach the service in the order they were sent", async () => {
|
|
749
|
+
await callRoute({
|
|
750
|
+
userIds: [userA.toString(), userB.toString()],
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
expect(
|
|
754
|
+
serviceCall().userIds.map((id: ObjectID): string => {
|
|
755
|
+
return id.toString();
|
|
756
|
+
}),
|
|
757
|
+
).toEqual([userA.toString(), userB.toString()]);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
test("an empty list is handed to the service, which owns that rule", async () => {
|
|
761
|
+
/*
|
|
762
|
+
* Deliberately NOT rejected here. The "select at least one" rule belongs to
|
|
763
|
+
* the service so it holds for every caller, and duplicating it at the route
|
|
764
|
+
* would let the two drift into disagreeing about what an empty request
|
|
765
|
+
* means.
|
|
766
|
+
*/
|
|
767
|
+
sendRemindersSpy.mockRejectedValue(
|
|
768
|
+
new BadDataException(
|
|
769
|
+
"Select at least one responder to send a setup reminder to.",
|
|
770
|
+
) as never,
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
const result: RouteCallResult = await callRoute({ userIds: [] });
|
|
774
|
+
|
|
775
|
+
expect(sendRemindersSpy).toHaveBeenCalled();
|
|
776
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
describe("OnCallReadinessAPI POST /send-setup-reminder - what comes back", () => {
|
|
781
|
+
test("the payload carries the counts and one entry per user", async () => {
|
|
782
|
+
sendRemindersSpy.mockResolvedValue({
|
|
783
|
+
projectId: projectId,
|
|
784
|
+
requestedCount: 2,
|
|
785
|
+
sentCount: 1,
|
|
786
|
+
skippedCount: 1,
|
|
787
|
+
failedCount: 0,
|
|
788
|
+
results: [
|
|
789
|
+
{
|
|
790
|
+
userId: userA,
|
|
791
|
+
outcome: SetupReminderOutcome.Sent,
|
|
792
|
+
message: "Reminder sent to their account email address.",
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
userId: userB,
|
|
796
|
+
outcome: SetupReminderOutcome.SkippedThrottled,
|
|
797
|
+
message: "Already reminded in the last 24 hours.",
|
|
798
|
+
},
|
|
799
|
+
],
|
|
800
|
+
} as never);
|
|
801
|
+
|
|
802
|
+
await callRoute({
|
|
803
|
+
userIds: [userA.toString(), userB.toString()],
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
const payload: Record<string, unknown> = jsonPayload();
|
|
807
|
+
|
|
808
|
+
expect(payload["requestedCount"]).toBe(2);
|
|
809
|
+
expect(payload["sentCount"]).toBe(1);
|
|
810
|
+
expect(payload["skippedCount"]).toBe(1);
|
|
811
|
+
expect(payload["failedCount"]).toBe(0);
|
|
812
|
+
expect(payload["results"]).toHaveLength(2);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
test("every id crosses the wire as a plain string, never as an ObjectID envelope", async () => {
|
|
816
|
+
/*
|
|
817
|
+
* ObjectID.toJSON() serialises to { _type: "ObjectID", value: "..." }, which
|
|
818
|
+
* is right for model payloads and wrong for a hand-rolled body. The browser
|
|
819
|
+
* matches these against row ids it holds as plain strings.
|
|
820
|
+
*/
|
|
821
|
+
await callRoute({ userIds: [userA.toString()] });
|
|
822
|
+
|
|
823
|
+
const payload: Record<string, unknown> = jsonPayload();
|
|
824
|
+
const results: Array<Record<string, unknown>> = payload["results"] as Array<
|
|
825
|
+
Record<string, unknown>
|
|
826
|
+
>;
|
|
827
|
+
|
|
828
|
+
expect(typeof payload["projectId"]).toBe("string");
|
|
829
|
+
expect(payload["projectId"]).toBe(projectId.toString());
|
|
830
|
+
expect(typeof results[0]!["userId"]).toBe("string");
|
|
831
|
+
expect(results[0]!["userId"]).toBe(userA.toString());
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
test("the outcome is carried as its own value, not flattened to a boolean", async () => {
|
|
835
|
+
sendRemindersSpy.mockResolvedValue({
|
|
836
|
+
projectId: projectId,
|
|
837
|
+
requestedCount: 1,
|
|
838
|
+
sentCount: 0,
|
|
839
|
+
skippedCount: 0,
|
|
840
|
+
failedCount: 1,
|
|
841
|
+
results: [
|
|
842
|
+
{
|
|
843
|
+
userId: userA,
|
|
844
|
+
outcome: SetupReminderOutcome.Failed,
|
|
845
|
+
message: "We could not send this reminder.",
|
|
846
|
+
},
|
|
847
|
+
],
|
|
848
|
+
} as never);
|
|
849
|
+
|
|
850
|
+
await callRoute({ userIds: [userA.toString()] });
|
|
851
|
+
|
|
852
|
+
const results: Array<Record<string, unknown>> = jsonPayload()[
|
|
853
|
+
"results"
|
|
854
|
+
] as Array<Record<string, unknown>>;
|
|
855
|
+
|
|
856
|
+
expect(results[0]!["outcome"]).toBe(SetupReminderOutcome.Failed);
|
|
857
|
+
expect(results[0]!["message"]).toBe("We could not send this reminder.");
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
test("no email address is echoed back in the response", async () => {
|
|
861
|
+
sendRemindersSpy.mockResolvedValue({
|
|
862
|
+
projectId: projectId,
|
|
863
|
+
requestedCount: 1,
|
|
864
|
+
sentCount: 1,
|
|
865
|
+
skippedCount: 0,
|
|
866
|
+
failedCount: 0,
|
|
867
|
+
results: [
|
|
868
|
+
{
|
|
869
|
+
userId: userA,
|
|
870
|
+
outcome: SetupReminderOutcome.Sent,
|
|
871
|
+
message: "Reminder sent to their account email address.",
|
|
872
|
+
},
|
|
873
|
+
],
|
|
874
|
+
} as never);
|
|
875
|
+
|
|
876
|
+
await callRoute({ userIds: [userA.toString()] });
|
|
877
|
+
|
|
878
|
+
const results: Array<Record<string, unknown>> = jsonPayload()[
|
|
879
|
+
"results"
|
|
880
|
+
] as Array<Record<string, unknown>>;
|
|
881
|
+
|
|
882
|
+
expect(Object.keys(results[0]!).sort()).toEqual([
|
|
883
|
+
"message",
|
|
884
|
+
"outcome",
|
|
885
|
+
"userId",
|
|
886
|
+
]);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test("a service failure is handed to the error middleware rather than answered as success", async () => {
|
|
890
|
+
sendRemindersSpy.mockRejectedValue(
|
|
891
|
+
new BadDataException("Project not found.") as never,
|
|
892
|
+
);
|
|
893
|
+
|
|
894
|
+
const result: RouteCallResult = await callRoute({
|
|
895
|
+
userIds: [userA.toString()],
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
expect(result.nextCallCount).toBe(1);
|
|
899
|
+
expect(result.thrownToNext).toBeInstanceOf(BadDataException);
|
|
900
|
+
expect(
|
|
901
|
+
(Response.sendJsonObjectResponse as unknown as jest.Mock).mock.calls,
|
|
902
|
+
).toHaveLength(0);
|
|
903
|
+
});
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
/*
|
|
907
|
+
* Imported for its side effect: OnCallReadinessAPI registers its routes when the
|
|
908
|
+
* MODULE is evaluated (it exports a bare router, not a class), so the reference
|
|
909
|
+
* below is what guarantees the import is not elided as unused.
|
|
910
|
+
*/
|
|
911
|
+
describe("OnCallReadinessAPI - the router under test", () => {
|
|
912
|
+
test("the module exports the router the routes were registered on", () => {
|
|
913
|
+
expect(OnCallReadinessAPI).toBe(mockRouter);
|
|
914
|
+
});
|
|
915
|
+
});
|