@oneuptime/common 12.0.7 → 12.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
- package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
- package/Models/DatabaseModels/AIConversation.ts +32 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
- package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
- package/Models/DatabaseModels/Index.ts +12 -4
- package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +184 -9
- package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
- package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +33 -7
- package/Models/DatabaseModels/NetworkDevice.ts +141 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
- package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
- package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
- package/Models/DatabaseModels/Project.ts +78 -0
- package/Models/DatabaseModels/UserCall.ts +42 -0
- package/Models/DatabaseModels/UserEmail.ts +44 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
- package/Models/DatabaseModels/UserPush.ts +49 -0
- package/Models/DatabaseModels/UserSMS.ts +43 -0
- package/Models/DatabaseModels/UserTelegram.ts +59 -0
- package/Models/DatabaseModels/UserWebhook.ts +52 -0
- package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
- package/Models/DatabaseModels/WorkflowLog.ts +38 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
- package/Server/API/AIChatAPI.ts +315 -1
- package/Server/API/DashboardAPI.ts +217 -1
- package/Server/API/OnCallReadinessAPI.ts +841 -0
- package/Server/API/TeamComplianceAPI.ts +69 -17
- package/Server/API/TelemetryAPI.ts +220 -12
- package/Server/API/UserAPI.ts +16 -1
- package/Server/EnvironmentConfig.ts +52 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +24 -0
- package/Server/Infrastructure/Queue.ts +78 -13
- package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
- package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
- package/Server/Services/AIService.ts +7 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/AlertSeverityService.ts +63 -0
- package/Server/Services/DashboardService.ts +9 -10
- package/Server/Services/DatabaseService.ts +32 -2
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/IncidentSeverityService.ts +76 -0
- package/Server/Services/Index.ts +12 -4
- package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
- package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +7 -4
- package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +203 -21
- package/Server/Services/LogAggregationService.ts +45 -8
- package/Server/Services/MetricAggregationService.ts +121 -0
- package/Server/Services/MetricService.ts +7 -7
- package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
- package/Server/Services/NetworkDeviceLinkService.ts +84 -0
- package/Server/Services/NetworkDeviceService.ts +140 -0
- package/Server/Services/NetworkSiteService.ts +77 -25
- package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
- package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
- package/Server/Services/OnCallReadinessService.ts +2803 -0
- package/Server/Services/OnCallSetupReminderService.ts +955 -0
- package/Server/Services/ProfileAggregationService.ts +123 -0
- package/Server/Services/StatusPageService.ts +9 -10
- package/Server/Services/TeamComplianceService.ts +429 -252
- package/Server/Services/UserCallService.ts +26 -1
- package/Server/Services/UserEmailService.ts +26 -1
- package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
- package/Server/Services/UserNotificationRuleService.ts +3812 -333
- package/Server/Services/UserOnCallLogService.ts +561 -48
- package/Server/Services/UserPushService.ts +29 -0
- package/Server/Services/UserService.ts +11 -0
- package/Server/Services/UserSmsService.ts +26 -1
- package/Server/Services/UserTelegramService.ts +24 -1
- package/Server/Services/UserWebhookService.ts +28 -1
- package/Server/Services/UserWhatsAppService.ts +24 -1
- package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
- package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
- package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
- package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
- package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
- package/Server/Types/Workflow/Components/API/Get.ts +1 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
- package/Server/Types/Workflow/Components/API/Post.ts +1 -1
- package/Server/Types/Workflow/Components/API/Put.ts +1 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
- package/Server/Types/Workflow/Components/Email.ts +25 -7
- package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
- package/Server/Types/Workflow/TriggerCode.ts +12 -0
- package/Server/Types/Workflow/Workflow.ts +5 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
- package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
- package/Server/Utils/AI/Toolbox/Index.ts +48 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
- package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
- package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
- package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
- package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
- package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
- package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
- package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
- package/Server/Utils/ClientIp.ts +221 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
- package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
- package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
- package/Server/Utils/Express.ts +12 -17
- package/Server/Utils/LLM/LLMService.ts +85 -8
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
- package/Server/Utils/SSRFProtection.ts +98 -23
- package/Server/Utils/StartServer.ts +12 -3
- package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
- package/Server/Utils/VM/VMAPI.ts +56 -8
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
- package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
- package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
- package/Tests/Models/InventoryItemModel.test.ts +174 -0
- package/Tests/Models/InventoryItemNaming.test.ts +302 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
- package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
- package/Tests/Server/API/Helpers.ts +24 -15
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
- package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
- package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
- package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +248 -0
- package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
- package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
- package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
- package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
- package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
- package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
- package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
- package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
- package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
- package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
- package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
- package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
- package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
- package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
- package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
- package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
- package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
- package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
- package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
- package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
- package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
- package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
- package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
- package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
- package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
- package/Tests/Server/Utils/ClientIp.test.ts +438 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
- package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
- package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
- package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
- package/Tests/Types/IP/IP.test.ts +263 -0
- package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
- package/Tests/Types/IP/IPv6.test.ts +12 -1
- package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
- package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
- package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
- package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
- package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +491 -0
- package/Tests/Types/Workflow/Templates.test.ts +1218 -0
- package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
- package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
- package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
- package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
- package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
- package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
- package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
- package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
- package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
- package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
- package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +893 -0
- package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +522 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +193 -0
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +388 -0
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +210 -0
- package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
- package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
- package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
- package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
- package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
- package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
- package/Tests/UI/EsbuildConfig.test.ts +607 -0
- package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
- package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
- package/Tests/UI/Utils/Platform.test.ts +147 -0
- package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +727 -0
- package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
- package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
- package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
- package/Tests/__mocks__/bullmq.js +55 -0
- package/Types/AI/AIChatMessageStatus.ts +8 -1
- package/Types/AI/AIChatTypes.ts +12 -0
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
- package/Types/Exception/ExceptionCode.ts +2 -0
- package/Types/Exception/ServiceUnavailableException.ts +8 -0
- package/Types/Exception/TooManyRequestsException.ts +8 -0
- package/Types/IP/IP.ts +93 -47
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +80 -3
- package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
- package/Types/OnCallDutyPolicy/Layer.ts +203 -149
- package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
- package/Types/Permission.ts +193 -0
- package/Types/SerializableObjectDictionary.ts +133 -39
- package/Types/Telemetry/EntityRelationshipType.ts +1 -1
- package/Types/Telemetry/EntitySource.ts +40 -0
- package/Types/Telemetry/EntityType.ts +28 -1
- package/Types/Telemetry/EntityTypeGroups.ts +65 -0
- package/Types/Workflow/Component.ts +29 -0
- package/Types/Workflow/Components/API.ts +35 -0
- package/Types/Workflow/Components/BaseModel.ts +77 -27
- package/Types/Workflow/Components/Discord.ts +1 -0
- package/Types/Workflow/Components/Email.ts +12 -3
- package/Types/Workflow/Components/JavaScript.ts +7 -0
- package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
- package/Types/Workflow/Components/Slack.ts +1 -0
- package/Types/Workflow/Components/Telegram.ts +1 -0
- package/Types/Workflow/StepTrace.ts +181 -0
- package/Types/Workflow/TemplateSyntax.ts +543 -0
- package/Types/Workflow/Templates.ts +2368 -0
- package/UI/Components/Calendar/Calendar.css +43 -0
- package/UI/Components/Calendar/Calendar.tsx +8 -0
- package/UI/Components/Card/Card.tsx +2 -2
- package/UI/Components/Checkbox/Checkbox.tsx +16 -0
- package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +71 -15
- package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
- package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
- package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
- package/UI/Components/Forms/Types/Field.ts +7 -0
- package/UI/Components/Forms/Validation.ts +63 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
- package/UI/Components/Input/Input.tsx +32 -3
- package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
- package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
- package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
- package/UI/Components/LogsViewer/types.ts +23 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
- package/UI/Components/Modal/Modal.tsx +37 -4
- package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
- package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
- package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
- package/UI/Components/Table/Table.tsx +49 -16
- package/UI/Components/Table/TableBody.tsx +53 -28
- package/UI/Components/Table/TableHeader.tsx +13 -0
- package/UI/Components/Table/TableRow.tsx +58 -26
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
- package/UI/Components/Workflow/ArgumentsForm.tsx +341 -8
- package/UI/Components/Workflow/Component.tsx +28 -26
- package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +79 -9
- package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
- package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
- package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
- package/UI/Components/Workflow/GraphLint.ts +628 -0
- package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +528 -0
- package/UI/Components/Workflow/ModelSchema.ts +278 -0
- package/UI/Components/Workflow/RunForm.tsx +41 -7
- package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
- package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
- package/UI/Components/Workflow/UseRunWatch.ts +212 -0
- package/UI/Components/Workflow/Utils.ts +93 -1
- package/UI/Components/Workflow/VariableModal.tsx +6 -2
- package/UI/Components/Workflow/Workflow.tsx +126 -7
- package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
- package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
- package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
- package/UI/Types/LayeredDismissal.ts +31 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
- package/UI/Utils/Platform.ts +149 -0
- package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
- package/UI/esbuild-config.js +22 -1
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +917 -155
- package/Utils/Telemetry/CrossSignalScope.ts +502 -0
- package/Utils/Telemetry/EntityKey.ts +71 -5
- package/Utils/Telemetry/EntityRelationship.ts +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +12 -4
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +212 -29
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +52 -25
- package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +80 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
- package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
- package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +237 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +165 -13
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
- package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +129 -18
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +16 -1
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +45 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +72 -13
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +1 -0
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +54 -0
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/DashboardService.js +10 -9
- package/build/dist/Server/Services/DashboardService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +24 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +12 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +9 -6
- package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +158 -23
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
- package/build/dist/Server/Services/LogAggregationService.js +27 -8
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +80 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +6 -6
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +53 -13
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
- package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
- package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
- package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +11 -10
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceService.js +312 -160
- package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +24 -1
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +24 -1
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +26 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +24 -1
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +22 -1
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +25 -1
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
- package/build/dist/Server/Utils/ClientIp.js +137 -0
- package/build/dist/Server/Utils/ClientIp.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -12
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +82 -21
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatTypes.js +12 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +2 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
- package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
- package/build/dist/Types/IP/IP.js +87 -43
- package/build/dist/Types/IP/IP.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +174 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +133 -39
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +39 -0
- package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +28 -1
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
- package/build/dist/Types/Workflow/Component.js +21 -0
- package/build/dist/Types/Workflow/Component.js.map +1 -1
- package/build/dist/Types/Workflow/Components/API.js +35 -0
- package/build/dist/Types/Workflow/Components/API.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +68 -26
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Discord.js +1 -0
- package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Email.js +12 -3
- package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
- package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Slack.js +1 -0
- package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
- package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
- package/build/dist/Types/Workflow/StepTrace.js +104 -0
- package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js +338 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +2111 -0
- package/build/dist/Types/Workflow/Templates.js.map +1 -0
- package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +47 -17
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +44 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +13 -4
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +31 -5
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +27 -15
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +24 -18
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +9 -1
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +29 -21
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +261 -14
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Component.js +20 -19
- package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +35 -7
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +425 -0
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +320 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js +156 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
- package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
- package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Utils.js +73 -1
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
- package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +92 -7
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
- package/build/dist/UI/Types/LayeredDismissal.js +21 -0
- package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Platform.js +118 -0
- package/build/dist/UI/Utils/Platform.js.map +1 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +661 -126
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +57 -5
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
- package/jest.config.json +1 -0
- package/package.json +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
|
@@ -0,0 +1,1021 @@
|
|
|
1
|
+
import DatabaseConfig from "../../../Server/DatabaseConfig";
|
|
2
|
+
import OnCallDutyPolicyService from "../../../Server/Services/OnCallDutyPolicyService";
|
|
3
|
+
import OnCallReadinessService, {
|
|
4
|
+
ReadinessStatus,
|
|
5
|
+
ReadinessSummary,
|
|
6
|
+
ResponderSource,
|
|
7
|
+
UserReadiness,
|
|
8
|
+
} from "../../../Server/Services/OnCallReadinessService";
|
|
9
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
10
|
+
import logger from "../../../Server/Utils/Logger";
|
|
11
|
+
import URL from "../../../Types/API/URL";
|
|
12
|
+
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
13
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
14
|
+
import { EVERY_WEEK } from "../../../Utils/CronTime";
|
|
15
|
+
import OnCallDutyPolicy from "../../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
16
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
17
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import Path from "path";
|
|
20
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* PHASE 4, the weekly digest.
|
|
24
|
+
*
|
|
25
|
+
* Everything else built for on-call readiness is either reactive (the fallback,
|
|
26
|
+
* which rescues a page at the moment it would otherwise be dropped) or
|
|
27
|
+
* on-demand (the readiness page, which answers the question for whoever thinks
|
|
28
|
+
* to ask it). Nobody thinks to ask it. This job is the only thing in the system
|
|
29
|
+
* that tells a project owner about an unreachable responder WITHOUT being
|
|
30
|
+
* asked, which puts an unusual amount of weight on properties that are easy to
|
|
31
|
+
* get wrong and invisible when they are:
|
|
32
|
+
*
|
|
33
|
+
* 1. IT IS SILENT WHEN THERE IS NOTHING TO SAY. A weekly "everything is fine"
|
|
34
|
+
* mail teaches its recipients to filter it, and the filter is still there
|
|
35
|
+
* the week it says something different. The silence is pinned here as hard
|
|
36
|
+
* as the sending is.
|
|
37
|
+
*
|
|
38
|
+
* 2. IT NAMES THE RIGHT PEOPLE. A digest that lists a Ready responder among
|
|
39
|
+
* the broken ones is a digest an owner learns to distrust, and an owner
|
|
40
|
+
* who distrusts it is an owner who does not read the one that matters.
|
|
41
|
+
*
|
|
42
|
+
* 3. IT NEVER FAILS SILENTLY. ProjectService.getOwners returns [] for a
|
|
43
|
+
* project where no team holds ProjectOwner, and sendEmailToProjectOwners
|
|
44
|
+
* then sends nothing and says nothing - so the last-resort warning about
|
|
45
|
+
* responders nobody can page would itself reach nobody, with no trace
|
|
46
|
+
* anywhere that it had not. That case has to be LOUD in the log even
|
|
47
|
+
* though it is silent in the inbox.
|
|
48
|
+
*
|
|
49
|
+
* 4. IT ESCAPES WHAT IT INTERPOLATES. sendEmailToProjectOwners hands the
|
|
50
|
+
* message to SimpleMessage.hbs, which renders it through a triple stache -
|
|
51
|
+
* i.e. as RAW HTML. Responder names, reason sentences and project names
|
|
52
|
+
* are all user-controlled strings.
|
|
53
|
+
*
|
|
54
|
+
* 5. IT IS REGISTERED. RunCron registers a job purely as a module side
|
|
55
|
+
* effect, so a job file nothing imports is never scheduled. That exact
|
|
56
|
+
* omission was a Phase 1 blocker, and it is invisible to every test that
|
|
57
|
+
* imports the job directly - which is why one test below reads
|
|
58
|
+
* App/FeatureSet/Workers/Index.ts as text.
|
|
59
|
+
*
|
|
60
|
+
* This suite lives beside SeverityRuleBackfill.test.ts, the Phase 1 worker
|
|
61
|
+
* tests, for the same reason they do: the job is an App module that imports
|
|
62
|
+
* Common through the `Common/*` specifier, and Common's tsconfig is the one
|
|
63
|
+
* that maps that specifier back to this tree.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* The job registers itself with RunCron at import time, which would otherwise
|
|
68
|
+
* reach for the Worker queue (and therefore Redis) as a side effect of
|
|
69
|
+
* importing this file. Mocked before the job module is imported, both to keep
|
|
70
|
+
* the import inert and to capture the registration itself.
|
|
71
|
+
*/
|
|
72
|
+
type CronHandler = () => Promise<void>;
|
|
73
|
+
|
|
74
|
+
interface CapturedCronOptions {
|
|
75
|
+
schedule: string;
|
|
76
|
+
runOnStartup: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mockCapturedJobs: Record<string, CronHandler> = {};
|
|
80
|
+
const mockCapturedOptions: Record<string, CapturedCronOptions> = {};
|
|
81
|
+
|
|
82
|
+
jest.mock("../../../../App/FeatureSet/Workers/Utils/Cron", () => {
|
|
83
|
+
return {
|
|
84
|
+
__esModule: true,
|
|
85
|
+
default: jest.fn(
|
|
86
|
+
(
|
|
87
|
+
jobName: string,
|
|
88
|
+
options: CapturedCronOptions,
|
|
89
|
+
runFunction: CronHandler,
|
|
90
|
+
): void => {
|
|
91
|
+
mockCapturedJobs[jobName] = runFunction;
|
|
92
|
+
mockCapturedOptions[jobName] = options;
|
|
93
|
+
},
|
|
94
|
+
),
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Imported AFTER the mock above so the registration lands in it.
|
|
99
|
+
import {
|
|
100
|
+
JOB_NAME,
|
|
101
|
+
MAX_RESPONDERS_LISTED_PER_SECTION,
|
|
102
|
+
ProjectDigest,
|
|
103
|
+
buildProjectDigest,
|
|
104
|
+
findProjectsWithOnCallPolicies,
|
|
105
|
+
sendDigestForProject,
|
|
106
|
+
sendWeeklyReadinessDigests,
|
|
107
|
+
} from "../../../../App/FeatureSet/Workers/Jobs/OnCallDutyPolicy/WeeklyReadinessDigest";
|
|
108
|
+
|
|
109
|
+
const WORKERS_INDEX_PATH: string = Path.resolve(
|
|
110
|
+
__dirname,
|
|
111
|
+
"../../../../App/FeatureSet/Workers/Index.ts",
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const PROJECT_ID: ObjectID = new ObjectID("project-1");
|
|
115
|
+
const OTHER_PROJECT_ID: ObjectID = new ObjectID("project-2");
|
|
116
|
+
|
|
117
|
+
const DASHBOARD_URL: string = "https://oneuptime.example.com/dashboard";
|
|
118
|
+
|
|
119
|
+
type MakeProjectFunction = (data?: {
|
|
120
|
+
id?: ObjectID;
|
|
121
|
+
// null means "this project has no name", which is a different case from "default".
|
|
122
|
+
name?: string | null;
|
|
123
|
+
}) => Project;
|
|
124
|
+
|
|
125
|
+
const makeProject: MakeProjectFunction = (data?: {
|
|
126
|
+
id?: ObjectID;
|
|
127
|
+
name?: string | null;
|
|
128
|
+
}): Project => {
|
|
129
|
+
const project: Project = new Project();
|
|
130
|
+
project.id = data?.id || PROJECT_ID;
|
|
131
|
+
|
|
132
|
+
const name: string | null =
|
|
133
|
+
data && "name" in data ? data.name ?? null : "Acme Production";
|
|
134
|
+
|
|
135
|
+
if (name) {
|
|
136
|
+
project.name = name;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return project;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
type MakePolicyFunction = (
|
|
143
|
+
policyProjectId: ObjectID | undefined,
|
|
144
|
+
) => OnCallDutyPolicy;
|
|
145
|
+
|
|
146
|
+
const makePolicy: MakePolicyFunction = (
|
|
147
|
+
policyProjectId: ObjectID | undefined,
|
|
148
|
+
): OnCallDutyPolicy => {
|
|
149
|
+
const policy: OnCallDutyPolicy = new OnCallDutyPolicy();
|
|
150
|
+
policy.id = new ObjectID(`policy-${policyProjectId?.toString() || "none"}`);
|
|
151
|
+
|
|
152
|
+
if (policyProjectId) {
|
|
153
|
+
policy.projectId = policyProjectId;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return policy;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
type MakeUserReadinessFunction = (
|
|
160
|
+
overrides?: Partial<UserReadiness>,
|
|
161
|
+
) => UserReadiness;
|
|
162
|
+
|
|
163
|
+
const makeUserReadiness: MakeUserReadinessFunction = (
|
|
164
|
+
overrides?: Partial<UserReadiness>,
|
|
165
|
+
): UserReadiness => {
|
|
166
|
+
return {
|
|
167
|
+
userId: new ObjectID("user-1"),
|
|
168
|
+
userName: "Ada Lovelace",
|
|
169
|
+
userEmail: "ada@example.com",
|
|
170
|
+
status: ReadinessStatus.NotReachable,
|
|
171
|
+
methods: [],
|
|
172
|
+
coverage: [],
|
|
173
|
+
reasons: ["No verified notification method - cannot be paged"],
|
|
174
|
+
reachedVia: [ResponderSource.Direct],
|
|
175
|
+
teams: [],
|
|
176
|
+
...overrides,
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
type MakeSummaryFunction = (
|
|
181
|
+
overrides?: Partial<ReadinessSummary>,
|
|
182
|
+
) => ReadinessSummary;
|
|
183
|
+
|
|
184
|
+
const makeSummary: MakeSummaryFunction = (
|
|
185
|
+
overrides?: Partial<ReadinessSummary>,
|
|
186
|
+
): ReadinessSummary => {
|
|
187
|
+
const users: Array<UserReadiness> = overrides?.users || [];
|
|
188
|
+
|
|
189
|
+
const countOf: (status: ReadinessStatus) => number = (
|
|
190
|
+
status: ReadinessStatus,
|
|
191
|
+
): number => {
|
|
192
|
+
return users.filter((user: UserReadiness): boolean => {
|
|
193
|
+
return user.status === status;
|
|
194
|
+
}).length;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
projectId: PROJECT_ID,
|
|
199
|
+
readyCount: countOf(ReadinessStatus.Ready),
|
|
200
|
+
partiallyReadyCount: countOf(ReadinessStatus.PartiallyReady),
|
|
201
|
+
notReachableCount: countOf(ReadinessStatus.NotReachable),
|
|
202
|
+
isFallbackEnabled: true,
|
|
203
|
+
isTruncated: false,
|
|
204
|
+
...overrides,
|
|
205
|
+
users: users,
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
type MakeOwnerFunction = (name: string) => User;
|
|
210
|
+
|
|
211
|
+
const makeOwner: MakeOwnerFunction = (name: string): User => {
|
|
212
|
+
const user: User = new User();
|
|
213
|
+
user.id = new ObjectID(`owner-${name}`);
|
|
214
|
+
|
|
215
|
+
return user;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/* Every spy the suite drives, refreshed per test. */
|
|
219
|
+
let policyFindAllBySpy: jest.SpyInstance;
|
|
220
|
+
let policyFindBySpy: jest.SpyInstance;
|
|
221
|
+
let projectFindAllBySpy: jest.SpyInstance;
|
|
222
|
+
let projectFindBySpy: jest.SpyInstance;
|
|
223
|
+
let getOwnersSpy: jest.SpyInstance;
|
|
224
|
+
let sendEmailSpy: jest.SpyInstance;
|
|
225
|
+
let readinessSpy: jest.SpyInstance;
|
|
226
|
+
let loggerWarnSpy: jest.SpyInstance;
|
|
227
|
+
let loggerErrorSpy: jest.SpyInstance;
|
|
228
|
+
|
|
229
|
+
/* The (projectId, subject, message) of the nth mail the job asked to send. */
|
|
230
|
+
type SentMail = {
|
|
231
|
+
projectId: ObjectID;
|
|
232
|
+
subject: string;
|
|
233
|
+
message: string;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
type GetSentMailFunction = (index: number) => SentMail;
|
|
237
|
+
|
|
238
|
+
const getSentMail: GetSentMailFunction = (index: number): SentMail => {
|
|
239
|
+
const call: Array<unknown> = sendEmailSpy.mock.calls[index] as Array<unknown>;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
projectId: call[0] as ObjectID,
|
|
243
|
+
subject: call[1] as string,
|
|
244
|
+
message: call[2] as string,
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
beforeEach(() => {
|
|
249
|
+
policyFindAllBySpy = jest
|
|
250
|
+
.spyOn(OnCallDutyPolicyService, "findAllBy")
|
|
251
|
+
.mockResolvedValue([]);
|
|
252
|
+
|
|
253
|
+
policyFindBySpy = jest
|
|
254
|
+
.spyOn(OnCallDutyPolicyService, "findBy")
|
|
255
|
+
.mockResolvedValue([]);
|
|
256
|
+
|
|
257
|
+
projectFindAllBySpy = jest
|
|
258
|
+
.spyOn(ProjectService, "findAllBy")
|
|
259
|
+
.mockResolvedValue([]);
|
|
260
|
+
|
|
261
|
+
projectFindBySpy = jest.spyOn(ProjectService, "findBy").mockResolvedValue([]);
|
|
262
|
+
|
|
263
|
+
getOwnersSpy = jest
|
|
264
|
+
.spyOn(ProjectService, "getOwners")
|
|
265
|
+
.mockResolvedValue([makeOwner("first")]);
|
|
266
|
+
|
|
267
|
+
sendEmailSpy = jest
|
|
268
|
+
.spyOn(ProjectService, "sendEmailToProjectOwners")
|
|
269
|
+
.mockResolvedValue(undefined);
|
|
270
|
+
|
|
271
|
+
readinessSpy = jest
|
|
272
|
+
.spyOn(OnCallReadinessService, "getReadinessForProject")
|
|
273
|
+
.mockResolvedValue(makeSummary());
|
|
274
|
+
|
|
275
|
+
jest
|
|
276
|
+
.spyOn(DatabaseConfig, "getDashboardUrl")
|
|
277
|
+
.mockResolvedValue(URL.fromString(DASHBOARD_URL));
|
|
278
|
+
|
|
279
|
+
// Silenced, and asserted on: the log IS the product in the no-owners case.
|
|
280
|
+
jest.spyOn(logger, "debug").mockImplementation((): void => {});
|
|
281
|
+
loggerWarnSpy = jest.spyOn(logger, "warn").mockImplementation((): void => {});
|
|
282
|
+
loggerErrorSpy = jest
|
|
283
|
+
.spyOn(logger, "error")
|
|
284
|
+
.mockImplementation((): void => {});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
afterEach(() => {
|
|
288
|
+
jest.restoreAllMocks();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("WeeklyReadinessDigest - registration", () => {
|
|
292
|
+
test("registers itself with RunCron on a weekly schedule, and never on startup", () => {
|
|
293
|
+
expect(JOB_NAME).toBe("OnCallDutyPolicy:WeeklyReadinessDigest");
|
|
294
|
+
expect(mockCapturedJobs[JOB_NAME]).toBeDefined();
|
|
295
|
+
|
|
296
|
+
const options: CapturedCronOptions | undefined =
|
|
297
|
+
mockCapturedOptions[JOB_NAME];
|
|
298
|
+
|
|
299
|
+
expect(options?.schedule).toBe(EVERY_WEEK);
|
|
300
|
+
|
|
301
|
+
/*
|
|
302
|
+
* runOnStartup would email every project owner in the instance on every
|
|
303
|
+
* worker boot - every deploy, every crash-loop, every scale-up. An owner
|
|
304
|
+
* whose inbox gets three digests during one rollout writes a filter rule,
|
|
305
|
+
* and that filter rule is still there the week the digest matters.
|
|
306
|
+
*/
|
|
307
|
+
expect(options?.runOnStartup).toBe(false);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("the registered cron handler runs the sweep", async () => {
|
|
311
|
+
const handler: CronHandler | undefined = mockCapturedJobs[JOB_NAME];
|
|
312
|
+
|
|
313
|
+
expect(handler).toBeDefined();
|
|
314
|
+
|
|
315
|
+
await handler!();
|
|
316
|
+
|
|
317
|
+
// The sweep starts by asking which projects have an on-call policy at all.
|
|
318
|
+
expect(policyFindAllBySpy).toHaveBeenCalledTimes(1);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("is imported by App/FeatureSet/Workers/Index.ts, without which it never runs", () => {
|
|
322
|
+
/*
|
|
323
|
+
* Not a stylistic check. RunCron registers a job purely as a module side
|
|
324
|
+
* effect, so a job file that nothing imports is never scheduled and cannot
|
|
325
|
+
* be enqueued by name either. Every other test in this file imports the job
|
|
326
|
+
* directly, so every other test would still pass with the import missing;
|
|
327
|
+
* reading the index as TEXT is the only assertion that can fail for the
|
|
328
|
+
* right reason. Phase 1 shipped this exact omission.
|
|
329
|
+
*/
|
|
330
|
+
const indexSource: string = fs.readFileSync(WORKERS_INDEX_PATH, {
|
|
331
|
+
encoding: "utf-8",
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
expect(indexSource).toContain(
|
|
335
|
+
'import "./Jobs/OnCallDutyPolicy/WeeklyReadinessDigest";',
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe("WeeklyReadinessDigest - finding the projects to check", () => {
|
|
341
|
+
test("collapses many policies into one read of their distinct projects", async () => {
|
|
342
|
+
policyFindAllBySpy.mockResolvedValue([
|
|
343
|
+
makePolicy(PROJECT_ID),
|
|
344
|
+
makePolicy(PROJECT_ID),
|
|
345
|
+
makePolicy(OTHER_PROJECT_ID),
|
|
346
|
+
]);
|
|
347
|
+
|
|
348
|
+
projectFindAllBySpy.mockResolvedValue([
|
|
349
|
+
makeProject({ id: PROJECT_ID }),
|
|
350
|
+
makeProject({ id: OTHER_PROJECT_ID, name: "Acme Staging" }),
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
const projects: Array<Project> = await findProjectsWithOnCallPolicies();
|
|
354
|
+
|
|
355
|
+
expect(projects).toHaveLength(2);
|
|
356
|
+
expect(projectFindAllBySpy).toHaveBeenCalledTimes(1);
|
|
357
|
+
|
|
358
|
+
const findBy: Record<string, unknown> = projectFindAllBySpy.mock
|
|
359
|
+
.calls[0]![0] as Record<string, unknown>;
|
|
360
|
+
const query: Record<string, unknown> = findBy["query"] as Record<
|
|
361
|
+
string,
|
|
362
|
+
unknown
|
|
363
|
+
>;
|
|
364
|
+
const includes: Includes = query["_id"] as Includes;
|
|
365
|
+
|
|
366
|
+
expect(includes).toBeInstanceOf(Includes);
|
|
367
|
+
expect(
|
|
368
|
+
(includes.values as Array<ObjectID>).map((value: ObjectID): string => {
|
|
369
|
+
return value.toString();
|
|
370
|
+
}),
|
|
371
|
+
).toEqual([PROJECT_ID.toString(), OTHER_PROJECT_ID.toString()]);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("reads nothing about projects when no project has an on-call policy", async () => {
|
|
375
|
+
policyFindAllBySpy.mockResolvedValue([]);
|
|
376
|
+
|
|
377
|
+
const projects: Array<Project> = await findProjectsWithOnCallPolicies();
|
|
378
|
+
|
|
379
|
+
expect(projects).toEqual([]);
|
|
380
|
+
expect(projectFindAllBySpy).not.toHaveBeenCalled();
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("skips policy rows with no project id rather than querying for undefined", async () => {
|
|
384
|
+
policyFindAllBySpy.mockResolvedValue([
|
|
385
|
+
makePolicy(undefined),
|
|
386
|
+
makePolicy(PROJECT_ID),
|
|
387
|
+
]);
|
|
388
|
+
|
|
389
|
+
await findProjectsWithOnCallPolicies();
|
|
390
|
+
|
|
391
|
+
const findBy: Record<string, unknown> = projectFindAllBySpy.mock
|
|
392
|
+
.calls[0]![0] as Record<string, unknown>;
|
|
393
|
+
const query: Record<string, unknown> = findBy["query"] as Record<
|
|
394
|
+
string,
|
|
395
|
+
unknown
|
|
396
|
+
>;
|
|
397
|
+
const includes: Includes = query["_id"] as Includes;
|
|
398
|
+
|
|
399
|
+
expect(includes.values).toHaveLength(1);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("pages both reads instead of taking whatever the first page returns", async () => {
|
|
403
|
+
/*
|
|
404
|
+
* findAllBy pages until the table is exhausted; findBy stops at its limit.
|
|
405
|
+
* A project whose only policy row falls off the end of a single page is a
|
|
406
|
+
* project whose unreachable responders are reported to nobody - which is
|
|
407
|
+
* indistinguishable from a project that is fine. Same reasoning as
|
|
408
|
+
* OnCallReadinessService's readEveryPage, and the same defect that made
|
|
409
|
+
* TeamComplianceService's `limit: 100` a comfortable lie.
|
|
410
|
+
*/
|
|
411
|
+
policyFindAllBySpy.mockResolvedValue([makePolicy(PROJECT_ID)]);
|
|
412
|
+
projectFindAllBySpy.mockResolvedValue([makeProject()]);
|
|
413
|
+
|
|
414
|
+
await findProjectsWithOnCallPolicies();
|
|
415
|
+
|
|
416
|
+
expect(policyFindAllBySpy).toHaveBeenCalledTimes(1);
|
|
417
|
+
expect(projectFindAllBySpy).toHaveBeenCalledTimes(1);
|
|
418
|
+
expect(policyFindBySpy).not.toHaveBeenCalled();
|
|
419
|
+
expect(projectFindBySpy).not.toHaveBeenCalled();
|
|
420
|
+
|
|
421
|
+
const policyFindBy: Record<string, any> = policyFindAllBySpy.mock
|
|
422
|
+
.calls[0]![0] as Record<string, any>;
|
|
423
|
+
const projectFindBy: Record<string, any> = projectFindAllBySpy.mock
|
|
424
|
+
.calls[0]![0] as Record<string, any>;
|
|
425
|
+
|
|
426
|
+
expect(policyFindBy["props"].isRoot).toBe(true);
|
|
427
|
+
expect(projectFindBy["props"].isRoot).toBe(true);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe("WeeklyReadinessDigest - saying nothing", () => {
|
|
432
|
+
test("sends no digest when every responder is ready", async () => {
|
|
433
|
+
readinessSpy.mockResolvedValue(
|
|
434
|
+
makeSummary({
|
|
435
|
+
users: [
|
|
436
|
+
makeUserReadiness({ status: ReadinessStatus.Ready, reasons: [] }),
|
|
437
|
+
makeUserReadiness({
|
|
438
|
+
userId: new ObjectID("user-2"),
|
|
439
|
+
userName: "Grace Hopper",
|
|
440
|
+
status: ReadinessStatus.Ready,
|
|
441
|
+
reasons: [],
|
|
442
|
+
}),
|
|
443
|
+
],
|
|
444
|
+
}),
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
await sendDigestForProject(makeProject());
|
|
448
|
+
|
|
449
|
+
expect(sendEmailSpy).not.toHaveBeenCalled();
|
|
450
|
+
|
|
451
|
+
/*
|
|
452
|
+
* Not even the owner lookup happens: a healthy project costs one readiness
|
|
453
|
+
* computation and nothing else, which is what makes running this over every
|
|
454
|
+
* project in the instance affordable.
|
|
455
|
+
*/
|
|
456
|
+
expect(getOwnersSpy).not.toHaveBeenCalled();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("sends no digest when the project's policies have no responders at all", async () => {
|
|
460
|
+
readinessSpy.mockResolvedValue(makeSummary({ users: [] }));
|
|
461
|
+
|
|
462
|
+
await sendDigestForProject(makeProject());
|
|
463
|
+
|
|
464
|
+
expect(sendEmailSpy).not.toHaveBeenCalled();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test("warns in the log, and still sends nothing, when a clean result came from a truncated read", async () => {
|
|
468
|
+
/*
|
|
469
|
+
* "We found no problems" and "we could not finish looking" are the same
|
|
470
|
+
* silence in an inbox. There is nobody to name, so there is no digest to
|
|
471
|
+
* send - but the difference has to survive somewhere, and the worker log is
|
|
472
|
+
* the only place left.
|
|
473
|
+
*/
|
|
474
|
+
readinessSpy.mockResolvedValue(
|
|
475
|
+
makeSummary({ users: [], isTruncated: true }),
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
await sendDigestForProject(makeProject());
|
|
479
|
+
|
|
480
|
+
expect(sendEmailSpy).not.toHaveBeenCalled();
|
|
481
|
+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
|
|
482
|
+
expect(loggerWarnSpy.mock.calls[0]![0]).toContain(PROJECT_ID.toString());
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
describe("WeeklyReadinessDigest - saying something", () => {
|
|
487
|
+
test("emails the owners, naming the unreachable responder and their reasons", async () => {
|
|
488
|
+
readinessSpy.mockResolvedValue(
|
|
489
|
+
makeSummary({
|
|
490
|
+
users: [
|
|
491
|
+
makeUserReadiness({
|
|
492
|
+
userName: "Ada Lovelace",
|
|
493
|
+
userEmail: "ada@example.com",
|
|
494
|
+
reasons: [
|
|
495
|
+
"No verified notification method - cannot be paged",
|
|
496
|
+
"Added SMS but never verified - unverified methods are never used",
|
|
497
|
+
],
|
|
498
|
+
}),
|
|
499
|
+
],
|
|
500
|
+
}),
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
await sendDigestForProject(makeProject());
|
|
504
|
+
|
|
505
|
+
expect(sendEmailSpy).toHaveBeenCalledTimes(1);
|
|
506
|
+
|
|
507
|
+
const mail: SentMail = getSentMail(0);
|
|
508
|
+
|
|
509
|
+
expect(mail.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
510
|
+
expect(mail.subject).toContain("[Action Required]");
|
|
511
|
+
expect(mail.subject).toContain("1 on-call responder");
|
|
512
|
+
expect(mail.subject).toContain("Acme Production");
|
|
513
|
+
expect(mail.subject).toContain("cannot be paged");
|
|
514
|
+
|
|
515
|
+
expect(mail.message).toContain("Ada Lovelace");
|
|
516
|
+
expect(mail.message).toContain("ada@example.com");
|
|
517
|
+
expect(mail.message).toContain(
|
|
518
|
+
"No verified notification method - cannot be paged",
|
|
519
|
+
);
|
|
520
|
+
expect(mail.message).toContain(
|
|
521
|
+
"Added SMS but never verified - unverified methods are never used",
|
|
522
|
+
);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test("links to that project's On-Call > Readiness page", async () => {
|
|
526
|
+
readinessSpy.mockResolvedValue(
|
|
527
|
+
makeSummary({ users: [makeUserReadiness()] }),
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
await sendDigestForProject(makeProject());
|
|
531
|
+
|
|
532
|
+
const mail: SentMail = getSentMail(0);
|
|
533
|
+
|
|
534
|
+
expect(mail.message).toContain(
|
|
535
|
+
`${DASHBOARD_URL}/${PROJECT_ID.toString()}/on-call-duty/readiness`,
|
|
536
|
+
);
|
|
537
|
+
expect(mail.message).toContain("<a href=");
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test("never names a responder who is ready", async () => {
|
|
541
|
+
readinessSpy.mockResolvedValue(
|
|
542
|
+
makeSummary({
|
|
543
|
+
users: [
|
|
544
|
+
makeUserReadiness({ userName: "Ada Lovelace" }),
|
|
545
|
+
makeUserReadiness({
|
|
546
|
+
userId: new ObjectID("user-2"),
|
|
547
|
+
userName: "Grace Hopper",
|
|
548
|
+
status: ReadinessStatus.Ready,
|
|
549
|
+
reasons: [],
|
|
550
|
+
}),
|
|
551
|
+
],
|
|
552
|
+
}),
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
await sendDigestForProject(makeProject());
|
|
556
|
+
|
|
557
|
+
const mail: SentMail = getSentMail(0);
|
|
558
|
+
|
|
559
|
+
expect(mail.message).toContain("Ada Lovelace");
|
|
560
|
+
expect(mail.message).not.toContain("Grace Hopper");
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test("separates 'cannot be paged at all' from 'reachable, but with gaps', worst first", async () => {
|
|
564
|
+
readinessSpy.mockResolvedValue(
|
|
565
|
+
makeSummary({
|
|
566
|
+
users: [
|
|
567
|
+
makeUserReadiness({
|
|
568
|
+
userName: "Ada Lovelace",
|
|
569
|
+
status: ReadinessStatus.NotReachable,
|
|
570
|
+
}),
|
|
571
|
+
makeUserReadiness({
|
|
572
|
+
userId: new ObjectID("user-2"),
|
|
573
|
+
userName: "Grace Hopper",
|
|
574
|
+
status: ReadinessStatus.PartiallyReady,
|
|
575
|
+
reasons: [
|
|
576
|
+
"No rules for Sev1 incidents - pages fall back to Email, Push",
|
|
577
|
+
],
|
|
578
|
+
}),
|
|
579
|
+
],
|
|
580
|
+
}),
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
await sendDigestForProject(makeProject());
|
|
584
|
+
|
|
585
|
+
const mail: SentMail = getSentMail(0);
|
|
586
|
+
|
|
587
|
+
expect(mail.message).toContain("Cannot be paged at all (1)");
|
|
588
|
+
expect(mail.message).toContain("Reachable, but with gaps (1)");
|
|
589
|
+
expect(mail.message.indexOf("Cannot be paged at all")).toBeLessThan(
|
|
590
|
+
mail.message.indexOf("Reachable, but with gaps"),
|
|
591
|
+
);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("drops the [Action Required] prefix when nobody is fully unreachable", async () => {
|
|
595
|
+
readinessSpy.mockResolvedValue(
|
|
596
|
+
makeSummary({
|
|
597
|
+
users: [
|
|
598
|
+
makeUserReadiness({
|
|
599
|
+
status: ReadinessStatus.PartiallyReady,
|
|
600
|
+
reasons: ["No rules for Sev1 incidents - pages fall back to Email"],
|
|
601
|
+
}),
|
|
602
|
+
],
|
|
603
|
+
}),
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
await sendDigestForProject(makeProject());
|
|
607
|
+
|
|
608
|
+
const mail: SentMail = getSentMail(0);
|
|
609
|
+
|
|
610
|
+
expect(mail.subject).not.toContain("[Action Required]");
|
|
611
|
+
expect(mail.subject).toContain("gaps in their notification rules");
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
test("calls the gaps DROPPED pages, not fallback pages, when the project has the fallback switched off", async () => {
|
|
615
|
+
/*
|
|
616
|
+
* The same coverage gap means two different things depending on one project
|
|
617
|
+
* setting: with the fallback on it is an untidy page on the wrong channel,
|
|
618
|
+
* with it off it is silence. Those must not share a sentence.
|
|
619
|
+
*/
|
|
620
|
+
readinessSpy.mockResolvedValue(
|
|
621
|
+
makeSummary({
|
|
622
|
+
isFallbackEnabled: false,
|
|
623
|
+
users: [
|
|
624
|
+
makeUserReadiness({
|
|
625
|
+
status: ReadinessStatus.PartiallyReady,
|
|
626
|
+
reasons: [
|
|
627
|
+
"No rules for Sev1 incidents - pages are dropped because on-call fallback is disabled for this project",
|
|
628
|
+
],
|
|
629
|
+
}),
|
|
630
|
+
],
|
|
631
|
+
}),
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
await sendDigestForProject(makeProject());
|
|
635
|
+
|
|
636
|
+
const mail: SentMail = getSentMail(0);
|
|
637
|
+
|
|
638
|
+
expect(mail.message).toContain("switched OFF");
|
|
639
|
+
expect(mail.message).toContain("silent pages");
|
|
640
|
+
expect(mail.message).not.toContain(
|
|
641
|
+
"fall back to whatever they have verified",
|
|
642
|
+
);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
test("admits in the mail when the readiness check did not finish reading the project", async () => {
|
|
646
|
+
readinessSpy.mockResolvedValue(
|
|
647
|
+
makeSummary({ isTruncated: true, users: [makeUserReadiness()] }),
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
await sendDigestForProject(makeProject());
|
|
651
|
+
|
|
652
|
+
expect(getSentMail(0).message).toContain("did not finish reading");
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
test("falls back to a generic project name rather than emailing about 'undefined'", async () => {
|
|
656
|
+
readinessSpy.mockResolvedValue(
|
|
657
|
+
makeSummary({ users: [makeUserReadiness()] }),
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
await sendDigestForProject(makeProject({ id: PROJECT_ID, name: null }));
|
|
661
|
+
|
|
662
|
+
const mail: SentMail = getSentMail(0);
|
|
663
|
+
|
|
664
|
+
expect(mail.subject).toContain("your project");
|
|
665
|
+
expect(mail.message).toContain("your project");
|
|
666
|
+
expect(mail.subject).not.toContain("undefined");
|
|
667
|
+
expect(mail.message).not.toContain("undefined");
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test("does nothing for a project row with no id", async () => {
|
|
671
|
+
/*
|
|
672
|
+
* Built without an id rather than by clearing one: the model's id setter
|
|
673
|
+
* ignores a null, so an "unset" id can only be produced by never setting
|
|
674
|
+
* it - which is exactly the shape a partial select would hand back.
|
|
675
|
+
*/
|
|
676
|
+
const project: Project = new Project();
|
|
677
|
+
project.name = "Acme Production";
|
|
678
|
+
|
|
679
|
+
await sendDigestForProject(project);
|
|
680
|
+
|
|
681
|
+
expect(readinessSpy).not.toHaveBeenCalled();
|
|
682
|
+
expect(sendEmailSpy).not.toHaveBeenCalled();
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
describe("WeeklyReadinessDigest - nobody to tell", () => {
|
|
687
|
+
test("logs an error, loudly, when the project has no owners to send to", async () => {
|
|
688
|
+
/*
|
|
689
|
+
* getOwners returns [] when no team in the project holds ProjectOwner, and
|
|
690
|
+
* sendEmailToProjectOwners then returns without sending and without
|
|
691
|
+
* complaining. Returning silently here would mean the last-resort warning
|
|
692
|
+
* about responders nobody can page itself reaches nobody, and nothing
|
|
693
|
+
* anywhere records that it did not.
|
|
694
|
+
*/
|
|
695
|
+
getOwnersSpy.mockResolvedValue([]);
|
|
696
|
+
|
|
697
|
+
readinessSpy.mockResolvedValue(
|
|
698
|
+
makeSummary({
|
|
699
|
+
users: [
|
|
700
|
+
makeUserReadiness(),
|
|
701
|
+
makeUserReadiness({
|
|
702
|
+
userId: new ObjectID("user-2"),
|
|
703
|
+
userName: "Grace Hopper",
|
|
704
|
+
status: ReadinessStatus.PartiallyReady,
|
|
705
|
+
reasons: ["No rules for Sev1 incidents - pages fall back to Email"],
|
|
706
|
+
}),
|
|
707
|
+
],
|
|
708
|
+
}),
|
|
709
|
+
);
|
|
710
|
+
|
|
711
|
+
await sendDigestForProject(makeProject());
|
|
712
|
+
|
|
713
|
+
expect(sendEmailSpy).not.toHaveBeenCalled();
|
|
714
|
+
expect(loggerErrorSpy).toHaveBeenCalledTimes(1);
|
|
715
|
+
|
|
716
|
+
const logged: string = loggerErrorSpy.mock.calls[0]![0] as string;
|
|
717
|
+
|
|
718
|
+
expect(logged).toContain(PROJECT_ID.toString());
|
|
719
|
+
expect(logged).toContain("ProjectOwner");
|
|
720
|
+
|
|
721
|
+
// The counts have to survive into the log, because they survive nowhere else.
|
|
722
|
+
expect(logged).toContain("1 responder(s) who cannot be paged");
|
|
723
|
+
expect(logged).toContain("1 with gaps");
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
test("does not look for owners at all when there is nothing to tell them", async () => {
|
|
727
|
+
getOwnersSpy.mockResolvedValue([]);
|
|
728
|
+
|
|
729
|
+
readinessSpy.mockResolvedValue(
|
|
730
|
+
makeSummary({
|
|
731
|
+
users: [
|
|
732
|
+
makeUserReadiness({ status: ReadinessStatus.Ready, reasons: [] }),
|
|
733
|
+
],
|
|
734
|
+
}),
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
await sendDigestForProject(makeProject());
|
|
738
|
+
|
|
739
|
+
expect(getOwnersSpy).not.toHaveBeenCalled();
|
|
740
|
+
expect(loggerErrorSpy).not.toHaveBeenCalled();
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
test("sends through sendEmailToProjectOwners once the owners exist", async () => {
|
|
744
|
+
getOwnersSpy.mockResolvedValue([makeOwner("first"), makeOwner("second")]);
|
|
745
|
+
|
|
746
|
+
readinessSpy.mockResolvedValue(
|
|
747
|
+
makeSummary({ users: [makeUserReadiness()] }),
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
await sendDigestForProject(makeProject());
|
|
751
|
+
|
|
752
|
+
/*
|
|
753
|
+
* One call, not one per owner: fanning the message out to each owner is
|
|
754
|
+
* ProjectService's job, and duplicating that loop here would duplicate its
|
|
755
|
+
* per-owner error handling too.
|
|
756
|
+
*/
|
|
757
|
+
expect(sendEmailSpy).toHaveBeenCalledTimes(1);
|
|
758
|
+
expect(loggerErrorSpy).not.toHaveBeenCalled();
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
describe("WeeklyReadinessDigest - building the message", () => {
|
|
763
|
+
test("escapes every user-controlled value it interpolates into the raw-HTML body", () => {
|
|
764
|
+
/*
|
|
765
|
+
* ProjectService.sendEmailToProjectOwners hands `message` to
|
|
766
|
+
* SimpleMessage.hbs, which renders it through a triple stache - as RAW
|
|
767
|
+
* HTML. A responder called `<script>` must read as a strange name.
|
|
768
|
+
*/
|
|
769
|
+
const digest: ProjectDigest = buildProjectDigest({
|
|
770
|
+
projectName: "<b>Acme</b> & Co",
|
|
771
|
+
notReachable: [
|
|
772
|
+
makeUserReadiness({
|
|
773
|
+
userName: '<script>alert("xss")</script>',
|
|
774
|
+
userEmail: "evil@example.com",
|
|
775
|
+
reasons: ["<img src=x onerror=alert(1)>"],
|
|
776
|
+
}),
|
|
777
|
+
],
|
|
778
|
+
partiallyReady: [],
|
|
779
|
+
isFallbackEnabled: true,
|
|
780
|
+
isTruncated: false,
|
|
781
|
+
readinessLink: `${DASHBOARD_URL}/${PROJECT_ID.toString()}/on-call-duty/readiness`,
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
expect(digest.messageHtml).not.toContain("<script>");
|
|
785
|
+
expect(digest.messageHtml).not.toContain("<img src=x");
|
|
786
|
+
expect(digest.messageHtml).toContain("<script>");
|
|
787
|
+
expect(digest.messageHtml).toContain("<img src=x");
|
|
788
|
+
expect(digest.messageHtml).toContain("<b>Acme</b> & Co");
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
test("escapes a hostile responder name in the PARTIALLY READY section too", () => {
|
|
792
|
+
const digest: ProjectDigest = buildProjectDigest({
|
|
793
|
+
projectName: "Acme Production",
|
|
794
|
+
notReachable: [],
|
|
795
|
+
partiallyReady: [
|
|
796
|
+
makeUserReadiness({
|
|
797
|
+
status: ReadinessStatus.PartiallyReady,
|
|
798
|
+
userName: "<svg onload=alert(1)>",
|
|
799
|
+
reasons: ["<b>not a tag</b>"],
|
|
800
|
+
}),
|
|
801
|
+
],
|
|
802
|
+
isFallbackEnabled: true,
|
|
803
|
+
isTruncated: false,
|
|
804
|
+
readinessLink: DASHBOARD_URL,
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
expect(digest.messageHtml).not.toContain("<svg");
|
|
808
|
+
expect(digest.messageHtml).toContain("<svg onload=alert(1)>");
|
|
809
|
+
expect(digest.messageHtml).toContain("<b>not a tag</b>");
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
test("leaves the subject RAW, because Handlebars escapes it and SMTP does not want it escaped", () => {
|
|
813
|
+
/*
|
|
814
|
+
* The subject is rendered through {{title}}, which Handlebars escapes on
|
|
815
|
+
* its own, and is also used verbatim as the SMTP subject header. Escaping
|
|
816
|
+
* here would double-escape the first and corrupt the second.
|
|
817
|
+
*/
|
|
818
|
+
const digest: ProjectDigest = buildProjectDigest({
|
|
819
|
+
projectName: "Acme & Co",
|
|
820
|
+
notReachable: [makeUserReadiness()],
|
|
821
|
+
partiallyReady: [],
|
|
822
|
+
isFallbackEnabled: true,
|
|
823
|
+
isTruncated: false,
|
|
824
|
+
readinessLink: DASHBOARD_URL,
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
expect(digest.subject).toContain("Acme & Co");
|
|
828
|
+
expect(digest.subject).not.toContain("&");
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
test("caps each section but never understates the count", () => {
|
|
832
|
+
const many: Array<UserReadiness> = [];
|
|
833
|
+
|
|
834
|
+
for (let index: number = 0; index < 25; index++) {
|
|
835
|
+
many.push(
|
|
836
|
+
makeUserReadiness({
|
|
837
|
+
userId: new ObjectID(`user-${index}`),
|
|
838
|
+
// Zero-padded so alphabetical order is also numeric order here.
|
|
839
|
+
userName: `Responder ${index.toString().padStart(2, "0")}`,
|
|
840
|
+
}),
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const digest: ProjectDigest = buildProjectDigest({
|
|
845
|
+
projectName: "Acme Production",
|
|
846
|
+
notReachable: many,
|
|
847
|
+
partiallyReady: [],
|
|
848
|
+
isFallbackEnabled: true,
|
|
849
|
+
isTruncated: false,
|
|
850
|
+
readinessLink: DASHBOARD_URL,
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
expect(MAX_RESPONDERS_LISTED_PER_SECTION).toBe(20);
|
|
854
|
+
|
|
855
|
+
// The heading counts all 25, even though the list stops at 20.
|
|
856
|
+
expect(digest.messageHtml).toContain("Cannot be paged at all (25)");
|
|
857
|
+
expect(digest.subject).toContain("25 on-call responders");
|
|
858
|
+
|
|
859
|
+
expect(digest.messageHtml).toContain("Responder 00");
|
|
860
|
+
expect(digest.messageHtml).toContain("Responder 19");
|
|
861
|
+
expect(digest.messageHtml).not.toContain("Responder 20");
|
|
862
|
+
expect(digest.messageHtml).toContain("and 5 more");
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
test("names one responder in the singular and several in the plural", () => {
|
|
866
|
+
const one: ProjectDigest = buildProjectDigest({
|
|
867
|
+
projectName: "Acme Production",
|
|
868
|
+
notReachable: [makeUserReadiness()],
|
|
869
|
+
partiallyReady: [],
|
|
870
|
+
isFallbackEnabled: true,
|
|
871
|
+
isTruncated: false,
|
|
872
|
+
readinessLink: DASHBOARD_URL,
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
const two: ProjectDigest = buildProjectDigest({
|
|
876
|
+
projectName: "Acme Production",
|
|
877
|
+
notReachable: [
|
|
878
|
+
makeUserReadiness(),
|
|
879
|
+
makeUserReadiness({ userId: new ObjectID("user-2") }),
|
|
880
|
+
],
|
|
881
|
+
partiallyReady: [],
|
|
882
|
+
isFallbackEnabled: true,
|
|
883
|
+
isTruncated: false,
|
|
884
|
+
readinessLink: DASHBOARD_URL,
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
expect(one.subject).toContain("1 on-call responder in");
|
|
888
|
+
expect(two.subject).toContain("2 on-call responders in");
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
test("omits the parenthesised email when the responder has none", () => {
|
|
892
|
+
const digest: ProjectDigest = buildProjectDigest({
|
|
893
|
+
projectName: "Acme Production",
|
|
894
|
+
notReachable: [
|
|
895
|
+
makeUserReadiness({ userName: "Ada Lovelace", userEmail: "" }),
|
|
896
|
+
],
|
|
897
|
+
partiallyReady: [],
|
|
898
|
+
isFallbackEnabled: true,
|
|
899
|
+
isTruncated: false,
|
|
900
|
+
readinessLink: DASHBOARD_URL,
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
expect(digest.messageHtml).toContain("<b>Ada Lovelace</b>");
|
|
904
|
+
expect(digest.messageHtml).not.toContain("Ada Lovelace</b> (");
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
test("says out loud that silence means everything is fine", () => {
|
|
908
|
+
/*
|
|
909
|
+
* The framing is load-bearing: an owner who knows this only arrives when
|
|
910
|
+
* something is broken reads it differently from one who assumes it is a
|
|
911
|
+
* routine weekly report they can skim.
|
|
912
|
+
*/
|
|
913
|
+
const digest: ProjectDigest = buildProjectDigest({
|
|
914
|
+
projectName: "Acme Production",
|
|
915
|
+
notReachable: [makeUserReadiness()],
|
|
916
|
+
partiallyReady: [],
|
|
917
|
+
isFallbackEnabled: true,
|
|
918
|
+
isTruncated: false,
|
|
919
|
+
readinessLink: DASHBOARD_URL,
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
expect(digest.messageHtml).toContain(
|
|
923
|
+
"a week with nothing wrong is a week with no email",
|
|
924
|
+
);
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
describe("WeeklyReadinessDigest - the weekly sweep", () => {
|
|
929
|
+
test("lists responders alphabetically so an unchanged week looks unchanged", async () => {
|
|
930
|
+
readinessSpy.mockResolvedValue(
|
|
931
|
+
makeSummary({
|
|
932
|
+
users: [
|
|
933
|
+
makeUserReadiness({
|
|
934
|
+
userId: new ObjectID("user-1"),
|
|
935
|
+
userName: "Zoe Zhang",
|
|
936
|
+
}),
|
|
937
|
+
makeUserReadiness({
|
|
938
|
+
userId: new ObjectID("user-2"),
|
|
939
|
+
userName: "Ada Lovelace",
|
|
940
|
+
}),
|
|
941
|
+
makeUserReadiness({
|
|
942
|
+
userId: new ObjectID("user-3"),
|
|
943
|
+
userName: "Marie Curie",
|
|
944
|
+
}),
|
|
945
|
+
],
|
|
946
|
+
}),
|
|
947
|
+
);
|
|
948
|
+
|
|
949
|
+
await sendDigestForProject(makeProject());
|
|
950
|
+
|
|
951
|
+
const message: string = getSentMail(0).message;
|
|
952
|
+
|
|
953
|
+
expect(message.indexOf("Ada Lovelace")).toBeLessThan(
|
|
954
|
+
message.indexOf("Marie Curie"),
|
|
955
|
+
);
|
|
956
|
+
expect(message.indexOf("Marie Curie")).toBeLessThan(
|
|
957
|
+
message.indexOf("Zoe Zhang"),
|
|
958
|
+
);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
test("walks every project with a policy, and asks about each one exactly once", async () => {
|
|
962
|
+
policyFindAllBySpy.mockResolvedValue([
|
|
963
|
+
makePolicy(PROJECT_ID),
|
|
964
|
+
makePolicy(OTHER_PROJECT_ID),
|
|
965
|
+
]);
|
|
966
|
+
|
|
967
|
+
projectFindAllBySpy.mockResolvedValue([
|
|
968
|
+
makeProject({ id: PROJECT_ID }),
|
|
969
|
+
makeProject({ id: OTHER_PROJECT_ID, name: "Acme Staging" }),
|
|
970
|
+
]);
|
|
971
|
+
|
|
972
|
+
readinessSpy.mockResolvedValue(
|
|
973
|
+
makeSummary({ users: [makeUserReadiness()] }),
|
|
974
|
+
);
|
|
975
|
+
|
|
976
|
+
await sendWeeklyReadinessDigests();
|
|
977
|
+
|
|
978
|
+
expect(readinessSpy).toHaveBeenCalledTimes(2);
|
|
979
|
+
expect(sendEmailSpy).toHaveBeenCalledTimes(2);
|
|
980
|
+
expect(getSentMail(1).subject).toContain("Acme Staging");
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
test("does nothing at all when no project has an on-call policy", async () => {
|
|
984
|
+
policyFindAllBySpy.mockResolvedValue([]);
|
|
985
|
+
|
|
986
|
+
await sendWeeklyReadinessDigests();
|
|
987
|
+
|
|
988
|
+
expect(readinessSpy).not.toHaveBeenCalled();
|
|
989
|
+
expect(sendEmailSpy).not.toHaveBeenCalled();
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
test("one project that throws does not stop the rest being told", async () => {
|
|
993
|
+
policyFindAllBySpy.mockResolvedValue([
|
|
994
|
+
makePolicy(PROJECT_ID),
|
|
995
|
+
makePolicy(OTHER_PROJECT_ID),
|
|
996
|
+
]);
|
|
997
|
+
|
|
998
|
+
projectFindAllBySpy.mockResolvedValue([
|
|
999
|
+
makeProject({ id: PROJECT_ID }),
|
|
1000
|
+
makeProject({ id: OTHER_PROJECT_ID, name: "Acme Staging" }),
|
|
1001
|
+
]);
|
|
1002
|
+
|
|
1003
|
+
readinessSpy.mockImplementation(
|
|
1004
|
+
async (askedProjectId: ObjectID): Promise<ReadinessSummary> => {
|
|
1005
|
+
if (askedProjectId.toString() === PROJECT_ID.toString()) {
|
|
1006
|
+
throw new Error("readiness read blew up");
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return makeSummary({ users: [makeUserReadiness()] });
|
|
1010
|
+
},
|
|
1011
|
+
);
|
|
1012
|
+
|
|
1013
|
+
await sendWeeklyReadinessDigests();
|
|
1014
|
+
|
|
1015
|
+
expect(sendEmailSpy).toHaveBeenCalledTimes(1);
|
|
1016
|
+
expect(getSentMail(0).projectId.toString()).toBe(
|
|
1017
|
+
OTHER_PROJECT_ID.toString(),
|
|
1018
|
+
);
|
|
1019
|
+
expect(loggerErrorSpy).toHaveBeenCalled();
|
|
1020
|
+
});
|
|
1021
|
+
});
|