@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
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
it,
|
|
7
|
+
jest,
|
|
8
|
+
} from "@jest/globals";
|
|
9
|
+
import APIKeyAccessPermission from "../../../../Server/Utils/APIKey/AccessPermission";
|
|
10
|
+
import ApiKeyPermissionService from "../../../../Server/Services/ApiKeyPermissionService";
|
|
11
|
+
import APIKeyPermission from "../../../../Models/DatabaseModels/ApiKeyPermission";
|
|
12
|
+
import Label from "../../../../Models/DatabaseModels/Label";
|
|
13
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
14
|
+
import Permission, {
|
|
15
|
+
UserGlobalAccessPermission,
|
|
16
|
+
UserPermission,
|
|
17
|
+
UserTenantAccessPermission,
|
|
18
|
+
} from "../../../../Types/Permission";
|
|
19
|
+
import { getJestSpyOn } from "../../../Spy";
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* This class turns an API key into the permission set a request runs under, so
|
|
23
|
+
* every method here is a privilege-granting decision. The security invariant
|
|
24
|
+
* that must hold across all of them: a *default* (ordinary) API key is never
|
|
25
|
+
* silently handed ProjectOwner, while a *master* key deliberately is. A
|
|
26
|
+
* regression that leaks ProjectOwner into the default path would give every API
|
|
27
|
+
* key full control of the project; one that dropped it from the master path
|
|
28
|
+
* would break trusted internal automation. Both directions are pinned below.
|
|
29
|
+
*
|
|
30
|
+
* getApiTenantAccessPermission also reshapes stored permission rows into the
|
|
31
|
+
* runtime UserPermission shape (labels -> labelIds, block flag preserved). That
|
|
32
|
+
* mapping is where a dropped label would quietly widen a scoped permission, so
|
|
33
|
+
* it is tested against a spied service rather than a live database.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
37
|
+
const apiKeyId: ObjectID = ObjectID.generate();
|
|
38
|
+
|
|
39
|
+
const permissionValues: (
|
|
40
|
+
permissions: Array<UserPermission>,
|
|
41
|
+
) => Array<Permission> = (
|
|
42
|
+
permissions: Array<UserPermission>,
|
|
43
|
+
): Array<Permission> => {
|
|
44
|
+
return permissions.map((p: UserPermission) => {
|
|
45
|
+
return p.permission;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe("APIKeyAccessPermission.getDefaultApiGlobalPermission", () => {
|
|
50
|
+
it("grants Public, User and CurrentUser for the project", async () => {
|
|
51
|
+
const perm: UserGlobalAccessPermission =
|
|
52
|
+
await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
|
|
53
|
+
|
|
54
|
+
expect(perm._type).toBe("UserGlobalAccessPermission");
|
|
55
|
+
expect(
|
|
56
|
+
perm.projectIds.map((p: ObjectID) => {
|
|
57
|
+
return p.toString();
|
|
58
|
+
}),
|
|
59
|
+
).toEqual([projectId.toString()]);
|
|
60
|
+
expect(perm.globalPermissions).toEqual([
|
|
61
|
+
Permission.Public,
|
|
62
|
+
Permission.User,
|
|
63
|
+
Permission.CurrentUser,
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("does NOT grant ProjectOwner to an ordinary key", async () => {
|
|
68
|
+
// The core boundary: a default key must never be an owner.
|
|
69
|
+
const perm: UserGlobalAccessPermission =
|
|
70
|
+
await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
|
|
71
|
+
expect(perm.globalPermissions).not.toContain(Permission.ProjectOwner);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("APIKeyAccessPermission.getMasterKeyApiGlobalPermission", () => {
|
|
76
|
+
it("grants everything the default key has, plus ProjectOwner", async () => {
|
|
77
|
+
const master: UserGlobalAccessPermission =
|
|
78
|
+
await APIKeyAccessPermission.getMasterKeyApiGlobalPermission(projectId);
|
|
79
|
+
const def: UserGlobalAccessPermission =
|
|
80
|
+
await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
|
|
81
|
+
|
|
82
|
+
// Superset: no default grant is lost when elevating to master.
|
|
83
|
+
for (const p of def.globalPermissions) {
|
|
84
|
+
expect(master.globalPermissions).toContain(p);
|
|
85
|
+
}
|
|
86
|
+
// The one deliberate elevation.
|
|
87
|
+
expect(master.globalPermissions).toContain(Permission.ProjectOwner);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("scopes to exactly the one project", async () => {
|
|
91
|
+
const master: UserGlobalAccessPermission =
|
|
92
|
+
await APIKeyAccessPermission.getMasterKeyApiGlobalPermission(projectId);
|
|
93
|
+
expect(
|
|
94
|
+
master.projectIds.map((p: ObjectID) => {
|
|
95
|
+
return p.toString();
|
|
96
|
+
}),
|
|
97
|
+
).toEqual([projectId.toString()]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("APIKeyAccessPermission.getMasterApiTenantAccessPermission", () => {
|
|
102
|
+
it("elevates the default tenant permission with ProjectOwner", async () => {
|
|
103
|
+
const perm: UserTenantAccessPermission =
|
|
104
|
+
await APIKeyAccessPermission.getMasterApiTenantAccessPermission(
|
|
105
|
+
projectId,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(perm._type).toBe("UserTenantAccessPermission");
|
|
109
|
+
expect(perm.projectId.toString()).toBe(projectId.toString());
|
|
110
|
+
expect(permissionValues(perm.permissions)).toContain(
|
|
111
|
+
Permission.ProjectOwner,
|
|
112
|
+
);
|
|
113
|
+
// The default tenant baseline is retained alongside the elevation.
|
|
114
|
+
expect(permissionValues(perm.permissions)).toContain(
|
|
115
|
+
Permission.CurrentUser,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("APIKeyAccessPermission.getApiTenantAccessPermission", () => {
|
|
121
|
+
const spyFindBy: jest.SpyInstance = getJestSpyOn(
|
|
122
|
+
ApiKeyPermissionService,
|
|
123
|
+
"findBy",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
jest.clearAllMocks();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
afterAll(() => {
|
|
131
|
+
jest.restoreAllMocks();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const makeRow: (data: {
|
|
135
|
+
permission: Permission;
|
|
136
|
+
labelIds?: Array<ObjectID>;
|
|
137
|
+
isBlockPermission?: boolean;
|
|
138
|
+
}) => APIKeyPermission = (data: {
|
|
139
|
+
permission: Permission;
|
|
140
|
+
labelIds?: Array<ObjectID>;
|
|
141
|
+
isBlockPermission?: boolean;
|
|
142
|
+
}): APIKeyPermission => {
|
|
143
|
+
const row: APIKeyPermission = new APIKeyPermission();
|
|
144
|
+
row.permission = data.permission;
|
|
145
|
+
if (data.labelIds) {
|
|
146
|
+
row.labels = data.labelIds.map((id: ObjectID) => {
|
|
147
|
+
const label: Label = new Label();
|
|
148
|
+
label.id = id;
|
|
149
|
+
return label;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
row.isBlockPermission = data.isBlockPermission ?? false;
|
|
153
|
+
return row;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
it("queries the permissions for exactly this api key", async () => {
|
|
157
|
+
spyFindBy.mockResolvedValue([] as never);
|
|
158
|
+
|
|
159
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
160
|
+
projectId,
|
|
161
|
+
apiKeyId,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(spyFindBy).toHaveBeenCalledTimes(1);
|
|
165
|
+
const arg: { query: { apiKeyId: ObjectID } } = spyFindBy.mock
|
|
166
|
+
.calls[0]![0] as { query: { apiKeyId: ObjectID } };
|
|
167
|
+
expect(arg.query.apiKeyId.toString()).toBe(apiKeyId.toString());
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("returns just the default baseline when the key has no permissions", async () => {
|
|
171
|
+
spyFindBy.mockResolvedValue([] as never);
|
|
172
|
+
|
|
173
|
+
const perm: UserTenantAccessPermission =
|
|
174
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
175
|
+
projectId,
|
|
176
|
+
apiKeyId,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// No stored rows -> no ProjectOwner. An empty key is not an owner.
|
|
180
|
+
expect(permissionValues(perm.permissions)).not.toContain(
|
|
181
|
+
Permission.ProjectOwner,
|
|
182
|
+
);
|
|
183
|
+
expect(permissionValues(perm.permissions)).toContain(
|
|
184
|
+
Permission.CurrentUser,
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("maps each stored permission row onto the tenant permission", async () => {
|
|
189
|
+
const labelId: ObjectID = ObjectID.generate();
|
|
190
|
+
spyFindBy.mockResolvedValue([
|
|
191
|
+
makeRow({
|
|
192
|
+
permission: Permission.CreateProjectIncident,
|
|
193
|
+
labelIds: [labelId],
|
|
194
|
+
isBlockPermission: false,
|
|
195
|
+
}),
|
|
196
|
+
] as never);
|
|
197
|
+
|
|
198
|
+
const perm: UserTenantAccessPermission =
|
|
199
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
200
|
+
projectId,
|
|
201
|
+
apiKeyId,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const mapped: UserPermission | undefined = perm.permissions.find(
|
|
205
|
+
(p: UserPermission) => {
|
|
206
|
+
return p.permission === Permission.CreateProjectIncident;
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
expect(mapped).toBeDefined();
|
|
210
|
+
/*
|
|
211
|
+
* The label scope must survive the mapping, or the permission silently
|
|
212
|
+
* widens from "these labels" to "all".
|
|
213
|
+
*/
|
|
214
|
+
expect(
|
|
215
|
+
mapped!.labelIds.map((l: ObjectID) => {
|
|
216
|
+
return l.toString();
|
|
217
|
+
}),
|
|
218
|
+
).toEqual([labelId.toString()]);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("preserves the block flag so a deny row stays a deny row", async () => {
|
|
222
|
+
spyFindBy.mockResolvedValue([
|
|
223
|
+
makeRow({
|
|
224
|
+
permission: Permission.CreateProjectIncident,
|
|
225
|
+
labelIds: [],
|
|
226
|
+
isBlockPermission: true,
|
|
227
|
+
}),
|
|
228
|
+
] as never);
|
|
229
|
+
|
|
230
|
+
const perm: UserTenantAccessPermission =
|
|
231
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
232
|
+
projectId,
|
|
233
|
+
apiKeyId,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const mapped: UserPermission | undefined = perm.permissions.find(
|
|
237
|
+
(p: UserPermission) => {
|
|
238
|
+
return p.permission === Permission.CreateProjectIncident;
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
// Flipping a block permission to an allow would invert an admin's intent.
|
|
242
|
+
expect(mapped!.isBlockPermission).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("treats a row with no labels as an empty (unscoped) label set", async () => {
|
|
246
|
+
const row: APIKeyPermission = new APIKeyPermission();
|
|
247
|
+
row.permission = Permission.CreateProjectIncident;
|
|
248
|
+
// labels intentionally left undefined.
|
|
249
|
+
row.isBlockPermission = false;
|
|
250
|
+
spyFindBy.mockResolvedValue([row] as never);
|
|
251
|
+
|
|
252
|
+
const perm: UserTenantAccessPermission =
|
|
253
|
+
await APIKeyAccessPermission.getApiTenantAccessPermission(
|
|
254
|
+
projectId,
|
|
255
|
+
apiKeyId,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const mapped: UserPermission | undefined = perm.permissions.find(
|
|
259
|
+
(p: UserPermission) => {
|
|
260
|
+
return p.permission === Permission.CreateProjectIncident;
|
|
261
|
+
},
|
|
262
|
+
);
|
|
263
|
+
expect(mapped).toBeDefined();
|
|
264
|
+
// No crash on undefined labels, and it becomes [] rather than undefined.
|
|
265
|
+
expect(mapped!.labelIds).toEqual([]);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
import ProjectScopedReferenceValidator from "../../../../Server/Utils/Database/ProjectScopedReferenceValidator";
|
|
2
|
+
import DatabaseService from "../../../../Server/Services/DatabaseService";
|
|
2
3
|
import IncidentSeverityService from "../../../../Server/Services/IncidentSeverityService";
|
|
3
4
|
import IncidentStateService from "../../../../Server/Services/IncidentStateService";
|
|
4
5
|
import MonitorStatusService from "../../../../Server/Services/MonitorStatusService";
|
|
6
|
+
import UserService from "../../../../Server/Services/UserService";
|
|
7
|
+
import DatabaseBaseModel from "../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
5
8
|
import IncidentSeverity from "../../../../Models/DatabaseModels/IncidentSeverity";
|
|
6
9
|
import IncidentState from "../../../../Models/DatabaseModels/IncidentState";
|
|
7
10
|
import MonitorStatus from "../../../../Models/DatabaseModels/MonitorStatus";
|
|
11
|
+
import User from "../../../../Models/DatabaseModels/User";
|
|
8
12
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
13
|
+
import Name from "../../../../Types/Name";
|
|
9
14
|
import ObjectID from "../../../../Types/ObjectID";
|
|
10
15
|
import { afterEach, describe, expect, it, jest } from "@jest/globals";
|
|
11
16
|
|
|
12
17
|
/*
|
|
13
18
|
* Contract under test: an incident (or alert, or scheduled maintenance event)
|
|
14
19
|
* stores the id of its state, its severity and the monitor status it switches
|
|
15
|
-
* monitors to.
|
|
16
|
-
*
|
|
17
|
-
* record, the row here still points at it, and the ON DELETE NO ACTION foreign
|
|
18
|
-
* key refuses the delete with 23503.
|
|
20
|
+
* monitors to. Two things can be wrong with one of those ids, and the guard
|
|
21
|
+
* refuses both.
|
|
19
22
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* An id belonging to a *different* project is what leaves the referenced
|
|
24
|
+
* project undeletable — deleting that project cascades into the record, the row
|
|
25
|
+
* here still points at it, and the ON DELETE NO ACTION foreign key refuses the
|
|
26
|
+
* delete with 23503.
|
|
27
|
+
*
|
|
28
|
+
* An id that exists NOWHERE is issue #3039: the REST API took any uuid for
|
|
29
|
+
* these, and the write then failed as a raw foreign key violation deep in
|
|
30
|
+
* Postgres — or, for ids stored in a JSON blob with no foreign key behind them,
|
|
31
|
+
* saved fine and blew up much later inside the probe worker. Rejecting it here
|
|
32
|
+
* names the id instead. Callers whose reference is allowed to dangle opt out
|
|
33
|
+
* per reference with `mustExist: false`.
|
|
23
34
|
*/
|
|
24
35
|
|
|
25
36
|
const PROJECT_ID: ObjectID = new ObjectID(
|
|
@@ -34,6 +45,7 @@ const FOREIGN_SEVERITY_ID: string = "cfc2f04f-79cb-4344-8c54-dafe5e3a290c";
|
|
|
34
45
|
const FOREIGN_STATE_ID: string = "a2eb67d4-bd2e-4186-9187-dad799c9316c";
|
|
35
46
|
const FOREIGN_STATUS_ID: string = "1a1cf3f2-0e35-4a1e-98a1-3f0f8b5f7f9e";
|
|
36
47
|
const UNKNOWN_ID: string = "9c0ba0b3-2f8e-4c02-a8d5-6a4d2f5b9c11";
|
|
48
|
+
const OWN_USER_ID: string = "7c2f6b40-6a1b-4f18-9d0e-2c5ba2d3f0a7";
|
|
37
49
|
|
|
38
50
|
const incidentSeverity: (
|
|
39
51
|
id: string,
|
|
@@ -172,17 +184,200 @@ describe("ProjectScopedReferenceValidator", () => {
|
|
|
172
184
|
).resolves.toBeUndefined();
|
|
173
185
|
});
|
|
174
186
|
|
|
175
|
-
it("
|
|
187
|
+
it("rejects an id that matches no record at all", async () => {
|
|
188
|
+
/*
|
|
189
|
+
* Issue #3039. This used to be allowed through on purpose, and the write
|
|
190
|
+
* then either came back as an opaque foreign key violation from Postgres or
|
|
191
|
+
* — in a JSON column with no foreign key — saved and failed later, at run
|
|
192
|
+
* time, with nothing reported to whoever supplied the id.
|
|
193
|
+
*/
|
|
194
|
+
mockLookups({ incidentSeverities: [] });
|
|
195
|
+
|
|
196
|
+
await expect(
|
|
197
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
198
|
+
projectId: PROJECT_ID,
|
|
199
|
+
subject: "incident",
|
|
200
|
+
references: [
|
|
201
|
+
{
|
|
202
|
+
modelName: "Incident Severity",
|
|
203
|
+
id: UNKNOWN_ID,
|
|
204
|
+
service: IncidentSeverityService,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
}),
|
|
208
|
+
).rejects.toThrow(BadDataException);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("accepts an UPPERCASE uuid for a record Postgres reads back lower-cased", async () => {
|
|
212
|
+
/*
|
|
213
|
+
* ObjectID keeps whatever case it was handed — its validation regex is
|
|
214
|
+
* case-insensitive — and Postgres compares `uuid` by parsed value, so an
|
|
215
|
+
* uppercase id in the payload selects the row fine but reads back
|
|
216
|
+
* lower-cased. Comparing the two verbatim reported a record that plainly
|
|
217
|
+
* exists as missing, and the API answered "does not exist" for an id the
|
|
218
|
+
* caller had just read out of a GET response.
|
|
219
|
+
*/
|
|
220
|
+
mockLookups({
|
|
221
|
+
incidentSeverities: [incidentSeverity(OWN_SEVERITY_ID, PROJECT_ID)],
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await expect(
|
|
225
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
226
|
+
projectId: PROJECT_ID,
|
|
227
|
+
references: [
|
|
228
|
+
{
|
|
229
|
+
modelName: "Incident Severity",
|
|
230
|
+
id: OWN_SEVERITY_ID.toUpperCase(),
|
|
231
|
+
service: IncidentSeverityService,
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
}),
|
|
235
|
+
).resolves.toBeUndefined();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("echoes the id as the caller wrote it when reporting it missing", async () => {
|
|
239
|
+
mockLookups({ incidentSeverities: [] });
|
|
240
|
+
|
|
241
|
+
await expect(
|
|
242
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
243
|
+
projectId: PROJECT_ID,
|
|
244
|
+
references: [
|
|
245
|
+
{
|
|
246
|
+
modelName: "Incident Severity",
|
|
247
|
+
id: UNKNOWN_ID.toUpperCase(),
|
|
248
|
+
service: IncidentSeverityService,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
}),
|
|
252
|
+
).rejects.toThrow(`Incident Severity "${UNKNOWN_ID.toUpperCase()}"`);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("names the missing id in the rejection so the caller can find it", async () => {
|
|
256
|
+
mockLookups({ incidentSeverities: [] });
|
|
257
|
+
|
|
258
|
+
await expect(
|
|
259
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
260
|
+
projectId: PROJECT_ID,
|
|
261
|
+
subject: "incident",
|
|
262
|
+
references: [
|
|
263
|
+
{
|
|
264
|
+
modelName: "Incident Severity",
|
|
265
|
+
id: UNKNOWN_ID,
|
|
266
|
+
service: IncidentSeverityService,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
}),
|
|
270
|
+
).rejects.toThrow(
|
|
271
|
+
`This incident references records that do not exist: Incident Severity "${UNKNOWN_ID}". Please pick values that exist in this project and try again.`,
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("leaves a missing id alone when the caller opts out with mustExist false", async () => {
|
|
276
|
+
/*
|
|
277
|
+
* The opt-out exists for references with no foreign key behind them, where
|
|
278
|
+
* a stored id really can dangle (deleting a monitor status does not rewrite
|
|
279
|
+
* the monitor criteria that name it). Refusing those would stop a user
|
|
280
|
+
* saving their way out of a record that was already broken.
|
|
281
|
+
*/
|
|
282
|
+
mockLookups({ incidentSeverities: [] });
|
|
283
|
+
|
|
284
|
+
await expect(
|
|
285
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
286
|
+
projectId: PROJECT_ID,
|
|
287
|
+
references: [
|
|
288
|
+
{
|
|
289
|
+
modelName: "Incident Severity",
|
|
290
|
+
id: UNKNOWN_ID,
|
|
291
|
+
service: IncidentSeverityService,
|
|
292
|
+
mustExist: false,
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
}),
|
|
296
|
+
).resolves.toBeUndefined();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("keeps the strictest requirement when the same id arrives twice", async () => {
|
|
176
300
|
/*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
301
|
+
* Two monitor criteria can name the same status, one operative and one not.
|
|
302
|
+
* They collapse to a single lookup, and the one that requires the record to
|
|
303
|
+
* exist must not be dropped by the one that does not.
|
|
180
304
|
*/
|
|
181
305
|
mockLookups({ incidentSeverities: [] });
|
|
182
306
|
|
|
183
307
|
await expect(
|
|
184
308
|
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
185
309
|
projectId: PROJECT_ID,
|
|
310
|
+
references: [
|
|
311
|
+
{
|
|
312
|
+
modelName: "Incident Severity",
|
|
313
|
+
id: UNKNOWN_ID,
|
|
314
|
+
service: IncidentSeverityService,
|
|
315
|
+
mustExist: false,
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
modelName: "Incident Severity",
|
|
319
|
+
id: UNKNOWN_ID,
|
|
320
|
+
service: IncidentSeverityService,
|
|
321
|
+
mustExist: true,
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
}),
|
|
325
|
+
).rejects.toThrow("do not exist");
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("reports a foreign reference and a missing one in the same message", async () => {
|
|
329
|
+
/*
|
|
330
|
+
* A payload with several bad ids should not have to be fixed one
|
|
331
|
+
* round-trip at a time.
|
|
332
|
+
*/
|
|
333
|
+
mockLookups({
|
|
334
|
+
incidentSeverities: [
|
|
335
|
+
incidentSeverity(FOREIGN_SEVERITY_ID, OTHER_PROJECT_ID),
|
|
336
|
+
],
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
let message: string = "";
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
343
|
+
projectId: PROJECT_ID,
|
|
344
|
+
subject: "incident",
|
|
345
|
+
references: [
|
|
346
|
+
{
|
|
347
|
+
modelName: "Incident Severity",
|
|
348
|
+
id: FOREIGN_SEVERITY_ID,
|
|
349
|
+
service: IncidentSeverityService,
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
modelName: "Incident Severity",
|
|
353
|
+
id: UNKNOWN_ID,
|
|
354
|
+
service: IncidentSeverityService,
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
});
|
|
358
|
+
} catch (error) {
|
|
359
|
+
message = (error as BadDataException).message;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
expect(message).toContain(
|
|
363
|
+
'belong to a different project: Incident Severity "Critical"',
|
|
364
|
+
);
|
|
365
|
+
expect(message).toContain(
|
|
366
|
+
`do not exist: Incident Severity "${UNKNOWN_ID}"`,
|
|
367
|
+
);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("does not require a record to exist when its project is unknown", async () => {
|
|
371
|
+
/*
|
|
372
|
+
* Same reason the cross-project half is skipped: root and internal writes
|
|
373
|
+
* do not always carry a project, and the guard must stay out of the way
|
|
374
|
+
* rather than guess.
|
|
375
|
+
*/
|
|
376
|
+
const { severityFindBy } = mockLookups({ incidentSeverities: [] });
|
|
377
|
+
|
|
378
|
+
await expect(
|
|
379
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
380
|
+
projectId: undefined,
|
|
186
381
|
references: [
|
|
187
382
|
{
|
|
188
383
|
modelName: "Incident Severity",
|
|
@@ -192,6 +387,52 @@ describe("ProjectScopedReferenceValidator", () => {
|
|
|
192
387
|
],
|
|
193
388
|
}),
|
|
194
389
|
).resolves.toBeUndefined();
|
|
390
|
+
|
|
391
|
+
expect(severityFindBy).not.toHaveBeenCalled();
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("checks existence against a model that has no project of its own", async () => {
|
|
395
|
+
/*
|
|
396
|
+
* Monitor criteria can name owner *users*, and User has no tenant column.
|
|
397
|
+
* The cross-project comparison has to be skipped for those — comparing an
|
|
398
|
+
* absent projectId against the write's project would report every user as
|
|
399
|
+
* belonging to a different project — while the existence check still runs.
|
|
400
|
+
*/
|
|
401
|
+
const user: User = new User();
|
|
402
|
+
user._id = OWN_USER_ID;
|
|
403
|
+
user.name = new Name("Alice");
|
|
404
|
+
|
|
405
|
+
jest.spyOn(UserService, "findBy").mockResolvedValue([user] as never);
|
|
406
|
+
|
|
407
|
+
await expect(
|
|
408
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
409
|
+
projectId: PROJECT_ID,
|
|
410
|
+
references: [
|
|
411
|
+
{
|
|
412
|
+
modelName: "User",
|
|
413
|
+
id: OWN_USER_ID,
|
|
414
|
+
service:
|
|
415
|
+
UserService as unknown as DatabaseService<DatabaseBaseModel>,
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
}),
|
|
419
|
+
).resolves.toBeUndefined();
|
|
420
|
+
|
|
421
|
+
jest.spyOn(UserService, "findBy").mockResolvedValue([] as never);
|
|
422
|
+
|
|
423
|
+
await expect(
|
|
424
|
+
ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
425
|
+
projectId: PROJECT_ID,
|
|
426
|
+
references: [
|
|
427
|
+
{
|
|
428
|
+
modelName: "User",
|
|
429
|
+
id: UNKNOWN_ID,
|
|
430
|
+
service:
|
|
431
|
+
UserService as unknown as DatabaseService<DatabaseBaseModel>,
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
}),
|
|
435
|
+
).rejects.toThrow(`User "${UNKNOWN_ID}"`);
|
|
195
436
|
});
|
|
196
437
|
|
|
197
438
|
it("reports every foreign reference in a single message", async () => {
|
|
@@ -272,9 +513,15 @@ describe("ProjectScopedReferenceValidator", () => {
|
|
|
272
513
|
it("looks each model up once, with every id it was asked about", async () => {
|
|
273
514
|
/*
|
|
274
515
|
* These run on the incident create path, so three references must not mean
|
|
275
|
-
* three round trips per model.
|
|
516
|
+
* three round trips per model. Both ids are returned so the existence check
|
|
517
|
+
* has nothing to complain about and the call reaches its end.
|
|
276
518
|
*/
|
|
277
|
-
const { severityFindBy } = mockLookups({
|
|
519
|
+
const { severityFindBy } = mockLookups({
|
|
520
|
+
incidentSeverities: [
|
|
521
|
+
incidentSeverity(OWN_SEVERITY_ID, PROJECT_ID),
|
|
522
|
+
incidentSeverity(FOREIGN_SEVERITY_ID, PROJECT_ID),
|
|
523
|
+
],
|
|
524
|
+
});
|
|
278
525
|
|
|
279
526
|
await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
280
527
|
projectId: PROJECT_ID,
|