@oneuptime/common 12.0.32 → 12.0.34
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/RumSession.ts +117 -1
- package/Models/AnalyticsModels/RumSessionChunk.ts +13 -0
- package/Models/AnalyticsModels/Span.ts +14 -0
- package/Models/DatabaseModels/AlertCustomField.ts +77 -3
- package/Models/DatabaseModels/ApiKeyPermission.ts +3 -27
- package/Models/DatabaseModels/EnterpriseLicense.ts +56 -0
- package/Models/DatabaseModels/IncidentCustomField.ts +77 -3
- package/Models/DatabaseModels/Index.ts +10 -0
- package/Models/DatabaseModels/InventoryItemCustomField.ts +77 -3
- package/Models/DatabaseModels/Monitor.ts +76 -0
- package/Models/DatabaseModels/MonitorCustomField.ts +77 -3
- package/Models/DatabaseModels/NetworkAlertPolicy.ts +690 -0
- package/Models/DatabaseModels/NetworkDevice.ts +176 -1
- package/Models/DatabaseModels/NetworkSite.ts +201 -2
- package/Models/DatabaseModels/NetworkSiteType.ts +102 -3
- package/Models/DatabaseModels/NetworkSnmpCredentialProfile.ts +841 -0
- package/Models/DatabaseModels/OnCallDutyPolicyCustomField.ts +77 -3
- package/Models/DatabaseModels/RumApplication.ts +6 -6
- package/Models/DatabaseModels/ScheduledMaintenanceCustomField.ts +77 -3
- package/Models/DatabaseModels/StatusPageCustomField.ts +77 -3
- package/Models/DatabaseModels/TeamCustomField.ts +77 -3
- package/Models/DatabaseModels/TeamMember.ts +0 -9
- package/Models/DatabaseModels/TeamMemberCustomField.ts +77 -3
- package/Models/DatabaseModels/TeamPermission.ts +3 -24
- package/Models/DatabaseModels/TelemetryException.ts +119 -0
- package/Models/DatabaseModels/TelemetryIngestionKey.ts +260 -0
- package/Models/DatabaseModels/UserNotificationEmailRollupBatch.ts +334 -0
- package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +370 -0
- package/Models/DatabaseModels/UserNotificationEmailRollupSetting.ts +282 -0
- package/Models/DatabaseModels/UserTelegram.ts +0 -4
- package/Scripts/benchmark-fanin-capacity.js +181 -0
- package/Server/API/EnterpriseLicenseAPI.ts +391 -64
- package/Server/API/LlmProviderAPI.ts +137 -22
- package/Server/API/TelemetryAPI.ts +1284 -164
- package/Server/API/UserNotificationSettingAPI.ts +55 -0
- package/Server/API/UserTelegramAPI.ts +48 -4
- package/Server/EnvironmentConfig.ts +49 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.ts +55 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.ts +124 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.ts +132 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.ts +191 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.ts +139 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.ts +121 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
- package/Server/Infrastructure/QueueWorker.ts +59 -23
- package/Server/Infrastructure/Semaphore.ts +2 -0
- package/Server/Middleware/BearerTokenAuthorization.ts +12 -0
- package/Server/Middleware/ProjectAuthorization.ts +11 -0
- package/Server/Middleware/SCIMAuthorization.ts +12 -0
- package/Server/Middleware/TelemetryIngest.ts +464 -7
- package/Server/Services/AIBillingService.ts +101 -27
- package/Server/Services/AccessTokenService.ts +1 -0
- package/Server/Services/AlertCustomFieldService.ts +98 -0
- package/Server/Services/AlertService.ts +37 -0
- package/Server/Services/ApiKeyPermissionService.ts +461 -29
- package/Server/Services/CustomFieldMappingService.ts +879 -0
- package/Server/Services/EnterpriseLicenseInstanceService.ts +197 -0
- package/Server/Services/EnterpriseLicenseService.ts +55 -0
- package/Server/Services/GlobalConfigService.ts +194 -0
- package/Server/Services/IncidentCustomFieldService.ts +98 -0
- package/Server/Services/IncidentService.ts +35 -0
- package/Server/Services/Index.ts +10 -0
- package/Server/Services/MetricService.ts +194 -0
- package/Server/Services/MonitorService.ts +175 -21
- package/Server/Services/MonitorStatusTimelineService.ts +150 -8
- package/Server/Services/MonitorTemplateService.ts +217 -1
- package/Server/Services/NetworkAlertPolicyEngineService.ts +2159 -0
- package/Server/Services/NetworkAlertPolicyService.ts +944 -0
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +87 -35
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +60 -15
- package/Server/Services/NetworkDeviceService.ts +1032 -73
- package/Server/Services/NetworkSiteService.ts +1502 -129
- package/Server/Services/NetworkSiteTypeService.ts +1216 -0
- package/Server/Services/NetworkSnmpCredentialProfileService.ts +238 -0
- package/Server/Services/NotificationService.ts +118 -28
- package/Server/Services/ProjectService.ts +127 -48
- package/Server/Services/RoutineEmailSettingsService.ts +73 -0
- package/Server/Services/RumSessionReplayViewService.ts +104 -24
- package/Server/Services/ScheduledMaintenanceCustomFieldService.ts +98 -0
- package/Server/Services/ScheduledMaintenanceService.ts +32 -0
- package/Server/Services/StatusPagePrivateUserService.ts +103 -3
- package/Server/Services/TeamMemberService.ts +78 -0
- package/Server/Services/TeamPermissionService.ts +235 -3
- package/Server/Services/TelemetryIngestionKeyService.ts +722 -19
- package/Server/Services/UserNotificationEmailRollupBatchService.ts +78 -0
- package/Server/Services/UserNotificationEmailRollupItemService.ts +79 -0
- package/Server/Services/UserNotificationEmailRollupSettingService.ts +113 -0
- package/Server/Services/UserNotificationSettingService.ts +90 -25
- package/Server/Services/UserService.ts +95 -0
- package/Server/Services/UserTelegramService.ts +326 -5
- package/Server/Types/Database/QueryHelper.ts +4 -2
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +14 -3
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +10 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +10 -0
- package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +62 -1
- package/Server/Utils/AI/Toolbox/AlertTools.ts +15 -1
- package/Server/Utils/APIKey/AccessPermission.ts +5 -2
- package/Server/Utils/Billing/BillingFailureNoticeThrottle.ts +80 -0
- package/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.ts +54 -0
- package/Server/Utils/CustomField/CustomFieldMappingRegistry.ts +415 -0
- package/Server/Utils/CustomField/CustomFieldMappingValidator.ts +335 -0
- package/Server/Utils/DataSource/EgressGuard.ts +177 -8
- package/Server/Utils/DataSource/HttpFetch.ts +9 -2
- package/Server/Utils/EmailRollup/EmailRollupConstants.ts +131 -0
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +925 -0
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +781 -0
- package/Server/Utils/EmailRollup/EmailRollupWriter.ts +368 -0
- package/Server/Utils/FrontendEnvironment.ts +40 -0
- package/Server/Utils/LLM/LLMService.ts +78 -0
- package/Server/Utils/LogRedaction.ts +28 -0
- package/Server/Utils/Logger.ts +90 -1
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +338 -77
- package/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.ts +190 -0
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +9 -2
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +30 -4
- package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +61 -1
- package/Server/Utils/Monitor/MonitorAlert.ts +29 -6
- package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +14 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +159 -15
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +255 -4
- package/Server/Utils/Monitor/MonitorIncident.ts +22 -6
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +41 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +72 -0
- package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +499 -19
- package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +66 -10
- package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +238 -44
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +146 -39
- package/Server/Utils/Monitor/SeriesContextEnricher.ts +304 -0
- package/Server/Utils/NetworkDevice/DeviceHealthAggregation.ts +31 -18
- package/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.ts +213 -0
- package/Server/Utils/OutboundUserAgent.ts +152 -0
- package/Server/Utils/SSRFProtection.ts +217 -10
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +239 -138
- package/Server/Utils/SessionReplay/SessionReplayHealthCounters.ts +305 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +1430 -289
- package/Server/Utils/StartServer.ts +53 -36
- package/Server/Utils/TelegramVerificationToken.ts +185 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +32 -8
- package/Server/Utils/Telemetry/ErrorClassResolver.ts +120 -0
- package/Server/Utils/Telemetry/PinServiceName.ts +197 -0
- package/Server/Utils/Telemetry/SpanUtil.ts +33 -0
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +38 -16
- package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +92 -0
- package/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.ts +217 -0
- package/Server/Utils/Telemetry.ts +198 -51
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +975 -95
- package/Server/Views/Partials/SensitiveUrlToken.ejs +140 -0
- package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +202 -0
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +134 -33
- package/Tests/App/Dashboard/AskAiSuggestedPrompts.test.ts +279 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +517 -0
- package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +404 -0
- package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +26 -0
- package/Tests/App/Dashboard/DeviceStatusHero.test.tsx +588 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +486 -9
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +632 -0
- package/Tests/App/Dashboard/MonitorRecommendationCreateProgress.test.tsx +682 -0
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +4 -2
- package/Tests/App/Dashboard/NetworkDeviceCreateFormMonitorBinding.test.tsx +676 -0
- package/Tests/App/Dashboard/NetworkDeviceCreateFormPingMonitor.test.tsx +791 -0
- package/Tests/App/Dashboard/NetworkDeviceCreateFormProbeAndSnmp.test.tsx +833 -0
- package/Tests/App/Dashboard/NetworkDeviceCriteriaFilter.test.ts +161 -0
- package/Tests/App/Dashboard/NetworkDeviceSettingsMonitoringSection.test.tsx +458 -0
- package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +41 -5
- package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +399 -0
- package/Tests/App/Dashboard/PingMonitorProvisioning.test.ts +368 -0
- package/Tests/App/Dashboard/RecommendationCard.test.tsx +796 -0
- package/Tests/App/Dashboard/RecommendationToolbar.test.tsx +519 -0
- package/Tests/App/Dashboard/RecommendationsList.test.tsx +661 -0
- package/Tests/App/Dashboard/UseBulkCreatePingMonitors.test.tsx +899 -0
- package/Tests/App/Dashboard/UseBulkSnmpCredentialProfileActions.test.tsx +732 -0
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +256 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +11 -1
- package/Tests/App/StatusPage/StatusPageLastUpdated.test.tsx +263 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +9 -4
- package/Tests/App/StatusPage/StatusPageOverviewLiveAndSearch.test.tsx +753 -0
- package/Tests/App/StatusPage/StatusPageResourceSearchBox.test.tsx +224 -0
- package/Tests/Models/AnalyticsModels/RumSessionReplayColumns.test.ts +262 -0
- package/Tests/Models/CustomFieldMappingColumns.test.ts +166 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +20 -6
- package/Tests/Models/NetworkSiteHierarchy.test.ts +41 -4
- package/Tests/ResponsiveVisibility.test.ts +115 -0
- package/Tests/ResponsiveVisibility.ts +183 -0
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +951 -7
- package/Tests/Server/API/LlmProviderConnectionTest.test.ts +522 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +1940 -216
- package/Tests/Server/API/UserNotificationSettingAPI.test.ts +186 -0
- package/Tests/Server/API/UserTelegramAPISecurity.test.ts +231 -0
- package/Tests/Server/EnvironmentConfigFrontendSecurity.test.ts +234 -0
- package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +202 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +41 -0
- package/Tests/Server/Infrastructure/Postgres/NetworkSnmpCredentialProfilesAndAlertPoliciesMigration.test.ts +854 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +18 -0
- package/Tests/Server/Infrastructure/Postgres/SessionReplayRecordEverySessionByDefaultMigration.test.ts +169 -0
- package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +535 -0
- package/Tests/Server/Infrastructure/QueueWorkerTimeout.test.ts +210 -0
- package/Tests/Server/Infrastructure/TelemetryExporterDeploymentConfig.test.ts +130 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -1
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +1163 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +5 -3
- package/Tests/Server/Services/AIChatPrivacyPin.test.ts +560 -0
- package/Tests/Server/Services/AddTelemetryIngestionKeyTypeMigration.test.ts +555 -0
- package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +678 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +276 -28
- package/Tests/Server/Services/BillingRechargeOwnerEmailGuards.test.ts +783 -0
- package/Tests/Server/Services/CustomFieldDropdownOptionsColumnWidth.test.ts +431 -0
- package/Tests/Server/Services/CustomFieldMappingService.test.ts +850 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +32 -0
- package/Tests/Server/Services/EnterpriseLicenseInstanceServiceDeletionUsage.test.ts +707 -0
- package/Tests/Server/Services/EnterpriseLicenseServiceUsageAggregationLock.test.ts +329 -0
- package/Tests/Server/Services/GlobalConfigService.test.ts +414 -1
- package/Tests/Server/Services/MetricRawEntityKeyPrune.test.ts +592 -0
- package/Tests/Server/Services/MonitorServiceDeleteNetworkDeviceCleanup.test.ts +294 -0
- package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +802 -0
- package/Tests/Server/Services/MonitorStatusTimelineService.test.ts +72 -2
- package/Tests/Server/Services/NetworkAlertPolicyEngineHooks.test.ts +752 -0
- package/Tests/Server/Services/NetworkAlertPolicyEngineService.test.ts +1849 -0
- package/Tests/Server/Services/NetworkAlertPolicyModel.test.ts +1793 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +273 -5
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +97 -12
- package/Tests/Server/Services/NetworkDeviceAutoMonitorLifecycle.test.ts +148 -3
- package/Tests/Server/Services/NetworkDeviceMonitorBindingUpdateGuard.test.ts +514 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +447 -58
- package/Tests/Server/Services/NetworkDeviceMonitoringMethodTransition.test.ts +739 -0
- package/Tests/Server/Services/NetworkDevicePollingTenancy.test.ts +501 -0
- package/Tests/Server/Services/NetworkDeviceSiteDefaultProbe.test.ts +432 -0
- package/Tests/Server/Services/NetworkSiteMonitoringDefaults.test.ts +498 -0
- package/Tests/Server/Services/NetworkSiteRollupPolicyAndMaintenance.test.ts +337 -55
- package/Tests/Server/Services/NetworkSiteService.test.ts +1868 -131
- package/Tests/Server/Services/NetworkSiteTypeService.test.ts +1109 -0
- package/Tests/Server/Services/NetworkSnmpCredentialProfileModel.test.ts +926 -0
- package/Tests/Server/Services/NetworkSnmpCredentialProfileService.test.ts +796 -0
- package/Tests/Server/Services/ProjectServiceNetworkSiteTypeDefaults.test.ts +214 -0
- package/Tests/Server/Services/RoutineEmailSettingsPostgres.test.ts +329 -0
- package/Tests/Server/Services/RumSessionReplayViewService.test.ts +267 -0
- package/Tests/Server/Services/StatusPagePrivateUserEmailChangePasswordResetExpiry.test.ts +269 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +9 -0
- package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +9 -0
- package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +834 -0
- package/Tests/Server/Services/TelemetryIngestionKeyPolicyResolution.test.ts +814 -0
- package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +998 -0
- package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +472 -0
- package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +569 -0
- package/Tests/Server/Services/UserNotificationEmailRollupSettingModel.test.ts +314 -0
- package/Tests/Server/Services/UserNotificationEmailRollupSettingService.test.ts +409 -0
- package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +997 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +110 -1
- package/Tests/Server/Services/UserTelegramVerificationSecurity.test.ts +688 -0
- package/Tests/Server/Types/Database/Permissions/DashboardAccessPermission.test.ts +321 -0
- package/Tests/Server/Types/Database/QueryHelperOperators.test.ts +17 -0
- package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +124 -0
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +208 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +235 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +253 -0
- package/Tests/Server/Utils/AI/SRE/Insights/FixRouting.test.ts +497 -0
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +383 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +7 -3
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterConfig.test.ts +315 -0
- package/Tests/Server/Utils/Captcha.test.ts +422 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingValidator.test.ts +437 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +300 -11
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +96 -2
- package/Tests/Server/Utils/Database/MigrationFailureLog.test.ts +466 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupBurstWindow.test.ts +83 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1096 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerClaim.test.ts +466 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerPreferences.test.ts +362 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +1428 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +828 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +864 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +190 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +26 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +23 -9
- package/Tests/Server/Utils/LoggerFaultDemotion.test.ts +346 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +584 -0
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +633 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestHeaderCriteria.test.ts +236 -0
- package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +196 -0
- package/Tests/Server/Utils/Monitor/DatabaseMetricWrite.test.ts +367 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +437 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +100 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +5 -0
- package/Tests/Server/Utils/Monitor/MonitorTemplateUtilSeriesContext.test.ts +176 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +512 -31
- package/Tests/Server/Utils/Monitor/NetworkDeviceMetricUtil.test.ts +252 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +479 -42
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +351 -5
- package/Tests/Server/Utils/Monitor/SeriesContextEnricher.test.ts +356 -0
- package/Tests/Server/Utils/NetworkDevice/DeviceHealthAggregation.test.ts +136 -12
- package/Tests/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.test.ts +306 -0
- package/Tests/Server/Utils/OutboundUserAgent.test.ts +266 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +595 -0
- package/Tests/Server/Utils/SecurityEvent/ThreatIntel/TaxiiClientUserAgent.test.ts +141 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +198 -1
- package/Tests/Server/Utils/SessionReplay/SessionReplayHealthCounters.test.ts +453 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1378 -0
- package/Tests/Server/Utils/SessionReplayOriginAllowListRefactor.test.ts +420 -0
- package/Tests/Server/Utils/TelegramVerificationToken.test.ts +253 -0
- package/Tests/Server/Utils/Telemetry/ErrorClassResolver.test.ts +298 -0
- package/Tests/Server/Utils/Telemetry/PinServiceName.test.ts +666 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriterCapacity.test.ts +458 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +424 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.test.ts +528 -0
- package/Tests/Server/Utils/Telemetry.test.ts +456 -66
- package/Tests/Server/Utils/TelemetryExporterEnvironment.test.ts +56 -0
- package/Tests/Server/Utils/VM/VMRunnerHostBridgeLatency.test.ts +74 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +100 -7
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +1176 -6
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +220 -0
- package/Tests/Types/CustomField/CustomFieldValueMapping.test.ts +348 -0
- package/Tests/Types/JSONFunctions.test.ts +260 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +422 -53
- package/Tests/Types/Monitor/DatabaseMetricCatalog.test.ts +388 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +495 -15
- package/Tests/Types/Monitor/DockerSwarmAlertTemplates.test.ts +191 -45
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +370 -42
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +375 -20
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +636 -42
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +189 -0
- package/Tests/Types/Monitor/KubernetesTemplateGroupByKeys.test.ts +95 -14
- package/Tests/Types/Monitor/MonitorStepDatabaseMonitor.test.ts +243 -0
- package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +16 -7
- package/Tests/Types/Monitor/NetworkDeviceAlertPack.test.ts +60 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +190 -21
- package/Tests/Types/Monitor/ProxmoxAlertTemplates.test.ts +352 -22
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationAlertDebuggability.test.ts +363 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +93 -18
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +259 -0
- package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +476 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +136 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesDebugHints.test.ts +661 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +507 -0
- package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +270 -0
- package/Tests/Types/Monitor/TemplateGroupByKeys.test.ts +38 -10
- package/Tests/Types/Monitor/Utils/RecommendationCriteriaAssertions.ts +102 -0
- package/Tests/Types/NetworkDevice/NetworkAlertPolicyScope.test.ts +655 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +115 -26
- package/Tests/Types/NetworkSite/DefaultNetworkSiteType.test.ts +39 -0
- package/Tests/Types/NotificationSetting/NotificationEmailRollupPolicy.test.ts +299 -0
- package/Tests/Types/NotificationSetting/RoutineEmailEvents.test.ts +47 -0
- package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +608 -0
- package/Tests/Types/Rum/SessionReplayCustomEvents.test.ts +434 -0
- package/Tests/Types/Telemetry/ErrorClass.test.ts +483 -0
- package/Tests/Types/WebsiteRequest.test.ts +285 -3
- package/Tests/UI/Components/BasicRadioButtons.test.tsx +336 -0
- package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +16 -3
- package/Tests/UI/Components/Charts/ChartTrailingBucketGap.test.ts +691 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +12 -4
- package/Tests/UI/Components/ComponentsModalUsability.test.tsx +518 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailMapping.test.tsx +324 -0
- package/Tests/UI/Components/CustomFields/MapFromCustomFieldInput.test.tsx +176 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +484 -0
- package/Tests/UI/Components/HeaderRightRail.test.tsx +157 -0
- package/Tests/UI/Components/IconDropdownItem.test.tsx +102 -0
- package/Tests/UI/Components/JSONTablePrototypePollution.test.tsx +117 -0
- package/Tests/UI/Components/KeyboardShortcutsModal.test.tsx +214 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +561 -0
- package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +302 -0
- package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +43 -12
- package/Tests/UI/Components/ProgressBar.test.tsx +38 -2
- package/Tests/UI/Components/ShortcutDialogGuard.test.tsx +74 -0
- package/Tests/UI/Components/SideMenuItem.test.tsx +226 -4
- package/Tests/UI/Components/SideMenuSection.test.tsx +310 -0
- package/Tests/UI/Components/SideOverSubmitState.test.tsx +335 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSectionAutoExpand.test.tsx +248 -0
- package/Tests/UI/Components/TableCellWrapping.test.tsx +625 -0
- package/Tests/UI/Components/TableLoadingStates.test.tsx +205 -0
- package/Tests/UI/Components/Workflow/NodePlacement.test.ts +148 -0
- package/Tests/UI/Components/Workflow/Workflow.test.tsx +981 -0
- package/Tests/UI/ConfigBrowserTelemetry.test.ts +70 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +1036 -2
- package/Tests/UI/Rum/FidelityNotices.test.ts +187 -0
- package/Tests/UI/Rum/InactivityMap.test.ts +341 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +259 -0
- package/Tests/UI/Rum/RecordingHealthCard.test.tsx +855 -0
- package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +594 -0
- package/Tests/UI/Rum/ReplayCard.test.tsx +451 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +536 -0
- package/Tests/UI/Rum/ReplayEngine.test.ts +2503 -0
- package/Tests/UI/Rum/ReplayEngineTypes.test.ts +171 -0
- package/Tests/UI/Rum/ReplayHeader.test.tsx +711 -0
- package/Tests/UI/Rum/ReplayLink.test.tsx +119 -0
- package/Tests/UI/Rum/ReplayPinControl.test.tsx +408 -0
- package/Tests/UI/Rum/ReplayPlaybackIntent.test.ts +152 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +1296 -0
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +900 -0
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +763 -147
- package/Tests/UI/Rum/ReplaySignalTypes.test.ts +189 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +1579 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +396 -473
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +918 -0
- package/Tests/UI/Rum/ReplayTimeline.test.tsx +696 -0
- package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +499 -0
- package/Tests/UI/Rum/SessionReplaySearchBar.test.tsx +346 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +545 -0
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +991 -0
- package/Tests/UI/Rum/TargetedCapturePanel.test.tsx +254 -0
- package/Tests/UI/Telemetry/BrowserExporterIsolation.test.ts +71 -0
- package/Tests/UI/Utils/GlobalKeyboardShortcut.test.ts +344 -0
- package/Tests/UI/Utils/PageScrollLock.test.tsx +377 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +78 -0
- package/Tests/UI/Utils/SensitiveUrlToken.test.ts +140 -0
- package/Tests/Utils/API.test.ts +308 -0
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +69 -0
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +7 -1
- package/Tests/Utils/EnterpriseLicenseUsage.test.ts +433 -2
- package/Tests/Utils/HTTPResponseBodyReader.test.ts +323 -0
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +280 -1
- package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +38 -0
- package/Tests/Utils/NetworkDevice/DeviceHealthStateUtil.test.ts +284 -23
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +177 -0
- package/Tests/Utils/NetworkDevice/MonitoringMethodThreadedClassifiers.test.ts +992 -0
- package/Tests/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.test.ts +427 -0
- package/Tests/Utils/NetworkDevice/ReachabilityStampConsistency.test.ts +185 -0
- package/Tests/Utils/NetworkDevice/SnmpCredentialUtil.test.ts +268 -0
- package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +55 -5
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +98 -21
- package/Tests/Utils/NetworkDiscovery/DiscoveryImportEligibility.test.ts +98 -20
- package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +8 -7
- package/Tests/Utils/NetworkDiscovery/DiscoveryScanStatus.test.ts +117 -0
- package/Tests/Utils/NetworkDiscovery/PingMonitorBuilderForAddress.test.ts +274 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +230 -51
- package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +209 -0
- package/Tests/Utils/Rum/ChunkMath.test.ts +83 -0
- package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +993 -0
- package/Tests/Utils/Rum/SessionReplayStringMap.test.ts +245 -0
- package/Tests/Utils/StatusPage/ResourceSearch.test.ts +631 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +145 -0
- package/Tests/Utils/Telemetry/OriginAllowList.test.ts +633 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +461 -0
- package/Tests/Utils/ValueFormatter.test.ts +148 -0
- package/Types/AI/ExceptionAIClassification.ts +10 -24
- package/Types/CustomField/CustomFieldMappingCatalog.ts +161 -0
- package/Types/CustomField/CustomFieldMappingSourceResource.ts +19 -0
- package/Types/CustomField/CustomFieldValueMapping.ts +288 -0
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +5 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.ts +13 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.ts +6 -0
- package/Types/Exception/ApiException.ts +10 -0
- package/Types/Exception/BadDataException.ts +11 -0
- package/Types/Exception/BadRequestException.ts +11 -0
- package/Types/Exception/Exception.ts +65 -0
- package/Types/Exception/ForbiddenException.ts +10 -0
- package/Types/Exception/NotAuthenticatedException.ts +10 -0
- package/Types/Exception/NotAuthorizedException.ts +10 -0
- package/Types/Exception/NotFoundException.ts +11 -0
- package/Types/Exception/PayloadTooLargeException.ts +11 -0
- package/Types/Exception/PaymentRequiredException.ts +10 -0
- package/Types/Exception/ServiceUnavailableException.ts +10 -0
- package/Types/Exception/SsoAuthorizationException.ts +10 -0
- package/Types/Exception/TenantNotFoundException.ts +11 -0
- package/Types/Exception/TimeoutException.ts +10 -0
- package/Types/Exception/TooManyRequestsException.ts +10 -0
- package/Types/Exception/UnableToReachServer.ts +10 -0
- package/Types/Exception/WebsiteRequestException.ts +10 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/JSONFunctions.ts +189 -43
- package/Types/Monitor/CephAlertTemplates.ts +157 -195
- package/Types/Monitor/CriteriaFilter.ts +60 -2
- package/Types/Monitor/DatabaseMetricCatalog.ts +693 -0
- package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +83 -0
- package/Types/Monitor/DockerAlertTemplates.ts +338 -128
- package/Types/Monitor/DockerSwarmAlertTemplates.ts +95 -112
- package/Types/Monitor/HostAlertTemplates.ts +302 -130
- package/Types/Monitor/IotAlertTemplates.ts +112 -104
- package/Types/Monitor/KubernetesAlertTemplates.ts +415 -224
- package/Types/Monitor/KubernetesMetricCatalog.ts +23 -19
- package/Types/Monitor/MonitorCriteriaInstance.ts +76 -0
- package/Types/Monitor/MonitorMetricType.ts +71 -0
- package/Types/Monitor/MonitorStep.ts +76 -0
- package/Types/Monitor/MonitorStepDatabaseMonitor.ts +171 -0
- package/Types/Monitor/MonitorStepSqlMonitor.ts +2 -20
- package/Types/Monitor/MonitorType.ts +47 -1
- package/Types/Monitor/PodmanAlertTemplates.ts +116 -163
- package/Types/Monitor/ProxmoxAlertTemplates.ts +217 -99
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +98 -6
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +44 -7
- package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +383 -0
- package/Types/Monitor/RumAlertTemplates.ts +46 -7
- package/Types/Monitor/SeriesContext/SeriesDebugHints.ts +596 -0
- package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +554 -0
- package/Types/Monitor/ServiceAlertTemplates.ts +98 -11
- package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +22 -1
- package/Types/Monitor/SqlConnectionConfig.ts +31 -0
- package/Types/Monitor/UptimeHistoryLabels.ts +56 -0
- package/Types/NetworkDevice/NetworkAlertPolicyScope.ts +343 -0
- package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +42 -22
- package/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.ts +30 -0
- package/Types/NotificationSetting/NotificationEmailRollupCategory.ts +255 -0
- package/Types/NotificationSetting/NotificationEmailRollupPolicy.ts +104 -0
- package/Types/NotificationSetting/RoutineEmailEvents.ts +30 -0
- package/Types/Permission.ts +90 -0
- package/Types/Probe/ProbeMonitorResponse.ts +16 -0
- package/Types/Rum/SessionReplay.ts +209 -0
- package/Types/Rum/SessionReplayApi.ts +722 -0
- package/Types/Rum/SessionReplayCaptureTrigger.ts +21 -12
- package/Types/Rum/SessionReplayConsentMode.ts +11 -7
- package/Types/Rum/SessionReplayCustomEvents.ts +549 -0
- package/Types/Rum/SessionReplayHealth.ts +238 -0
- package/Types/Telemetry/ErrorClass.ts +274 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +95 -0
- package/Types/Telemetry/TelemetryIngestionKeyPolicy.ts +75 -0
- package/Types/Telemetry/TelemetryIngestionKeyType.ts +29 -0
- package/Types/Telemetry/UnitOfWork.ts +59 -0
- package/Types/WebsiteRequest.ts +85 -3
- package/UI/Components/Charts/Area/AreaChart.tsx +13 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +6 -2
- package/UI/Components/Charts/Line/LineChart.tsx +13 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +29 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +22 -3
- package/UI/Components/Charts/Utils/TimeAnnotation.ts +76 -9
- package/UI/Components/Charts/Utils/XAxis.ts +217 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +2 -8
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +143 -25
- package/UI/Components/CustomFields/MapFromCustomFieldInput.tsx +216 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +4 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +173 -8
- package/UI/Components/Graphs/UptimeBarTooltip.tsx +22 -278
- package/UI/Components/Graphs/UptimeDaySummary.tsx +327 -0
- package/UI/Components/Header/Header.tsx +25 -9
- package/UI/Components/Header/IconDropdown/IconDropdownItem.tsx +10 -1
- package/UI/Components/Header/IconDropdown/IconDropdownMenu.tsx +6 -1
- package/UI/Components/Header/ProjectPicker/ProjectPicker.tsx +6 -1
- package/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.tsx +6 -1
- package/UI/Components/Icon/Icon.tsx +11 -0
- package/UI/Components/JSONTable/JSONTable.tsx +2 -2
- package/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.tsx +125 -0
- package/UI/Components/KeyboardShortcut/Screenshots/README.md +19 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-command-palette.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-dialog-dark.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-dialog.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-help-menu.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-narrow.png +0 -0
- package/UI/Components/Modal/Modal.tsx +2 -8
- package/UI/Components/ModelTable/Column.ts +15 -0
- package/UI/Components/Monitor/SeriesDebugCommandsViewer.tsx +64 -0
- package/UI/Components/Monitor/SeriesLabelsViewer.tsx +93 -0
- package/UI/Components/MonitorGraphs/Uptime.tsx +14 -2
- package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +63 -10
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +88 -0
- package/UI/Components/ProgressBar/ProgressBar.tsx +12 -1
- package/UI/Components/RadioButtons/BasicRadioButtons.tsx +37 -7
- package/UI/Components/SideMenu/SideMenu.tsx +123 -5
- package/UI/Components/SideMenu/SideMenuItem.tsx +76 -84
- package/UI/Components/SideMenu/SideMenuSection.tsx +55 -23
- package/UI/Components/SideOver/SideOver.tsx +21 -20
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +40 -0
- package/UI/Components/Table/CellClassName.ts +81 -0
- package/UI/Components/Table/TableRow.tsx +20 -14
- package/UI/Components/Table/TableSkeletonRows.tsx +9 -9
- package/UI/Components/Table/Types/Column.ts +36 -0
- package/UI/Components/Tooltip/Tooltip.tsx +11 -1
- package/UI/Components/Workflow/ComponentsModal.tsx +60 -39
- package/UI/Components/Workflow/NodePlacement.ts +57 -0
- package/UI/Components/Workflow/Workflow.tsx +47 -18
- package/UI/Config.ts +15 -33
- package/UI/Utils/GlobalKeyboardShortcut.ts +208 -0
- package/UI/Utils/PageScrollLock.ts +79 -3
- package/UI/Utils/PermissionGate.ts +61 -0
- package/UI/Utils/SensitiveUrlToken.ts +112 -0
- package/UI/Utils/Telemetry/BrowserTelemetryConfig.ts +28 -0
- package/UI/Utils/Telemetry/Telemetry.ts +10 -8
- package/UI/Utils/TestLLMProvider.ts +11 -0
- package/Utils/API.ts +129 -3
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +16 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +191 -11
- package/Utils/HTTPResponseBodyReader.ts +221 -0
- package/Utils/Monitor/MonitorMetricType.ts +153 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +23 -3
- package/Utils/NetworkDevice/DeviceHealthStateUtil.ts +65 -14
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +43 -14
- package/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.ts +247 -0
- package/Utils/NetworkDevice/SnmpCredentialUtil.ts +68 -0
- package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +15 -7
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +52 -17
- package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +55 -23
- package/Utils/NetworkDiscovery/DiscoveryScanStatus.ts +77 -0
- package/Utils/NetworkDiscovery/PingMonitorBuilder.ts +115 -13
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +66 -20
- package/Utils/NetworkSite/TypeHierarchyUtil.ts +313 -0
- package/Utils/Rum/ChunkMath.ts +106 -0
- package/Utils/Rum/SessionReplayHealth.ts +732 -0
- package/Utils/Rum/SessionReplayStringMap.ts +226 -0
- package/Utils/Schema/ModelSchema.ts +1 -0
- package/Utils/StatusPage/ResourceSearch.ts +290 -0
- package/Utils/Telemetry/OriginAllowList.ts +355 -0
- package/Utils/Uptime/DayUptimeGraphUtil.ts +205 -0
- package/Utils/ValueFormatter.ts +35 -1
- package/build/dist/Models/AnalyticsModels/RumSession.js +102 -19
- package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +11 -9
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +14 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/AlertCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ApiKeyPermission.js +3 -27
- package/build/dist/Models/DatabaseModels/ApiKeyPermission.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +59 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +10 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +75 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js +713 -0
- package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +180 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSite.js +200 -2
- package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteType.js +102 -3
- package/build/dist/Models/DatabaseModels/NetworkSiteType.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js +878 -0
- package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +6 -6
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/StatusPageCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/TeamCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamMember.js +0 -9
- package/build/dist/Models/DatabaseModels/TeamMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamPermission.js +3 -24
- package/build/dist/Models/DatabaseModels/TeamPermission.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +123 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +267 -0
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js +363 -0
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +402 -0
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js +296 -0
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserTelegram.js +0 -4
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +273 -60
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +108 -13
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +769 -109
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserNotificationSettingAPI.js +35 -0
- package/build/dist/Server/API/UserNotificationSettingAPI.js.map +1 -0
- package/build/dist/Server/API/UserTelegramAPI.js +28 -6
- package/build/dist/Server/API/UserTelegramAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +41 -2
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js +111 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js +77 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js +83 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.js +60 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.js +53 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.js +82 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +52 -12
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +1 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/BearerTokenAuthorization.js +12 -0
- package/build/dist/Server/Middleware/BearerTokenAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +11 -0
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +12 -0
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +326 -8
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIBillingService.js +70 -16
- package/build/dist/Server/Services/AIBillingService.js.map +1 -1
- package/build/dist/Server/Services/AccessTokenService.js +1 -0
- package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
- package/build/dist/Server/Services/AlertCustomFieldService.js +93 -0
- package/build/dist/Server/Services/AlertCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +34 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +331 -28
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/CustomFieldMappingService.js +614 -0
- package/build/dist/Server/Services/CustomFieldMappingService.js.map +1 -0
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +167 -0
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +39 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +133 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +93 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +32 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +10 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +163 -0
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +148 -21
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +127 -8
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +162 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js +1683 -0
- package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js.map +1 -0
- package/build/dist/Server/Services/NetworkAlertPolicyService.js +732 -0
- package/build/dist/Server/Services/NetworkAlertPolicyService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +89 -36
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +54 -11
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +841 -70
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +1145 -107
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteTypeService.js +863 -0
- package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js +217 -0
- package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js.map +1 -0
- package/build/dist/Server/Services/NotificationService.js +80 -17
- package/build/dist/Server/Services/NotificationService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +88 -30
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RoutineEmailSettingsService.js +69 -0
- package/build/dist/Server/Services/RoutineEmailSettingsService.js.map +1 -0
- package/build/dist/Server/Services/RumSessionReplayViewService.js +86 -24
- package/build/dist/Server/Services/RumSessionReplayViewService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceCustomFieldService.js +93 -0
- package/build/dist/Server/Services/ScheduledMaintenanceCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +29 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +92 -2
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +62 -2
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamPermissionService.js +166 -3
- package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +562 -18
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js +76 -0
- package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js +77 -0
- package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js +111 -0
- package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js +66 -20
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +90 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +257 -4
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +14 -3
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +10 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +10 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +54 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +18 -4
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +2 -2
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js +59 -0
- package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.js +27 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingRegistry.js +226 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingRegistry.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingValidator.js +189 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingValidator.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +121 -7
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +2 -1
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +124 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +733 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +497 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +257 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js +33 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -0
- package/build/dist/Server/Utils/LLM/LLMService.js +64 -1
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +28 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
- package/build/dist/Server/Utils/Logger.js +73 -1
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +287 -68
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js +141 -0
- package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +20 -4
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +51 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +29 -6
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +7 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +125 -13
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +158 -6
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +54 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +330 -21
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +55 -10
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +178 -49
- package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +119 -29
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesContextEnricher.js +237 -0
- package/build/dist/Server/Utils/Monitor/SeriesContextEnricher.js.map +1 -0
- package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js +30 -17
- package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js.map +1 -1
- package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js +158 -0
- package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js.map +1 -0
- package/build/dist/Server/Utils/OutboundUserAgent.js +123 -0
- package/build/dist/Server/Utils/OutboundUserAgent.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +136 -9
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +174 -89
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayHealthCounters.js +223 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayHealthCounters.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +945 -164
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +41 -28
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TelegramVerificationToken.js +129 -0
- package/build/dist/Server/Utils/TelegramVerificationToken.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +28 -8
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js +95 -0
- package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/PinServiceName.js +163 -0
- package/build/dist/Server/Utils/Telemetry/PinServiceName.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/SpanUtil.js +29 -0
- package/build/dist/Server/Utils/Telemetry/SpanUtil.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +28 -15
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +64 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.js +171 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry.js +162 -46
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +780 -69
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Types/AI/ExceptionAIClassification.js +10 -24
- package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +60 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingSourceResource.js +20 -0
- package/build/dist/Types/CustomField/CustomFieldMappingSourceResource.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueMapping.js +141 -0
- package/build/dist/Types/CustomField/CustomFieldValueMapping.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js +2 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js.map +1 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js +7 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js.map +1 -0
- package/build/dist/Types/Exception/ApiException.js +9 -0
- package/build/dist/Types/Exception/ApiException.js.map +1 -1
- package/build/dist/Types/Exception/BadDataException.js +10 -0
- package/build/dist/Types/Exception/BadDataException.js.map +1 -1
- package/build/dist/Types/Exception/BadRequestException.js +10 -0
- package/build/dist/Types/Exception/BadRequestException.js.map +1 -1
- package/build/dist/Types/Exception/Exception.js +56 -0
- package/build/dist/Types/Exception/Exception.js.map +1 -1
- package/build/dist/Types/Exception/ForbiddenException.js +9 -0
- package/build/dist/Types/Exception/ForbiddenException.js.map +1 -1
- package/build/dist/Types/Exception/NotAuthenticatedException.js +9 -0
- package/build/dist/Types/Exception/NotAuthenticatedException.js.map +1 -1
- package/build/dist/Types/Exception/NotAuthorizedException.js +9 -0
- package/build/dist/Types/Exception/NotAuthorizedException.js.map +1 -1
- package/build/dist/Types/Exception/NotFoundException.js +10 -0
- package/build/dist/Types/Exception/NotFoundException.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +10 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -1
- package/build/dist/Types/Exception/PaymentRequiredException.js +9 -0
- package/build/dist/Types/Exception/PaymentRequiredException.js.map +1 -1
- package/build/dist/Types/Exception/ServiceUnavailableException.js +9 -0
- package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -1
- package/build/dist/Types/Exception/SsoAuthorizationException.js +9 -0
- package/build/dist/Types/Exception/SsoAuthorizationException.js.map +1 -1
- package/build/dist/Types/Exception/TenantNotFoundException.js +10 -0
- package/build/dist/Types/Exception/TenantNotFoundException.js.map +1 -1
- package/build/dist/Types/Exception/TimeoutException.js +9 -0
- package/build/dist/Types/Exception/TimeoutException.js.map +1 -1
- package/build/dist/Types/Exception/TooManyRequestsException.js +9 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -1
- package/build/dist/Types/Exception/UnableToReachServer.js +9 -0
- package/build/dist/Types/Exception/UnableToReachServer.js.map +1 -1
- package/build/dist/Types/Exception/WebsiteRequestException.js +9 -0
- package/build/dist/Types/Exception/WebsiteRequestException.js.map +1 -1
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +118 -37
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Monitor/CephAlertTemplates.js +130 -171
- package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +45 -2
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/DatabaseMetricCatalog.js +594 -0
- package/build/dist/Types/Monitor/DatabaseMetricCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js +21 -0
- package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js.map +1 -0
- package/build/dist/Types/Monitor/DockerAlertTemplates.js +286 -121
- package/build/dist/Types/Monitor/DockerAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/DockerSwarmAlertTemplates.js +84 -105
- package/build/dist/Types/Monitor/DockerSwarmAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/HostAlertTemplates.js +250 -122
- package/build/dist/Types/Monitor/HostAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/IotAlertTemplates.js +81 -81
- package/build/dist/Types/Monitor/IotAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +375 -215
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +19 -19
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +70 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorMetricType.js +61 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +54 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js +122 -0
- package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +45 -1
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Monitor/PodmanAlertTemplates.js +104 -153
- package/build/dist/Types/Monitor/PodmanAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +156 -91
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +67 -6
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +38 -7
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +261 -0
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -0
- package/build/dist/Types/Monitor/RumAlertTemplates.js +46 -7
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js +422 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js.map +1 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +441 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -0
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js +79 -15
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +21 -1
- package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -1
- package/build/dist/Types/Monitor/SqlConnectionConfig.js +2 -0
- package/build/dist/Types/Monitor/SqlConnectionConfig.js.map +1 -0
- package/build/dist/Types/Monitor/UptimeHistoryLabels.js +17 -0
- package/build/dist/Types/Monitor/UptimeHistoryLabels.js.map +1 -0
- package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js +172 -0
- package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js.map +1 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +40 -22
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -1
- package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js +26 -0
- package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js.map +1 -0
- package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js +186 -0
- package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js.map +1 -0
- package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js +87 -0
- package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js.map +1 -0
- package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js +29 -0
- package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js.map +1 -0
- package/build/dist/Types/Permission.js +80 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +117 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayApi.js +181 -0
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js +21 -12
- package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayConsentMode.js +11 -7
- package/build/dist/Types/Rum/SessionReplayConsentMode.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js +172 -0
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayHealth.js +37 -0
- package/build/dist/Types/Rum/SessionReplayHealth.js.map +1 -0
- package/build/dist/Types/Telemetry/ErrorClass.js +216 -0
- package/build/dist/Types/Telemetry/ErrorClass.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +85 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyPolicy.js +15 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyPolicy.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyType.js +30 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyType.js.map +1 -0
- package/build/dist/Types/Telemetry/UnitOfWork.js +59 -0
- package/build/dist/Types/Telemetry/UnitOfWork.js.map +1 -0
- package/build/dist/Types/WebsiteRequest.js +55 -3
- package/build/dist/Types/WebsiteRequest.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -2
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +6 -2
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +13 -2
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Types/XAxis/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -3
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +40 -9
- package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +186 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +2 -7
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +80 -6
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/MapFromCustomFieldInput.js +118 -0
- package/build/dist/UI/Components/CustomFields/MapFromCustomFieldInput.js.map +1 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +3 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +112 -7
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -162
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeDaySummary.js +181 -0
- package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -0
- package/build/dist/UI/Components/Header/Header.js +2 -3
- package/build/dist/UI/Components/Header/Header.js.map +1 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownItem.js +2 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownItem.js.map +1 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownMenu.js +7 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownMenu.js.map +1 -1
- package/build/dist/UI/Components/Header/ProjectPicker/ProjectPicker.js +7 -1
- package/build/dist/UI/Components/Header/ProjectPicker/ProjectPicker.js.map +1 -1
- package/build/dist/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.js +7 -1
- package/build/dist/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +5 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/JSONTable/JSONTable.js +2 -2
- package/build/dist/UI/Components/JSONTable/JSONTable.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.js +35 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.js.map +1 -0
- package/build/dist/UI/Components/Modal/Modal.js +2 -7
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Monitor/SeriesDebugCommandsViewer.js +34 -0
- package/build/dist/UI/Components/Monitor/SeriesDebugCommandsViewer.js.map +1 -0
- package/build/dist/UI/Components/Monitor/SeriesLabelsViewer.js +49 -0
- package/build/dist/UI/Components/Monitor/SeriesLabelsViewer.js.map +1 -0
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +32 -6
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +77 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
- package/build/dist/UI/Components/ProgressBar/ProgressBar.js +12 -1
- package/build/dist/UI/Components/ProgressBar/ProgressBar.js.map +1 -1
- package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +6 -6
- package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +67 -5
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js +49 -53
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuSection.js +25 -7
- package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -20
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +22 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -1
- package/build/dist/UI/Components/Table/CellClassName.js +55 -0
- package/build/dist/UI/Components/Table/CellClassName.js.map +1 -0
- package/build/dist/UI/Components/Table/TableRow.js +16 -13
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/Table/TableSkeletonRows.js +9 -8
- package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +25 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +47 -30
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/NodePlacement.js +37 -0
- package/build/dist/UI/Components/Workflow/NodePlacement.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Workflow.js +35 -18
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Config.js +10 -20
- package/build/dist/UI/Config.js.map +1 -1
- package/build/dist/UI/Utils/GlobalKeyboardShortcut.js +138 -0
- package/build/dist/UI/Utils/GlobalKeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Utils/PageScrollLock.js +31 -3
- package/build/dist/UI/Utils/PageScrollLock.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +42 -1
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +97 -0
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -0
- package/build/dist/UI/Utils/Telemetry/BrowserTelemetryConfig.js +14 -0
- package/build/dist/UI/Utils/Telemetry/BrowserTelemetryConfig.js.map +1 -0
- package/build/dist/UI/Utils/Telemetry/Telemetry.js +7 -5
- package/build/dist/UI/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/UI/Utils/TestLLMProvider.js +4 -5
- package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -1
- package/build/dist/Utils/API.js +67 -5
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +15 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +111 -9
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
- package/build/dist/Utils/HTTPResponseBodyReader.js +157 -0
- package/build/dist/Utils/HTTPResponseBodyReader.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js +109 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +8 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js +27 -7
- package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +7 -5
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js +180 -0
- package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js +12 -0
- package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +15 -5
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +44 -15
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +52 -23
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js +69 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js +85 -13
- package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js.map +1 -1
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +30 -15
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +191 -0
- package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -0
- package/build/dist/Utils/Rum/ChunkMath.js +76 -0
- package/build/dist/Utils/Rum/ChunkMath.js.map +1 -1
- package/build/dist/Utils/Rum/SessionReplayHealth.js +534 -0
- package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -0
- package/build/dist/Utils/Rum/SessionReplayStringMap.js +165 -0
- package/build/dist/Utils/Rum/SessionReplayStringMap.js.map +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceSearch.js +200 -0
- package/build/dist/Utils/StatusPage/ResourceSearch.js.map +1 -0
- package/build/dist/Utils/Telemetry/OriginAllowList.js +299 -0
- package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +147 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -0
- package/build/dist/Utils/ValueFormatter.js +32 -1
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,1793 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
4
|
+
import { getMetadataArgsStorage } from "typeorm";
|
|
5
|
+
import { ColumnMetadataArgs } from "typeorm/metadata-args/ColumnMetadataArgs";
|
|
6
|
+
import { IndexMetadataArgs } from "typeorm/metadata-args/IndexMetadataArgs";
|
|
7
|
+
import { RelationMetadataArgs } from "typeorm/metadata-args/RelationMetadataArgs";
|
|
8
|
+
import AllModelTypes from "../../../Models/DatabaseModels/Index";
|
|
9
|
+
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
10
|
+
import MonitorTemplate from "../../../Models/DatabaseModels/MonitorTemplate";
|
|
11
|
+
import NetworkAlertPolicy from "../../../Models/DatabaseModels/NetworkAlertPolicy";
|
|
12
|
+
import NetworkDeviceAutoImportRule from "../../../Models/DatabaseModels/NetworkDeviceAutoImportRule";
|
|
13
|
+
import LabelService from "../../../Server/Services/LabelService";
|
|
14
|
+
import MonitorTemplateService from "../../../Server/Services/MonitorTemplateService";
|
|
15
|
+
import NetworkAlertPolicyService from "../../../Server/Services/NetworkAlertPolicyService";
|
|
16
|
+
import NetworkDeviceAutoImportRuleService from "../../../Server/Services/NetworkDeviceAutoImportRuleService";
|
|
17
|
+
import NetworkDeviceRoleService from "../../../Server/Services/NetworkDeviceRoleService";
|
|
18
|
+
import NetworkSiteService from "../../../Server/Services/NetworkSiteService";
|
|
19
|
+
import NetworkSite from "../../../Models/DatabaseModels/NetworkSite";
|
|
20
|
+
import TablePermission from "../../../Server/Types/Database/Permissions/TablePermission";
|
|
21
|
+
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
22
|
+
import DatabaseRequestType from "../../../Server/Types/BaseDatabase/DatabaseRequestType";
|
|
23
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
24
|
+
import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
|
|
25
|
+
import Services from "../../../Server/Services/Index";
|
|
26
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
27
|
+
import FindBy from "../../../Server/Types/Database/FindBy";
|
|
28
|
+
import FindOneBy from "../../../Server/Types/Database/FindOneBy";
|
|
29
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
30
|
+
import { ColumnAccessControl } from "../../../Types/BaseDatabase/AccessControl";
|
|
31
|
+
import ColumnType from "../../../Types/Database/ColumnType";
|
|
32
|
+
import { TableColumnMetadata } from "../../../Types/Database/TableColumn";
|
|
33
|
+
import TableColumnType from "../../../Types/Database/TableColumnType";
|
|
34
|
+
import { getUniqueColumnBy } from "../../../Types/Database/UniqueColumnBy";
|
|
35
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
36
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
37
|
+
import GenericFunction from "../../../Types/GenericFunction";
|
|
38
|
+
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
39
|
+
import NetworkAlertPolicyScope from "../../../Types/NetworkDevice/NetworkAlertPolicyScope";
|
|
40
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
41
|
+
import Permission, {
|
|
42
|
+
PermissionGroup,
|
|
43
|
+
PermissionHelper,
|
|
44
|
+
PermissionProps,
|
|
45
|
+
} from "../../../Types/Permission";
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* NetworkAlertPolicy: the contracts that live in metadata and in wiring, and
|
|
49
|
+
* the write-time guards in its service.
|
|
50
|
+
*
|
|
51
|
+
* A policy is "alert on a SET of devices": one Network Device monitor per
|
|
52
|
+
* matching device, cloned from a template by an engine
|
|
53
|
+
* (NetworkAlertPolicyEngineService) that hangs off this service's hooks. The
|
|
54
|
+
* engine's own behaviour is pinned by NetworkAlertPolicyEngineService.test.ts
|
|
55
|
+
* and its attachment to these hooks by NetworkAlertPolicyEngineHooks.test.ts;
|
|
56
|
+
* what THIS file pins is everything the engine has to be able to trust about
|
|
57
|
+
* a saved row:
|
|
58
|
+
*
|
|
59
|
+
* 1. THE ROW IS TENANT-SCOPED AND API-EXPOSED, with its own four granular
|
|
60
|
+
* permissions in the Monitor group, and writes held to owners, admins
|
|
61
|
+
* and those granular permissions — no ProjectMember, no Viewer — because
|
|
62
|
+
* a policy is spend, not a label. Four engine-owned columns are readable
|
|
63
|
+
* and never writable through the API. At most one policy per template
|
|
64
|
+
* per project, by partial unique index.
|
|
65
|
+
*
|
|
66
|
+
* 2. THE TEMPLATE IS REQUIRED TO THE OPERATOR, NULLABLE TO THE DATABASE, and
|
|
67
|
+
* the FK is SET NULL. Deleting a template must disable the policies that
|
|
68
|
+
* used it, never delete them.
|
|
69
|
+
*
|
|
70
|
+
* 3. THE SCOPE IS jsonb, NOT NULL, DEFAULTED TO `{}`, and every write goes
|
|
71
|
+
* through NetworkAlertPolicyScopeUtil.normalize, so the stored form is
|
|
72
|
+
* always the canonical one.
|
|
73
|
+
*
|
|
74
|
+
* 4. THE TEMPLATE IS TENANCY-CHECKED under both spellings a relation
|
|
75
|
+
* arrives in, a template from another project is indistinguishable from
|
|
76
|
+
* one that does not exist, and only a Network Device template is
|
|
77
|
+
* accepted.
|
|
78
|
+
*
|
|
79
|
+
* 5. THE CALLER MAY CAUSE MONITORS TO EXIST. Saving a policy — and enabling
|
|
80
|
+
* one, which is the same decision a day later — provisions billable
|
|
81
|
+
* monitors, so it takes the MONITOR table's create permission and a read
|
|
82
|
+
* of the template with the caller's own scopes. Without the second, a
|
|
83
|
+
* user who may edit policies could point one at a template their label
|
|
84
|
+
* scopes hide and have the engine clone it as root across the fleet.
|
|
85
|
+
*
|
|
86
|
+
* 6. EVERY SCOPE ID BELONGS TO THIS PROJECT, checked where the operator
|
|
87
|
+
* typed it. The scope is jsonb with no foreign keys behind it, so
|
|
88
|
+
* nothing else can catch a site id pasted from another tenant's URL —
|
|
89
|
+
* which would not leak that tenant's data, but would produce a policy
|
|
90
|
+
* that silently matches nothing forever. A non-UUID is refused outright,
|
|
91
|
+
* because `uuid = 'nonsense'` is a statement error rather than an empty
|
|
92
|
+
* result and would take a sweep down.
|
|
93
|
+
*
|
|
94
|
+
* Nothing here touches a database: the model is read for its decorator
|
|
95
|
+
* metadata, the service hooks are run against spied collaborator services,
|
|
96
|
+
* and the API router is read off its source.
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
100
|
+
"22222222-2222-4222-8222-222222222222",
|
|
101
|
+
);
|
|
102
|
+
const OTHER_PROJECT_ID: ObjectID = new ObjectID(
|
|
103
|
+
"44444444-4444-4444-8444-444444444444",
|
|
104
|
+
);
|
|
105
|
+
const TEMPLATE_ID: ObjectID = new ObjectID(
|
|
106
|
+
"11111111-1111-4111-8111-111111111111",
|
|
107
|
+
);
|
|
108
|
+
const OTHER_TEMPLATE_ID: ObjectID = new ObjectID(
|
|
109
|
+
"33333333-3333-4333-8333-333333333333",
|
|
110
|
+
);
|
|
111
|
+
const POLICY_ID: ObjectID = new ObjectID(
|
|
112
|
+
"55555555-5555-4555-8555-555555555555",
|
|
113
|
+
);
|
|
114
|
+
const OTHER_POLICY_ID: ObjectID = new ObjectID(
|
|
115
|
+
"66666666-6666-4666-8666-666666666666",
|
|
116
|
+
);
|
|
117
|
+
const SITE_ID: string = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
|
|
118
|
+
const ROLE_ID: string = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb";
|
|
119
|
+
const LABEL_ID: string = "cccccccc-cccc-4ccc-8ccc-cccccccccccc";
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* Partial<> under exactOptionalPropertyTypes refuses an explicit undefined,
|
|
123
|
+
* and "this field was left out of the payload" is exactly what several of
|
|
124
|
+
* the cases below have to send.
|
|
125
|
+
*/
|
|
126
|
+
type PolicyOverrides = {
|
|
127
|
+
[Key in keyof NetworkAlertPolicy]?:
|
|
128
|
+
| NetworkAlertPolicy[Key]
|
|
129
|
+
| undefined
|
|
130
|
+
| null;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
type MonitorTemplateOverrides = {
|
|
134
|
+
[Key in keyof MonitorTemplate]?: MonitorTemplate[Key] | undefined;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* The API router lives in the App workspace, which this suite cannot
|
|
139
|
+
* import. It is read as text — as the other wiring tests in this directory
|
|
140
|
+
* do — so the assertion is on the registration itself.
|
|
141
|
+
*/
|
|
142
|
+
const BASE_API_INDEX_SOURCE: string = fs.readFileSync(
|
|
143
|
+
path.join(__dirname, "../../../../App/FeatureSet/BaseAPI/Index.ts"),
|
|
144
|
+
"utf8",
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
function relationArgs(
|
|
148
|
+
target: GenericFunction,
|
|
149
|
+
propertyName: string,
|
|
150
|
+
): RelationMetadataArgs | undefined {
|
|
151
|
+
return getMetadataArgsStorage().relations.find(
|
|
152
|
+
(relation: RelationMetadataArgs): boolean => {
|
|
153
|
+
return (
|
|
154
|
+
relation.target === target && relation.propertyName === propertyName
|
|
155
|
+
);
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function columnArgs(
|
|
161
|
+
target: GenericFunction,
|
|
162
|
+
propertyName: string,
|
|
163
|
+
): ColumnMetadataArgs | undefined {
|
|
164
|
+
return getMetadataArgsStorage().columns.find(
|
|
165
|
+
(column: ColumnMetadataArgs): boolean => {
|
|
166
|
+
return column.target === target && column.propertyName === propertyName;
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function accessControlFor(
|
|
172
|
+
model: BaseModel,
|
|
173
|
+
columnName: string,
|
|
174
|
+
): ColumnAccessControl {
|
|
175
|
+
const accessControl: ColumnAccessControl | null =
|
|
176
|
+
model.getColumnAccessControlFor(columnName);
|
|
177
|
+
|
|
178
|
+
expect(accessControl).not.toBeNull();
|
|
179
|
+
|
|
180
|
+
return accessControl as ColumnAccessControl;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
describe("NetworkAlertPolicy is a project-scoped, API-exposed table", () => {
|
|
184
|
+
const policy: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
185
|
+
|
|
186
|
+
test("it is scoped to a project by projectId", () => {
|
|
187
|
+
expect(policy.getTenantColumn()).toBe("projectId");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("it is served at /network-alert-policy", () => {
|
|
191
|
+
expect(policy.getCrudApiPath()?.toString()).toBe("/network-alert-policy");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
* A model that is not in AllModelTypes gets no table and no permission
|
|
196
|
+
* catalogue coverage; a service that is not in Services is never wired
|
|
197
|
+
* into the API's per-model routing.
|
|
198
|
+
*/
|
|
199
|
+
test("the model is registered", () => {
|
|
200
|
+
expect(AllModelTypes).toContain(NetworkAlertPolicy);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("the service is registered", () => {
|
|
204
|
+
expect(Services).toContain(NetworkAlertPolicyService);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("the CRUD router is mounted in the API", () => {
|
|
208
|
+
expect(BASE_API_INDEX_SOURCE).toContain(
|
|
209
|
+
'import NetworkAlertPolicy from "Common/Models/DatabaseModels/NetworkAlertPolicy";',
|
|
210
|
+
);
|
|
211
|
+
expect(BASE_API_INDEX_SOURCE).toContain(
|
|
212
|
+
"new BaseAPI<NetworkAlertPolicy, NetworkAlertPolicyServiceType>(",
|
|
213
|
+
);
|
|
214
|
+
expect(BASE_API_INDEX_SOURCE).toContain("NetworkAlertPolicyService,");
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe("the policy's columns", () => {
|
|
219
|
+
const policy: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
220
|
+
|
|
221
|
+
test("name is required short text, unique within the project", () => {
|
|
222
|
+
const metadata: TableColumnMetadata = policy.getTableColumnMetadata("name");
|
|
223
|
+
|
|
224
|
+
expect(metadata.required).toBe(true);
|
|
225
|
+
expect(metadata.type).toBe(TableColumnType.ShortText);
|
|
226
|
+
expect(columnArgs(NetworkAlertPolicy, "name")?.options.nullable).toBe(
|
|
227
|
+
false,
|
|
228
|
+
);
|
|
229
|
+
/*
|
|
230
|
+
* Per project, not globally: the first project to write "Reachability"
|
|
231
|
+
* must not stop every other project from having one.
|
|
232
|
+
*/
|
|
233
|
+
expect(getUniqueColumnBy(policy, "name")).toBe("projectId");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("description is optional long text", () => {
|
|
237
|
+
const metadata: TableColumnMetadata =
|
|
238
|
+
policy.getTableColumnMetadata("description");
|
|
239
|
+
|
|
240
|
+
expect(metadata.required).toBe(false);
|
|
241
|
+
expect(metadata.type).toBe(TableColumnType.LongText);
|
|
242
|
+
expect(
|
|
243
|
+
columnArgs(NetworkAlertPolicy, "description")?.options.nullable,
|
|
244
|
+
).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("isEnabled is a NOT NULL boolean defaulting to true", () => {
|
|
248
|
+
const metadata: TableColumnMetadata =
|
|
249
|
+
policy.getTableColumnMetadata("isEnabled");
|
|
250
|
+
|
|
251
|
+
expect(metadata.type).toBe(TableColumnType.Boolean);
|
|
252
|
+
expect(metadata.defaultValue).toBe(true);
|
|
253
|
+
expect(metadata.isDefaultValueColumn).toBe(true);
|
|
254
|
+
|
|
255
|
+
const column: ColumnMetadataArgs | undefined = columnArgs(
|
|
256
|
+
NetworkAlertPolicy,
|
|
257
|
+
"isEnabled",
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
expect(column?.options.nullable).toBe(false);
|
|
261
|
+
expect(column?.options.default).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* THE FK CONTRACT. Required to the operator (the form and the API refuse
|
|
266
|
+
* a policy without one, because there is nothing to provision from) but
|
|
267
|
+
* nullable to the database, so that the SET NULL below has somewhere to
|
|
268
|
+
* land.
|
|
269
|
+
*/
|
|
270
|
+
test("monitorTemplateId is required to the API but nullable in the database", () => {
|
|
271
|
+
const idMetadata: TableColumnMetadata =
|
|
272
|
+
policy.getTableColumnMetadata("monitorTemplateId");
|
|
273
|
+
|
|
274
|
+
expect(idMetadata.type).toBe(TableColumnType.ObjectID);
|
|
275
|
+
expect(idMetadata.required).toBe(true);
|
|
276
|
+
expect(idMetadata.canReadOnRelationQuery).toBe(true);
|
|
277
|
+
expect(
|
|
278
|
+
columnArgs(NetworkAlertPolicy, "monitorTemplateId")?.options.nullable,
|
|
279
|
+
).toBe(true);
|
|
280
|
+
|
|
281
|
+
const relationMetadata: TableColumnMetadata =
|
|
282
|
+
policy.getTableColumnMetadata("monitorTemplate");
|
|
283
|
+
|
|
284
|
+
expect(relationMetadata.type).toBe(TableColumnType.Entity);
|
|
285
|
+
expect(relationMetadata.modelType).toBe(MonitorTemplate);
|
|
286
|
+
expect(relationMetadata.manyToOneRelationColumn).toBe("monitorTemplateId");
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
/*
|
|
290
|
+
* Configuration must never delete intent. Deleting "Reachability v1"
|
|
291
|
+
* takes the template away from the policies that used it — they can no
|
|
292
|
+
* longer provision and the settings table can say so — but their names,
|
|
293
|
+
* scopes and descriptions survive for the operator to repair. A CASCADE
|
|
294
|
+
* here would make deleting one template delete six policies silently.
|
|
295
|
+
*/
|
|
296
|
+
test("deleting the template detaches the policy rather than deleting it", () => {
|
|
297
|
+
const relation: RelationMetadataArgs | undefined = relationArgs(
|
|
298
|
+
NetworkAlertPolicy,
|
|
299
|
+
"monitorTemplate",
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
expect(relation?.options.onDelete).toBe("SET NULL");
|
|
303
|
+
expect(relation?.options.nullable).toBe(true);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
/*
|
|
307
|
+
* The template columns take the MonitorTemplate's own read permissions
|
|
308
|
+
* (the NetworkDeviceAutoImportRule precedent): selecting the relation
|
|
309
|
+
* reads a template's name and type, so it costs what reading a template
|
|
310
|
+
* costs. Writing them, by contrast, is gated on the policy's own
|
|
311
|
+
* permissions and never on a cross-model monitor permission.
|
|
312
|
+
*/
|
|
313
|
+
test.each(["monitorTemplate", "monitorTemplateId"])(
|
|
314
|
+
"%s is read under the monitor template's permissions and written under the policy's",
|
|
315
|
+
(columnName: string) => {
|
|
316
|
+
const accessControl: ColumnAccessControl = accessControlFor(
|
|
317
|
+
policy,
|
|
318
|
+
columnName,
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
expect(accessControl.read).toContain(Permission.ReadMonitorTemplate);
|
|
322
|
+
expect(accessControl.create).toContain(
|
|
323
|
+
Permission.CreateNetworkAlertPolicy,
|
|
324
|
+
);
|
|
325
|
+
expect(accessControl.update).toContain(Permission.EditNetworkAlertPolicy);
|
|
326
|
+
expect(accessControl.create).not.toContain(
|
|
327
|
+
Permission.CreateProjectMonitor,
|
|
328
|
+
);
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
/*
|
|
333
|
+
* THE SCOPE CONTRACT. jsonb so the whole picker state is one field; NOT
|
|
334
|
+
* NULL with a `{}` default so the engine always finds a readable scope
|
|
335
|
+
* (`{}` being "all devices", a real policy); isDefaultValueColumn so a
|
|
336
|
+
* create that omits it falls through to that default rather than being
|
|
337
|
+
* refused for a required column.
|
|
338
|
+
*/
|
|
339
|
+
test("scope is a NOT NULL jsonb column defaulting to {}", () => {
|
|
340
|
+
const metadata: TableColumnMetadata =
|
|
341
|
+
policy.getTableColumnMetadata("scope");
|
|
342
|
+
|
|
343
|
+
expect(metadata.type).toBe(TableColumnType.JSON);
|
|
344
|
+
expect(metadata.required).toBe(true);
|
|
345
|
+
expect(metadata.isDefaultValueColumn).toBe(true);
|
|
346
|
+
expect(metadata.defaultValue).toEqual({});
|
|
347
|
+
|
|
348
|
+
const column: ColumnMetadataArgs | undefined = columnArgs(
|
|
349
|
+
NetworkAlertPolicy,
|
|
350
|
+
"scope",
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
expect(column?.options.type).toBe(ColumnType.JSON);
|
|
354
|
+
expect(column?.options.type).toBe("jsonb");
|
|
355
|
+
expect(column?.options.nullable).toBe(false);
|
|
356
|
+
expect(typeof column?.options.default).toBe("function");
|
|
357
|
+
expect((column?.options.default as () => string)()).toBe("'{}'");
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("scope is writable by the policy's own create and edit permissions", () => {
|
|
361
|
+
const accessControl: ColumnAccessControl = accessControlFor(
|
|
362
|
+
policy,
|
|
363
|
+
"scope",
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
expect(accessControl.create).toContain(Permission.CreateNetworkAlertPolicy);
|
|
367
|
+
expect(accessControl.update).toContain(Permission.EditNetworkAlertPolicy);
|
|
368
|
+
expect(accessControl.read).toContain(Permission.ReadNetworkAlertPolicy);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe("who may write a policy", () => {
|
|
373
|
+
const policy: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
* A policy provisions billable monitors — an unscoped one in a large
|
|
377
|
+
* estate is thousands from a single submit — so writes are held to
|
|
378
|
+
* ProjectOwner, ProjectAdmin and the policy's own granular permissions.
|
|
379
|
+
* Not ProjectMember, not Viewer, and none of the Settings* roles that may
|
|
380
|
+
* edit a device role or a site type: those are labels, this is spend. A
|
|
381
|
+
* member who needs to run policies is handed CreateNetworkAlertPolicy.
|
|
382
|
+
*/
|
|
383
|
+
test.each([
|
|
384
|
+
["create", policy.createRecordPermissions],
|
|
385
|
+
["update", policy.updateRecordPermissions],
|
|
386
|
+
["delete", policy.deleteRecordPermissions],
|
|
387
|
+
])(
|
|
388
|
+
"%s is granted to owners and admins only — never members, viewers or the Settings roles",
|
|
389
|
+
(_operation: string, permissions: Array<Permission>) => {
|
|
390
|
+
expect(permissions).toContain(Permission.ProjectOwner);
|
|
391
|
+
expect(permissions).toContain(Permission.ProjectAdmin);
|
|
392
|
+
expect(permissions).not.toContain(Permission.ProjectMember);
|
|
393
|
+
expect(permissions).not.toContain(Permission.Viewer);
|
|
394
|
+
expect(permissions).not.toContain(Permission.SettingsAdmin);
|
|
395
|
+
expect(permissions).not.toContain(Permission.SettingsMember);
|
|
396
|
+
expect(permissions).not.toContain(Permission.SettingsViewer);
|
|
397
|
+
},
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
/*
|
|
401
|
+
* The table gate is only half of it. A column that still listed
|
|
402
|
+
* ProjectMember on create would pass a member's write at the column and
|
|
403
|
+
* refuse it at the table — and PermissionCatalogueCoverage reads such a
|
|
404
|
+
* mismatch as a mis-key. Every column's write lists have to agree with
|
|
405
|
+
* the table's, so this sweeps all of them rather than naming a few.
|
|
406
|
+
*/
|
|
407
|
+
test("no column grants create or update to ProjectMember either", () => {
|
|
408
|
+
const allColumns: Dictionary<ColumnAccessControl> =
|
|
409
|
+
policy.getColumnAccessControlForAllColumns();
|
|
410
|
+
const offenders: Array<string> = [];
|
|
411
|
+
|
|
412
|
+
for (const columnName of Object.keys(allColumns)) {
|
|
413
|
+
const accessControl: ColumnAccessControl = allColumns[
|
|
414
|
+
columnName
|
|
415
|
+
] as ColumnAccessControl;
|
|
416
|
+
|
|
417
|
+
if (
|
|
418
|
+
accessControl.create.includes(Permission.ProjectMember) ||
|
|
419
|
+
accessControl.update.includes(Permission.ProjectMember)
|
|
420
|
+
) {
|
|
421
|
+
offenders.push(columnName);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
expect(offenders).toEqual([]);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
/*
|
|
429
|
+
* Reading stays open to every project role: the policy list is how
|
|
430
|
+
* anybody finds out why a monitor exists.
|
|
431
|
+
*/
|
|
432
|
+
test("reading stays open to members and viewers", () => {
|
|
433
|
+
expect(policy.readRecordPermissions).toContain(Permission.ProjectMember);
|
|
434
|
+
expect(policy.readRecordPermissions).toContain(Permission.Viewer);
|
|
435
|
+
expect(policy.readRecordPermissions).toContain(
|
|
436
|
+
Permission.ReadNetworkAlertPolicy,
|
|
437
|
+
);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
/*
|
|
441
|
+
* The table's own access control has to accept the granular permissions,
|
|
442
|
+
* or granting "Create Network Alert Policy" to a team would grant nothing
|
|
443
|
+
* at all.
|
|
444
|
+
*/
|
|
445
|
+
test("the model's table access control honours each granular permission", () => {
|
|
446
|
+
expect(policy.createRecordPermissions).toContain(
|
|
447
|
+
Permission.CreateNetworkAlertPolicy,
|
|
448
|
+
);
|
|
449
|
+
expect(policy.readRecordPermissions).toContain(
|
|
450
|
+
Permission.ReadNetworkAlertPolicy,
|
|
451
|
+
);
|
|
452
|
+
expect(policy.updateRecordPermissions).toContain(
|
|
453
|
+
Permission.EditNetworkAlertPolicy,
|
|
454
|
+
);
|
|
455
|
+
expect(policy.deleteRecordPermissions).toContain(
|
|
456
|
+
Permission.DeleteNetworkAlertPolicy,
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
describe("the engine's own columns", () => {
|
|
462
|
+
const policy: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
463
|
+
|
|
464
|
+
interface EngineColumnUnderTest {
|
|
465
|
+
name: string;
|
|
466
|
+
type: TableColumnType;
|
|
467
|
+
columnType: ColumnType;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const ENGINE_COLUMNS: Array<EngineColumnUnderTest> = [
|
|
471
|
+
{
|
|
472
|
+
name: "lastSyncAt",
|
|
473
|
+
type: TableColumnType.Date,
|
|
474
|
+
columnType: ColumnType.Date,
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "lastSyncError",
|
|
478
|
+
type: TableColumnType.LongText,
|
|
479
|
+
columnType: ColumnType.LongText,
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: "coveredDeviceCount",
|
|
483
|
+
type: TableColumnType.Number,
|
|
484
|
+
columnType: ColumnType.Number,
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
name: "templateSyncedAt",
|
|
488
|
+
type: TableColumnType.Date,
|
|
489
|
+
columnType: ColumnType.Date,
|
|
490
|
+
},
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
/*
|
|
494
|
+
* Written by the engine as root after each pass; the API can read them
|
|
495
|
+
* and never set them (the Monitor.autoProvisionedNetworkDeviceId
|
|
496
|
+
* pairing). A client that could write coveredDeviceCount would be lying
|
|
497
|
+
* to the settings table, and one that could clear lastSyncError would
|
|
498
|
+
* hide a failing policy.
|
|
499
|
+
*/
|
|
500
|
+
test.each(ENGINE_COLUMNS)(
|
|
501
|
+
"$name can be read through the API but never created or updated",
|
|
502
|
+
(column: EngineColumnUnderTest) => {
|
|
503
|
+
const accessControl: ColumnAccessControl = accessControlFor(
|
|
504
|
+
policy,
|
|
505
|
+
column.name,
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
expect(accessControl.create).toEqual([]);
|
|
509
|
+
expect(accessControl.update).toEqual([]);
|
|
510
|
+
expect(accessControl.read).toContain(Permission.ReadNetworkAlertPolicy);
|
|
511
|
+
expect(accessControl.read).toContain(Permission.Viewer);
|
|
512
|
+
},
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
test.each(ENGINE_COLUMNS)(
|
|
516
|
+
"$name is optional and nullable, of the declared type",
|
|
517
|
+
(column: EngineColumnUnderTest) => {
|
|
518
|
+
const metadata: TableColumnMetadata = policy.getTableColumnMetadata(
|
|
519
|
+
column.name,
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
expect(metadata.type).toBe(column.type);
|
|
523
|
+
expect(metadata.required).toBe(false);
|
|
524
|
+
|
|
525
|
+
const dbColumn: ColumnMetadataArgs | undefined = columnArgs(
|
|
526
|
+
NetworkAlertPolicy,
|
|
527
|
+
column.name,
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
expect(dbColumn?.options.type).toBe(column.columnType);
|
|
531
|
+
expect(dbColumn?.options.nullable).toBe(true);
|
|
532
|
+
},
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
test("coveredDeviceCount starts at 0, so a never-synced policy reads as covering nothing rather than as blank", () => {
|
|
536
|
+
expect(
|
|
537
|
+
columnArgs(NetworkAlertPolicy, "coveredDeviceCount")?.options.default,
|
|
538
|
+
).toBe(0);
|
|
539
|
+
expect(
|
|
540
|
+
policy.getTableColumnMetadata("coveredDeviceCount").defaultValue,
|
|
541
|
+
).toBe(0);
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
describe("one policy per template per project", () => {
|
|
546
|
+
/*
|
|
547
|
+
* A provisioned monitor's provenance is (device, template) — the unique
|
|
548
|
+
* index Monitor itself carries — so that pair is the only key the engine
|
|
549
|
+
* has for "is this monitor mine?". A template used by two policies would
|
|
550
|
+
* make ownership of every (device, template) monitor ambiguous: both
|
|
551
|
+
* would claim it, the second to provision would fail on Monitor's index
|
|
552
|
+
* forever, and neither could safely tear it down. The service refuses
|
|
553
|
+
* the second policy with a sentence; this index is what makes the
|
|
554
|
+
* refusal true under a race.
|
|
555
|
+
*/
|
|
556
|
+
test("is enforced by a partial unique index on (projectId, monitorTemplateId)", () => {
|
|
557
|
+
const index: IndexMetadataArgs | undefined =
|
|
558
|
+
getMetadataArgsStorage().indices.find(
|
|
559
|
+
(candidate: IndexMetadataArgs): boolean => {
|
|
560
|
+
return (
|
|
561
|
+
candidate.target === NetworkAlertPolicy &&
|
|
562
|
+
Array.isArray(candidate.columns) &&
|
|
563
|
+
candidate.columns.length === 2 &&
|
|
564
|
+
candidate.columns[0] === "projectId" &&
|
|
565
|
+
candidate.columns[1] === "monitorTemplateId"
|
|
566
|
+
);
|
|
567
|
+
},
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
expect(index).toBeDefined();
|
|
571
|
+
expect(index?.unique).toBe(true);
|
|
572
|
+
/*
|
|
573
|
+
* Partial on both counts: a soft-deleted policy must release its
|
|
574
|
+
* template for a replacement, and a policy whose template was SET NULL
|
|
575
|
+
* by a template delete must not collide with the next one in the same
|
|
576
|
+
* state.
|
|
577
|
+
*/
|
|
578
|
+
expect(index?.where).toContain('"deletedAt" IS NULL');
|
|
579
|
+
expect(index?.where).toContain('"monitorTemplateId" IS NOT NULL');
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
describe("the four policy permissions", () => {
|
|
584
|
+
interface PolicyPermissionUnderTest {
|
|
585
|
+
name: string;
|
|
586
|
+
permission: Permission;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/*
|
|
590
|
+
* The names are written out rather than derived from the enum, because
|
|
591
|
+
* the stored value is the half that matters: a team's permission row
|
|
592
|
+
* persists the string, so a value that drifts from its member name
|
|
593
|
+
* orphans every grant already handed out.
|
|
594
|
+
*/
|
|
595
|
+
const POLICY_PERMISSIONS: Array<PolicyPermissionUnderTest> = [
|
|
596
|
+
{
|
|
597
|
+
name: "CreateNetworkAlertPolicy",
|
|
598
|
+
permission: Permission.CreateNetworkAlertPolicy,
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
name: "ReadNetworkAlertPolicy",
|
|
602
|
+
permission: Permission.ReadNetworkAlertPolicy,
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
name: "EditNetworkAlertPolicy",
|
|
606
|
+
permission: Permission.EditNetworkAlertPolicy,
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "DeleteNetworkAlertPolicy",
|
|
610
|
+
permission: Permission.DeleteNetworkAlertPolicy,
|
|
611
|
+
},
|
|
612
|
+
];
|
|
613
|
+
|
|
614
|
+
/*
|
|
615
|
+
* A permission the enum declares but the catalogue does not describe
|
|
616
|
+
* cannot be granted: the team-permission editor renders from
|
|
617
|
+
* getAllPermissionProps.
|
|
618
|
+
*/
|
|
619
|
+
test.each(POLICY_PERMISSIONS)(
|
|
620
|
+
"$name has a catalogue entry in the Monitor group, assignable to a team",
|
|
621
|
+
(entry: PolicyPermissionUnderTest) => {
|
|
622
|
+
const descriptor: PermissionProps | undefined =
|
|
623
|
+
PermissionHelper.getAllPermissionProps().find(
|
|
624
|
+
(props: PermissionProps): boolean => {
|
|
625
|
+
return props.permission === entry.permission;
|
|
626
|
+
},
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
expect(descriptor).toBeDefined();
|
|
630
|
+
expect(descriptor?.group).toBe(PermissionGroup.Monitor);
|
|
631
|
+
expect(descriptor?.isAssignableToTenant).toBe(true);
|
|
632
|
+
expect(descriptor?.title).toContain("Network Alert Policy");
|
|
633
|
+
},
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
test.each(POLICY_PERMISSIONS)(
|
|
637
|
+
"$name is stored under exactly its own name",
|
|
638
|
+
(entry: PolicyPermissionUnderTest) => {
|
|
639
|
+
expect(entry.permission.toString()).toBe(entry.name);
|
|
640
|
+
},
|
|
641
|
+
);
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
/*
|
|
645
|
+
* ---------------------------------------------------------------------------
|
|
646
|
+
* The service's write-time guards.
|
|
647
|
+
* ---------------------------------------------------------------------------
|
|
648
|
+
*/
|
|
649
|
+
|
|
650
|
+
function makeCreateBy(
|
|
651
|
+
overrides: PolicyOverrides = {},
|
|
652
|
+
props: CreateBy<NetworkAlertPolicy>["props"] = { isRoot: true },
|
|
653
|
+
): CreateBy<NetworkAlertPolicy> {
|
|
654
|
+
const policy: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
655
|
+
policy.projectId = PROJECT_ID;
|
|
656
|
+
policy.name = "Warehouse switches";
|
|
657
|
+
policy.monitorTemplateId = TEMPLATE_ID;
|
|
658
|
+
Object.assign(policy, overrides);
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
data: policy,
|
|
662
|
+
props: props,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function makeUpdateBy(
|
|
667
|
+
data: Record<string, unknown>,
|
|
668
|
+
props: UpdateBy<NetworkAlertPolicy>["props"] = {
|
|
669
|
+
isRoot: false,
|
|
670
|
+
tenantId: PROJECT_ID,
|
|
671
|
+
},
|
|
672
|
+
): UpdateBy<NetworkAlertPolicy> {
|
|
673
|
+
return {
|
|
674
|
+
query: { _id: POLICY_ID.toString() },
|
|
675
|
+
data: data,
|
|
676
|
+
props: props,
|
|
677
|
+
} as unknown as UpdateBy<NetworkAlertPolicy>;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/*
|
|
681
|
+
* The template lookup is keyed on BOTH id and project — that query is the
|
|
682
|
+
* whole tenancy check — so the stub answers the way the database would:
|
|
683
|
+
* with the template only when the query names its project, and null for
|
|
684
|
+
* any other project.
|
|
685
|
+
*/
|
|
686
|
+
function mockTemplateOwnedBy(
|
|
687
|
+
ownerProjectId: ObjectID,
|
|
688
|
+
overrides: MonitorTemplateOverrides = {},
|
|
689
|
+
): jest.SpyInstance {
|
|
690
|
+
const monitorTemplate: MonitorTemplate = new MonitorTemplate();
|
|
691
|
+
monitorTemplate._id = TEMPLATE_ID.toString();
|
|
692
|
+
monitorTemplate.projectId = ownerProjectId;
|
|
693
|
+
monitorTemplate.monitorType = MonitorType.NetworkDevice;
|
|
694
|
+
Object.assign(monitorTemplate, overrides);
|
|
695
|
+
|
|
696
|
+
return jest
|
|
697
|
+
.spyOn(MonitorTemplateService, "findOneBy")
|
|
698
|
+
.mockImplementation(
|
|
699
|
+
async (
|
|
700
|
+
findOneBy: FindOneBy<MonitorTemplate>,
|
|
701
|
+
): Promise<MonitorTemplate | null> => {
|
|
702
|
+
const queriedProjectId: unknown = (
|
|
703
|
+
findOneBy.query as Record<string, unknown>
|
|
704
|
+
)["projectId"];
|
|
705
|
+
const queriedId: unknown = (findOneBy.query as Record<string, unknown>)[
|
|
706
|
+
"_id"
|
|
707
|
+
];
|
|
708
|
+
|
|
709
|
+
if (
|
|
710
|
+
queriedProjectId?.toString() === ownerProjectId.toString() &&
|
|
711
|
+
queriedId?.toString() === TEMPLATE_ID.toString()
|
|
712
|
+
) {
|
|
713
|
+
return monitorTemplate;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return null;
|
|
717
|
+
},
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function runOnBeforeCreate(
|
|
722
|
+
createBy: CreateBy<NetworkAlertPolicy>,
|
|
723
|
+
): Promise<{ createBy: CreateBy<NetworkAlertPolicy> }> {
|
|
724
|
+
return (NetworkAlertPolicyService as any).onBeforeCreate(createBy);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function runOnBeforeUpdate(
|
|
728
|
+
updateBy: UpdateBy<NetworkAlertPolicy>,
|
|
729
|
+
): Promise<{ updateBy: UpdateBy<NetworkAlertPolicy> }> {
|
|
730
|
+
return (NetworkAlertPolicyService as any).onBeforeUpdate(updateBy);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/*
|
|
734
|
+
* After the template itself passes, the service asks two more questions —
|
|
735
|
+
* "does another policy in this project already use it?" and "does an
|
|
736
|
+
* auto-import rule?" — through NetworkAlertPolicyService.findBy and
|
|
737
|
+
* NetworkDeviceAutoImportRuleService.findBy. This answers both with "no",
|
|
738
|
+
* and answers the update path's matched-row read (the same findBy, without
|
|
739
|
+
* a monitorTemplateId in its query) with one policy in the caller's
|
|
740
|
+
* project. Tests about the conflicts themselves override it.
|
|
741
|
+
*/
|
|
742
|
+
function mockNoTemplateConflicts(): {
|
|
743
|
+
policies: jest.SpyInstance;
|
|
744
|
+
rules: jest.SpyInstance;
|
|
745
|
+
} {
|
|
746
|
+
const existing: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
747
|
+
existing._id = POLICY_ID.toString();
|
|
748
|
+
existing.projectId = PROJECT_ID;
|
|
749
|
+
|
|
750
|
+
const policies: jest.SpyInstance = jest
|
|
751
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
752
|
+
.mockImplementation(
|
|
753
|
+
async (
|
|
754
|
+
findBy: FindBy<NetworkAlertPolicy>,
|
|
755
|
+
): Promise<Array<NetworkAlertPolicy>> => {
|
|
756
|
+
const query: Record<string, unknown> = findBy.query as Record<
|
|
757
|
+
string,
|
|
758
|
+
unknown
|
|
759
|
+
>;
|
|
760
|
+
|
|
761
|
+
if (query["monitorTemplateId"]) {
|
|
762
|
+
return [];
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return [existing];
|
|
766
|
+
},
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
const rules: jest.SpyInstance = jest
|
|
770
|
+
.spyOn(NetworkDeviceAutoImportRuleService, "findBy")
|
|
771
|
+
.mockResolvedValue([]);
|
|
772
|
+
|
|
773
|
+
return { policies, rules };
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/*
|
|
777
|
+
* The rest of the write path saying "yes".
|
|
778
|
+
*
|
|
779
|
+
* Two more guards run on every accepted write and both would otherwise reach
|
|
780
|
+
* a database (or a permission stack) these tests do not have:
|
|
781
|
+
*
|
|
782
|
+
* - THE CALLER MAY CAUSE MONITORS TO EXIST: the Monitor table's create
|
|
783
|
+
* permission, plus a read of the template with the CALLER's own props.
|
|
784
|
+
* Stubbed to allow, so the cases about templates and scopes are about
|
|
785
|
+
* templates and scopes; the block about the permission itself removes
|
|
786
|
+
* these stubs and asserts the refusals.
|
|
787
|
+
* - EVERY SCOPE ID BELONGS TO THIS PROJECT: one read per non-empty kind
|
|
788
|
+
* against NetworkSite, NetworkDeviceRole and Label. Stubbed to echo back
|
|
789
|
+
* whatever ids the query asked about, i.e. "they all exist here"; the
|
|
790
|
+
* scope block overrides it to answer with a missing row.
|
|
791
|
+
*/
|
|
792
|
+
function mockScopeAndPermissionChecksPass(): {
|
|
793
|
+
monitorCreateAllowed: jest.SpyInstance;
|
|
794
|
+
templateReadableByCaller: jest.SpyInstance;
|
|
795
|
+
} {
|
|
796
|
+
const monitorCreateAllowed: jest.SpyInstance = jest
|
|
797
|
+
.spyOn(TablePermission, "checkTableLevelPermissions")
|
|
798
|
+
.mockImplementation((): void => {
|
|
799
|
+
return undefined;
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
jest
|
|
803
|
+
.spyOn(TablePermission, "checkTableLevelBlockPermissions")
|
|
804
|
+
.mockImplementation((): void => {
|
|
805
|
+
return undefined;
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const readableTemplate: MonitorTemplate = new MonitorTemplate();
|
|
809
|
+
readableTemplate._id = TEMPLATE_ID.toString();
|
|
810
|
+
|
|
811
|
+
const templateReadableByCaller: jest.SpyInstance = jest
|
|
812
|
+
.spyOn(MonitorTemplateService, "findOneById")
|
|
813
|
+
.mockResolvedValue(readableTemplate);
|
|
814
|
+
|
|
815
|
+
const echoQueriedIds: (
|
|
816
|
+
findBy: FindBy<BaseModel>,
|
|
817
|
+
) => Promise<Array<BaseModel>> = async (
|
|
818
|
+
findBy: FindBy<BaseModel>,
|
|
819
|
+
): Promise<Array<BaseModel>> => {
|
|
820
|
+
/*
|
|
821
|
+
* The service asks with an `Includes` so the ids stay readable — both to
|
|
822
|
+
* this stub and to whoever debugs the query later.
|
|
823
|
+
*/
|
|
824
|
+
const queried: Includes = (findBy.query as Record<string, unknown>)[
|
|
825
|
+
"_id"
|
|
826
|
+
] as Includes;
|
|
827
|
+
|
|
828
|
+
return (queried?.values || []).map(
|
|
829
|
+
(id: string | ObjectID | number): BaseModel => {
|
|
830
|
+
const row: NetworkSite = new NetworkSite();
|
|
831
|
+
row._id = id.toString();
|
|
832
|
+
|
|
833
|
+
return row as unknown as BaseModel;
|
|
834
|
+
},
|
|
835
|
+
);
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
jest
|
|
839
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
840
|
+
.mockImplementation(echoQueriedIds as never);
|
|
841
|
+
jest
|
|
842
|
+
.spyOn(NetworkDeviceRoleService, "findBy")
|
|
843
|
+
.mockImplementation(echoQueriedIds as never);
|
|
844
|
+
jest
|
|
845
|
+
.spyOn(LabelService, "findBy")
|
|
846
|
+
.mockImplementation(echoQueriedIds as never);
|
|
847
|
+
|
|
848
|
+
return { monitorCreateAllowed, templateReadableByCaller };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function policyRow(id: ObjectID, projectId: ObjectID): NetworkAlertPolicy {
|
|
852
|
+
const policy: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
853
|
+
policy._id = id.toString();
|
|
854
|
+
policy.projectId = projectId;
|
|
855
|
+
|
|
856
|
+
return policy;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
describe("NetworkAlertPolicyService.onBeforeCreate", () => {
|
|
860
|
+
beforeEach(() => {
|
|
861
|
+
mockNoTemplateConflicts();
|
|
862
|
+
mockScopeAndPermissionChecksPass();
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
afterEach(() => {
|
|
866
|
+
jest.restoreAllMocks();
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test("accepts a Network Device template from the policy's own project", async () => {
|
|
870
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
871
|
+
|
|
872
|
+
const result: { createBy: CreateBy<NetworkAlertPolicy> } =
|
|
873
|
+
await runOnBeforeCreate(makeCreateBy());
|
|
874
|
+
|
|
875
|
+
expect(result.createBy.data.monitorTemplateId?.toString()).toBe(
|
|
876
|
+
TEMPLATE_ID.toString(),
|
|
877
|
+
);
|
|
878
|
+
/*
|
|
879
|
+
* The tenancy check IS the query. A lookup by id alone, followed by a
|
|
880
|
+
* comparison, would still be correct — but the query form is what makes
|
|
881
|
+
* "other project" and "does not exist" the same answer.
|
|
882
|
+
*/
|
|
883
|
+
expect(findSpy).toHaveBeenCalledWith(
|
|
884
|
+
expect.objectContaining({
|
|
885
|
+
query: expect.objectContaining({
|
|
886
|
+
_id: TEMPLATE_ID,
|
|
887
|
+
projectId: PROJECT_ID,
|
|
888
|
+
}),
|
|
889
|
+
}),
|
|
890
|
+
);
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
/*
|
|
894
|
+
* THE INVARIANT THIS BLOCK EXISTS FOR. The FK only proves the template
|
|
895
|
+
* row exists; a policy pointing at another tenant's template would have
|
|
896
|
+
* the engine clone it, as root, into this project. And the refusal must
|
|
897
|
+
* not say "wrong project", because that would confirm the id exists.
|
|
898
|
+
*/
|
|
899
|
+
test("refuses a template from another project as not found", async () => {
|
|
900
|
+
mockTemplateOwnedBy(OTHER_PROJECT_ID);
|
|
901
|
+
|
|
902
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
903
|
+
"Monitor Template not found.",
|
|
904
|
+
);
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
test("refuses a template that does not exist with the same message", async () => {
|
|
908
|
+
jest.spyOn(MonitorTemplateService, "findOneBy").mockResolvedValue(null);
|
|
909
|
+
|
|
910
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
911
|
+
"Monitor Template not found.",
|
|
912
|
+
);
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
/*
|
|
916
|
+
* Belt and braces: even if the lookup ever stopped filtering by project,
|
|
917
|
+
* a returned row from elsewhere is still refused, with the same message.
|
|
918
|
+
*/
|
|
919
|
+
test("refuses a returned template whose projectId disagrees, as not found", async () => {
|
|
920
|
+
const monitorTemplate: MonitorTemplate = new MonitorTemplate();
|
|
921
|
+
monitorTemplate.projectId = OTHER_PROJECT_ID;
|
|
922
|
+
monitorTemplate.monitorType = MonitorType.NetworkDevice;
|
|
923
|
+
|
|
924
|
+
jest
|
|
925
|
+
.spyOn(MonitorTemplateService, "findOneBy")
|
|
926
|
+
.mockResolvedValue(monitorTemplate);
|
|
927
|
+
|
|
928
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
929
|
+
"Monitor Template not found.",
|
|
930
|
+
);
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
test("refuses a template that is not a Network Device template", async () => {
|
|
934
|
+
mockTemplateOwnedBy(PROJECT_ID, { monitorType: MonitorType.Ping });
|
|
935
|
+
|
|
936
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
937
|
+
"Monitor Template must be a Network Device monitor template.",
|
|
938
|
+
);
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
test("refuses a template with no type at all", async () => {
|
|
942
|
+
mockTemplateOwnedBy(PROJECT_ID, { monitorType: undefined });
|
|
943
|
+
|
|
944
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
945
|
+
BadDataException,
|
|
946
|
+
);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
/*
|
|
950
|
+
* The dashboard posts the relation object, not the FK column. A guard
|
|
951
|
+
* that only read `monitorTemplateId` would wave every UI-created policy
|
|
952
|
+
* through unchecked.
|
|
953
|
+
*/
|
|
954
|
+
test("reads the template under the relation spelling the dashboard posts", async () => {
|
|
955
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(OTHER_PROJECT_ID);
|
|
956
|
+
|
|
957
|
+
const monitorTemplate: MonitorTemplate = new MonitorTemplate();
|
|
958
|
+
monitorTemplate._id = TEMPLATE_ID.toString();
|
|
959
|
+
|
|
960
|
+
await expect(
|
|
961
|
+
runOnBeforeCreate(
|
|
962
|
+
makeCreateBy({
|
|
963
|
+
monitorTemplateId: undefined,
|
|
964
|
+
monitorTemplate: monitorTemplate,
|
|
965
|
+
}),
|
|
966
|
+
),
|
|
967
|
+
).rejects.toThrow("Monitor Template not found.");
|
|
968
|
+
|
|
969
|
+
expect(findSpy).toHaveBeenCalledWith(
|
|
970
|
+
expect.objectContaining({
|
|
971
|
+
query: expect.objectContaining({ _id: TEMPLATE_ID }),
|
|
972
|
+
}),
|
|
973
|
+
);
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
test("refuses a payload whose two template spellings disagree, before any lookup", async () => {
|
|
977
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
978
|
+
|
|
979
|
+
const monitorTemplate: MonitorTemplate = new MonitorTemplate();
|
|
980
|
+
monitorTemplate._id = OTHER_TEMPLATE_ID.toString();
|
|
981
|
+
|
|
982
|
+
await expect(
|
|
983
|
+
runOnBeforeCreate(
|
|
984
|
+
makeCreateBy({
|
|
985
|
+
monitorTemplateId: TEMPLATE_ID,
|
|
986
|
+
monitorTemplate: monitorTemplate,
|
|
987
|
+
}),
|
|
988
|
+
),
|
|
989
|
+
).rejects.toThrow("Conflicting Monitor Template references were provided.");
|
|
990
|
+
|
|
991
|
+
expect(findSpy).not.toHaveBeenCalled();
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
test("refuses a policy with no template", async () => {
|
|
995
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
996
|
+
|
|
997
|
+
await expect(
|
|
998
|
+
runOnBeforeCreate(makeCreateBy({ monitorTemplateId: undefined })),
|
|
999
|
+
).rejects.toThrow("Monitor Template is required.");
|
|
1000
|
+
await expect(
|
|
1001
|
+
runOnBeforeCreate(
|
|
1002
|
+
makeCreateBy({
|
|
1003
|
+
monitorTemplateId: null as unknown as ObjectID,
|
|
1004
|
+
}),
|
|
1005
|
+
),
|
|
1006
|
+
).rejects.toThrow("Monitor Template is required.");
|
|
1007
|
+
|
|
1008
|
+
expect(findSpy).not.toHaveBeenCalled();
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
test("takes the project from the caller's tenant when the data carries none", async () => {
|
|
1012
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
1013
|
+
|
|
1014
|
+
await runOnBeforeCreate(
|
|
1015
|
+
makeCreateBy(
|
|
1016
|
+
{ projectId: undefined },
|
|
1017
|
+
{ isRoot: false, tenantId: PROJECT_ID },
|
|
1018
|
+
),
|
|
1019
|
+
);
|
|
1020
|
+
|
|
1021
|
+
expect(findSpy).toHaveBeenCalledWith(
|
|
1022
|
+
expect.objectContaining({
|
|
1023
|
+
query: expect.objectContaining({ projectId: PROJECT_ID }),
|
|
1024
|
+
}),
|
|
1025
|
+
);
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
test("refuses a create with no project anywhere", async () => {
|
|
1029
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
1030
|
+
|
|
1031
|
+
await expect(
|
|
1032
|
+
runOnBeforeCreate(
|
|
1033
|
+
makeCreateBy({ projectId: undefined }, { isRoot: true }),
|
|
1034
|
+
),
|
|
1035
|
+
).rejects.toThrow("Project ID is required");
|
|
1036
|
+
|
|
1037
|
+
expect(findSpy).not.toHaveBeenCalled();
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
/*
|
|
1041
|
+
* name is unique per project. Untrimmed, "Warehouse " and "Warehouse"
|
|
1042
|
+
* are two policies to the database and one to everybody reading the
|
|
1043
|
+
* table.
|
|
1044
|
+
*/
|
|
1045
|
+
test("trims the name", async () => {
|
|
1046
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1047
|
+
|
|
1048
|
+
const result: { createBy: CreateBy<NetworkAlertPolicy> } =
|
|
1049
|
+
await runOnBeforeCreate(
|
|
1050
|
+
makeCreateBy({ name: " Warehouse switches \n" }),
|
|
1051
|
+
);
|
|
1052
|
+
|
|
1053
|
+
expect(result.createBy.data.name).toBe("Warehouse switches");
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
test.each(["", " ", "\t\n", undefined, null])(
|
|
1057
|
+
"refuses a name of %p",
|
|
1058
|
+
async (name: string | undefined | null) => {
|
|
1059
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1060
|
+
|
|
1061
|
+
await expect(
|
|
1062
|
+
runOnBeforeCreate(makeCreateBy({ name: name as string | undefined })),
|
|
1063
|
+
).rejects.toThrow("Name is required.");
|
|
1064
|
+
},
|
|
1065
|
+
);
|
|
1066
|
+
|
|
1067
|
+
/*
|
|
1068
|
+
* The engine reads the column on every device event and never runs
|
|
1069
|
+
* normalize on the read path for the dedup — the canonical form has to be
|
|
1070
|
+
* what is stored.
|
|
1071
|
+
*/
|
|
1072
|
+
test("stores the scope in its canonical form", async () => {
|
|
1073
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1074
|
+
|
|
1075
|
+
const result: { createBy: CreateBy<NetworkAlertPolicy> } =
|
|
1076
|
+
await runOnBeforeCreate(
|
|
1077
|
+
makeCreateBy({
|
|
1078
|
+
scope: {
|
|
1079
|
+
siteIds: [SITE_ID, SITE_ID, " ", null],
|
|
1080
|
+
// A lone string is one id — a client that sent it meant a list.
|
|
1081
|
+
networkDeviceRoleIds: ROLE_ID,
|
|
1082
|
+
stray: true,
|
|
1083
|
+
} as unknown as NetworkAlertPolicyScope,
|
|
1084
|
+
}),
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
expect(result.createBy.data.scope).toEqual({
|
|
1088
|
+
siteIds: [SITE_ID],
|
|
1089
|
+
networkDeviceRoleIds: [ROLE_ID],
|
|
1090
|
+
labelIds: [],
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
test("writes the empty scope when none was sent, so the column is never null", async () => {
|
|
1095
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1096
|
+
|
|
1097
|
+
const result: { createBy: CreateBy<NetworkAlertPolicy> } =
|
|
1098
|
+
await runOnBeforeCreate(makeCreateBy({ scope: undefined }));
|
|
1099
|
+
|
|
1100
|
+
expect(result.createBy.data.scope).toEqual({
|
|
1101
|
+
siteIds: [],
|
|
1102
|
+
networkDeviceRoleIds: [],
|
|
1103
|
+
labelIds: [],
|
|
1104
|
+
});
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
/*
|
|
1108
|
+
* A provisioned monitor's provenance is (device, template) — Monitor's
|
|
1109
|
+
* own unique index — so one template has room for one owner. The second
|
|
1110
|
+
* policy on a template would fail on that index at the first shared
|
|
1111
|
+
* device while both claimed the monitor; the operator is told here, in a
|
|
1112
|
+
* sentence, rather than by the constraint violation from the partial
|
|
1113
|
+
* unique index that backs this check.
|
|
1114
|
+
*/
|
|
1115
|
+
test("refuses a template another policy in the project already uses", async () => {
|
|
1116
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1117
|
+
|
|
1118
|
+
const policiesSpy: jest.SpyInstance = jest
|
|
1119
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
1120
|
+
.mockResolvedValue([policyRow(OTHER_POLICY_ID, PROJECT_ID)]);
|
|
1121
|
+
|
|
1122
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
1123
|
+
"Another alert policy already uses this Monitor Template.",
|
|
1124
|
+
);
|
|
1125
|
+
|
|
1126
|
+
// Root and project-keyed: the question is about THIS project's policies.
|
|
1127
|
+
expect(policiesSpy).toHaveBeenCalledWith(
|
|
1128
|
+
expect.objectContaining({
|
|
1129
|
+
query: expect.objectContaining({
|
|
1130
|
+
projectId: PROJECT_ID,
|
|
1131
|
+
monitorTemplateId: TEMPLATE_ID,
|
|
1132
|
+
}),
|
|
1133
|
+
props: { isRoot: true },
|
|
1134
|
+
}),
|
|
1135
|
+
);
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
/*
|
|
1139
|
+
* The same collision from the other side: the rule provisions on import,
|
|
1140
|
+
* the policy then finds a monitor that looks like its own and takes it
|
|
1141
|
+
* down when the device leaves the scope.
|
|
1142
|
+
*/
|
|
1143
|
+
test("refuses a template an auto-import rule in the project references", async () => {
|
|
1144
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1145
|
+
|
|
1146
|
+
const rule: NetworkDeviceAutoImportRule = new NetworkDeviceAutoImportRule();
|
|
1147
|
+
rule.projectId = PROJECT_ID;
|
|
1148
|
+
rule.monitorTemplateId = TEMPLATE_ID;
|
|
1149
|
+
|
|
1150
|
+
const rulesSpy: jest.SpyInstance = jest
|
|
1151
|
+
.spyOn(NetworkDeviceAutoImportRuleService, "findBy")
|
|
1152
|
+
.mockResolvedValue([rule]);
|
|
1153
|
+
|
|
1154
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
1155
|
+
"This Monitor Template is used by an auto-import rule; pick a different template.",
|
|
1156
|
+
);
|
|
1157
|
+
|
|
1158
|
+
expect(rulesSpy).toHaveBeenCalledWith(
|
|
1159
|
+
expect.objectContaining({
|
|
1160
|
+
query: expect.objectContaining({
|
|
1161
|
+
projectId: PROJECT_ID,
|
|
1162
|
+
monitorTemplateId: TEMPLATE_ID,
|
|
1163
|
+
}),
|
|
1164
|
+
props: { isRoot: true },
|
|
1165
|
+
}),
|
|
1166
|
+
);
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
/*
|
|
1170
|
+
* Ordering: a template that fails its own checks never reaches the claim
|
|
1171
|
+
* checks, so "not found" for another tenant's template stays "not found"
|
|
1172
|
+
* and cannot leak whether that template is in use anywhere.
|
|
1173
|
+
*/
|
|
1174
|
+
test("asks about claims only after the template itself has passed", async () => {
|
|
1175
|
+
mockTemplateOwnedBy(OTHER_PROJECT_ID);
|
|
1176
|
+
|
|
1177
|
+
const spies: { policies: jest.SpyInstance; rules: jest.SpyInstance } =
|
|
1178
|
+
mockNoTemplateConflicts();
|
|
1179
|
+
|
|
1180
|
+
await expect(runOnBeforeCreate(makeCreateBy())).rejects.toThrow(
|
|
1181
|
+
"Monitor Template not found.",
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
expect(spies.policies).not.toHaveBeenCalled();
|
|
1185
|
+
expect(spies.rules).not.toHaveBeenCalled();
|
|
1186
|
+
});
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
describe("NetworkAlertPolicyService.onBeforeUpdate", () => {
|
|
1190
|
+
beforeEach(() => {
|
|
1191
|
+
mockNoTemplateConflicts();
|
|
1192
|
+
mockScopeAndPermissionChecksPass();
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
afterEach(() => {
|
|
1196
|
+
jest.restoreAllMocks();
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
test("leaves the template alone, and looks nothing up, when the update does not touch it", async () => {
|
|
1200
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
1201
|
+
const policiesSpy: jest.SpyInstance = jest
|
|
1202
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
1203
|
+
.mockResolvedValue([]);
|
|
1204
|
+
|
|
1205
|
+
const result: { updateBy: UpdateBy<NetworkAlertPolicy> } =
|
|
1206
|
+
await runOnBeforeUpdate(makeUpdateBy({ description: "Updated" }));
|
|
1207
|
+
|
|
1208
|
+
expect(result.updateBy.data).toEqual({ description: "Updated" });
|
|
1209
|
+
expect(findSpy).not.toHaveBeenCalled();
|
|
1210
|
+
expect(policiesSpy).not.toHaveBeenCalled();
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
test("checks a re-pointed template against the caller's tenant", async () => {
|
|
1214
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
1215
|
+
|
|
1216
|
+
await runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID }));
|
|
1217
|
+
|
|
1218
|
+
expect(findSpy).toHaveBeenCalledWith(
|
|
1219
|
+
expect.objectContaining({
|
|
1220
|
+
query: expect.objectContaining({
|
|
1221
|
+
_id: TEMPLATE_ID,
|
|
1222
|
+
projectId: PROJECT_ID,
|
|
1223
|
+
}),
|
|
1224
|
+
}),
|
|
1225
|
+
);
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
test("refuses re-pointing at another project's template as not found", async () => {
|
|
1229
|
+
mockTemplateOwnedBy(OTHER_PROJECT_ID);
|
|
1230
|
+
|
|
1231
|
+
await expect(
|
|
1232
|
+
runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID })),
|
|
1233
|
+
).rejects.toThrow("Monitor Template not found.");
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
test("refuses re-pointing at a template that is not a Network Device template", async () => {
|
|
1237
|
+
mockTemplateOwnedBy(PROJECT_ID, { monitorType: MonitorType.Website });
|
|
1238
|
+
|
|
1239
|
+
await expect(
|
|
1240
|
+
runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID })),
|
|
1241
|
+
).rejects.toThrow(
|
|
1242
|
+
"Monitor Template must be a Network Device monitor template.",
|
|
1243
|
+
);
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1246
|
+
test("reads the relation spelling on update too", async () => {
|
|
1247
|
+
mockTemplateOwnedBy(OTHER_PROJECT_ID);
|
|
1248
|
+
|
|
1249
|
+
await expect(
|
|
1250
|
+
runOnBeforeUpdate(
|
|
1251
|
+
makeUpdateBy({ monitorTemplate: { _id: TEMPLATE_ID.toString() } }),
|
|
1252
|
+
),
|
|
1253
|
+
).rejects.toThrow("Monitor Template not found.");
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
test("refuses conflicting spellings on update", async () => {
|
|
1257
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
1258
|
+
|
|
1259
|
+
await expect(
|
|
1260
|
+
runOnBeforeUpdate(
|
|
1261
|
+
makeUpdateBy({
|
|
1262
|
+
monitorTemplateId: TEMPLATE_ID,
|
|
1263
|
+
monitorTemplate: { _id: OTHER_TEMPLATE_ID.toString() },
|
|
1264
|
+
}),
|
|
1265
|
+
),
|
|
1266
|
+
).rejects.toThrow("Conflicting Monitor Template references were provided.");
|
|
1267
|
+
|
|
1268
|
+
expect(findSpy).not.toHaveBeenCalled();
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
/*
|
|
1272
|
+
* The database may null the column (SET NULL on template delete); the API
|
|
1273
|
+
* may not. A policy with no template provisions nothing, and the operator
|
|
1274
|
+
* has isEnabled for switching one off.
|
|
1275
|
+
*/
|
|
1276
|
+
test.each([
|
|
1277
|
+
{ monitorTemplateId: null },
|
|
1278
|
+
{ monitorTemplate: null },
|
|
1279
|
+
{ monitorTemplateId: "" },
|
|
1280
|
+
])(
|
|
1281
|
+
"refuses clearing the template with %p",
|
|
1282
|
+
async (data: Record<string, unknown>) => {
|
|
1283
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1284
|
+
|
|
1285
|
+
await expect(runOnBeforeUpdate(makeUpdateBy(data))).rejects.toThrow(
|
|
1286
|
+
"Monitor Template is required.",
|
|
1287
|
+
);
|
|
1288
|
+
},
|
|
1289
|
+
);
|
|
1290
|
+
|
|
1291
|
+
/*
|
|
1292
|
+
* A root caller with no tenant — a worker updating by id — has no props
|
|
1293
|
+
* to take the project from, so the matched rows are read (as root, since
|
|
1294
|
+
* the hook runs before the query is permission-checked) and the template
|
|
1295
|
+
* is checked against each distinct project among them.
|
|
1296
|
+
*/
|
|
1297
|
+
test("for a tenant-less root caller, takes the project from the matched rows", async () => {
|
|
1298
|
+
const findSpy: jest.SpyInstance = mockTemplateOwnedBy(PROJECT_ID);
|
|
1299
|
+
|
|
1300
|
+
const existing: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
1301
|
+
existing._id = POLICY_ID.toString();
|
|
1302
|
+
existing.projectId = PROJECT_ID;
|
|
1303
|
+
|
|
1304
|
+
const policiesSpy: jest.SpyInstance = jest
|
|
1305
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
1306
|
+
.mockResolvedValue([existing, existing]);
|
|
1307
|
+
|
|
1308
|
+
await runOnBeforeUpdate(
|
|
1309
|
+
makeUpdateBy({ monitorTemplateId: TEMPLATE_ID }, { isRoot: true }),
|
|
1310
|
+
);
|
|
1311
|
+
|
|
1312
|
+
expect(policiesSpy).toHaveBeenCalledWith(
|
|
1313
|
+
expect.objectContaining({
|
|
1314
|
+
query: { _id: POLICY_ID.toString() },
|
|
1315
|
+
props: { isRoot: true },
|
|
1316
|
+
}),
|
|
1317
|
+
);
|
|
1318
|
+
// One distinct project among the matched rows: one template lookup.
|
|
1319
|
+
expect(findSpy).toHaveBeenCalledTimes(1);
|
|
1320
|
+
expect(findSpy).toHaveBeenCalledWith(
|
|
1321
|
+
expect.objectContaining({
|
|
1322
|
+
query: expect.objectContaining({ projectId: PROJECT_ID }),
|
|
1323
|
+
}),
|
|
1324
|
+
);
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
test("for a tenant-less root caller, a row in another project refuses the template", async () => {
|
|
1328
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1329
|
+
|
|
1330
|
+
const foreign: NetworkAlertPolicy = new NetworkAlertPolicy();
|
|
1331
|
+
foreign.projectId = OTHER_PROJECT_ID;
|
|
1332
|
+
|
|
1333
|
+
jest
|
|
1334
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
1335
|
+
.mockResolvedValue([foreign]);
|
|
1336
|
+
|
|
1337
|
+
await expect(
|
|
1338
|
+
runOnBeforeUpdate(
|
|
1339
|
+
makeUpdateBy({ monitorTemplateId: TEMPLATE_ID }, { isRoot: true }),
|
|
1340
|
+
),
|
|
1341
|
+
).rejects.toThrow("Monitor Template not found.");
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
test("trims a written name and refuses a blank one", async () => {
|
|
1345
|
+
const trimmed: { updateBy: UpdateBy<NetworkAlertPolicy> } =
|
|
1346
|
+
await runOnBeforeUpdate(makeUpdateBy({ name: " Depot routers " }));
|
|
1347
|
+
|
|
1348
|
+
expect(trimmed.updateBy.data.name).toBe("Depot routers");
|
|
1349
|
+
|
|
1350
|
+
await expect(
|
|
1351
|
+
runOnBeforeUpdate(makeUpdateBy({ name: " " })),
|
|
1352
|
+
).rejects.toThrow("Name is required.");
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
test("normalizes a written scope", async () => {
|
|
1356
|
+
const result: { updateBy: UpdateBy<NetworkAlertPolicy> } =
|
|
1357
|
+
await runOnBeforeUpdate(
|
|
1358
|
+
makeUpdateBy({
|
|
1359
|
+
scope: { labelIds: [SITE_ID, SITE_ID, ""], siteIds: null },
|
|
1360
|
+
}),
|
|
1361
|
+
);
|
|
1362
|
+
|
|
1363
|
+
expect(result.updateBy.data.scope).toEqual({
|
|
1364
|
+
siteIds: [],
|
|
1365
|
+
networkDeviceRoleIds: [],
|
|
1366
|
+
labelIds: [SITE_ID],
|
|
1367
|
+
});
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
test("normalizes a scope written as null to the empty scope rather than storing null", async () => {
|
|
1371
|
+
const result: { updateBy: UpdateBy<NetworkAlertPolicy> } =
|
|
1372
|
+
await runOnBeforeUpdate(makeUpdateBy({ scope: null }));
|
|
1373
|
+
|
|
1374
|
+
expect(result.updateBy.data.scope).toEqual({
|
|
1375
|
+
siteIds: [],
|
|
1376
|
+
networkDeviceRoleIds: [],
|
|
1377
|
+
labelIds: [],
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
/*
|
|
1382
|
+
* Re-saving a policy with the template it already holds is not a
|
|
1383
|
+
* conflict: the rows the update matches are left out of "another
|
|
1384
|
+
* policy". Without this every edit of a policy from the settings form —
|
|
1385
|
+
* which posts the whole row, template included — would refuse itself.
|
|
1386
|
+
*/
|
|
1387
|
+
test("a policy keeping its own template is not in conflict with itself", async () => {
|
|
1388
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1389
|
+
|
|
1390
|
+
jest
|
|
1391
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
1392
|
+
.mockResolvedValue([policyRow(POLICY_ID, PROJECT_ID)]);
|
|
1393
|
+
|
|
1394
|
+
await expect(
|
|
1395
|
+
runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID })),
|
|
1396
|
+
).resolves.toBeDefined();
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
test("refuses re-pointing at a template another policy already uses", async () => {
|
|
1400
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1401
|
+
|
|
1402
|
+
jest
|
|
1403
|
+
.spyOn(NetworkAlertPolicyService, "findBy")
|
|
1404
|
+
.mockImplementation(
|
|
1405
|
+
async (
|
|
1406
|
+
findBy: FindBy<NetworkAlertPolicy>,
|
|
1407
|
+
): Promise<Array<NetworkAlertPolicy>> => {
|
|
1408
|
+
const query: Record<string, unknown> = findBy.query as Record<
|
|
1409
|
+
string,
|
|
1410
|
+
unknown
|
|
1411
|
+
>;
|
|
1412
|
+
|
|
1413
|
+
// The matched-row read sees the policy itself; the claim read sees both.
|
|
1414
|
+
return query["monitorTemplateId"]
|
|
1415
|
+
? [
|
|
1416
|
+
policyRow(POLICY_ID, PROJECT_ID),
|
|
1417
|
+
policyRow(OTHER_POLICY_ID, PROJECT_ID),
|
|
1418
|
+
]
|
|
1419
|
+
: [policyRow(POLICY_ID, PROJECT_ID)];
|
|
1420
|
+
},
|
|
1421
|
+
);
|
|
1422
|
+
|
|
1423
|
+
await expect(
|
|
1424
|
+
runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID })),
|
|
1425
|
+
).rejects.toThrow(
|
|
1426
|
+
"Another alert policy already uses this Monitor Template.",
|
|
1427
|
+
);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
test("refuses re-pointing at a template an auto-import rule references", async () => {
|
|
1431
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1432
|
+
|
|
1433
|
+
jest
|
|
1434
|
+
.spyOn(NetworkDeviceAutoImportRuleService, "findBy")
|
|
1435
|
+
.mockResolvedValue([new NetworkDeviceAutoImportRule()]);
|
|
1436
|
+
|
|
1437
|
+
await expect(
|
|
1438
|
+
runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID })),
|
|
1439
|
+
).rejects.toThrow(
|
|
1440
|
+
"This Monitor Template is used by an auto-import rule; pick a different template.",
|
|
1441
|
+
);
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
/*
|
|
1445
|
+
* onBeforeUpdate runs before DatabaseService permission-checks the query,
|
|
1446
|
+
* so the matched-row read — root, because it must see projectId — is
|
|
1447
|
+
* re-scoped to the caller's tenant first. A guessed id from another
|
|
1448
|
+
* project matches nothing here rather than becoming an oracle through the
|
|
1449
|
+
* checks that follow.
|
|
1450
|
+
*/
|
|
1451
|
+
test("scopes the matched-row read to the caller's tenant", async () => {
|
|
1452
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1453
|
+
|
|
1454
|
+
const spies: { policies: jest.SpyInstance; rules: jest.SpyInstance } =
|
|
1455
|
+
mockNoTemplateConflicts();
|
|
1456
|
+
|
|
1457
|
+
await runOnBeforeUpdate(makeUpdateBy({ monitorTemplateId: TEMPLATE_ID }));
|
|
1458
|
+
|
|
1459
|
+
expect(spies.policies).toHaveBeenCalledWith(
|
|
1460
|
+
expect.objectContaining({
|
|
1461
|
+
query: { _id: POLICY_ID.toString(), projectId: PROJECT_ID },
|
|
1462
|
+
props: { isRoot: true },
|
|
1463
|
+
}),
|
|
1464
|
+
);
|
|
1465
|
+
});
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
/*
|
|
1469
|
+
* PREREQUISITE 9. The scope is three lists of ids in a jsonb column with no
|
|
1470
|
+
* foreign keys behind them, so this hook is the only thing between a pasted
|
|
1471
|
+
* id and a policy that quietly covers nothing. Each non-empty kind is read
|
|
1472
|
+
* back against the policy's own project.
|
|
1473
|
+
*/
|
|
1474
|
+
describe("NetworkAlertPolicyService scope id validation", () => {
|
|
1475
|
+
beforeEach(() => {
|
|
1476
|
+
mockNoTemplateConflicts();
|
|
1477
|
+
mockScopeAndPermissionChecksPass();
|
|
1478
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
afterEach(() => {
|
|
1482
|
+
jest.restoreAllMocks();
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
test("accepts a scope whose sites, roles and labels all exist in the project", async () => {
|
|
1486
|
+
await expect(
|
|
1487
|
+
runOnBeforeCreate(
|
|
1488
|
+
makeCreateBy({
|
|
1489
|
+
scope: {
|
|
1490
|
+
siteIds: [SITE_ID],
|
|
1491
|
+
networkDeviceRoleIds: [ROLE_ID],
|
|
1492
|
+
labelIds: [LABEL_ID],
|
|
1493
|
+
},
|
|
1494
|
+
}),
|
|
1495
|
+
),
|
|
1496
|
+
).resolves.toBeDefined();
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
test("reads each kind against the policy's own project", async () => {
|
|
1500
|
+
const siteSpy: jest.SpyInstance = jest.spyOn(NetworkSiteService, "findBy");
|
|
1501
|
+
|
|
1502
|
+
await runOnBeforeCreate(makeCreateBy({ scope: { siteIds: [SITE_ID] } }));
|
|
1503
|
+
|
|
1504
|
+
/*
|
|
1505
|
+
* projectId in the QUERY, not compared after the fact: that is what makes
|
|
1506
|
+
* "a site in another project" and "a site that does not exist" the same
|
|
1507
|
+
* answer, so the refusal cannot confirm another tenant's site id.
|
|
1508
|
+
*/
|
|
1509
|
+
expect(siteSpy).toHaveBeenCalledWith(
|
|
1510
|
+
expect.objectContaining({
|
|
1511
|
+
query: expect.objectContaining({ projectId: PROJECT_ID }),
|
|
1512
|
+
props: { isRoot: true },
|
|
1513
|
+
}),
|
|
1514
|
+
);
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
test("refuses a site id that belongs to another project", async () => {
|
|
1518
|
+
// The row exists somewhere; it is simply not in THIS project's read.
|
|
1519
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([]);
|
|
1520
|
+
|
|
1521
|
+
await expect(
|
|
1522
|
+
runOnBeforeCreate(makeCreateBy({ scope: { siteIds: [SITE_ID] } })),
|
|
1523
|
+
).rejects.toThrow(
|
|
1524
|
+
`Network Site ${SITE_ID} does not belong to this project.`,
|
|
1525
|
+
);
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1528
|
+
test("refuses a role id that belongs to another project", async () => {
|
|
1529
|
+
jest.spyOn(NetworkDeviceRoleService, "findBy").mockResolvedValue([]);
|
|
1530
|
+
|
|
1531
|
+
await expect(
|
|
1532
|
+
runOnBeforeCreate(
|
|
1533
|
+
makeCreateBy({ scope: { networkDeviceRoleIds: [ROLE_ID] } }),
|
|
1534
|
+
),
|
|
1535
|
+
).rejects.toThrow(
|
|
1536
|
+
`Network Device Role ${ROLE_ID} does not belong to this project.`,
|
|
1537
|
+
);
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
test("refuses a label id that belongs to another project", async () => {
|
|
1541
|
+
jest.spyOn(LabelService, "findBy").mockResolvedValue([]);
|
|
1542
|
+
|
|
1543
|
+
await expect(
|
|
1544
|
+
runOnBeforeCreate(makeCreateBy({ scope: { labelIds: [LABEL_ID] } })),
|
|
1545
|
+
).rejects.toThrow(`Label ${LABEL_ID} does not belong to this project.`);
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
/*
|
|
1549
|
+
* A non-UUID never reaches Postgres. `uuid = 'nonsense'` is a statement
|
|
1550
|
+
* ERROR rather than an empty result, so one hand-crafted API call would
|
|
1551
|
+
* otherwise take down every sweep that read the row afterwards.
|
|
1552
|
+
*/
|
|
1553
|
+
test("refuses an id that is not a UUID without asking the database", async () => {
|
|
1554
|
+
const siteSpy: jest.SpyInstance = jest.spyOn(NetworkSiteService, "findBy");
|
|
1555
|
+
|
|
1556
|
+
await expect(
|
|
1557
|
+
runOnBeforeCreate(
|
|
1558
|
+
makeCreateBy({
|
|
1559
|
+
scope: { siteIds: ["'; drop table --"] },
|
|
1560
|
+
}),
|
|
1561
|
+
),
|
|
1562
|
+
).rejects.toThrow(BadDataException);
|
|
1563
|
+
|
|
1564
|
+
expect(siteSpy).not.toHaveBeenCalled();
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
// An empty kind matches every device, so there is nothing to look up.
|
|
1568
|
+
test("asks nothing for an unscoped policy", async () => {
|
|
1569
|
+
const siteSpy: jest.SpyInstance = jest.spyOn(NetworkSiteService, "findBy");
|
|
1570
|
+
const roleSpy: jest.SpyInstance = jest.spyOn(
|
|
1571
|
+
NetworkDeviceRoleService,
|
|
1572
|
+
"findBy",
|
|
1573
|
+
);
|
|
1574
|
+
const labelSpy: jest.SpyInstance = jest.spyOn(LabelService, "findBy");
|
|
1575
|
+
|
|
1576
|
+
await runOnBeforeCreate(makeCreateBy({ scope: {} }));
|
|
1577
|
+
|
|
1578
|
+
expect(siteSpy).not.toHaveBeenCalled();
|
|
1579
|
+
expect(roleSpy).not.toHaveBeenCalled();
|
|
1580
|
+
expect(labelSpy).not.toHaveBeenCalled();
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
test("validates the scope an update writes, too", async () => {
|
|
1584
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([]);
|
|
1585
|
+
|
|
1586
|
+
await expect(
|
|
1587
|
+
runOnBeforeUpdate(makeUpdateBy({ scope: { siteIds: [SITE_ID] } })),
|
|
1588
|
+
).rejects.toThrow(
|
|
1589
|
+
`Network Site ${SITE_ID} does not belong to this project.`,
|
|
1590
|
+
);
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
/*
|
|
1594
|
+
* An update that does not touch the scope must not re-validate the stored
|
|
1595
|
+
* one. Ids go stale — a site is deleted, a label is renamed away — and a
|
|
1596
|
+
* policy that could no longer be RENAMED because one of its sites is gone
|
|
1597
|
+
* would be a trap. Stale ids match nothing; that is the whole contract.
|
|
1598
|
+
*/
|
|
1599
|
+
test("does not re-validate a stored scope an update does not touch", async () => {
|
|
1600
|
+
const siteSpy: jest.SpyInstance = jest.spyOn(NetworkSiteService, "findBy");
|
|
1601
|
+
|
|
1602
|
+
await runOnBeforeUpdate(makeUpdateBy({ isEnabled: false }));
|
|
1603
|
+
|
|
1604
|
+
expect(siteSpy).not.toHaveBeenCalled();
|
|
1605
|
+
});
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
/*
|
|
1609
|
+
* PREREQUISITE 4. A policy row is cheap; the monitors it provisions are
|
|
1610
|
+
* billed. So the write takes the MONITOR table's create permission and a read
|
|
1611
|
+
* of the template through the CALLER's own scopes — the same pair
|
|
1612
|
+
* NetworkDeviceAutoImportRuleService requires before a rule may name a
|
|
1613
|
+
* template.
|
|
1614
|
+
*/
|
|
1615
|
+
describe("NetworkAlertPolicyService monitor-provisioning permission", () => {
|
|
1616
|
+
beforeEach(() => {
|
|
1617
|
+
mockNoTemplateConflicts();
|
|
1618
|
+
mockTemplateOwnedBy(PROJECT_ID);
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
afterEach(() => {
|
|
1622
|
+
jest.restoreAllMocks();
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1625
|
+
function allowMonitorCreate(): {
|
|
1626
|
+
allow: jest.SpyInstance;
|
|
1627
|
+
block: jest.SpyInstance;
|
|
1628
|
+
} {
|
|
1629
|
+
const allow: jest.SpyInstance = jest
|
|
1630
|
+
.spyOn(TablePermission, "checkTableLevelPermissions")
|
|
1631
|
+
.mockImplementation((): void => {
|
|
1632
|
+
return undefined;
|
|
1633
|
+
});
|
|
1634
|
+
const block: jest.SpyInstance = jest
|
|
1635
|
+
.spyOn(TablePermission, "checkTableLevelBlockPermissions")
|
|
1636
|
+
.mockImplementation((): void => {
|
|
1637
|
+
return undefined;
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
return { allow, block };
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
/*
|
|
1644
|
+
* makeCreateBy defaults to a ROOT caller, which is exempt from both checks
|
|
1645
|
+
* (a worker or a seeder has no user to check). Every case in this block is
|
|
1646
|
+
* about a real API caller, so each one says so.
|
|
1647
|
+
*/
|
|
1648
|
+
const API_CALLER: CreateBy<NetworkAlertPolicy>["props"] = {
|
|
1649
|
+
isRoot: false,
|
|
1650
|
+
tenantId: PROJECT_ID,
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
function templateVisibleToCaller(): jest.SpyInstance {
|
|
1654
|
+
const template: MonitorTemplate = new MonitorTemplate();
|
|
1655
|
+
template._id = TEMPLATE_ID.toString();
|
|
1656
|
+
|
|
1657
|
+
return jest
|
|
1658
|
+
.spyOn(MonitorTemplateService, "findOneById")
|
|
1659
|
+
.mockResolvedValue(template);
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
test("a create checks both the allow and the block half of Monitor create", async () => {
|
|
1663
|
+
const permissions: { allow: jest.SpyInstance; block: jest.SpyInstance } =
|
|
1664
|
+
allowMonitorCreate();
|
|
1665
|
+
templateVisibleToCaller();
|
|
1666
|
+
|
|
1667
|
+
await runOnBeforeCreate(makeCreateBy({}, API_CALLER));
|
|
1668
|
+
|
|
1669
|
+
expect(permissions.allow).toHaveBeenCalledWith(
|
|
1670
|
+
Monitor,
|
|
1671
|
+
expect.anything(),
|
|
1672
|
+
DatabaseRequestType.Create,
|
|
1673
|
+
);
|
|
1674
|
+
expect(permissions.block).toHaveBeenCalledWith(
|
|
1675
|
+
Monitor,
|
|
1676
|
+
expect.anything(),
|
|
1677
|
+
DatabaseRequestType.Create,
|
|
1678
|
+
);
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1681
|
+
/*
|
|
1682
|
+
* The template read carries the caller's props, not root. A user who may
|
|
1683
|
+
* edit policies but cannot SEE a template must not be able to attach it and
|
|
1684
|
+
* have the engine clone it as root for the whole fleet.
|
|
1685
|
+
*/
|
|
1686
|
+
test("a create reads the template with the caller's own props", async () => {
|
|
1687
|
+
allowMonitorCreate();
|
|
1688
|
+
const readSpy: jest.SpyInstance = templateVisibleToCaller();
|
|
1689
|
+
|
|
1690
|
+
await runOnBeforeCreate(makeCreateBy({}, API_CALLER));
|
|
1691
|
+
|
|
1692
|
+
expect(readSpy).toHaveBeenCalledWith(
|
|
1693
|
+
expect.objectContaining({
|
|
1694
|
+
id: TEMPLATE_ID,
|
|
1695
|
+
props: expect.objectContaining({ tenantId: PROJECT_ID }),
|
|
1696
|
+
}),
|
|
1697
|
+
);
|
|
1698
|
+
});
|
|
1699
|
+
|
|
1700
|
+
/*
|
|
1701
|
+
* ...and the refusal is the SAME "not found" the project check gives, so a
|
|
1702
|
+
* caller cannot use the difference to enumerate template ids they are not
|
|
1703
|
+
* allowed to see.
|
|
1704
|
+
*/
|
|
1705
|
+
test("a template the caller cannot see is refused as not found", async () => {
|
|
1706
|
+
allowMonitorCreate();
|
|
1707
|
+
jest.spyOn(MonitorTemplateService, "findOneById").mockResolvedValue(null);
|
|
1708
|
+
|
|
1709
|
+
await expect(
|
|
1710
|
+
runOnBeforeCreate(makeCreateBy({}, API_CALLER)),
|
|
1711
|
+
).rejects.toThrow("Monitor Template not found.");
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
/*
|
|
1715
|
+
* A root caller has no user and no label scopes to check — a worker, a
|
|
1716
|
+
* seeder, the recommended-policy bootstrap. Exempting it is what keeps
|
|
1717
|
+
* those paths working; it is not a hole, because reaching root at all
|
|
1718
|
+
* already means the request never came from a browser.
|
|
1719
|
+
*/
|
|
1720
|
+
test("a root caller is exempt from both checks", async () => {
|
|
1721
|
+
const permissions: { allow: jest.SpyInstance; block: jest.SpyInstance } =
|
|
1722
|
+
allowMonitorCreate();
|
|
1723
|
+
const readSpy: jest.SpyInstance = jest.spyOn(
|
|
1724
|
+
MonitorTemplateService,
|
|
1725
|
+
"findOneById",
|
|
1726
|
+
);
|
|
1727
|
+
|
|
1728
|
+
await expect(runOnBeforeCreate(makeCreateBy())).resolves.toBeDefined();
|
|
1729
|
+
|
|
1730
|
+
expect(permissions.allow).not.toHaveBeenCalled();
|
|
1731
|
+
expect(readSpy).not.toHaveBeenCalled();
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
test("a caller without Monitor create permission cannot save a policy", async () => {
|
|
1735
|
+
templateVisibleToCaller();
|
|
1736
|
+
jest
|
|
1737
|
+
.spyOn(TablePermission, "checkTableLevelPermissions")
|
|
1738
|
+
.mockImplementation((): void => {
|
|
1739
|
+
throw new NotAuthorizedException("no monitor create");
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
await expect(
|
|
1743
|
+
runOnBeforeCreate(makeCreateBy({}, API_CALLER)),
|
|
1744
|
+
).rejects.toThrow("no monitor create");
|
|
1745
|
+
});
|
|
1746
|
+
|
|
1747
|
+
/*
|
|
1748
|
+
* ENABLING is the same decision as saving, one day later: the moment the
|
|
1749
|
+
* switch goes on the engine starts creating billable monitors. A payload
|
|
1750
|
+
* that touches nothing but `isEnabled: true` therefore takes the same two
|
|
1751
|
+
* checks.
|
|
1752
|
+
*/
|
|
1753
|
+
test("enabling a policy takes the Monitor create permission", async () => {
|
|
1754
|
+
templateVisibleToCaller();
|
|
1755
|
+
mockScopeAndPermissionChecksPass();
|
|
1756
|
+
jest
|
|
1757
|
+
.spyOn(TablePermission, "checkTableLevelPermissions")
|
|
1758
|
+
.mockImplementation((): void => {
|
|
1759
|
+
throw new NotAuthorizedException("no monitor create");
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
jest.spyOn(NetworkAlertPolicyService, "findBy").mockResolvedValue([
|
|
1763
|
+
Object.assign(policyRow(POLICY_ID, PROJECT_ID), {
|
|
1764
|
+
monitorTemplateId: TEMPLATE_ID,
|
|
1765
|
+
}),
|
|
1766
|
+
]);
|
|
1767
|
+
|
|
1768
|
+
await expect(
|
|
1769
|
+
runOnBeforeUpdate(makeUpdateBy({ isEnabled: true })),
|
|
1770
|
+
).rejects.toThrow("no monitor create");
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
/*
|
|
1774
|
+
* DISABLING does not. Taking a policy out of service must stay available to
|
|
1775
|
+
* whoever can edit it, including after their Monitor permissions were
|
|
1776
|
+
* narrowed — otherwise the only way to stop a runaway policy would be to
|
|
1777
|
+
* delete it.
|
|
1778
|
+
*/
|
|
1779
|
+
test("disabling a policy does not take the Monitor create permission", async () => {
|
|
1780
|
+
mockScopeAndPermissionChecksPass();
|
|
1781
|
+
const allow: jest.SpyInstance = jest
|
|
1782
|
+
.spyOn(TablePermission, "checkTableLevelPermissions")
|
|
1783
|
+
.mockImplementation((): void => {
|
|
1784
|
+
throw new NotAuthorizedException("no monitor create");
|
|
1785
|
+
});
|
|
1786
|
+
|
|
1787
|
+
await expect(
|
|
1788
|
+
runOnBeforeUpdate(makeUpdateBy({ isEnabled: false })),
|
|
1789
|
+
).resolves.toBeDefined();
|
|
1790
|
+
|
|
1791
|
+
expect(allow).not.toHaveBeenCalled();
|
|
1792
|
+
});
|
|
1793
|
+
});
|