@oneuptime/common 12.0.33 → 13.0.0
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/DatabaseModels/AlertCustomField.ts +77 -3
- package/Models/DatabaseModels/ApiKeyPermission.ts +3 -27
- package/Models/DatabaseModels/GlobalConfig.ts +12 -12
- package/Models/DatabaseModels/IncidentCustomField.ts +77 -3
- package/Models/DatabaseModels/InventoryItem.ts +44 -1
- package/Models/DatabaseModels/InventoryItemCustomField.ts +77 -3
- package/Models/DatabaseModels/MonitorCustomField.ts +77 -3
- package/Models/DatabaseModels/NetworkDevice.ts +126 -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/TelemetryIngestionKey.ts +260 -0
- package/Models/DatabaseModels/UserTelegram.ts +0 -4
- package/Scripts/benchmark-fanin-capacity.js +181 -0
- 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 +82 -18
- 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/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Infrastructure/QueueWorker.ts +20 -14
- package/Server/Infrastructure/Redis.ts +8 -8
- package/Server/Infrastructure/Semaphore.ts +2 -0
- package/Server/Infrastructure/Status.ts +10 -4
- package/Server/Middleware/TelemetryIngest.ts +453 -7
- 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/DatabaseService.ts +65 -13
- package/Server/Services/GlobalConfigService.ts +194 -0
- package/Server/Services/IncidentCustomFieldService.ts +98 -0
- package/Server/Services/IncidentService.ts +35 -0
- package/Server/Services/InventoryItemService.ts +31 -1
- package/Server/Services/MetricService.ts +194 -0
- package/Server/Services/MonitorService.ts +32 -0
- package/Server/Services/MonitorTemplateService.ts +140 -19
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +427 -66
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
- package/Server/Services/NetworkDeviceService.ts +69 -0
- package/Server/Services/ProjectService.ts +22 -0
- 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/TeamMemberService.ts +78 -0
- package/Server/Services/TeamPermissionService.ts +235 -3
- package/Server/Services/TelemetryIngestionKeyService.ts +722 -19
- package/Server/Services/UserNotificationRuleService.ts +76 -25
- package/Server/Services/UserNotificationSettingService.ts +34 -6
- package/Server/Services/UserTelegramService.ts +326 -5
- package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
- package/Server/Types/Database/QueryHelper.ts +2 -0
- package/Server/Types/Database/QueryUtil.ts +56 -0
- package/Server/Types/Markdown.ts +93 -1
- package/Server/Utils/APIKey/AccessPermission.ts +5 -2
- 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/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
- package/Server/Utils/DataSource/EgressGuard.ts +177 -8
- package/Server/Utils/EmailRollup/EmailRollupConstants.ts +38 -5
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +67 -5
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
- package/Server/Utils/FrontendEnvironment.ts +40 -0
- package/Server/Utils/LogRedaction.ts +20 -0
- package/Server/Utils/Logger.ts +11 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +394 -86
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +30 -4
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
- package/Server/Utils/Monitor/MonitorAlert.ts +29 -6
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +312 -52
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
- package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
- package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
- package/Server/Utils/Monitor/MonitorIncident.ts +22 -6
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +29 -0
- package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
- package/Server/Utils/Monitor/SeriesContextEnricher.ts +304 -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 +2 -17
- package/Server/Utils/TelegramVerificationToken.ts +185 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
- package/Server/Utils/Telemetry/PinServiceName.ts +197 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
- 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/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +975 -95
- package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +202 -0
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
- package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
- package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +404 -0
- package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
- package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +468 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
- package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
- package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
- package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
- package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
- package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
- package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
- package/Tests/App/StatusPage/StatusPageLastUpdated.test.tsx +263 -0
- package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
- 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/InventoryItemStatus.test.ts +147 -0
- package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
- package/Tests/ResponsiveVisibility.test.ts +115 -0
- package/Tests/ResponsiveVisibility.ts +183 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -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/EnvironmentConfigValkey.test.ts +227 -0
- package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
- package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +241 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +41 -0
- package/Tests/Server/Infrastructure/Postgres/SessionReplayRecordEverySessionByDefaultMigration.test.ts +169 -0
- package/Tests/Server/Infrastructure/QueueWorkerTimeout.test.ts +210 -0
- package/Tests/Server/Infrastructure/TelemetryExporterDeploymentConfig.test.ts +130 -0
- package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
- package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -1
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +1163 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +5 -3
- 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/CustomFieldDropdownOptionsColumnWidth.test.ts +431 -0
- package/Tests/Server/Services/CustomFieldMappingService.test.ts +850 -0
- package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
- package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +32 -0
- package/Tests/Server/Services/GlobalConfigService.test.ts +414 -1
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
- package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
- package/Tests/Server/Services/MetricRawEntityKeyPrune.test.ts +592 -0
- package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1351 -74
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
- package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
- package/Tests/Server/Services/RoutineEmailSettingsPostgres.test.ts +329 -0
- package/Tests/Server/Services/RumSessionReplayViewService.test.ts +267 -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/UserNotificationSettingRollupRouting.test.ts +141 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +18 -10
- package/Tests/Server/Services/UserTelegramVerificationSecurity.test.ts +688 -0
- package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
- package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
- package/Tests/Server/Types/Markdown.test.ts +194 -1
- package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
- package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -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/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingValidator.test.ts +437 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +300 -11
- package/Tests/Server/Utils/EmailRollup/EmailRollupBurstWindow.test.ts +83 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +2 -2
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerPreferences.test.ts +362 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
- package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +77 -1
- package/Tests/Server/Utils/EventLoop.test.ts +359 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +190 -0
- package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +26 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +23 -9
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +590 -0
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestHeaderCriteria.test.ts +236 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +659 -8
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
- package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
- package/Tests/Server/Utils/Monitor/MonitorTemplateUtilSeriesContext.test.ts +176 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
- package/Tests/Server/Utils/Monitor/SeriesContextEnricher.test.ts +356 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +595 -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/ContextSpanProcessor.test.ts +281 -0
- package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
- package/Tests/Server/Utils/Telemetry/PinServiceName.test.ts +666 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
- 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/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/Database/IncludesAnyOfGroups.test.ts +106 -0
- package/Tests/Types/JSONFunctions.test.ts +300 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +422 -53
- 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/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/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
- package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
- 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/InventoryLiveness.test.ts +99 -0
- package/Tests/Types/WebsiteRequest.test.ts +285 -3
- 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/CustomFieldsDetail.test.tsx +160 -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 +28 -12
- package/Tests/UI/Components/ShortcutDialogGuard.test.tsx +74 -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/ReactRouterSingletonBuild.test.ts +133 -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 +719 -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/ReplayPlayerChrome.test.tsx +514 -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/ReplayTimeFormat.test.ts +94 -0
- package/Tests/UI/Rum/ReplayTimeline.test.tsx +696 -0
- package/Tests/UI/Rum/ReplayUi.test.tsx +559 -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/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
- package/Tests/UI/Utils/GlobalKeyboardShortcut.test.ts +344 -0
- package/Tests/Utils/API.test.ts +308 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
- package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
- package/Tests/Utils/HTTPResponseBodyReader.test.ts +323 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
- package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
- package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
- package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
- package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveryScanStatus.test.ts +117 -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/OriginAllowList.test.ts +633 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +461 -0
- package/Tests/Utils/ValueFormatter.test.ts +148 -0
- package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +161 -0
- package/Types/CustomField/CustomFieldMappingSourceResource.ts +19 -0
- package/Types/CustomField/CustomFieldValueMapping.ts +288 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
- package/Types/Dashboard/DashboardVariable.ts +8 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/JSON.ts +4 -0
- package/Types/JSONFunctions.ts +205 -43
- package/Types/Monitor/CephAlertTemplates.ts +157 -195
- 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/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/NetworkTopology.ts +13 -0
- package/Types/Monitor/UptimeHistoryLabels.ts +56 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
- package/Types/NotificationSetting/RoutineEmailEvents.ts +30 -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/SerializableObjectDictionary.ts +4 -0
- package/Types/Telemetry/InventoryLiveness.ts +56 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +95 -0
- package/Types/Telemetry/TelemetryIngestionKeyPolicy.ts +75 -0
- package/Types/Telemetry/TelemetryIngestionKeyType.ts +29 -0
- package/Types/WebsiteRequest.ts +85 -3
- package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
- 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/CustomFields/CustomFieldsDetail.tsx +189 -46
- package/UI/Components/CustomFields/MapFromCustomFieldInput.tsx +216 -0
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -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/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 +44 -0
- 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/Telemetry/BrowserTelemetryConfig.ts +28 -0
- package/UI/Utils/Telemetry/Telemetry.ts +10 -8
- package/UI/esbuild-config.js +6 -0
- package/Utils/API.ts +129 -3
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
- package/Utils/Dashboard/LabelVariable.ts +152 -0
- package/Utils/Dashboard/VariableUrlState.ts +27 -5
- package/Utils/HTTPResponseBodyReader.ts +221 -0
- package/Utils/MetricUnitUtil.ts +34 -0
- package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
- package/Utils/Monitor/MetricValueFormatter.ts +342 -0
- package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
- package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
- package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
- package/Utils/NetworkDiscovery/DiscoveryScanStatus.ts +77 -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/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/GlobalConfig.js +12 -12
- package/build/dist/Models/DatabaseModels/GlobalConfig.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/InventoryItem.js +34 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.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/MonitorCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- 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/TelemetryIngestionKey.js +267 -0
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +0 -4
- package/build/dist/Models/DatabaseModels/UserTelegram.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 +73 -14
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- 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/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +19 -7
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Infrastructure/Redis.js +8 -8
- package/build/dist/Server/Infrastructure/Redis.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/Infrastructure/Status.js +10 -4
- package/build/dist/Server/Infrastructure/Status.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +315 -8
- package/build/dist/Server/Middleware/TelemetryIngest.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/DatabaseService.js +58 -13
- package/build/dist/Server/Services/DatabaseService.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/InventoryItemService.js +28 -1
- package/build/dist/Server/Services/InventoryItemService.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 +29 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +349 -64
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- 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/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/UserNotificationRuleService.js +60 -25
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +26 -6
- package/build/dist/Server/Services/UserNotificationSettingService.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/Permissions/ReadPermission.js +29 -4
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +75 -0
- package/build/dist/Server/Types/Markdown.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/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/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- 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/EmailRollup/EmailRollupConstants.js +38 -5
- package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +54 -3
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
- package/build/dist/Server/Utils/FrontendEnvironment.js +33 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -0
- package/build/dist/Server/Utils/LogRedaction.js +20 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
- package/build/dist/Server/Utils/Logger.js +10 -0
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +323 -81
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.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/MetricMonitorCriteria.js +29 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.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/MonitorCriteriaEvaluator.js +254 -48
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
- 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/MonitorTemplateUtil.js +22 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
- 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/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 +3 -14
- 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/EntityRegistry.js +9 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- 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/TelemetryEntity.js +70 -13
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.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/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/BaseDatabase/IncludesAnyOfGroups.js +54 -0
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
- 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/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
- package/build/dist/Types/Dashboard/DashboardVariable.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/JSON.js +1 -0
- package/build/dist/Types/JSON.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +131 -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/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/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/UptimeHistoryLabels.js +17 -0
- package/build/dist/Types/Monitor/UptimeHistoryLabels.js.map +1 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js +29 -0
- package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js.map +1 -0
- 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/SerializableObjectDictionary.js +4 -0
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.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/WebsiteRequest.js +55 -3
- package/build/dist/Types/WebsiteRequest.js.map +1 -1
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.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/CustomFields/CustomFieldsDetail.js +89 -8
- 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/Dashboard/DashboardVariableControl.js +137 -0
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
- 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/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 +38 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.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/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/Utils/API.js +67 -5
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
- package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
- package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
- package/build/dist/Utils/Dashboard/VariableUrlState.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/MetricUnitUtil.js +26 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js +69 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.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/jest.config.json +2 -0
- package/package.json +1 -1
- package/test-setup.sh +9 -9
- package/tsconfig.json +51 -11
|
@@ -0,0 +1,998 @@
|
|
|
1
|
+
import { Service as TelemetryIngestionKeyServiceType } from "../../../Server/Services/TelemetryIngestionKeyService";
|
|
2
|
+
import Model from "../../../Models/DatabaseModels/TelemetryIngestionKey";
|
|
3
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
4
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
5
|
+
import DeleteBy from "../../../Server/Types/Database/DeleteBy";
|
|
6
|
+
import Query from "../../../Server/Types/Database/Query";
|
|
7
|
+
import {
|
|
8
|
+
OnCreate,
|
|
9
|
+
OnDelete,
|
|
10
|
+
OnUpdate,
|
|
11
|
+
} from "../../../Server/Types/Database/Hooks";
|
|
12
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
13
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
14
|
+
import ColumnLength from "../../../Types/Database/ColumnLength";
|
|
15
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
16
|
+
import TelemetryIngestionKeyType from "../../../Types/Telemetry/TelemetryIngestionKeyType";
|
|
17
|
+
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
|
|
18
|
+
import { beforeEach, describe, expect, jest, test } from "@jest/globals";
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* WHAT THIS FILE IS DEFENDING
|
|
22
|
+
*
|
|
23
|
+
* The one rule that makes a Browser ingestion key safe to publish at all: it
|
|
24
|
+
* cannot be created, and cannot be left, without an origin allowlist.
|
|
25
|
+
*
|
|
26
|
+
* A Browser key is pasted into public page source. "Secret" is a fiction for
|
|
27
|
+
* it - anyone who views source has the bearer token - so the ONLY thing
|
|
28
|
+
* standing between a scraped key and forged telemetry written into a
|
|
29
|
+
* customer's project is the list of origins the key is bound to. There is no
|
|
30
|
+
* safe permissive default for that list, which is why an empty one is refused
|
|
31
|
+
* at the write path rather than defaulted to "any origin": a Browser key with
|
|
32
|
+
* no origins is precisely the credential this whole feature exists to stop
|
|
33
|
+
* shipping.
|
|
34
|
+
*
|
|
35
|
+
* Postgres cannot express "non-empty only when keyType is Browser", so the
|
|
36
|
+
* service layer is the only place the rule lives on the write side. These
|
|
37
|
+
* tests therefore go through the real onBeforeCreate / onBeforeUpdate hooks.
|
|
38
|
+
*
|
|
39
|
+
* Three further things are pinned here because each of them fails silently:
|
|
40
|
+
*
|
|
41
|
+
* 1. keyType is IMMUTABLE. Browser -> Server strips the origin binding off
|
|
42
|
+
* a key that is still sitting in a public page; Server -> Browser breaks
|
|
43
|
+
* a collector that has shipped traces for a year the moment it sends
|
|
44
|
+
* from no Origin. The column declares `update: []`, but that is an
|
|
45
|
+
* API-layer control - an internal caller reaching for updateOneById
|
|
46
|
+
* bypasses it, so the service refuses the patch too.
|
|
47
|
+
* 2. THE BACKWARDS-COMPATIBILITY CONTRACT. A create with no keyType is a
|
|
48
|
+
* Server key, needs no allowlist, and behaves exactly as every key that
|
|
49
|
+
* existed before this shipped. Rows that predate the column read back
|
|
50
|
+
* with keyType NULL and must not be mistaken for Browser keys by the
|
|
51
|
+
* "you may not empty this list" guard.
|
|
52
|
+
* 3. COST. The guard that refuses an empty allowlist has to read the rows
|
|
53
|
+
* the update matches. That read must happen ONLY when the patch actually
|
|
54
|
+
* empties the list - the common update (rename, toggle isEnabled, set an
|
|
55
|
+
* expiry) must not start paying a query for a rule it cannot break.
|
|
56
|
+
*
|
|
57
|
+
* Nothing here touches Postgres or Redis: the hooks are invoked directly on a
|
|
58
|
+
* fresh service instance whose findBy/findOneBy are stubbed.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
const KEY_ID: ObjectID = new ObjectID("11111111-1111-4111-8111-111111111111");
|
|
62
|
+
const SECOND_KEY_ID: ObjectID = new ObjectID(
|
|
63
|
+
"22222222-2222-4222-8222-222222222222",
|
|
64
|
+
);
|
|
65
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
66
|
+
"33333333-3333-4333-8333-333333333333",
|
|
67
|
+
);
|
|
68
|
+
const OTHER_PROJECT_ID: ObjectID = new ObjectID(
|
|
69
|
+
"44444444-4444-4444-8444-444444444444",
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const HOUR_MS: number = 60 * 60 * 1000;
|
|
73
|
+
|
|
74
|
+
type ServiceInternals = {
|
|
75
|
+
onBeforeCreate: (createBy: CreateBy<Model>) => Promise<OnCreate<Model>>;
|
|
76
|
+
onBeforeUpdate: (updateBy: UpdateBy<Model>) => Promise<OnUpdate<Model>>;
|
|
77
|
+
onBeforeDelete: (deleteBy: DeleteBy<Model>) => Promise<OnDelete<Model>>;
|
|
78
|
+
policyCache: {
|
|
79
|
+
set: (key: string, value: null, ttlMs: number) => void;
|
|
80
|
+
size: () => number;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* Typed loosely on purpose: jest.spyOn's SpiedFunction and this repo's
|
|
86
|
+
* @types/jest disagree about the optionality of mock.lastCall, and nothing
|
|
87
|
+
* below needs more than mockResolvedValue and the recorded calls.
|
|
88
|
+
*/
|
|
89
|
+
type FindSpy = {
|
|
90
|
+
mockResolvedValue: (value: never) => unknown;
|
|
91
|
+
mock: { calls: Array<Array<unknown>> };
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
interface Harness {
|
|
95
|
+
internals: ServiceInternals;
|
|
96
|
+
findBy: FindSpy;
|
|
97
|
+
findOneBy: FindSpy;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
jest.restoreAllMocks();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
function buildService(): Harness {
|
|
105
|
+
const service: TelemetryIngestionKeyServiceType =
|
|
106
|
+
new TelemetryIngestionKeyServiceType();
|
|
107
|
+
|
|
108
|
+
const findBy: FindSpy = jest.spyOn(service, "findBy") as unknown as FindSpy;
|
|
109
|
+
findBy.mockResolvedValue([] as never);
|
|
110
|
+
|
|
111
|
+
const findOneBy: FindSpy = jest.spyOn(
|
|
112
|
+
service,
|
|
113
|
+
"findOneBy",
|
|
114
|
+
) as unknown as FindSpy;
|
|
115
|
+
findOneBy.mockResolvedValue(null as never);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
internals: service as unknown as ServiceInternals,
|
|
119
|
+
findBy: findBy,
|
|
120
|
+
findOneBy: findOneBy,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/*
|
|
125
|
+
* The realistic create payload: a model instance, which is what the API layer
|
|
126
|
+
* hands the service. Every declared column is an OWN property on it (each is
|
|
127
|
+
* initialised to `undefined` in the class body), so this shape - not a
|
|
128
|
+
* hand-built bag - is what the hook's hasOwnProperty guards actually meet.
|
|
129
|
+
*/
|
|
130
|
+
function createByModel(payload: Record<string, unknown>): CreateBy<Model> {
|
|
131
|
+
return {
|
|
132
|
+
data: Object.assign(new Model(), payload) as Model,
|
|
133
|
+
props: { isRoot: true },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* A payload with no own property for the columns it does not mention, which
|
|
139
|
+
* is what an internal caller building a literal produces.
|
|
140
|
+
*/
|
|
141
|
+
function createByBag(payload: Record<string, unknown>): CreateBy<Model> {
|
|
142
|
+
return {
|
|
143
|
+
data: payload as unknown as Model,
|
|
144
|
+
props: { isRoot: true },
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function updateByBag(
|
|
149
|
+
payload: Record<string, unknown>,
|
|
150
|
+
query: Query<Model> = { _id: KEY_ID.toString() },
|
|
151
|
+
props: DatabaseCommonInteractionProps = { isRoot: true },
|
|
152
|
+
): UpdateBy<Model> {
|
|
153
|
+
return {
|
|
154
|
+
query: query,
|
|
155
|
+
data: payload,
|
|
156
|
+
props: props,
|
|
157
|
+
limit: LIMIT_PER_PROJECT,
|
|
158
|
+
skip: 0,
|
|
159
|
+
} as unknown as UpdateBy<Model>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/*
|
|
163
|
+
* A row as the pre-read sees it. `keyTypeValue` is deliberately `unknown` so a
|
|
164
|
+
* legacy row - written before the column existed, and therefore NULL - can be
|
|
165
|
+
* modelled exactly.
|
|
166
|
+
*/
|
|
167
|
+
function affectedRow(id: ObjectID, keyTypeValue: unknown): Model {
|
|
168
|
+
const row: Model = new Model(id);
|
|
169
|
+
(row as unknown as Record<string, unknown>)["keyType"] = keyTypeValue;
|
|
170
|
+
return row;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function writtenValues(createBy: CreateBy<Model>): Record<string, unknown> {
|
|
174
|
+
return createBy.data as unknown as Record<string, unknown>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
describe("TelemetryIngestionKeyService.onBeforeCreate - a Browser key cannot be created without an origin allowlist", () => {
|
|
178
|
+
/*
|
|
179
|
+
* Every shape a "no origins" allowlist arrives in. [""] and [" "] matter
|
|
180
|
+
* as much as [] does: a blank entry is dropped by the matcher, so a list of
|
|
181
|
+
* blanks is non-empty in the database and matches nothing at ingest time -
|
|
182
|
+
* a published key that appears configured and is bound to nothing.
|
|
183
|
+
*/
|
|
184
|
+
const emptyAllowLists: Array<[string, Record<string, unknown>]> = [
|
|
185
|
+
["absent from the payload", {}],
|
|
186
|
+
["explicitly null", { allowedOrigins: null }],
|
|
187
|
+
["an empty array", { allowedOrigins: [] }],
|
|
188
|
+
["an array holding one empty string", { allowedOrigins: [""] }],
|
|
189
|
+
["an array holding one blank string", { allowedOrigins: [" "] }],
|
|
190
|
+
[
|
|
191
|
+
"an array of nothing but blanks",
|
|
192
|
+
{ allowedOrigins: ["", " ", "\t", "\n"] },
|
|
193
|
+
],
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
test.each(emptyAllowLists)(
|
|
197
|
+
"refuses a Browser key whose allowed origins are %s",
|
|
198
|
+
async (_label: string, payload: Record<string, unknown>): Promise<void> => {
|
|
199
|
+
const { internals }: Harness = buildService();
|
|
200
|
+
|
|
201
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
202
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
203
|
+
...payload,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await expect(internals.onBeforeCreate(createBy)).rejects.toThrow(
|
|
207
|
+
BadDataException,
|
|
208
|
+
);
|
|
209
|
+
await expect(internals.onBeforeCreate(createBy)).rejects.toThrow(
|
|
210
|
+
"must list at least one allowed origin",
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
test("refuses a Browser key when allowedOrigins is not an own property of the payload at all", async () => {
|
|
216
|
+
const { internals }: Harness = buildService();
|
|
217
|
+
|
|
218
|
+
await expect(
|
|
219
|
+
internals.onBeforeCreate(
|
|
220
|
+
createByBag({ keyType: TelemetryIngestionKeyType.Browser }),
|
|
221
|
+
),
|
|
222
|
+
).rejects.toThrow("must list at least one allowed origin");
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe("TelemetryIngestionKeyService.onBeforeCreate - allowlist entries are validated, and the refusal names the entry", () => {
|
|
227
|
+
/*
|
|
228
|
+
* Each of these is something a customer plainly meant but that `matches`
|
|
229
|
+
* will never accept, so storing it hands them an allowlist entry that
|
|
230
|
+
* silently matches nothing. The message has to name the offending entry:
|
|
231
|
+
* "one of your origins is wrong" is unfixable advice on a list of ten.
|
|
232
|
+
*/
|
|
233
|
+
const invalidOrigins: Array<string> = [
|
|
234
|
+
"app.example.com",
|
|
235
|
+
"https://app.example.com/some/path",
|
|
236
|
+
"https://*",
|
|
237
|
+
"https://a.*.example.com",
|
|
238
|
+
"https://*example.com",
|
|
239
|
+
"ftp://example.com",
|
|
240
|
+
"https://user@example.com",
|
|
241
|
+
"https://example.com:70000",
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
test.each(invalidOrigins)(
|
|
245
|
+
"refuses %s and says so by name",
|
|
246
|
+
async (badOrigin: string): Promise<void> => {
|
|
247
|
+
const { internals }: Harness = buildService();
|
|
248
|
+
|
|
249
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
250
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
251
|
+
allowedOrigins: [badOrigin],
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
await expect(internals.onBeforeCreate(createBy)).rejects.toThrow(
|
|
255
|
+
BadDataException,
|
|
256
|
+
);
|
|
257
|
+
await expect(internals.onBeforeCreate(createBy)).rejects.toThrow(
|
|
258
|
+
badOrigin,
|
|
259
|
+
);
|
|
260
|
+
},
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
test("names the offending entry rather than the first entry when a good origin precedes a bad one", async () => {
|
|
264
|
+
const { internals }: Harness = buildService();
|
|
265
|
+
|
|
266
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
267
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
268
|
+
allowedOrigins: ["https://good.example.com", "https://*"],
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await expect(internals.onBeforeCreate(createBy)).rejects.toThrow(
|
|
272
|
+
"https://*",
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
let message: string = "";
|
|
276
|
+
try {
|
|
277
|
+
await internals.onBeforeCreate(createBy);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
message = (err as Error).message;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
expect(message).toContain("https://*");
|
|
283
|
+
expect(message).not.toContain("good.example.com");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("refuses an allowedOrigins that is not a list at all", async () => {
|
|
287
|
+
const { internals }: Harness = buildService();
|
|
288
|
+
|
|
289
|
+
await expect(
|
|
290
|
+
internals.onBeforeCreate(
|
|
291
|
+
createByModel({
|
|
292
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
293
|
+
allowedOrigins: "https://app.example.com",
|
|
294
|
+
}),
|
|
295
|
+
),
|
|
296
|
+
).rejects.toThrow("must be a list of origins");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("refuses an allowlist entry that is not text", async () => {
|
|
300
|
+
const { internals }: Harness = buildService();
|
|
301
|
+
|
|
302
|
+
await expect(
|
|
303
|
+
internals.onBeforeCreate(
|
|
304
|
+
createByModel({
|
|
305
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
306
|
+
allowedOrigins: [{ origin: "https://app.example.com" }],
|
|
307
|
+
}),
|
|
308
|
+
),
|
|
309
|
+
).rejects.toThrow("must be text");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
/*
|
|
313
|
+
* Validated on a Server key too, even though a Server key never enforces
|
|
314
|
+
* them. A list typed in "for later" that is quietly full of entries which
|
|
315
|
+
* can never match is a trap that only springs the day the customer decides
|
|
316
|
+
* to rely on it.
|
|
317
|
+
*/
|
|
318
|
+
test("validates the allowlist on a Server key as well, even though it is not required there", async () => {
|
|
319
|
+
const { internals }: Harness = buildService();
|
|
320
|
+
|
|
321
|
+
await expect(
|
|
322
|
+
internals.onBeforeCreate(
|
|
323
|
+
createByModel({
|
|
324
|
+
keyType: TelemetryIngestionKeyType.Server,
|
|
325
|
+
allowedOrigins: ["https://a.*.example.com"],
|
|
326
|
+
}),
|
|
327
|
+
),
|
|
328
|
+
).rejects.toThrow("https://a.*.example.com");
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe("TelemetryIngestionKeyService.onBeforeCreate - the other constrained columns", () => {
|
|
333
|
+
test("refuses a pinned service name longer than the column can hold", async () => {
|
|
334
|
+
const { internals }: Harness = buildService();
|
|
335
|
+
|
|
336
|
+
await expect(
|
|
337
|
+
internals.onBeforeCreate(
|
|
338
|
+
createByModel({
|
|
339
|
+
pinnedServiceName: "a".repeat(ColumnLength.ShortText + 1),
|
|
340
|
+
}),
|
|
341
|
+
),
|
|
342
|
+
).rejects.toThrow(
|
|
343
|
+
`cannot be longer than ${ColumnLength.ShortText} characters`,
|
|
344
|
+
);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("refuses a pinned service name that is not text", async () => {
|
|
348
|
+
const { internals }: Harness = buildService();
|
|
349
|
+
|
|
350
|
+
await expect(
|
|
351
|
+
internals.onBeforeCreate(createByModel({ pinnedServiceName: 42 })),
|
|
352
|
+
).rejects.toThrow("must be text");
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
/*
|
|
356
|
+
* DELIBERATELY NOT A REFUSAL. A blank pinned service name means "not
|
|
357
|
+
* pinned", and is normalised to null rather than rejected, because "" and
|
|
358
|
+
* " " are exactly what an emptied form field posts - refusing them would
|
|
359
|
+
* leave a customer who once pinned a service name unable to ever unpin it.
|
|
360
|
+
* A Browser key does not require a pin (TelemetryIngestionKeyGuard never
|
|
361
|
+
* consults one), so accepting blank as "no pin" gives up nothing.
|
|
362
|
+
*/
|
|
363
|
+
test("treats a blank pinned service name as no pin rather than refusing it", async () => {
|
|
364
|
+
const { internals }: Harness = buildService();
|
|
365
|
+
|
|
366
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
367
|
+
pinnedServiceName: " ",
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
371
|
+
expect(writtenValues(createBy)["pinnedServiceName"]).toBeNull();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const invalidLimits: Array<[string, unknown]> = [
|
|
375
|
+
["zero", 0],
|
|
376
|
+
["negative", -1],
|
|
377
|
+
["fractional", 1.5],
|
|
378
|
+
["NaN", Number.NaN],
|
|
379
|
+
["text", "not-a-number"],
|
|
380
|
+
["an object", {}],
|
|
381
|
+
];
|
|
382
|
+
|
|
383
|
+
test.each(invalidLimits)(
|
|
384
|
+
"refuses a requests-per-minute limit that is %s",
|
|
385
|
+
async (_label: string, limit: unknown): Promise<void> => {
|
|
386
|
+
const { internals }: Harness = buildService();
|
|
387
|
+
|
|
388
|
+
await expect(
|
|
389
|
+
internals.onBeforeCreate(
|
|
390
|
+
createByModel({ requestsPerMinuteLimit: limit }),
|
|
391
|
+
),
|
|
392
|
+
).rejects.toThrow("whole number greater than 0");
|
|
393
|
+
},
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
/*
|
|
397
|
+
* Refused rather than accepted-and-immediately-dead. Saving a past expiry
|
|
398
|
+
* bricks the key on save, and the symptom - telemetry stops, key still
|
|
399
|
+
* looks fine in the list - costs an afternoon to diagnose.
|
|
400
|
+
*/
|
|
401
|
+
test("refuses an expiry that is already in the past", async () => {
|
|
402
|
+
const { internals }: Harness = buildService();
|
|
403
|
+
|
|
404
|
+
await expect(
|
|
405
|
+
internals.onBeforeCreate(
|
|
406
|
+
createByModel({ expiresAt: new Date(Date.now() - HOUR_MS) }),
|
|
407
|
+
),
|
|
408
|
+
).rejects.toThrow("must be in the future");
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
describe("TelemetryIngestionKeyService.onBeforeCreate - what must keep working", () => {
|
|
413
|
+
/*
|
|
414
|
+
* THE BACKWARDS-COMPATIBILITY STORY, at the write path. A create with no
|
|
415
|
+
* key type is a Server key, and a Server key needs no allowlist. If this
|
|
416
|
+
* ever starts defaulting to Browser - or starts demanding origins - every
|
|
417
|
+
* existing integration that creates keys breaks at once.
|
|
418
|
+
*/
|
|
419
|
+
test("a create with no key type is written as a Server key", async () => {
|
|
420
|
+
const { internals }: Harness = buildService();
|
|
421
|
+
|
|
422
|
+
const createBy: CreateBy<Model> = createByModel({});
|
|
423
|
+
|
|
424
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
425
|
+
expect(writtenValues(createBy)["keyType"]).toBe(
|
|
426
|
+
TelemetryIngestionKeyType.Server,
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("an empty-string key type is written as a Server key rather than refused", async () => {
|
|
431
|
+
const { internals }: Harness = buildService();
|
|
432
|
+
|
|
433
|
+
const createBy: CreateBy<Model> = createByModel({ keyType: "" });
|
|
434
|
+
|
|
435
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
436
|
+
expect(writtenValues(createBy)["keyType"]).toBe(
|
|
437
|
+
TelemetryIngestionKeyType.Server,
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
/*
|
|
442
|
+
* An unrecognised type is refused rather than coerced: "I asked for a
|
|
443
|
+
* Browser key and got a Server key" is a security surprise, not a typo to
|
|
444
|
+
* paper over.
|
|
445
|
+
*/
|
|
446
|
+
test("an unrecognised key type is refused, not silently coerced to Server", async () => {
|
|
447
|
+
const { internals }: Harness = buildService();
|
|
448
|
+
|
|
449
|
+
await expect(
|
|
450
|
+
internals.onBeforeCreate(createByModel({ keyType: "browser" })),
|
|
451
|
+
).rejects.toThrow("is not a valid ingestion key type");
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
test("a Server key with no allowed origins is created - the overwhelmingly common case", async () => {
|
|
455
|
+
const { internals }: Harness = buildService();
|
|
456
|
+
|
|
457
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
458
|
+
keyType: TelemetryIngestionKeyType.Server,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
462
|
+
expect(writtenValues(createBy)["keyType"]).toBe(
|
|
463
|
+
TelemetryIngestionKeyType.Server,
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test("a Server key may still carry a valid allowlist", async () => {
|
|
468
|
+
const { internals }: Harness = buildService();
|
|
469
|
+
|
|
470
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
471
|
+
keyType: TelemetryIngestionKeyType.Server,
|
|
472
|
+
allowedOrigins: ["https://app.example.com"],
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
476
|
+
expect(writtenValues(createBy)["allowedOrigins"]).toEqual([
|
|
477
|
+
"https://app.example.com",
|
|
478
|
+
]);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("a Browser key with a single valid origin is created", async () => {
|
|
482
|
+
const { internals }: Harness = buildService();
|
|
483
|
+
|
|
484
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
485
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
486
|
+
allowedOrigins: ["https://app.example.com"],
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
490
|
+
expect(writtenValues(createBy)["keyType"]).toBe(
|
|
491
|
+
TelemetryIngestionKeyType.Browser,
|
|
492
|
+
);
|
|
493
|
+
expect(writtenValues(createBy)["allowedOrigins"]).toEqual([
|
|
494
|
+
"https://app.example.com",
|
|
495
|
+
]);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("a Browser key with several valid origins keeps all of them", async () => {
|
|
499
|
+
const { internals }: Harness = buildService();
|
|
500
|
+
|
|
501
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
502
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
503
|
+
allowedOrigins: [
|
|
504
|
+
"https://app.example.com",
|
|
505
|
+
"https://*.example.com",
|
|
506
|
+
"http://localhost:3000",
|
|
507
|
+
],
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
511
|
+
expect(writtenValues(createBy)["allowedOrigins"]).toEqual([
|
|
512
|
+
"https://app.example.com",
|
|
513
|
+
"https://*.example.com",
|
|
514
|
+
"http://localhost:3000",
|
|
515
|
+
]);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
/*
|
|
519
|
+
* Stored in the form the matcher compares against, so what the dashboard
|
|
520
|
+
* lists is exactly what will be matched at ingest time. A pasted
|
|
521
|
+
* "https://App.Example.com/" that silently never matches is a total ingest
|
|
522
|
+
* failure whose only symptom is a trailing character.
|
|
523
|
+
*/
|
|
524
|
+
test("origins are stored canonicalised and de-duplicated", async () => {
|
|
525
|
+
const { internals }: Harness = buildService();
|
|
526
|
+
|
|
527
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
528
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
529
|
+
allowedOrigins: [
|
|
530
|
+
" HTTPS://App.Example.com/ ",
|
|
531
|
+
"https://app.example.com",
|
|
532
|
+
"",
|
|
533
|
+
],
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
537
|
+
expect(writtenValues(createBy)["allowedOrigins"]).toEqual([
|
|
538
|
+
"https://app.example.com",
|
|
539
|
+
]);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
test("a valid pinned service name is trimmed before it is written", async () => {
|
|
543
|
+
const { internals }: Harness = buildService();
|
|
544
|
+
|
|
545
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
546
|
+
pinnedServiceName: " checkout-web ",
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
550
|
+
expect(writtenValues(createBy)["pinnedServiceName"]).toBe("checkout-web");
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("a valid requests-per-minute limit passes through unchanged", async () => {
|
|
554
|
+
const { internals }: Harness = buildService();
|
|
555
|
+
|
|
556
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
557
|
+
requestsPerMinuteLimit: 1200,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
561
|
+
expect(writtenValues(createBy)["requestsPerMinuteLimit"]).toBe(1200);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test("a future expiry passes through unchanged", async () => {
|
|
565
|
+
const { internals }: Harness = buildService();
|
|
566
|
+
|
|
567
|
+
const expiresAt: Date = new Date(Date.now() + HOUR_MS);
|
|
568
|
+
const createBy: CreateBy<Model> = createByModel({ expiresAt: expiresAt });
|
|
569
|
+
|
|
570
|
+
await expect(internals.onBeforeCreate(createBy)).resolves.toBeDefined();
|
|
571
|
+
expect((writtenValues(createBy)["expiresAt"] as Date).getTime()).toBe(
|
|
572
|
+
expiresAt.getTime(),
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* Pre-existing behaviour that the new validation must not have displaced:
|
|
578
|
+
* the secret is generated server-side when the caller does not supply one.
|
|
579
|
+
* A key created without one would be unusable, and - worse - a caller could
|
|
580
|
+
* not tell that from a key created with one.
|
|
581
|
+
*/
|
|
582
|
+
test("the secret key is still generated when the payload does not carry one", async () => {
|
|
583
|
+
const { internals }: Harness = buildService();
|
|
584
|
+
|
|
585
|
+
const createBy: CreateBy<Model> = createByModel({
|
|
586
|
+
keyType: TelemetryIngestionKeyType.Browser,
|
|
587
|
+
allowedOrigins: ["https://app.example.com"],
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
await internals.onBeforeCreate(createBy);
|
|
591
|
+
|
|
592
|
+
const secretKey: unknown = writtenValues(createBy)["secretKey"];
|
|
593
|
+
expect(secretKey).toBeInstanceOf(ObjectID);
|
|
594
|
+
expect((secretKey as ObjectID).toString().length).toBeGreaterThan(0);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
test("a supplied secret key is left alone", async () => {
|
|
598
|
+
const { internals }: Harness = buildService();
|
|
599
|
+
|
|
600
|
+
const supplied: ObjectID = ObjectID.generate();
|
|
601
|
+
const createBy: CreateBy<Model> = createByModel({ secretKey: supplied });
|
|
602
|
+
|
|
603
|
+
await internals.onBeforeCreate(createBy);
|
|
604
|
+
|
|
605
|
+
expect((writtenValues(createBy)["secretKey"] as ObjectID).toString()).toBe(
|
|
606
|
+
supplied.toString(),
|
|
607
|
+
);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
describe("TelemetryIngestionKeyService.onBeforeUpdate - the key type is immutable", () => {
|
|
612
|
+
const keyTypePatches: Array<[string, TelemetryIngestionKeyType]> = [
|
|
613
|
+
["Server -> Browser", TelemetryIngestionKeyType.Browser],
|
|
614
|
+
["Browser -> Server", TelemetryIngestionKeyType.Server],
|
|
615
|
+
];
|
|
616
|
+
|
|
617
|
+
test.each(keyTypePatches)(
|
|
618
|
+
"refuses a patch that changes the key type (%s) and tells the customer to create a new key",
|
|
619
|
+
async (
|
|
620
|
+
_label: string,
|
|
621
|
+
keyType: TelemetryIngestionKeyType,
|
|
622
|
+
): Promise<void> => {
|
|
623
|
+
const { internals }: Harness = buildService();
|
|
624
|
+
|
|
625
|
+
await expect(
|
|
626
|
+
internals.onBeforeUpdate(updateByBag({ keyType: keyType })),
|
|
627
|
+
).rejects.toThrow(BadDataException);
|
|
628
|
+
await expect(
|
|
629
|
+
internals.onBeforeUpdate(updateByBag({ keyType: keyType })),
|
|
630
|
+
).rejects.toThrow("Create a new ingestion key instead");
|
|
631
|
+
},
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
test("refuses the patch even when the key type is bundled with an otherwise harmless edit", async () => {
|
|
635
|
+
const { internals }: Harness = buildService();
|
|
636
|
+
|
|
637
|
+
await expect(
|
|
638
|
+
internals.onBeforeUpdate(
|
|
639
|
+
updateByBag({
|
|
640
|
+
name: "renamed",
|
|
641
|
+
keyType: TelemetryIngestionKeyType.Server,
|
|
642
|
+
}),
|
|
643
|
+
),
|
|
644
|
+
).rejects.toThrow("Create a new ingestion key instead");
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
/*
|
|
648
|
+
* A patch built from a model instance carries every column as an own
|
|
649
|
+
* property with the value `undefined`. That is an absence, not a change,
|
|
650
|
+
* and treating it as one would refuse every ordinary edit posted from a
|
|
651
|
+
* hydrated entity.
|
|
652
|
+
*/
|
|
653
|
+
test("an undefined key type on a model-shaped patch is an absence, not a change", async () => {
|
|
654
|
+
const { internals }: Harness = buildService();
|
|
655
|
+
|
|
656
|
+
const patch: Record<string, unknown> = Object.assign(
|
|
657
|
+
new Model(),
|
|
658
|
+
{},
|
|
659
|
+
) as unknown as Record<string, unknown>;
|
|
660
|
+
patch["name"] = "renamed";
|
|
661
|
+
|
|
662
|
+
await expect(
|
|
663
|
+
internals.onBeforeUpdate(updateByBag(patch)),
|
|
664
|
+
).resolves.toBeDefined();
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
describe("TelemetryIngestionKeyService.onBeforeUpdate - the allowlist cannot be emptied on a Browser key", () => {
|
|
669
|
+
const emptyingPatches: Array<[string, unknown]> = [
|
|
670
|
+
["an empty array", []],
|
|
671
|
+
["null", null],
|
|
672
|
+
["a list of blanks", ["", " "]],
|
|
673
|
+
];
|
|
674
|
+
|
|
675
|
+
test.each(emptyingPatches)(
|
|
676
|
+
"refuses to clear the allowlist with %s when an affected row is a Browser key",
|
|
677
|
+
async (_label: string, allowedOrigins: unknown): Promise<void> => {
|
|
678
|
+
const { internals, findBy }: Harness = buildService();
|
|
679
|
+
|
|
680
|
+
findBy.mockResolvedValue([
|
|
681
|
+
affectedRow(KEY_ID, TelemetryIngestionKeyType.Browser),
|
|
682
|
+
] as never);
|
|
683
|
+
|
|
684
|
+
await expect(
|
|
685
|
+
internals.onBeforeUpdate(
|
|
686
|
+
updateByBag({ allowedOrigins: allowedOrigins }),
|
|
687
|
+
),
|
|
688
|
+
).rejects.toThrow(BadDataException);
|
|
689
|
+
await expect(
|
|
690
|
+
internals.onBeforeUpdate(
|
|
691
|
+
updateByBag({ allowedOrigins: allowedOrigins }),
|
|
692
|
+
),
|
|
693
|
+
).rejects.toThrow("must keep at least one allowed origin");
|
|
694
|
+
},
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
test("the refusal names the key that blocked it, so a bulk update can be fixed", async () => {
|
|
698
|
+
const { internals, findBy }: Harness = buildService();
|
|
699
|
+
|
|
700
|
+
findBy.mockResolvedValue([
|
|
701
|
+
affectedRow(SECOND_KEY_ID, TelemetryIngestionKeyType.Server),
|
|
702
|
+
affectedRow(KEY_ID, TelemetryIngestionKeyType.Browser),
|
|
703
|
+
] as never);
|
|
704
|
+
|
|
705
|
+
await expect(
|
|
706
|
+
internals.onBeforeUpdate(updateByBag({ allowedOrigins: [] })),
|
|
707
|
+
).rejects.toThrow(KEY_ID.toString());
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
test("allows the allowlist to be cleared when every affected row is a Server key", async () => {
|
|
711
|
+
const { internals, findBy }: Harness = buildService();
|
|
712
|
+
|
|
713
|
+
findBy.mockResolvedValue([
|
|
714
|
+
affectedRow(KEY_ID, TelemetryIngestionKeyType.Server),
|
|
715
|
+
affectedRow(SECOND_KEY_ID, TelemetryIngestionKeyType.Server),
|
|
716
|
+
] as never);
|
|
717
|
+
|
|
718
|
+
const updateBy: UpdateBy<Model> = updateByBag({ allowedOrigins: [] });
|
|
719
|
+
|
|
720
|
+
await expect(internals.onBeforeUpdate(updateBy)).resolves.toBeDefined();
|
|
721
|
+
expect(
|
|
722
|
+
(updateBy.data as unknown as Record<string, unknown>)["allowedOrigins"],
|
|
723
|
+
).toEqual([]);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
/*
|
|
727
|
+
* THE BACKWARDS-COMPATIBILITY CONTRACT, on the guard itself. Every key that
|
|
728
|
+
* existed before this shipped reads back with keyType NULL. Those are
|
|
729
|
+
* Server keys, and clearing an allowlist they never had must not be refused
|
|
730
|
+
* because the guard could not recognise them.
|
|
731
|
+
*/
|
|
732
|
+
test("a legacy row with a NULL key type is treated as a Server key by the guard", async () => {
|
|
733
|
+
const { internals, findBy }: Harness = buildService();
|
|
734
|
+
|
|
735
|
+
findBy.mockResolvedValue([
|
|
736
|
+
affectedRow(KEY_ID, null),
|
|
737
|
+
affectedRow(SECOND_KEY_ID, undefined),
|
|
738
|
+
] as never);
|
|
739
|
+
|
|
740
|
+
await expect(
|
|
741
|
+
internals.onBeforeUpdate(updateByBag({ allowedOrigins: [] })),
|
|
742
|
+
).resolves.toBeDefined();
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
test("accepts a valid non-empty allowlist on a Browser key", async () => {
|
|
746
|
+
const { internals, findBy }: Harness = buildService();
|
|
747
|
+
|
|
748
|
+
const updateBy: UpdateBy<Model> = updateByBag({
|
|
749
|
+
allowedOrigins: [" HTTPS://App.Example.com/ ", "https://*.example.com"],
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
await expect(internals.onBeforeUpdate(updateBy)).resolves.toBeDefined();
|
|
753
|
+
expect(
|
|
754
|
+
(updateBy.data as unknown as Record<string, unknown>)["allowedOrigins"],
|
|
755
|
+
).toEqual(["https://app.example.com", "https://*.example.com"]);
|
|
756
|
+
|
|
757
|
+
/*
|
|
758
|
+
* A non-empty list is legal on both key types, so it must not pay for the
|
|
759
|
+
* pre-read either.
|
|
760
|
+
*/
|
|
761
|
+
expect(findBy.mock.calls).toHaveLength(0);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
/*
|
|
765
|
+
* The pre-read is a policy check, so it runs as root - it has to see every
|
|
766
|
+
* row the update will touch, including ones the caller could not read. That
|
|
767
|
+
* makes tenant scoping load-bearing: without it a broad query would be
|
|
768
|
+
* checked against another project's Browser keys.
|
|
769
|
+
*/
|
|
770
|
+
test("the pre-read is scoped to the caller's tenant when the query does not name a project", async () => {
|
|
771
|
+
const { internals, findBy }: Harness = buildService();
|
|
772
|
+
|
|
773
|
+
await internals.onBeforeUpdate(
|
|
774
|
+
updateByBag({ allowedOrigins: [] }, { _id: KEY_ID.toString() }, {
|
|
775
|
+
tenantId: PROJECT_ID,
|
|
776
|
+
} as DatabaseCommonInteractionProps),
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
expect(findBy.mock.calls).toHaveLength(1);
|
|
780
|
+
const call: {
|
|
781
|
+
query: Record<string, unknown>;
|
|
782
|
+
props: { isRoot?: boolean };
|
|
783
|
+
} = findBy.mock.calls[0]![0] as {
|
|
784
|
+
query: Record<string, unknown>;
|
|
785
|
+
props: { isRoot?: boolean };
|
|
786
|
+
};
|
|
787
|
+
expect((call.query["projectId"] as ObjectID).toString()).toBe(
|
|
788
|
+
PROJECT_ID.toString(),
|
|
789
|
+
);
|
|
790
|
+
expect(call.props.isRoot).toBe(true);
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
test("a project named by the query itself is not overwritten by the caller's tenant", async () => {
|
|
794
|
+
const { internals, findBy }: Harness = buildService();
|
|
795
|
+
|
|
796
|
+
await internals.onBeforeUpdate(
|
|
797
|
+
updateByBag({ allowedOrigins: [] }, { projectId: OTHER_PROJECT_ID }, {
|
|
798
|
+
tenantId: PROJECT_ID,
|
|
799
|
+
} as DatabaseCommonInteractionProps),
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
const call: { query: Record<string, unknown> } = findBy.mock
|
|
803
|
+
.calls[0]![0] as { query: Record<string, unknown> };
|
|
804
|
+
expect((call.query["projectId"] as ObjectID).toString()).toBe(
|
|
805
|
+
OTHER_PROJECT_ID.toString(),
|
|
806
|
+
);
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
describe("TelemetryIngestionKeyService.onBeforeUpdate - the other constrained columns", () => {
|
|
811
|
+
test("refuses an invalid origin pattern and names it", async () => {
|
|
812
|
+
const { internals }: Harness = buildService();
|
|
813
|
+
|
|
814
|
+
await expect(
|
|
815
|
+
internals.onBeforeUpdate(
|
|
816
|
+
updateByBag({ allowedOrigins: ["https://exa_mple.com"] }),
|
|
817
|
+
),
|
|
818
|
+
).rejects.toThrow("https://exa_mple.com");
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
test("refuses a pinned service name longer than the column can hold", async () => {
|
|
822
|
+
const { internals }: Harness = buildService();
|
|
823
|
+
|
|
824
|
+
await expect(
|
|
825
|
+
internals.onBeforeUpdate(
|
|
826
|
+
updateByBag({
|
|
827
|
+
pinnedServiceName: "a".repeat(ColumnLength.ShortText + 1),
|
|
828
|
+
}),
|
|
829
|
+
),
|
|
830
|
+
).rejects.toThrow(
|
|
831
|
+
`cannot be longer than ${ColumnLength.ShortText} characters`,
|
|
832
|
+
);
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
/*
|
|
836
|
+
* The other half of "blank means unpin": on update it is the ONLY way a
|
|
837
|
+
* customer can remove a pin they no longer want.
|
|
838
|
+
*/
|
|
839
|
+
test("a blank pinned service name clears the pin instead of being refused", async () => {
|
|
840
|
+
const { internals }: Harness = buildService();
|
|
841
|
+
|
|
842
|
+
const updateBy: UpdateBy<Model> = updateByBag({ pinnedServiceName: " " });
|
|
843
|
+
|
|
844
|
+
await expect(internals.onBeforeUpdate(updateBy)).resolves.toBeDefined();
|
|
845
|
+
expect(
|
|
846
|
+
(updateBy.data as unknown as Record<string, unknown>)[
|
|
847
|
+
"pinnedServiceName"
|
|
848
|
+
],
|
|
849
|
+
).toBeNull();
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
test("refuses a requests-per-minute limit that is not a whole number above zero", async () => {
|
|
853
|
+
const { internals }: Harness = buildService();
|
|
854
|
+
|
|
855
|
+
await expect(
|
|
856
|
+
internals.onBeforeUpdate(updateByBag({ requestsPerMinuteLimit: 0 })),
|
|
857
|
+
).rejects.toThrow("whole number greater than 0");
|
|
858
|
+
await expect(
|
|
859
|
+
internals.onBeforeUpdate(updateByBag({ requestsPerMinuteLimit: -5 })),
|
|
860
|
+
).rejects.toThrow("whole number greater than 0");
|
|
861
|
+
await expect(
|
|
862
|
+
internals.onBeforeUpdate(updateByBag({ requestsPerMinuteLimit: 2.5 })),
|
|
863
|
+
).rejects.toThrow("whole number greater than 0");
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test("refuses an expiry that is already in the past", async () => {
|
|
867
|
+
const { internals }: Harness = buildService();
|
|
868
|
+
|
|
869
|
+
await expect(
|
|
870
|
+
internals.onBeforeUpdate(
|
|
871
|
+
updateByBag({ expiresAt: new Date(Date.now() - HOUR_MS) }),
|
|
872
|
+
),
|
|
873
|
+
).rejects.toThrow("must be in the future");
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
test("accepts a future expiry and a positive limit together", async () => {
|
|
877
|
+
const { internals }: Harness = buildService();
|
|
878
|
+
|
|
879
|
+
const expiresAt: Date = new Date(Date.now() + HOUR_MS);
|
|
880
|
+
const updateBy: UpdateBy<Model> = updateByBag({
|
|
881
|
+
expiresAt: expiresAt,
|
|
882
|
+
requestsPerMinuteLimit: 6000,
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
await expect(internals.onBeforeUpdate(updateBy)).resolves.toBeDefined();
|
|
886
|
+
|
|
887
|
+
const written: Record<string, unknown> = updateBy.data as unknown as Record<
|
|
888
|
+
string,
|
|
889
|
+
unknown
|
|
890
|
+
>;
|
|
891
|
+
expect((written["expiresAt"] as Date).getTime()).toBe(expiresAt.getTime());
|
|
892
|
+
expect(written["requestsPerMinuteLimit"]).toBe(6000);
|
|
893
|
+
});
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
describe("TelemetryIngestionKeyService.onBeforeUpdate - the common update path pays nothing extra", () => {
|
|
897
|
+
/*
|
|
898
|
+
* The pre-read exists for exactly one rule: "you may not empty a Browser
|
|
899
|
+
* key's allowlist". Every other edit - rename, toggle isEnabled, set an
|
|
900
|
+
* expiry, set a limit - cannot break that rule and must not issue a query
|
|
901
|
+
* to prove it. This path runs on every dashboard save; a stray SELECT here
|
|
902
|
+
* is a permanent tax paid for a case that is not in play.
|
|
903
|
+
*/
|
|
904
|
+
const cheapPatches: Array<[string, Record<string, unknown>]> = [
|
|
905
|
+
["a rename", { name: "renamed" }],
|
|
906
|
+
["the kill switch", { isEnabled: false }],
|
|
907
|
+
["an expiry", { expiresAt: new Date(Date.now() + HOUR_MS) }],
|
|
908
|
+
["a rate limit", { requestsPerMinuteLimit: 6000 }],
|
|
909
|
+
["a pinned service name", { pinnedServiceName: "checkout-web" }],
|
|
910
|
+
];
|
|
911
|
+
|
|
912
|
+
test.each(cheapPatches)(
|
|
913
|
+
"issues no pre-read query for a patch that only sets %s",
|
|
914
|
+
async (_label: string, payload: Record<string, unknown>): Promise<void> => {
|
|
915
|
+
const { internals, findBy, findOneBy }: Harness = buildService();
|
|
916
|
+
|
|
917
|
+
await expect(
|
|
918
|
+
internals.onBeforeUpdate(updateByBag(payload)),
|
|
919
|
+
).resolves.toBeDefined();
|
|
920
|
+
|
|
921
|
+
expect(findBy.mock.calls).toHaveLength(0);
|
|
922
|
+
expect(findOneBy.mock.calls).toHaveLength(0);
|
|
923
|
+
},
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
test("issues no pre-read query for a model-shaped patch whose allowedOrigins is merely undefined", async () => {
|
|
927
|
+
const { internals, findBy, findOneBy }: Harness = buildService();
|
|
928
|
+
|
|
929
|
+
const patch: Record<string, unknown> = Object.assign(
|
|
930
|
+
new Model(),
|
|
931
|
+
{},
|
|
932
|
+
) as unknown as Record<string, unknown>;
|
|
933
|
+
patch["isEnabled"] = false;
|
|
934
|
+
|
|
935
|
+
await expect(
|
|
936
|
+
internals.onBeforeUpdate(updateByBag(patch)),
|
|
937
|
+
).resolves.toBeDefined();
|
|
938
|
+
|
|
939
|
+
expect(findBy.mock.calls).toHaveLength(0);
|
|
940
|
+
expect(findOneBy.mock.calls).toHaveLength(0);
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
describe("TelemetryIngestionKeyService - the policy cache is invalidated on write", () => {
|
|
945
|
+
/*
|
|
946
|
+
* Pre-existing behaviour that must survive the new validation. The cached
|
|
947
|
+
* policy now carries the kill switch, the expiry, the allowlist and the
|
|
948
|
+
* rate limit, so almost any edit invalidates it - and a revoked or
|
|
949
|
+
* disabled key that keeps working for a minute longer than the operator
|
|
950
|
+
* believes is the failure this cache is allowed to have only because it is
|
|
951
|
+
* cleared here.
|
|
952
|
+
*/
|
|
953
|
+
function seedCache(internals: ServiceInternals): void {
|
|
954
|
+
internals.policyCache.set("seeded-entry", null, 60 * 1000);
|
|
955
|
+
expect(internals.policyCache.size()).toBe(1);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
test("an accepted update clears the cache", async () => {
|
|
959
|
+
const { internals }: Harness = buildService();
|
|
960
|
+
seedCache(internals);
|
|
961
|
+
|
|
962
|
+
await internals.onBeforeUpdate(updateByBag({ isEnabled: false }));
|
|
963
|
+
|
|
964
|
+
expect(internals.policyCache.size()).toBe(0);
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
test("a delete clears the cache", async () => {
|
|
968
|
+
const { internals }: Harness = buildService();
|
|
969
|
+
seedCache(internals);
|
|
970
|
+
|
|
971
|
+
await internals.onBeforeDelete({
|
|
972
|
+
query: { _id: KEY_ID.toString() },
|
|
973
|
+
props: { isRoot: true },
|
|
974
|
+
limit: LIMIT_PER_PROJECT,
|
|
975
|
+
skip: 0,
|
|
976
|
+
} as unknown as DeleteBy<Model>);
|
|
977
|
+
|
|
978
|
+
expect(internals.policyCache.size()).toBe(0);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
/*
|
|
982
|
+
* A rejected update must NOT churn the cache: throwing away every cached
|
|
983
|
+
* policy in the process sends the whole ingest fleet back to Postgres, and
|
|
984
|
+
* a caller retrying a bad patch could do that repeatedly.
|
|
985
|
+
*/
|
|
986
|
+
test("a rejected update leaves the cache alone", async () => {
|
|
987
|
+
const { internals }: Harness = buildService();
|
|
988
|
+
seedCache(internals);
|
|
989
|
+
|
|
990
|
+
await expect(
|
|
991
|
+
internals.onBeforeUpdate(
|
|
992
|
+
updateByBag({ keyType: TelemetryIngestionKeyType.Browser }),
|
|
993
|
+
),
|
|
994
|
+
).rejects.toThrow(BadDataException);
|
|
995
|
+
|
|
996
|
+
expect(internals.policyCache.size()).toBe(1);
|
|
997
|
+
});
|
|
998
|
+
});
|