@oneuptime/common 12.0.23 → 12.0.25
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/Span.ts +101 -0
- package/Models/DatabaseModels/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +27 -7
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/BaseAPI.ts +0 -24
- package/Server/API/SlackAPI.ts +0 -2
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Middleware/SlackAuthorization.ts +96 -18
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +205 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +181 -0
- package/Server/Utils/Telemetry/LlmSpan.ts +46 -0
- package/Server/Utils/Workspace/Slack/Actions/Auth.ts +0 -12
- package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
- package/Server/Views/Partials/Head.ejs +2 -1
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
- package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
- package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
- package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
- package/Tests/App/Dashboard/LlmCallsTableIdentity.test.tsx +322 -0
- package/Tests/App/Dashboard/LlmOverview.test.tsx +335 -0
- package/Tests/App/Dashboard/LlmSpanDisplay.test.ts +391 -0
- package/Tests/App/Dashboard/LlmUsageBreakdown.test.tsx +1007 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
- package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
- package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
- package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
- package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
- package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
- package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
- package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
- package/Tests/Server/API/BaseAPI.test.ts +41 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +9 -16
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
- package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
- package/Tests/Server/Middleware/SlackAuthorization.test.ts +262 -5
- package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +387 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +386 -0
- package/Tests/Server/Utils/Telemetry/LlmSpan.test.ts +804 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
- package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
- package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
- package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +584 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
- package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
- package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
- package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
- package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
- package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
- package/Tests/UI/Components/SideOver.test.tsx +1 -1
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
- package/Tests/UI/Components/Toggle.test.tsx +25 -0
- package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +770 -0
- package/Types/Code/YamlSyntax.ts +396 -0
- package/Types/Exception/ExceptionCode.ts +1 -0
- package/Types/Exception/PayloadTooLargeException.ts +8 -0
- package/Types/Marketing/Attribution.ts +10 -0
- package/Types/Marketing/MarketingEvent.ts +4 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmConventions.ts +255 -0
- package/Types/Telemetry/LlmMetricConventions.ts +329 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
- package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
- package/UI/Components/Detail/Detail.tsx +10 -0
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
- package/UI/Components/Forms/Fields/FormField.tsx +31 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
- package/UI/Components/Forms/Validation.ts +46 -0
- package/UI/Components/Input/Input.tsx +3 -0
- package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
- package/UI/Components/ModelTable/Column.ts +8 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
- package/UI/Components/SideOver/SideOver.tsx +5 -5
- package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
- package/UI/Components/StatusPage/ResourceList.tsx +197 -38
- package/UI/Components/Toggle/Toggle.tsx +9 -1
- package/UI/Components/Types/FieldType.ts +1 -0
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +287 -0
- package/build/dist/Models/AnalyticsModels/Span.js +89 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +27 -7
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +3 -19
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +0 -2
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js +58 -7
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +204 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +151 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js +24 -1
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +0 -10
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
- package/build/dist/Types/Code/YamlSyntax.js +212 -0
- package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +10 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmConventions.js +236 -0
- package/build/dist/Types/Telemetry/LlmConventions.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmMetricConventions.js +290 -0
- package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +9 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +27 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Types/FieldType.js +1 -0
- package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +175 -0
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/Tests/UI/Components/Toast.test.tsx +0 -197
- package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
- package/UI/Components/Toast/Toast.tsx +0 -189
- package/UI/Components/Toast/ToastInit.tsx +0 -110
- package/build/dist/UI/Components/Toast/Toast.js +0 -123
- package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
- package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
|
@@ -2,6 +2,7 @@ import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResourc
|
|
|
2
2
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
3
3
|
import StatusPageResourceExplorerUtil from "../../../Utils/StatusPage/ResourceExplorer";
|
|
4
4
|
import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
|
|
5
|
+
import CheckboxElement from "../Checkbox/Checkbox";
|
|
5
6
|
import Icon from "../Icon/Icon";
|
|
6
7
|
import MoreMenu from "../MoreMenu/MoreMenu";
|
|
7
8
|
import MoreMenuItem from "../MoreMenu/MoreMenuItem";
|
|
@@ -40,6 +41,28 @@ export interface ComponentProps {
|
|
|
40
41
|
*/
|
|
41
42
|
isReorderable: boolean;
|
|
42
43
|
|
|
44
|
+
/*
|
|
45
|
+
* Whether the rows carry checkboxes at all.
|
|
46
|
+
*
|
|
47
|
+
* Off for anybody who cannot remove a resource. Removing is the only thing a
|
|
48
|
+
* selection is for here, so without it the column would be a row of controls
|
|
49
|
+
* that lead nowhere - and it would cost every row 28px of the width its
|
|
50
|
+
* monitor's name is drawn in.
|
|
51
|
+
*/
|
|
52
|
+
isSelectable: boolean;
|
|
53
|
+
/* Held by the caller: a selection outlives this list's re-renders. */
|
|
54
|
+
selectedResourceIds: Set<string>;
|
|
55
|
+
/*
|
|
56
|
+
* The state of the box at the top of the list, computed by the caller over
|
|
57
|
+
* everything this list was handed - which is the whole point of it being
|
|
58
|
+
* computed there. A filtered list's "select all" means the rows the search
|
|
59
|
+
* matched, and rows held back behind "Show more" are matched rows too.
|
|
60
|
+
*/
|
|
61
|
+
isAllSelected: boolean;
|
|
62
|
+
isSelectionIndeterminate: boolean;
|
|
63
|
+
onToggleResourceSelected: (statusPageResource: StatusPageResource) => void;
|
|
64
|
+
onToggleAllSelected: (isSelected: boolean) => void;
|
|
65
|
+
|
|
43
66
|
onEdit: (statusPageResource: StatusPageResource) => void;
|
|
44
67
|
onDelete: (statusPageResource: StatusPageResource) => void;
|
|
45
68
|
onShowId: (statusPageResource: StatusPageResource) => void;
|
|
@@ -90,6 +113,12 @@ export interface ComponentProps {
|
|
|
90
113
|
* rather than one copy per breakpoint.
|
|
91
114
|
*/
|
|
92
115
|
const GRIP_COLUMN_CLASS_NAME: string = "w-5 flex-shrink-0";
|
|
116
|
+
/*
|
|
117
|
+
* Wider than the 16px box it holds: the checkbox draws an empty label beside
|
|
118
|
+
* itself with a 12px gutter, and a narrower column would push the grip out from
|
|
119
|
+
* under the header's own box.
|
|
120
|
+
*/
|
|
121
|
+
const SELECT_COLUMN_CLASS_NAME: string = "flex w-7 flex-shrink-0 items-center";
|
|
93
122
|
/*
|
|
94
123
|
* Held back on a phone, where 24px of number costs a fifth of what is left for
|
|
95
124
|
* the monitor's name and the rows are already in the order it describes.
|
|
@@ -239,6 +268,49 @@ const ResourceList: FunctionComponent<ComponentProps> = (
|
|
|
239
268
|
);
|
|
240
269
|
};
|
|
241
270
|
|
|
271
|
+
type RenderSelectFunction = (
|
|
272
|
+
statusPageResource: StatusPageResource,
|
|
273
|
+
) => ReactElement;
|
|
274
|
+
|
|
275
|
+
/*
|
|
276
|
+
* One row's checkbox. Nothing is drawn at all when selection is off, rather
|
|
277
|
+
* than an empty column: unlike the grip - which is held open so that typing
|
|
278
|
+
* into the search box does not shift every row sideways - the checkbox column
|
|
279
|
+
* appears and disappears with the operator's permissions, which do not change
|
|
280
|
+
* while they are looking at the list.
|
|
281
|
+
*/
|
|
282
|
+
const renderSelect: RenderSelectFunction = (
|
|
283
|
+
statusPageResource: StatusPageResource,
|
|
284
|
+
): ReactElement => {
|
|
285
|
+
if (!props.isSelectable) {
|
|
286
|
+
return <></>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const resourceId: string | null =
|
|
290
|
+
StatusPageResourceExplorerUtil.getResourceId(statusPageResource);
|
|
291
|
+
const name: string =
|
|
292
|
+
StatusPageResourceExplorerUtil.getResourceName(statusPageResource);
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<span
|
|
296
|
+
className={SELECT_COLUMN_CLASS_NAME}
|
|
297
|
+
data-testid="status-page-resource-row-select"
|
|
298
|
+
>
|
|
299
|
+
<CheckboxElement
|
|
300
|
+
value={Boolean(
|
|
301
|
+
resourceId && props.selectedResourceIds.has(resourceId),
|
|
302
|
+
)}
|
|
303
|
+
disabled={!resourceId}
|
|
304
|
+
ariaLabel={`Select ${name}`}
|
|
305
|
+
hoverText={`Select ${name}`}
|
|
306
|
+
onChange={() => {
|
|
307
|
+
props.onToggleResourceSelected(statusPageResource);
|
|
308
|
+
}}
|
|
309
|
+
/>
|
|
310
|
+
</span>
|
|
311
|
+
);
|
|
312
|
+
};
|
|
313
|
+
|
|
242
314
|
type RenderRowBodyFunction = (
|
|
243
315
|
statusPageResource: StatusPageResource,
|
|
244
316
|
index: number,
|
|
@@ -256,6 +328,8 @@ const ResourceList: FunctionComponent<ComponentProps> = (
|
|
|
256
328
|
|
|
257
329
|
return (
|
|
258
330
|
<>
|
|
331
|
+
{renderSelect(statusPageResource)}
|
|
332
|
+
|
|
259
333
|
{dragHandle}
|
|
260
334
|
|
|
261
335
|
{/*
|
|
@@ -347,32 +421,60 @@ const ResourceList: FunctionComponent<ComponentProps> = (
|
|
|
347
421
|
);
|
|
348
422
|
};
|
|
349
423
|
|
|
350
|
-
type GetRowClassNameFunction = (
|
|
351
|
-
isDragging: boolean
|
|
352
|
-
isBusy: boolean
|
|
353
|
-
isLastRow: boolean
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
424
|
+
type GetRowClassNameFunction = (data: {
|
|
425
|
+
isDragging: boolean;
|
|
426
|
+
isBusy: boolean;
|
|
427
|
+
isLastRow: boolean;
|
|
428
|
+
isSelected: boolean;
|
|
429
|
+
}) => string;
|
|
430
|
+
|
|
431
|
+
const getRowClassName: GetRowClassNameFunction = (data: {
|
|
432
|
+
isDragging: boolean;
|
|
433
|
+
isBusy: boolean;
|
|
434
|
+
isLastRow: boolean;
|
|
435
|
+
isSelected: boolean;
|
|
436
|
+
}): string => {
|
|
437
|
+
/*
|
|
438
|
+
* A selected row is filled, so that a selection made near the top of a long
|
|
439
|
+
* list is still visible from the bottom of it - the count in the bulk bar
|
|
440
|
+
* says how many, and this says which.
|
|
441
|
+
*
|
|
442
|
+
* Un-suffixed colour tokens throughout, for the same reason the dragged row
|
|
443
|
+
* uses them: the dark theme remaps those and not the transparent ones, so
|
|
444
|
+
* an opacity modifier leaves a light row in a dark list.
|
|
445
|
+
*/
|
|
446
|
+
const restingClassName: string = data.isSelected
|
|
447
|
+
? "bg-indigo-50 hover:bg-indigo-100"
|
|
448
|
+
: "bg-white hover:bg-gray-50";
|
|
449
|
+
|
|
361
450
|
return `${ROW_CLASS_NAME} ${
|
|
362
|
-
isDragging
|
|
451
|
+
data.isDragging
|
|
363
452
|
? /*
|
|
364
|
-
* bg-indigo-50 rather than bg-indigo-50/70: the dark theme remaps the
|
|
365
|
-
* un-suffixed colour tokens and not the transparent ones, so an
|
|
366
|
-
* opacity modifier leaves a light row in a dark list.
|
|
367
|
-
*
|
|
368
453
|
* A lifted row is rounded on all four corners because it is off the
|
|
369
454
|
* list while it is being dragged; a settled one only rounds where the
|
|
370
455
|
* container does, so its hover fill cannot square off the card's own
|
|
371
456
|
* bottom corners.
|
|
372
457
|
*/
|
|
373
458
|
"rounded-xl bg-indigo-50 shadow-md ring-1 ring-indigo-200"
|
|
374
|
-
:
|
|
375
|
-
} ${isBusy ? "opacity-60" : ""}`;
|
|
459
|
+
: `${restingClassName} ${data.isLastRow ? "rounded-b-xl" : ""}`
|
|
460
|
+
} ${data.isBusy ? "opacity-60" : ""}`;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
type IsResourceSelectedFunction = (
|
|
464
|
+
statusPageResource: StatusPageResource,
|
|
465
|
+
) => boolean;
|
|
466
|
+
|
|
467
|
+
const isResourceSelected: IsResourceSelectedFunction = (
|
|
468
|
+
statusPageResource: StatusPageResource,
|
|
469
|
+
): boolean => {
|
|
470
|
+
if (!props.isSelectable) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const resourceId: string | null =
|
|
475
|
+
StatusPageResourceExplorerUtil.getResourceId(statusPageResource);
|
|
476
|
+
|
|
477
|
+
return Boolean(resourceId && props.selectedResourceIds.has(resourceId));
|
|
376
478
|
};
|
|
377
479
|
|
|
378
480
|
type IsLastRowFunction = (index: number) => boolean;
|
|
@@ -388,32 +490,82 @@ const ResourceList: FunctionComponent<ComponentProps> = (
|
|
|
388
490
|
type RenderHeaderFunction = () => ReactElement;
|
|
389
491
|
|
|
390
492
|
/*
|
|
391
|
-
* Column headings, from sm up
|
|
392
|
-
*
|
|
393
|
-
*
|
|
493
|
+
* Column headings, from sm up, and the box that selects the whole list.
|
|
494
|
+
*
|
|
495
|
+
* The headings are what makes the second column read as "the name visitors
|
|
496
|
+
* see" rather than as a stray subtitle, and they are the only place the
|
|
497
|
+
* position column can be explained at all. Below sm the published name wraps
|
|
498
|
+
* under the monitor instead of sitting beside it, so they would be labelling
|
|
499
|
+
* columns that are not there - and the row is hidden.
|
|
394
500
|
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
501
|
+
* The select-all box is not a heading, though, and a phone is exactly where
|
|
502
|
+
* removing twenty seven monitors one at a time hurts most. So when there is a
|
|
503
|
+
* checkbox column the row is drawn at every width, with the headings held
|
|
504
|
+
* back below sm and the box given a visible label of its own instead.
|
|
505
|
+
*
|
|
506
|
+
* aria-hidden sits on the individual headings rather than on the row, because
|
|
507
|
+
* the row now contains a control: hiding a focusable checkbox from the
|
|
508
|
+
* accessibility tree while leaving it in the tab order is worse than not
|
|
509
|
+
* offering it.
|
|
397
510
|
*/
|
|
398
511
|
const renderHeader: RenderHeaderFunction = (): ReactElement => {
|
|
399
512
|
return (
|
|
400
513
|
<div
|
|
401
|
-
className=
|
|
514
|
+
className={`${
|
|
515
|
+
props.isSelectable ? "flex" : "hidden sm:flex"
|
|
516
|
+
} items-center gap-x-3 rounded-t-xl border-b border-gray-200 bg-gray-50 px-4 py-2 text-[11px] font-semibold uppercase tracking-wide text-gray-500 sm:flex`}
|
|
402
517
|
data-testid="status-page-resource-list-header"
|
|
403
|
-
aria-hidden="true"
|
|
404
518
|
>
|
|
405
|
-
|
|
519
|
+
{props.isSelectable ? (
|
|
520
|
+
<span className={SELECT_COLUMN_CLASS_NAME}>
|
|
521
|
+
<CheckboxElement
|
|
522
|
+
value={props.isAllSelected}
|
|
523
|
+
isIndeterminate={props.isSelectionIndeterminate}
|
|
524
|
+
ariaLabel="Select all resources"
|
|
525
|
+
hoverText="Select all resources"
|
|
526
|
+
dataTestId="status-page-resource-list-select-all"
|
|
527
|
+
onChange={(value: boolean) => {
|
|
528
|
+
props.onToggleAllSelected(value);
|
|
529
|
+
}}
|
|
530
|
+
/>
|
|
531
|
+
</span>
|
|
532
|
+
) : (
|
|
533
|
+
<></>
|
|
534
|
+
)}
|
|
535
|
+
|
|
536
|
+
{props.isSelectable ? (
|
|
537
|
+
<span className="sm:hidden">Select all</span>
|
|
538
|
+
) : (
|
|
539
|
+
<></>
|
|
540
|
+
)}
|
|
541
|
+
|
|
542
|
+
<span
|
|
543
|
+
className={`${GRIP_COLUMN_CLASS_NAME} hidden sm:block`}
|
|
544
|
+
aria-hidden="true"
|
|
545
|
+
/>
|
|
406
546
|
<span
|
|
407
547
|
className={POSITION_COLUMN_CLASS_NAME}
|
|
408
548
|
title="Position on the status page"
|
|
549
|
+
aria-hidden="true"
|
|
409
550
|
>
|
|
410
551
|
#
|
|
411
552
|
</span>
|
|
412
|
-
<span
|
|
413
|
-
|
|
553
|
+
<span
|
|
554
|
+
className={`${MONITOR_COLUMN_CLASS_NAME} hidden sm:block`}
|
|
555
|
+
aria-hidden="true"
|
|
556
|
+
>
|
|
557
|
+
Monitor
|
|
558
|
+
</span>
|
|
559
|
+
<span
|
|
560
|
+
className={`${PUBLISHED_NAME_COLUMN_CLASS_NAME} hidden sm:block`}
|
|
561
|
+
aria-hidden="true"
|
|
562
|
+
>
|
|
414
563
|
Shown on the status page as
|
|
415
564
|
</span>
|
|
416
|
-
<span
|
|
565
|
+
<span
|
|
566
|
+
className={`${ACTIONS_COLUMN_CLASS_NAME} hidden sm:flex`}
|
|
567
|
+
aria-hidden="true"
|
|
568
|
+
/>
|
|
417
569
|
</div>
|
|
418
570
|
);
|
|
419
571
|
};
|
|
@@ -437,11 +589,13 @@ const ResourceList: FunctionComponent<ComponentProps> = (
|
|
|
437
589
|
key={resourceId}
|
|
438
590
|
data-testid="status-page-resource-row"
|
|
439
591
|
data-resource-id={resourceId}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
592
|
+
data-selected={isResourceSelected(statusPageResource)}
|
|
593
|
+
className={getRowClassName({
|
|
594
|
+
isDragging: false,
|
|
595
|
+
isBusy: props.busyResourceId === resourceId,
|
|
596
|
+
isLastRow: isLastRow(index),
|
|
597
|
+
isSelected: isResourceSelected(statusPageResource),
|
|
598
|
+
})}
|
|
445
599
|
>
|
|
446
600
|
{renderRowBody(
|
|
447
601
|
statusPageResource,
|
|
@@ -497,11 +651,16 @@ const ResourceList: FunctionComponent<ComponentProps> = (
|
|
|
497
651
|
{...draggableProvided.draggableProps}
|
|
498
652
|
data-testid="status-page-resource-row"
|
|
499
653
|
data-resource-id={resourceId}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
props.busyResourceId === resourceId,
|
|
503
|
-
isLastRow(index),
|
|
654
|
+
data-selected={isResourceSelected(
|
|
655
|
+
statusPageResource,
|
|
504
656
|
)}
|
|
657
|
+
className={getRowClassName({
|
|
658
|
+
isDragging: snapshot.isDragging,
|
|
659
|
+
isBusy: props.busyResourceId === resourceId,
|
|
660
|
+
isLastRow: isLastRow(index),
|
|
661
|
+
isSelected:
|
|
662
|
+
isResourceSelected(statusPageResource),
|
|
663
|
+
})}
|
|
505
664
|
>
|
|
506
665
|
{renderRowBody(
|
|
507
666
|
statusPageResource,
|
|
@@ -78,8 +78,16 @@ const Toggle: FunctionComponent<ComponentProps> = (
|
|
|
78
78
|
if (props.onBlur) {
|
|
79
79
|
props.onBlur();
|
|
80
80
|
}
|
|
81
|
+
/*
|
|
82
|
+
* handleChange already calls props.onChange. Calling it a second
|
|
83
|
+
* time here ran every consumer's handler twice per click, which an
|
|
84
|
+
* idempotent handler never notices and a non-idempotent one cannot
|
|
85
|
+
* survive. The monitor criteria switches are one click away from
|
|
86
|
+
* that: they seed a blank incident / alert row on the way on, and
|
|
87
|
+
* only the "is the array still empty" guard around that seed kept
|
|
88
|
+
* the second pass from adding a second row.
|
|
89
|
+
*/
|
|
81
90
|
handleChange(!isChecked);
|
|
82
|
-
props.onChange(!isChecked);
|
|
83
91
|
}}
|
|
84
92
|
onFocus={() => {
|
|
85
93
|
if (props.onFocus) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OneUptimeDate from "../../Types/Date";
|
|
2
|
+
import { NetworkDeviceMonitoringMethodUtil } from "../../Types/NetworkDevice/NetworkDeviceMonitoringMethod";
|
|
2
3
|
|
|
3
4
|
/*
|
|
4
5
|
* The one place that decides whether a NetworkDevice is reachable.
|
|
@@ -23,6 +24,14 @@ import OneUptimeDate from "../../Types/Date";
|
|
|
23
24
|
* NetworkDevice.isReachable, stamped by the walk pipeline on every ingested
|
|
24
25
|
* walk — and freshness is only a backstop for "the polling pipeline itself
|
|
25
26
|
* stopped", measured generously against the device's own interval.
|
|
27
|
+
*
|
|
28
|
+
* All of which is about a device that gets POLLED. A monitor-backed device
|
|
29
|
+
* (monitoringMethod "Monitor") is never polled at all: no probe, no
|
|
30
|
+
* credentials, no walk. Every poll column on such a row is NULL forever, so
|
|
31
|
+
* the rule above can only ever answer "Pending" for it — which is
|
|
32
|
+
* OneUptime/oneuptime#3392, a correctly bound ping-only device stuck on
|
|
33
|
+
* Pending. Its health comes from the Monitor bound to it, stamped onto
|
|
34
|
+
* NetworkDevice.currentMonitorStatusId, and that is the branch below.
|
|
26
35
|
*/
|
|
27
36
|
|
|
28
37
|
export enum NetworkDeviceReachability {
|
|
@@ -71,6 +80,28 @@ export interface DeviceReachabilityInput {
|
|
|
71
80
|
lastSeenAt?: Date | string | null | undefined;
|
|
72
81
|
// The device's own schedule; sizes the staleness backstop.
|
|
73
82
|
pollingIntervalInMinutes?: number | null | undefined;
|
|
83
|
+
/*
|
|
84
|
+
* How this device's health is established. NULL, empty and anything
|
|
85
|
+
* unrecognised read as SNMP — see NetworkDeviceMonitoringMethodUtil.parse,
|
|
86
|
+
* which is why an omitted value keeps every existing caller on the poll
|
|
87
|
+
* rule unchanged.
|
|
88
|
+
*/
|
|
89
|
+
monitoringMethod?: string | null | undefined;
|
|
90
|
+
/*
|
|
91
|
+
* The OFFLINE end of the device's stamped MonitorStatus row, and only
|
|
92
|
+
* consulted for a monitor-backed device. `undefined` means no monitor has
|
|
93
|
+
* reported yet (nothing bound, or bound and never evaluated), which is a
|
|
94
|
+
* real "Pending" rather than a healthy default.
|
|
95
|
+
*
|
|
96
|
+
* The offline end and not the operational end, because MonitorStatus is a
|
|
97
|
+
* ladder rather than a pair: a "Degraded" row is NEITHER operational nor
|
|
98
|
+
* offline, and the device map already resolves that ladder with
|
|
99
|
+
* `isOfflineState ? down : up`. Reading the operational flag here instead
|
|
100
|
+
* would paint every degraded-but-reachable device red on one surface and
|
|
101
|
+
* green on the other. See DeviceHealthStateUtil, which reads it the same
|
|
102
|
+
* way for the same reason.
|
|
103
|
+
*/
|
|
104
|
+
monitorStatusIsOffline?: boolean | null | undefined;
|
|
74
105
|
}
|
|
75
106
|
|
|
76
107
|
export interface DeviceReachabilityResult {
|
|
@@ -87,6 +118,13 @@ export interface DeviceReachabilityResult {
|
|
|
87
118
|
staleWindowInMinutes: number;
|
|
88
119
|
// Latest of lastPolledAt / lastSeenAt; null when neither is set.
|
|
89
120
|
lastContactAt: Date | null;
|
|
121
|
+
/*
|
|
122
|
+
* True when this verdict came from the bound Monitor rather than from an
|
|
123
|
+
* SNMP poll. Carried so a surface can word its pill and its tooltip for
|
|
124
|
+
* what actually decided the answer — "the last SNMP poll reached this
|
|
125
|
+
* device" is a lie on a device nothing polls.
|
|
126
|
+
*/
|
|
127
|
+
isMonitorBacked: boolean;
|
|
90
128
|
}
|
|
91
129
|
|
|
92
130
|
const MS_PER_MINUTE: number = 60 * 1000;
|
|
@@ -153,6 +191,11 @@ export default class DeviceReachabilityUtil {
|
|
|
153
191
|
staleWindowInMinutes * MS_PER_MINUTE
|
|
154
192
|
: false;
|
|
155
193
|
|
|
194
|
+
const isMonitorBacked: boolean =
|
|
195
|
+
NetworkDeviceMonitoringMethodUtil.isMonitorBacked(
|
|
196
|
+
device.monitoringMethod,
|
|
197
|
+
);
|
|
198
|
+
|
|
156
199
|
const result: (
|
|
157
200
|
status: NetworkDeviceReachability,
|
|
158
201
|
) => DeviceReachabilityResult = (
|
|
@@ -160,12 +203,41 @@ export default class DeviceReachabilityUtil {
|
|
|
160
203
|
): DeviceReachabilityResult => {
|
|
161
204
|
return {
|
|
162
205
|
status: status,
|
|
163
|
-
|
|
206
|
+
/*
|
|
207
|
+
* Staleness is "nothing has polled this device lately", so it can
|
|
208
|
+
* only ever be false for a device nothing polls BY DESIGN. Letting
|
|
209
|
+
* it through would put an amber "check this device's probe" pill on
|
|
210
|
+
* every monitor-backed device forever — and it has no probe.
|
|
211
|
+
*/
|
|
212
|
+
isStale: isMonitorBacked ? false : isStale,
|
|
164
213
|
staleWindowInMinutes: staleWindowInMinutes,
|
|
165
214
|
lastContactAt: lastContactAt,
|
|
215
|
+
isMonitorBacked: isMonitorBacked,
|
|
166
216
|
};
|
|
167
217
|
};
|
|
168
218
|
|
|
219
|
+
/*
|
|
220
|
+
* Monitor-backed: the bound Monitor's verdict IS the device's, and the
|
|
221
|
+
* poll columns below are meaningless for it (they are NULL forever, and
|
|
222
|
+
* on a device switched over from SNMP they are worse than meaningless —
|
|
223
|
+
* they are the last thing a probe found before it stopped asking).
|
|
224
|
+
*
|
|
225
|
+
* No stamped status means Pending, and it is honest in both of the ways
|
|
226
|
+
* it happens: no monitor is bound yet (discovery import creates devices
|
|
227
|
+
* that way on purpose), or one is bound and has not been evaluated yet.
|
|
228
|
+
*/
|
|
229
|
+
if (isMonitorBacked) {
|
|
230
|
+
if (device.monitorStatusIsOffline === true) {
|
|
231
|
+
return result(NetworkDeviceReachability.Down);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (device.monitorStatusIsOffline === false) {
|
|
235
|
+
return result(NetworkDeviceReachability.Up);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result(NetworkDeviceReachability.Pending);
|
|
239
|
+
}
|
|
240
|
+
|
|
169
241
|
/*
|
|
170
242
|
* The probe asked and got nothing. Authoritative however long ago it
|
|
171
243
|
* was — nothing since has said otherwise.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The optional operator-supplied name on a Network Device Discovery Scan.
|
|
5
|
+
*
|
|
6
|
+
* WHY IT EXISTS
|
|
7
|
+
*
|
|
8
|
+
* A scan used to be identified by its target alone — `10.15.128.0-255`,
|
|
9
|
+
* `10.240-249.0-254.220-226`. Half a dozen rows in, nothing on the Discovery
|
|
10
|
+
* Scans list says which one was the router sweep and which one was the switch
|
|
11
|
+
* range for a region; the only way to find out is to open each scan and read
|
|
12
|
+
* the range back against a subnet plan kept somewhere else (OneUptime issue
|
|
13
|
+
* #3391). A name is that missing sentence, stored with the scan rather than in
|
|
14
|
+
* a spreadsheet beside it.
|
|
15
|
+
*
|
|
16
|
+
* It is deliberately OPTIONAL and deliberately NOT unique: it is a label for
|
|
17
|
+
* humans, not an identifier. Every scan that existed before this column did
|
|
18
|
+
* has none, and every surface that shows a name therefore has to keep working
|
|
19
|
+
* when it is absent — which is what getScanLabel() below is for.
|
|
20
|
+
*
|
|
21
|
+
* WHY IT LIVES IN Common
|
|
22
|
+
*
|
|
23
|
+
* The same two questions — "is this name storable?" and "what do I call this
|
|
24
|
+
* scan?" — are asked by the create form (App Dashboard), the write hooks
|
|
25
|
+
* (Common server service), the recurring-scan worker (App Workers) and the
|
|
26
|
+
* sweep itself (Probe). Answering them in one module is what keeps the form's
|
|
27
|
+
* error message identical to the server's, and the probe's log line readable
|
|
28
|
+
* against the row the operator is looking at. Same reason ScanTargetUtil sits
|
|
29
|
+
* next door.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* The parts of a scan that answer "which scan is this?". Structural rather
|
|
34
|
+
* than the model type, so a partially-selected row — the workers and the probe
|
|
35
|
+
* both select two or three columns — satisfies it, and so this module stays
|
|
36
|
+
* importable from the Probe without dragging a database model behind it.
|
|
37
|
+
*
|
|
38
|
+
* Both are nullable: `cidr` is NOT NULL in the database, but a caller that did
|
|
39
|
+
* not select it hands over a row where it is simply missing.
|
|
40
|
+
*/
|
|
41
|
+
export interface DiscoveryScanIdentity {
|
|
42
|
+
name?: string | null | undefined;
|
|
43
|
+
cidr?: string | null | undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ScanNameUtil {
|
|
47
|
+
/*
|
|
48
|
+
* Matches the column: NetworkDeviceDiscoveryScan.name is a ShortText, so a
|
|
49
|
+
* longer value does not truncate in Postgres — it throws, failing the whole
|
|
50
|
+
* INSERT. Read off ColumnLength rather than written as 100 so the two cannot
|
|
51
|
+
* drift apart if the column type ever changes.
|
|
52
|
+
*/
|
|
53
|
+
public static readonly MAX_SCAN_NAME_LENGTH: number = ColumnLength.ShortText;
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* ASCII control characters, including DEL. Written as escapes rather than as
|
|
57
|
+
* a literal range so the characters this guards against cannot end up inside
|
|
58
|
+
* the guard itself, and so the file stays readable in a diff.
|
|
59
|
+
*
|
|
60
|
+
* no-control-regex is disabled deliberately, the same way
|
|
61
|
+
* MetricResourceAttributeUtil disables it: a control character inside a
|
|
62
|
+
* user-typed name is precisely what this has to match in order to remove it.
|
|
63
|
+
*/
|
|
64
|
+
// eslint-disable-next-line no-control-regex
|
|
65
|
+
private static readonly CONTROL_CHARACTERS: RegExp = /[\u0000-\u001F\u007F]/g;
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* The name as it should be stored, or null when the operator supplied none.
|
|
69
|
+
*
|
|
70
|
+
* Three things happen here, in this order:
|
|
71
|
+
*
|
|
72
|
+
* - anything that is not a string becomes null. The value arrives straight
|
|
73
|
+
* from request JSON (BaseAPI assigns ShortText columns verbatim), so a
|
|
74
|
+
* client really can hand over a number, an object or an array. Callers
|
|
75
|
+
* that want to REJECT those call getValidationError() first; this
|
|
76
|
+
* function's job is only to say what would be stored.
|
|
77
|
+
* - control characters — a newline pasted out of a spreadsheet cell, a
|
|
78
|
+
* stray tab — become spaces, and every run of whitespace collapses to a
|
|
79
|
+
* single space. A name is rendered on one line in a table cell and
|
|
80
|
+
* inlined into log messages; a multi-line one breaks both, and nothing
|
|
81
|
+
* downstream would ever repair it.
|
|
82
|
+
* - the result is trimmed, and an empty result is null rather than "".
|
|
83
|
+
* "Not named" is one state, not two: an empty string in the column would
|
|
84
|
+
* read as a name everywhere `name ?` is asked, and render as a blank
|
|
85
|
+
* first line above the scan target.
|
|
86
|
+
*/
|
|
87
|
+
public static normalize(name: unknown): string | null {
|
|
88
|
+
if (typeof name !== "string") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const collapsed: string = name
|
|
93
|
+
.replace(ScanNameUtil.CONTROL_CHARACTERS, " ")
|
|
94
|
+
.replace(/\s+/g, " ")
|
|
95
|
+
.trim();
|
|
96
|
+
|
|
97
|
+
return collapsed.length > 0 ? collapsed : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
* The single validation entry point for a scan name, shared by the create
|
|
102
|
+
* form and both write hooks. Returns null when the name is storable —
|
|
103
|
+
* INCLUDING when there is no name at all, because the field is optional and
|
|
104
|
+
* "" is a perfectly good way to say so.
|
|
105
|
+
*
|
|
106
|
+
* `name` is typed unknown for the same reason ScanTargetUtil's target is:
|
|
107
|
+
* the server hook runs before the model's own type and length checks, so it
|
|
108
|
+
* is the first thing to see whatever the client actually sent.
|
|
109
|
+
*/
|
|
110
|
+
public static getValidationError(name: unknown): string | null {
|
|
111
|
+
if (name === undefined || name === null) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof name !== "string") {
|
|
116
|
+
return "A scan name must be text.";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const normalized: string | null = ScanNameUtil.normalize(name);
|
|
120
|
+
|
|
121
|
+
if (normalized === null) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/*
|
|
126
|
+
* Measured against the NORMALIZED value, which is what would be stored:
|
|
127
|
+
* rejecting a 101-character value that is 100 characters plus a trailing
|
|
128
|
+
* newline would be refusing to save something this module was about to fix
|
|
129
|
+
* anyway.
|
|
130
|
+
*/
|
|
131
|
+
if (normalized.length > ScanNameUtil.MAX_SCAN_NAME_LENGTH) {
|
|
132
|
+
return (
|
|
133
|
+
`A scan name cannot be longer than ${ScanNameUtil.MAX_SCAN_NAME_LENGTH} characters. ` +
|
|
134
|
+
`This one is ${normalized.length}.`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* The name to show for a scan, or null when it has none — so a caller can
|
|
143
|
+
* render the scan target instead rather than an empty line.
|
|
144
|
+
*
|
|
145
|
+
* Normalized on the way out as well as on the way in, because rows written
|
|
146
|
+
* before this column existed, and rows written by any writer that bypasses
|
|
147
|
+
* the hooks (migrations, the probe-ingest endpoints), are not guaranteed to
|
|
148
|
+
* have been through normalize().
|
|
149
|
+
*/
|
|
150
|
+
public static getDisplayName(scan: DiscoveryScanIdentity): string | null {
|
|
151
|
+
return ScanNameUtil.normalize(scan.name);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* How to refer to a scan in one line of running text — a log message, a
|
|
156
|
+
* modal's description, a status note.
|
|
157
|
+
*
|
|
158
|
+
* A named scan carries its target along with it (`Router Discovery — Region
|
|
159
|
+
* 1100 (10.15.128.0-255)`): the name says which scan, the target says what
|
|
160
|
+
* it sweeps, and an operator reading a log line usually wants both. An
|
|
161
|
+
* unnamed one is exactly what it was before this column existed — the target
|
|
162
|
+
* alone.
|
|
163
|
+
*
|
|
164
|
+
* Returns "" when the row has neither, which happens only when the caller
|
|
165
|
+
* selected neither column. Callers append their own fallback (the scan id)
|
|
166
|
+
* rather than having one invented for them here.
|
|
167
|
+
*/
|
|
168
|
+
public static getScanLabel(scan: DiscoveryScanIdentity): string {
|
|
169
|
+
const name: string | null = ScanNameUtil.getDisplayName(scan);
|
|
170
|
+
const target: string =
|
|
171
|
+
typeof scan.cidr === "string" ? scan.cidr.trim() : "";
|
|
172
|
+
|
|
173
|
+
if (name && target) {
|
|
174
|
+
return `${name} (${target})`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return name || target || "";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export default ScanNameUtil;
|