@oneuptime/common 11.7.3 → 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/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/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/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 +8 -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/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/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/BaseModelTableSearchLabels.test.tsx +397 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +427 -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/Types/Date.ts +15 -6
- 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/Modal/Modal.tsx +16 -60
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
- package/UI/Components/ModelTable/BaseModelTable.tsx +116 -67
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +150 -47
- package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -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/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/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/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 +8 -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/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/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 +69 -33
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +73 -32
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- 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/StatusPage/ResourceGroupSection.js +73 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
- 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/package.json +1 -1
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import DatabaseService from "../../../Server/Services/DatabaseService";
|
|
2
|
+
import LogDropFilter from "../../../Models/DatabaseModels/LogDropFilter";
|
|
3
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
4
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
5
|
+
import { getJestSpyOn } from "../../Spy";
|
|
6
|
+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Contract under test — `atomicAddToColumnsByIdWithoutHooks`, the primitive
|
|
10
|
+
* the drop-filter counter flush is built on.
|
|
11
|
+
*
|
|
12
|
+
* Neither existing helper could express this write:
|
|
13
|
+
*
|
|
14
|
+
* - `updateColumnsByIdWithoutHooks` sets literals, so a counter bump would
|
|
15
|
+
* have to be a read-modify-write, which loses concurrent writers'
|
|
16
|
+
* increments (several ingest replicas flush the same filter row).
|
|
17
|
+
* - `getRepository().increment()` goes through TypeORM's
|
|
18
|
+
* UpdateQueryBuilder, which always appends `version = version + 1`. An
|
|
19
|
+
* ingest-driven bump would then fight the optimistic lock on a row a
|
|
20
|
+
* human may be editing in the dashboard — and it cannot set a second
|
|
21
|
+
* column (`lastDroppedAt`) in the same statement anyway.
|
|
22
|
+
*
|
|
23
|
+
* So this issues one raw parameterized UPDATE. The tests below pin the three
|
|
24
|
+
* properties that make it safe to run from the ingest path: it adds rather
|
|
25
|
+
* than assigns, it does not touch `version`, and identifiers come from entity
|
|
26
|
+
* metadata rather than from the caller.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const FILTER_ID: ObjectID = new ObjectID(
|
|
30
|
+
"11111111-1111-4111-8111-111111111111",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
interface CapturedQuery {
|
|
34
|
+
sql: string;
|
|
35
|
+
params: Array<unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("DatabaseService.atomicAddToColumnsByIdWithoutHooks", () => {
|
|
39
|
+
let service: DatabaseService<LogDropFilter>;
|
|
40
|
+
let captured: Array<CapturedQuery>;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
jest.restoreAllMocks();
|
|
44
|
+
captured = [];
|
|
45
|
+
|
|
46
|
+
service = new DatabaseService<LogDropFilter>(LogDropFilter);
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* `getRepository()` needs a live Postgres connection, so the whole
|
|
50
|
+
* repository is stood in for — the same approach the other
|
|
51
|
+
* DatabaseService unit tests take.
|
|
52
|
+
*
|
|
53
|
+
* The stand-in models the two things this method actually reads from
|
|
54
|
+
* TypeORM: metadata (to resolve property names to real column names, and
|
|
55
|
+
* to find the primary key and updatedAt columns) and the driver's
|
|
56
|
+
* persist-value hook. Column names are deliberately spelled out here
|
|
57
|
+
* rather than derived, so if a model rename ever silently changed the
|
|
58
|
+
* generated SQL, these assertions would still be checking the names the
|
|
59
|
+
* migration actually created.
|
|
60
|
+
*/
|
|
61
|
+
const columns: Record<string, string> = {
|
|
62
|
+
droppedCount: "droppedCount",
|
|
63
|
+
lastDroppedAt: "lastDroppedAt",
|
|
64
|
+
sortOrder: "sortOrder",
|
|
65
|
+
_id: "_id",
|
|
66
|
+
updatedAt: "updatedAt",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
getJestSpyOn(service, "getRepository").mockReturnValue({
|
|
70
|
+
metadata: {
|
|
71
|
+
tableName: "LogDropFilter",
|
|
72
|
+
findColumnWithPropertyName: (
|
|
73
|
+
propertyName: string,
|
|
74
|
+
): { databaseName: string } | undefined => {
|
|
75
|
+
return columns[propertyName]
|
|
76
|
+
? { databaseName: columns[propertyName]! }
|
|
77
|
+
: undefined;
|
|
78
|
+
},
|
|
79
|
+
updateDateColumn: { databaseName: "updatedAt" },
|
|
80
|
+
primaryColumns: [{ databaseName: "_id" }],
|
|
81
|
+
},
|
|
82
|
+
manager: {
|
|
83
|
+
connection: {
|
|
84
|
+
driver: {
|
|
85
|
+
// Identity persist: these tests are about the SQL, not coercion.
|
|
86
|
+
preparePersistentValue: (value: unknown): unknown => {
|
|
87
|
+
return value;
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
query: async (
|
|
92
|
+
sql: string,
|
|
93
|
+
params: Array<unknown>,
|
|
94
|
+
): Promise<Array<unknown>> => {
|
|
95
|
+
captured.push({ sql, params });
|
|
96
|
+
return [];
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
} as any);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("adds to the column instead of assigning it", async () => {
|
|
103
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
104
|
+
id: FILTER_ID,
|
|
105
|
+
add: { droppedCount: 42 },
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(captured).toHaveLength(1);
|
|
109
|
+
expect(captured[0]!.sql).toContain(
|
|
110
|
+
`"droppedCount" = COALESCE("droppedCount", 0) +`,
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
* COALESCE matters for rows that predate the column, or any future nullable
|
|
116
|
+
* counter: `NULL + 1` is NULL, so without it a counter could stay null
|
|
117
|
+
* forever no matter how many drops happened.
|
|
118
|
+
*/
|
|
119
|
+
it("treats a NULL counter as zero rather than propagating NULL", async () => {
|
|
120
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
121
|
+
id: FILTER_ID,
|
|
122
|
+
add: { droppedCount: 1 },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(captured[0]!.sql).toContain("COALESCE");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("binds the delta as a parameter, not as inline SQL", async () => {
|
|
129
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
130
|
+
id: FILTER_ID,
|
|
131
|
+
add: { droppedCount: 7 },
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(captured[0]!.params).toContain(7);
|
|
135
|
+
expect(captured[0]!.sql).not.toContain("7");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("sets literal columns in the same statement as the add", async () => {
|
|
139
|
+
const when: Date = new Date("2026-07-29T05:14:03.000Z");
|
|
140
|
+
|
|
141
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
142
|
+
id: FILTER_ID,
|
|
143
|
+
add: { droppedCount: 3 },
|
|
144
|
+
set: { lastDroppedAt: when } as any,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(captured).toHaveLength(1);
|
|
148
|
+
expect(captured[0]!.sql).toContain(`"droppedCount" = COALESCE(`);
|
|
149
|
+
expect(captured[0]!.sql).toContain(`"lastDroppedAt" =`);
|
|
150
|
+
expect(captured[0]!.params).toContain(when);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* The reason this helper exists rather than using increment(). A bumped
|
|
155
|
+
* `version` on an ingest write would make the next dashboard save fail its
|
|
156
|
+
* optimistic-lock check for no reason the user could understand.
|
|
157
|
+
*/
|
|
158
|
+
it("never bumps the optimistic-lock version column", async () => {
|
|
159
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
160
|
+
id: FILTER_ID,
|
|
161
|
+
add: { droppedCount: 1 },
|
|
162
|
+
set: { lastDroppedAt: new Date() } as any,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
expect(captured[0]!.sql).not.toContain("version");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("refreshes updatedAt so the row does not look stale", async () => {
|
|
169
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
170
|
+
id: FILTER_ID,
|
|
171
|
+
add: { droppedCount: 1 },
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(captured[0]!.sql).toContain(`"updatedAt" = CURRENT_TIMESTAMP`);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("scopes the update to the one row by primary key", async () => {
|
|
178
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
179
|
+
id: FILTER_ID,
|
|
180
|
+
add: { droppedCount: 1 },
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
expect(captured[0]!.sql).toMatch(/WHERE "_id" = \$\d+$/);
|
|
184
|
+
expect(captured[0]!.params[captured[0]!.params.length - 1]).toBe(
|
|
185
|
+
FILTER_ID.toString(),
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("targets the model's real table", async () => {
|
|
190
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
191
|
+
id: FILTER_ID,
|
|
192
|
+
add: { droppedCount: 1 },
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(captured[0]!.sql).toContain(`UPDATE "LogDropFilter"`);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("adds to several columns in one statement", async () => {
|
|
199
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
200
|
+
id: FILTER_ID,
|
|
201
|
+
add: { droppedCount: 2, sortOrder: 1 } as any,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(captured).toHaveLength(1);
|
|
205
|
+
expect(captured[0]!.sql).toContain(`"droppedCount" = COALESCE(`);
|
|
206
|
+
expect(captured[0]!.sql).toContain(`"sortOrder" = COALESCE(`);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("rejects inputs it cannot execute safely", () => {
|
|
210
|
+
it("requires an id", async () => {
|
|
211
|
+
await expect(
|
|
212
|
+
service.atomicAddToColumnsByIdWithoutHooks({
|
|
213
|
+
id: undefined as unknown as ObjectID,
|
|
214
|
+
add: { droppedCount: 1 },
|
|
215
|
+
}),
|
|
216
|
+
).rejects.toThrow(BadDataException);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
* Identifiers come from entity metadata, never from the caller, so an
|
|
221
|
+
* unknown property is a programming error rather than an injection
|
|
222
|
+
* vector — but it must fail loudly instead of silently writing nothing.
|
|
223
|
+
*/
|
|
224
|
+
it("rejects a column that does not exist on the model", async () => {
|
|
225
|
+
await expect(
|
|
226
|
+
service.atomicAddToColumnsByIdWithoutHooks({
|
|
227
|
+
id: FILTER_ID,
|
|
228
|
+
add: { notAColumn: 1 } as any,
|
|
229
|
+
}),
|
|
230
|
+
).rejects.toThrow(/unknown column/);
|
|
231
|
+
expect(captured).toHaveLength(0);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("rejects a non-numeric delta", async () => {
|
|
235
|
+
for (const bad of ["5", null, undefined, {}, NaN, Infinity]) {
|
|
236
|
+
await expect(
|
|
237
|
+
service.atomicAddToColumnsByIdWithoutHooks({
|
|
238
|
+
id: FILTER_ID,
|
|
239
|
+
add: { droppedCount: bad } as any,
|
|
240
|
+
}),
|
|
241
|
+
).rejects.toThrow(/finite number/);
|
|
242
|
+
}
|
|
243
|
+
expect(captured).toHaveLength(0);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("rejects a SQL-expression value in the literal set", async () => {
|
|
247
|
+
await expect(
|
|
248
|
+
service.atomicAddToColumnsByIdWithoutHooks({
|
|
249
|
+
id: FILTER_ID,
|
|
250
|
+
add: { droppedCount: 1 },
|
|
251
|
+
set: {
|
|
252
|
+
lastDroppedAt: () => {
|
|
253
|
+
return "NOW()";
|
|
254
|
+
},
|
|
255
|
+
} as any,
|
|
256
|
+
}),
|
|
257
|
+
).rejects.toThrow(/SQL-expression values are not supported/);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("issues no statement at all when there is nothing to write", async () => {
|
|
261
|
+
await service.atomicAddToColumnsByIdWithoutHooks({
|
|
262
|
+
id: FILTER_ID,
|
|
263
|
+
add: {},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(captured).toHaveLength(0);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import DatabaseService from "../../../Server/Services/DatabaseService";
|
|
2
|
+
import ModelPermission from "../../../Server/Types/Database/Permissions/Index";
|
|
3
|
+
import UpdateBy from "../../../Server/Types/Database/UpdateBy";
|
|
4
|
+
import Probe from "../../../Models/DatabaseModels/Probe";
|
|
5
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
6
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
7
|
+
import { getJestSpyOn } from "../../Spy";
|
|
8
|
+
import {
|
|
9
|
+
afterEach,
|
|
10
|
+
beforeEach,
|
|
11
|
+
describe,
|
|
12
|
+
expect,
|
|
13
|
+
it,
|
|
14
|
+
jest,
|
|
15
|
+
} from "@jest/globals";
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* A customer's workflow PUT a flat body to /api/workflow-variable/<id>, so
|
|
19
|
+
* BaseAPI read `body["data"]` as undefined and handed DatabaseService an empty
|
|
20
|
+
* update payload. _updateBy still ran its internal find, matched the row, wrote
|
|
21
|
+
* no columns at all, and returned the MATCHED count (1). Every caller - the API
|
|
22
|
+
* included - reads that number as "rows written", so the request answered 200
|
|
23
|
+
* while the database was untouched, and the guard that exists precisely for
|
|
24
|
+
* this ("numberOfDocsAffected === 0") could never fire.
|
|
25
|
+
*
|
|
26
|
+
* The fix: when the sanitized update data carries no columns, _updateBy skips
|
|
27
|
+
* the find entirely and reports 0. These tests pin that down, and pin down that
|
|
28
|
+
* it did not change anything about updates that DO carry columns.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type ProbeUpdateData = UpdateBy<Probe>["data"];
|
|
32
|
+
|
|
33
|
+
const EMPTY_DATA: ProbeUpdateData = {} as ProbeUpdateData;
|
|
34
|
+
|
|
35
|
+
const TEST_ID: string = "550e8400-e29b-41d4-a716-446655440021";
|
|
36
|
+
const OTHER_ID: string = "550e8400-e29b-41d4-a716-446655440022";
|
|
37
|
+
|
|
38
|
+
const matchedRow: (id: string) => Probe = (id: string): Probe => {
|
|
39
|
+
const probe: Probe = new Probe();
|
|
40
|
+
probe._id = id;
|
|
41
|
+
return probe;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe("DatabaseService._updateBy when the update carries no columns", () => {
|
|
45
|
+
let service: DatabaseService<Probe>;
|
|
46
|
+
let findBySpy: jest.SpyInstance;
|
|
47
|
+
let saveMock: MockFunction;
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
jest.restoreAllMocks();
|
|
51
|
+
jest.clearAllMocks();
|
|
52
|
+
|
|
53
|
+
service = new DatabaseService<Probe>(Probe);
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Only the two surfaces that need a live Postgres are stubbed - the
|
|
57
|
+
* internal find (`_findBy`) and the repository behind `save()`. Mocking
|
|
58
|
+
* the TypeORM repository itself is impractical here because `_findBy`
|
|
59
|
+
* builds real query metadata against a DataSource that does not exist in
|
|
60
|
+
* a unit test; stubbing `_findBy` keeps the mock at the exact boundary the
|
|
61
|
+
* fix is about (was the find run at all?) while leaving every line of
|
|
62
|
+
* _updateBy under test - sanitizing, the hooks and the return arithmetic.
|
|
63
|
+
*
|
|
64
|
+
* The stub deliberately returns a MATCHING row, because that is what a
|
|
65
|
+
* real database does here: the row exists and the query finds it, there is
|
|
66
|
+
* simply nothing to write to it. That is the whole trap - so if _updateBy
|
|
67
|
+
* ever runs this find again for an empty payload, every expectation below
|
|
68
|
+
* reports the old, wrong answer (1 row "changed") instead of 0.
|
|
69
|
+
*/
|
|
70
|
+
findBySpy = getJestSpyOn(service as any, "_findBy").mockResolvedValue([
|
|
71
|
+
matchedRow(TEST_ID),
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
saveMock = getJestMockFunction();
|
|
75
|
+
saveMock.mockImplementation((item: unknown) => {
|
|
76
|
+
return Promise.resolve(item);
|
|
77
|
+
});
|
|
78
|
+
getJestSpyOn(service, "getRepository").mockReturnValue({
|
|
79
|
+
save: saveMock,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// The permission layer is not what these tests are about, and needs a DB.
|
|
83
|
+
getJestSpyOn(
|
|
84
|
+
ModelPermission,
|
|
85
|
+
"checkUpdateQueryPermissions",
|
|
86
|
+
).mockImplementation((_modelType: unknown, query: unknown) => {
|
|
87
|
+
return Promise.resolve(query);
|
|
88
|
+
});
|
|
89
|
+
getJestSpyOn(
|
|
90
|
+
ModelPermission,
|
|
91
|
+
"checkUpdatePermissionByModel",
|
|
92
|
+
).mockResolvedValue(undefined);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
jest.restoreAllMocks();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("reports zero rows changed when updateOneBy is handed an empty payload", async () => {
|
|
100
|
+
const numberOfDocsAffected: number = await service.updateOneBy({
|
|
101
|
+
query: { _id: TEST_ID } as any,
|
|
102
|
+
data: EMPTY_DATA,
|
|
103
|
+
props: { isRoot: true },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(numberOfDocsAffected).toBe(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("reports zero rows changed when updateBy is handed an empty payload", async () => {
|
|
110
|
+
const numberOfDocsAffected: number = await service.updateBy({
|
|
111
|
+
query: {} as any,
|
|
112
|
+
data: EMPTY_DATA,
|
|
113
|
+
props: { isRoot: true },
|
|
114
|
+
limit: 10,
|
|
115
|
+
skip: 0,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(numberOfDocsAffected).toBe(0);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("reports zero rows changed when the empty payload arrives through updateOneById, which is the path the API takes", async () => {
|
|
122
|
+
const numberOfDocsAffected: number = await service.updateOneById({
|
|
123
|
+
id: new ObjectID(TEST_ID),
|
|
124
|
+
data: EMPTY_DATA,
|
|
125
|
+
props: { isRoot: true },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* This is the exact number BaseAPI.updateItem checks before answering
|
|
130
|
+
* 200, so a no-column update can no longer be reported as a save.
|
|
131
|
+
*/
|
|
132
|
+
expect(numberOfDocsAffected).toBe(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("never runs the find, because a payload with no columns cannot change any row", async () => {
|
|
136
|
+
await service.updateOneBy({
|
|
137
|
+
query: { _id: TEST_ID } as any,
|
|
138
|
+
data: EMPTY_DATA,
|
|
139
|
+
props: { isRoot: true },
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("never writes anything, so the count it reports and the rows it touched agree", async () => {
|
|
146
|
+
/*
|
|
147
|
+
* The semantic in prose: an update that writes no columns must not be
|
|
148
|
+
* reported as a successful write. Nothing is saved AND the caller is told
|
|
149
|
+
* zero - previously the second half of that sentence was a lie, because
|
|
150
|
+
* `items.length` counted rows the query MATCHED, not rows it changed, and
|
|
151
|
+
* the row matched here (see the `_findBy` stub above).
|
|
152
|
+
*/
|
|
153
|
+
const numberOfDocsAffected: number = await service.updateOneBy({
|
|
154
|
+
query: { _id: TEST_ID } as any,
|
|
155
|
+
data: EMPTY_DATA,
|
|
156
|
+
props: { isRoot: true },
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
expect(saveMock).not.toHaveBeenCalled();
|
|
160
|
+
expect(numberOfDocsAffected).toBe(0);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("still calls onUpdateSuccess, with an empty list of ids, so hooks behave exactly as they do when the query matches nothing", async () => {
|
|
164
|
+
const onUpdateSuccessSpy: jest.SpyInstance = getJestSpyOn(
|
|
165
|
+
service as any,
|
|
166
|
+
"onUpdateSuccess",
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
await service.updateOneBy({
|
|
170
|
+
query: { _id: TEST_ID } as any,
|
|
171
|
+
data: EMPTY_DATA,
|
|
172
|
+
props: { isRoot: true, ignoreHooks: false },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
/*
|
|
176
|
+
* Skipping the find must not quietly skip the hooks too: subclasses
|
|
177
|
+
* override onUpdateSuccess and some of them rely on being told about
|
|
178
|
+
* every update attempt. The id list is empty because nothing was written -
|
|
179
|
+
* before the fix this hook was handed the id of the row that merely
|
|
180
|
+
* matched, telling every subclass a write had happened.
|
|
181
|
+
*/
|
|
182
|
+
expect(onUpdateSuccessSpy).toHaveBeenCalledTimes(1);
|
|
183
|
+
expect(onUpdateSuccessSpy.mock.calls[0]![1]).toEqual([]);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("DatabaseService._updateBy when the update does carry columns", () => {
|
|
188
|
+
let service: DatabaseService<Probe>;
|
|
189
|
+
let findBySpy: jest.SpyInstance;
|
|
190
|
+
let saveMock: MockFunction;
|
|
191
|
+
|
|
192
|
+
beforeEach(() => {
|
|
193
|
+
jest.restoreAllMocks();
|
|
194
|
+
jest.clearAllMocks();
|
|
195
|
+
|
|
196
|
+
service = new DatabaseService<Probe>(Probe);
|
|
197
|
+
|
|
198
|
+
findBySpy = getJestSpyOn(service as any, "_findBy").mockResolvedValue([
|
|
199
|
+
matchedRow(TEST_ID),
|
|
200
|
+
matchedRow(OTHER_ID),
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
saveMock = getJestMockFunction();
|
|
204
|
+
saveMock.mockImplementation((item: unknown) => {
|
|
205
|
+
return Promise.resolve(item);
|
|
206
|
+
});
|
|
207
|
+
getJestSpyOn(service, "getRepository").mockReturnValue({
|
|
208
|
+
save: saveMock,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
getJestSpyOn(
|
|
212
|
+
ModelPermission,
|
|
213
|
+
"checkUpdateQueryPermissions",
|
|
214
|
+
).mockImplementation((_modelType: unknown, query: unknown) => {
|
|
215
|
+
return Promise.resolve(query);
|
|
216
|
+
});
|
|
217
|
+
getJestSpyOn(
|
|
218
|
+
ModelPermission,
|
|
219
|
+
"checkUpdatePermissionByModel",
|
|
220
|
+
).mockResolvedValue(undefined);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
afterEach(() => {
|
|
224
|
+
jest.restoreAllMocks();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("still runs the find and still reports the number of rows it matched", async () => {
|
|
228
|
+
const numberOfDocsAffected: number = await service.updateBy({
|
|
229
|
+
query: {} as any,
|
|
230
|
+
data: { name: "renamed-probe" } as ProbeUpdateData,
|
|
231
|
+
props: { isRoot: true },
|
|
232
|
+
limit: 10,
|
|
233
|
+
skip: 0,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
/*
|
|
237
|
+
* This assertion is also what proves the `_findBy` spy in the sibling
|
|
238
|
+
* describe is wired to the method _updateBy really calls: the same spy,
|
|
239
|
+
* on the same private method, IS exercised as soon as the payload has a
|
|
240
|
+
* column. "not.toHaveBeenCalled()" over there is therefore meaningful.
|
|
241
|
+
*/
|
|
242
|
+
expect(findBySpy).toHaveBeenCalledTimes(1);
|
|
243
|
+
expect(numberOfDocsAffected).toBe(2);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("still writes every row the find returned", async () => {
|
|
247
|
+
await service.updateOneBy({
|
|
248
|
+
query: { _id: TEST_ID } as any,
|
|
249
|
+
data: { name: "renamed-probe" } as ProbeUpdateData,
|
|
250
|
+
props: { isRoot: true },
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
expect(saveMock).toHaveBeenCalledTimes(2);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("still calls onUpdateSuccess with the ids of the rows it updated", async () => {
|
|
257
|
+
const onUpdateSuccessSpy: jest.SpyInstance = getJestSpyOn(
|
|
258
|
+
service as any,
|
|
259
|
+
"onUpdateSuccess",
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
await service.updateOneBy({
|
|
263
|
+
query: { _id: TEST_ID } as any,
|
|
264
|
+
data: { name: "renamed-probe" } as ProbeUpdateData,
|
|
265
|
+
props: { isRoot: true, ignoreHooks: false },
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const updatedIds: Array<ObjectID> = onUpdateSuccessSpy.mock
|
|
269
|
+
.calls[0]![1] as Array<ObjectID>;
|
|
270
|
+
|
|
271
|
+
expect(
|
|
272
|
+
updatedIds.map((id: ObjectID) => {
|
|
273
|
+
return id.toString();
|
|
274
|
+
}),
|
|
275
|
+
).toEqual([TEST_ID, OTHER_ID]);
|
|
276
|
+
});
|
|
277
|
+
});
|