@oneuptime/common 11.0.1 → 11.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Alert.ts +110 -0
- package/Models/DatabaseModels/CephCluster.ts +964 -0
- package/Models/DatabaseModels/CephClusterLabelRule.ts +514 -0
- package/Models/DatabaseModels/CephClusterOwnerRule.ts +596 -0
- package/Models/DatabaseModels/CephClusterOwnerTeam.ts +487 -0
- package/Models/DatabaseModels/CephClusterOwnerUser.ts +486 -0
- package/Models/DatabaseModels/CephResource.ts +809 -0
- package/Models/DatabaseModels/Host.ts +64 -0
- package/Models/DatabaseModels/Incident.ts +110 -0
- package/Models/DatabaseModels/Index.ts +24 -0
- package/Models/DatabaseModels/ProxmoxCluster.ts +943 -0
- package/Models/DatabaseModels/ProxmoxClusterLabelRule.ts +514 -0
- package/Models/DatabaseModels/ProxmoxClusterOwnerRule.ts +596 -0
- package/Models/DatabaseModels/ProxmoxClusterOwnerTeam.ts +487 -0
- package/Models/DatabaseModels/ProxmoxClusterOwnerUser.ts +486 -0
- package/Models/DatabaseModels/ProxmoxResource.ts +726 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +110 -0
- package/Server/API/BillingInvoiceAPI.ts +47 -7
- package/Server/API/CephResourceAPI.ts +134 -0
- package/Server/API/DashboardAPI.ts +46 -0
- package/Server/API/ProjectAPI.ts +15 -0
- package/Server/API/ProxmoxResourceAPI.ts +132 -0
- package/Server/API/ResellerPlanAPI.ts +17 -0
- package/Server/Infrastructure/GlobalCache.ts +8 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1781500000000-AddProxmoxAndCephClusterTables.ts +163 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1781600000000-AddProxmoxCephV2Columns.ts +211 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1781600000001-AddProxmoxCephActivityAndRules.ts +590 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1781700000000-AddProxmoxCephV3Columns.ts +64 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Redis.ts +40 -12
- package/Server/Services/AnalyticsDatabaseService.ts +1 -1
- package/Server/Services/BillingService.ts +109 -21
- package/Server/Services/CephClusterLabelRuleEngineService.ts +200 -0
- package/Server/Services/CephClusterLabelRuleService.ts +14 -0
- package/Server/Services/CephClusterOwnerRuleEngineService.ts +218 -0
- package/Server/Services/CephClusterOwnerRuleService.ts +14 -0
- package/Server/Services/CephClusterOwnerTeamService.ts +10 -0
- package/Server/Services/CephClusterOwnerUserService.ts +10 -0
- package/Server/Services/CephClusterService.ts +401 -0
- package/Server/Services/CephResourceService.ts +383 -0
- package/Server/Services/CloudResourceService.ts +11 -3
- package/Server/Services/DockerHostService.ts +11 -3
- package/Server/Services/ExceptionAggregationService.ts +2 -0
- package/Server/Services/HostService.ts +11 -3
- package/Server/Services/Index.ts +24 -0
- package/Server/Services/KubernetesClusterService.ts +11 -3
- package/Server/Services/LogAggregationService.ts +2 -0
- package/Server/Services/MetricAggregationService.ts +2 -0
- package/Server/Services/OpenTelemetryIngestService.ts +36 -0
- package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +204 -0
- package/Server/Services/ProxmoxClusterLabelRuleService.ts +14 -0
- package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +222 -0
- package/Server/Services/ProxmoxClusterOwnerRuleService.ts +14 -0
- package/Server/Services/ProxmoxClusterOwnerTeamService.ts +10 -0
- package/Server/Services/ProxmoxClusterOwnerUserService.ts +10 -0
- package/Server/Services/ProxmoxClusterService.ts +382 -0
- package/Server/Services/ProxmoxResourceService.ts +404 -0
- package/Server/Services/RumApplicationService.ts +11 -3
- package/Server/Services/ServerlessFunctionService.ts +11 -3
- package/Server/Services/TelemetryUsageBillingService.ts +41 -3
- package/Server/Services/TraceAggregationService.ts +2 -0
- package/Server/Types/AnalyticsDatabase/AggregateBy.ts +8 -23
- package/Server/Utils/Monitor/MonitorAlert.ts +45 -0
- package/Server/Utils/Monitor/MonitorClusterContext.ts +129 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +344 -4
- package/Server/Utils/Monitor/MonitorIncident.ts +130 -7
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +39 -6
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +3 -1
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +33 -0
- package/Server/Utils/Profiling.ts +37 -2
- package/Server/Utils/Telemetry/EntityRegistry.ts +4 -0
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +1096 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +85 -0
- package/Server/Utils/Telemetry.ts +8 -19
- package/Tests/Server/API/BillingInvoiceAPI.test.ts +194 -0
- package/Tests/Server/API/ProjectAPI.test.ts +91 -0
- package/Tests/Server/API/ResellerPlanAPI.test.ts +207 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +100 -0
- package/Tests/Server/Services/BillingService.test.ts +323 -0
- package/Tests/Server/Services/CephResourceService.test.ts +264 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +326 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +322 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +13 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +879 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +196 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +1231 -0
- package/Tests/Types/Monitor/ProxmoxAlertTemplates.test.ts +732 -0
- package/Tests/Utils/Telemetry/EntityRelationship.test.ts +49 -0
- package/Tests/Utils/Telemetry/HeartbeatAvailability.test.ts +423 -0
- package/Types/BaseDatabase/AggregationIntervalUtil.ts +74 -0
- package/Types/Dashboard/DashboardComponentType.ts +4 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +2 -0
- package/Types/Dashboard/DashboardComponents/DashboardCephOsdListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardCephPoolListComponent.ts +14 -0
- package/Types/Dashboard/DashboardComponents/DashboardProxmoxGuestListComponent.ts +17 -0
- package/Types/Dashboard/DashboardComponents/DashboardProxmoxNodeListComponent.ts +16 -0
- package/Types/Dashboard/DashboardTemplates.ts +446 -0
- package/Types/Icon/IconProp.ts +2 -0
- package/Types/Monitor/CephAlertTemplates.ts +1647 -0
- package/Types/Monitor/CephMetricCatalog.ts +409 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +44 -0
- package/Types/Monitor/MonitorStep.ts +64 -0
- package/Types/Monitor/MonitorStepCephMonitor.ts +57 -0
- package/Types/Monitor/MonitorStepProxmoxMonitor.ts +81 -0
- package/Types/Monitor/MonitorType.ts +29 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +899 -0
- package/Types/Monitor/ProxmoxMetricCatalog.ts +382 -0
- package/Types/Permission.ts +464 -0
- package/Types/Telemetry/EntityType.ts +11 -0
- package/Types/Telemetry/ServiceType.ts +2 -0
- package/UI/Components/Icon/Icon.tsx +84 -0
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +9 -5
- package/UI/Utils/Telemetry/Telemetry.ts +16 -21
- package/UI/Utils/TelemetryService.ts +7 -3
- package/Utils/Dashboard/Components/DashboardCephOsdListComponent.ts +63 -0
- package/Utils/Dashboard/Components/DashboardCephPoolListComponent.ts +32 -0
- package/Utils/Dashboard/Components/DashboardCephResourceListShared.ts +61 -0
- package/Utils/Dashboard/Components/DashboardProxmoxGuestListComponent.ts +69 -0
- package/Utils/Dashboard/Components/DashboardProxmoxNodeListComponent.ts +55 -0
- package/Utils/Dashboard/Components/DashboardProxmoxResourceListShared.ts +61 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/Telemetry/EntityKey.ts +35 -0
- package/Utils/Telemetry/EntityRelationship.ts +6 -0
- package/Utils/Telemetry/HeartbeatAvailability.ts +262 -0
- package/build/dist/Models/DatabaseModels/Alert.js +108 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +992 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -0
- package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js +522 -0
- package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js +603 -0
- package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/CephClusterOwnerTeam.js +503 -0
- package/build/dist/Models/DatabaseModels/CephClusterOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/CephClusterOwnerUser.js +502 -0
- package/build/dist/Models/DatabaseModels/CephClusterOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/CephResource.js +846 -0
- package/build/dist/Models/DatabaseModels/CephResource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Host.js +63 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +108 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +24 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +967 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js +522 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js +603 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerTeam.js +503 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerUser.js +502 -0
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +761 -0
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +108 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js +35 -5
- package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
- package/build/dist/Server/API/CephResourceAPI.js +98 -0
- package/build/dist/Server/API/CephResourceAPI.js.map +1 -0
- package/build/dist/Server/API/DashboardAPI.js +46 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +11 -0
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +95 -0
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -0
- package/build/dist/Server/API/ResellerPlanAPI.js +17 -3
- package/build/dist/Server/API/ResellerPlanAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +7 -2
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781500000000-AddProxmoxAndCephClusterTables.js +76 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781500000000-AddProxmoxAndCephClusterTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000000-AddProxmoxCephV2Columns.js +108 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000000-AddProxmoxCephV2Columns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000001-AddProxmoxCephActivityAndRules.js +253 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000001-AddProxmoxCephActivityAndRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781700000000-AddProxmoxCephV3Columns.js +43 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781700000000-AddProxmoxCephV3Columns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Redis.js +31 -8
- package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +85 -23
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +166 -0
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/CephClusterLabelRuleService.js +13 -0
- package/build/dist/Server/Services/CephClusterLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +186 -0
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/CephClusterOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/CephClusterOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/CephClusterOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/CephClusterOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/CephClusterOwnerUserService.js +9 -0
- package/build/dist/Server/Services/CephClusterOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/CephClusterService.js +353 -0
- package/build/dist/Server/Services/CephClusterService.js.map +1 -0
- package/build/dist/Server/Services/CephResourceService.js +257 -0
- package/build/dist/Server/Services/CephResourceService.js.map +1 -0
- package/build/dist/Server/Services/CloudResourceService.js +10 -2
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +10 -2
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/ExceptionAggregationService.js +2 -0
- package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +10 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +24 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +10 -2
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +2 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +37 -7
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +166 -0
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleService.js +13 -0
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +186 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerUserService.js +9 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js +337 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -0
- package/build/dist/Server/Services/ProxmoxResourceService.js +285 -0
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -0
- package/build/dist/Server/Services/RumApplicationService.js +10 -2
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +10 -2
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +30 -3
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js +8 -25
- package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +36 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +90 -0
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +228 -4
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +103 -8
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +23 -6
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +23 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Profiling.js +24 -3
- package/build/dist/Server/Utils/Profiling.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +854 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +62 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +8 -10
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js +69 -0
- package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +4 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephOsdListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephOsdListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephPoolListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephPoolListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxGuestListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxGuestListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxNodeListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxNodeListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +394 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Icon/IconProp.js +2 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Monitor/CephAlertTemplates.js +1379 -0
- package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/CephMetricCatalog.js +353 -0
- package/build/dist/Types/Monitor/CephMetricCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStep.js +46 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepCephMonitor.js +34 -0
- package/build/dist/Types/Monitor/MonitorStepCephMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStepProxmoxMonitor.js +36 -0
- package/build/dist/Types/Monitor/MonitorStepProxmoxMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +27 -1
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +743 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxMetricCatalog.js +320 -0
- package/build/dist/Types/Monitor/ProxmoxMetricCatalog.js.map +1 -0
- package/build/dist/Types/Permission.js +408 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +11 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/ServiceType.js +2 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +33 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +5 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
- package/build/dist/UI/Utils/Telemetry/Telemetry.js +11 -10
- package/build/dist/UI/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/UI/Utils/TelemetryService.js +5 -2
- package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardCephOsdListComponent.js +50 -0
- package/build/dist/Utils/Dashboard/Components/DashboardCephOsdListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardCephPoolListComponent.js +27 -0
- package/build/dist/Utils/Dashboard/Components/DashboardCephPoolListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardCephResourceListShared.js +46 -0
- package/build/dist/Utils/Dashboard/Components/DashboardCephResourceListShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardProxmoxGuestListComponent.js +55 -0
- package/build/dist/Utils/Dashboard/Components/DashboardProxmoxGuestListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardProxmoxNodeListComponent.js +42 -0
- package/build/dist/Utils/Dashboard/Components/DashboardProxmoxNodeListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardProxmoxResourceListShared.js +46 -0
- package/build/dist/Utils/Dashboard/Components/DashboardProxmoxResourceListShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityKey.js +27 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityRelationship.js +3 -0
- package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
- package/build/dist/Utils/Telemetry/HeartbeatAvailability.js +174 -0
- package/build/dist/Utils/Telemetry/HeartbeatAvailability.js.map +1 -0
- package/package.json +29 -21
|
@@ -533,6 +533,70 @@ export default class TelemetryEntity {
|
|
|
533
533
|
return { entityType: EntityType.KubernetesDeployment, id };
|
|
534
534
|
},
|
|
535
535
|
|
|
536
|
+
// proxmox.cluster — proxmox.cluster.name only (see proxmoxClusterIdentity).
|
|
537
|
+
(attrs: EntityAttributes) => {
|
|
538
|
+
const id: Dictionary<string> | null =
|
|
539
|
+
TelemetryEntity.proxmoxClusterIdentity(attrs);
|
|
540
|
+
return id ? { entityType: EntityType.ProxmoxCluster, id } : null;
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
// proxmox.node — cluster + proxmox.node.name.
|
|
544
|
+
(attrs: EntityAttributes) => {
|
|
545
|
+
const nodeName: string | null = TelemetryEntity.str(
|
|
546
|
+
attrs,
|
|
547
|
+
"proxmox.node.name",
|
|
548
|
+
);
|
|
549
|
+
if (!nodeName) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
const id: Dictionary<string> = {
|
|
553
|
+
...(TelemetryEntity.proxmoxClusterIdentity(attrs) || {}),
|
|
554
|
+
"proxmox.node.name": nodeName,
|
|
555
|
+
};
|
|
556
|
+
return { entityType: EntityType.ProxmoxNode, id };
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
/*
|
|
560
|
+
* proxmox.guest — cluster + proxmox.guest.vmid. The node name is
|
|
561
|
+
* deliberately NOT part of guest identity: vmids are cluster-unique
|
|
562
|
+
* and a live migration moves a guest between nodes without changing
|
|
563
|
+
* what it is, so folding the node in would fork the key on every
|
|
564
|
+
* migration. Guest name/type are descriptive (a guest can be renamed).
|
|
565
|
+
*/
|
|
566
|
+
(attrs: EntityAttributes) => {
|
|
567
|
+
const vmid: string | null = TelemetryEntity.str(
|
|
568
|
+
attrs,
|
|
569
|
+
"proxmox.guest.vmid",
|
|
570
|
+
);
|
|
571
|
+
if (!vmid) {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
const id: Dictionary<string> = {
|
|
575
|
+
...(TelemetryEntity.proxmoxClusterIdentity(attrs) || {}),
|
|
576
|
+
"proxmox.guest.vmid": vmid,
|
|
577
|
+
};
|
|
578
|
+
return { entityType: EntityType.ProxmoxGuest, id };
|
|
579
|
+
},
|
|
580
|
+
|
|
581
|
+
/*
|
|
582
|
+
* ceph.cluster — ceph.cluster.name only. `ceph.cluster.fsid` is
|
|
583
|
+
* descriptive, not identity: the typed Postgres row (CephCluster) and
|
|
584
|
+
* the read side (`EntityKey.keyForCephCluster`) are name-based, and
|
|
585
|
+
* the fsid is only optionally stamped by the agent.
|
|
586
|
+
*/
|
|
587
|
+
(attrs: EntityAttributes) => {
|
|
588
|
+
const name: string | null = TelemetryEntity.str(
|
|
589
|
+
attrs,
|
|
590
|
+
"ceph.cluster.name",
|
|
591
|
+
);
|
|
592
|
+
return name
|
|
593
|
+
? {
|
|
594
|
+
entityType: EntityType.CephCluster,
|
|
595
|
+
id: { "ceph.cluster.name": name },
|
|
596
|
+
}
|
|
597
|
+
: null;
|
|
598
|
+
},
|
|
599
|
+
|
|
536
600
|
/*
|
|
537
601
|
* container — container.id. High-churn: flows as a membership key but
|
|
538
602
|
* is membership-only by default (not promoted to a registry row
|
|
@@ -652,6 +716,8 @@ export default class TelemetryEntity {
|
|
|
652
716
|
"container.image.tag",
|
|
653
717
|
"container.image.tags",
|
|
654
718
|
],
|
|
719
|
+
[EntityType.ProxmoxGuest]: ["proxmox.guest.name", "proxmox.guest.type"],
|
|
720
|
+
[EntityType.CephCluster]: ["ceph.cluster.fsid"],
|
|
655
721
|
};
|
|
656
722
|
|
|
657
723
|
private static descriptiveAttributesFor(
|
|
@@ -739,6 +805,25 @@ export default class TelemetryEntity {
|
|
|
739
805
|
return null;
|
|
740
806
|
}
|
|
741
807
|
|
|
808
|
+
/*
|
|
809
|
+
* Proxmox cluster identity — proxmox.cluster.name only, mirroring
|
|
810
|
+
* k8sClusterIdentity above: the typed Postgres row (ProxmoxCluster) and
|
|
811
|
+
* the read side (`EntityKey.keyForProxmoxCluster`) are name-based, and
|
|
812
|
+
* the attribute is the user-configured join key our agent stamps on
|
|
813
|
+
* every resource (see Internal/Roadmap/ProxmoxCephProducts.md §1). This
|
|
814
|
+
* identity is also folded into the composite proxmox node/guest
|
|
815
|
+
* identities, which must stay name-based with it.
|
|
816
|
+
*/
|
|
817
|
+
private static proxmoxClusterIdentity(
|
|
818
|
+
attrs: EntityAttributes,
|
|
819
|
+
): Dictionary<string> | null {
|
|
820
|
+
const name: string | null = this.str(attrs, "proxmox.cluster.name");
|
|
821
|
+
if (name) {
|
|
822
|
+
return { "proxmox.cluster.name": name };
|
|
823
|
+
}
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
|
|
742
827
|
/** Copy `key` from attrs into `id` (canonicalized) when scalar & present. */
|
|
743
828
|
private static addIfPresent(
|
|
744
829
|
id: Dictionary<string>,
|
|
@@ -24,16 +24,14 @@ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto";
|
|
|
24
24
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
25
25
|
import { AWSXRayIdGenerator } from "@opentelemetry/id-generator-aws-xray";
|
|
26
26
|
import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
|
|
27
|
-
import { Resource } from "@opentelemetry/resources";
|
|
27
|
+
import { Resource, resourceFromAttributes } from "@opentelemetry/resources";
|
|
28
28
|
import {
|
|
29
29
|
BatchLogRecordProcessor,
|
|
30
30
|
LoggerProvider,
|
|
31
31
|
LogRecordProcessor,
|
|
32
32
|
type LoggerProviderConfig,
|
|
33
33
|
} from "@opentelemetry/sdk-logs";
|
|
34
|
-
import type { Resource as LogsResource } from "@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources/build/src/Resource";
|
|
35
34
|
import {
|
|
36
|
-
Aggregation,
|
|
37
35
|
MeterProvider,
|
|
38
36
|
PeriodicExportingMetricReader,
|
|
39
37
|
} from "@opentelemetry/sdk-metrics";
|
|
@@ -56,7 +54,7 @@ import GracefulShutdown, { ShutdownPriority } from "./GracefulShutdown";
|
|
|
56
54
|
import ContextSpanProcessor from "./Telemetry/ContextSpanProcessor";
|
|
57
55
|
import RuntimeMetrics from "./Telemetry/RuntimeMetrics";
|
|
58
56
|
|
|
59
|
-
type ResourceWithRawAttributes =
|
|
57
|
+
type ResourceWithRawAttributes = Resource & {
|
|
60
58
|
getRawAttributes?: () => Array<[string, AttributeValue | undefined]>;
|
|
61
59
|
};
|
|
62
60
|
|
|
@@ -165,7 +163,7 @@ export default class Telemetry {
|
|
|
165
163
|
}
|
|
166
164
|
|
|
167
165
|
public static getResource(data: { serviceName: string }): Resource {
|
|
168
|
-
return
|
|
166
|
+
return resourceFromAttributes({
|
|
169
167
|
[ATTR_SERVICE_NAME]: data.serviceName,
|
|
170
168
|
[ATTR_SERVICE_VERSION]: AppVersion,
|
|
171
169
|
["deployment.environment"]: Env,
|
|
@@ -203,20 +201,11 @@ export default class Telemetry {
|
|
|
203
201
|
compression: CompressionAlgorithm.GZIP,
|
|
204
202
|
}) as unknown as PushMetricExporter;
|
|
205
203
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
(
|
|
212
|
-
metricExporter as unknown as {
|
|
213
|
-
selectAggregation: (..._args: Array<unknown>) => Aggregation;
|
|
214
|
-
}
|
|
215
|
-
).selectAggregation = () => {
|
|
216
|
-
return Aggregation.Default();
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
204
|
+
/*
|
|
205
|
+
* No aggregation-selector shim is needed anymore: the OTLP metric
|
|
206
|
+
* exporter and the sdk-metrics package now come from the same release
|
|
207
|
+
* line, so the exporter's default selector already matches the SDK.
|
|
208
|
+
*/
|
|
220
209
|
this.metricReader = new PeriodicExportingMetricReader({
|
|
221
210
|
exporter: metricExporter,
|
|
222
211
|
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import BillingInvoiceAPI from "../../../Server/API/BillingInvoiceAPI";
|
|
2
|
+
import BillingInvoiceService from "../../../Server/Services/BillingInvoiceService";
|
|
3
|
+
import BillingService from "../../../Server/Services/BillingService";
|
|
4
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
5
|
+
import {
|
|
6
|
+
NextFunction,
|
|
7
|
+
OneUptimeRequest,
|
|
8
|
+
OneUptimeResponse,
|
|
9
|
+
} from "../../../Server/Utils/Express";
|
|
10
|
+
import Response from "../../../Server/Utils/Response";
|
|
11
|
+
import { mockRouter } from "./Helpers";
|
|
12
|
+
import { describe, expect, it } from "@jest/globals";
|
|
13
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
14
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
15
|
+
import Permission, { UserPermission } from "../../../Types/Permission";
|
|
16
|
+
import BillingInvoice from "../../../Models/DatabaseModels/BillingInvoice";
|
|
17
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
18
|
+
|
|
19
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
20
|
+
return {
|
|
21
|
+
getRouter: () => {
|
|
22
|
+
return mockRouter;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
28
|
+
return {
|
|
29
|
+
sendEntityArrayResponse: jest.fn().mockImplementation((...args: []) => {
|
|
30
|
+
return args;
|
|
31
|
+
}),
|
|
32
|
+
sendJsonObjectResponse: jest.fn().mockImplementation((...args: []) => {
|
|
33
|
+
return args;
|
|
34
|
+
}),
|
|
35
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
36
|
+
sendEntityResponse: jest.fn().mockImplementation((...args: []) => {
|
|
37
|
+
return args;
|
|
38
|
+
}),
|
|
39
|
+
sendErrorResponse: jest.fn().mockImplementation((...args: []) => {
|
|
40
|
+
return args;
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
46
|
+
return {
|
|
47
|
+
...jest.requireActual("../../../Server/EnvironmentConfig"),
|
|
48
|
+
IsBillingEnabled: true,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
jest.mock("../../../Server/Services/BillingInvoiceService");
|
|
53
|
+
jest.mock("../../../Server/Services/BillingService");
|
|
54
|
+
jest.mock("../../../Server/Services/ProjectService");
|
|
55
|
+
|
|
56
|
+
describe("BillingInvoiceAPI", () => {
|
|
57
|
+
let mockRequest: OneUptimeRequest;
|
|
58
|
+
let mockResponse: OneUptimeResponse;
|
|
59
|
+
let nextFunction: NextFunction;
|
|
60
|
+
|
|
61
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
62
|
+
const projectCustomerId: string = "cus_own_project";
|
|
63
|
+
const invoiceId: string = "in_123";
|
|
64
|
+
|
|
65
|
+
let project: Project;
|
|
66
|
+
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
new BillingInvoiceAPI();
|
|
69
|
+
|
|
70
|
+
project = new Project();
|
|
71
|
+
project.id = projectId;
|
|
72
|
+
project.paymentProviderCustomerId = projectCustomerId;
|
|
73
|
+
project.paymentProviderSubscriptionId = "sub_123";
|
|
74
|
+
|
|
75
|
+
jest
|
|
76
|
+
.spyOn(BillingInvoiceAPI.prototype, "getPermissionsForTenant")
|
|
77
|
+
.mockResolvedValue([
|
|
78
|
+
{
|
|
79
|
+
permission: Permission.EditInvoices,
|
|
80
|
+
} as UserPermission,
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
ProjectService.findOneById = jest.fn().mockResolvedValue(project);
|
|
84
|
+
BillingInvoiceService.findOneBy = jest
|
|
85
|
+
.fn()
|
|
86
|
+
.mockResolvedValue(new BillingInvoice());
|
|
87
|
+
BillingInvoiceService.updateOneBy = jest.fn().mockResolvedValue(undefined);
|
|
88
|
+
BillingInvoiceService.refreshSubscriptionStatus = jest
|
|
89
|
+
.fn()
|
|
90
|
+
.mockResolvedValue(undefined);
|
|
91
|
+
BillingService.payInvoice = jest.fn().mockResolvedValue({
|
|
92
|
+
id: invoiceId,
|
|
93
|
+
status: "paid",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
mockRequest = {
|
|
97
|
+
tenantId: projectId,
|
|
98
|
+
body: {
|
|
99
|
+
data: {
|
|
100
|
+
paymentProviderInvoiceId: invoiceId,
|
|
101
|
+
paymentProviderCustomerId: projectCustomerId,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
} as unknown as OneUptimeRequest;
|
|
105
|
+
mockResponse = {
|
|
106
|
+
send: jest.fn(),
|
|
107
|
+
json: jest.fn(),
|
|
108
|
+
status: jest.fn().mockReturnThis(),
|
|
109
|
+
} as unknown as OneUptimeResponse;
|
|
110
|
+
nextFunction = jest.fn();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
afterEach(() => {
|
|
114
|
+
jest.restoreAllMocks();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("POST /billing-invoices/pay", () => {
|
|
118
|
+
it("should reject a customer id that does not belong to the project", async () => {
|
|
119
|
+
mockRequest.body["data"] = {
|
|
120
|
+
paymentProviderInvoiceId: invoiceId,
|
|
121
|
+
paymentProviderCustomerId: "cus_victim_tenant",
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
await mockRouter
|
|
125
|
+
.match("post", "/billing-invoices/pay")
|
|
126
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
127
|
+
|
|
128
|
+
expect(nextFunction).toHaveBeenCalledWith(
|
|
129
|
+
new BadDataException("Customer ID does not belong to this project"),
|
|
130
|
+
);
|
|
131
|
+
expect(BillingService.payInvoice).not.toHaveBeenCalled();
|
|
132
|
+
expect(BillingInvoiceService.updateOneBy).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should reject an invoice that does not belong to the project", async () => {
|
|
136
|
+
BillingInvoiceService.findOneBy = jest.fn().mockResolvedValue(null);
|
|
137
|
+
|
|
138
|
+
await mockRouter
|
|
139
|
+
.match("post", "/billing-invoices/pay")
|
|
140
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
141
|
+
|
|
142
|
+
expect(BillingInvoiceService.findOneBy).toHaveBeenCalledWith(
|
|
143
|
+
expect.objectContaining({
|
|
144
|
+
query: {
|
|
145
|
+
projectId: projectId,
|
|
146
|
+
paymentProviderInvoiceId: invoiceId,
|
|
147
|
+
},
|
|
148
|
+
/*
|
|
149
|
+
* ignoreHooks is required: BillingInvoiceService.onBeforeFind
|
|
150
|
+
* throws without props.tenantId and would re-sync all invoices
|
|
151
|
+
* from Stripe on every pay attempt.
|
|
152
|
+
*/
|
|
153
|
+
props: expect.objectContaining({
|
|
154
|
+
ignoreHooks: true,
|
|
155
|
+
}),
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
expect(nextFunction).toHaveBeenCalledWith(
|
|
159
|
+
new BadDataException("Invoice not found for this project"),
|
|
160
|
+
);
|
|
161
|
+
expect(BillingService.payInvoice).not.toHaveBeenCalled();
|
|
162
|
+
expect(BillingInvoiceService.updateOneBy).not.toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should pay with the project's own customer id and scope the status update to the project", async () => {
|
|
166
|
+
// body omits the customer id — the project's own customer is charged.
|
|
167
|
+
mockRequest.body["data"] = {
|
|
168
|
+
paymentProviderInvoiceId: invoiceId,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
await mockRouter
|
|
172
|
+
.match("post", "/billing-invoices/pay")
|
|
173
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
174
|
+
|
|
175
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
176
|
+
expect(BillingService.payInvoice).toHaveBeenCalledWith(
|
|
177
|
+
projectCustomerId,
|
|
178
|
+
invoiceId,
|
|
179
|
+
);
|
|
180
|
+
expect(BillingInvoiceService.updateOneBy).toHaveBeenCalledWith(
|
|
181
|
+
expect.objectContaining({
|
|
182
|
+
query: {
|
|
183
|
+
projectId: projectId,
|
|
184
|
+
paymentProviderInvoiceId: invoiceId,
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
|
|
189
|
+
mockRequest,
|
|
190
|
+
mockResponse,
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import ProjectAPI from "../../../Server/API/ProjectAPI";
|
|
2
|
+
import BillingService from "../../../Server/Services/BillingService";
|
|
3
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
2
4
|
import TeamMemberService from "../../../Server/Services/TeamMemberService";
|
|
3
5
|
import {
|
|
4
6
|
NextFunction,
|
|
@@ -15,6 +17,17 @@ import ObjectID from "../../../Types/ObjectID";
|
|
|
15
17
|
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
16
18
|
import Project from "../../../Models/DatabaseModels/Project";
|
|
17
19
|
import TeamMember from "../../../Models/DatabaseModels/TeamMember";
|
|
20
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
21
|
+
import Permission, { UserPermission } from "../../../Types/Permission";
|
|
22
|
+
|
|
23
|
+
jest.mock("../../../Server/EnvironmentConfig", () => {
|
|
24
|
+
return {
|
|
25
|
+
...jest.requireActual("../../../Server/EnvironmentConfig"),
|
|
26
|
+
IsBillingEnabled: true,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
jest.mock("../../../Server/Services/BillingService");
|
|
18
31
|
|
|
19
32
|
jest.mock("../../../Server/Utils/Express", () => {
|
|
20
33
|
return {
|
|
@@ -216,4 +229,82 @@ describe("ProjectAPI", () => {
|
|
|
216
229
|
expect(nextFunction).toHaveBeenCalledWith(authError);
|
|
217
230
|
});
|
|
218
231
|
});
|
|
232
|
+
|
|
233
|
+
describe("PUT /project/:id/change-plan", () => {
|
|
234
|
+
const planId: string = "plan_123";
|
|
235
|
+
|
|
236
|
+
const tenantMismatchError: BadDataException = new BadDataException(
|
|
237
|
+
"Project ID in the URL does not match the project the request is authenticated for",
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
beforeEach(() => {
|
|
241
|
+
ProjectService.findOneById = jest.fn().mockResolvedValue({
|
|
242
|
+
paymentProviderCustomerId: "cus_123",
|
|
243
|
+
} as Project);
|
|
244
|
+
ProjectService.changePlan = jest.fn().mockResolvedValue(undefined);
|
|
245
|
+
BillingService.hasPaymentMethods = jest.fn().mockResolvedValue(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should reject when the URL project id does not match the authenticated tenant", async () => {
|
|
249
|
+
const victimProjectId: ObjectID = ObjectID.generate();
|
|
250
|
+
const attackerProjectId: ObjectID = ObjectID.generate();
|
|
251
|
+
|
|
252
|
+
mockRequest.params = { id: victimProjectId.toString() };
|
|
253
|
+
mockRequest.tenantId = attackerProjectId;
|
|
254
|
+
mockRequest.body = { data: { paymentProviderPlanId: planId } };
|
|
255
|
+
|
|
256
|
+
await mockRouter
|
|
257
|
+
.match("put", "/project/:id/change-plan")
|
|
258
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
259
|
+
|
|
260
|
+
expect(nextFunction).toHaveBeenCalledWith(tenantMismatchError);
|
|
261
|
+
expect(ProjectService.changePlan).not.toHaveBeenCalled();
|
|
262
|
+
// the foreign project must not even be looked up.
|
|
263
|
+
expect(ProjectService.findOneById).not.toHaveBeenCalled();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should reject when the request has no authenticated tenant", async () => {
|
|
267
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
268
|
+
|
|
269
|
+
mockRequest.params = { id: projectId.toString() };
|
|
270
|
+
mockRequest.body = { data: { paymentProviderPlanId: planId } };
|
|
271
|
+
|
|
272
|
+
await mockRouter
|
|
273
|
+
.match("put", "/project/:id/change-plan")
|
|
274
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
275
|
+
|
|
276
|
+
expect(nextFunction).toHaveBeenCalledWith(tenantMismatchError);
|
|
277
|
+
expect(ProjectService.changePlan).not.toHaveBeenCalled();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("should change the plan of the authenticated tenant's own project", async () => {
|
|
281
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
282
|
+
|
|
283
|
+
mockRequest.params = { id: projectId.toString() };
|
|
284
|
+
mockRequest.tenantId = projectId;
|
|
285
|
+
mockRequest.body = { data: { paymentProviderPlanId: planId } };
|
|
286
|
+
|
|
287
|
+
jest
|
|
288
|
+
.spyOn(ProjectAPI.prototype, "getPermissionsForTenant")
|
|
289
|
+
.mockResolvedValue([
|
|
290
|
+
{
|
|
291
|
+
permission: Permission.ProjectOwner,
|
|
292
|
+
} as UserPermission,
|
|
293
|
+
]);
|
|
294
|
+
|
|
295
|
+
await mockRouter
|
|
296
|
+
.match("put", "/project/:id/change-plan")
|
|
297
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
298
|
+
|
|
299
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
300
|
+
expect(ProjectService.changePlan).toHaveBeenCalledWith({
|
|
301
|
+
projectId: projectId,
|
|
302
|
+
paymentProviderPlanId: planId,
|
|
303
|
+
});
|
|
304
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
|
|
305
|
+
mockRequest,
|
|
306
|
+
mockResponse,
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
219
310
|
});
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import ResellerPlanAPI from "../../../Server/API/ResellerPlanAPI";
|
|
2
|
+
import ProjectService from "../../../Server/Services/ProjectService";
|
|
3
|
+
import ResellerPlanService from "../../../Server/Services/ResellerPlanService";
|
|
4
|
+
import {
|
|
5
|
+
NextFunction,
|
|
6
|
+
OneUptimeRequest,
|
|
7
|
+
OneUptimeResponse,
|
|
8
|
+
} from "../../../Server/Utils/Express";
|
|
9
|
+
import Response from "../../../Server/Utils/Response";
|
|
10
|
+
import { mockRouter } from "./Helpers";
|
|
11
|
+
import { describe, expect, it } from "@jest/globals";
|
|
12
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
13
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
14
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
15
|
+
import ResellerPlan from "../../../Models/DatabaseModels/ResellerPlan";
|
|
16
|
+
|
|
17
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
18
|
+
return {
|
|
19
|
+
getRouter: () => {
|
|
20
|
+
return mockRouter;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
26
|
+
return {
|
|
27
|
+
sendEntityArrayResponse: jest.fn().mockImplementation((...args: []) => {
|
|
28
|
+
return args;
|
|
29
|
+
}),
|
|
30
|
+
sendJsonObjectResponse: jest.fn().mockImplementation((...args: []) => {
|
|
31
|
+
return args;
|
|
32
|
+
}),
|
|
33
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
34
|
+
sendEntityResponse: jest.fn().mockImplementation((...args: []) => {
|
|
35
|
+
return args;
|
|
36
|
+
}),
|
|
37
|
+
sendErrorResponse: jest.fn().mockImplementation((...args: []) => {
|
|
38
|
+
return args;
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
jest.mock("../../../Server/Services/ResellerPlanService");
|
|
44
|
+
jest.mock("../../../Server/Services/ProjectService");
|
|
45
|
+
jest.mock("../../../Server/Services/BillingService");
|
|
46
|
+
jest.mock("../../../Server/Services/PromoCodeService");
|
|
47
|
+
|
|
48
|
+
describe("ResellerPlanAPI", () => {
|
|
49
|
+
let mockRequest: OneUptimeRequest;
|
|
50
|
+
let mockResponse: OneUptimeResponse;
|
|
51
|
+
let nextFunction: NextFunction;
|
|
52
|
+
|
|
53
|
+
const resellerExternalId: string = "reseller_a";
|
|
54
|
+
const resellerObjectId: ObjectID = ObjectID.generate();
|
|
55
|
+
const resellerPlanObjectId: ObjectID = ObjectID.generate();
|
|
56
|
+
const projectObjectId: ObjectID = ObjectID.generate();
|
|
57
|
+
const licenseKey: string = "license_123";
|
|
58
|
+
|
|
59
|
+
const mockResellerPlan: ResellerPlan = {
|
|
60
|
+
id: resellerPlanObjectId,
|
|
61
|
+
planId: "plan_basic",
|
|
62
|
+
reseller: {
|
|
63
|
+
id: resellerObjectId,
|
|
64
|
+
resellerId: resellerExternalId,
|
|
65
|
+
},
|
|
66
|
+
monitorLimit: 10,
|
|
67
|
+
teamMemberLimit: 5,
|
|
68
|
+
} as unknown as ResellerPlan;
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
new ResellerPlanAPI();
|
|
72
|
+
|
|
73
|
+
ResellerPlanService.findOneBy = jest
|
|
74
|
+
.fn()
|
|
75
|
+
.mockResolvedValue(mockResellerPlan);
|
|
76
|
+
ProjectService.findOneBy = jest.fn().mockResolvedValue(null);
|
|
77
|
+
ProjectService.updateOneById = jest.fn().mockResolvedValue(undefined);
|
|
78
|
+
ProjectService.deleteOneBy = jest.fn().mockResolvedValue(undefined);
|
|
79
|
+
|
|
80
|
+
mockRequest = {
|
|
81
|
+
params: { resellerId: resellerExternalId },
|
|
82
|
+
bearerTokenData: { resellerId: resellerExternalId },
|
|
83
|
+
body: {
|
|
84
|
+
action: "enhance_tier",
|
|
85
|
+
plan_id: "plan_basic",
|
|
86
|
+
uuid: licenseKey,
|
|
87
|
+
activation_email: "customer@example.com",
|
|
88
|
+
},
|
|
89
|
+
} as unknown as OneUptimeRequest;
|
|
90
|
+
mockResponse = {
|
|
91
|
+
send: jest.fn(),
|
|
92
|
+
json: jest.fn(),
|
|
93
|
+
status: jest.fn().mockReturnThis(),
|
|
94
|
+
} as unknown as OneUptimeResponse;
|
|
95
|
+
nextFunction = jest.fn();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
afterEach(() => {
|
|
99
|
+
jest.restoreAllMocks();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("POST /reseller-plan/action/:resellerId", () => {
|
|
103
|
+
it("should scope the project lookup to the authenticated reseller on tier change", async () => {
|
|
104
|
+
await mockRouter
|
|
105
|
+
.match("post", "/reseller-plan/action/:resellerId")
|
|
106
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
107
|
+
|
|
108
|
+
expect(ProjectService.findOneBy).toHaveBeenCalledWith(
|
|
109
|
+
expect.objectContaining({
|
|
110
|
+
query: {
|
|
111
|
+
resellerLicenseId: licenseKey,
|
|
112
|
+
resellerId: resellerObjectId,
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
// license key belongs to another reseller -> scoped lookup finds nothing.
|
|
117
|
+
expect(nextFunction).toHaveBeenCalledWith(
|
|
118
|
+
new BadDataException("Project not found with this license key"),
|
|
119
|
+
);
|
|
120
|
+
expect(ProjectService.updateOneById).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should not delete another reseller's project on refund", async () => {
|
|
124
|
+
mockRequest.body["action"] = "refund";
|
|
125
|
+
|
|
126
|
+
await mockRouter
|
|
127
|
+
.match("post", "/reseller-plan/action/:resellerId")
|
|
128
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
129
|
+
|
|
130
|
+
expect(ProjectService.findOneBy).toHaveBeenCalledWith(
|
|
131
|
+
expect.objectContaining({
|
|
132
|
+
query: {
|
|
133
|
+
resellerLicenseId: licenseKey,
|
|
134
|
+
resellerId: resellerObjectId,
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
expect(ProjectService.deleteOneBy).not.toHaveBeenCalled();
|
|
139
|
+
// refund is idempotent: respond as refunded without deleting anything.
|
|
140
|
+
expect(Response.sendJsonObjectResponse).toHaveBeenCalledWith(
|
|
141
|
+
mockRequest,
|
|
142
|
+
mockResponse,
|
|
143
|
+
{
|
|
144
|
+
message: "product refunded",
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should update limits for the owning reseller's project", async () => {
|
|
150
|
+
ProjectService.findOneBy = jest.fn().mockResolvedValue({
|
|
151
|
+
id: projectObjectId,
|
|
152
|
+
} as Project);
|
|
153
|
+
|
|
154
|
+
await mockRouter
|
|
155
|
+
.match("post", "/reseller-plan/action/:resellerId")
|
|
156
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
157
|
+
|
|
158
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
159
|
+
expect(ProjectService.updateOneById).toHaveBeenCalledWith(
|
|
160
|
+
expect.objectContaining({
|
|
161
|
+
id: projectObjectId,
|
|
162
|
+
data: {
|
|
163
|
+
activeMonitorsLimit: 10,
|
|
164
|
+
seatLimit: 5,
|
|
165
|
+
resellerPlanId: resellerPlanObjectId,
|
|
166
|
+
},
|
|
167
|
+
}),
|
|
168
|
+
);
|
|
169
|
+
expect(Response.sendJsonObjectResponse).toHaveBeenCalledWith(
|
|
170
|
+
mockRequest,
|
|
171
|
+
mockResponse,
|
|
172
|
+
{
|
|
173
|
+
message: "product enhanced",
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should delete the owning reseller's project on refund with a reseller-scoped query", async () => {
|
|
179
|
+
mockRequest.body["action"] = "refund";
|
|
180
|
+
ProjectService.findOneBy = jest.fn().mockResolvedValue({
|
|
181
|
+
id: projectObjectId,
|
|
182
|
+
} as Project);
|
|
183
|
+
|
|
184
|
+
await mockRouter
|
|
185
|
+
.match("post", "/reseller-plan/action/:resellerId")
|
|
186
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
187
|
+
|
|
188
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
189
|
+
expect(ProjectService.deleteOneBy).toHaveBeenCalledWith(
|
|
190
|
+
expect.objectContaining({
|
|
191
|
+
query: {
|
|
192
|
+
resellerLicenseId: licenseKey,
|
|
193
|
+
resellerId: resellerObjectId,
|
|
194
|
+
_id: projectObjectId.toString(),
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
expect(Response.sendJsonObjectResponse).toHaveBeenCalledWith(
|
|
199
|
+
mockRequest,
|
|
200
|
+
mockResponse,
|
|
201
|
+
{
|
|
202
|
+
message: "product refunded",
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|