@oneuptime/common 12.0.1 → 12.0.3
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/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/Host.ts +12 -4
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
- package/Models/DatabaseModels/KubernetesResource.ts +8 -7
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/PodmanResource.ts +6 -6
- package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
- package/Models/DatabaseModels/StatusPage.ts +95 -0
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +39 -0
- package/Server/API/TeamMemberAPI.ts +85 -0
- package/Server/EnvironmentConfig.ts +31 -1
- package/Server/Infrastructure/GlobalCache.ts +115 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Middleware/ProjectAuthorization.ts +66 -4
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/BillingService.ts +600 -51
- package/Server/Services/CephClusterService.ts +27 -45
- package/Server/Services/CephResourceService.ts +43 -4
- package/Server/Services/CloudResourceService.ts +24 -42
- package/Server/Services/DatabaseService.ts +198 -6
- package/Server/Services/DockerHostService.ts +20 -38
- package/Server/Services/DockerResourceService.ts +62 -8
- package/Server/Services/DockerSwarmClusterService.ts +28 -46
- package/Server/Services/DockerSwarmResourceService.ts +53 -4
- package/Server/Services/HostService.ts +47 -57
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTDeviceService.ts +1 -17
- package/Server/Services/IoTFleetService.ts +20 -38
- package/Server/Services/KubernetesClusterService.ts +18 -36
- package/Server/Services/KubernetesContainerService.ts +50 -9
- package/Server/Services/KubernetesResourceService.ts +53 -7
- package/Server/Services/LabelService.ts +153 -1
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +146 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/PodmanHostService.ts +20 -38
- package/Server/Services/PodmanResourceService.ts +62 -8
- package/Server/Services/ProjectService.ts +165 -35
- package/Server/Services/ProxmoxClusterService.ts +24 -42
- package/Server/Services/ProxmoxResourceService.ts +43 -4
- package/Server/Services/RumApplicationService.ts +20 -38
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +25 -43
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/StatusPageService.ts +95 -36
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
- package/Server/Services/StatusPageSubscriberService.ts +3 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Types/Database/JSONColumnQuery.ts +621 -0
- package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
- package/Server/Types/Database/QueryHelper.ts +17 -32
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
- 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/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/UsersTableGroupsByPerson.test.tsx +448 -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/BaseAPIApiKeyAuth.test.ts +704 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
- package/Tests/Server/Services/BillingService.test.ts +78 -3
- package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
- package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
- package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +658 -0
- package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
- package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
- 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/ProjectServiceChangePlan.test.ts +650 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +636 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -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/StatusPageSubscriberReport.test.ts +29 -13
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
- package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -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/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/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/Events/Recurring.test.ts +157 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
- package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
- package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
- package/Types/CustomField/CustomFieldDefinition.ts +24 -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/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Date.ts +112 -0
- package/Types/Decimal.ts +16 -4
- package/Types/Events/Recurring.ts +99 -1
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/Types/StatusPage/StatusPageReport.ts +11 -0
- package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
- package/UI/Utils/TeamMembersByUser.ts +302 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/StatusPage/ReportPeriod.ts +236 -0
- package/Utils/Telemetry/HostIpAddresses.ts +98 -0
- 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/Host.js +12 -4
- package/build/dist/Models/DatabaseModels/Host.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/KubernetesContainer.js +4 -4
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
- package/build/dist/Models/DatabaseModels/KubernetesResource.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/PodmanResource.js +6 -6
- package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
- package/build/dist/Models/DatabaseModels/StatusPage.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/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/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +39 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +57 -2
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +27 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +91 -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/1785900000000-AddSloMonitorLabelRule.js +45 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
- 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/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
- package/build/dist/Server/Middleware/ProjectAuthorization.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/BillingService.js +466 -36
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -39
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CephResourceService.js +28 -3
- package/build/dist/Server/Services/CephResourceService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -36
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +155 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -32
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +34 -4
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -40
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
- package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -64
- 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/IoTDeviceService.js +1 -8
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -32
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -30
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
- package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +127 -0
- 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 +128 -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/PodmanHostService.js +19 -32
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +34 -4
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +135 -21
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -36
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -32
- 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 -37
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.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/StatusPageService.js +69 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- 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/Types/Database/JSONColumnQuery.js +402 -0
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- 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/Database/TruncateColumnValue.js +32 -0
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
- 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/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/CustomField/CustomFieldDefinition.js +2 -0
- package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
- 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/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/Date.js +68 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +63 -0
- package/build/dist/Types/Events/Recurring.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/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
- 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/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
- package/build/dist/UI/Utils/TeamMembersByUser.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/StatusPage/ReportPeriod.js +131 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +12 -1
|
@@ -7,6 +7,7 @@ import Typeof from "../../../Types/Typeof";
|
|
|
7
7
|
import { FindOperator, Raw } from "typeorm";
|
|
8
8
|
import { FindWhereProperty } from "../../../Types/BaseDatabase/Query";
|
|
9
9
|
import CaptureSpan from "../../Utils/Telemetry/CaptureSpan";
|
|
10
|
+
import buildJSONColumnQuery, { JSONColumnQuery } from "./JSONColumnQuery";
|
|
10
11
|
|
|
11
12
|
export default class QueryHelper {
|
|
12
13
|
@CaptureSpan()
|
|
@@ -668,40 +669,25 @@ export default class QueryHelper {
|
|
|
668
669
|
) as FindWhereProperty<any>;
|
|
669
670
|
}
|
|
670
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Filter on the keys of a jsonb column — `customFields` above all, whose
|
|
674
|
+
* keys are the names a project gave its custom fields.
|
|
675
|
+
*
|
|
676
|
+
* The per-key predicates (equality, "is any of", contains, numeric
|
|
677
|
+
* comparison, is-empty, and their negations) live in JSONColumnQuery. Two
|
|
678
|
+
* things this used to get wrong and no longer does: the key was spliced
|
|
679
|
+
* straight into the SQL string, and every value was compared with `=`, so an
|
|
680
|
+
* operator such as `Includes` reached Postgres stringified.
|
|
681
|
+
*/
|
|
671
682
|
@CaptureSpan()
|
|
672
683
|
public static queryJson(value: JSONObject): FindWhereProperty<any> {
|
|
673
|
-
// seed random text
|
|
674
|
-
const values: JSONObject = {};
|
|
675
|
-
|
|
676
|
-
let queryText: string = "";
|
|
677
|
-
|
|
678
|
-
if (typeof value === Typeof.String) {
|
|
679
|
-
value = JSON.parse(value.toString());
|
|
680
|
-
}
|
|
681
|
-
|
|
682
684
|
if (value instanceof FindOperator) {
|
|
683
685
|
return value;
|
|
684
686
|
}
|
|
685
687
|
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
const temp: string = Text.generateRandomText(10);
|
|
690
|
-
|
|
691
|
-
values[temp] = value[key];
|
|
692
|
-
|
|
693
|
-
if (!queryText) {
|
|
694
|
-
queryText = `(COLUMN_NAME_ALIAS->>'${key}' = :${temp as string}`;
|
|
695
|
-
} else {
|
|
696
|
-
queryText += ` AND COLUMN_NAME_ALIAS->>'${key}' = :${temp as string}`;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (hasValue) {
|
|
701
|
-
queryText += ")";
|
|
702
|
-
} else {
|
|
703
|
-
queryText = "(COLUMN_NAME_ALIAS IS NULL OR COLUMN_NAME_ALIAS = '{}')";
|
|
704
|
-
}
|
|
688
|
+
const jsonQuery: JSONColumnQuery = buildJSONColumnQuery({
|
|
689
|
+
value: value as JSONObject,
|
|
690
|
+
});
|
|
705
691
|
|
|
706
692
|
return Raw((alias: string) => {
|
|
707
693
|
/*
|
|
@@ -709,16 +695,15 @@ export default class QueryHelper {
|
|
|
709
695
|
* we need to convert this to "tableName"."columnName"
|
|
710
696
|
*/
|
|
711
697
|
|
|
712
|
-
|
|
698
|
+
const columnReference: string = alias
|
|
713
699
|
.split(".")
|
|
714
700
|
.map((item: string) => {
|
|
715
701
|
return `"${item}"`;
|
|
716
702
|
})
|
|
717
703
|
.join(".");
|
|
718
704
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
}, values);
|
|
705
|
+
return jsonQuery.toSql(columnReference);
|
|
706
|
+
}, jsonQuery.parameters);
|
|
722
707
|
}
|
|
723
708
|
|
|
724
709
|
@CaptureSpan()
|
|
@@ -3,6 +3,7 @@ import Query from "../../Types/Database/Query";
|
|
|
3
3
|
import QueryHelper from "../../Types/Database/QueryHelper";
|
|
4
4
|
import Select from "../../Types/Database/Select";
|
|
5
5
|
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
|
|
6
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
6
7
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
7
8
|
import ObjectID from "../../../Types/ObjectID";
|
|
8
9
|
import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
@@ -20,12 +21,23 @@ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/
|
|
|
20
21
|
* FKs are ON DELETE NO ACTION, so Postgres raises 23503 and the API answers
|
|
21
22
|
* "This item cannot be deleted because Incident records still reference it."
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
24
|
+
* An id that matches NO row is rejected too (issue #3039). The REST API used to
|
|
25
|
+
* take any uuid for these columns; the write then either blew up deep inside
|
|
26
|
+
* Postgres as a raw 23503 (an opaque 500 rather than "you picked a bad id"), or
|
|
27
|
+
* — for ids that live in a JSON blob with no foreign key behind it, like
|
|
28
|
+
* monitorSteps — saved happily and only failed much later, inside the probe
|
|
29
|
+
* worker, as
|
|
30
|
+
* insert or update on table "MonitorStatusTimeline" violates foreign key
|
|
31
|
+
* constraint
|
|
32
|
+
* with nothing surfaced to the caller who typed the bad id.
|
|
33
|
+
*
|
|
34
|
+
* Rejecting a missing id is safe for the columns validated here because every
|
|
35
|
+
* one of them is a real foreign key and deletes in this codebase are hard
|
|
36
|
+
* deletes (DatabaseService._deleteBy), so a *stored* id can never dangle: the
|
|
37
|
+
* ON DELETE NO ACTION constraint blocks the delete while a row still points at
|
|
38
|
+
* it. A missing id therefore only ever comes from the payload being validated.
|
|
39
|
+
* Callers whose reference is NOT foreign-key backed (monitorSteps ids, say) can
|
|
40
|
+
* opt a single reference out with `mustExist: false`.
|
|
29
41
|
*/
|
|
30
42
|
|
|
31
43
|
export interface ProjectScopedReference {
|
|
@@ -34,6 +46,13 @@ export interface ProjectScopedReference {
|
|
|
34
46
|
id: ObjectID | string | undefined | null;
|
|
35
47
|
// The service that owns the referenced model, e.g. IncidentSeverityService.
|
|
36
48
|
service: DatabaseService<DatabaseBaseModel>;
|
|
49
|
+
/*
|
|
50
|
+
* Defaults to true: an id that matches no record is rejected. Set false for a
|
|
51
|
+
* reference that is allowed to dangle — one with no foreign key behind it,
|
|
52
|
+
* where refusing would block a user from saving their way out of a record
|
|
53
|
+
* that already points at something deleted.
|
|
54
|
+
*/
|
|
55
|
+
mustExist?: boolean | undefined;
|
|
37
56
|
}
|
|
38
57
|
|
|
39
58
|
interface ForeignReference {
|
|
@@ -41,6 +60,33 @@ interface ForeignReference {
|
|
|
41
60
|
name: string;
|
|
42
61
|
}
|
|
43
62
|
|
|
63
|
+
interface MissingReference {
|
|
64
|
+
modelName: string;
|
|
65
|
+
id: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// What the caller asked about, keyed by id inside one service's lookup.
|
|
69
|
+
interface RequestedReference {
|
|
70
|
+
modelName: string;
|
|
71
|
+
mustExist: boolean;
|
|
72
|
+
// The id exactly as the caller wrote it, so the message echoes their input.
|
|
73
|
+
id: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* Ids are matched case-insensitively.
|
|
78
|
+
*
|
|
79
|
+
* ObjectID keeps whatever case it was handed (its validation regex is
|
|
80
|
+
* case-insensitive) and Postgres compares `uuid` values by parsed value, so a
|
|
81
|
+
* payload carrying an UPPERCASE uuid selects the row fine — but reads the id
|
|
82
|
+
* back lower-cased, because that is how Postgres renders `uuid`. Comparing the
|
|
83
|
+
* two verbatim would find no match and report a record that plainly exists as
|
|
84
|
+
* missing.
|
|
85
|
+
*/
|
|
86
|
+
function normalizeId(id: string): string {
|
|
87
|
+
return id.trim().toLowerCase();
|
|
88
|
+
}
|
|
89
|
+
|
|
44
90
|
/*
|
|
45
91
|
* The same reference reaches a service hook in several shapes: the id column
|
|
46
92
|
* (`incidentSeverityId`), a relation object (`incidentSeverity: { _id }`), an
|
|
@@ -92,11 +138,11 @@ export default class ProjectScopedReferenceValidator {
|
|
|
92
138
|
*/
|
|
93
139
|
const idsByService: Map<
|
|
94
140
|
DatabaseService<DatabaseBaseModel>,
|
|
95
|
-
Map<string,
|
|
141
|
+
Map<string, RequestedReference>
|
|
96
142
|
> = new Map();
|
|
97
143
|
|
|
98
144
|
for (const reference of data.references) {
|
|
99
|
-
const id: string = reference.id?.toString() || "";
|
|
145
|
+
const id: string = reference.id?.toString().trim() || "";
|
|
100
146
|
|
|
101
147
|
if (!id) {
|
|
102
148
|
continue;
|
|
@@ -106,7 +152,25 @@ export default class ProjectScopedReferenceValidator {
|
|
|
106
152
|
idsByService.set(reference.service, new Map());
|
|
107
153
|
}
|
|
108
154
|
|
|
109
|
-
idsByService.get(
|
|
155
|
+
const requested: Map<string, RequestedReference> = idsByService.get(
|
|
156
|
+
reference.service,
|
|
157
|
+
)!;
|
|
158
|
+
|
|
159
|
+
const key: string = normalizeId(id);
|
|
160
|
+
|
|
161
|
+
const existing: RequestedReference | undefined = requested.get(key);
|
|
162
|
+
|
|
163
|
+
requested.set(key, {
|
|
164
|
+
modelName: reference.modelName,
|
|
165
|
+
id: existing?.id || id,
|
|
166
|
+
/*
|
|
167
|
+
* The same id can arrive twice for one service (e.g. two monitor
|
|
168
|
+
* criteria pointing at the same status). If ANY of them requires the
|
|
169
|
+
* record to exist, the strictest wins.
|
|
170
|
+
*/
|
|
171
|
+
mustExist:
|
|
172
|
+
(existing?.mustExist ?? false) || (reference.mustExist ?? true),
|
|
173
|
+
});
|
|
110
174
|
}
|
|
111
175
|
|
|
112
176
|
if (idsByService.size === 0) {
|
|
@@ -114,21 +178,38 @@ export default class ProjectScopedReferenceValidator {
|
|
|
114
178
|
}
|
|
115
179
|
|
|
116
180
|
const foreignReferences: Array<ForeignReference> = [];
|
|
181
|
+
const missingReferences: Array<MissingReference> = [];
|
|
182
|
+
|
|
183
|
+
for (const [service, requestedById] of idsByService) {
|
|
184
|
+
/*
|
|
185
|
+
* Read the tenant and name columns off the model rather than assuming
|
|
186
|
+
* `projectId` / `name`. Not every referenced model is project scoped —
|
|
187
|
+
* a monitor criteria can name owner *users*, and User has no tenant
|
|
188
|
+
* column at all. Selecting a column a model does not have throws, and
|
|
189
|
+
* comparing an absent projectId against the write's project would
|
|
190
|
+
* report every such record as belonging to "a different project".
|
|
191
|
+
*/
|
|
192
|
+
const referencedModel: DatabaseBaseModel = service.getModel();
|
|
193
|
+
const tenantColumnName: string | null = referencedModel.getTenantColumn();
|
|
194
|
+
const hasNameColumn: boolean = referencedModel.hasColumn("name");
|
|
195
|
+
|
|
196
|
+
const select: Select<DatabaseBaseModel> = {
|
|
197
|
+
_id: true,
|
|
198
|
+
} as Select<DatabaseBaseModel>;
|
|
199
|
+
|
|
200
|
+
if (hasNameColumn) {
|
|
201
|
+
(select as Dictionary<boolean>)["name"] = true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (tenantColumnName) {
|
|
205
|
+
(select as Dictionary<boolean>)[tenantColumnName] = true;
|
|
206
|
+
}
|
|
117
207
|
|
|
118
|
-
for (const [service, idToModelName] of idsByService) {
|
|
119
208
|
const records: Array<DatabaseBaseModel> = await service.findBy({
|
|
120
209
|
query: {
|
|
121
|
-
_id: QueryHelper.any(Array.from(
|
|
210
|
+
_id: QueryHelper.any(Array.from(requestedById.keys())),
|
|
122
211
|
},
|
|
123
|
-
|
|
124
|
-
* `name` and `projectId` are not on DatabaseBaseModel, but every model
|
|
125
|
-
* passed here is project scoped and named, so the cast is safe.
|
|
126
|
-
*/
|
|
127
|
-
select: {
|
|
128
|
-
_id: true,
|
|
129
|
-
name: true,
|
|
130
|
-
projectId: true,
|
|
131
|
-
} as Select<DatabaseBaseModel>,
|
|
212
|
+
select: select,
|
|
132
213
|
limit: LIMIT_PER_PROJECT,
|
|
133
214
|
skip: 0,
|
|
134
215
|
props: {
|
|
@@ -136,37 +217,84 @@ export default class ProjectScopedReferenceValidator {
|
|
|
136
217
|
},
|
|
137
218
|
});
|
|
138
219
|
|
|
220
|
+
const foundIds: Set<string> = new Set();
|
|
221
|
+
|
|
139
222
|
for (const record of records) {
|
|
223
|
+
const id: string = normalizeId(record._id?.toString() || "");
|
|
224
|
+
|
|
225
|
+
foundIds.add(id);
|
|
226
|
+
|
|
227
|
+
if (!tenantColumnName) {
|
|
228
|
+
// Global record (e.g. User) — there is no project to compare against.
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
140
232
|
const recordProjectId: ObjectID | undefined =
|
|
141
|
-
record.getValue<ObjectID>(
|
|
233
|
+
record.getValue<ObjectID>(tenantColumnName) || undefined;
|
|
142
234
|
|
|
143
235
|
if (recordProjectId?.toString() === data.projectId.toString()) {
|
|
144
236
|
continue;
|
|
145
237
|
}
|
|
146
238
|
|
|
147
|
-
const id: string = record._id?.toString() || "";
|
|
148
|
-
|
|
149
239
|
foreignReferences.push({
|
|
150
|
-
modelName:
|
|
151
|
-
name:
|
|
240
|
+
modelName: requestedById.get(id)?.modelName || "Record",
|
|
241
|
+
name:
|
|
242
|
+
(hasNameColumn && record.getValue<string>("name")?.toString()) ||
|
|
243
|
+
id,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const [id, requested] of requestedById) {
|
|
248
|
+
if (foundIds.has(id) || !requested.mustExist) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
missingReferences.push({
|
|
253
|
+
modelName: requested.modelName,
|
|
254
|
+
// Echo the id as the caller wrote it, not the normalized key.
|
|
255
|
+
id: requested.id,
|
|
152
256
|
});
|
|
153
257
|
}
|
|
154
258
|
}
|
|
155
259
|
|
|
156
|
-
|
|
157
|
-
|
|
260
|
+
/*
|
|
261
|
+
* Both problems are reported in one go so a caller fixing a payload with
|
|
262
|
+
* several bad ids does not have to discover them one round-trip at a time.
|
|
263
|
+
* The wording of each clause is kept verbatim from when they were separate
|
|
264
|
+
* messages — support runbooks and tests match on it.
|
|
265
|
+
*/
|
|
266
|
+
const clauses: Array<string> = [];
|
|
267
|
+
|
|
268
|
+
if (foreignReferences.length > 0) {
|
|
269
|
+
const described: string = foreignReferences
|
|
270
|
+
.map((reference: ForeignReference) => {
|
|
271
|
+
return `${reference.modelName} "${reference.name}"`;
|
|
272
|
+
})
|
|
273
|
+
.join(", ");
|
|
274
|
+
|
|
275
|
+
clauses.push(
|
|
276
|
+
`references records that belong to a different project: ${described}. Please pick values from this project and try again.`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (missingReferences.length > 0) {
|
|
281
|
+
const described: string = missingReferences
|
|
282
|
+
.map((reference: MissingReference) => {
|
|
283
|
+
return `${reference.modelName} "${reference.id}"`;
|
|
284
|
+
})
|
|
285
|
+
.join(", ");
|
|
286
|
+
|
|
287
|
+
clauses.push(
|
|
288
|
+
`references records that do not exist: ${described}. Please pick values that exist in this project and try again.`,
|
|
289
|
+
);
|
|
158
290
|
}
|
|
159
291
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
})
|
|
164
|
-
.join(", ");
|
|
292
|
+
if (clauses.length === 0) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
165
295
|
|
|
166
296
|
throw new BadDataException(
|
|
167
|
-
`This ${
|
|
168
|
-
data.subject || "request"
|
|
169
|
-
} references records that belong to a different project: ${described}. Please pick values from this project and try again.`,
|
|
297
|
+
`This ${data.subject || "request"} ${clauses.join(" It also ")}`,
|
|
170
298
|
);
|
|
171
299
|
}
|
|
172
300
|
|
|
@@ -178,10 +306,13 @@ export default class ProjectScopedReferenceValidator {
|
|
|
178
306
|
* there fails the whole ingest job for that monitor, so those callers ask
|
|
179
307
|
* this instead and fall back to their project's own default.
|
|
180
308
|
*
|
|
181
|
-
* Note the deliberate difference from validateReferencesBelongToProject:
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
309
|
+
* Note the deliberate difference from validateReferencesBelongToProject:
|
|
310
|
+
* that one refuses the write and names what is wrong, which is right when
|
|
311
|
+
* someone is typing an id in. This one answers a question and never throws,
|
|
312
|
+
* which is right when a worker is reading an id that was typed in long ago —
|
|
313
|
+
* an id monitorSteps is allowed to keep holding (the write guard exempts what
|
|
314
|
+
* a record already stores, since none of those ids has a foreign key behind
|
|
315
|
+
* it and there is no migration to clean them up).
|
|
185
316
|
*/
|
|
186
317
|
public static async isUsableInProject(data: {
|
|
187
318
|
projectId: ObjectID | undefined;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import ColumnLength from "../../../Types/Database/ColumnLength";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* TruncateColumnValue
|
|
6
|
+
*
|
|
7
|
+
* Per-value clamps for bounded varchar columns written by the bulk
|
|
8
|
+
* inventory upsert paths (Kubernetes / Docker / Docker Swarm / Podman /
|
|
9
|
+
* Proxmox / Ceph / IoT).
|
|
10
|
+
*
|
|
11
|
+
* Those services hand-build `INSERT ... ON CONFLICT` statements and
|
|
12
|
+
* flush them in chunks of 500 rows through
|
|
13
|
+
* `getRepository().manager.query(...)`, which bypasses the length
|
|
14
|
+
* validation DatabaseService applies to ordinary CRUD writes. A single
|
|
15
|
+
* value past its column limit raises 22001 ("value too long for type
|
|
16
|
+
* character varying(N)") and aborts the WHOLE multi-row INSERT, so one
|
|
17
|
+
* malformed resource silently drops up to 499 healthy inventory rows
|
|
18
|
+
* with it. Clamping per value keeps the blast radius at one field.
|
|
19
|
+
*
|
|
20
|
+
* Truncation is the last line of defense, not the primary fix: columns
|
|
21
|
+
* that legitimately carry long values (image references, Kubernetes
|
|
22
|
+
* DNS-subdomain names) should be widened in the model + a migration so
|
|
23
|
+
* these clamps effectively never fire.
|
|
24
|
+
* ------------------------------------------------------------------
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export function truncateToLength(value: string, maxLength: number): string;
|
|
28
|
+
export function truncateToLength(
|
|
29
|
+
value: string | null | undefined,
|
|
30
|
+
maxLength: number,
|
|
31
|
+
): string | null;
|
|
32
|
+
export function truncateToLength(
|
|
33
|
+
value: string | null | undefined,
|
|
34
|
+
maxLength: number,
|
|
35
|
+
): string | null {
|
|
36
|
+
if (value === null || value === undefined) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (value.length <= maxLength) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const clipped: string = value.substring(0, maxLength);
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* JS string length counts UTF-16 code units, so a cut at maxLength can
|
|
48
|
+
* land in the middle of a surrogate pair (any astral-plane character:
|
|
49
|
+
* emoji, rare CJK). The orphaned high surrogate is not valid UTF-8 —
|
|
50
|
+
* Node encodes it as U+FFFD on the way to the driver — so drop it.
|
|
51
|
+
* A well-formed string never ends a character on a high surrogate,
|
|
52
|
+
* which makes this check unambiguous. Postgres counts characters, not
|
|
53
|
+
* code units, so clipping by code units is always conservative: the
|
|
54
|
+
* result can only be shorter than the column bound, never longer.
|
|
55
|
+
*/
|
|
56
|
+
const lastCodeUnit: number = clipped.charCodeAt(clipped.length - 1);
|
|
57
|
+
if (lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff) {
|
|
58
|
+
return clipped.substring(0, clipped.length - 1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return clipped;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Clamp to ColumnLength.ShortText — varchar(100). */
|
|
65
|
+
export function truncateShortText(value: string): string;
|
|
66
|
+
export function truncateShortText(
|
|
67
|
+
value: string | null | undefined,
|
|
68
|
+
): string | null;
|
|
69
|
+
export function truncateShortText(
|
|
70
|
+
value: string | null | undefined,
|
|
71
|
+
): string | null {
|
|
72
|
+
return truncateToLength(value, ColumnLength.ShortText);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Clamp to ColumnLength.LongText — varchar(500). */
|
|
76
|
+
export function truncateLongText(value: string): string;
|
|
77
|
+
export function truncateLongText(
|
|
78
|
+
value: string | null | undefined,
|
|
79
|
+
): string | null;
|
|
80
|
+
export function truncateLongText(
|
|
81
|
+
value: string | null | undefined,
|
|
82
|
+
): string | null {
|
|
83
|
+
return truncateToLength(value, ColumnLength.LongText);
|
|
84
|
+
}
|
|
@@ -50,6 +50,7 @@ import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
|
50
50
|
import ExceptionMessages from "../../../Types/Exception/ExceptionMessages";
|
|
51
51
|
import MonitorEvaluationSummary from "../../../Types/Monitor/MonitorEvaluationSummary";
|
|
52
52
|
import MonitorStatusService from "../../Services/MonitorStatusService";
|
|
53
|
+
import ProjectScopedReferenceValidator from "../Database/ProjectScopedReferenceValidator";
|
|
53
54
|
import { ProbeConnectionStatus } from "../../../Models/DatabaseModels/Probe";
|
|
54
55
|
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
|
|
55
56
|
|
|
@@ -1063,48 +1064,74 @@ export default class MonitorResourceUtil {
|
|
|
1063
1064
|
*/
|
|
1064
1065
|
let revertedToDefaultStatus: boolean = true;
|
|
1065
1066
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1067
|
+
/*
|
|
1068
|
+
* The stored default status can be a status that no longer exists, or
|
|
1069
|
+
* one from another project: monitorSteps is a JSON blob with no
|
|
1070
|
+
* foreign key behind it, and before issue #3039 was fixed the API
|
|
1071
|
+
* accepted any uuid for it. Writing it raised the
|
|
1072
|
+
* "violates foreign key constraint" error from the probe/telemetry
|
|
1073
|
+
* worker and failed the whole ingest run for this monitor, losing the
|
|
1074
|
+
* monitor log and payload persisted below. Skip the revert and log,
|
|
1075
|
+
* the same way the criteria path in Utils/Monitor/MonitorStatusTimeline
|
|
1076
|
+
* does. New writes are rejected up front by
|
|
1077
|
+
* MonitorStepsProjectValidator.
|
|
1078
|
+
*/
|
|
1079
|
+
const isDefaultStatusUsable: boolean =
|
|
1080
|
+
await ProjectScopedReferenceValidator.isUsableInProject({
|
|
1081
|
+
projectId: monitor.projectId!,
|
|
1082
|
+
id: monitorSteps.data.defaultMonitorStatusId,
|
|
1083
|
+
service: MonitorStatusService,
|
|
1072
1084
|
});
|
|
1073
|
-
|
|
1074
|
-
|
|
1085
|
+
|
|
1086
|
+
if (!isDefaultStatusUsable) {
|
|
1087
|
+
logger.error(
|
|
1088
|
+
`${dataToProcess.monitorId.toString()} - The default monitor status ${monitorSteps.data.defaultMonitorStatusId.toString()} does not exist in project ${monitor.projectId?.toString()}. Skipping revert to default status. Please pick a default monitor status that exists in this project.`,
|
|
1075
1089
|
);
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
err instanceof BadDataException &&
|
|
1086
|
-
err.message === MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE
|
|
1087
|
-
) {
|
|
1090
|
+
revertedToDefaultStatus = false;
|
|
1091
|
+
} else {
|
|
1092
|
+
try {
|
|
1093
|
+
await MonitorStatusTimelineService.create({
|
|
1094
|
+
data: monitorStatusTimeline,
|
|
1095
|
+
props: {
|
|
1096
|
+
isRoot: true,
|
|
1097
|
+
},
|
|
1098
|
+
});
|
|
1088
1099
|
logger.debug(
|
|
1089
|
-
`${dataToProcess.monitorId.toString()} - Monitor status
|
|
1100
|
+
`${dataToProcess.monitorId.toString()} - Monitor status updated to default.`,
|
|
1090
1101
|
);
|
|
1091
|
-
}
|
|
1092
|
-
err instanceof ServerException &&
|
|
1093
|
-
err.message === MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE
|
|
1094
|
-
) {
|
|
1102
|
+
} catch (err) {
|
|
1095
1103
|
/*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1104
|
+
* Idempotent concurrency race (see MonitorStatusTimeline.ts): a
|
|
1105
|
+
* concurrent result already moved the monitor to this default status,
|
|
1106
|
+
* so onBeforeCreate's dedupe check throws this exact BadDataException.
|
|
1107
|
+
* Treat as a no-op at debug level rather than failing the job. Match the
|
|
1108
|
+
* exact message so unrelated BadDataExceptions still propagate.
|
|
1101
1109
|
*/
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1110
|
+
if (
|
|
1111
|
+
err instanceof BadDataException &&
|
|
1112
|
+
err.message === MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE
|
|
1113
|
+
) {
|
|
1114
|
+
logger.debug(
|
|
1115
|
+
`${dataToProcess.monitorId.toString()} - Monitor status already at default; skipping duplicate status timeline (concurrent race).`,
|
|
1116
|
+
);
|
|
1117
|
+
} else if (
|
|
1118
|
+
err instanceof ServerException &&
|
|
1119
|
+
err.message === MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE
|
|
1120
|
+
) {
|
|
1121
|
+
/*
|
|
1122
|
+
* The per-monitor timeline mutex could not be acquired (fail-closed
|
|
1123
|
+
* create, see MonitorStatusTimelineService). Skipping is recoverable:
|
|
1124
|
+
* the next probe result re-evaluates the same criteria and recreates
|
|
1125
|
+
* the revert-to-default, while failing here would abort the whole
|
|
1126
|
+
* ingest run and lose the monitor log for this probe result.
|
|
1127
|
+
*/
|
|
1128
|
+
logger.error(
|
|
1129
|
+
`${dataToProcess.monitorId.toString()} - Could not acquire the monitor status timeline lock; skipping revert to default status. It will be retried on the next probe result.`,
|
|
1130
|
+
);
|
|
1131
|
+
revertedToDefaultStatus = false;
|
|
1132
|
+
} else {
|
|
1133
|
+
throw err;
|
|
1134
|
+
}
|
|
1108
1135
|
}
|
|
1109
1136
|
}
|
|
1110
1137
|
|
|
@@ -9,7 +9,9 @@ import MonitorStatusTimelineService, {
|
|
|
9
9
|
MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE,
|
|
10
10
|
MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE,
|
|
11
11
|
} from "../../Services/MonitorStatusTimelineService";
|
|
12
|
+
import MonitorStatusService from "../../Services/MonitorStatusService";
|
|
12
13
|
import ServerException from "../../../Types/Exception/ServerException";
|
|
14
|
+
import ProjectScopedReferenceValidator from "../Database/ProjectScopedReferenceValidator";
|
|
13
15
|
import logger, { LogAttributes } from "../Logger";
|
|
14
16
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
15
17
|
import DataToProcess from "./DataToProcess";
|
|
@@ -112,6 +114,40 @@ export default class MonitorStatusTimelineUtil {
|
|
|
112
114
|
throw new BadDataException("Monitor status is not defined.");
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
/*
|
|
118
|
+
* The criteria can name a status that no longer exists, or that belongs to
|
|
119
|
+
* another project: monitorSteps is a JSON blob with no foreign key behind
|
|
120
|
+
* it, so deleting a monitor status does not rewrite the criteria that
|
|
121
|
+
* point at it, and before issue #3039 was fixed the API accepted any uuid
|
|
122
|
+
* here in the first place. Writing it anyway is what raised
|
|
123
|
+
* insert or update on table "MonitorStatusTimeline" violates foreign key
|
|
124
|
+
* constraint
|
|
125
|
+
* inside the probe/telemetry worker, failing the whole ingest run for this
|
|
126
|
+
* monitor - no status change, and no monitor log or payload persisted
|
|
127
|
+
* either, because those come after this. Skip the status change and log,
|
|
128
|
+
* the same way MonitorIncident and MonitorAlert handle an unusable
|
|
129
|
+
* severity. New writes are rejected up front by
|
|
130
|
+
* MonitorStepsProjectValidator, so this only ever fires for monitors saved
|
|
131
|
+
* before that guard existed.
|
|
132
|
+
*/
|
|
133
|
+
const isMonitorStatusUsable: boolean =
|
|
134
|
+
await ProjectScopedReferenceValidator.isUsableInProject({
|
|
135
|
+
projectId: input.monitor.projectId!,
|
|
136
|
+
id: monitorStatusId,
|
|
137
|
+
service: MonitorStatusService,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!isMonitorStatusUsable) {
|
|
141
|
+
logger.error(
|
|
142
|
+
`${input.monitor.id?.toString()} - Criteria "${
|
|
143
|
+
input.criteriaInstance.data?.name
|
|
144
|
+
}" changes the monitor status to ${monitorStatusId.toString()}, which does not exist in project ${input.monitor.projectId?.toString()}. Skipping the status change. Please pick a monitor status that exists in this project.`,
|
|
145
|
+
monitorLogAttributes,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
115
151
|
//change monitor status.
|
|
116
152
|
|
|
117
153
|
// get last status of this monitor.
|