@oneuptime/common 14.0.3 → 14.0.6
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/DatabaseModels/Alert.ts +55 -0
- package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
- package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
- package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
- package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
- package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
- package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
- package/Models/DatabaseModels/Incident.ts +55 -0
- package/Models/DatabaseModels/Index.ts +16 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
- package/Models/DatabaseModels/TelemetryException.ts +2 -0
- package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/MultipartFormData.ts +1 -1
- package/Server/Middleware/TelemetryIngest.ts +15 -8
- package/Server/Services/AlertReminderRuleService.ts +100 -24
- package/Server/Services/DatabaseServerEndpointService.ts +895 -0
- package/Server/Services/DatabaseServerFeedService.ts +128 -0
- package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
- package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
- package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
- package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
- package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
- package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
- package/Server/Services/DatabaseServerService.ts +4546 -0
- package/Server/Services/DatabaseService.ts +14 -8
- package/Server/Services/DockerResourceService.ts +43 -2
- package/Server/Services/IncidentReminderRuleService.ts +103 -23
- package/Server/Services/Index.ts +16 -0
- package/Server/Services/InventoryItemService.ts +113 -5
- package/Server/Services/IoTDeviceService.ts +145 -37
- package/Server/Services/MetricService.ts +1 -1
- package/Server/Services/OpenTelemetryIngestService.ts +19 -0
- package/Server/Services/PodmanResourceService.ts +43 -2
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
- package/Server/Services/ServiceService.ts +1 -1
- package/Server/Services/TelemetryUsageBillingService.ts +29 -2
- package/Server/Services/UserProjectSsoConsentService.ts +74 -0
- package/Server/Services/UserService.ts +14 -1
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
- package/Server/Types/Database/QueryHelper.ts +84 -2
- package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +81 -2
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
- package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +192 -9
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2264 -755
- package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
- package/Server/Utils/Monitor/MonitorResource.ts +47 -11
- package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
- package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/StartServer.ts +26 -6
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
- package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
- package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
- package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
- package/Server/Utils/Telemetry/Telemetry.ts +16 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
- package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
- package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
- package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
- package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
- package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
- package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
- package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
- package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
- package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
- package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
- package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
- package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
- package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
- package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
- package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
- package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
- package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
- package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
- package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
- package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
- package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
- package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
- package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
- package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
- package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
- package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
- package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
- package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
- package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
- package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
- package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
- package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
- package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
- package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
- package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
- package/Tests/ClassStrings.ts +178 -0
- package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
- package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/RenderedMarkup.ts +518 -0
- package/Tests/ResponsiveVisibility.test.ts +490 -4
- package/Tests/ResponsiveVisibility.ts +505 -52
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
- package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
- package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
- package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
- package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
- package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
- package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
- package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
- package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
- package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
- package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
- package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
- package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
- package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
- package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
- package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
- package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
- package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
- package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
- package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
- package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
- package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
- package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
- package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
- package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
- package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
- package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
- package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
- package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
- package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
- package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
- package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
- package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1815 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
- package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
- package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
- package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
- package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
- package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
- package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
- package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
- package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
- package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
- package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
- package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
- package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
- package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
- package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
- package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
- package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
- package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +292 -0
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +272 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
- package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
- package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +202 -0
- package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
- package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
- package/Tests/UI/Components/Card.test.tsx +93 -2
- package/Tests/UI/Components/FeedOptions.test.ts +6 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
- package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
- package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
- package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
- package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
- package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
- package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
- package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
- package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
- package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
- package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +70 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +21 -0
- package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
- package/Tests/UI/Utils/Navigation.test.ts +102 -0
- package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
- package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
- package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
- package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
- package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
- package/Tests/Utils/ValueFormatter.test.ts +50 -0
- package/Tests/tsconfig.json +16 -0
- package/Types/AI/AIResourceContext.ts +7 -0
- package/Types/BaseDatabase/AggregationType.ts +7 -0
- package/Types/Dashboard/DashboardTemplates.ts +22 -8
- package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
- package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
- package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
- package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
- package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
- package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
- package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
- package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Monitor/CephAlertTemplates.ts +18 -0
- package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
- package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
- package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
- package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
- package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
- package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
- package/Types/Permission.ts +267 -0
- package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
- package/Types/Rules/RuleRun.ts +12 -0
- package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
- package/Types/Rum/WebVitals.ts +13 -0
- package/Types/Telemetry/EntityType.ts +23 -0
- package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
- package/Types/Telemetry/ServiceType.ts +8 -0
- package/UI/Components/Banner/Banner.tsx +1 -1
- package/UI/Components/Card/Card.tsx +1 -1
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
- package/UI/Components/Charts/Line/LineChart.tsx +7 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
- package/UI/Components/Date/StartAndEndDate.tsx +5 -5
- package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
- package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
- package/UI/Components/Forms/BasicForm.tsx +1 -1
- package/UI/Components/Header/Header.tsx +1 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
- package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
- package/UI/Components/Input/Input.tsx +11 -0
- package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
- package/UI/Components/Modal/ModalFooter.tsx +1 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +4 -4
- package/UI/Components/Navbar/NavBar.tsx +9 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
- package/UI/Components/Page/Page.tsx +1 -1
- package/UI/Components/Pagination/Pagination.tsx +3 -3
- package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
- package/UI/Components/SideMenu/SideMenu.tsx +4 -1
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
- package/UI/Components/StatusPage/ResourceList.tsx +6 -6
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
- package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
- package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
- package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
- package/UI/Styles/Theme.css +8 -1
- package/UI/Utils/Navigation.ts +89 -42
- package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
- package/UI/Utils/TelemetryService.ts +4 -3
- package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/MetricUnitUtil.ts +103 -11
- package/Utils/Monitor/MetricValueFormatter.ts +66 -13
- package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
- package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
- package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
- package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
- package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
- package/Utils/Telemetry/EntityKey.ts +176 -0
- package/Utils/ValueFormatter.ts +16 -0
- package/build/dist/Models/DatabaseModels/Alert.js +54 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Incident.js +54 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +16 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
- package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
- package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
- package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
- package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
- package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +8 -4
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +30 -3
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +16 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +88 -5
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +111 -24
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +30 -3
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +1 -1
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
- package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
- package/build/dist/Server/Services/UserService.js +13 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +73 -2
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +58 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +135 -14
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.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/MonitorMaintenanceSuppression.js +196 -6
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
- package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +20 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
- package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
- package/build/dist/Types/AI/AIResourceContext.js +7 -0
- package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
- package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
- package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +22 -8
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
- package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
- package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
- package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
- package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
- package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
- package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
- package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
- package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
- package/build/dist/Types/Permission.js +234 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +12 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +23 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
- package/build/dist/Types/Telemetry/ServiceType.js +8 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/UI/Components/Banner/Banner.js +1 -1
- package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
- package/build/dist/UI/Components/Card/Card.js +1 -1
- package/build/dist/UI/Components/Card/Card.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
- package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
- package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
- package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +4 -4
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +1 -1
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
- package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +66 -33
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
- package/build/dist/UI/Utils/TelemetryService.js +3 -2
- package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +77 -13
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
- package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
- package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
- package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
- package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/ValueFormatter.js +15 -0
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/package.json +1 -1
- package/tsconfig.tests.json +7 -5
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
import Entities from "../../../../Models/DatabaseModels/Index";
|
|
2
|
+
import AlertReminderRule from "../../../../Models/DatabaseModels/AlertReminderRule";
|
|
3
|
+
import BaseModel from "../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
4
|
+
import RelationOnlyRuleBaseModel from "../../../../Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel";
|
|
5
|
+
import IncidentReminderRule from "../../../../Models/DatabaseModels/IncidentReminderRule";
|
|
6
|
+
import ScheduledMaintenanceReminderRule from "../../../../Models/DatabaseModels/ScheduledMaintenanceReminderRule";
|
|
7
|
+
import AlertReminderRuleService from "../../../../Server/Services/AlertReminderRuleService";
|
|
8
|
+
import DatabaseService from "../../../../Server/Services/DatabaseService";
|
|
9
|
+
import IncidentReminderRuleService from "../../../../Server/Services/IncidentReminderRuleService";
|
|
10
|
+
import ScheduledMaintenanceReminderRuleService from "../../../../Server/Services/ScheduledMaintenanceReminderRuleService";
|
|
11
|
+
import Query from "../../../../Server/Types/Database/Query";
|
|
12
|
+
import QueryHelper from "../../../../Server/Types/Database/QueryHelper";
|
|
13
|
+
import Select from "../../../../Server/Types/Database/Select";
|
|
14
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
15
|
+
import {
|
|
16
|
+
DataSource,
|
|
17
|
+
EntityMetadata,
|
|
18
|
+
FindOperator,
|
|
19
|
+
FindOptionsSelect,
|
|
20
|
+
FindOptionsWhere,
|
|
21
|
+
QueryFailedError,
|
|
22
|
+
QueryResult,
|
|
23
|
+
QueryRunner,
|
|
24
|
+
ReplicationMode,
|
|
25
|
+
Repository,
|
|
26
|
+
} from "typeorm";
|
|
27
|
+
import { ColumnMetadata } from "typeorm/metadata/ColumnMetadata";
|
|
28
|
+
import {
|
|
29
|
+
afterAll,
|
|
30
|
+
afterEach,
|
|
31
|
+
beforeAll,
|
|
32
|
+
beforeEach,
|
|
33
|
+
describe,
|
|
34
|
+
expect,
|
|
35
|
+
jest,
|
|
36
|
+
test,
|
|
37
|
+
} from "@jest/globals";
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* OneUptime/oneuptime#4030: "Reminder scheduling failed in
|
|
41
|
+
* IncidentService.onCreateSuccess: QueryFailedError: missing FROM-clause entry
|
|
42
|
+
* for table "incidentreminderrule"".
|
|
43
|
+
*
|
|
44
|
+
* Every read or write of a relation-only rule (IncidentReminderRule,
|
|
45
|
+
* AlertReminderRule, ScheduledMaintenanceReminderRule) that filters on a
|
|
46
|
+
* boolean `isEnabled` is rewritten by DatabaseService into
|
|
47
|
+
* QueryHelper.booleanForCriteriaBackedRule("criteria", value): a TypeORM `Raw`
|
|
48
|
+
* whose SQL has to read a SECOND column, `criteria`, next to the one TypeORM
|
|
49
|
+
* hands it. TypeORM calls the Raw with the UNESCAPED alias path
|
|
50
|
+
* (`IncidentReminderRule.isEnabled` from a SELECT builder, plain `isEnabled`
|
|
51
|
+
* from UPDATE/DELETE builders) and only afterwards rewrites tokens shaped
|
|
52
|
+
* exactly `Alias.<known property>` into `"Alias"."column"`.
|
|
53
|
+
*
|
|
54
|
+
* The helper used to build the second reference as `IncidentReminderRule."criteria"`.
|
|
55
|
+
* That token is not `Alias.<known property>`, so TypeORM left it alone, and
|
|
56
|
+
* Postgres folded the unquoted qualifier to `incidentreminderrule`, which names
|
|
57
|
+
* nothing in FROM (the alias is the quoted, mixed-case "IncidentReminderRule").
|
|
58
|
+
* The error is raised while PLANNING the statement, so it did not depend on any
|
|
59
|
+
* rows existing: every findMatchingRule() of every reminder rule kind failed,
|
|
60
|
+
* whether or not a project had rules, and reminders were never scheduled.
|
|
61
|
+
*
|
|
62
|
+
* None of this needs a database to observe, so this file runs in the regular
|
|
63
|
+
* Common test workflow (.github/workflows/test.common.yaml), with no migrated
|
|
64
|
+
* Postgres. It checks three layers:
|
|
65
|
+
*
|
|
66
|
+
* 1. The fragment the Raw renders for each alias shape TypeORM produces.
|
|
67
|
+
* 2. The SQL real TypeORM generates for the three rule models, on every query
|
|
68
|
+
* builder path DatabaseService uses. The DataSource builds entity metadata
|
|
69
|
+
* without connecting, and its query runners are swapped for a stand-in that
|
|
70
|
+
* records each statement instead of sending it (see installRecordingRunner).
|
|
71
|
+
* 3. The public DatabaseService API (the `isEnabled` translation) and the
|
|
72
|
+
* reminder services' findMatchingRule, the exact call from the issue, with
|
|
73
|
+
* getRepository() returning that same metadata-only repository.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
type RuleModelType = { new (): RelationOnlyRuleBaseModel };
|
|
77
|
+
type AnyModelType = { new (): BaseModel };
|
|
78
|
+
type RawRow = Record<string, unknown>;
|
|
79
|
+
|
|
80
|
+
interface RawOperator {
|
|
81
|
+
type: string;
|
|
82
|
+
objectLiteralParameters: Record<string, unknown>;
|
|
83
|
+
/* TypeORM renders the SQL through this, given the column alias path. */
|
|
84
|
+
getSql: (aliasPath: string) => string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface CapturedQuery {
|
|
88
|
+
sql: string;
|
|
89
|
+
parameters: Array<unknown>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type RecordingQuery = (
|
|
93
|
+
sql: string,
|
|
94
|
+
parameters?: Array<unknown>,
|
|
95
|
+
useStructuredResult?: boolean,
|
|
96
|
+
) => Promise<unknown>;
|
|
97
|
+
|
|
98
|
+
type CreateQueryRunner = (mode: ReplicationMode) => QueryRunner;
|
|
99
|
+
|
|
100
|
+
const asRaw: (operator: unknown) => RawOperator = (
|
|
101
|
+
operator: unknown,
|
|
102
|
+
): RawOperator => {
|
|
103
|
+
return operator as RawOperator;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const soleParameterName: (operator: RawOperator) => string = (
|
|
107
|
+
operator: RawOperator,
|
|
108
|
+
): string => {
|
|
109
|
+
const names: Array<string> = Object.keys(operator.objectLiteralParameters);
|
|
110
|
+
expect(names).toHaveLength(1);
|
|
111
|
+
return names[0]!;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
* The fragment for a given criteria reference and isEnabled path, with the
|
|
116
|
+
* CASE semantics the rollout encoding depends on: a legacy row (criteria NULL)
|
|
117
|
+
* is enabled only when isEnabled is true, a criteria-backed row only when
|
|
118
|
+
* isEnabled IS NULL.
|
|
119
|
+
*/
|
|
120
|
+
const expectedFragment: (data: {
|
|
121
|
+
criteria: string;
|
|
122
|
+
isEnabled: string;
|
|
123
|
+
parameterName: string;
|
|
124
|
+
}) => string = (data: {
|
|
125
|
+
criteria: string;
|
|
126
|
+
isEnabled: string;
|
|
127
|
+
parameterName: string;
|
|
128
|
+
}): string => {
|
|
129
|
+
return `((CASE WHEN ${data.criteria} IS NULL THEN COALESCE(${data.isEnabled}, false) ELSE ${data.isEnabled} IS NULL END) = :${data.parameterName})`;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
* An unquoted identifier used as a qualifier: `word.` right before a column,
|
|
134
|
+
* not preceded by a quote. Postgres folds such a qualifier to lower case, and
|
|
135
|
+
* every alias TypeORM declares for these models is a quoted mixed-case name, so
|
|
136
|
+
* a folded one names nothing in FROM. This is the #4030 shape.
|
|
137
|
+
*/
|
|
138
|
+
const UNQUOTED_QUALIFIER: RegExp =
|
|
139
|
+
/(?<![\w"$])([A-Za-z_][\w$]*)\.(?=["A-Za-z_])/;
|
|
140
|
+
|
|
141
|
+
// A positional parameter such as `$2` at the start of a string.
|
|
142
|
+
const LEADING_PLACEHOLDER: RegExp = /^\$(\d+)/;
|
|
143
|
+
|
|
144
|
+
const escapeRegExp: (text: string) => string = (text: string): string => {
|
|
145
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// `Alias.` anywhere in the statement without a double quote in front of it.
|
|
149
|
+
const unquotedReferenceTo: (alias: string) => RegExp = (
|
|
150
|
+
alias: string,
|
|
151
|
+
): RegExp => {
|
|
152
|
+
return new RegExp(`(?<![\\w"])${escapeRegExp(alias)}\\.`);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/*
|
|
156
|
+
* Every model the DatabaseService translation applies to, discovered the same
|
|
157
|
+
* way DatabaseService decides (`this.model instanceof
|
|
158
|
+
* RelationOnlyRuleBaseModel`), so a future fourth model is exercised here
|
|
159
|
+
* without anyone remembering to add it.
|
|
160
|
+
*/
|
|
161
|
+
const RULE_MODELS: Array<RuleModelType> = (
|
|
162
|
+
Entities as Array<AnyModelType>
|
|
163
|
+
).filter((modelType: AnyModelType): boolean => {
|
|
164
|
+
return new modelType() instanceof RelationOnlyRuleBaseModel;
|
|
165
|
+
}) as Array<RuleModelType>;
|
|
166
|
+
|
|
167
|
+
const RULE_MODEL_CASES: Array<[string, RuleModelType]> = RULE_MODELS.map(
|
|
168
|
+
(modelType: RuleModelType): [string, RuleModelType] => {
|
|
169
|
+
return [modelType.name, modelType];
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
let dataSource: DataSource;
|
|
174
|
+
let originalCreateQueryRunner: CreateQueryRunner;
|
|
175
|
+
const capturedQueries: Array<CapturedQuery> = [];
|
|
176
|
+
|
|
177
|
+
/*
|
|
178
|
+
* Rows the stand-in runner hands back to an entity SELECT. Empty unless a test
|
|
179
|
+
* needs rows to reach a later statement (hardDeleteBy's DELETE, or a rule that
|
|
180
|
+
* findMatchingRule should return).
|
|
181
|
+
*/
|
|
182
|
+
let entityRows: Array<RawRow> = [];
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
* TypeORM pages a find that joins a to-many relation in two statements: a
|
|
186
|
+
* DISTINCT query for the page of ids, then the real SELECT restricted to those
|
|
187
|
+
* ids. Returning one id from the first makes TypeORM render the second too.
|
|
188
|
+
*/
|
|
189
|
+
const DISTINCT_ID_COLUMN: RegExp = /AS "(ids_[^"]+)"/;
|
|
190
|
+
|
|
191
|
+
const rowsFor: (sql: string) => Array<RawRow> = (
|
|
192
|
+
sql: string,
|
|
193
|
+
): Array<RawRow> => {
|
|
194
|
+
const distinctIdColumn: RegExpExecArray | null = DISTINCT_ID_COLUMN.exec(sql);
|
|
195
|
+
|
|
196
|
+
if (distinctIdColumn) {
|
|
197
|
+
const firstRowId: unknown = entityRows[0]
|
|
198
|
+
? Object.values(entityRows[0])[0]
|
|
199
|
+
: ObjectID.generate().toString();
|
|
200
|
+
|
|
201
|
+
return [{ [distinctIdColumn[1]!]: firstRowId }];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (sql.startsWith("SELECT") && !sql.startsWith("SELECT COUNT(")) {
|
|
205
|
+
return entityRows;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return [];
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* Every query runner the DataSource creates keeps its real construction (no
|
|
213
|
+
* connection is opened until a query is sent) but records the statement
|
|
214
|
+
* instead of sending it. It refuses a statement with an unquoted qualifier the
|
|
215
|
+
* way Postgres does, with the production error, so a regression shows up as
|
|
216
|
+
* the #4030 failure and not only as a mismatched string.
|
|
217
|
+
*
|
|
218
|
+
* Assigned directly rather than through jest.spyOn so restoreAllMocks() in the
|
|
219
|
+
* service tests cannot silently put the real runner back.
|
|
220
|
+
*/
|
|
221
|
+
const installRecordingRunner: () => void = (): void => {
|
|
222
|
+
originalCreateQueryRunner = dataSource.driver.createQueryRunner.bind(
|
|
223
|
+
dataSource.driver,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
dataSource.driver.createQueryRunner = (
|
|
227
|
+
mode: ReplicationMode,
|
|
228
|
+
): QueryRunner => {
|
|
229
|
+
const runner: QueryRunner = originalCreateQueryRunner(mode);
|
|
230
|
+
|
|
231
|
+
const recordingQuery: RecordingQuery = (
|
|
232
|
+
sql: string,
|
|
233
|
+
parameters?: Array<unknown>,
|
|
234
|
+
useStructuredResult?: boolean,
|
|
235
|
+
): Promise<unknown> => {
|
|
236
|
+
capturedQueries.push({ sql, parameters: parameters || [] });
|
|
237
|
+
|
|
238
|
+
const unquotedQualifier: RegExpExecArray | null =
|
|
239
|
+
UNQUOTED_QUALIFIER.exec(sql);
|
|
240
|
+
|
|
241
|
+
if (unquotedQualifier) {
|
|
242
|
+
return Promise.reject(
|
|
243
|
+
new QueryFailedError(
|
|
244
|
+
sql,
|
|
245
|
+
parameters,
|
|
246
|
+
new Error(
|
|
247
|
+
`missing FROM-clause entry for table "${unquotedQualifier[1]!.toLowerCase()}"`,
|
|
248
|
+
),
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const rows: Array<RawRow> = rowsFor(sql);
|
|
254
|
+
|
|
255
|
+
if (!useStructuredResult) {
|
|
256
|
+
return Promise.resolve(rows);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const result: QueryResult = new QueryResult();
|
|
260
|
+
result.records = rows;
|
|
261
|
+
result.raw = rows;
|
|
262
|
+
result.affected = rows.length;
|
|
263
|
+
|
|
264
|
+
return Promise.resolve(result);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
(runner as unknown as { query: RecordingQuery }).query = recordingQuery;
|
|
268
|
+
|
|
269
|
+
return runner;
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const takeCapturedQueries: () => Array<CapturedQuery> =
|
|
274
|
+
(): Array<CapturedQuery> => {
|
|
275
|
+
return capturedQueries.splice(0, capturedQueries.length);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
* The effective-enabled predicate exactly as Postgres must receive it. With a
|
|
280
|
+
* table alias (SELECT builders) both columns read `"Alias"."column"`; UPDATE
|
|
281
|
+
* and DELETE builders do not prefix, so both read a bare `"column"`. Beyond
|
|
282
|
+
* the predicate, the whole statement must be free of unquoted qualifiers, and
|
|
283
|
+
* the placeholder in it must be bound to the requested state.
|
|
284
|
+
*/
|
|
285
|
+
const expectEffectiveEnabledPredicate: (
|
|
286
|
+
query: CapturedQuery,
|
|
287
|
+
data: {
|
|
288
|
+
alias: string;
|
|
289
|
+
qualified: boolean;
|
|
290
|
+
value: boolean;
|
|
291
|
+
},
|
|
292
|
+
) => void = (
|
|
293
|
+
query: CapturedQuery,
|
|
294
|
+
data: {
|
|
295
|
+
alias: string;
|
|
296
|
+
qualified: boolean;
|
|
297
|
+
value: boolean;
|
|
298
|
+
},
|
|
299
|
+
): void => {
|
|
300
|
+
const column: (name: string) => string = (name: string): string => {
|
|
301
|
+
return data.qualified ? `"${data.alias}"."${name}"` : `"${name}"`;
|
|
302
|
+
};
|
|
303
|
+
const predicate: string = `(CASE WHEN ${column("criteria")} IS NULL THEN COALESCE(${column("isEnabled")}, false) ELSE ${column("isEnabled")} IS NULL END) = $`;
|
|
304
|
+
|
|
305
|
+
expect(query.sql).toContain(predicate);
|
|
306
|
+
expect(query.sql).not.toMatch(UNQUOTED_QUALIFIER);
|
|
307
|
+
expect(query.sql).not.toMatch(unquotedReferenceTo(data.alias));
|
|
308
|
+
|
|
309
|
+
const placeholder: RegExpExecArray | null = LEADING_PLACEHOLDER.exec(
|
|
310
|
+
query.sql.slice(query.sql.indexOf(predicate) + predicate.length - 1),
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
expect(placeholder).not.toBeNull();
|
|
314
|
+
expect(query.parameters[Number(placeholder![1]) - 1]).toBe(data.value);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const whereFor: (
|
|
318
|
+
value: boolean,
|
|
319
|
+
) => FindOptionsWhere<RelationOnlyRuleBaseModel> = (
|
|
320
|
+
value: boolean,
|
|
321
|
+
): FindOptionsWhere<RelationOnlyRuleBaseModel> => {
|
|
322
|
+
return {
|
|
323
|
+
projectId: ObjectID.generate().toString(),
|
|
324
|
+
isEnabled: QueryHelper.booleanForCriteriaBackedRule("criteria", value),
|
|
325
|
+
} as unknown as FindOptionsWhere<RelationOnlyRuleBaseModel>;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const manyToManyRelationNames: (metadata: EntityMetadata) => Array<string> = (
|
|
329
|
+
metadata: EntityMetadata,
|
|
330
|
+
): Array<string> => {
|
|
331
|
+
return metadata.manyToManyRelations.map(
|
|
332
|
+
(relation: EntityMetadata["manyToManyRelations"][number]): string => {
|
|
333
|
+
return relation.propertyName;
|
|
334
|
+
},
|
|
335
|
+
);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
beforeAll(async () => {
|
|
339
|
+
dataSource = new DataSource({
|
|
340
|
+
type: "postgres",
|
|
341
|
+
entities: Entities,
|
|
342
|
+
synchronize: false,
|
|
343
|
+
});
|
|
344
|
+
/*
|
|
345
|
+
* DataSource.initialize() would connect. buildMetadatas() is the part of it
|
|
346
|
+
* that compiles the entity metadata the query builders need, and is enough
|
|
347
|
+
* to render and "run" real SQL offline. Protected, hence the cast.
|
|
348
|
+
*/
|
|
349
|
+
await (
|
|
350
|
+
dataSource as unknown as { buildMetadatas: () => Promise<void> }
|
|
351
|
+
).buildMetadatas();
|
|
352
|
+
installRecordingRunner();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
afterAll(() => {
|
|
356
|
+
if (dataSource && originalCreateQueryRunner) {
|
|
357
|
+
dataSource.driver.createQueryRunner = originalCreateQueryRunner;
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
beforeEach(() => {
|
|
362
|
+
capturedQueries.length = 0;
|
|
363
|
+
entityRows = [];
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe("QueryHelper.booleanForCriteriaBackedRule: the rendered Raw fragment", () => {
|
|
367
|
+
test.each([
|
|
368
|
+
["IncidentReminderRule"],
|
|
369
|
+
["AlertReminderRule"],
|
|
370
|
+
["ScheduledMaintenanceReminderRule"],
|
|
371
|
+
])(
|
|
372
|
+
"quotes the table qualifier of criteria for a SELECT alias path on %s (the #4030 shape)",
|
|
373
|
+
(alias: string) => {
|
|
374
|
+
const operator: RawOperator = asRaw(
|
|
375
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", true),
|
|
376
|
+
);
|
|
377
|
+
const parameterName: string = soleParameterName(operator);
|
|
378
|
+
const sql: string = operator.getSql(`${alias}.isEnabled`);
|
|
379
|
+
|
|
380
|
+
/*
|
|
381
|
+
* The isEnabled path stays exactly as TypeORM passed it, because TypeORM
|
|
382
|
+
* quotes that token itself afterwards. Only the criteria reference,
|
|
383
|
+
* which TypeORM does not recognise, has to arrive already quoted.
|
|
384
|
+
*/
|
|
385
|
+
expect(sql).toBe(
|
|
386
|
+
expectedFragment({
|
|
387
|
+
criteria: `"${alias}"."criteria"`,
|
|
388
|
+
isEnabled: `${alias}.isEnabled`,
|
|
389
|
+
parameterName,
|
|
390
|
+
}),
|
|
391
|
+
);
|
|
392
|
+
// The pre-fix `IncidentReminderRule."criteria"`, spelled out.
|
|
393
|
+
expect(sql).not.toMatch(
|
|
394
|
+
new RegExp(`(?<!")${escapeRegExp(alias)}\\."criteria"`),
|
|
395
|
+
);
|
|
396
|
+
},
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
test("uses a bare quoted column when the path has no qualifier (UPDATE and DELETE builders)", () => {
|
|
400
|
+
const operator: RawOperator = asRaw(
|
|
401
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", false),
|
|
402
|
+
);
|
|
403
|
+
const parameterName: string = soleParameterName(operator);
|
|
404
|
+
|
|
405
|
+
expect(operator.getSql("isEnabled")).toBe(
|
|
406
|
+
expectedFragment({
|
|
407
|
+
criteria: '"criteria"',
|
|
408
|
+
isEnabled: "isEnabled",
|
|
409
|
+
parameterName,
|
|
410
|
+
}),
|
|
411
|
+
);
|
|
412
|
+
expect(operator.getSql("isEnabled")).not.toContain('."criteria"');
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test.each([
|
|
416
|
+
// A joined relation alias as SelectQueryBuilder names it.
|
|
417
|
+
["IncidentReminderRule__IncidentReminderRule_labels"],
|
|
418
|
+
["AlertReminderRule__AlertReminderRule_alertSeverities"],
|
|
419
|
+
// DatabaseService.aggregateBy's de-duplicating subquery alias.
|
|
420
|
+
["IncidentReminderRule_aggregate_scope"],
|
|
421
|
+
["ScheduledMaintenanceReminderRule_aggregate_scope"],
|
|
422
|
+
/*
|
|
423
|
+
* TypeORM's hashed alias for a name over Postgres' 63 characters. It
|
|
424
|
+
* starts with a digit, which would not even parse unquoted.
|
|
425
|
+
*/
|
|
426
|
+
["466ecbee0a1bc4f90a7bd5528e511bb1766a0db1"],
|
|
427
|
+
])(
|
|
428
|
+
"quotes a long or generated alias %s as one identifier",
|
|
429
|
+
(qualifier: string) => {
|
|
430
|
+
const operator: RawOperator = asRaw(
|
|
431
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", true),
|
|
432
|
+
);
|
|
433
|
+
const parameterName: string = soleParameterName(operator);
|
|
434
|
+
|
|
435
|
+
expect(operator.getSql(`${qualifier}.isEnabled`)).toBe(
|
|
436
|
+
expectedFragment({
|
|
437
|
+
criteria: `"${qualifier}"."criteria"`,
|
|
438
|
+
isEnabled: `${qualifier}.isEnabled`,
|
|
439
|
+
parameterName,
|
|
440
|
+
}),
|
|
441
|
+
);
|
|
442
|
+
},
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
test("does not quote a qualifier that already arrives quoted", () => {
|
|
446
|
+
const operator: RawOperator = asRaw(
|
|
447
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", true),
|
|
448
|
+
);
|
|
449
|
+
const parameterName: string = soleParameterName(operator);
|
|
450
|
+
const sql: string = operator.getSql('"IncidentReminderRule"."isEnabled"');
|
|
451
|
+
|
|
452
|
+
expect(sql).toBe(
|
|
453
|
+
expectedFragment({
|
|
454
|
+
criteria: '"IncidentReminderRule"."criteria"',
|
|
455
|
+
isEnabled: '"IncidentReminderRule"."isEnabled"',
|
|
456
|
+
parameterName,
|
|
457
|
+
}),
|
|
458
|
+
);
|
|
459
|
+
expect(sql).not.toContain('""IncidentReminderRule""');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test.each([
|
|
463
|
+
['Odd"Alias', '"Odd""Alias"'],
|
|
464
|
+
['a"b"c', '"a""b""c"'],
|
|
465
|
+
// Opens with a quote but is not a quoted identifier: escaped, not kept.
|
|
466
|
+
['"Half', '"""Half"'],
|
|
467
|
+
// A lone quote is not a quoted identifier either.
|
|
468
|
+
['"', '""""'],
|
|
469
|
+
])(
|
|
470
|
+
"escapes a double quote inside the qualifier %s by doubling it",
|
|
471
|
+
(qualifier: string, quotedQualifier: string) => {
|
|
472
|
+
const operator: RawOperator = asRaw(
|
|
473
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", true),
|
|
474
|
+
);
|
|
475
|
+
const parameterName: string = soleParameterName(operator);
|
|
476
|
+
|
|
477
|
+
expect(operator.getSql(`${qualifier}.isEnabled`)).toBe(
|
|
478
|
+
expectedFragment({
|
|
479
|
+
criteria: `${quotedQualifier}."criteria"`,
|
|
480
|
+
isEnabled: `${qualifier}.isEnabled`,
|
|
481
|
+
parameterName,
|
|
482
|
+
}),
|
|
483
|
+
);
|
|
484
|
+
},
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
test("qualifies a custom criteria column name the same way", () => {
|
|
488
|
+
const operator: RawOperator = asRaw(
|
|
489
|
+
QueryHelper.booleanForCriteriaBackedRule("ruleCriteria_2", true),
|
|
490
|
+
);
|
|
491
|
+
const parameterName: string = soleParameterName(operator);
|
|
492
|
+
|
|
493
|
+
expect(operator.getSql("AlertReminderRule.isEnabled")).toBe(
|
|
494
|
+
expectedFragment({
|
|
495
|
+
criteria: '"AlertReminderRule"."ruleCriteria_2"',
|
|
496
|
+
isEnabled: "AlertReminderRule.isEnabled",
|
|
497
|
+
parameterName,
|
|
498
|
+
}),
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test("renders correctly for each alias it is given, because one operator is reused across builders", () => {
|
|
503
|
+
/*
|
|
504
|
+
* hardDeleteBy passes the same query object to a SELECT (alias-prefixed)
|
|
505
|
+
* and then to repository.delete() (no prefix), so one Raw renders under
|
|
506
|
+
* both alias shapes.
|
|
507
|
+
*/
|
|
508
|
+
const operator: RawOperator = asRaw(
|
|
509
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", false),
|
|
510
|
+
);
|
|
511
|
+
const parameterName: string = soleParameterName(operator);
|
|
512
|
+
|
|
513
|
+
expect(operator.getSql("IncidentReminderRule.isEnabled")).toBe(
|
|
514
|
+
expectedFragment({
|
|
515
|
+
criteria: '"IncidentReminderRule"."criteria"',
|
|
516
|
+
isEnabled: "IncidentReminderRule.isEnabled",
|
|
517
|
+
parameterName,
|
|
518
|
+
}),
|
|
519
|
+
);
|
|
520
|
+
expect(operator.getSql("isEnabled")).toBe(
|
|
521
|
+
expectedFragment({
|
|
522
|
+
criteria: '"criteria"',
|
|
523
|
+
isEnabled: "isEnabled",
|
|
524
|
+
parameterName,
|
|
525
|
+
}),
|
|
526
|
+
);
|
|
527
|
+
expect(
|
|
528
|
+
operator.getSql("IncidentReminderRule_aggregate_scope.isEnabled"),
|
|
529
|
+
).toContain('"IncidentReminderRule_aggregate_scope"."criteria"');
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test.each([true, false])(
|
|
533
|
+
"binds the requested state %p as its only parameter, never inlined",
|
|
534
|
+
(value: boolean) => {
|
|
535
|
+
const operator: RawOperator = asRaw(
|
|
536
|
+
QueryHelper.booleanForCriteriaBackedRule("criteria", value),
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
expect(operator).toBeInstanceOf(FindOperator);
|
|
540
|
+
expect(operator.type).toBe("raw");
|
|
541
|
+
|
|
542
|
+
const parameterName: string = soleParameterName(operator);
|
|
543
|
+
|
|
544
|
+
expect(parameterName).toMatch(/^[A-Za-z]{10}$/);
|
|
545
|
+
expect(operator.objectLiteralParameters[parameterName]).toBe(value);
|
|
546
|
+
expect(operator.getSql("IncidentReminderRule.isEnabled")).toMatch(
|
|
547
|
+
new RegExp(`\\) = :${parameterName}\\)$`),
|
|
548
|
+
);
|
|
549
|
+
},
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
test("mints a distinct parameter name per call, so two filters can share a statement", () => {
|
|
553
|
+
const names: Set<string> = new Set<string>();
|
|
554
|
+
|
|
555
|
+
for (let index: number = 0; index < 25; index++) {
|
|
556
|
+
names.add(
|
|
557
|
+
soleParameterName(
|
|
558
|
+
asRaw(
|
|
559
|
+
QueryHelper.booleanForCriteriaBackedRule(
|
|
560
|
+
"criteria",
|
|
561
|
+
index % 2 === 0,
|
|
562
|
+
),
|
|
563
|
+
),
|
|
564
|
+
),
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
expect(names.size).toBe(25);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
test.each([
|
|
572
|
+
['criteria" IS NULL OR TRUE --'],
|
|
573
|
+
['"criteria"'],
|
|
574
|
+
["1criteria"],
|
|
575
|
+
["crit eria"],
|
|
576
|
+
["schema.criteria"],
|
|
577
|
+
[""],
|
|
578
|
+
])(
|
|
579
|
+
"rejects the criteria column name %p because it is not a plain identifier",
|
|
580
|
+
(criteriaColumnName: string) => {
|
|
581
|
+
expect(() => {
|
|
582
|
+
QueryHelper.booleanForCriteriaBackedRule(criteriaColumnName, true);
|
|
583
|
+
}).toThrow("Criteria column name must be a plain identifier.");
|
|
584
|
+
},
|
|
585
|
+
);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
describe("SQL TypeORM generates for relation-only rules (metadata only, no connection)", () => {
|
|
589
|
+
test("the relation-only rule models are exactly the three reminder rules", () => {
|
|
590
|
+
/*
|
|
591
|
+
* If a fourth model starts extending RelationOnlyRuleBaseModel it is
|
|
592
|
+
* already covered by every per-model test below; this makes sure the
|
|
593
|
+
* change is noticed and this list is updated on purpose.
|
|
594
|
+
*/
|
|
595
|
+
expect(
|
|
596
|
+
RULE_MODELS.map((modelType: RuleModelType): string => {
|
|
597
|
+
return modelType.name;
|
|
598
|
+
}).sort(),
|
|
599
|
+
).toEqual([
|
|
600
|
+
"AlertReminderRule",
|
|
601
|
+
"IncidentReminderRule",
|
|
602
|
+
"ScheduledMaintenanceReminderRule",
|
|
603
|
+
]);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
describe.each(RULE_MODEL_CASES)(
|
|
607
|
+
"%s",
|
|
608
|
+
(_name: string, modelType: RuleModelType) => {
|
|
609
|
+
let repository: Repository<RelationOnlyRuleBaseModel>;
|
|
610
|
+
let metadata: EntityMetadata;
|
|
611
|
+
let alias: string;
|
|
612
|
+
|
|
613
|
+
beforeAll(() => {
|
|
614
|
+
repository = dataSource.getRepository(modelType);
|
|
615
|
+
metadata = repository.metadata;
|
|
616
|
+
// Repository.find() aliases the main table by the entity name.
|
|
617
|
+
alias = metadata.name;
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
test("stores criteria and isEnabled in physical columns of exactly those names", () => {
|
|
621
|
+
/*
|
|
622
|
+
* DatabaseService passes the PROPERTY name "criteria" and the Raw
|
|
623
|
+
* writes it into SQL as a COLUMN name, so the two must not diverge.
|
|
624
|
+
*/
|
|
625
|
+
const criteria: ColumnMetadata | undefined =
|
|
626
|
+
metadata.findColumnWithPropertyName("criteria");
|
|
627
|
+
const isEnabled: ColumnMetadata | undefined =
|
|
628
|
+
metadata.findColumnWithPropertyName("isEnabled");
|
|
629
|
+
|
|
630
|
+
expect(criteria?.databaseName).toBe("criteria");
|
|
631
|
+
expect(isEnabled?.databaseName).toBe("isEnabled");
|
|
632
|
+
// The rollout encoding stores "enabled, criteria-backed" as NULL.
|
|
633
|
+
expect(isEnabled?.isNullable).toBe(true);
|
|
634
|
+
expect(alias).toBe(modelType.name);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
test.each([true, false])(
|
|
638
|
+
"find: the WHERE qualifies criteria with the quoted main alias (isEnabled = %p)",
|
|
639
|
+
async (value: boolean) => {
|
|
640
|
+
await repository.find({
|
|
641
|
+
where: whereFor(value),
|
|
642
|
+
order: { _id: "ASC" },
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
646
|
+
|
|
647
|
+
expect(queries).toHaveLength(1);
|
|
648
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
649
|
+
alias,
|
|
650
|
+
qualified: true,
|
|
651
|
+
value,
|
|
652
|
+
});
|
|
653
|
+
},
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
test("findOne: LIMIT 1 does not change the predicate", async () => {
|
|
657
|
+
await repository.findOne({ where: whereFor(true) });
|
|
658
|
+
|
|
659
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
660
|
+
|
|
661
|
+
expect(queries).toHaveLength(1);
|
|
662
|
+
expect(queries[0]!.sql).toContain("LIMIT 1");
|
|
663
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
664
|
+
alias,
|
|
665
|
+
qualified: true,
|
|
666
|
+
value: true,
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test("count: the COUNT statement carries the same quoted predicate", async () => {
|
|
671
|
+
await repository.count({ where: whereFor(false), skip: 0, take: 10 });
|
|
672
|
+
|
|
673
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
674
|
+
|
|
675
|
+
expect(queries).toHaveLength(1);
|
|
676
|
+
expect(queries[0]!.sql).toMatch(/^SELECT COUNT\(/);
|
|
677
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
678
|
+
alias,
|
|
679
|
+
qualified: true,
|
|
680
|
+
value: false,
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
test("find with every many-to-many relation and take/skip: both paging statements are quoted", async () => {
|
|
685
|
+
/*
|
|
686
|
+
* findMatchingRule selects the rule's labels (and severities) with a
|
|
687
|
+
* limit, which sends TypeORM down its two-statement distinct paging
|
|
688
|
+
* path. The predicate appears in both statements.
|
|
689
|
+
*/
|
|
690
|
+
const relations: Array<string> = manyToManyRelationNames(metadata);
|
|
691
|
+
|
|
692
|
+
expect(relations).toContain("labels");
|
|
693
|
+
|
|
694
|
+
const select: Record<string, unknown> = { _id: true, name: true };
|
|
695
|
+
|
|
696
|
+
for (const relation of relations) {
|
|
697
|
+
select[relation] = { _id: true };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
await repository.find({
|
|
701
|
+
where: whereFor(true),
|
|
702
|
+
select: select as FindOptionsSelect<RelationOnlyRuleBaseModel>,
|
|
703
|
+
relations: relations,
|
|
704
|
+
order: { _id: "ASC" },
|
|
705
|
+
take: 500,
|
|
706
|
+
skip: 0,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
710
|
+
|
|
711
|
+
expect(queries).toHaveLength(2);
|
|
712
|
+
expect(queries[0]!.sql).toMatch(/^SELECT DISTINCT "distinctAlias"\./);
|
|
713
|
+
|
|
714
|
+
for (const query of queries) {
|
|
715
|
+
expect(query.sql).toContain("LEFT JOIN");
|
|
716
|
+
expectEffectiveEnabledPredicate(query, {
|
|
717
|
+
alias,
|
|
718
|
+
qualified: true,
|
|
719
|
+
value: true,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
test("a derived alias (aggregateBy's scope subquery) is quoted as a whole", () => {
|
|
725
|
+
const scopeAlias: string = `${alias}_aggregate_scope`;
|
|
726
|
+
const [sql, parameters]: [string, Array<unknown>] = repository
|
|
727
|
+
.createQueryBuilder(scopeAlias)
|
|
728
|
+
.setFindOptions({ where: whereFor(true) })
|
|
729
|
+
.getQueryAndParameters();
|
|
730
|
+
|
|
731
|
+
expectEffectiveEnabledPredicate(
|
|
732
|
+
{ sql, parameters },
|
|
733
|
+
{ alias: scopeAlias, qualified: true, value: true },
|
|
734
|
+
);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
test("update: the UpdateQueryBuilder reads the bare criteria column", async () => {
|
|
738
|
+
await repository.update(whereFor(false), {
|
|
739
|
+
name: "Renamed",
|
|
740
|
+
} as unknown as Parameters<
|
|
741
|
+
Repository<RelationOnlyRuleBaseModel>["update"]
|
|
742
|
+
>[1]);
|
|
743
|
+
|
|
744
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
745
|
+
|
|
746
|
+
expect(queries).toHaveLength(1);
|
|
747
|
+
expect(queries[0]!.sql).toMatch(/^UPDATE "/);
|
|
748
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
749
|
+
alias,
|
|
750
|
+
qualified: false,
|
|
751
|
+
value: false,
|
|
752
|
+
});
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
test("delete: the DeleteQueryBuilder reads the bare criteria column", async () => {
|
|
756
|
+
await repository.delete(whereFor(true));
|
|
757
|
+
|
|
758
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
759
|
+
|
|
760
|
+
expect(queries).toHaveLength(1);
|
|
761
|
+
expect(queries[0]!.sql).toMatch(/^DELETE FROM "/);
|
|
762
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
763
|
+
alias,
|
|
764
|
+
qualified: false,
|
|
765
|
+
value: true,
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
},
|
|
769
|
+
);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
describe("DatabaseService translates a boolean isEnabled on relation-only rules", () => {
|
|
773
|
+
describe.each(RULE_MODEL_CASES)(
|
|
774
|
+
"%s",
|
|
775
|
+
(_name: string, modelType: RuleModelType) => {
|
|
776
|
+
let service: DatabaseService<RelationOnlyRuleBaseModel>;
|
|
777
|
+
let alias: string;
|
|
778
|
+
|
|
779
|
+
beforeEach(() => {
|
|
780
|
+
service = new DatabaseService<RelationOnlyRuleBaseModel>(modelType);
|
|
781
|
+
jest
|
|
782
|
+
.spyOn(service, "getRepository")
|
|
783
|
+
.mockReturnValue(dataSource.getRepository(modelType));
|
|
784
|
+
alias = dataSource.getMetadata(modelType).name;
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
afterEach(() => {
|
|
788
|
+
jest.restoreAllMocks();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
const queryFor: (
|
|
792
|
+
data: Record<string, unknown>,
|
|
793
|
+
) => Query<RelationOnlyRuleBaseModel> = (
|
|
794
|
+
data: Record<string, unknown>,
|
|
795
|
+
): Query<RelationOnlyRuleBaseModel> => {
|
|
796
|
+
return data as Query<RelationOnlyRuleBaseModel>;
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
test("findBy with the findMatchingRule shape (relations, limit) sends only quoted SQL", async () => {
|
|
800
|
+
const select: Record<string, unknown> = {
|
|
801
|
+
_id: true,
|
|
802
|
+
name: true,
|
|
803
|
+
criteria: true,
|
|
804
|
+
labels: { _id: true },
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const rules: Array<RelationOnlyRuleBaseModel> = await service.findBy({
|
|
808
|
+
query: queryFor({ projectId: ObjectID.generate(), isEnabled: true }),
|
|
809
|
+
select: select as Select<RelationOnlyRuleBaseModel>,
|
|
810
|
+
limit: 500,
|
|
811
|
+
skip: 0,
|
|
812
|
+
props: { isRoot: true },
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
expect(rules).toEqual([]);
|
|
816
|
+
|
|
817
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
818
|
+
|
|
819
|
+
expect(queries).toHaveLength(2);
|
|
820
|
+
|
|
821
|
+
for (const query of queries) {
|
|
822
|
+
expectEffectiveEnabledPredicate(query, {
|
|
823
|
+
alias,
|
|
824
|
+
qualified: true,
|
|
825
|
+
value: true,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
test("findOneBy for disabled rules", async () => {
|
|
831
|
+
await service.findOneBy({
|
|
832
|
+
query: queryFor({ isEnabled: false }),
|
|
833
|
+
select: { _id: true },
|
|
834
|
+
props: { isRoot: true },
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
838
|
+
|
|
839
|
+
expect(queries).toHaveLength(1);
|
|
840
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
841
|
+
alias,
|
|
842
|
+
qualified: true,
|
|
843
|
+
value: false,
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
test("countBy", async () => {
|
|
848
|
+
const count: { toNumber: () => number } = await service.countBy({
|
|
849
|
+
query: queryFor({ projectId: ObjectID.generate(), isEnabled: true }),
|
|
850
|
+
props: { isRoot: true },
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
expect(count.toNumber()).toBe(0);
|
|
854
|
+
|
|
855
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
856
|
+
|
|
857
|
+
expect(queries).toHaveLength(1);
|
|
858
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
859
|
+
alias,
|
|
860
|
+
qualified: true,
|
|
861
|
+
value: true,
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
test("aggregateBy, flat and through the de-duplicating relation scope", async () => {
|
|
866
|
+
await service.aggregateBy({
|
|
867
|
+
query: queryFor({ isEnabled: true }),
|
|
868
|
+
select: [{ expression: "COUNT(*)", alias: "total" }],
|
|
869
|
+
props: { isRoot: true },
|
|
870
|
+
});
|
|
871
|
+
await service.aggregateBy({
|
|
872
|
+
query: queryFor({ isEnabled: false, labels: [ObjectID.generate()] }),
|
|
873
|
+
select: [{ expression: "COUNT(*)", alias: "total" }],
|
|
874
|
+
props: { isRoot: true },
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
878
|
+
|
|
879
|
+
expect(queries).toHaveLength(2);
|
|
880
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
881
|
+
alias,
|
|
882
|
+
qualified: true,
|
|
883
|
+
value: true,
|
|
884
|
+
});
|
|
885
|
+
expectEffectiveEnabledPredicate(queries[1]!, {
|
|
886
|
+
alias: `${alias}_aggregate_scope`,
|
|
887
|
+
qualified: true,
|
|
888
|
+
value: false,
|
|
889
|
+
});
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
test("updateBy locates rows with the quoted predicate", async () => {
|
|
893
|
+
await service.updateBy({
|
|
894
|
+
query: queryFor({ isEnabled: true }),
|
|
895
|
+
data: { name: "Renamed" } as unknown as Parameters<
|
|
896
|
+
DatabaseService<RelationOnlyRuleBaseModel>["updateBy"]
|
|
897
|
+
>[0]["data"],
|
|
898
|
+
limit: 10,
|
|
899
|
+
skip: 0,
|
|
900
|
+
props: { isRoot: true },
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
904
|
+
|
|
905
|
+
expect(queries).toHaveLength(1);
|
|
906
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
907
|
+
alias,
|
|
908
|
+
qualified: true,
|
|
909
|
+
value: true,
|
|
910
|
+
});
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
test("deleteBy locates rows with the quoted predicate", async () => {
|
|
914
|
+
await service.deleteBy({
|
|
915
|
+
query: queryFor({ isEnabled: false }),
|
|
916
|
+
limit: 10,
|
|
917
|
+
skip: 0,
|
|
918
|
+
props: { isRoot: true },
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
922
|
+
|
|
923
|
+
expect(queries).toHaveLength(1);
|
|
924
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
925
|
+
alias,
|
|
926
|
+
qualified: true,
|
|
927
|
+
value: false,
|
|
928
|
+
});
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
test("hardDeleteBy renders the same operator for its SELECT and its DELETE", async () => {
|
|
932
|
+
const ruleId: string = ObjectID.generate().toString();
|
|
933
|
+
entityRows = [{ [`${alias}__id`]: ruleId }];
|
|
934
|
+
|
|
935
|
+
await service.hardDeleteBy({
|
|
936
|
+
query: queryFor({ isEnabled: false }),
|
|
937
|
+
limit: 10,
|
|
938
|
+
skip: 0,
|
|
939
|
+
props: { isRoot: true },
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
943
|
+
|
|
944
|
+
expect(queries).toHaveLength(2);
|
|
945
|
+
expect(queries[0]!.sql).toMatch(/^SELECT /);
|
|
946
|
+
expectEffectiveEnabledPredicate(queries[0]!, {
|
|
947
|
+
alias,
|
|
948
|
+
qualified: true,
|
|
949
|
+
value: false,
|
|
950
|
+
});
|
|
951
|
+
expect(queries[1]!.sql).toMatch(/^DELETE FROM "/);
|
|
952
|
+
expect(queries[1]!.parameters).toContain(ruleId);
|
|
953
|
+
expectEffectiveEnabledPredicate(queries[1]!, {
|
|
954
|
+
alias,
|
|
955
|
+
qualified: false,
|
|
956
|
+
value: false,
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
},
|
|
960
|
+
);
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
describe("findMatchingRule on the reminder rule services (the call in the #4030 log)", () => {
|
|
964
|
+
interface ReminderRuleServiceCase {
|
|
965
|
+
modelType: RuleModelType;
|
|
966
|
+
service: { getRepository: () => unknown };
|
|
967
|
+
findMatchingRule: () => Promise<RelationOnlyRuleBaseModel | null>;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const serviceCases: Array<[string, ReminderRuleServiceCase]> = [
|
|
971
|
+
[
|
|
972
|
+
"IncidentReminderRule",
|
|
973
|
+
{
|
|
974
|
+
modelType: IncidentReminderRule,
|
|
975
|
+
service: IncidentReminderRuleService,
|
|
976
|
+
findMatchingRule: (): Promise<IncidentReminderRule | null> => {
|
|
977
|
+
return IncidentReminderRuleService.findMatchingRule({
|
|
978
|
+
projectId: ObjectID.generate(),
|
|
979
|
+
incidentSeverityId: ObjectID.generate(),
|
|
980
|
+
labelIds: [ObjectID.generate()],
|
|
981
|
+
});
|
|
982
|
+
},
|
|
983
|
+
},
|
|
984
|
+
],
|
|
985
|
+
[
|
|
986
|
+
"AlertReminderRule",
|
|
987
|
+
{
|
|
988
|
+
modelType: AlertReminderRule,
|
|
989
|
+
service: AlertReminderRuleService,
|
|
990
|
+
findMatchingRule: (): Promise<AlertReminderRule | null> => {
|
|
991
|
+
return AlertReminderRuleService.findMatchingRule({
|
|
992
|
+
projectId: ObjectID.generate(),
|
|
993
|
+
alertSeverityId: ObjectID.generate(),
|
|
994
|
+
labelIds: [ObjectID.generate()],
|
|
995
|
+
});
|
|
996
|
+
},
|
|
997
|
+
},
|
|
998
|
+
],
|
|
999
|
+
[
|
|
1000
|
+
"ScheduledMaintenanceReminderRule",
|
|
1001
|
+
{
|
|
1002
|
+
modelType: ScheduledMaintenanceReminderRule,
|
|
1003
|
+
service: ScheduledMaintenanceReminderRuleService,
|
|
1004
|
+
findMatchingRule:
|
|
1005
|
+
(): Promise<ScheduledMaintenanceReminderRule | null> => {
|
|
1006
|
+
return ScheduledMaintenanceReminderRuleService.findMatchingRule({
|
|
1007
|
+
projectId: ObjectID.generate(),
|
|
1008
|
+
labelIds: [ObjectID.generate()],
|
|
1009
|
+
});
|
|
1010
|
+
},
|
|
1011
|
+
},
|
|
1012
|
+
],
|
|
1013
|
+
];
|
|
1014
|
+
|
|
1015
|
+
test("covers every relation-only rule model", () => {
|
|
1016
|
+
expect(
|
|
1017
|
+
serviceCases
|
|
1018
|
+
.map((serviceCase: [string, ReminderRuleServiceCase]): string => {
|
|
1019
|
+
return serviceCase[0];
|
|
1020
|
+
})
|
|
1021
|
+
.sort(),
|
|
1022
|
+
).toEqual(
|
|
1023
|
+
RULE_MODELS.map((modelType: RuleModelType): string => {
|
|
1024
|
+
return modelType.name;
|
|
1025
|
+
}).sort(),
|
|
1026
|
+
);
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
describe.each(serviceCases)(
|
|
1030
|
+
"%s",
|
|
1031
|
+
(_name: string, serviceCase: ReminderRuleServiceCase) => {
|
|
1032
|
+
let alias: string;
|
|
1033
|
+
|
|
1034
|
+
beforeEach(() => {
|
|
1035
|
+
jest
|
|
1036
|
+
.spyOn(serviceCase.service, "getRepository")
|
|
1037
|
+
.mockReturnValue(dataSource.getRepository(serviceCase.modelType));
|
|
1038
|
+
alias = dataSource.getMetadata(serviceCase.modelType).name;
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
afterEach(() => {
|
|
1042
|
+
jest.restoreAllMocks();
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
test("resolves to null, instead of failing, when the project has no rules", async () => {
|
|
1046
|
+
await expect(serviceCase.findMatchingRule()).resolves.toBeNull();
|
|
1047
|
+
|
|
1048
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
1049
|
+
|
|
1050
|
+
expect(queries).toHaveLength(2);
|
|
1051
|
+
|
|
1052
|
+
for (const query of queries) {
|
|
1053
|
+
expectEffectiveEnabledPredicate(query, {
|
|
1054
|
+
alias,
|
|
1055
|
+
qualified: true,
|
|
1056
|
+
value: true,
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
test("returns a configured legacy rule that matches everything", async () => {
|
|
1062
|
+
const ruleId: string = ObjectID.generate().toString();
|
|
1063
|
+
entityRows = [
|
|
1064
|
+
{
|
|
1065
|
+
[`${alias}__id`]: ruleId,
|
|
1066
|
+
[`${alias}_name`]: "Remind every 30 minutes",
|
|
1067
|
+
[`${alias}_criteria`]: null,
|
|
1068
|
+
[`${alias}_isEnabled`]: true,
|
|
1069
|
+
},
|
|
1070
|
+
];
|
|
1071
|
+
|
|
1072
|
+
const rule: RelationOnlyRuleBaseModel | null =
|
|
1073
|
+
await serviceCase.findMatchingRule();
|
|
1074
|
+
|
|
1075
|
+
expect(rule).not.toBeNull();
|
|
1076
|
+
expect(rule!.id?.toString()).toBe(ruleId);
|
|
1077
|
+
expect(rule).toBeInstanceOf(serviceCase.modelType);
|
|
1078
|
+
|
|
1079
|
+
const queries: Array<CapturedQuery> = takeCapturedQueries();
|
|
1080
|
+
|
|
1081
|
+
expect(queries).toHaveLength(2);
|
|
1082
|
+
expect(queries[1]!.parameters).toContain(ruleId);
|
|
1083
|
+
|
|
1084
|
+
for (const query of queries) {
|
|
1085
|
+
expectEffectiveEnabledPredicate(query, {
|
|
1086
|
+
alias,
|
|
1087
|
+
qualified: true,
|
|
1088
|
+
value: true,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
},
|
|
1093
|
+
);
|
|
1094
|
+
});
|