@oneuptime/common 11.7.2 → 11.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
- package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Index.ts +5 -0
- package/Models/DatabaseModels/LogDropFilter.ts +89 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
- package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
- package/Models/DatabaseModels/TableView.ts +40 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
- package/Server/API/AlertAPI.ts +32 -17
- package/Server/API/BaseAPI.ts +31 -2
- package/Server/API/BaseAnalyticsAPI.ts +36 -2
- package/Server/API/CommonAPI.ts +57 -0
- package/Server/API/IncidentAPI.ts +60 -33
- package/Server/API/IncidentEpisodeAPI.ts +31 -16
- package/Server/API/MonitorAPI.ts +30 -0
- package/Server/API/MonitorTemplateAPI.ts +8 -0
- package/Server/API/ProbeAPI.ts +196 -15
- package/Server/API/ProjectAPI.ts +79 -1
- package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
- package/Server/API/StatusPageAPI.ts +74 -37
- package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AlertEpisodeService.ts +47 -0
- package/Server/Services/AlertService.ts +125 -0
- package/Server/Services/BillingService.ts +98 -0
- package/Server/Services/DatabaseService.ts +184 -20
- package/Server/Services/IncidentEpisodeService.ts +47 -0
- package/Server/Services/IncidentService.ts +123 -0
- package/Server/Services/IncidentTemplateService.ts +123 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/LogDropFilterService.ts +133 -0
- package/Server/Services/MonitorProbeService.ts +94 -4
- package/Server/Services/MonitorService.ts +48 -43
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
- package/Server/Services/ProbeService.ts +66 -0
- package/Server/Services/ProjectService.ts +131 -7
- package/Server/Services/RecommendationDismissalService.ts +52 -0
- package/Server/Services/RunbookAgentJobService.ts +68 -7
- package/Server/Services/ScheduledMaintenanceService.ts +103 -0
- package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
- package/Server/Services/StatusPageGroupService.ts +186 -0
- package/Server/Services/TraceDropFilterService.ts +133 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
- package/Server/Types/Workflow/Components/API/Get.ts +12 -0
- package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
- package/Server/Types/Workflow/Components/API/Post.ts +12 -0
- package/Server/Types/Workflow/Components/API/Put.ts +13 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
- package/Server/Utils/DropFilterValidation.ts +127 -0
- package/Server/Utils/Errors.ts +5 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
- package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
- package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
- package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
- package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
- package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
- package/Tests/Server/API/ProbeAPI.test.ts +422 -53
- package/Tests/Server/API/ProjectAPI.test.ts +180 -0
- package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
- package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
- package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
- package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
- package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
- package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
- package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
- package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
- package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
- package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
- package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
- package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
- package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
- package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
- package/Tests/Types/DateUserTimezone.test.ts +52 -0
- package/Tests/Types/JSONFunctions.test.ts +103 -1
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
- package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
- package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
- package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
- package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
- package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
- package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
- package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
- package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
- package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
- package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
- package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
- package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
- package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
- package/Tests/UI/Components/InfoCard.test.tsx +358 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
- package/Tests/UI/Components/Modal.test.tsx +43 -5
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +999 -0
- package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
- package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
- package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
- package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
- package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
- package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
- package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
- package/Tests/Utils/UserPreferences.test.ts +563 -0
- package/Types/Date.ts +15 -6
- package/Types/JSONFunctions.ts +7 -2
- package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
- package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
- package/Types/Monitor/MonitorStep.ts +10 -1
- package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
- package/Types/Permission.ts +45 -0
- package/Types/Recommendation/RecommendationType.ts +20 -0
- package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
- package/Types/Runbook/RunbookStepTimeout.ts +202 -0
- package/Types/Telemetry/DropFilterSampling.ts +92 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
- package/UI/Components/Accordion/Accordion.tsx +10 -2
- package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
- package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
- package/UI/Components/Error/PageError.tsx +0 -4
- package/UI/Components/ErrorBoundary.tsx +252 -15
- package/UI/Components/InfoCard/InfoCard.tsx +58 -1
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
- package/UI/Components/MasterPage/MasterPage.tsx +65 -0
- package/UI/Components/Modal/Modal.tsx +16 -60
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
- package/UI/Components/ModelTable/BaseModelTable.tsx +503 -71
- package/UI/Components/ModelTable/Column.ts +17 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +523 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
- package/UI/Components/ModelTable/TableView.tsx +24 -2
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
- package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
- package/UI/Components/SideMenu/SideMenu.tsx +24 -4
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
- package/UI/Components/Table/Table.tsx +14 -1
- package/UI/Components/Table/TableRow.tsx +180 -175
- package/UI/Components/Table/Types/Column.ts +2 -0
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
- package/UI/Utils/DropdownAlignment.ts +162 -0
- package/UI/Utils/TableFilterUrlState.ts +59 -20
- package/Utils/API.ts +106 -2
- package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
- package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
- package/Utils/StatusPage/GroupTree.ts +307 -0
- package/Utils/StatusPage/ResourceUptime.ts +90 -17
- package/Utils/UserPreferences.ts +53 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.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/LogDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TableView.js +40 -0
- package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +22 -13
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +22 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +42 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +43 -26
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorAPI.js +23 -0
- package/build/dist/Server/API/MonitorAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
- package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +131 -15
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +62 -3
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +61 -33
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.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/Services/AlertEpisodeService.js +39 -0
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +95 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +71 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +140 -17
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +100 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
- package/build/dist/Server/Services/IncidentTemplateService.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/LogDropFilterService.js +93 -0
- package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +73 -5
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +34 -39
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +64 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +103 -7
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
- package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
- package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
- package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
- package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
- package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
- package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
- package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
- package/build/dist/Server/Utils/Errors.js +3 -0
- package/build/dist/Server/Utils/Errors.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
- package/build/dist/Types/Date.js +14 -4
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +8 -3
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +6 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
- package/build/dist/Types/Permission.js +40 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
- package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
- package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Error/PageError.js +1 -3
- package/build/dist/UI/Components/Error/PageError.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +145 -11
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +4 -32
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +308 -40
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +230 -0
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
- package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +15 -2
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +11 -6
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
- package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
- package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
- package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
- package/build/dist/Utils/API.js +39 -2
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
- package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/UserPreferences.js +33 -0
- package/build/dist/Utils/UserPreferences.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import BaseAPI from "../../../Server/API/BaseAPI";
|
|
2
|
+
import DatabaseService from "../../../Server/Services/DatabaseService";
|
|
3
|
+
import {
|
|
4
|
+
ExpressResponse,
|
|
5
|
+
OneUptimeRequest,
|
|
6
|
+
} from "../../../Server/Utils/Express";
|
|
7
|
+
import Response from "../../../Server/Utils/Response";
|
|
8
|
+
import { mockRouter } from "./Helpers";
|
|
9
|
+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
10
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
11
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
12
|
+
import Probe from "../../../Models/DatabaseModels/Probe";
|
|
13
|
+
import { getJestSpyOn } from "../../Spy";
|
|
14
|
+
|
|
15
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
16
|
+
return {
|
|
17
|
+
getRouter: () => {
|
|
18
|
+
return mockRouter;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
24
|
+
return {
|
|
25
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
26
|
+
sendEntityResponse: jest.fn(),
|
|
27
|
+
sendEntityArrayResponse: jest.fn(),
|
|
28
|
+
sendJsonObjectResponse: jest.fn(),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* PUT /<model>/:id takes its payload wrapped as { "data": { ...columns... } }.
|
|
34
|
+
*
|
|
35
|
+
* A caller that sent the columns flat instead - no "data" key - got HTTP 200
|
|
36
|
+
* and an empty body, and nothing was written. body["data"] was undefined,
|
|
37
|
+
* deserializing that handed back {}, and an update carrying no columns still
|
|
38
|
+
* matched the row, so the "matched nothing" guard never fired. The fields were
|
|
39
|
+
* dropped on the floor and the caller was told the write succeeded.
|
|
40
|
+
*
|
|
41
|
+
* A customer hit this rotating an API token from a Workflow: every run
|
|
42
|
+
* reported success and every run read back the previous token.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const TEST_ID: string = "550e8400-e29b-41d4-a716-446655440009";
|
|
46
|
+
|
|
47
|
+
describe("BaseAPI.updateItem payload validation", () => {
|
|
48
|
+
let service: DatabaseService<Probe>;
|
|
49
|
+
let api: BaseAPI<Probe, DatabaseService<Probe>>;
|
|
50
|
+
let response: ExpressResponse;
|
|
51
|
+
|
|
52
|
+
type RequestWithBodyFunction = (body: JSONObject) => OneUptimeRequest;
|
|
53
|
+
|
|
54
|
+
const requestWithBody: RequestWithBodyFunction = (
|
|
55
|
+
body: JSONObject,
|
|
56
|
+
): OneUptimeRequest => {
|
|
57
|
+
return {
|
|
58
|
+
params: { id: TEST_ID },
|
|
59
|
+
body: body,
|
|
60
|
+
headers: {},
|
|
61
|
+
} as unknown as OneUptimeRequest;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
jest.restoreAllMocks();
|
|
66
|
+
jest.clearAllMocks();
|
|
67
|
+
|
|
68
|
+
service = new DatabaseService<Probe>(Probe);
|
|
69
|
+
api = new BaseAPI<Probe, DatabaseService<Probe>>(Probe, service);
|
|
70
|
+
|
|
71
|
+
response = {
|
|
72
|
+
status: jest.fn().mockReturnThis(),
|
|
73
|
+
json: jest.fn().mockReturnThis(),
|
|
74
|
+
send: jest.fn().mockReturnThis(),
|
|
75
|
+
} as unknown as ExpressResponse;
|
|
76
|
+
|
|
77
|
+
getJestSpyOn(service, "updateOneById").mockResolvedValue(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("when the payload is not wrapped in a data object", () => {
|
|
81
|
+
/*
|
|
82
|
+
* This is the customer's exact request body, minus the token value. It is
|
|
83
|
+
* the case the whole fix exists for.
|
|
84
|
+
*/
|
|
85
|
+
it("refuses a flat body that puts the columns at the top level", async () => {
|
|
86
|
+
const request: OneUptimeRequest = requestWithBody({
|
|
87
|
+
content: "new-token-value",
|
|
88
|
+
name: "Airflow-Token",
|
|
89
|
+
description: "Token to connect to Airflow.",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
93
|
+
BadDataException,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
97
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("names the shape it wants so the caller can fix the request", async () => {
|
|
101
|
+
const request: OneUptimeRequest = requestWithBody({
|
|
102
|
+
content: "new-token-value",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(/data/);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("refuses an empty body", async () => {
|
|
109
|
+
await expect(
|
|
110
|
+
api.updateItem(requestWithBody({}), response),
|
|
111
|
+
).rejects.toThrow(BadDataException);
|
|
112
|
+
|
|
113
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("refuses a null data payload", async () => {
|
|
117
|
+
await expect(
|
|
118
|
+
api.updateItem(requestWithBody({ data: null }), response),
|
|
119
|
+
).rejects.toThrow(BadDataException);
|
|
120
|
+
|
|
121
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/*
|
|
125
|
+
* A string used to survive deserialization as its own index map -
|
|
126
|
+
* "abc" became { "0": "a", "1": "b", "2": "c" } - and went to the service
|
|
127
|
+
* as three nonsense columns.
|
|
128
|
+
*/
|
|
129
|
+
it("refuses a string data payload rather than indexing it into columns", async () => {
|
|
130
|
+
await expect(
|
|
131
|
+
api.updateItem(
|
|
132
|
+
requestWithBody({ data: "abc" } as JSONObject),
|
|
133
|
+
response,
|
|
134
|
+
),
|
|
135
|
+
).rejects.toThrow(BadDataException);
|
|
136
|
+
|
|
137
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("refuses a numeric data payload", async () => {
|
|
141
|
+
await expect(
|
|
142
|
+
api.updateItem(requestWithBody({ data: 42 } as JSONObject), response),
|
|
143
|
+
).rejects.toThrow(BadDataException);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("refuses a boolean data payload", async () => {
|
|
147
|
+
await expect(
|
|
148
|
+
api.updateItem(requestWithBody({ data: true } as JSONObject), response),
|
|
149
|
+
).rejects.toThrow(BadDataException);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("refuses an empty array data payload", async () => {
|
|
153
|
+
await expect(
|
|
154
|
+
api.updateItem(requestWithBody({ data: [] } as JSONObject), response),
|
|
155
|
+
).rejects.toThrow(BadDataException);
|
|
156
|
+
|
|
157
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("refuses an array of objects, which updates one row not many", async () => {
|
|
161
|
+
await expect(
|
|
162
|
+
api.updateItem(
|
|
163
|
+
requestWithBody({ data: [{ name: "renamed-probe" }] } as JSONObject),
|
|
164
|
+
response,
|
|
165
|
+
),
|
|
166
|
+
).rejects.toThrow(BadDataException);
|
|
167
|
+
|
|
168
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("when the payload carries no fields to write", () => {
|
|
173
|
+
it("refuses an empty data object", async () => {
|
|
174
|
+
await expect(
|
|
175
|
+
api.updateItem(requestWithBody({ data: {} }), response),
|
|
176
|
+
).rejects.toThrow(BadDataException);
|
|
177
|
+
|
|
178
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
179
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("says there is nothing to update", async () => {
|
|
183
|
+
await expect(
|
|
184
|
+
api.updateItem(requestWithBody({ data: {} }), response),
|
|
185
|
+
).rejects.toThrow(/No fields to update/i);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* _id, createdAt and updatedAt are stripped before the write, so a payload
|
|
190
|
+
* of nothing but those leaves zero columns. The emptiness check has to run
|
|
191
|
+
* after the strip to catch it.
|
|
192
|
+
*/
|
|
193
|
+
it("refuses a payload of only the fields it strips", async () => {
|
|
194
|
+
const request: OneUptimeRequest = requestWithBody({
|
|
195
|
+
data: {
|
|
196
|
+
_id: TEST_ID,
|
|
197
|
+
createdAt: "2026-07-28T20:58:55.000Z",
|
|
198
|
+
updatedAt: "2026-07-28T20:58:55.000Z",
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
203
|
+
/No fields to update/i,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("when the payload is well formed", () => {
|
|
211
|
+
it("writes the update and reports success", async () => {
|
|
212
|
+
const request: OneUptimeRequest = requestWithBody({
|
|
213
|
+
data: { name: "renamed-probe" },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await api.updateItem(request, response);
|
|
217
|
+
|
|
218
|
+
expect(service.updateOneById).toHaveBeenCalledTimes(1);
|
|
219
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
|
|
220
|
+
request,
|
|
221
|
+
response,
|
|
222
|
+
);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("passes the payload through to the service untouched", async () => {
|
|
226
|
+
await api.updateItem(
|
|
227
|
+
requestWithBody({ data: { name: "renamed-probe" } }),
|
|
228
|
+
response,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
expect(service.updateOneById).toHaveBeenCalledWith(
|
|
232
|
+
expect.objectContaining({
|
|
233
|
+
data: { name: "renamed-probe" },
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
/*
|
|
239
|
+
* The strip has to survive the new guard: a real client payload echoes
|
|
240
|
+
* _id/createdAt/updatedAt back and only the remaining columns may be
|
|
241
|
+
* written.
|
|
242
|
+
*/
|
|
243
|
+
it("still strips _id, createdAt and updatedAt from a payload that also has real fields", async () => {
|
|
244
|
+
await api.updateItem(
|
|
245
|
+
requestWithBody({
|
|
246
|
+
data: {
|
|
247
|
+
_id: TEST_ID,
|
|
248
|
+
createdAt: "2026-07-28T20:58:55.000Z",
|
|
249
|
+
updatedAt: "2026-07-28T20:58:55.000Z",
|
|
250
|
+
name: "renamed-probe",
|
|
251
|
+
},
|
|
252
|
+
}),
|
|
253
|
+
response,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
expect(service.updateOneById).toHaveBeenCalledWith(
|
|
257
|
+
expect.objectContaining({
|
|
258
|
+
data: { name: "renamed-probe" },
|
|
259
|
+
}),
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* PUT /:id, POST /:id/update-item and GET /:id/update-item all land on this
|
|
266
|
+
* one handler, so none of them can be left answering 200 to a payload that
|
|
267
|
+
* writes nothing. GET carries no body at all.
|
|
268
|
+
*/
|
|
269
|
+
describe("across every route that reaches updateItem", () => {
|
|
270
|
+
it("refuses the flat body on the POST update-item route", async () => {
|
|
271
|
+
const request: OneUptimeRequest = requestWithBody({
|
|
272
|
+
content: "new-token-value",
|
|
273
|
+
name: "Airflow-Token",
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
277
|
+
BadDataException,
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("refuses the bodyless GET update-item route instead of quietly writing nothing", async () => {
|
|
282
|
+
await expect(
|
|
283
|
+
api.updateItem(requestWithBody({}), response),
|
|
284
|
+
).rejects.toThrow(BadDataException);
|
|
285
|
+
|
|
286
|
+
expect(service.updateOneById).not.toHaveBeenCalled();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("registers all three update routes", () => {
|
|
290
|
+
const crudPath: string = new Probe().getCrudApiPath()!.toString();
|
|
291
|
+
|
|
292
|
+
expect(mockRouter.match("PUT", `${crudPath}/:id`)).toBeTruthy();
|
|
293
|
+
expect(
|
|
294
|
+
mockRouter.match("POST", `${crudPath}/:id/update-item`),
|
|
295
|
+
).toBeTruthy();
|
|
296
|
+
expect(
|
|
297
|
+
mockRouter.match("GET", `${crudPath}/:id/update-item`),
|
|
298
|
+
).toBeTruthy();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
/*
|
|
303
|
+
* The original symptom, stated as an assertion: the customer saw
|
|
304
|
+
* "response-status":200,"response-body":{} on every run while the variable
|
|
305
|
+
* never changed.
|
|
306
|
+
*/
|
|
307
|
+
it("never reports success for a request that would write nothing", async () => {
|
|
308
|
+
const request: OneUptimeRequest = requestWithBody({
|
|
309
|
+
content: "new-token-value",
|
|
310
|
+
name: "Airflow-Token",
|
|
311
|
+
description: "Token to connect to Airflow.",
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
315
|
+
BadDataException,
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import "../TestingUtils/Init";
|
|
2
|
+
import BaseAnalyticsAPI from "../../../Server/API/BaseAnalyticsAPI";
|
|
3
|
+
import AnalyticsDatabaseService from "../../../Server/Services/AnalyticsDatabaseService";
|
|
4
|
+
import MonitorLog from "../../../Models/AnalyticsModels/MonitorLog";
|
|
5
|
+
import {
|
|
6
|
+
ExpressResponse,
|
|
7
|
+
OneUptimeRequest,
|
|
8
|
+
} from "../../../Server/Utils/Express";
|
|
9
|
+
import Response from "../../../Server/Utils/Response";
|
|
10
|
+
import { mockRouter } from "./Helpers";
|
|
11
|
+
import { getJestSpyOn } from "../../Spy";
|
|
12
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
13
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
14
|
+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
15
|
+
|
|
16
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
17
|
+
return {
|
|
18
|
+
getRouter: () => {
|
|
19
|
+
return mockRouter;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
25
|
+
return {
|
|
26
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
27
|
+
sendEntityResponse: jest.fn(),
|
|
28
|
+
sendEntityArrayResponse: jest.fn(),
|
|
29
|
+
sendJsonObjectResponse: jest.fn(),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* PUT /<analytics-model>/:id answered 200 with an empty body while writing
|
|
35
|
+
* nothing at all.
|
|
36
|
+
*
|
|
37
|
+
* The wire contract is a WRAPPED payload - { "data": { ...columns... } } - but
|
|
38
|
+
* nothing enforced it. A client that sent the columns flat (the shape a
|
|
39
|
+
* hand-written Workflow naturally produces) had body["data"] === undefined,
|
|
40
|
+
* which deserialized into an empty model, which matched the row and updated
|
|
41
|
+
* zero columns. The request looked successful and the edit silently vanished.
|
|
42
|
+
*
|
|
43
|
+
* These tests pin the guards in BaseAnalyticsAPI.updateItem: the payload must
|
|
44
|
+
* be a wrapped plain object, and it must still carry at least one updatable
|
|
45
|
+
* field once _id / createdAt / updatedAt are stripped.
|
|
46
|
+
*
|
|
47
|
+
* Note the emptiness check has to count fields on the RAW body, not on the
|
|
48
|
+
* hydrated model: an analytics model instance always carries its own internal
|
|
49
|
+
* properties (data, _tableColumns), so Object.keys() on the instance is never
|
|
50
|
+
* empty and could never detect a no-op update.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
const TEST_ID: string = "550e8400-e29b-41d4-a716-446655440011";
|
|
54
|
+
|
|
55
|
+
describe("BaseAnalyticsAPI.updateItem payload validation", () => {
|
|
56
|
+
let service: AnalyticsDatabaseService<MonitorLog>;
|
|
57
|
+
let api: BaseAnalyticsAPI<MonitorLog, AnalyticsDatabaseService<MonitorLog>>;
|
|
58
|
+
let response: ExpressResponse;
|
|
59
|
+
|
|
60
|
+
type MakeRequestFunction = (body: unknown) => OneUptimeRequest;
|
|
61
|
+
|
|
62
|
+
const makeRequest: MakeRequestFunction = (
|
|
63
|
+
body: unknown,
|
|
64
|
+
): OneUptimeRequest => {
|
|
65
|
+
return {
|
|
66
|
+
params: { id: TEST_ID },
|
|
67
|
+
body: body as JSONObject,
|
|
68
|
+
headers: {},
|
|
69
|
+
} as unknown as OneUptimeRequest;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
jest.restoreAllMocks();
|
|
74
|
+
jest.clearAllMocks();
|
|
75
|
+
|
|
76
|
+
service = new AnalyticsDatabaseService<MonitorLog>({
|
|
77
|
+
modelType: MonitorLog,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
api = new BaseAnalyticsAPI<
|
|
81
|
+
MonitorLog,
|
|
82
|
+
AnalyticsDatabaseService<MonitorLog>
|
|
83
|
+
>(MonitorLog, service);
|
|
84
|
+
|
|
85
|
+
/*
|
|
86
|
+
* Stubbed so a payload that WRONGLY gets past the guards still fails the
|
|
87
|
+
* assertion rather than trying to reach ClickHouse.
|
|
88
|
+
*/
|
|
89
|
+
getJestSpyOn(service, "updateBy").mockResolvedValue(undefined);
|
|
90
|
+
|
|
91
|
+
response = {
|
|
92
|
+
status: jest.fn().mockReturnThis(),
|
|
93
|
+
json: jest.fn().mockReturnThis(),
|
|
94
|
+
send: jest.fn().mockReturnThis(),
|
|
95
|
+
} as unknown as ExpressResponse;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("rejects a flat body that forgot to wrap its columns in data", async () => {
|
|
99
|
+
const request: OneUptimeRequest = makeRequest({
|
|
100
|
+
logBody: { message: "flat-payload" },
|
|
101
|
+
monitorId: TEST_ID,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
105
|
+
BadDataException,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("tells the caller the payload has to look like { data: { ... } }", async () => {
|
|
112
|
+
const request: OneUptimeRequest = makeRequest({
|
|
113
|
+
logBody: { message: "flat-payload" },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(/"data"/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("rejects a completely empty body", async () => {
|
|
120
|
+
const request: OneUptimeRequest = makeRequest({});
|
|
121
|
+
|
|
122
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
123
|
+
BadDataException,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("rejects data: {} and says there are no fields to update", async () => {
|
|
130
|
+
const request: OneUptimeRequest = makeRequest({ data: {} });
|
|
131
|
+
|
|
132
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
133
|
+
BadDataException,
|
|
134
|
+
);
|
|
135
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
136
|
+
/no fields to update/i,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("rejects data: null", async () => {
|
|
143
|
+
const request: OneUptimeRequest = makeRequest({ data: null });
|
|
144
|
+
|
|
145
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
146
|
+
BadDataException,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* A string is iterable and `for (const key in "abc")` yields "0", "1", "2",
|
|
154
|
+
* so a lenient implementation could quietly treat it as an object with
|
|
155
|
+
* numeric keys. Assert both that it is refused AND that nothing about it
|
|
156
|
+
* looked like a field set worth forwarding.
|
|
157
|
+
*/
|
|
158
|
+
it("rejects a string data payload instead of treating it as an object", async () => {
|
|
159
|
+
const request: OneUptimeRequest = makeRequest({ data: "some string" });
|
|
160
|
+
|
|
161
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
162
|
+
BadDataException,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
166
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("rejects an empty array data payload", async () => {
|
|
170
|
+
const request: OneUptimeRequest = makeRequest({ data: [] });
|
|
171
|
+
|
|
172
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
173
|
+
BadDataException,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("rejects an array of objects data payload", async () => {
|
|
180
|
+
const request: OneUptimeRequest = makeRequest({
|
|
181
|
+
data: [{ logBody: { message: "one" } }, { logBody: { message: "two" } }],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
185
|
+
BadDataException,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("rejects a numeric data payload", async () => {
|
|
192
|
+
const request: OneUptimeRequest = makeRequest({ data: 42 });
|
|
193
|
+
|
|
194
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
195
|
+
BadDataException,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("rejects a boolean data payload", async () => {
|
|
202
|
+
const request: OneUptimeRequest = makeRequest({ data: true });
|
|
203
|
+
|
|
204
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
205
|
+
BadDataException,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* _id / createdAt / updatedAt are stripped before the update is issued, so a
|
|
213
|
+
* payload made only of those would write nothing. This proves the emptiness
|
|
214
|
+
* check runs on the STRIPPED field set, not on the raw key count.
|
|
215
|
+
*/
|
|
216
|
+
it("rejects a payload of only _id, createdAt and updatedAt because nothing updatable survives stripping", async () => {
|
|
217
|
+
const request: OneUptimeRequest = makeRequest({
|
|
218
|
+
data: {
|
|
219
|
+
_id: TEST_ID,
|
|
220
|
+
createdAt: "2026-07-29T00:00:00.000Z",
|
|
221
|
+
updatedAt: "2026-07-29T00:00:00.000Z",
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
226
|
+
BadDataException,
|
|
227
|
+
);
|
|
228
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
229
|
+
/no fields to update/i,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
/*
|
|
236
|
+
* No-regression guard: the guards above must not have made the happy path
|
|
237
|
+
* stricter. A correctly wrapped payload with one real column of the model
|
|
238
|
+
* still reaches the service and still reports success.
|
|
239
|
+
*/
|
|
240
|
+
it("still updates and reports success for a correctly wrapped payload", async () => {
|
|
241
|
+
const request: OneUptimeRequest = makeRequest({
|
|
242
|
+
data: { logBody: { message: "wrapped-payload" } },
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await api.updateItem(request, response);
|
|
246
|
+
|
|
247
|
+
expect(service.updateBy).toHaveBeenCalledTimes(1);
|
|
248
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
|
|
249
|
+
request,
|
|
250
|
+
response,
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("keeps the _id in the update query and out of the written columns", async () => {
|
|
255
|
+
const request: OneUptimeRequest = makeRequest({
|
|
256
|
+
data: { _id: TEST_ID, logBody: { message: "wrapped-payload" } },
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await api.updateItem(request, response);
|
|
260
|
+
|
|
261
|
+
expect(service.updateBy).toHaveBeenCalledTimes(1);
|
|
262
|
+
|
|
263
|
+
const updateBy: { query: JSONObject; data: MonitorLog } = (
|
|
264
|
+
service.updateBy as unknown as jest.Mock
|
|
265
|
+
).mock.calls[0]![0] as { query: JSONObject; data: MonitorLog };
|
|
266
|
+
|
|
267
|
+
expect(updateBy.query["_id"]).toBe(TEST_ID);
|
|
268
|
+
expect(updateBy.data.getColumnValue("logBody")).toEqual({
|
|
269
|
+
message: "wrapped-payload",
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
/*
|
|
274
|
+
* Named for the original symptom: the customer's Workflow PUT a flat body,
|
|
275
|
+
* got HTTP 200 with an empty object back, and nothing was written. The 200
|
|
276
|
+
* came from sendEmptySuccessResponse, so that is the exact call that must
|
|
277
|
+
* never happen for an unwrapped payload.
|
|
278
|
+
*/
|
|
279
|
+
it("never reports an empty success for the flat body that silently wrote nothing", async () => {
|
|
280
|
+
const request: OneUptimeRequest = makeRequest({
|
|
281
|
+
content: "<token>",
|
|
282
|
+
name: "Airflow-Token",
|
|
283
|
+
description: "token used by the airflow workflow",
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await expect(api.updateItem(request, response)).rejects.toThrow(
|
|
287
|
+
BadDataException,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
291
|
+
expect(service.updateBy).not.toHaveBeenCalled();
|
|
292
|
+
});
|
|
293
|
+
});
|