@oneuptime/common 12.0.2 → 12.0.4
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/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +16 -2
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +458 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +38 -4
- package/Server/Infrastructure/GlobalCache.ts +152 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +296 -2
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/PasswordHash.ts +306 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/Decimal.ts +16 -4
- package/Types/HashedString.ts +139 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/TeamMembersByProject.ts +237 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +16 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +343 -10
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +34 -4
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +246 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/PasswordHash.js +237 -0
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- 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/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/HashedString.js +95 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import AlertStateService from "../../../Server/Services/AlertStateService";
|
|
2
|
+
import AlertStateTimelineService from "../../../Server/Services/AlertStateTimelineService";
|
|
3
|
+
import IncidentStateService from "../../../Server/Services/IncidentStateService";
|
|
4
|
+
import IncidentStateTimelineService from "../../../Server/Services/IncidentStateTimelineService";
|
|
5
|
+
import MonitorService from "../../../Server/Services/MonitorService";
|
|
6
|
+
import MonitorStatusService from "../../../Server/Services/MonitorStatusService";
|
|
7
|
+
import MonitorStatusTimelineService from "../../../Server/Services/MonitorStatusTimelineService";
|
|
8
|
+
import MonitorTemplateService from "../../../Server/Services/MonitorTemplateService";
|
|
9
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
10
|
+
import ScheduledMaintenanceStateService from "../../../Server/Services/ScheduledMaintenanceStateService";
|
|
11
|
+
import ScheduledMaintenanceStateTimelineService from "../../../Server/Services/ScheduledMaintenanceStateTimelineService";
|
|
12
|
+
import DatabaseService from "../../../Server/Services/DatabaseService";
|
|
13
|
+
import MonitorStepsProjectValidator from "../../../Server/Utils/Monitor/MonitorStepsProjectValidator";
|
|
14
|
+
import ProjectScopedReferenceValidator from "../../../Server/Utils/Database/ProjectScopedReferenceValidator";
|
|
15
|
+
import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
16
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
17
|
+
import MonitorTemplate from "../../../Models/DatabaseModels/MonitorTemplate";
|
|
18
|
+
import { JSONObject, ObjectType } from "../../../Types/JSON";
|
|
19
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
20
|
+
import {
|
|
21
|
+
afterEach,
|
|
22
|
+
beforeEach,
|
|
23
|
+
describe,
|
|
24
|
+
expect,
|
|
25
|
+
jest,
|
|
26
|
+
test,
|
|
27
|
+
} from "@jest/globals";
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Contract under test: issue #3039 — the write paths that used to accept a
|
|
31
|
+
* reference id matching no record at all.
|
|
32
|
+
*
|
|
33
|
+
* Two distinct halves are covered here.
|
|
34
|
+
*
|
|
35
|
+
* The four *StateTimeline services are the choke point every status/state
|
|
36
|
+
* change funnels through. A bogus id used to travel all the way to Postgres and
|
|
37
|
+
* come back as a raw 23503 ("violates foreign key constraint
|
|
38
|
+
* FK_574feb4161c5216c2c7ee0faaf8" — the error in the issue), which is an opaque
|
|
39
|
+
* 500 over the API and a job that retries forever in the probe worker. The
|
|
40
|
+
* three sibling services DID look their state up, but only inside the ordering
|
|
41
|
+
* branch, so the FIRST timeline row for a record always went through unchecked.
|
|
42
|
+
*
|
|
43
|
+
* MonitorService and MonitorTemplateService own the monitorSteps blob, which
|
|
44
|
+
* has no foreign key behind it at all. They must pass what is currently stored
|
|
45
|
+
* to the validator, so that an id the record ALREADY holds is exempt: a monitor
|
|
46
|
+
* saved broken before this guard existed has to stay editable.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
50
|
+
"0b5fbbd0-8d1c-4a72-9a2b-4b8bd1cba0e9",
|
|
51
|
+
);
|
|
52
|
+
const MONITOR_STATUS_ID: ObjectID = new ObjectID(
|
|
53
|
+
"8ff02a3f-3f18-4c1d-9db1-6cf27bb2a4a1",
|
|
54
|
+
);
|
|
55
|
+
const INCIDENT_STATE_ID: ObjectID = new ObjectID(
|
|
56
|
+
"2b0a94a4-2f8c-49f0-8a2e-0f1ff5df41c9",
|
|
57
|
+
);
|
|
58
|
+
const ALERT_STATE_ID: ObjectID = new ObjectID(
|
|
59
|
+
"6a56b0f9-6c8f-4f76-9b53-0a1a5b0ec1a2",
|
|
60
|
+
);
|
|
61
|
+
const SCHEDULED_MAINTENANCE_STATE_ID: ObjectID = new ObjectID(
|
|
62
|
+
"7c2f6b40-6a1b-4f18-9d0e-2c5ba2d3f0a7",
|
|
63
|
+
);
|
|
64
|
+
const MONITOR_ID: ObjectID = new ObjectID(
|
|
65
|
+
"1b7c5d3e-9a4f-4c2b-8e61-3d5a7f9b1c22",
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
type ValidatorCall = {
|
|
69
|
+
projectId: ObjectID | undefined;
|
|
70
|
+
subject?: string | undefined;
|
|
71
|
+
references: Array<{
|
|
72
|
+
modelName: string;
|
|
73
|
+
id: ObjectID | string | undefined | null;
|
|
74
|
+
service: DatabaseService<DatabaseBaseModel>;
|
|
75
|
+
mustExist?: boolean | undefined;
|
|
76
|
+
}>;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Identity -> readable name, so an assertion can name the service it expects.
|
|
80
|
+
const SERVICE_NAMES: Map<unknown, string> = new Map<unknown, string>([
|
|
81
|
+
[MonitorStatusService, "MonitorStatusService"],
|
|
82
|
+
[IncidentStateService, "IncidentStateService"],
|
|
83
|
+
[AlertStateService, "AlertStateService"],
|
|
84
|
+
[ScheduledMaintenanceStateService, "ScheduledMaintenanceStateService"],
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
type StepsValidatorCall = {
|
|
88
|
+
monitorSteps?: unknown;
|
|
89
|
+
projectId: ObjectID | undefined;
|
|
90
|
+
alreadyStoredMonitorSteps?: unknown;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
let validatorCalls: Array<ValidatorCall> = [];
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* Records what each service asks the validator about. `rejects` throws only
|
|
97
|
+
* when the call carries at least one id, so a guard that has been emptied out
|
|
98
|
+
* cannot masquerade as a working one by throwing anyway.
|
|
99
|
+
*/
|
|
100
|
+
function spyOnValidator(rejects?: boolean): void {
|
|
101
|
+
jest
|
|
102
|
+
.spyOn(ProjectScopedReferenceValidator, "validateReferencesBelongToProject")
|
|
103
|
+
.mockImplementation((async (data: ValidatorCall): Promise<void> => {
|
|
104
|
+
validatorCalls.push(data);
|
|
105
|
+
|
|
106
|
+
const hasId: boolean = data.references.some(
|
|
107
|
+
(reference: { id: ObjectID | string | undefined | null }) => {
|
|
108
|
+
return Boolean(reference.id);
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
if (rejects && hasId) {
|
|
113
|
+
throw new Error("references records that do not exist");
|
|
114
|
+
}
|
|
115
|
+
}) as never);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function callHook(
|
|
119
|
+
service: unknown,
|
|
120
|
+
hook: "onBeforeCreate" | "onBeforeUpdate",
|
|
121
|
+
payload: unknown,
|
|
122
|
+
): Promise<unknown> {
|
|
123
|
+
return (service as Record<string, (input: unknown) => Promise<unknown>>)[
|
|
124
|
+
hook
|
|
125
|
+
]!(payload);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function serviceNameOf(service: unknown): string {
|
|
129
|
+
return SERVICE_NAMES.get(service) || "unknown";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const stepsReferencing: (statusId: string) => JSONObject = (
|
|
133
|
+
statusId: string,
|
|
134
|
+
): JSONObject => {
|
|
135
|
+
return {
|
|
136
|
+
_type: ObjectType.MonitorSteps,
|
|
137
|
+
value: {
|
|
138
|
+
defaultMonitorStatusId: statusId,
|
|
139
|
+
monitorStepsInstanceArray: [],
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
describe("missing-reference guard on write", () => {
|
|
145
|
+
beforeEach(() => {
|
|
146
|
+
validatorCalls = [];
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
afterEach(() => {
|
|
150
|
+
jest.restoreAllMocks();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("MonitorStatusTimelineService", () => {
|
|
154
|
+
/*
|
|
155
|
+
* This is the exact insert the issue reports failing. It is checked before
|
|
156
|
+
* the dedupe lookups so a bad id costs one query rather than three.
|
|
157
|
+
*/
|
|
158
|
+
test("create checks the monitor status against MonitorStatusService", async () => {
|
|
159
|
+
spyOnValidator();
|
|
160
|
+
jest
|
|
161
|
+
.spyOn(MonitorStatusTimelineService, "findOneBy")
|
|
162
|
+
.mockResolvedValue(null as never);
|
|
163
|
+
|
|
164
|
+
await callHook(MonitorStatusTimelineService, "onBeforeCreate", {
|
|
165
|
+
data: {
|
|
166
|
+
monitorId: MONITOR_ID,
|
|
167
|
+
projectId: PROJECT_ID,
|
|
168
|
+
monitorStatusId: MONITOR_STATUS_ID,
|
|
169
|
+
},
|
|
170
|
+
props: {},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(validatorCalls).toHaveLength(1);
|
|
174
|
+
expect(validatorCalls[0]!.subject).toBe("monitor status timeline");
|
|
175
|
+
expect(validatorCalls[0]!.references).toHaveLength(1);
|
|
176
|
+
expect(validatorCalls[0]!.references[0]!.modelName).toBe(
|
|
177
|
+
"Monitor Status",
|
|
178
|
+
);
|
|
179
|
+
expect(validatorCalls[0]!.references[0]!.id).toBe(MONITOR_STATUS_ID);
|
|
180
|
+
expect(serviceNameOf(validatorCalls[0]!.references[0]!.service)).toBe(
|
|
181
|
+
"MonitorStatusService",
|
|
182
|
+
);
|
|
183
|
+
expect(validatorCalls[0]!.projectId).toBe(PROJECT_ID);
|
|
184
|
+
/*
|
|
185
|
+
* The whole point is that the record must EXIST. Passing
|
|
186
|
+
* `mustExist: false` here would opt this column out of issue #3039's fix
|
|
187
|
+
* while every other assertion in this test still passed, so pin that the
|
|
188
|
+
* service does not — the validator's default is strict.
|
|
189
|
+
*/
|
|
190
|
+
expect(validatorCalls[0]!.references[0]!.mustExist).not.toBe(false);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("create is rejected when the monitor status does not exist", async () => {
|
|
194
|
+
spyOnValidator(true);
|
|
195
|
+
jest
|
|
196
|
+
.spyOn(MonitorStatusTimelineService, "findOneBy")
|
|
197
|
+
.mockResolvedValue(null as never);
|
|
198
|
+
|
|
199
|
+
await expect(
|
|
200
|
+
callHook(MonitorStatusTimelineService, "onBeforeCreate", {
|
|
201
|
+
data: {
|
|
202
|
+
monitorId: MONITOR_ID,
|
|
203
|
+
projectId: PROJECT_ID,
|
|
204
|
+
monitorStatusId: MONITOR_STATUS_ID,
|
|
205
|
+
},
|
|
206
|
+
props: {},
|
|
207
|
+
}),
|
|
208
|
+
).rejects.toThrow("do not exist");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("create falls back to the write's tenant when the row carries no project", async () => {
|
|
212
|
+
spyOnValidator();
|
|
213
|
+
jest
|
|
214
|
+
.spyOn(MonitorStatusTimelineService, "findOneBy")
|
|
215
|
+
.mockResolvedValue(null as never);
|
|
216
|
+
|
|
217
|
+
await callHook(MonitorStatusTimelineService, "onBeforeCreate", {
|
|
218
|
+
data: {
|
|
219
|
+
monitorId: MONITOR_ID,
|
|
220
|
+
monitorStatusId: MONITOR_STATUS_ID,
|
|
221
|
+
},
|
|
222
|
+
props: { tenantId: PROJECT_ID },
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(validatorCalls[0]!.projectId).toBe(PROJECT_ID);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe("IncidentStateTimelineService", () => {
|
|
230
|
+
test("create checks the incident state against IncidentStateService", async () => {
|
|
231
|
+
spyOnValidator();
|
|
232
|
+
jest
|
|
233
|
+
.spyOn(IncidentStateTimelineService, "findOneBy")
|
|
234
|
+
.mockResolvedValue(null as never);
|
|
235
|
+
|
|
236
|
+
await callHook(IncidentStateTimelineService, "onBeforeCreate", {
|
|
237
|
+
data: {
|
|
238
|
+
incidentId: MONITOR_ID,
|
|
239
|
+
projectId: PROJECT_ID,
|
|
240
|
+
incidentStateId: INCIDENT_STATE_ID,
|
|
241
|
+
},
|
|
242
|
+
props: {},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(validatorCalls).toHaveLength(1);
|
|
246
|
+
expect(validatorCalls[0]!.subject).toBe("incident state timeline");
|
|
247
|
+
expect(validatorCalls[0]!.references[0]!.modelName).toBe(
|
|
248
|
+
"Incident State",
|
|
249
|
+
);
|
|
250
|
+
expect(validatorCalls[0]!.references[0]!.id).toBe(INCIDENT_STATE_ID);
|
|
251
|
+
expect(serviceNameOf(validatorCalls[0]!.references[0]!.service)).toBe(
|
|
252
|
+
"IncidentStateService",
|
|
253
|
+
);
|
|
254
|
+
expect(validatorCalls[0]!.references[0]!.mustExist).not.toBe(false);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("create is rejected when the incident state does not exist", async () => {
|
|
258
|
+
spyOnValidator(true);
|
|
259
|
+
jest
|
|
260
|
+
.spyOn(IncidentStateTimelineService, "findOneBy")
|
|
261
|
+
.mockResolvedValue(null as never);
|
|
262
|
+
|
|
263
|
+
await expect(
|
|
264
|
+
callHook(IncidentStateTimelineService, "onBeforeCreate", {
|
|
265
|
+
data: {
|
|
266
|
+
incidentId: MONITOR_ID,
|
|
267
|
+
projectId: PROJECT_ID,
|
|
268
|
+
incidentStateId: INCIDENT_STATE_ID,
|
|
269
|
+
},
|
|
270
|
+
props: {},
|
|
271
|
+
}),
|
|
272
|
+
).rejects.toThrow("do not exist");
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("AlertStateTimelineService", () => {
|
|
277
|
+
test("create checks the alert state against AlertStateService", async () => {
|
|
278
|
+
spyOnValidator();
|
|
279
|
+
jest
|
|
280
|
+
.spyOn(AlertStateTimelineService, "findOneBy")
|
|
281
|
+
.mockResolvedValue(null as never);
|
|
282
|
+
|
|
283
|
+
await callHook(AlertStateTimelineService, "onBeforeCreate", {
|
|
284
|
+
data: {
|
|
285
|
+
alertId: MONITOR_ID,
|
|
286
|
+
projectId: PROJECT_ID,
|
|
287
|
+
alertStateId: ALERT_STATE_ID,
|
|
288
|
+
},
|
|
289
|
+
props: {},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
expect(validatorCalls).toHaveLength(1);
|
|
293
|
+
expect(validatorCalls[0]!.subject).toBe("alert state timeline");
|
|
294
|
+
expect(validatorCalls[0]!.references[0]!.modelName).toBe("Alert State");
|
|
295
|
+
expect(validatorCalls[0]!.references[0]!.id).toBe(ALERT_STATE_ID);
|
|
296
|
+
expect(serviceNameOf(validatorCalls[0]!.references[0]!.service)).toBe(
|
|
297
|
+
"AlertStateService",
|
|
298
|
+
);
|
|
299
|
+
expect(validatorCalls[0]!.references[0]!.mustExist).not.toBe(false);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("create is rejected when the alert state does not exist", async () => {
|
|
303
|
+
spyOnValidator(true);
|
|
304
|
+
jest
|
|
305
|
+
.spyOn(AlertStateTimelineService, "findOneBy")
|
|
306
|
+
.mockResolvedValue(null as never);
|
|
307
|
+
|
|
308
|
+
await expect(
|
|
309
|
+
callHook(AlertStateTimelineService, "onBeforeCreate", {
|
|
310
|
+
data: {
|
|
311
|
+
alertId: MONITOR_ID,
|
|
312
|
+
projectId: PROJECT_ID,
|
|
313
|
+
alertStateId: ALERT_STATE_ID,
|
|
314
|
+
},
|
|
315
|
+
props: {},
|
|
316
|
+
}),
|
|
317
|
+
).rejects.toThrow("do not exist");
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe("ScheduledMaintenanceStateTimelineService", () => {
|
|
322
|
+
test("create checks the state against ScheduledMaintenanceStateService", async () => {
|
|
323
|
+
spyOnValidator();
|
|
324
|
+
jest
|
|
325
|
+
.spyOn(ScheduledMaintenanceStateTimelineService, "findOneBy")
|
|
326
|
+
.mockResolvedValue(null as never);
|
|
327
|
+
|
|
328
|
+
await callHook(
|
|
329
|
+
ScheduledMaintenanceStateTimelineService,
|
|
330
|
+
"onBeforeCreate",
|
|
331
|
+
{
|
|
332
|
+
data: {
|
|
333
|
+
scheduledMaintenanceId: MONITOR_ID,
|
|
334
|
+
projectId: PROJECT_ID,
|
|
335
|
+
scheduledMaintenanceStateId: SCHEDULED_MAINTENANCE_STATE_ID,
|
|
336
|
+
},
|
|
337
|
+
props: {},
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
expect(validatorCalls).toHaveLength(1);
|
|
342
|
+
expect(validatorCalls[0]!.subject).toBe(
|
|
343
|
+
"scheduled maintenance state timeline",
|
|
344
|
+
);
|
|
345
|
+
expect(validatorCalls[0]!.references[0]!.modelName).toBe(
|
|
346
|
+
"Scheduled Maintenance State",
|
|
347
|
+
);
|
|
348
|
+
expect(validatorCalls[0]!.references[0]!.id).toBe(
|
|
349
|
+
SCHEDULED_MAINTENANCE_STATE_ID,
|
|
350
|
+
);
|
|
351
|
+
expect(serviceNameOf(validatorCalls[0]!.references[0]!.service)).toBe(
|
|
352
|
+
"ScheduledMaintenanceStateService",
|
|
353
|
+
);
|
|
354
|
+
expect(validatorCalls[0]!.references[0]!.mustExist).not.toBe(false);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("create is rejected when the state does not exist", async () => {
|
|
358
|
+
spyOnValidator(true);
|
|
359
|
+
jest
|
|
360
|
+
.spyOn(ScheduledMaintenanceStateTimelineService, "findOneBy")
|
|
361
|
+
.mockResolvedValue(null as never);
|
|
362
|
+
|
|
363
|
+
await expect(
|
|
364
|
+
callHook(ScheduledMaintenanceStateTimelineService, "onBeforeCreate", {
|
|
365
|
+
data: {
|
|
366
|
+
scheduledMaintenanceId: MONITOR_ID,
|
|
367
|
+
projectId: PROJECT_ID,
|
|
368
|
+
scheduledMaintenanceStateId: SCHEDULED_MAINTENANCE_STATE_ID,
|
|
369
|
+
},
|
|
370
|
+
props: {},
|
|
371
|
+
}),
|
|
372
|
+
).rejects.toThrow("do not exist");
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
describe("MonitorService passes the stored monitorSteps to the validator", () => {
|
|
377
|
+
/*
|
|
378
|
+
* Without this the guard would refuse every edit of a monitor that already
|
|
379
|
+
* holds a dangling id — including renaming it — over a field the user never
|
|
380
|
+
* touched and cannot see is wrong.
|
|
381
|
+
*/
|
|
382
|
+
function spyOnStepsValidator(): Array<StepsValidatorCall> {
|
|
383
|
+
const calls: Array<StepsValidatorCall> = [];
|
|
384
|
+
|
|
385
|
+
jest
|
|
386
|
+
.spyOn(
|
|
387
|
+
MonitorStepsProjectValidator,
|
|
388
|
+
"validateMonitorStepsBelongToProject",
|
|
389
|
+
)
|
|
390
|
+
.mockImplementation((async (
|
|
391
|
+
data: StepsValidatorCall,
|
|
392
|
+
): Promise<void> => {
|
|
393
|
+
calls.push({
|
|
394
|
+
monitorSteps: data.monitorSteps,
|
|
395
|
+
projectId: data.projectId,
|
|
396
|
+
alreadyStoredMonitorSteps: data.alreadyStoredMonitorSteps,
|
|
397
|
+
});
|
|
398
|
+
}) as never);
|
|
399
|
+
|
|
400
|
+
return calls;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
test("update hands over what each matched monitor currently stores", async () => {
|
|
404
|
+
spyOnValidator();
|
|
405
|
+
const calls: Array<StepsValidatorCall> = spyOnStepsValidator();
|
|
406
|
+
|
|
407
|
+
const stored: JSONObject = stepsReferencing(MONITOR_STATUS_ID.toString());
|
|
408
|
+
const incoming: JSONObject = stepsReferencing(
|
|
409
|
+
MONITOR_STATUS_ID.toString(),
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const monitor: Monitor = new Monitor();
|
|
413
|
+
monitor.projectId = PROJECT_ID;
|
|
414
|
+
monitor.monitorSteps = stored as never;
|
|
415
|
+
|
|
416
|
+
jest
|
|
417
|
+
.spyOn(MonitorService, "findBy")
|
|
418
|
+
.mockResolvedValue([monitor] as never);
|
|
419
|
+
|
|
420
|
+
await callHook(MonitorService, "onBeforeUpdate", {
|
|
421
|
+
data: { monitorSteps: incoming },
|
|
422
|
+
query: {},
|
|
423
|
+
props: { tenantId: PROJECT_ID },
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
expect(calls).toHaveLength(1);
|
|
427
|
+
expect(calls[0]!.projectId).toBe(PROJECT_ID);
|
|
428
|
+
/*
|
|
429
|
+
* Identity, not equality, on both blobs: `stored` and `incoming` are
|
|
430
|
+
* structurally identical, so a swap of the two arguments — validating the
|
|
431
|
+
* incoming payload as if it were what was already saved, which would
|
|
432
|
+
* exempt every id from the check — passes a deep-equality assertion.
|
|
433
|
+
*/
|
|
434
|
+
expect(calls[0]!.monitorSteps).toBe(incoming);
|
|
435
|
+
expect(calls[0]!.alreadyStoredMonitorSteps).toBe(stored);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("update validates each matched monitor against its own project", async () => {
|
|
439
|
+
/*
|
|
440
|
+
* A root/API update does not always carry a tenantId, so the project has
|
|
441
|
+
* to come from the monitor the query actually matched.
|
|
442
|
+
*/
|
|
443
|
+
spyOnValidator();
|
|
444
|
+
const calls: Array<StepsValidatorCall> = spyOnStepsValidator();
|
|
445
|
+
|
|
446
|
+
const first: Monitor = new Monitor();
|
|
447
|
+
first.projectId = PROJECT_ID;
|
|
448
|
+
const second: Monitor = new Monitor();
|
|
449
|
+
second.projectId = MONITOR_ID;
|
|
450
|
+
|
|
451
|
+
jest
|
|
452
|
+
.spyOn(MonitorService, "findBy")
|
|
453
|
+
.mockResolvedValue([first, second] as never);
|
|
454
|
+
|
|
455
|
+
await callHook(MonitorService, "onBeforeUpdate", {
|
|
456
|
+
data: { monitorSteps: stepsReferencing(MONITOR_STATUS_ID.toString()) },
|
|
457
|
+
query: {},
|
|
458
|
+
props: {},
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
expect(
|
|
462
|
+
calls.map((call: { projectId: ObjectID | undefined }) => {
|
|
463
|
+
return call.projectId;
|
|
464
|
+
}),
|
|
465
|
+
).toEqual([PROJECT_ID, MONITOR_ID]);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("create grandfathers nothing, because a create introduces every id it carries", async () => {
|
|
469
|
+
spyOnValidator();
|
|
470
|
+
const calls: Array<StepsValidatorCall> = spyOnStepsValidator();
|
|
471
|
+
|
|
472
|
+
/*
|
|
473
|
+
* onBeforeCreate resolves the project's operational status for the new
|
|
474
|
+
* monitor after validating, and that lookup would otherwise hit the
|
|
475
|
+
* database. The plan lookup runs before it and only when billing is
|
|
476
|
+
* enabled, which depends on the environment this suite is run in.
|
|
477
|
+
*/
|
|
478
|
+
jest.spyOn(ProjectService, "getCurrentPlan").mockResolvedValue({
|
|
479
|
+
plan: null,
|
|
480
|
+
isSubscriptionUnpaid: false,
|
|
481
|
+
} as never);
|
|
482
|
+
jest
|
|
483
|
+
.spyOn(MonitorStatusService, "findOneBy")
|
|
484
|
+
.mockResolvedValue({ id: MONITOR_STATUS_ID } as never);
|
|
485
|
+
jest
|
|
486
|
+
.spyOn(MonitorService, "addDefaultProbesToMonitor")
|
|
487
|
+
.mockResolvedValue(undefined as never);
|
|
488
|
+
|
|
489
|
+
await callHook(MonitorService, "onBeforeCreate", {
|
|
490
|
+
data: {
|
|
491
|
+
monitorType: "Manual",
|
|
492
|
+
monitorSteps: stepsReferencing(MONITOR_STATUS_ID.toString()),
|
|
493
|
+
},
|
|
494
|
+
props: { tenantId: PROJECT_ID },
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
expect(calls).toHaveLength(1);
|
|
498
|
+
expect(calls[0]!.alreadyStoredMonitorSteps).toBeUndefined();
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
describe("MonitorTemplateService", () => {
|
|
503
|
+
/*
|
|
504
|
+
* A template's monitorSteps reaches real monitors through "create monitor
|
|
505
|
+
* from template" and through syncLinkedMonitors, which pushes the blob onto
|
|
506
|
+
* every linked monitor. Validating it here is what makes the error land on
|
|
507
|
+
* the template the bad id was typed into rather than on a monitor sync days
|
|
508
|
+
* later.
|
|
509
|
+
*/
|
|
510
|
+
test("create validates the template's monitorSteps", async () => {
|
|
511
|
+
const calls: Array<{ projectId: ObjectID | undefined }> = [];
|
|
512
|
+
|
|
513
|
+
jest
|
|
514
|
+
.spyOn(
|
|
515
|
+
MonitorStepsProjectValidator,
|
|
516
|
+
"validateMonitorStepsBelongToProject",
|
|
517
|
+
)
|
|
518
|
+
.mockImplementation((async (data: {
|
|
519
|
+
projectId: ObjectID | undefined;
|
|
520
|
+
}): Promise<void> => {
|
|
521
|
+
calls.push({ projectId: data.projectId });
|
|
522
|
+
}) as never);
|
|
523
|
+
|
|
524
|
+
await callHook(MonitorTemplateService, "onBeforeCreate", {
|
|
525
|
+
data: { monitorSteps: stepsReferencing(MONITOR_STATUS_ID.toString()) },
|
|
526
|
+
props: { tenantId: PROJECT_ID },
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
expect(calls).toEqual([{ projectId: PROJECT_ID }]);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test("create is rejected when the template names a status that does not exist", async () => {
|
|
533
|
+
jest
|
|
534
|
+
.spyOn(
|
|
535
|
+
MonitorStepsProjectValidator,
|
|
536
|
+
"validateMonitorStepsBelongToProject",
|
|
537
|
+
)
|
|
538
|
+
.mockImplementation((async (): Promise<void> => {
|
|
539
|
+
throw new Error("references records that do not exist");
|
|
540
|
+
}) as never);
|
|
541
|
+
|
|
542
|
+
await expect(
|
|
543
|
+
callHook(MonitorTemplateService, "onBeforeCreate", {
|
|
544
|
+
data: {
|
|
545
|
+
monitorSteps: stepsReferencing(MONITOR_STATUS_ID.toString()),
|
|
546
|
+
},
|
|
547
|
+
props: { tenantId: PROJECT_ID },
|
|
548
|
+
}),
|
|
549
|
+
).rejects.toThrow("do not exist");
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
test("update hands over what the template currently stores", async () => {
|
|
553
|
+
const calls: Array<{ alreadyStoredMonitorSteps: unknown }> = [];
|
|
554
|
+
|
|
555
|
+
jest
|
|
556
|
+
.spyOn(
|
|
557
|
+
MonitorStepsProjectValidator,
|
|
558
|
+
"validateMonitorStepsBelongToProject",
|
|
559
|
+
)
|
|
560
|
+
.mockImplementation((async (data: {
|
|
561
|
+
alreadyStoredMonitorSteps: unknown;
|
|
562
|
+
}): Promise<void> => {
|
|
563
|
+
calls.push({
|
|
564
|
+
alreadyStoredMonitorSteps: data.alreadyStoredMonitorSteps,
|
|
565
|
+
});
|
|
566
|
+
}) as never);
|
|
567
|
+
|
|
568
|
+
const stored: JSONObject = stepsReferencing(MONITOR_STATUS_ID.toString());
|
|
569
|
+
|
|
570
|
+
const template: MonitorTemplate = new MonitorTemplate();
|
|
571
|
+
template.projectId = PROJECT_ID;
|
|
572
|
+
template.monitorSteps = stored as never;
|
|
573
|
+
|
|
574
|
+
jest
|
|
575
|
+
.spyOn(MonitorTemplateService, "findBy")
|
|
576
|
+
.mockResolvedValue([template] as never);
|
|
577
|
+
|
|
578
|
+
await callHook(MonitorTemplateService, "onBeforeUpdate", {
|
|
579
|
+
data: { monitorSteps: stepsReferencing(MONITOR_STATUS_ID.toString()) },
|
|
580
|
+
query: {},
|
|
581
|
+
props: { tenantId: PROJECT_ID },
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
expect(calls).toHaveLength(1);
|
|
585
|
+
expect(calls[0]!.alreadyStoredMonitorSteps).toBe(stored);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
test("update looks nothing up when monitorSteps is not being written", async () => {
|
|
589
|
+
const calls: Array<unknown> = [];
|
|
590
|
+
|
|
591
|
+
jest
|
|
592
|
+
.spyOn(
|
|
593
|
+
MonitorStepsProjectValidator,
|
|
594
|
+
"validateMonitorStepsBelongToProject",
|
|
595
|
+
)
|
|
596
|
+
.mockImplementation((async (): Promise<void> => {
|
|
597
|
+
calls.push(true);
|
|
598
|
+
}) as never);
|
|
599
|
+
|
|
600
|
+
const findBy: unknown = jest
|
|
601
|
+
.spyOn(MonitorTemplateService, "findBy")
|
|
602
|
+
.mockResolvedValue([] as never);
|
|
603
|
+
|
|
604
|
+
await callHook(MonitorTemplateService, "onBeforeUpdate", {
|
|
605
|
+
data: { description: "renamed" },
|
|
606
|
+
query: {},
|
|
607
|
+
props: { tenantId: PROJECT_ID },
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
expect(calls).toHaveLength(0);
|
|
611
|
+
expect(findBy).not.toHaveBeenCalled();
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
});
|
|
@@ -71,6 +71,29 @@ jest.mock("../../../Server/Utils/Logger", () => {
|
|
|
71
71
|
};
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
/*
|
|
75
|
+
* Past the fast-path, the util screens the criteria's monitor status against
|
|
76
|
+
* the monitor's project before writing (issue #3039 — a status that no longer
|
|
77
|
+
* exists used to reach Postgres as a raw foreign key violation and fail the
|
|
78
|
+
* whole ingest run). That is a database round trip; answer "usable" here so
|
|
79
|
+
* these cases stay about the fast-path. The skip behaviour itself is pinned by
|
|
80
|
+
* Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts, which
|
|
81
|
+
* also asserts this lookup is NOT reached on the fast-path.
|
|
82
|
+
*/
|
|
83
|
+
jest.mock(
|
|
84
|
+
"../../../Server/Utils/Database/ProjectScopedReferenceValidator",
|
|
85
|
+
() => {
|
|
86
|
+
return {
|
|
87
|
+
__esModule: true,
|
|
88
|
+
default: {
|
|
89
|
+
isUsableInProject: () => {
|
|
90
|
+
return Promise.resolve(true);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
|
|
74
97
|
import MonitorStatusTimelineUtil from "../../../Server/Utils/Monitor/MonitorStatusTimeline";
|
|
75
98
|
import {
|
|
76
99
|
MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE,
|