@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
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import NetworkSiteService from "../../../Server/Services/NetworkSiteService";
|
|
2
|
+
import DatabaseService from "../../../Server/Services/DatabaseService";
|
|
3
|
+
import NetworkSiteTypeService from "../../../Server/Services/NetworkSiteTypeService";
|
|
2
4
|
import NetworkSiteStatusTimelineService from "../../../Server/Services/NetworkSiteStatusTimelineService";
|
|
3
5
|
import NetworkDeviceService from "../../../Server/Services/NetworkDeviceService";
|
|
4
6
|
import NetworkSiteMaintenanceSuppression from "../../../Server/Utils/NetworkSite/NetworkSiteMaintenanceSuppression";
|
|
7
|
+
import NetworkSiteHierarchyLock, {
|
|
8
|
+
NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE,
|
|
9
|
+
} from "../../../Server/Utils/NetworkSite/NetworkSiteHierarchyLock";
|
|
5
10
|
import MonitorService from "../../../Server/Services/MonitorService";
|
|
6
11
|
import MonitorStatusService from "../../../Server/Services/MonitorStatusService";
|
|
7
12
|
import NetworkSite from "../../../Models/DatabaseModels/NetworkSite";
|
|
@@ -10,6 +15,8 @@ import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
|
10
15
|
import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
|
|
11
16
|
import NetworkSiteStatusTimeline from "../../../Models/DatabaseModels/NetworkSiteStatusTimeline";
|
|
12
17
|
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
18
|
+
import NetworkDeviceMonitoringMethod from "../../../Types/NetworkDevice/NetworkDeviceMonitoringMethod";
|
|
19
|
+
import logger from "../../../Server/Utils/Logger";
|
|
13
20
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
14
21
|
import ObjectID from "../../../Types/ObjectID";
|
|
15
22
|
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
@@ -22,7 +29,26 @@ import {
|
|
|
22
29
|
DeviceHealthGroup,
|
|
23
30
|
} from "../../../Server/Utils/NetworkDevice/DeviceHealthAggregation";
|
|
24
31
|
import { AggregateColumn } from "../../../Server/Types/Database/AggregateBy";
|
|
25
|
-
import { describe, expect, it, afterEach } from "@jest/globals";
|
|
32
|
+
import { describe, expect, it, afterEach, beforeEach } from "@jest/globals";
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* NetworkSiteService only needs the type service's findOneById boundary.
|
|
36
|
+
* Replacing that boundary keeps this focused unit suite from loading the
|
|
37
|
+
* reciprocal NetworkSiteTypeService -> NetworkSiteService graph (and all of
|
|
38
|
+
* its monitoring dependencies) before each isolated hierarchy scenario.
|
|
39
|
+
*/
|
|
40
|
+
jest.mock("../../../Server/Services/NetworkSiteTypeService", () => {
|
|
41
|
+
return {
|
|
42
|
+
__esModule: true,
|
|
43
|
+
default: {
|
|
44
|
+
findOneById: jest.fn(),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
jest.clearAllMocks();
|
|
51
|
+
});
|
|
26
52
|
|
|
27
53
|
/*
|
|
28
54
|
* Contract under test - the persisted rollup engine and the hierarchy
|
|
@@ -61,6 +87,18 @@ const DEVICE_ID: ObjectID = new ObjectID(
|
|
|
61
87
|
const OTHER_PROJECT_ID: ObjectID = new ObjectID(
|
|
62
88
|
"44444444-4444-4444-8444-444444444444",
|
|
63
89
|
);
|
|
90
|
+
const ROOT_SITE_TYPE_ID: ObjectID = new ObjectID(
|
|
91
|
+
"55555555-5555-4555-8555-555555555555",
|
|
92
|
+
);
|
|
93
|
+
const CHILD_SITE_TYPE_ID: ObjectID = new ObjectID(
|
|
94
|
+
"66666666-6666-4666-8666-666666666666",
|
|
95
|
+
);
|
|
96
|
+
const ALTERNATE_ROOT_SITE_TYPE_ID: ObjectID = new ObjectID(
|
|
97
|
+
"77777777-7777-4777-8777-777777777777",
|
|
98
|
+
);
|
|
99
|
+
const GRANDCHILD_SITE_TYPE_ID: ObjectID = new ObjectID(
|
|
100
|
+
"88888888-8888-4888-8888-888888888888",
|
|
101
|
+
);
|
|
64
102
|
|
|
65
103
|
function fakeSite(overrides: Record<string, unknown>): NetworkSite {
|
|
66
104
|
return {
|
|
@@ -71,6 +109,33 @@ function fakeSite(overrides: Record<string, unknown>): NetworkSite {
|
|
|
71
109
|
} as unknown as NetworkSite;
|
|
72
110
|
}
|
|
73
111
|
|
|
112
|
+
function fakeNetworkSiteType(data: {
|
|
113
|
+
id: ObjectID;
|
|
114
|
+
parentNetworkSiteTypeId?: ObjectID | undefined;
|
|
115
|
+
projectId?: ObjectID | undefined;
|
|
116
|
+
}): any {
|
|
117
|
+
return {
|
|
118
|
+
id: data.id,
|
|
119
|
+
_id: data.id.toString(),
|
|
120
|
+
projectId: data.projectId || PROJECT_ID,
|
|
121
|
+
parentNetworkSiteTypeId: data.parentNetworkSiteTypeId,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function mockNetworkSiteTypes(types: Array<any>): jest.SpyInstance {
|
|
126
|
+
const typesById: Map<string, any> = new Map(
|
|
127
|
+
types.map((type: any): [string, any] => {
|
|
128
|
+
return [type.id.toString(), type];
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return jest
|
|
133
|
+
.spyOn(NetworkSiteTypeService, "findOneById")
|
|
134
|
+
.mockImplementation((input: any) => {
|
|
135
|
+
return Promise.resolve(typesById.get(input.id.toString()) || null);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
74
139
|
function fakeStatuses(): Array<MonitorStatus> {
|
|
75
140
|
return [
|
|
76
141
|
{
|
|
@@ -216,9 +281,18 @@ describe("NetworkSiteService.recomputeRollupForSite", () => {
|
|
|
216
281
|
const spies: RollupSpies = setupRollup({
|
|
217
282
|
site: fakeSite({ currentMonitorStatusId: OPERATIONAL_STATUS_ID }),
|
|
218
283
|
devices: [
|
|
284
|
+
/*
|
|
285
|
+
* An ordinary probe-polled device - no monitoringMethod, which parses
|
|
286
|
+
* to Probe - whose last poll got nothing back. Its poll outcome is
|
|
287
|
+
* its vote, so the site's worst-of lands on the project's offline row
|
|
288
|
+
* and the timeline has to roll.
|
|
289
|
+
*/
|
|
219
290
|
{
|
|
220
291
|
id: DEVICE_ID,
|
|
221
|
-
|
|
292
|
+
isReachable: false,
|
|
293
|
+
lastPolledAt: new Date(Date.now() - 60 * 1000),
|
|
294
|
+
lastSeenAt: new Date(Date.now() - 60 * 60 * 1000),
|
|
295
|
+
pollingIntervalInMinutes: 5,
|
|
222
296
|
},
|
|
223
297
|
] as unknown as Array<NetworkDevice>,
|
|
224
298
|
});
|
|
@@ -255,9 +329,20 @@ describe("NetworkSiteService.recomputeRollupForSite", () => {
|
|
|
255
329
|
const spies: RollupSpies = setupRollup({
|
|
256
330
|
site: fakeSite({ currentMonitorStatusId: OFFLINE_STATUS_ID }),
|
|
257
331
|
devices: [
|
|
332
|
+
/*
|
|
333
|
+
* The device votes offline (its poll got nothing back) and the site
|
|
334
|
+
* already reads offline. The device has to CONTRIBUTE a verdict for
|
|
335
|
+
* this to be the "unchanged" case rather than the "nothing to say"
|
|
336
|
+
* case - a device with no poll columns at all would be Pending, which
|
|
337
|
+
* takes the same branch for an entirely different reason and would
|
|
338
|
+
* leave this assertion passing while proving nothing.
|
|
339
|
+
*/
|
|
258
340
|
{
|
|
259
341
|
id: DEVICE_ID,
|
|
260
|
-
|
|
342
|
+
isReachable: false,
|
|
343
|
+
lastPolledAt: new Date(Date.now() - 60 * 1000),
|
|
344
|
+
lastSeenAt: new Date(Date.now() - 60 * 60 * 1000),
|
|
345
|
+
pollingIntervalInMinutes: 5,
|
|
261
346
|
},
|
|
262
347
|
] as unknown as Array<NetworkDevice>,
|
|
263
348
|
});
|
|
@@ -285,13 +370,78 @@ describe("NetworkSiteService.recomputeRollupForSite", () => {
|
|
|
285
370
|
expect(spies.timelineCreate).not.toHaveBeenCalled();
|
|
286
371
|
});
|
|
287
372
|
|
|
288
|
-
it("
|
|
373
|
+
it("a probe-polled device with no stamped status votes with its poll", async () => {
|
|
374
|
+
const spies: RollupSpies = setupRollup({
|
|
375
|
+
site: fakeSite({ currentMonitorStatusId: OPERATIONAL_STATUS_ID }),
|
|
376
|
+
devices: [
|
|
377
|
+
{
|
|
378
|
+
id: DEVICE_ID,
|
|
379
|
+
// Nothing has ever stamped it, and its last poll got nothing back.
|
|
380
|
+
isReachable: false,
|
|
381
|
+
lastPolledAt: new Date(Date.now() - 60 * 1000),
|
|
382
|
+
lastSeenAt: new Date(Date.now() - 60 * 60 * 1000),
|
|
383
|
+
pollingIntervalInMinutes: 5,
|
|
384
|
+
},
|
|
385
|
+
] as unknown as Array<NetworkDevice>,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
await NetworkSiteService.recomputeRollupForSite(SITE_ID);
|
|
389
|
+
|
|
390
|
+
const updateArgs: any = spies.updateColumns.mock.calls[0]![0];
|
|
391
|
+
expect(updateArgs.data.currentMonitorStatusId.toString()).toBe(
|
|
392
|
+
OFFLINE_STATUS_ID.toString(),
|
|
393
|
+
);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
/*
|
|
397
|
+
* Health precedence, and which device the site card is describing.
|
|
398
|
+
*
|
|
399
|
+
* A stamped MonitorStatus is a device's verdict only on a MONITOR-BACKED
|
|
400
|
+
* device. A Network Device monitor watching a switch's SNMP walk stamps the
|
|
401
|
+
* device it watches, so an "interface down -> Offline" criterion stamps
|
|
402
|
+
* Offline on a switch that answers every ping - and letting that vote
|
|
403
|
+
* turned this site card and the topology node above it red while every
|
|
404
|
+
* device row underneath read Up. The pill, the card and the map now read
|
|
405
|
+
* the same rule: for a probe-polled device the poll decides.
|
|
406
|
+
*/
|
|
407
|
+
it("a probe-polled device stamped Offline does not turn the site offline while it answers", async () => {
|
|
408
|
+
const spies: RollupSpies = setupRollup({
|
|
409
|
+
site: fakeSite({ currentMonitorStatusId: OFFLINE_STATUS_ID }),
|
|
410
|
+
devices: [
|
|
411
|
+
{
|
|
412
|
+
id: DEVICE_ID,
|
|
413
|
+
// The stamp its own monitor wrote for a dark interface...
|
|
414
|
+
currentMonitorStatusId: OFFLINE_STATUS_ID,
|
|
415
|
+
// ...on a switch that answered its last poll.
|
|
416
|
+
isReachable: true,
|
|
417
|
+
lastPolledAt: new Date(Date.now() - 60 * 1000),
|
|
418
|
+
lastSeenAt: new Date(Date.now() - 60 * 1000),
|
|
419
|
+
pollingIntervalInMinutes: 5,
|
|
420
|
+
},
|
|
421
|
+
] as unknown as Array<NetworkDevice>,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
await NetworkSiteService.recomputeRollupForSite(SITE_ID);
|
|
425
|
+
|
|
426
|
+
const updateArgs: any = spies.updateColumns.mock.calls[0]![0];
|
|
427
|
+
expect(updateArgs.data.currentMonitorStatusId.toString()).toBe(
|
|
428
|
+
OPERATIONAL_STATUS_ID.toString(),
|
|
429
|
+
);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it("the same probe-polled device DOES turn the site offline once its poll fails", async () => {
|
|
433
|
+
/*
|
|
434
|
+
* The stamp is unchanged; only the poll outcome moved, and the site
|
|
435
|
+
* follows it. This is the half of the rule that stops "the stamp does not
|
|
436
|
+
* vote" from quietly becoming "a probe-polled device can never take its
|
|
437
|
+
* site offline" - which would be the same bug with the colours swapped.
|
|
438
|
+
*/
|
|
289
439
|
const spies: RollupSpies = setupRollup({
|
|
290
440
|
site: fakeSite({ currentMonitorStatusId: OPERATIONAL_STATUS_ID }),
|
|
291
441
|
devices: [
|
|
292
442
|
{
|
|
293
443
|
id: DEVICE_ID,
|
|
294
|
-
|
|
444
|
+
currentMonitorStatusId: OFFLINE_STATUS_ID,
|
|
295
445
|
isReachable: false,
|
|
296
446
|
lastPolledAt: new Date(Date.now() - 60 * 1000),
|
|
297
447
|
lastSeenAt: new Date(Date.now() - 60 * 60 * 1000),
|
|
@@ -308,6 +458,32 @@ describe("NetworkSiteService.recomputeRollupForSite", () => {
|
|
|
308
458
|
);
|
|
309
459
|
});
|
|
310
460
|
|
|
461
|
+
it("a monitor-backed device still votes with its stamped status", async () => {
|
|
462
|
+
/*
|
|
463
|
+
* The override, and the reason the stamp is carried at all: nothing polls
|
|
464
|
+
* this device, so it has no poll columns whatsoever. Reachability alone
|
|
465
|
+
* would call it Pending and the site would keep whatever it said before;
|
|
466
|
+
* its bound Monitor's status is its entire verdict, and it is offline.
|
|
467
|
+
*/
|
|
468
|
+
const spies: RollupSpies = setupRollup({
|
|
469
|
+
site: fakeSite({ currentMonitorStatusId: OPERATIONAL_STATUS_ID }),
|
|
470
|
+
devices: [
|
|
471
|
+
{
|
|
472
|
+
id: DEVICE_ID,
|
|
473
|
+
monitoringMethod: NetworkDeviceMonitoringMethod.Monitor,
|
|
474
|
+
currentMonitorStatusId: OFFLINE_STATUS_ID,
|
|
475
|
+
},
|
|
476
|
+
] as unknown as Array<NetworkDevice>,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
await NetworkSiteService.recomputeRollupForSite(SITE_ID);
|
|
480
|
+
|
|
481
|
+
const updateArgs: any = spies.updateColumns.mock.calls[0]![0];
|
|
482
|
+
expect(updateArgs.data.currentMonitorStatusId.toString()).toBe(
|
|
483
|
+
OFFLINE_STATUS_ID.toString(),
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
|
|
311
487
|
/*
|
|
312
488
|
* Issue #3220 at the site card. A probe behind on a large fleet leaves
|
|
313
489
|
* every device's last successful poll well outside the old fixed
|
|
@@ -428,9 +604,13 @@ describe("NetworkSiteService.recomputeRollupForSite", () => {
|
|
|
428
604
|
const spies: RollupSpies = setupRollup({
|
|
429
605
|
site: fakeSite({ currentMonitorStatusId: OPERATIONAL_STATUS_ID }),
|
|
430
606
|
devices: [
|
|
607
|
+
// One ordinary probe-polled device, and its last poll got nothing back.
|
|
431
608
|
{
|
|
432
609
|
id: DEVICE_ID,
|
|
433
|
-
|
|
610
|
+
isReachable: false,
|
|
611
|
+
lastPolledAt: new Date(Date.now() - 60 * 1000),
|
|
612
|
+
lastSeenAt: new Date(Date.now() - 60 * 60 * 1000),
|
|
613
|
+
pollingIntervalInMinutes: 5,
|
|
434
614
|
},
|
|
435
615
|
] as unknown as Array<NetworkDevice>,
|
|
436
616
|
maintainedSiteIds: [],
|
|
@@ -656,15 +836,23 @@ describe("NetworkSiteService.onBeforeUpdate (cycle rejection)", () => {
|
|
|
656
836
|
});
|
|
657
837
|
|
|
658
838
|
it("allows a legal move and carries the previous state forward", async () => {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
839
|
+
mockNetworkSiteTypes([
|
|
840
|
+
fakeNetworkSiteType({
|
|
841
|
+
id: CHILD_SITE_TYPE_ID,
|
|
842
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
843
|
+
}),
|
|
844
|
+
]);
|
|
845
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([
|
|
846
|
+
fakeSite({
|
|
847
|
+
materializedPath: `/${SITE_ID.toString()}/`,
|
|
848
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
849
|
+
}),
|
|
850
|
+
]);
|
|
664
851
|
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
665
852
|
fakeSite({
|
|
666
853
|
id: PARENT_SITE_ID,
|
|
667
854
|
_id: PARENT_SITE_ID.toString(),
|
|
855
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
668
856
|
}),
|
|
669
857
|
);
|
|
670
858
|
jest
|
|
@@ -736,15 +924,23 @@ describe("NetworkSiteService.onBeforeUpdate (cycle rejection)", () => {
|
|
|
736
924
|
});
|
|
737
925
|
|
|
738
926
|
it("carries a legal move made through the `parentSite` relation key", async () => {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
927
|
+
mockNetworkSiteTypes([
|
|
928
|
+
fakeNetworkSiteType({
|
|
929
|
+
id: CHILD_SITE_TYPE_ID,
|
|
930
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
931
|
+
}),
|
|
932
|
+
]);
|
|
933
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([
|
|
934
|
+
fakeSite({
|
|
935
|
+
materializedPath: `/${SITE_ID.toString()}/`,
|
|
936
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
937
|
+
}),
|
|
938
|
+
]);
|
|
744
939
|
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
745
940
|
fakeSite({
|
|
746
941
|
id: PARENT_SITE_ID,
|
|
747
942
|
_id: PARENT_SITE_ID.toString(),
|
|
943
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
748
944
|
}),
|
|
749
945
|
);
|
|
750
946
|
jest
|
|
@@ -782,6 +978,67 @@ describe("NetworkSiteService.onBeforeUpdate (cycle rejection)", () => {
|
|
|
782
978
|
expect(result.carryForward).toBeNull();
|
|
783
979
|
expect(findBySpy).not.toHaveBeenCalled();
|
|
784
980
|
});
|
|
981
|
+
|
|
982
|
+
it("rejects moving a site to another project", async () => {
|
|
983
|
+
jest
|
|
984
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
985
|
+
.mockResolvedValue([fakeSite({ projectId: PROJECT_ID })]);
|
|
986
|
+
|
|
987
|
+
await expect(
|
|
988
|
+
(NetworkSiteService as any).onBeforeUpdate({
|
|
989
|
+
query: { _id: SITE_ID.toString() },
|
|
990
|
+
data: { projectId: OTHER_PROJECT_ID },
|
|
991
|
+
props: { isRoot: true },
|
|
992
|
+
} as unknown as UpdateBy<NetworkSite>),
|
|
993
|
+
).rejects.toThrow("cannot be moved to another project");
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
it.each([
|
|
997
|
+
["materializedPath", `/${SITE_ID.toString()}/`],
|
|
998
|
+
["depth", 0],
|
|
999
|
+
])(
|
|
1000
|
+
"rejects a direct write to server-managed %s",
|
|
1001
|
+
async (field: string, value: string | number) => {
|
|
1002
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
1003
|
+
NetworkSiteService,
|
|
1004
|
+
"findBy",
|
|
1005
|
+
);
|
|
1006
|
+
|
|
1007
|
+
await expect(
|
|
1008
|
+
(NetworkSiteService as any).onBeforeUpdate({
|
|
1009
|
+
query: { _id: SITE_ID.toString() },
|
|
1010
|
+
data: { [field]: value },
|
|
1011
|
+
props: { isRoot: true },
|
|
1012
|
+
} as unknown as UpdateBy<NetworkSite>),
|
|
1013
|
+
).rejects.toThrow("cannot be updated directly");
|
|
1014
|
+
|
|
1015
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
1016
|
+
},
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
it("treats undefined relation properties on a model instance as omitted", async () => {
|
|
1020
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
1021
|
+
NetworkSiteService,
|
|
1022
|
+
"findBy",
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
const result: OnUpdate<NetworkSite> = await (
|
|
1026
|
+
NetworkSiteService as any
|
|
1027
|
+
).onBeforeUpdate({
|
|
1028
|
+
query: { _id: SITE_ID.toString() },
|
|
1029
|
+
data: {
|
|
1030
|
+
name: "renamed",
|
|
1031
|
+
parentSite: undefined,
|
|
1032
|
+
parentSiteId: undefined,
|
|
1033
|
+
networkSiteType: undefined,
|
|
1034
|
+
networkSiteTypeId: undefined,
|
|
1035
|
+
},
|
|
1036
|
+
props: { isRoot: true },
|
|
1037
|
+
} as unknown as UpdateBy<NetworkSite>);
|
|
1038
|
+
|
|
1039
|
+
expect(result.carryForward).toBeNull();
|
|
1040
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
1041
|
+
});
|
|
785
1042
|
});
|
|
786
1043
|
|
|
787
1044
|
/*
|
|
@@ -870,6 +1127,23 @@ describe("NetworkSiteService.onBeforeUpdate (tenant scoping)", () => {
|
|
|
870
1127
|
expect(result.carryForward.previousItems).toHaveLength(1);
|
|
871
1128
|
expect(result.carryForward.newParentPath).toBeNull();
|
|
872
1129
|
});
|
|
1130
|
+
|
|
1131
|
+
it("reads the same limit and skip window that the bulk update will write", async () => {
|
|
1132
|
+
const findBySpy: jest.SpyInstance = jest
|
|
1133
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1134
|
+
.mockResolvedValue([]);
|
|
1135
|
+
|
|
1136
|
+
await (NetworkSiteService as any).onBeforeUpdate({
|
|
1137
|
+
query: {},
|
|
1138
|
+
data: { parentSiteId: null },
|
|
1139
|
+
limit: 7,
|
|
1140
|
+
skip: 3,
|
|
1141
|
+
props: { tenantId: PROJECT_ID },
|
|
1142
|
+
} as unknown as UpdateBy<NetworkSite>);
|
|
1143
|
+
|
|
1144
|
+
expect(findBySpy.mock.calls[0]![0].limit).toBe(7);
|
|
1145
|
+
expect(findBySpy.mock.calls[0]![0].skip).toBe(3);
|
|
1146
|
+
});
|
|
873
1147
|
});
|
|
874
1148
|
|
|
875
1149
|
describe("NetworkSiteService.onBeforeCreate (cross-project parent guard)", () => {
|
|
@@ -918,10 +1192,17 @@ describe("NetworkSiteService.onBeforeCreate (cross-project parent guard)", () =>
|
|
|
918
1192
|
});
|
|
919
1193
|
|
|
920
1194
|
it("carries the parent path forward for a same-project parent", async () => {
|
|
1195
|
+
mockNetworkSiteTypes([
|
|
1196
|
+
fakeNetworkSiteType({
|
|
1197
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1198
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1199
|
+
}),
|
|
1200
|
+
]);
|
|
921
1201
|
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
922
1202
|
fakeSite({
|
|
923
1203
|
id: PARENT_SITE_ID,
|
|
924
1204
|
_id: PARENT_SITE_ID.toString(),
|
|
1205
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
925
1206
|
}),
|
|
926
1207
|
);
|
|
927
1208
|
jest
|
|
@@ -932,6 +1213,7 @@ describe("NetworkSiteService.onBeforeCreate (cross-project parent guard)", () =>
|
|
|
932
1213
|
data: {
|
|
933
1214
|
projectId: PROJECT_ID,
|
|
934
1215
|
parentSiteId: PARENT_SITE_ID,
|
|
1216
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
935
1217
|
},
|
|
936
1218
|
props: { tenantId: PROJECT_ID },
|
|
937
1219
|
});
|
|
@@ -942,88 +1224,852 @@ describe("NetworkSiteService.onBeforeCreate (cross-project parent guard)", () =>
|
|
|
942
1224
|
});
|
|
943
1225
|
});
|
|
944
1226
|
|
|
945
|
-
describe("NetworkSiteService
|
|
1227
|
+
describe("NetworkSiteService site-type hierarchy enforcement on create", () => {
|
|
946
1228
|
afterEach(() => {
|
|
947
1229
|
jest.restoreAllMocks();
|
|
948
1230
|
});
|
|
949
1231
|
|
|
950
|
-
it("
|
|
951
|
-
const
|
|
952
|
-
|
|
953
|
-
);
|
|
954
|
-
const oldParentId: ObjectID = PARENT_SITE_ID;
|
|
955
|
-
const newParentId: ObjectID = new ObjectID(
|
|
956
|
-
"ffffffff-ffff-4fff-8fff-ffffffffffff",
|
|
957
|
-
);
|
|
958
|
-
|
|
959
|
-
const oldPath: string = `/${oldParentId.toString()}/${SITE_ID.toString()}/`;
|
|
960
|
-
const oldChildPath: string = `${oldPath}${childId.toString()}/`;
|
|
961
|
-
|
|
962
|
-
const updateColumnsSpy: jest.SpyInstance = jest
|
|
963
|
-
.spyOn(NetworkSiteService, "updateColumnsByIdWithoutHooks")
|
|
964
|
-
.mockResolvedValue(undefined as never);
|
|
965
|
-
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([
|
|
966
|
-
// The prefix query matches the moved site itself and its child.
|
|
967
|
-
fakeSite({ materializedPath: oldPath }),
|
|
968
|
-
fakeSite({
|
|
969
|
-
id: childId,
|
|
970
|
-
_id: childId.toString(),
|
|
971
|
-
materializedPath: oldChildPath,
|
|
972
|
-
}),
|
|
1232
|
+
it("allows a root type without a parent when the type arrives as a relation", async () => {
|
|
1233
|
+
const typeLookup: jest.SpyInstance = mockNetworkSiteTypes([
|
|
1234
|
+
fakeNetworkSiteType({ id: ROOT_SITE_TYPE_ID }),
|
|
973
1235
|
]);
|
|
974
|
-
const rollupSpy: jest.SpyInstance = jest
|
|
975
|
-
.spyOn(NetworkSiteService, "recomputeRollupForSiteAndAncestors")
|
|
976
|
-
.mockResolvedValue(undefined as never);
|
|
977
1236
|
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
props: { isRoot: true },
|
|
983
|
-
} as unknown as UpdateBy<NetworkSite>,
|
|
984
|
-
carryForward: {
|
|
985
|
-
previousItems: [
|
|
986
|
-
fakeSite({
|
|
987
|
-
materializedPath: oldPath,
|
|
988
|
-
parentSiteId: oldParentId,
|
|
989
|
-
}),
|
|
990
|
-
],
|
|
991
|
-
newParentId: newParentId,
|
|
992
|
-
newParentPath: `/${newParentId.toString()}/`,
|
|
1237
|
+
const result: any = await (NetworkSiteService as any).onBeforeCreate({
|
|
1238
|
+
data: {
|
|
1239
|
+
projectId: PROJECT_ID,
|
|
1240
|
+
networkSiteType: { _id: ROOT_SITE_TYPE_ID.toString() },
|
|
993
1241
|
},
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
await (NetworkSiteService as any).onUpdateSuccess(onUpdate, [SITE_ID]);
|
|
997
|
-
|
|
998
|
-
const newPath: string = `/${newParentId.toString()}/${SITE_ID.toString()}/`;
|
|
999
|
-
|
|
1000
|
-
// Moved site rewritten...
|
|
1001
|
-
const selfUpdate: any = updateColumnsSpy.mock.calls.find((call: any) => {
|
|
1002
|
-
return call[0].id.toString() === SITE_ID.toString();
|
|
1242
|
+
props: { tenantId: PROJECT_ID },
|
|
1003
1243
|
});
|
|
1004
|
-
expect(selfUpdate[0].data.materializedPath).toBe(newPath);
|
|
1005
|
-
expect(selfUpdate[0].data.depth).toBe(1);
|
|
1006
1244
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
expect(childUpdate[0].data.materializedPath).toBe(
|
|
1012
|
-
`${newPath}${childId.toString()}/`,
|
|
1245
|
+
expect(result.carryForward.parentPath).toBeNull();
|
|
1246
|
+
expect(typeLookup).toHaveBeenCalledTimes(1);
|
|
1247
|
+
expect(typeLookup.mock.calls[0]![0].id.toString()).toBe(
|
|
1248
|
+
ROOT_SITE_TYPE_ID.toString(),
|
|
1013
1249
|
);
|
|
1014
|
-
|
|
1250
|
+
});
|
|
1015
1251
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1252
|
+
it("rejects a parent for a root type", async () => {
|
|
1253
|
+
mockNetworkSiteTypes([fakeNetworkSiteType({ id: ROOT_SITE_TYPE_ID })]);
|
|
1254
|
+
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
1255
|
+
fakeSite({
|
|
1256
|
+
id: PARENT_SITE_ID,
|
|
1257
|
+
_id: PARENT_SITE_ID.toString(),
|
|
1258
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1259
|
+
}),
|
|
1021
1260
|
);
|
|
1022
|
-
expect(rollupIds).toContain(SITE_ID.toString());
|
|
1023
|
-
expect(rollupIds).toContain(oldParentId.toString());
|
|
1024
|
-
});
|
|
1025
1261
|
|
|
1026
|
-
|
|
1262
|
+
await expect(
|
|
1263
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1264
|
+
data: {
|
|
1265
|
+
projectId: PROJECT_ID,
|
|
1266
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1267
|
+
parentSiteId: PARENT_SITE_ID,
|
|
1268
|
+
},
|
|
1269
|
+
props: { tenantId: PROJECT_ID },
|
|
1270
|
+
}),
|
|
1271
|
+
).rejects.toThrow(
|
|
1272
|
+
"A site with a root network site type cannot have a parent site.",
|
|
1273
|
+
);
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
it("requires a parent for a type configured below another type", async () => {
|
|
1277
|
+
mockNetworkSiteTypes([
|
|
1278
|
+
fakeNetworkSiteType({
|
|
1279
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1280
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1281
|
+
}),
|
|
1282
|
+
]);
|
|
1283
|
+
|
|
1284
|
+
await expect(
|
|
1285
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1286
|
+
data: {
|
|
1287
|
+
projectId: PROJECT_ID,
|
|
1288
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
1289
|
+
},
|
|
1290
|
+
props: { tenantId: PROJECT_ID },
|
|
1291
|
+
}),
|
|
1292
|
+
).rejects.toThrow("This network site type requires a parent site.");
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
it("accepts a direct parent with exactly the configured type through relation fields", async () => {
|
|
1296
|
+
mockNetworkSiteTypes([
|
|
1297
|
+
fakeNetworkSiteType({
|
|
1298
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1299
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1300
|
+
}),
|
|
1301
|
+
]);
|
|
1302
|
+
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
1303
|
+
fakeSite({
|
|
1304
|
+
id: PARENT_SITE_ID,
|
|
1305
|
+
_id: PARENT_SITE_ID.toString(),
|
|
1306
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1307
|
+
}),
|
|
1308
|
+
);
|
|
1309
|
+
jest
|
|
1310
|
+
.spyOn(NetworkSiteService, "getMaterializedPathForSite")
|
|
1311
|
+
.mockResolvedValue(`/${PARENT_SITE_ID.toString()}/`);
|
|
1312
|
+
|
|
1313
|
+
const result: any = await (NetworkSiteService as any).onBeforeCreate({
|
|
1314
|
+
data: {
|
|
1315
|
+
projectId: PROJECT_ID,
|
|
1316
|
+
networkSiteType: { id: CHILD_SITE_TYPE_ID },
|
|
1317
|
+
parentSite: { _id: PARENT_SITE_ID.toString() },
|
|
1318
|
+
},
|
|
1319
|
+
props: { tenantId: PROJECT_ID },
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
expect(result.carryForward.parentPath).toBe(
|
|
1323
|
+
`/${PARENT_SITE_ID.toString()}/`,
|
|
1324
|
+
);
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
it("rejects a parent whose type is not the configured direct parent", async () => {
|
|
1328
|
+
mockNetworkSiteTypes([
|
|
1329
|
+
fakeNetworkSiteType({
|
|
1330
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1331
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1332
|
+
}),
|
|
1333
|
+
]);
|
|
1334
|
+
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
1335
|
+
fakeSite({
|
|
1336
|
+
id: PARENT_SITE_ID,
|
|
1337
|
+
_id: PARENT_SITE_ID.toString(),
|
|
1338
|
+
networkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1339
|
+
}),
|
|
1340
|
+
);
|
|
1341
|
+
|
|
1342
|
+
await expect(
|
|
1343
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1344
|
+
data: {
|
|
1345
|
+
projectId: PROJECT_ID,
|
|
1346
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
1347
|
+
parentSiteId: PARENT_SITE_ID,
|
|
1348
|
+
},
|
|
1349
|
+
props: { tenantId: PROJECT_ID },
|
|
1350
|
+
}),
|
|
1351
|
+
).rejects.toThrow(
|
|
1352
|
+
"Parent site must use the configured parent network site type.",
|
|
1353
|
+
);
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
it("rejects a type from another project", async () => {
|
|
1357
|
+
mockNetworkSiteTypes([
|
|
1358
|
+
fakeNetworkSiteType({
|
|
1359
|
+
id: ROOT_SITE_TYPE_ID,
|
|
1360
|
+
projectId: OTHER_PROJECT_ID,
|
|
1361
|
+
}),
|
|
1362
|
+
]);
|
|
1363
|
+
|
|
1364
|
+
await expect(
|
|
1365
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1366
|
+
data: {
|
|
1367
|
+
projectId: PROJECT_ID,
|
|
1368
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1369
|
+
},
|
|
1370
|
+
props: { tenantId: PROJECT_ID },
|
|
1371
|
+
}),
|
|
1372
|
+
).rejects.toThrow("Network site type must belong to the same project.");
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
it("uses a project relation payload for cross-project validation", async () => {
|
|
1376
|
+
mockNetworkSiteTypes([
|
|
1377
|
+
fakeNetworkSiteType({
|
|
1378
|
+
id: ROOT_SITE_TYPE_ID,
|
|
1379
|
+
projectId: OTHER_PROJECT_ID,
|
|
1380
|
+
}),
|
|
1381
|
+
]);
|
|
1382
|
+
|
|
1383
|
+
await expect(
|
|
1384
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1385
|
+
data: {
|
|
1386
|
+
project: { _id: PROJECT_ID.toString() },
|
|
1387
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1388
|
+
},
|
|
1389
|
+
props: { isRoot: true },
|
|
1390
|
+
}),
|
|
1391
|
+
).rejects.toThrow("Network site type must belong to the same project.");
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
it("rejects a missing type", async () => {
|
|
1395
|
+
mockNetworkSiteTypes([]);
|
|
1396
|
+
|
|
1397
|
+
await expect(
|
|
1398
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1399
|
+
data: {
|
|
1400
|
+
projectId: PROJECT_ID,
|
|
1401
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1402
|
+
},
|
|
1403
|
+
props: { tenantId: PROJECT_ID },
|
|
1404
|
+
}),
|
|
1405
|
+
).rejects.toThrow("Network site type not found.");
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
it("rejects conflicting scalar and relation references before doing lookups", async () => {
|
|
1409
|
+
const siteLookup: jest.SpyInstance = jest.spyOn(
|
|
1410
|
+
NetworkSiteService,
|
|
1411
|
+
"findOneById",
|
|
1412
|
+
);
|
|
1413
|
+
const typeLookup: jest.SpyInstance = jest.spyOn(
|
|
1414
|
+
NetworkSiteTypeService,
|
|
1415
|
+
"findOneById",
|
|
1416
|
+
);
|
|
1417
|
+
|
|
1418
|
+
await expect(
|
|
1419
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1420
|
+
data: {
|
|
1421
|
+
projectId: PROJECT_ID,
|
|
1422
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1423
|
+
networkSiteType: { _id: CHILD_SITE_TYPE_ID.toString() },
|
|
1424
|
+
},
|
|
1425
|
+
props: { tenantId: PROJECT_ID },
|
|
1426
|
+
}),
|
|
1427
|
+
).rejects.toThrow(
|
|
1428
|
+
"Conflicting Network Site Type references were provided.",
|
|
1429
|
+
);
|
|
1430
|
+
|
|
1431
|
+
expect(siteLookup).not.toHaveBeenCalled();
|
|
1432
|
+
expect(typeLookup).not.toHaveBeenCalled();
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
it("rejects a raw parent SQL expression instead of treating it as a clear", async () => {
|
|
1436
|
+
const siteLookup: jest.SpyInstance = jest.spyOn(
|
|
1437
|
+
NetworkSiteService,
|
|
1438
|
+
"findOneById",
|
|
1439
|
+
);
|
|
1440
|
+
|
|
1441
|
+
await expect(
|
|
1442
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1443
|
+
data: {
|
|
1444
|
+
projectId: PROJECT_ID,
|
|
1445
|
+
parentSiteId: () => {
|
|
1446
|
+
return "some-parent-id";
|
|
1447
|
+
},
|
|
1448
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1449
|
+
},
|
|
1450
|
+
props: { tenantId: PROJECT_ID },
|
|
1451
|
+
}),
|
|
1452
|
+
).rejects.toThrow(
|
|
1453
|
+
"parentSiteId cannot be set to a raw SQL expression because the network site hierarchy must be validated against an actual ID.",
|
|
1454
|
+
);
|
|
1455
|
+
|
|
1456
|
+
expect(siteLookup).not.toHaveBeenCalled();
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
it("rejects a malformed non-null site type relation", async () => {
|
|
1460
|
+
const typeLookup: jest.SpyInstance = jest.spyOn(
|
|
1461
|
+
NetworkSiteTypeService,
|
|
1462
|
+
"findOneById",
|
|
1463
|
+
);
|
|
1464
|
+
|
|
1465
|
+
await expect(
|
|
1466
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1467
|
+
data: {
|
|
1468
|
+
projectId: PROJECT_ID,
|
|
1469
|
+
networkSiteType: { name: "not an id" },
|
|
1470
|
+
},
|
|
1471
|
+
props: { tenantId: PROJECT_ID },
|
|
1472
|
+
}),
|
|
1473
|
+
).rejects.toThrow(
|
|
1474
|
+
"networkSiteType must contain a valid Network Site Type ID.",
|
|
1475
|
+
);
|
|
1476
|
+
|
|
1477
|
+
expect(typeLookup).not.toHaveBeenCalled();
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
it("does not permit an untyped child under a parent", async () => {
|
|
1481
|
+
jest
|
|
1482
|
+
.spyOn(NetworkSiteService, "findOneById")
|
|
1483
|
+
.mockResolvedValue(fakeSite({ id: PARENT_SITE_ID }));
|
|
1484
|
+
|
|
1485
|
+
await expect(
|
|
1486
|
+
(NetworkSiteService as any).onBeforeCreate({
|
|
1487
|
+
data: {
|
|
1488
|
+
projectId: PROJECT_ID,
|
|
1489
|
+
parentSiteId: PARENT_SITE_ID,
|
|
1490
|
+
},
|
|
1491
|
+
props: { tenantId: PROJECT_ID },
|
|
1492
|
+
}),
|
|
1493
|
+
).rejects.toThrow(
|
|
1494
|
+
"A network site with a parent must have a network site type.",
|
|
1495
|
+
);
|
|
1496
|
+
});
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
describe("NetworkSiteService site-type hierarchy enforcement on update", () => {
|
|
1500
|
+
afterEach(() => {
|
|
1501
|
+
jest.restoreAllMocks();
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
function makeTypeUpdate(
|
|
1505
|
+
data: Record<string, unknown>,
|
|
1506
|
+
): UpdateBy<NetworkSite> {
|
|
1507
|
+
return {
|
|
1508
|
+
query: { _id: SITE_ID.toString() },
|
|
1509
|
+
data: data,
|
|
1510
|
+
props: { tenantId: PROJECT_ID },
|
|
1511
|
+
} as unknown as UpdateBy<NetworkSite>;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
it("rejects attaching a root-typed site to a parent", async () => {
|
|
1515
|
+
mockNetworkSiteTypes([fakeNetworkSiteType({ id: ROOT_SITE_TYPE_ID })]);
|
|
1516
|
+
jest
|
|
1517
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1518
|
+
.mockResolvedValue([fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID })]);
|
|
1519
|
+
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
1520
|
+
fakeSite({
|
|
1521
|
+
id: PARENT_SITE_ID,
|
|
1522
|
+
_id: PARENT_SITE_ID.toString(),
|
|
1523
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1524
|
+
}),
|
|
1525
|
+
);
|
|
1526
|
+
jest
|
|
1527
|
+
.spyOn(NetworkSiteService, "getMaterializedPathForSite")
|
|
1528
|
+
.mockResolvedValue(`/${PARENT_SITE_ID.toString()}/`);
|
|
1529
|
+
|
|
1530
|
+
await expect(
|
|
1531
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1532
|
+
makeTypeUpdate({ parentSiteId: PARENT_SITE_ID }),
|
|
1533
|
+
),
|
|
1534
|
+
).rejects.toThrow(
|
|
1535
|
+
"A site with a root network site type cannot have a parent site.",
|
|
1536
|
+
);
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
it("rejects detaching a site whose type requires a parent", async () => {
|
|
1540
|
+
mockNetworkSiteTypes([
|
|
1541
|
+
fakeNetworkSiteType({
|
|
1542
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1543
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1544
|
+
}),
|
|
1545
|
+
]);
|
|
1546
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([
|
|
1547
|
+
fakeSite({
|
|
1548
|
+
parentSiteId: PARENT_SITE_ID,
|
|
1549
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
1550
|
+
}),
|
|
1551
|
+
]);
|
|
1552
|
+
|
|
1553
|
+
await expect(
|
|
1554
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1555
|
+
makeTypeUpdate({ parentSite: null }),
|
|
1556
|
+
),
|
|
1557
|
+
).rejects.toThrow("This network site type requires a parent site.");
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
it("validates a type-only update against the site's existing parent", async () => {
|
|
1561
|
+
mockNetworkSiteTypes([
|
|
1562
|
+
fakeNetworkSiteType({
|
|
1563
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1564
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1565
|
+
}),
|
|
1566
|
+
]);
|
|
1567
|
+
const findBySpy: jest.SpyInstance = jest
|
|
1568
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1569
|
+
.mockResolvedValueOnce([
|
|
1570
|
+
fakeSite({
|
|
1571
|
+
parentSiteId: PARENT_SITE_ID,
|
|
1572
|
+
networkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1573
|
+
}),
|
|
1574
|
+
])
|
|
1575
|
+
.mockResolvedValueOnce([]);
|
|
1576
|
+
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
1577
|
+
fakeSite({
|
|
1578
|
+
id: PARENT_SITE_ID,
|
|
1579
|
+
_id: PARENT_SITE_ID.toString(),
|
|
1580
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1581
|
+
}),
|
|
1582
|
+
);
|
|
1583
|
+
|
|
1584
|
+
const result: OnUpdate<NetworkSite> = await (
|
|
1585
|
+
NetworkSiteService as any
|
|
1586
|
+
).onBeforeUpdate(makeTypeUpdate({ networkSiteTypeId: CHILD_SITE_TYPE_ID }));
|
|
1587
|
+
|
|
1588
|
+
expect(result.carryForward).toBeNull();
|
|
1589
|
+
expect(findBySpy).toHaveBeenCalledTimes(2);
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
it("uses both proposed values when type and parent change together as relations", async () => {
|
|
1593
|
+
mockNetworkSiteTypes([
|
|
1594
|
+
fakeNetworkSiteType({
|
|
1595
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1596
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1597
|
+
}),
|
|
1598
|
+
]);
|
|
1599
|
+
jest
|
|
1600
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1601
|
+
.mockResolvedValueOnce([
|
|
1602
|
+
fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID }),
|
|
1603
|
+
])
|
|
1604
|
+
.mockResolvedValueOnce([]);
|
|
1605
|
+
jest.spyOn(NetworkSiteService, "findOneById").mockResolvedValue(
|
|
1606
|
+
fakeSite({
|
|
1607
|
+
id: PARENT_SITE_ID,
|
|
1608
|
+
_id: PARENT_SITE_ID.toString(),
|
|
1609
|
+
networkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1610
|
+
}),
|
|
1611
|
+
);
|
|
1612
|
+
jest
|
|
1613
|
+
.spyOn(NetworkSiteService, "getMaterializedPathForSite")
|
|
1614
|
+
.mockResolvedValue(`/${PARENT_SITE_ID.toString()}/`);
|
|
1615
|
+
|
|
1616
|
+
const result: any = await (NetworkSiteService as any).onBeforeUpdate(
|
|
1617
|
+
makeTypeUpdate({
|
|
1618
|
+
networkSiteType: { _id: CHILD_SITE_TYPE_ID.toString() },
|
|
1619
|
+
parentSite: { id: PARENT_SITE_ID },
|
|
1620
|
+
}),
|
|
1621
|
+
);
|
|
1622
|
+
|
|
1623
|
+
expect(result.carryForward.newParentId.toString()).toBe(
|
|
1624
|
+
PARENT_SITE_ID.toString(),
|
|
1625
|
+
);
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
it("rejects a type change that would invalidate a direct child", async () => {
|
|
1629
|
+
mockNetworkSiteTypes([
|
|
1630
|
+
fakeNetworkSiteType({ id: ALTERNATE_ROOT_SITE_TYPE_ID }),
|
|
1631
|
+
fakeNetworkSiteType({
|
|
1632
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1633
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1634
|
+
}),
|
|
1635
|
+
]);
|
|
1636
|
+
const childId: ObjectID = new ObjectID(
|
|
1637
|
+
"99999999-9999-4999-8999-999999999999",
|
|
1638
|
+
);
|
|
1639
|
+
jest
|
|
1640
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1641
|
+
.mockResolvedValueOnce([
|
|
1642
|
+
fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID }),
|
|
1643
|
+
])
|
|
1644
|
+
.mockResolvedValueOnce([
|
|
1645
|
+
fakeSite({
|
|
1646
|
+
id: childId,
|
|
1647
|
+
_id: childId.toString(),
|
|
1648
|
+
parentSiteId: SITE_ID,
|
|
1649
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
1650
|
+
}),
|
|
1651
|
+
]);
|
|
1652
|
+
|
|
1653
|
+
await expect(
|
|
1654
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1655
|
+
makeTypeUpdate({
|
|
1656
|
+
networkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1657
|
+
}),
|
|
1658
|
+
),
|
|
1659
|
+
).rejects.toThrow(
|
|
1660
|
+
"Parent site must use the configured parent network site type.",
|
|
1661
|
+
);
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
it("allows a type change when every direct child expects the new type", async () => {
|
|
1665
|
+
mockNetworkSiteTypes([
|
|
1666
|
+
fakeNetworkSiteType({ id: ALTERNATE_ROOT_SITE_TYPE_ID }),
|
|
1667
|
+
fakeNetworkSiteType({
|
|
1668
|
+
id: GRANDCHILD_SITE_TYPE_ID,
|
|
1669
|
+
parentNetworkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1670
|
+
}),
|
|
1671
|
+
]);
|
|
1672
|
+
const childId: ObjectID = new ObjectID(
|
|
1673
|
+
"99999999-9999-4999-8999-999999999999",
|
|
1674
|
+
);
|
|
1675
|
+
const findBySpy: jest.SpyInstance = jest
|
|
1676
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1677
|
+
.mockResolvedValueOnce([
|
|
1678
|
+
fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID }),
|
|
1679
|
+
])
|
|
1680
|
+
.mockResolvedValueOnce([
|
|
1681
|
+
fakeSite({
|
|
1682
|
+
id: childId,
|
|
1683
|
+
_id: childId.toString(),
|
|
1684
|
+
parentSiteId: SITE_ID,
|
|
1685
|
+
networkSiteTypeId: GRANDCHILD_SITE_TYPE_ID,
|
|
1686
|
+
}),
|
|
1687
|
+
]);
|
|
1688
|
+
|
|
1689
|
+
await expect(
|
|
1690
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1691
|
+
makeTypeUpdate({
|
|
1692
|
+
networkSiteType: {
|
|
1693
|
+
_id: ALTERNATE_ROOT_SITE_TYPE_ID.toString(),
|
|
1694
|
+
},
|
|
1695
|
+
}),
|
|
1696
|
+
),
|
|
1697
|
+
).resolves.toMatchObject({ carryForward: null });
|
|
1698
|
+
|
|
1699
|
+
const childQuery: any = findBySpy.mock.calls[1]![0].query;
|
|
1700
|
+
expect(childQuery.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
1701
|
+
expect(childQuery.parentSiteId.toString()).toBe(SITE_ID.toString());
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
it("pages past a full batch so wide sites cannot hide invalid direct children", async () => {
|
|
1705
|
+
mockNetworkSiteTypes([
|
|
1706
|
+
fakeNetworkSiteType({ id: ALTERNATE_ROOT_SITE_TYPE_ID }),
|
|
1707
|
+
fakeNetworkSiteType({
|
|
1708
|
+
id: GRANDCHILD_SITE_TYPE_ID,
|
|
1709
|
+
parentNetworkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1710
|
+
}),
|
|
1711
|
+
fakeNetworkSiteType({
|
|
1712
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1713
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1714
|
+
}),
|
|
1715
|
+
]);
|
|
1716
|
+
const child: NetworkSite = fakeSite({
|
|
1717
|
+
id: new ObjectID("99999999-9999-4999-8999-999999999999"),
|
|
1718
|
+
networkSiteTypeId: GRANDCHILD_SITE_TYPE_ID,
|
|
1719
|
+
});
|
|
1720
|
+
const childReadSkips: Array<number> = [];
|
|
1721
|
+
const invalidChildOnSecondPage: NetworkSite = fakeSite({
|
|
1722
|
+
id: new ObjectID("aaaaaaaa-1111-4111-8111-111111111111"),
|
|
1723
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
1724
|
+
});
|
|
1725
|
+
const findBySpy: jest.SpyInstance = jest
|
|
1726
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1727
|
+
.mockImplementation((input: any) => {
|
|
1728
|
+
if (input.query._id) {
|
|
1729
|
+
return Promise.resolve([
|
|
1730
|
+
fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID }),
|
|
1731
|
+
]);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
childReadSkips.push(input.skip);
|
|
1735
|
+
if (input.skip === 0) {
|
|
1736
|
+
return Promise.resolve(
|
|
1737
|
+
Array.from({ length: input.limit }, () => {
|
|
1738
|
+
return child;
|
|
1739
|
+
}),
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
return Promise.resolve([invalidChildOnSecondPage]);
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
await expect(
|
|
1747
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1748
|
+
makeTypeUpdate({
|
|
1749
|
+
networkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1750
|
+
}),
|
|
1751
|
+
),
|
|
1752
|
+
).rejects.toThrow(
|
|
1753
|
+
"Parent site must use the configured parent network site type.",
|
|
1754
|
+
);
|
|
1755
|
+
|
|
1756
|
+
expect(findBySpy).toHaveBeenCalledTimes(3);
|
|
1757
|
+
expect(childReadSkips).toEqual([0, 1000]);
|
|
1758
|
+
});
|
|
1759
|
+
|
|
1760
|
+
it("rejects clearing a site's type while it still has direct children", async () => {
|
|
1761
|
+
mockNetworkSiteTypes([
|
|
1762
|
+
fakeNetworkSiteType({
|
|
1763
|
+
id: CHILD_SITE_TYPE_ID,
|
|
1764
|
+
parentNetworkSiteTypeId: ROOT_SITE_TYPE_ID,
|
|
1765
|
+
}),
|
|
1766
|
+
]);
|
|
1767
|
+
const childId: ObjectID = new ObjectID(
|
|
1768
|
+
"99999999-9999-4999-8999-999999999999",
|
|
1769
|
+
);
|
|
1770
|
+
jest
|
|
1771
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1772
|
+
.mockResolvedValueOnce([
|
|
1773
|
+
fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID }),
|
|
1774
|
+
])
|
|
1775
|
+
.mockResolvedValueOnce([
|
|
1776
|
+
fakeSite({
|
|
1777
|
+
id: childId,
|
|
1778
|
+
_id: childId.toString(),
|
|
1779
|
+
networkSiteTypeId: CHILD_SITE_TYPE_ID,
|
|
1780
|
+
}),
|
|
1781
|
+
]);
|
|
1782
|
+
|
|
1783
|
+
await expect(
|
|
1784
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1785
|
+
makeTypeUpdate({ networkSiteTypeId: null }),
|
|
1786
|
+
),
|
|
1787
|
+
).rejects.toThrow(
|
|
1788
|
+
"Parent site must use the configured parent network site type.",
|
|
1789
|
+
);
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
it("rejects a type-only update to a foreign-project type", async () => {
|
|
1793
|
+
mockNetworkSiteTypes([
|
|
1794
|
+
fakeNetworkSiteType({
|
|
1795
|
+
id: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1796
|
+
projectId: OTHER_PROJECT_ID,
|
|
1797
|
+
}),
|
|
1798
|
+
]);
|
|
1799
|
+
jest
|
|
1800
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1801
|
+
.mockResolvedValue([fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID })]);
|
|
1802
|
+
|
|
1803
|
+
await expect(
|
|
1804
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1805
|
+
makeTypeUpdate({
|
|
1806
|
+
networkSiteTypeId: ALTERNATE_ROOT_SITE_TYPE_ID,
|
|
1807
|
+
}),
|
|
1808
|
+
),
|
|
1809
|
+
).rejects.toThrow("Network site type must belong to the same project.");
|
|
1810
|
+
});
|
|
1811
|
+
|
|
1812
|
+
it("rejects conflicting parent spellings before hierarchy reads", async () => {
|
|
1813
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
1814
|
+
NetworkSiteService,
|
|
1815
|
+
"findBy",
|
|
1816
|
+
);
|
|
1817
|
+
|
|
1818
|
+
await expect(
|
|
1819
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1820
|
+
makeTypeUpdate({
|
|
1821
|
+
parentSiteId: PARENT_SITE_ID,
|
|
1822
|
+
parentSite: { _id: SITE_ID.toString() },
|
|
1823
|
+
}),
|
|
1824
|
+
),
|
|
1825
|
+
).rejects.toThrow("Conflicting Parent Site references were provided.");
|
|
1826
|
+
|
|
1827
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1830
|
+
it("rejects a raw site-type SQL expression before hierarchy reads", async () => {
|
|
1831
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
1832
|
+
NetworkSiteService,
|
|
1833
|
+
"findBy",
|
|
1834
|
+
);
|
|
1835
|
+
|
|
1836
|
+
await expect(
|
|
1837
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1838
|
+
makeTypeUpdate({
|
|
1839
|
+
networkSiteTypeId: () => {
|
|
1840
|
+
return "some-type-id";
|
|
1841
|
+
},
|
|
1842
|
+
}),
|
|
1843
|
+
),
|
|
1844
|
+
).rejects.toThrow(
|
|
1845
|
+
"networkSiteTypeId cannot be set to a raw SQL expression because the network site hierarchy must be validated against an actual ID.",
|
|
1846
|
+
);
|
|
1847
|
+
|
|
1848
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
1849
|
+
});
|
|
1850
|
+
|
|
1851
|
+
it("rejects a malformed non-null parent relation before hierarchy reads", async () => {
|
|
1852
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
1853
|
+
NetworkSiteService,
|
|
1854
|
+
"findBy",
|
|
1855
|
+
);
|
|
1856
|
+
|
|
1857
|
+
await expect(
|
|
1858
|
+
(NetworkSiteService as any).onBeforeUpdate(
|
|
1859
|
+
makeTypeUpdate({ parentSite: { name: "not an id" } }),
|
|
1860
|
+
),
|
|
1861
|
+
).rejects.toThrow("parentSite must contain a valid Parent Site ID.");
|
|
1862
|
+
|
|
1863
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
it("does not re-query direct children for a no-op type write", async () => {
|
|
1867
|
+
mockNetworkSiteTypes([fakeNetworkSiteType({ id: ROOT_SITE_TYPE_ID })]);
|
|
1868
|
+
const findBySpy: jest.SpyInstance = jest
|
|
1869
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1870
|
+
.mockResolvedValue([fakeSite({ networkSiteTypeId: ROOT_SITE_TYPE_ID })]);
|
|
1871
|
+
|
|
1872
|
+
await (NetworkSiteService as any).onBeforeUpdate(
|
|
1873
|
+
makeTypeUpdate({ networkSiteTypeId: ROOT_SITE_TYPE_ID }),
|
|
1874
|
+
);
|
|
1875
|
+
|
|
1876
|
+
expect(findBySpy).toHaveBeenCalledTimes(1);
|
|
1877
|
+
});
|
|
1878
|
+
});
|
|
1879
|
+
|
|
1880
|
+
describe("NetworkSiteService.onUpdateSuccess (subtree rebase)", () => {
|
|
1881
|
+
afterEach(() => {
|
|
1882
|
+
jest.restoreAllMocks();
|
|
1883
|
+
});
|
|
1884
|
+
|
|
1885
|
+
it("rebases the moved site and every descendant, then refreshes both chains", async () => {
|
|
1886
|
+
const childId: ObjectID = new ObjectID(
|
|
1887
|
+
"eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
|
|
1888
|
+
);
|
|
1889
|
+
const oldParentId: ObjectID = PARENT_SITE_ID;
|
|
1890
|
+
const newParentId: ObjectID = new ObjectID(
|
|
1891
|
+
"ffffffff-ffff-4fff-8fff-ffffffffffff",
|
|
1892
|
+
);
|
|
1893
|
+
|
|
1894
|
+
const oldPath: string = `/${oldParentId.toString()}/${SITE_ID.toString()}/`;
|
|
1895
|
+
const oldChildPath: string = `${oldPath}${childId.toString()}/`;
|
|
1896
|
+
|
|
1897
|
+
const updateColumnsSpy: jest.SpyInstance = jest
|
|
1898
|
+
.spyOn(NetworkSiteService, "updateColumnsByIdWithoutHooks")
|
|
1899
|
+
.mockResolvedValue(undefined as never);
|
|
1900
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([
|
|
1901
|
+
// The prefix query matches the moved site itself and its child.
|
|
1902
|
+
fakeSite({ materializedPath: oldPath }),
|
|
1903
|
+
fakeSite({
|
|
1904
|
+
id: childId,
|
|
1905
|
+
_id: childId.toString(),
|
|
1906
|
+
materializedPath: oldChildPath,
|
|
1907
|
+
}),
|
|
1908
|
+
]);
|
|
1909
|
+
const rollupSpy: jest.SpyInstance = jest
|
|
1910
|
+
.spyOn(NetworkSiteService, "recomputeRollupForSiteAndAncestors")
|
|
1911
|
+
.mockResolvedValue(undefined as never);
|
|
1912
|
+
|
|
1913
|
+
const onUpdate: OnUpdate<NetworkSite> = {
|
|
1914
|
+
updateBy: {
|
|
1915
|
+
query: { _id: SITE_ID.toString() },
|
|
1916
|
+
data: { parentSiteId: newParentId },
|
|
1917
|
+
props: { isRoot: true },
|
|
1918
|
+
} as unknown as UpdateBy<NetworkSite>,
|
|
1919
|
+
carryForward: {
|
|
1920
|
+
previousItems: [
|
|
1921
|
+
fakeSite({
|
|
1922
|
+
materializedPath: oldPath,
|
|
1923
|
+
parentSiteId: oldParentId,
|
|
1924
|
+
}),
|
|
1925
|
+
],
|
|
1926
|
+
newParentId: newParentId,
|
|
1927
|
+
newParentPath: `/${newParentId.toString()}/`,
|
|
1928
|
+
},
|
|
1929
|
+
};
|
|
1930
|
+
|
|
1931
|
+
await (NetworkSiteService as any).onUpdateSuccess(onUpdate, [SITE_ID]);
|
|
1932
|
+
|
|
1933
|
+
const newPath: string = `/${newParentId.toString()}/${SITE_ID.toString()}/`;
|
|
1934
|
+
|
|
1935
|
+
// Moved site rewritten...
|
|
1936
|
+
const selfUpdate: any = updateColumnsSpy.mock.calls.find((call: any) => {
|
|
1937
|
+
return call[0].id.toString() === SITE_ID.toString();
|
|
1938
|
+
});
|
|
1939
|
+
expect(selfUpdate[0].data.materializedPath).toBe(newPath);
|
|
1940
|
+
expect(selfUpdate[0].data.depth).toBe(1);
|
|
1941
|
+
|
|
1942
|
+
// ...and the descendant rebased under the new prefix.
|
|
1943
|
+
const childUpdate: any = updateColumnsSpy.mock.calls.find((call: any) => {
|
|
1944
|
+
return call[0].id.toString() === childId.toString();
|
|
1945
|
+
});
|
|
1946
|
+
expect(childUpdate[0].data.materializedPath).toBe(
|
|
1947
|
+
`${newPath}${childId.toString()}/`,
|
|
1948
|
+
);
|
|
1949
|
+
expect(childUpdate[0].data.depth).toBe(2);
|
|
1950
|
+
|
|
1951
|
+
// Rollups: the moved site's new chain + the old parent's chain.
|
|
1952
|
+
const rollupIds: Array<string> = rollupSpy.mock.calls.map(
|
|
1953
|
+
(call: Array<any>) => {
|
|
1954
|
+
return call[0].toString();
|
|
1955
|
+
},
|
|
1956
|
+
);
|
|
1957
|
+
expect(rollupIds).toContain(SITE_ID.toString());
|
|
1958
|
+
expect(rollupIds).toContain(oldParentId.toString());
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
it("pages from the old prefix until a subtree larger than one batch is fully rebased", async () => {
|
|
1962
|
+
const newParentId: ObjectID = new ObjectID(
|
|
1963
|
+
"ffffffff-ffff-4fff-8fff-ffffffffffff",
|
|
1964
|
+
);
|
|
1965
|
+
const oldPath: string = `/${PARENT_SITE_ID.toString()}/${SITE_ID.toString()}/`;
|
|
1966
|
+
const firstBatch: Array<NetworkSite> = Array.from(
|
|
1967
|
+
{ length: 1000 },
|
|
1968
|
+
(): NetworkSite => {
|
|
1969
|
+
const id: ObjectID = ObjectID.generate();
|
|
1970
|
+
return fakeSite({
|
|
1971
|
+
id,
|
|
1972
|
+
_id: id.toString(),
|
|
1973
|
+
materializedPath: `${oldPath}${id.toString()}/`,
|
|
1974
|
+
});
|
|
1975
|
+
},
|
|
1976
|
+
);
|
|
1977
|
+
const finalId: ObjectID = ObjectID.generate();
|
|
1978
|
+
const finalSite: NetworkSite = fakeSite({
|
|
1979
|
+
id: finalId,
|
|
1980
|
+
_id: finalId.toString(),
|
|
1981
|
+
materializedPath: `${oldPath}${finalId.toString()}/`,
|
|
1982
|
+
});
|
|
1983
|
+
const findBySpy: jest.SpyInstance = jest
|
|
1984
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
1985
|
+
.mockResolvedValueOnce(firstBatch)
|
|
1986
|
+
.mockResolvedValueOnce([finalSite]);
|
|
1987
|
+
const updateColumnsSpy: jest.SpyInstance = jest
|
|
1988
|
+
.spyOn(NetworkSiteService, "updateColumnsByIdWithoutHooks")
|
|
1989
|
+
.mockResolvedValue(undefined as never);
|
|
1990
|
+
jest
|
|
1991
|
+
.spyOn(NetworkSiteService, "recomputeRollupForSiteAndAncestors")
|
|
1992
|
+
.mockResolvedValue(undefined as never);
|
|
1993
|
+
|
|
1994
|
+
await (NetworkSiteService as any).onUpdateSuccess(
|
|
1995
|
+
{
|
|
1996
|
+
updateBy: {
|
|
1997
|
+
query: { _id: SITE_ID.toString() },
|
|
1998
|
+
data: { parentSiteId: newParentId },
|
|
1999
|
+
props: { isRoot: true },
|
|
2000
|
+
},
|
|
2001
|
+
carryForward: {
|
|
2002
|
+
previousItems: [
|
|
2003
|
+
fakeSite({
|
|
2004
|
+
materializedPath: oldPath,
|
|
2005
|
+
parentSiteId: PARENT_SITE_ID,
|
|
2006
|
+
}),
|
|
2007
|
+
],
|
|
2008
|
+
newParentId,
|
|
2009
|
+
newParentPath: `/${newParentId.toString()}/`,
|
|
2010
|
+
},
|
|
2011
|
+
},
|
|
2012
|
+
[SITE_ID],
|
|
2013
|
+
);
|
|
2014
|
+
|
|
2015
|
+
expect(findBySpy).toHaveBeenCalledTimes(2);
|
|
2016
|
+
expect(findBySpy.mock.calls[0]![0]).toEqual(
|
|
2017
|
+
expect.objectContaining({ limit: 1000, skip: 0 }),
|
|
2018
|
+
);
|
|
2019
|
+
expect(findBySpy.mock.calls[1]![0]).toEqual(
|
|
2020
|
+
expect.objectContaining({ limit: 1000, skip: 0 }),
|
|
2021
|
+
);
|
|
2022
|
+
expect(updateColumnsSpy).toHaveBeenCalledTimes(1002);
|
|
2023
|
+
expect(updateColumnsSpy).toHaveBeenCalledWith(
|
|
2024
|
+
expect.objectContaining({ id: finalId }),
|
|
2025
|
+
);
|
|
2026
|
+
});
|
|
2027
|
+
|
|
2028
|
+
it("processes overlapping bulk move roots deepest-first", async () => {
|
|
2029
|
+
const descendantId: ObjectID = ObjectID.generate();
|
|
2030
|
+
const ancestorPath: string = `/${SITE_ID.toString()}/`;
|
|
2031
|
+
const descendantPath: string = `${ancestorPath}${descendantId.toString()}/`;
|
|
2032
|
+
const updateColumnsSpy: jest.SpyInstance = jest
|
|
2033
|
+
.spyOn(NetworkSiteService, "updateColumnsByIdWithoutHooks")
|
|
2034
|
+
.mockResolvedValue(undefined as never);
|
|
2035
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([]);
|
|
2036
|
+
jest
|
|
2037
|
+
.spyOn(NetworkSiteService, "recomputeRollupForSiteAndAncestors")
|
|
2038
|
+
.mockResolvedValue(undefined as never);
|
|
2039
|
+
|
|
2040
|
+
await (NetworkSiteService as any).onUpdateSuccess(
|
|
2041
|
+
{
|
|
2042
|
+
updateBy: {
|
|
2043
|
+
query: {},
|
|
2044
|
+
data: { parentSiteId: PARENT_SITE_ID },
|
|
2045
|
+
props: { isRoot: true },
|
|
2046
|
+
},
|
|
2047
|
+
carryForward: {
|
|
2048
|
+
previousItems: [
|
|
2049
|
+
fakeSite({ materializedPath: ancestorPath }),
|
|
2050
|
+
fakeSite({
|
|
2051
|
+
id: descendantId,
|
|
2052
|
+
_id: descendantId.toString(),
|
|
2053
|
+
parentSiteId: SITE_ID,
|
|
2054
|
+
materializedPath: descendantPath,
|
|
2055
|
+
}),
|
|
2056
|
+
],
|
|
2057
|
+
newParentId: PARENT_SITE_ID,
|
|
2058
|
+
newParentPath: `/${PARENT_SITE_ID.toString()}/`,
|
|
2059
|
+
},
|
|
2060
|
+
},
|
|
2061
|
+
[SITE_ID, descendantId],
|
|
2062
|
+
);
|
|
2063
|
+
|
|
2064
|
+
expect(updateColumnsSpy.mock.calls[0]![0].id.toString()).toBe(
|
|
2065
|
+
descendantId.toString(),
|
|
2066
|
+
);
|
|
2067
|
+
expect(updateColumnsSpy.mock.calls[1]![0].id.toString()).toBe(
|
|
2068
|
+
SITE_ID.toString(),
|
|
2069
|
+
);
|
|
2070
|
+
});
|
|
2071
|
+
|
|
2072
|
+
/*
|
|
1027
2073
|
* DatabaseService calls onUpdateSuccess even when the tenant-scoped UPDATE
|
|
1028
2074
|
* matched zero rows. Nothing may be written for a row the UPDATE did not
|
|
1029
2075
|
* touch - otherwise a cross-project id in the query rewrites the victim's
|
|
@@ -1220,59 +2266,324 @@ describe("NetworkSiteService.onMonitorStatusChanged", () => {
|
|
|
1220
2266
|
"recomputeRollupForSiteAndAncestors",
|
|
1221
2267
|
);
|
|
1222
2268
|
|
|
1223
|
-
await NetworkSiteService.onMonitorStatusChanged({
|
|
1224
|
-
projectId: PROJECT_ID,
|
|
1225
|
-
monitorIds: [MONITOR_ID],
|
|
1226
|
-
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2269
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2270
|
+
projectId: PROJECT_ID,
|
|
2271
|
+
monitorIds: [MONITOR_ID],
|
|
2272
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2273
|
+
});
|
|
2274
|
+
|
|
2275
|
+
expect(stampSpy).toHaveBeenCalledTimes(1);
|
|
2276
|
+
expect(rollupSpy).not.toHaveBeenCalled();
|
|
2277
|
+
});
|
|
2278
|
+
|
|
2279
|
+
it("does nothing when the monitors are not NetworkDevice monitors", async () => {
|
|
2280
|
+
jest.spyOn(MonitorService, "findBy").mockResolvedValue([]);
|
|
2281
|
+
/*
|
|
2282
|
+
* A device can be bound to a monitor either through a NetworkDevice
|
|
2283
|
+
* monitor's steps or directly by its own monitorId column, so the service
|
|
2284
|
+
* still asks NetworkDeviceService for devices carrying these monitorIds.
|
|
2285
|
+
* When that also comes back empty there is genuinely nothing to act on.
|
|
2286
|
+
*/
|
|
2287
|
+
jest
|
|
2288
|
+
.spyOn(NetworkDeviceService, "findBy")
|
|
2289
|
+
.mockResolvedValue([] as unknown as Array<NetworkDevice>);
|
|
2290
|
+
const stampSpy: jest.SpyInstance = jest.spyOn(
|
|
2291
|
+
NetworkDeviceService,
|
|
2292
|
+
"updateColumnsByIdWithoutHooks",
|
|
2293
|
+
);
|
|
2294
|
+
const rollupSpy: jest.SpyInstance = jest.spyOn(
|
|
2295
|
+
NetworkSiteService,
|
|
2296
|
+
"recomputeRollupForSiteAndAncestors",
|
|
2297
|
+
);
|
|
2298
|
+
|
|
2299
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2300
|
+
projectId: PROJECT_ID,
|
|
2301
|
+
monitorIds: [MONITOR_ID],
|
|
2302
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2303
|
+
});
|
|
2304
|
+
|
|
2305
|
+
// No device resolved, so nothing is stamped and no rollup is recomputed.
|
|
2306
|
+
expect(stampSpy).not.toHaveBeenCalled();
|
|
2307
|
+
expect(rollupSpy).not.toHaveBeenCalled();
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2310
|
+
it("NEVER throws - a failing lookup is logged, not propagated", async () => {
|
|
2311
|
+
jest
|
|
2312
|
+
.spyOn(MonitorService, "findBy")
|
|
2313
|
+
.mockRejectedValue(new Error("database is down"));
|
|
2314
|
+
|
|
2315
|
+
await expect(
|
|
2316
|
+
NetworkSiteService.onMonitorStatusChanged({
|
|
2317
|
+
projectId: PROJECT_ID,
|
|
2318
|
+
monitorIds: [MONITOR_ID],
|
|
2319
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2320
|
+
}),
|
|
2321
|
+
).resolves.toBeUndefined();
|
|
2322
|
+
});
|
|
2323
|
+
|
|
2324
|
+
/*
|
|
2325
|
+
* Monitor-backed devices (monitoringMethod "Monitor") are never polled,
|
|
2326
|
+
* so their isReachable - the column the device list's summary tiles and
|
|
2327
|
+
* Status filter count and filter on in SQL - stays NULL forever unless
|
|
2328
|
+
* the server derives it from the bound monitor's status. These cases pin
|
|
2329
|
+
* that derivation: `isReachable = !MonitorStatus.isOfflineState`, read
|
|
2330
|
+
* from the status row ONCE per call and only when a monitor-backed
|
|
2331
|
+
* device is in the set, while a probe-polled device's isReachable stays
|
|
2332
|
+
* its own poll's to write.
|
|
2333
|
+
*/
|
|
2334
|
+
describe("isReachable for monitor-backed devices", () => {
|
|
2335
|
+
const SECOND_DEVICE_ID: ObjectID = new ObjectID(
|
|
2336
|
+
"dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
2337
|
+
);
|
|
2338
|
+
const THIRD_DEVICE_ID: ObjectID = new ObjectID(
|
|
2339
|
+
"eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
|
|
2340
|
+
);
|
|
2341
|
+
|
|
2342
|
+
function monitorBackedDevice(id: ObjectID): NetworkDevice {
|
|
2343
|
+
return {
|
|
2344
|
+
id: id,
|
|
2345
|
+
siteId: SITE_ID,
|
|
2346
|
+
monitorId: MONITOR_ID,
|
|
2347
|
+
monitoringMethod: NetworkDeviceMonitoringMethod.Monitor,
|
|
2348
|
+
} as unknown as NetworkDevice;
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
function probePolledDevice(
|
|
2352
|
+
id: ObjectID,
|
|
2353
|
+
monitoringMethod: string | undefined,
|
|
2354
|
+
): NetworkDevice {
|
|
2355
|
+
return {
|
|
2356
|
+
id: id,
|
|
2357
|
+
siteId: SITE_ID,
|
|
2358
|
+
monitoringMethod: monitoringMethod,
|
|
2359
|
+
} as unknown as NetworkDevice;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
function fakeStatus(isOfflineState: boolean): MonitorStatus {
|
|
2363
|
+
return {
|
|
2364
|
+
id: OFFLINE_STATUS_ID,
|
|
2365
|
+
_id: OFFLINE_STATUS_ID.toString(),
|
|
2366
|
+
isOfflineState: isOfflineState,
|
|
2367
|
+
} as unknown as MonitorStatus;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
function stampedData(stampSpy: jest.SpyInstance): Array<any> {
|
|
2371
|
+
return stampSpy.mock.calls.map((call: Array<any>) => {
|
|
2372
|
+
return call[0].data;
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
/*
|
|
2377
|
+
* Wires the two device lookups for a "bound by monitorId" set: no
|
|
2378
|
+
* Network Device monitor references anything, every device comes back
|
|
2379
|
+
* from the monitorId query.
|
|
2380
|
+
*/
|
|
2381
|
+
function mockBoundDevices(devices: Array<NetworkDevice>): {
|
|
2382
|
+
stamp: jest.SpyInstance;
|
|
2383
|
+
status: jest.SpyInstance;
|
|
2384
|
+
} {
|
|
2385
|
+
jest.spyOn(MonitorService, "findBy").mockResolvedValue([]);
|
|
2386
|
+
jest.spyOn(NetworkDeviceService, "findBy").mockResolvedValue(devices);
|
|
2387
|
+
jest
|
|
2388
|
+
.spyOn(NetworkSiteService, "recomputeRollupForSiteAndAncestors")
|
|
2389
|
+
.mockResolvedValue(undefined as never);
|
|
2390
|
+
|
|
2391
|
+
const stamp: jest.SpyInstance = jest
|
|
2392
|
+
.spyOn(NetworkDeviceService, "updateColumnsByIdWithoutHooks")
|
|
2393
|
+
.mockResolvedValue(undefined as never);
|
|
2394
|
+
const status: jest.SpyInstance = jest.spyOn(
|
|
2395
|
+
MonitorStatusService,
|
|
2396
|
+
"findOneBy",
|
|
2397
|
+
);
|
|
2398
|
+
|
|
2399
|
+
return { stamp, status };
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
it("reads the status row once for N monitor-backed rows and stamps isReachable false for an offline status", async () => {
|
|
2403
|
+
const { stamp, status } = mockBoundDevices([
|
|
2404
|
+
monitorBackedDevice(DEVICE_ID),
|
|
2405
|
+
monitorBackedDevice(SECOND_DEVICE_ID),
|
|
2406
|
+
monitorBackedDevice(THIRD_DEVICE_ID),
|
|
2407
|
+
]);
|
|
2408
|
+
status.mockResolvedValue(fakeStatus(true));
|
|
2409
|
+
|
|
2410
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2411
|
+
projectId: PROJECT_ID,
|
|
2412
|
+
monitorIds: [MONITOR_ID],
|
|
2413
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2414
|
+
});
|
|
2415
|
+
|
|
2416
|
+
expect(status).toHaveBeenCalledTimes(1);
|
|
2417
|
+
const statusArgs: any = status.mock.calls[0]![0];
|
|
2418
|
+
expect(statusArgs.query._id.toString()).toBe(
|
|
2419
|
+
OFFLINE_STATUS_ID.toString(),
|
|
2420
|
+
);
|
|
2421
|
+
expect(statusArgs.query.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
2422
|
+
expect(statusArgs.select).toEqual({ _id: true, isOfflineState: true });
|
|
2423
|
+
expect(statusArgs.props).toEqual({ isRoot: true });
|
|
2424
|
+
|
|
2425
|
+
expect(stamp).toHaveBeenCalledTimes(3);
|
|
2426
|
+
for (const data of stampedData(stamp)) {
|
|
2427
|
+
expect(data.currentMonitorStatusId.toString()).toBe(
|
|
2428
|
+
OFFLINE_STATUS_ID.toString(),
|
|
2429
|
+
);
|
|
2430
|
+
expect(data.isReachable).toBe(false);
|
|
2431
|
+
}
|
|
2432
|
+
});
|
|
2433
|
+
|
|
2434
|
+
it("stamps isReachable true for an operational (non-offline) status", async () => {
|
|
2435
|
+
const { stamp, status } = mockBoundDevices([
|
|
2436
|
+
monitorBackedDevice(DEVICE_ID),
|
|
2437
|
+
]);
|
|
2438
|
+
status.mockResolvedValue(fakeStatus(false));
|
|
2439
|
+
|
|
2440
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2441
|
+
projectId: PROJECT_ID,
|
|
2442
|
+
monitorIds: [MONITOR_ID],
|
|
2443
|
+
monitorStatusId: OPERATIONAL_STATUS_ID,
|
|
2444
|
+
});
|
|
2445
|
+
|
|
2446
|
+
expect(status).toHaveBeenCalledTimes(1);
|
|
2447
|
+
expect(stamp).toHaveBeenCalledTimes(1);
|
|
2448
|
+
expect(stampedData(stamp)[0].isReachable).toBe(true);
|
|
2449
|
+
expect(stampedData(stamp)[0].currentMonitorStatusId.toString()).toBe(
|
|
2450
|
+
OPERATIONAL_STATUS_ID.toString(),
|
|
2451
|
+
);
|
|
2452
|
+
});
|
|
2453
|
+
|
|
2454
|
+
/*
|
|
2455
|
+
* A "Degraded" status is neither operational nor offline. It is read
|
|
2456
|
+
* off the offline end, like DeviceReachabilityUtil does, so a degraded
|
|
2457
|
+
* device stays reachable rather than being painted down.
|
|
2458
|
+
*/
|
|
2459
|
+
it("treats a status with no offline flag at all as reachable", async () => {
|
|
2460
|
+
const { stamp, status } = mockBoundDevices([
|
|
2461
|
+
monitorBackedDevice(DEVICE_ID),
|
|
2462
|
+
]);
|
|
2463
|
+
status.mockResolvedValue({
|
|
2464
|
+
id: OFFLINE_STATUS_ID,
|
|
2465
|
+
_id: OFFLINE_STATUS_ID.toString(),
|
|
2466
|
+
} as unknown as MonitorStatus);
|
|
2467
|
+
|
|
2468
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2469
|
+
projectId: PROJECT_ID,
|
|
2470
|
+
monitorIds: [MONITOR_ID],
|
|
2471
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2472
|
+
});
|
|
2473
|
+
|
|
2474
|
+
expect(stampedData(stamp)[0].isReachable).toBe(true);
|
|
2475
|
+
});
|
|
2476
|
+
|
|
2477
|
+
it("never reads the status row for an all-probe-polled set, and stamps the id only", async () => {
|
|
2478
|
+
const { stamp, status } = mockBoundDevices([
|
|
2479
|
+
probePolledDevice(DEVICE_ID, NetworkDeviceMonitoringMethod.Probe),
|
|
2480
|
+
// A row written before the column existed: NULL reads as Probe.
|
|
2481
|
+
probePolledDevice(SECOND_DEVICE_ID, undefined),
|
|
2482
|
+
]);
|
|
2483
|
+
|
|
2484
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2485
|
+
projectId: PROJECT_ID,
|
|
2486
|
+
monitorIds: [MONITOR_ID],
|
|
2487
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2488
|
+
});
|
|
2489
|
+
|
|
2490
|
+
expect(status).not.toHaveBeenCalled();
|
|
2491
|
+
expect(stamp).toHaveBeenCalledTimes(2);
|
|
2492
|
+
for (const data of stampedData(stamp)) {
|
|
2493
|
+
expect(data.currentMonitorStatusId.toString()).toBe(
|
|
2494
|
+
OFFLINE_STATUS_ID.toString(),
|
|
2495
|
+
);
|
|
2496
|
+
// The poll owns a probe-polled device's isReachable: the key is absent, not undefined.
|
|
2497
|
+
expect(data).not.toHaveProperty("isReachable");
|
|
2498
|
+
expect(Object.keys(data)).toEqual(["currentMonitorStatusId"]);
|
|
2499
|
+
}
|
|
1227
2500
|
});
|
|
1228
2501
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
2502
|
+
it("in a mixed set only the monitor-backed rows get isReachable, from one status read", async () => {
|
|
2503
|
+
const { stamp, status } = mockBoundDevices([
|
|
2504
|
+
probePolledDevice(DEVICE_ID, NetworkDeviceMonitoringMethod.Probe),
|
|
2505
|
+
monitorBackedDevice(SECOND_DEVICE_ID),
|
|
2506
|
+
probePolledDevice(THIRD_DEVICE_ID, undefined),
|
|
2507
|
+
]);
|
|
2508
|
+
status.mockResolvedValue(fakeStatus(true));
|
|
1232
2509
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
* still asks NetworkDeviceService for devices carrying these monitorIds.
|
|
1239
|
-
* When that also comes back empty there is genuinely nothing to act on.
|
|
1240
|
-
*/
|
|
1241
|
-
jest
|
|
1242
|
-
.spyOn(NetworkDeviceService, "findBy")
|
|
1243
|
-
.mockResolvedValue([] as unknown as Array<NetworkDevice>);
|
|
1244
|
-
const stampSpy: jest.SpyInstance = jest.spyOn(
|
|
1245
|
-
NetworkDeviceService,
|
|
1246
|
-
"updateColumnsByIdWithoutHooks",
|
|
1247
|
-
);
|
|
1248
|
-
const rollupSpy: jest.SpyInstance = jest.spyOn(
|
|
1249
|
-
NetworkSiteService,
|
|
1250
|
-
"recomputeRollupForSiteAndAncestors",
|
|
1251
|
-
);
|
|
2510
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2511
|
+
projectId: PROJECT_ID,
|
|
2512
|
+
monitorIds: [MONITOR_ID],
|
|
2513
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2514
|
+
});
|
|
1252
2515
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
2516
|
+
expect(status).toHaveBeenCalledTimes(1);
|
|
2517
|
+
expect(stamp).toHaveBeenCalledTimes(3);
|
|
2518
|
+
|
|
2519
|
+
const byDeviceId: Map<string, any> = new Map(
|
|
2520
|
+
stamp.mock.calls.map((call: Array<any>) => {
|
|
2521
|
+
return [call[0].id.toString(), call[0].data];
|
|
2522
|
+
}),
|
|
2523
|
+
);
|
|
2524
|
+
|
|
2525
|
+
expect(byDeviceId.get(SECOND_DEVICE_ID.toString()).isReachable).toBe(
|
|
2526
|
+
false,
|
|
2527
|
+
);
|
|
2528
|
+
expect(byDeviceId.get(DEVICE_ID.toString())).not.toHaveProperty(
|
|
2529
|
+
"isReachable",
|
|
2530
|
+
);
|
|
2531
|
+
expect(byDeviceId.get(THIRD_DEVICE_ID.toString())).not.toHaveProperty(
|
|
2532
|
+
"isReachable",
|
|
2533
|
+
);
|
|
1257
2534
|
});
|
|
1258
2535
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
2536
|
+
it("stamps the id but leaves isReachable alone, with a warning, when the status row is not found", async () => {
|
|
2537
|
+
const { stamp, status } = mockBoundDevices([
|
|
2538
|
+
monitorBackedDevice(DEVICE_ID),
|
|
2539
|
+
]);
|
|
2540
|
+
status.mockResolvedValue(null);
|
|
2541
|
+
const warn: jest.SpyInstance = jest
|
|
2542
|
+
.spyOn(logger, "warn")
|
|
2543
|
+
.mockImplementation(() => {
|
|
2544
|
+
return undefined;
|
|
2545
|
+
});
|
|
1263
2546
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
2547
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
2548
|
+
projectId: PROJECT_ID,
|
|
2549
|
+
monitorIds: [MONITOR_ID],
|
|
2550
|
+
monitorStatusId: OFFLINE_STATUS_ID,
|
|
2551
|
+
});
|
|
1268
2552
|
|
|
1269
|
-
|
|
1270
|
-
|
|
2553
|
+
expect(stamp).toHaveBeenCalledTimes(1);
|
|
2554
|
+
expect(stampedData(stamp)[0].currentMonitorStatusId.toString()).toBe(
|
|
2555
|
+
OFFLINE_STATUS_ID.toString(),
|
|
2556
|
+
);
|
|
2557
|
+
expect(stampedData(stamp)[0]).not.toHaveProperty("isReachable");
|
|
2558
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
2559
|
+
expect(String(warn.mock.calls[0]![0])).toContain(
|
|
2560
|
+
OFFLINE_STATUS_ID.toString(),
|
|
2561
|
+
);
|
|
2562
|
+
});
|
|
2563
|
+
|
|
2564
|
+
it("selects monitoringMethod on BOTH device lookups", async () => {
|
|
2565
|
+
// A Network Device monitor referencing a device drives the first lookup...
|
|
2566
|
+
jest
|
|
2567
|
+
.spyOn(MonitorService, "findBy")
|
|
2568
|
+
.mockResolvedValue([fakeNetworkDeviceMonitor([DEVICE_ID.toString()])]);
|
|
2569
|
+
// ...and the monitorId query is always the second.
|
|
2570
|
+
const findDevices: jest.SpyInstance = jest
|
|
2571
|
+
.spyOn(NetworkDeviceService, "findBy")
|
|
2572
|
+
.mockResolvedValue([]);
|
|
2573
|
+
|
|
2574
|
+
await NetworkSiteService.onMonitorStatusChanged({
|
|
1271
2575
|
projectId: PROJECT_ID,
|
|
1272
2576
|
monitorIds: [MONITOR_ID],
|
|
1273
2577
|
monitorStatusId: OFFLINE_STATUS_ID,
|
|
1274
|
-
})
|
|
1275
|
-
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
expect(findDevices).toHaveBeenCalledTimes(2);
|
|
2581
|
+
for (const call of findDevices.mock.calls) {
|
|
2582
|
+
expect((call[0] as any).select.monitoringMethod).toBe(true);
|
|
2583
|
+
expect((call[0] as any).select._id).toBe(true);
|
|
2584
|
+
expect((call[0] as any).select.siteId).toBe(true);
|
|
2585
|
+
}
|
|
2586
|
+
});
|
|
1276
2587
|
});
|
|
1277
2588
|
});
|
|
1278
2589
|
|
|
@@ -1450,10 +2761,10 @@ describe("NetworkSiteService.getMaterializedPathForSite", () => {
|
|
|
1450
2761
|
});
|
|
1451
2762
|
|
|
1452
2763
|
/*
|
|
1453
|
-
*
|
|
1454
|
-
* its
|
|
1455
|
-
*
|
|
1456
|
-
*
|
|
2764
|
+
* A typed child cannot be promoted or attached to its grandparent without
|
|
2765
|
+
* violating its configured direct-parent type. The before hook therefore
|
|
2766
|
+
* blocks partial subtree deletes. The success-hook tests retain the old
|
|
2767
|
+
* orphan repair as a defensive fallback for hook-bypassing/internal deletes.
|
|
1457
2768
|
*/
|
|
1458
2769
|
describe("NetworkSiteService delete hooks (orphan repair)", () => {
|
|
1459
2770
|
afterEach(() => {
|
|
@@ -1501,6 +2812,148 @@ describe("NetworkSiteService delete hooks (orphan repair)", () => {
|
|
|
1501
2812
|
expect(result.carryForward.sitesToDelete).toEqual([]);
|
|
1502
2813
|
});
|
|
1503
2814
|
|
|
2815
|
+
it("uses the same tenant scope for a root delete preflight", async () => {
|
|
2816
|
+
const findBySpy: jest.SpyInstance = jest
|
|
2817
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
2818
|
+
.mockResolvedValue([]);
|
|
2819
|
+
|
|
2820
|
+
await (NetworkSiteService as any).onBeforeDelete({
|
|
2821
|
+
query: { _id: DISTRICT_ID.toString() },
|
|
2822
|
+
props: { tenantId: PROJECT_ID, isRoot: true },
|
|
2823
|
+
} as unknown as DeleteBy<NetworkSite>);
|
|
2824
|
+
|
|
2825
|
+
expect(findBySpy.mock.calls[0]![0].query.projectId.toString()).toBe(
|
|
2826
|
+
PROJECT_ID.toString(),
|
|
2827
|
+
);
|
|
2828
|
+
});
|
|
2829
|
+
|
|
2830
|
+
it("does not invent a tenant scope for a root multi-tenant delete preflight", async () => {
|
|
2831
|
+
const findBySpy: jest.SpyInstance = jest
|
|
2832
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
2833
|
+
.mockResolvedValue([]);
|
|
2834
|
+
|
|
2835
|
+
await (NetworkSiteService as any).onBeforeDelete({
|
|
2836
|
+
query: { _id: DISTRICT_ID.toString() },
|
|
2837
|
+
props: {
|
|
2838
|
+
tenantId: PROJECT_ID,
|
|
2839
|
+
isRoot: true,
|
|
2840
|
+
isMultiTenantRequest: true,
|
|
2841
|
+
},
|
|
2842
|
+
} as unknown as DeleteBy<NetworkSite>);
|
|
2843
|
+
|
|
2844
|
+
expect(findBySpy.mock.calls[0]![0].query.projectId).toBeUndefined();
|
|
2845
|
+
});
|
|
2846
|
+
|
|
2847
|
+
it("rejects deleting a site while a direct child would survive", async () => {
|
|
2848
|
+
const findBySpy: jest.SpyInstance = jest
|
|
2849
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
2850
|
+
.mockResolvedValueOnce([deletedDistrict()])
|
|
2851
|
+
.mockResolvedValueOnce([
|
|
2852
|
+
fakeSite({
|
|
2853
|
+
id: STORE_ID,
|
|
2854
|
+
_id: STORE_ID.toString(),
|
|
2855
|
+
parentSiteId: DISTRICT_ID,
|
|
2856
|
+
}),
|
|
2857
|
+
]);
|
|
2858
|
+
|
|
2859
|
+
await expect(
|
|
2860
|
+
(NetworkSiteService as any).onBeforeDelete({
|
|
2861
|
+
query: { _id: DISTRICT_ID.toString() },
|
|
2862
|
+
limit: 1,
|
|
2863
|
+
skip: 0,
|
|
2864
|
+
props: { tenantId: PROJECT_ID },
|
|
2865
|
+
} as unknown as DeleteBy<NetworkSite>),
|
|
2866
|
+
).rejects.toThrow(
|
|
2867
|
+
"A network site with child sites cannot be deleted. Move or delete its child sites first.",
|
|
2868
|
+
);
|
|
2869
|
+
|
|
2870
|
+
const childQuery: any = findBySpy.mock.calls[1]![0].query;
|
|
2871
|
+
expect(childQuery.projectId).toBeInstanceOf(FindOperator);
|
|
2872
|
+
expect(
|
|
2873
|
+
Object.values(
|
|
2874
|
+
childQuery.projectId.objectLiteralParameters as Record<
|
|
2875
|
+
string,
|
|
2876
|
+
Array<string>
|
|
2877
|
+
>,
|
|
2878
|
+
).flat(),
|
|
2879
|
+
).toContain(PROJECT_ID.toString());
|
|
2880
|
+
});
|
|
2881
|
+
|
|
2882
|
+
it("allows a bulk delete when every direct child is in the delete set", async () => {
|
|
2883
|
+
const store: NetworkSite = fakeSite({
|
|
2884
|
+
id: STORE_ID,
|
|
2885
|
+
_id: STORE_ID.toString(),
|
|
2886
|
+
parentSiteId: DISTRICT_ID,
|
|
2887
|
+
materializedPath: STORE_PATH,
|
|
2888
|
+
});
|
|
2889
|
+
jest
|
|
2890
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
2891
|
+
.mockResolvedValueOnce([deletedDistrict(), store])
|
|
2892
|
+
.mockResolvedValueOnce([store]);
|
|
2893
|
+
|
|
2894
|
+
const result: OnDelete<NetworkSite> = await (
|
|
2895
|
+
NetworkSiteService as any
|
|
2896
|
+
).onBeforeDelete({
|
|
2897
|
+
query: {},
|
|
2898
|
+
limit: 2,
|
|
2899
|
+
skip: 0,
|
|
2900
|
+
props: { tenantId: PROJECT_ID },
|
|
2901
|
+
} as unknown as DeleteBy<NetworkSite>);
|
|
2902
|
+
|
|
2903
|
+
expect(result.carryForward.sitesToDelete).toEqual([
|
|
2904
|
+
deletedDistrict(),
|
|
2905
|
+
store,
|
|
2906
|
+
]);
|
|
2907
|
+
});
|
|
2908
|
+
|
|
2909
|
+
it("rejects a bulk parent-and-child delete when a grandchild would survive", async () => {
|
|
2910
|
+
const store: NetworkSite = fakeSite({
|
|
2911
|
+
id: STORE_ID,
|
|
2912
|
+
_id: STORE_ID.toString(),
|
|
2913
|
+
parentSiteId: DISTRICT_ID,
|
|
2914
|
+
materializedPath: STORE_PATH,
|
|
2915
|
+
});
|
|
2916
|
+
jest
|
|
2917
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
2918
|
+
.mockResolvedValueOnce([deletedDistrict(), store])
|
|
2919
|
+
.mockResolvedValueOnce([
|
|
2920
|
+
store,
|
|
2921
|
+
fakeSite({
|
|
2922
|
+
id: UNIT_ID,
|
|
2923
|
+
_id: UNIT_ID.toString(),
|
|
2924
|
+
parentSiteId: STORE_ID,
|
|
2925
|
+
materializedPath: UNIT_PATH,
|
|
2926
|
+
}),
|
|
2927
|
+
]);
|
|
2928
|
+
|
|
2929
|
+
await expect(
|
|
2930
|
+
(NetworkSiteService as any).onBeforeDelete({
|
|
2931
|
+
query: {},
|
|
2932
|
+
limit: 2,
|
|
2933
|
+
skip: 0,
|
|
2934
|
+
props: { tenantId: PROJECT_ID },
|
|
2935
|
+
} as unknown as DeleteBy<NetworkSite>),
|
|
2936
|
+
).rejects.toThrow(
|
|
2937
|
+
"A network site with child sites cannot be deleted. Move or delete its child sites first.",
|
|
2938
|
+
);
|
|
2939
|
+
});
|
|
2940
|
+
|
|
2941
|
+
it("uses the requested window when determining the exact bulk delete set", async () => {
|
|
2942
|
+
const findBySpy: jest.SpyInstance = jest
|
|
2943
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
2944
|
+
.mockResolvedValue([]);
|
|
2945
|
+
|
|
2946
|
+
await (NetworkSiteService as any).onBeforeDelete({
|
|
2947
|
+
query: {},
|
|
2948
|
+
limit: 7,
|
|
2949
|
+
skip: 3,
|
|
2950
|
+
props: { tenantId: PROJECT_ID },
|
|
2951
|
+
} as unknown as DeleteBy<NetworkSite>);
|
|
2952
|
+
|
|
2953
|
+
expect(findBySpy.mock.calls[0]![0].limit).toBe(7);
|
|
2954
|
+
expect(findBySpy.mock.calls[0]![0].skip).toBe(3);
|
|
2955
|
+
});
|
|
2956
|
+
|
|
1504
2957
|
it("reparents the direct child and rebases the whole subtree", async () => {
|
|
1505
2958
|
jest
|
|
1506
2959
|
.spyOn(NetworkSiteService, "getMaterializedPathForSite")
|
|
@@ -1560,6 +3013,57 @@ describe("NetworkSiteService delete hooks (orphan repair)", () => {
|
|
|
1560
3013
|
expect(rollupSpy.mock.calls[0]![0].toString()).toBe(REGION_ID.toString());
|
|
1561
3014
|
});
|
|
1562
3015
|
|
|
3016
|
+
it("pages legacy orphan repair from offset zero until every descendant is rewritten", async () => {
|
|
3017
|
+
const repeatedId: ObjectID = ObjectID.generate();
|
|
3018
|
+
const repeatedDescendant: NetworkSite = fakeSite({
|
|
3019
|
+
id: repeatedId,
|
|
3020
|
+
_id: repeatedId.toString(),
|
|
3021
|
+
materializedPath: `${DISTRICT_PATH}${repeatedId.toString()}/`,
|
|
3022
|
+
});
|
|
3023
|
+
const finalId: ObjectID = ObjectID.generate();
|
|
3024
|
+
const finalDescendant: NetworkSite = fakeSite({
|
|
3025
|
+
id: finalId,
|
|
3026
|
+
_id: finalId.toString(),
|
|
3027
|
+
materializedPath: `${DISTRICT_PATH}${finalId.toString()}/`,
|
|
3028
|
+
});
|
|
3029
|
+
|
|
3030
|
+
jest
|
|
3031
|
+
.spyOn(NetworkSiteService, "getMaterializedPathForSite")
|
|
3032
|
+
.mockResolvedValue(`/${REGION_ID.toString()}/`);
|
|
3033
|
+
const findBySpy: jest.SpyInstance = jest
|
|
3034
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
3035
|
+
.mockResolvedValueOnce(
|
|
3036
|
+
new Array<NetworkSite>(1000).fill(repeatedDescendant),
|
|
3037
|
+
)
|
|
3038
|
+
.mockResolvedValueOnce([finalDescendant]);
|
|
3039
|
+
const updateColumnsSpy: jest.SpyInstance = jest
|
|
3040
|
+
.spyOn(NetworkSiteService, "updateColumnsByIdWithoutHooks")
|
|
3041
|
+
.mockResolvedValue(undefined as never);
|
|
3042
|
+
jest
|
|
3043
|
+
.spyOn(NetworkSiteService, "recomputeRollupForSiteAndAncestors")
|
|
3044
|
+
.mockResolvedValue(undefined as never);
|
|
3045
|
+
|
|
3046
|
+
await (NetworkSiteService as any).onDeleteSuccess(
|
|
3047
|
+
{
|
|
3048
|
+
deleteBy: { query: {}, props: { tenantId: PROJECT_ID } },
|
|
3049
|
+
carryForward: { sitesToDelete: [deletedDistrict()] },
|
|
3050
|
+
},
|
|
3051
|
+
[DISTRICT_ID],
|
|
3052
|
+
);
|
|
3053
|
+
|
|
3054
|
+
expect(findBySpy).toHaveBeenCalledTimes(2);
|
|
3055
|
+
expect(findBySpy.mock.calls[0]![0]).toEqual(
|
|
3056
|
+
expect.objectContaining({ limit: 1000, skip: 0 }),
|
|
3057
|
+
);
|
|
3058
|
+
expect(findBySpy.mock.calls[1]![0]).toEqual(
|
|
3059
|
+
expect.objectContaining({ limit: 1000, skip: 0 }),
|
|
3060
|
+
);
|
|
3061
|
+
expect(updateColumnsSpy).toHaveBeenCalledTimes(1001);
|
|
3062
|
+
expect(updateColumnsSpy).toHaveBeenCalledWith(
|
|
3063
|
+
expect.objectContaining({ id: finalId }),
|
|
3064
|
+
);
|
|
3065
|
+
});
|
|
3066
|
+
|
|
1563
3067
|
it("promotes children to roots when the deleted site was a root", async () => {
|
|
1564
3068
|
const rootPath: string = `/${DISTRICT_ID.toString()}/`;
|
|
1565
3069
|
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([
|
|
@@ -1647,3 +3151,236 @@ describe("NetworkSiteService delete hooks (orphan repair)", () => {
|
|
|
1647
3151
|
).resolves.toBeDefined();
|
|
1648
3152
|
});
|
|
1649
3153
|
});
|
|
3154
|
+
|
|
3155
|
+
describe("NetworkSiteService hierarchy mutation lock", () => {
|
|
3156
|
+
const runThroughLock: (data: {
|
|
3157
|
+
operation: () => Promise<unknown>;
|
|
3158
|
+
}) => Promise<unknown> = async (data: {
|
|
3159
|
+
operation: () => Promise<unknown>;
|
|
3160
|
+
}): Promise<unknown> => {
|
|
3161
|
+
return await data.operation();
|
|
3162
|
+
};
|
|
3163
|
+
|
|
3164
|
+
beforeEach(() => {
|
|
3165
|
+
jest.restoreAllMocks();
|
|
3166
|
+
});
|
|
3167
|
+
|
|
3168
|
+
afterEach(() => {
|
|
3169
|
+
jest.restoreAllMocks();
|
|
3170
|
+
});
|
|
3171
|
+
|
|
3172
|
+
it("uses the shared project lock around create", async () => {
|
|
3173
|
+
const site: NetworkSite = fakeSite({});
|
|
3174
|
+
const runExclusiveSpy: jest.SpyInstance = jest
|
|
3175
|
+
.spyOn(NetworkSiteHierarchyLock, "runExclusive")
|
|
3176
|
+
.mockImplementation(runThroughLock as never);
|
|
3177
|
+
const superCreateSpy: jest.SpyInstance = jest
|
|
3178
|
+
.spyOn(DatabaseService.prototype, "create")
|
|
3179
|
+
.mockResolvedValue(site);
|
|
3180
|
+
|
|
3181
|
+
await expect(
|
|
3182
|
+
NetworkSiteService.create({
|
|
3183
|
+
data: site,
|
|
3184
|
+
props: { isRoot: true },
|
|
3185
|
+
}),
|
|
3186
|
+
).resolves.toBe(site);
|
|
3187
|
+
|
|
3188
|
+
expect(runExclusiveSpy).toHaveBeenCalledWith(
|
|
3189
|
+
expect.objectContaining({ projectIds: [PROJECT_ID] }),
|
|
3190
|
+
);
|
|
3191
|
+
expect(runExclusiveSpy.mock.invocationCallOrder[0]!).toBeLessThan(
|
|
3192
|
+
superCreateSpy.mock.invocationCallOrder[0]!,
|
|
3193
|
+
);
|
|
3194
|
+
});
|
|
3195
|
+
|
|
3196
|
+
it.each([
|
|
3197
|
+
["parentSiteId", PARENT_SITE_ID],
|
|
3198
|
+
["networkSiteTypeId", CHILD_SITE_TYPE_ID],
|
|
3199
|
+
["projectId", PROJECT_ID],
|
|
3200
|
+
["depth", 0],
|
|
3201
|
+
])(
|
|
3202
|
+
"locks an update that changes %s",
|
|
3203
|
+
async (field: string, value: ObjectID | number) => {
|
|
3204
|
+
const runExclusiveSpy: jest.SpyInstance = jest
|
|
3205
|
+
.spyOn(NetworkSiteHierarchyLock, "runExclusive")
|
|
3206
|
+
.mockImplementation(runThroughLock as never);
|
|
3207
|
+
const findBySpy: jest.SpyInstance = jest
|
|
3208
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
3209
|
+
.mockResolvedValue([fakeSite({})]);
|
|
3210
|
+
const superUpdateSpy: jest.SpyInstance = jest
|
|
3211
|
+
.spyOn(DatabaseService.prototype, "updateOneBy")
|
|
3212
|
+
.mockResolvedValue(1);
|
|
3213
|
+
|
|
3214
|
+
await expect(
|
|
3215
|
+
NetworkSiteService.updateOneBy({
|
|
3216
|
+
query: { _id: SITE_ID.toString() },
|
|
3217
|
+
data: { [field]: value },
|
|
3218
|
+
props: { isRoot: true },
|
|
3219
|
+
} as any),
|
|
3220
|
+
).resolves.toBe(1);
|
|
3221
|
+
|
|
3222
|
+
expect(findBySpy).toHaveBeenCalledWith(
|
|
3223
|
+
expect.objectContaining({ select: { projectId: true } }),
|
|
3224
|
+
);
|
|
3225
|
+
expect(runExclusiveSpy).toHaveBeenCalledWith(
|
|
3226
|
+
expect.objectContaining({ projectIds: [PROJECT_ID] }),
|
|
3227
|
+
);
|
|
3228
|
+
expect(superUpdateSpy).toHaveBeenCalledTimes(1);
|
|
3229
|
+
},
|
|
3230
|
+
);
|
|
3231
|
+
|
|
3232
|
+
it("locks deletes with the same project key used by type mutations", async () => {
|
|
3233
|
+
const runExclusiveSpy: jest.SpyInstance = jest
|
|
3234
|
+
.spyOn(NetworkSiteHierarchyLock, "runExclusive")
|
|
3235
|
+
.mockImplementation(runThroughLock as never);
|
|
3236
|
+
jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([fakeSite({})]);
|
|
3237
|
+
jest.spyOn(DatabaseService.prototype, "deleteOneBy").mockResolvedValue(1);
|
|
3238
|
+
|
|
3239
|
+
await expect(
|
|
3240
|
+
NetworkSiteService.deleteOneBy({
|
|
3241
|
+
query: { _id: SITE_ID.toString() },
|
|
3242
|
+
props: { isRoot: true },
|
|
3243
|
+
}),
|
|
3244
|
+
).resolves.toBe(1);
|
|
3245
|
+
|
|
3246
|
+
expect(runExclusiveSpy).toHaveBeenCalledWith(
|
|
3247
|
+
expect.objectContaining({ projectIds: [PROJECT_ID] }),
|
|
3248
|
+
);
|
|
3249
|
+
});
|
|
3250
|
+
|
|
3251
|
+
it("bypasses the lock for rollup-only and ignoreHooks updates", async () => {
|
|
3252
|
+
const runExclusiveSpy: jest.SpyInstance = jest.spyOn(
|
|
3253
|
+
NetworkSiteHierarchyLock,
|
|
3254
|
+
"runExclusive",
|
|
3255
|
+
);
|
|
3256
|
+
const superUpdateSpy: jest.SpyInstance = jest
|
|
3257
|
+
.spyOn(DatabaseService.prototype, "updateOneBy")
|
|
3258
|
+
.mockResolvedValue(1);
|
|
3259
|
+
|
|
3260
|
+
await NetworkSiteService.updateOneBy({
|
|
3261
|
+
query: { _id: SITE_ID.toString() },
|
|
3262
|
+
data: { lastRollupAt: new Date() },
|
|
3263
|
+
props: { isRoot: true },
|
|
3264
|
+
});
|
|
3265
|
+
await NetworkSiteService.updateOneBy({
|
|
3266
|
+
query: { _id: SITE_ID.toString() },
|
|
3267
|
+
data: { parentSiteId: PARENT_SITE_ID },
|
|
3268
|
+
props: { isRoot: true, ignoreHooks: true },
|
|
3269
|
+
});
|
|
3270
|
+
|
|
3271
|
+
expect(runExclusiveSpy).not.toHaveBeenCalled();
|
|
3272
|
+
expect(superUpdateSpy).toHaveBeenCalledTimes(2);
|
|
3273
|
+
});
|
|
3274
|
+
|
|
3275
|
+
it("rejects an unscoped root hierarchy bulk mutation before writing", async () => {
|
|
3276
|
+
const superUpdateSpy: jest.SpyInstance = jest.spyOn(
|
|
3277
|
+
DatabaseService.prototype,
|
|
3278
|
+
"updateBy",
|
|
3279
|
+
);
|
|
3280
|
+
|
|
3281
|
+
await expect(
|
|
3282
|
+
NetworkSiteService.updateBy({
|
|
3283
|
+
query: { name: "Any matching site" },
|
|
3284
|
+
data: { parentSiteId: null },
|
|
3285
|
+
limit: 10,
|
|
3286
|
+
skip: 0,
|
|
3287
|
+
props: { isRoot: true },
|
|
3288
|
+
}),
|
|
3289
|
+
).rejects.toThrow(NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE);
|
|
3290
|
+
|
|
3291
|
+
expect(superUpdateSpy).not.toHaveBeenCalled();
|
|
3292
|
+
});
|
|
3293
|
+
|
|
3294
|
+
it("rejects an open-ended root update even when root supplies tenantId", async () => {
|
|
3295
|
+
const superUpdateSpy: jest.SpyInstance = jest.spyOn(
|
|
3296
|
+
DatabaseService.prototype,
|
|
3297
|
+
"updateBy",
|
|
3298
|
+
);
|
|
3299
|
+
|
|
3300
|
+
await expect(
|
|
3301
|
+
NetworkSiteService.updateBy({
|
|
3302
|
+
query: { name: "Any matching site" },
|
|
3303
|
+
data: { parentSiteId: null },
|
|
3304
|
+
limit: 10,
|
|
3305
|
+
skip: 0,
|
|
3306
|
+
props: { isRoot: true, tenantId: PROJECT_ID },
|
|
3307
|
+
}),
|
|
3308
|
+
).rejects.toThrow(NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE);
|
|
3309
|
+
|
|
3310
|
+
expect(superUpdateSpy).not.toHaveBeenCalled();
|
|
3311
|
+
});
|
|
3312
|
+
|
|
3313
|
+
it("rejects an open-ended root write to a materialized hierarchy field", async () => {
|
|
3314
|
+
const superUpdateSpy: jest.SpyInstance = jest.spyOn(
|
|
3315
|
+
DatabaseService.prototype,
|
|
3316
|
+
"updateBy",
|
|
3317
|
+
);
|
|
3318
|
+
|
|
3319
|
+
await expect(
|
|
3320
|
+
NetworkSiteService.updateBy({
|
|
3321
|
+
query: { name: "Any matching site" },
|
|
3322
|
+
data: { depth: 0 },
|
|
3323
|
+
limit: 10,
|
|
3324
|
+
skip: 0,
|
|
3325
|
+
props: { isRoot: true, tenantId: PROJECT_ID },
|
|
3326
|
+
}),
|
|
3327
|
+
).rejects.toThrow(NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE);
|
|
3328
|
+
|
|
3329
|
+
expect(superUpdateSpy).not.toHaveBeenCalled();
|
|
3330
|
+
});
|
|
3331
|
+
|
|
3332
|
+
it("turns the retention cron's open root query into a leaf-only ID batch", async () => {
|
|
3333
|
+
const parentId: ObjectID = PARENT_SITE_ID;
|
|
3334
|
+
const leafId: ObjectID = SITE_ID;
|
|
3335
|
+
const retentionQuery: any = { deletedAt: { olderThanThirtyDays: true } };
|
|
3336
|
+
|
|
3337
|
+
jest
|
|
3338
|
+
.spyOn(NetworkSiteService, "findBy")
|
|
3339
|
+
.mockImplementation(async (findBy: any) => {
|
|
3340
|
+
if (findBy.query?.parentSiteId) {
|
|
3341
|
+
return [
|
|
3342
|
+
fakeSite({
|
|
3343
|
+
id: leafId,
|
|
3344
|
+
_id: leafId.toString(),
|
|
3345
|
+
parentSiteId: parentId,
|
|
3346
|
+
}),
|
|
3347
|
+
];
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
if (findBy.skip > 0) {
|
|
3351
|
+
return [];
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
return [
|
|
3355
|
+
fakeSite({ id: parentId, _id: parentId.toString() }),
|
|
3356
|
+
fakeSite({ id: leafId, _id: leafId.toString() }),
|
|
3357
|
+
];
|
|
3358
|
+
});
|
|
3359
|
+
const runExclusiveSpy: jest.SpyInstance = jest
|
|
3360
|
+
.spyOn(NetworkSiteHierarchyLock, "runExclusive")
|
|
3361
|
+
.mockImplementation(runThroughLock as never);
|
|
3362
|
+
const superHardDeleteSpy: jest.SpyInstance = jest
|
|
3363
|
+
.spyOn(DatabaseService.prototype, "hardDeleteBy")
|
|
3364
|
+
.mockResolvedValue(1);
|
|
3365
|
+
|
|
3366
|
+
await expect(
|
|
3367
|
+
NetworkSiteService.hardDeleteBy({
|
|
3368
|
+
query: retentionQuery,
|
|
3369
|
+
limit: 2,
|
|
3370
|
+
skip: 0,
|
|
3371
|
+
props: { isRoot: true },
|
|
3372
|
+
} as DeleteBy<NetworkSite>),
|
|
3373
|
+
).resolves.toBe(1);
|
|
3374
|
+
|
|
3375
|
+
expect(runExclusiveSpy).toHaveBeenCalledWith(
|
|
3376
|
+
expect.objectContaining({ projectIds: [PROJECT_ID] }),
|
|
3377
|
+
);
|
|
3378
|
+
const closedDelete: any = superHardDeleteSpy.mock.calls[0]![0];
|
|
3379
|
+
expect(closedDelete.query.deletedAt).toBe(retentionQuery.deletedAt);
|
|
3380
|
+
expect(
|
|
3381
|
+
Object.values(closedDelete.query._id.objectLiteralParameters)[0],
|
|
3382
|
+
).toEqual([leafId.toString()]);
|
|
3383
|
+
expect(closedDelete.limit).toBe(1);
|
|
3384
|
+
expect(closedDelete.skip).toBe(0);
|
|
3385
|
+
});
|
|
3386
|
+
});
|