@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,120 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import DashboardComponentsUtil from "../../../../Utils/Dashboard/Components/Index";
|
|
3
|
+
import DashboardComponentType from "../../../../Types/Dashboard/DashboardComponentType";
|
|
4
|
+
import {
|
|
5
|
+
ComponentArgument,
|
|
6
|
+
ComponentInputType,
|
|
7
|
+
} from "../../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
8
|
+
import DashboardBaseComponent from "../../../../Types/Dashboard/DashboardComponents/DashboardBaseComponent";
|
|
9
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* DashboardComponentsUtil.getComponentSettingsArguments is the single dispatch
|
|
13
|
+
* from a widget's type to the config-form fields the dashboard editor renders
|
|
14
|
+
* for it. It is a long if/else chain that ends in `throw BadDataException`, so
|
|
15
|
+
* the failure mode it invites is a real one: add a widget to the
|
|
16
|
+
* DashboardComponentType enum, ship the component util, and forget the branch
|
|
17
|
+
* here — every attempt to configure that widget then throws at runtime with no
|
|
18
|
+
* compiler warning, because the chain has no exhaustiveness check.
|
|
19
|
+
*
|
|
20
|
+
* This suite is that missing exhaustiveness check. It also validates the shape
|
|
21
|
+
* of what every branch returns, so a malformed argument list (a duplicate id
|
|
22
|
+
* that silently drops a form field, an empty label) fails here instead of in
|
|
23
|
+
* the editor.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const ALL_TYPES: Array<DashboardComponentType> = Object.values(
|
|
27
|
+
DashboardComponentType,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const VALID_INPUT_TYPES: Set<string> = new Set<string>(
|
|
31
|
+
Object.values(ComponentInputType),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
describe("DashboardComponentsUtil.getComponentSettingsArguments", () => {
|
|
35
|
+
// Sanity: the enum is non-trivial, so an empty loop can't vacuously pass.
|
|
36
|
+
test("there is more than one dashboard component type to cover", () => {
|
|
37
|
+
expect(ALL_TYPES.length).toBeGreaterThan(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test.each(ALL_TYPES)(
|
|
41
|
+
"%s is wired into the dispatcher and returns an argument array",
|
|
42
|
+
(type: DashboardComponentType) => {
|
|
43
|
+
// The whole point: no enum member may fall through to BadDataException.
|
|
44
|
+
const args: Array<ComponentArgument<DashboardBaseComponent>> =
|
|
45
|
+
DashboardComponentsUtil.getComponentSettingsArguments(type);
|
|
46
|
+
expect(Array.isArray(args)).toBe(true);
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
test.each(ALL_TYPES)(
|
|
51
|
+
"every config argument for %s is well-formed",
|
|
52
|
+
(type: DashboardComponentType) => {
|
|
53
|
+
const args: Array<ComponentArgument<DashboardBaseComponent>> =
|
|
54
|
+
DashboardComponentsUtil.getComponentSettingsArguments(type);
|
|
55
|
+
|
|
56
|
+
for (const arg of args) {
|
|
57
|
+
// A blank label renders an unlabeled form field.
|
|
58
|
+
expect(typeof arg.name).toBe("string");
|
|
59
|
+
expect(arg.name.length).toBeGreaterThan(0);
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* id is the key the entered value is stored under; empty means the
|
|
63
|
+
* value has nowhere to go.
|
|
64
|
+
*/
|
|
65
|
+
expect(typeof arg.id).toBe("string");
|
|
66
|
+
expect(String(arg.id).length).toBeGreaterThan(0);
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* required drives validation; it must be an actual boolean, not
|
|
70
|
+
* undefined coerced as falsy.
|
|
71
|
+
*/
|
|
72
|
+
expect(typeof arg.required).toBe("boolean");
|
|
73
|
+
|
|
74
|
+
// An unknown input type renders no editor at all.
|
|
75
|
+
expect(VALID_INPUT_TYPES.has(arg.type)).toBe(true);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
test.each(ALL_TYPES)(
|
|
81
|
+
"%s has no duplicate argument ids",
|
|
82
|
+
(type: DashboardComponentType) => {
|
|
83
|
+
const args: Array<ComponentArgument<DashboardBaseComponent>> =
|
|
84
|
+
DashboardComponentsUtil.getComponentSettingsArguments(type);
|
|
85
|
+
|
|
86
|
+
const ids: Array<string> = args.map(
|
|
87
|
+
(a: ComponentArgument<DashboardBaseComponent>) => {
|
|
88
|
+
return String(a.id);
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
// A duplicate id means the second field overwrites the first's value.
|
|
92
|
+
expect(new Set<string>(ids).size).toBe(ids.length);
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
test("an unknown component type is rejected with BadDataException", () => {
|
|
97
|
+
expect(() => {
|
|
98
|
+
return DashboardComponentsUtil.getComponentSettingsArguments(
|
|
99
|
+
"NotARealComponentType" as DashboardComponentType,
|
|
100
|
+
);
|
|
101
|
+
}).toThrow(BadDataException);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("calling twice returns equivalent, independent argument arrays", () => {
|
|
105
|
+
/*
|
|
106
|
+
* The editor may request the arguments repeatedly; each call must yield a
|
|
107
|
+
* fresh array so mutating one rendering cannot corrupt the next.
|
|
108
|
+
*/
|
|
109
|
+
const first: Array<ComponentArgument<DashboardBaseComponent>> =
|
|
110
|
+
DashboardComponentsUtil.getComponentSettingsArguments(
|
|
111
|
+
DashboardComponentType.Text,
|
|
112
|
+
);
|
|
113
|
+
const second: Array<ComponentArgument<DashboardBaseComponent>> =
|
|
114
|
+
DashboardComponentsUtil.getComponentSettingsArguments(
|
|
115
|
+
DashboardComponentType.Text,
|
|
116
|
+
);
|
|
117
|
+
expect(second).toEqual(first);
|
|
118
|
+
expect(second).not.toBe(first);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComponentArgument,
|
|
3
|
+
ComponentArgumentSection,
|
|
4
|
+
ComponentInputType,
|
|
5
|
+
EntityFilterModelType,
|
|
6
|
+
} from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
7
|
+
import DashboardBaseComponent from "../../../Types/Dashboard/DashboardComponents/DashboardBaseComponent";
|
|
8
|
+
import DashboardNetworkMapComponent from "../../../Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent";
|
|
9
|
+
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
10
|
+
import { ObjectType } from "../../../Types/JSON";
|
|
11
|
+
import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
|
|
12
|
+
import DashboardComponentsUtil from "../../../Utils/Dashboard/Components/Index";
|
|
13
|
+
import DashboardNetworkMapComponentUtil, {
|
|
14
|
+
DEFAULT_MAX_SITES,
|
|
15
|
+
} from "../../../Utils/Dashboard/Components/DashboardNetworkMapComponent";
|
|
16
|
+
|
|
17
|
+
type ArgumentIds = keyof DashboardNetworkMapComponent["arguments"];
|
|
18
|
+
|
|
19
|
+
function getArguments(): Array<
|
|
20
|
+
ComponentArgument<DashboardNetworkMapComponent>
|
|
21
|
+
> {
|
|
22
|
+
return DashboardNetworkMapComponentUtil.getComponentConfigArguments();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getArgumentById(
|
|
26
|
+
id: ArgumentIds,
|
|
27
|
+
): ComponentArgument<DashboardNetworkMapComponent> {
|
|
28
|
+
const found: ComponentArgument<DashboardNetworkMapComponent> | undefined =
|
|
29
|
+
getArguments().find(
|
|
30
|
+
(arg: ComponentArgument<DashboardNetworkMapComponent>) => {
|
|
31
|
+
return arg.id === id;
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (!found) {
|
|
36
|
+
throw new Error(`No Network Map widget argument declared with id "${id}"`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return found;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getDropdownValues(id: ArgumentIds): Array<string> {
|
|
43
|
+
return (getArgumentById(id).dropdownOptions || []).map(
|
|
44
|
+
(option: DropdownOption): string => {
|
|
45
|
+
return String(option.value);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("DashboardNetworkMapComponentUtil", () => {
|
|
51
|
+
describe("getDefaultComponent", () => {
|
|
52
|
+
it("declares its componentType as NetworkMap so the renderer dispatch matches", () => {
|
|
53
|
+
expect(
|
|
54
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent().componentType,
|
|
55
|
+
).toBe(DashboardComponentType.NetworkMap);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("marks itself as a dashboard component so it survives config serialization", () => {
|
|
59
|
+
expect(DashboardNetworkMapComponentUtil.getDefaultComponent()._type).toBe(
|
|
60
|
+
ObjectType.DashboardComponent,
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("places a 6x5 tile at the canvas origin", () => {
|
|
65
|
+
const component: DashboardNetworkMapComponent =
|
|
66
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
67
|
+
|
|
68
|
+
expect(component.widthInDashboardUnits).toBe(6);
|
|
69
|
+
expect(component.heightInDashboardUnits).toBe(5);
|
|
70
|
+
expect(component.topInDashboardUnits).toBe(0);
|
|
71
|
+
expect(component.leftInDashboardUnits).toBe(0);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* The world is roughly 2:1, so a tile that is allowed to be short and
|
|
76
|
+
* wide letterboxes the map into an unreadable strip. The minimum has to
|
|
77
|
+
* bound BOTH axes, and the default has to sit at or above it — a widget
|
|
78
|
+
* that spawns below its own minimum is one the resize handles then
|
|
79
|
+
* refuse to shrink.
|
|
80
|
+
*/
|
|
81
|
+
it("cannot be resized below a square-ish 4x4, and spawns at or above that", () => {
|
|
82
|
+
const component: DashboardNetworkMapComponent =
|
|
83
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
84
|
+
|
|
85
|
+
expect(component.minWidthInDashboardUnits).toBe(4);
|
|
86
|
+
expect(component.minHeightInDashboardUnits).toBe(4);
|
|
87
|
+
expect(component.widthInDashboardUnits).toBeGreaterThanOrEqual(
|
|
88
|
+
component.minWidthInDashboardUnits,
|
|
89
|
+
);
|
|
90
|
+
expect(component.heightInDashboardUnits).toBeGreaterThanOrEqual(
|
|
91
|
+
component.minHeightInDashboardUnits,
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("fits inside the 12-unit dashboard grid", () => {
|
|
96
|
+
const component: DashboardNetworkMapComponent =
|
|
97
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
98
|
+
|
|
99
|
+
expect(
|
|
100
|
+
component.leftInDashboardUnits + component.widthInDashboardUnits,
|
|
101
|
+
).toBeLessThanOrEqual(12);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("opens on the map rather than the list, with names on and the row cap seeded", () => {
|
|
105
|
+
const component: DashboardNetworkMapComponent =
|
|
106
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
107
|
+
|
|
108
|
+
expect(component.arguments.viewMode).toBe("map");
|
|
109
|
+
expect(component.arguments.showLabels).toBe(true);
|
|
110
|
+
expect(component.arguments.maxSites).toBe(DEFAULT_MAX_SITES);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("seeds no filters, so a freshly added widget shows the whole estate", () => {
|
|
114
|
+
const component: DashboardNetworkMapComponent =
|
|
115
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
116
|
+
|
|
117
|
+
expect(component.arguments.statusFilter).toBeUndefined();
|
|
118
|
+
expect(component.arguments.networkSiteTypeIds).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* Two widgets dropped on the same dashboard must not share an id — the
|
|
123
|
+
* canvas keys, selects and persists components by it.
|
|
124
|
+
*/
|
|
125
|
+
it("generates a fresh component id on every call", () => {
|
|
126
|
+
const first: DashboardNetworkMapComponent =
|
|
127
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
128
|
+
const second: DashboardNetworkMapComponent =
|
|
129
|
+
DashboardNetworkMapComponentUtil.getDefaultComponent();
|
|
130
|
+
|
|
131
|
+
expect(first.componentId.toString()).not.toBe(
|
|
132
|
+
second.componentId.toString(),
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("caps the fetch at a bounded number of sites", () => {
|
|
137
|
+
expect(DEFAULT_MAX_SITES).toBeGreaterThan(0);
|
|
138
|
+
expect(Number.isInteger(DEFAULT_MAX_SITES)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("getComponentConfigArguments", () => {
|
|
143
|
+
it("declares exactly the arguments the widget reads", () => {
|
|
144
|
+
const ids: Array<unknown> = getArguments().map(
|
|
145
|
+
(arg: ComponentArgument<DashboardNetworkMapComponent>) => {
|
|
146
|
+
return arg.id;
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(ids).toEqual([
|
|
151
|
+
"title",
|
|
152
|
+
"viewMode",
|
|
153
|
+
"maxSites",
|
|
154
|
+
"showLabels",
|
|
155
|
+
"networkSiteTypeIds",
|
|
156
|
+
"statusFilter",
|
|
157
|
+
]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("declares no duplicate argument ids", () => {
|
|
161
|
+
const ids: Array<unknown> = getArguments().map(
|
|
162
|
+
(arg: ComponentArgument<DashboardNetworkMapComponent>) => {
|
|
163
|
+
return arg.id;
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("gives every argument a name, a description and no required flag", () => {
|
|
171
|
+
for (const arg of getArguments()) {
|
|
172
|
+
expect(arg.name.trim().length).toBeGreaterThan(0);
|
|
173
|
+
expect(arg.description.trim().length).toBeGreaterThan(0);
|
|
174
|
+
/*
|
|
175
|
+
* Every argument has a working default, so none of them may be
|
|
176
|
+
* required — a required field with a default is a form the user
|
|
177
|
+
* cannot submit without re-entering what is already true.
|
|
178
|
+
*/
|
|
179
|
+
expect(arg.required).toBe(false);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("types each argument as the input the settings form should render", () => {
|
|
184
|
+
expect(getArgumentById("title").type).toBe(ComponentInputType.Text);
|
|
185
|
+
expect(getArgumentById("viewMode").type).toBe(
|
|
186
|
+
ComponentInputType.Dropdown,
|
|
187
|
+
);
|
|
188
|
+
expect(getArgumentById("maxSites").type).toBe(ComponentInputType.Number);
|
|
189
|
+
expect(getArgumentById("showLabels").type).toBe(
|
|
190
|
+
ComponentInputType.Boolean,
|
|
191
|
+
);
|
|
192
|
+
expect(getArgumentById("networkSiteTypeIds").type).toBe(
|
|
193
|
+
ComponentInputType.EntityMultiSelectDropdown,
|
|
194
|
+
);
|
|
195
|
+
expect(getArgumentById("statusFilter").type).toBe(
|
|
196
|
+
ComponentInputType.Dropdown,
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* The site-type picker has to name a model the EntityFilterDropdown
|
|
202
|
+
* switch knows about, or it throws when the settings modal opens.
|
|
203
|
+
*/
|
|
204
|
+
it("binds the site-type picker to the NetworkSiteType lookup table", () => {
|
|
205
|
+
expect(getArgumentById("networkSiteTypeIds").entityFilterModelType).toBe(
|
|
206
|
+
EntityFilterModelType.NetworkSiteType,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("gives only the entity picker an entityFilterModelType", () => {
|
|
211
|
+
for (const arg of getArguments()) {
|
|
212
|
+
if (arg.id === "networkSiteTypeIds") {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
expect(arg.entityFilterModelType).toBeUndefined();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("offers exactly the two view modes the renderer implements", () => {
|
|
220
|
+
expect(getDropdownValues("viewMode")).toEqual(["map", "list"]);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
* The empty option is what lets somebody UNDO a status filter. Without
|
|
225
|
+
* it the only way back to "all sites" is to delete the widget.
|
|
226
|
+
*/
|
|
227
|
+
it("offers an empty status option so the filter can be cleared", () => {
|
|
228
|
+
expect(getDropdownValues("statusFilter")).toEqual([
|
|
229
|
+
"",
|
|
230
|
+
"down",
|
|
231
|
+
"operational",
|
|
232
|
+
]);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("gives every dropdown option a non-empty label", () => {
|
|
236
|
+
for (const arg of getArguments()) {
|
|
237
|
+
for (const option of arg.dropdownOptions || []) {
|
|
238
|
+
expect(String(option.label).trim().length).toBeGreaterThan(0);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("splits the arguments into a Display section and a collapsed Filters section", () => {
|
|
244
|
+
const sectionOf: (
|
|
245
|
+
id: ArgumentIds,
|
|
246
|
+
) => ComponentArgumentSection | undefined = (
|
|
247
|
+
id: ArgumentIds,
|
|
248
|
+
): ComponentArgumentSection | undefined => {
|
|
249
|
+
return getArgumentById(id).section;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
for (const id of [
|
|
253
|
+
"title",
|
|
254
|
+
"viewMode",
|
|
255
|
+
"maxSites",
|
|
256
|
+
"showLabels",
|
|
257
|
+
] as Array<ArgumentIds>) {
|
|
258
|
+
expect(sectionOf(id)?.name).toBe("Display Options");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const id of [
|
|
262
|
+
"networkSiteTypeIds",
|
|
263
|
+
"statusFilter",
|
|
264
|
+
] as Array<ArgumentIds>) {
|
|
265
|
+
expect(sectionOf(id)?.name).toBe("Filters");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
expect(sectionOf("title")?.order).toBeLessThan(
|
|
269
|
+
sectionOf("statusFilter")?.order as number,
|
|
270
|
+
);
|
|
271
|
+
expect(sectionOf("statusFilter")?.defaultCollapsed).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("returns a fresh array each call so a caller cannot mutate the declaration", () => {
|
|
275
|
+
const first: Array<ComponentArgument<DashboardNetworkMapComponent>> =
|
|
276
|
+
getArguments();
|
|
277
|
+
first.pop();
|
|
278
|
+
|
|
279
|
+
expect(getArguments()).toHaveLength(6);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
/*
|
|
284
|
+
* These go through the registry's public lookup rather than the util
|
|
285
|
+
* directly, so forgetting to wire DashboardComponentType.NetworkMap into
|
|
286
|
+
* Common/Utils/Dashboard/Components/Index.ts fails the suite. Nothing else
|
|
287
|
+
* in the codebase asserts that registry is exhaustive.
|
|
288
|
+
*/
|
|
289
|
+
describe("registration in DashboardComponentsUtil", () => {
|
|
290
|
+
it("resolves DashboardComponentType.NetworkMap instead of throwing", () => {
|
|
291
|
+
expect(() => {
|
|
292
|
+
return DashboardComponentsUtil.getComponentSettingsArguments(
|
|
293
|
+
DashboardComponentType.NetworkMap,
|
|
294
|
+
);
|
|
295
|
+
}).not.toThrow();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("returns the Network Map widget's own arguments for that type", () => {
|
|
299
|
+
const fromRegistry: Array<ComponentArgument<DashboardBaseComponent>> =
|
|
300
|
+
DashboardComponentsUtil.getComponentSettingsArguments(
|
|
301
|
+
DashboardComponentType.NetworkMap,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
expect(
|
|
305
|
+
fromRegistry.map(
|
|
306
|
+
(arg: ComponentArgument<DashboardBaseComponent>): unknown => {
|
|
307
|
+
return arg.id;
|
|
308
|
+
},
|
|
309
|
+
),
|
|
310
|
+
).toEqual(
|
|
311
|
+
getArguments().map(
|
|
312
|
+
(arg: ComponentArgument<DashboardNetworkMapComponent>): unknown => {
|
|
313
|
+
return arg.id;
|
|
314
|
+
},
|
|
315
|
+
),
|
|
316
|
+
);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("still throws for a component type that is not registered", () => {
|
|
320
|
+
expect(() => {
|
|
321
|
+
return DashboardComponentsUtil.getComponentSettingsArguments(
|
|
322
|
+
"NotARealWidget" as DashboardComponentType,
|
|
323
|
+
);
|
|
324
|
+
}).toThrow();
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The window a scheduled status page report covers.
|
|
3
|
+
*
|
|
4
|
+
* The reported bug: "is there a way to have the reports be sent out monthly,
|
|
5
|
+
* i.e. to show July 1st - July 31st?" - it was not, because the window was
|
|
6
|
+
* always "the last N days counted back from whenever the cron fired", so a
|
|
7
|
+
* monthly report sent on 1 Aug covered 2 Jul - 1 Aug and never lined up with a
|
|
8
|
+
* calendar month. These tests pin the calendar option that fixes it, the
|
|
9
|
+
* timezone the boundaries are resolved in, and the rolling behaviour that
|
|
10
|
+
* existing status pages keep.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
14
|
+
import EventInterval from "../../../Types/Events/EventInterval";
|
|
15
|
+
import Recurring from "../../../Types/Events/Recurring";
|
|
16
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
17
|
+
import StatusPageReportPeriodType from "../../../Types/StatusPage/StatusPageReportPeriodType";
|
|
18
|
+
import Timezone from "../../../Types/Timezone";
|
|
19
|
+
import StatusPageReportPeriodUtil, {
|
|
20
|
+
StatusPageReportPeriod,
|
|
21
|
+
} from "../../../Utils/StatusPage/ReportPeriod";
|
|
22
|
+
import { describe, expect, test } from "@jest/globals";
|
|
23
|
+
|
|
24
|
+
function every(intervalType: EventInterval, intervalCount: number): Recurring {
|
|
25
|
+
const recurring: Recurring = new Recurring();
|
|
26
|
+
recurring.intervalType = intervalType;
|
|
27
|
+
recurring.intervalCount = new PositiveNumber(intervalCount);
|
|
28
|
+
return recurring;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function iso(date: Date): string {
|
|
32
|
+
return date.toISOString();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("StatusPageReportPeriodUtil.getReportPeriod", () => {
|
|
36
|
+
describe("the previous calendar period", () => {
|
|
37
|
+
test("a monthly report sent in August covers all of July", () => {
|
|
38
|
+
const period: StatusPageReportPeriod =
|
|
39
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
40
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
41
|
+
reportRecurringInterval: every(EventInterval.Month, 1),
|
|
42
|
+
timezone: Timezone.UTC,
|
|
43
|
+
sentAt: OneUptimeDate.fromString("2026-08-01T09:00:00.000Z"),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(iso(period.startDate)).toBe("2026-07-01T00:00:00.000Z");
|
|
47
|
+
expect(iso(period.endDate)).toBe("2026-07-31T23:59:59.999Z");
|
|
48
|
+
expect(period.periodName).toBe("July 2026");
|
|
49
|
+
expect(period.reportDates).toBe("Jul 1, 2026 - Jul 31, 2026");
|
|
50
|
+
expect(period.numberOfDays).toBe(31);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("still covers July when the email goes out later in August", () => {
|
|
54
|
+
/*
|
|
55
|
+
* The period is the last WHOLE one, not "the month before this instant",
|
|
56
|
+
* so moving the send time inside August must not slide the window.
|
|
57
|
+
*/
|
|
58
|
+
const period: StatusPageReportPeriod =
|
|
59
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
60
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
61
|
+
reportRecurringInterval: every(EventInterval.Month, 1),
|
|
62
|
+
timezone: Timezone.UTC,
|
|
63
|
+
sentAt: OneUptimeDate.fromString("2026-08-27T16:45:00.000Z"),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(iso(period.startDate)).toBe("2026-07-01T00:00:00.000Z");
|
|
67
|
+
expect(period.periodName).toBe("July 2026");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("resolves the month boundary in the report timezone, not the server's", () => {
|
|
71
|
+
/*
|
|
72
|
+
* 1 Aug 03:00 UTC is still 31 Jul in New York, so a status page reporting
|
|
73
|
+
* in New York has not finished July yet and must report June.
|
|
74
|
+
*/
|
|
75
|
+
const period: StatusPageReportPeriod =
|
|
76
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
77
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
78
|
+
reportRecurringInterval: every(EventInterval.Month, 1),
|
|
79
|
+
timezone: Timezone.AmericaNew_York,
|
|
80
|
+
sentAt: OneUptimeDate.fromString("2026-08-01T03:00:00.000Z"),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// 1 Jun 00:00 EDT and 30 Jun 23:59:59.999 EDT, both UTC-4.
|
|
84
|
+
expect(iso(period.startDate)).toBe("2026-06-01T04:00:00.000Z");
|
|
85
|
+
expect(iso(period.endDate)).toBe("2026-07-01T03:59:59.999Z");
|
|
86
|
+
expect(period.periodName).toBe("June 2026");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("a quarterly report covers the three whole months before it", () => {
|
|
90
|
+
const period: StatusPageReportPeriod =
|
|
91
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
92
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
93
|
+
reportRecurringInterval: every(EventInterval.Month, 3),
|
|
94
|
+
timezone: Timezone.UTC,
|
|
95
|
+
sentAt: OneUptimeDate.fromString("2026-10-01T09:00:00.000Z"),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(iso(period.startDate)).toBe("2026-07-01T00:00:00.000Z");
|
|
99
|
+
expect(iso(period.endDate)).toBe("2026-09-30T23:59:59.999Z");
|
|
100
|
+
// Three months have no single name a reader knows, so it stays a range.
|
|
101
|
+
expect(period.periodName).toBe("Jul 1, 2026 - Sep 30, 2026");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("a weekly report covers Monday to Sunday", () => {
|
|
105
|
+
// Wednesday 5 Aug 2026.
|
|
106
|
+
const period: StatusPageReportPeriod =
|
|
107
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
108
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
109
|
+
reportRecurringInterval: every(EventInterval.Week, 1),
|
|
110
|
+
timezone: Timezone.UTC,
|
|
111
|
+
sentAt: OneUptimeDate.fromString("2026-08-05T09:00:00.000Z"),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Monday 27 Jul through Sunday 2 Aug.
|
|
115
|
+
expect(iso(period.startDate)).toBe("2026-07-27T00:00:00.000Z");
|
|
116
|
+
expect(iso(period.endDate)).toBe("2026-08-02T23:59:59.999Z");
|
|
117
|
+
expect(period.periodName).toBe("the week of Jul 27, 2026");
|
|
118
|
+
expect(period.numberOfDays).toBe(7);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("a daily report covers yesterday", () => {
|
|
122
|
+
const period: StatusPageReportPeriod =
|
|
123
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
124
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
125
|
+
reportRecurringInterval: every(EventInterval.Day, 1),
|
|
126
|
+
timezone: Timezone.UTC,
|
|
127
|
+
sentAt: OneUptimeDate.fromString("2026-08-05T09:00:00.000Z"),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(iso(period.startDate)).toBe("2026-08-04T00:00:00.000Z");
|
|
131
|
+
expect(iso(period.endDate)).toBe("2026-08-04T23:59:59.999Z");
|
|
132
|
+
expect(period.periodName).toBe("Aug 4, 2026");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("a yearly report covers the previous calendar year", () => {
|
|
136
|
+
const period: StatusPageReportPeriod =
|
|
137
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
138
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
139
|
+
reportRecurringInterval: every(EventInterval.Year, 1),
|
|
140
|
+
timezone: Timezone.UTC,
|
|
141
|
+
sentAt: OneUptimeDate.fromString("2026-01-01T00:05:00.000Z"),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(iso(period.startDate)).toBe("2025-01-01T00:00:00.000Z");
|
|
145
|
+
expect(iso(period.endDate)).toBe("2025-12-31T23:59:59.999Z");
|
|
146
|
+
expect(period.periodName).toBe("2025");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("falls back to a daily period when no frequency is configured", () => {
|
|
150
|
+
const period: StatusPageReportPeriod =
|
|
151
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
152
|
+
periodType: StatusPageReportPeriodType.PreviousCalendarPeriod,
|
|
153
|
+
timezone: Timezone.UTC,
|
|
154
|
+
sentAt: OneUptimeDate.fromString("2026-08-05T09:00:00.000Z"),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Recurring's own default is every 1 day.
|
|
158
|
+
expect(iso(period.startDate)).toBe("2026-08-04T00:00:00.000Z");
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("the rolling window", () => {
|
|
163
|
+
test("ends when the report is sent and runs back the configured days", () => {
|
|
164
|
+
const period: StatusPageReportPeriod =
|
|
165
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
166
|
+
periodType: StatusPageReportPeriodType.Rolling,
|
|
167
|
+
reportDataInDays: 30,
|
|
168
|
+
timezone: Timezone.UTC,
|
|
169
|
+
sentAt: OneUptimeDate.fromString("2026-07-30T09:00:00.000Z"),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(iso(period.startDate)).toBe("2026-06-30T09:00:00.000Z");
|
|
173
|
+
expect(iso(period.endDate)).toBe("2026-07-30T09:00:00.000Z");
|
|
174
|
+
expect(period.periodName).toBe("the last 30 days");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("keeps the reportDates string custom email templates were written against", () => {
|
|
178
|
+
const period: StatusPageReportPeriod =
|
|
179
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
180
|
+
periodType: StatusPageReportPeriodType.Rolling,
|
|
181
|
+
reportDataInDays: 30,
|
|
182
|
+
timezone: Timezone.UTC,
|
|
183
|
+
sentAt: OneUptimeDate.fromString("2026-07-30T09:00:00.000Z"),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(period.reportDates).toBe("30 days (Jun 30, 2026 - Jul 30, 2026)");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("is what a status page with no period configured gets", () => {
|
|
190
|
+
// Every column is null on status pages created before this setting existed.
|
|
191
|
+
const period: StatusPageReportPeriod =
|
|
192
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
193
|
+
sentAt: OneUptimeDate.fromString("2026-07-30T09:00:00.000Z"),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
expect(iso(period.startDate)).toBe("2026-06-30T09:00:00.000Z");
|
|
197
|
+
expect(period.numberOfDays).toBe(
|
|
198
|
+
StatusPageReportPeriodUtil.DEFAULT_REPORT_DATA_IN_DAYS,
|
|
199
|
+
);
|
|
200
|
+
expect(period.timezone).toBe(Timezone.UTC);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("refuses a zero, negative or non-numeric day count", () => {
|
|
204
|
+
for (const badValue of [0, -5, NaN, undefined]) {
|
|
205
|
+
const period: StatusPageReportPeriod =
|
|
206
|
+
StatusPageReportPeriodUtil.getReportPeriod({
|
|
207
|
+
periodType: StatusPageReportPeriodType.Rolling,
|
|
208
|
+
reportDataInDays: badValue as number,
|
|
209
|
+
sentAt: OneUptimeDate.fromString("2026-07-30T09:00:00.000Z"),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
expect(period.numberOfDays).toBe(
|
|
213
|
+
StatusPageReportPeriodUtil.DEFAULT_REPORT_DATA_IN_DAYS,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
});
|