@oneuptime/common 12.0.22 → 12.0.24
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/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -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 +12 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +207 -0
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -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/Monitor/NetworkInventoryUtil.ts +39 -1
- 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 +130 -0
- 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/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/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/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -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/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -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/Monitor/NetworkInventoryUtil.test.ts +97 -1
- 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 +368 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -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 +193 -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/AutoImportRuleMatcher.test.ts +516 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -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/NetworkAutomation/RuleRunResult.ts +84 -0
- package/Types/Permission.ts +47 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmMetricConventions.ts +124 -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/AutoImportRuleMatcher.ts +257 -0
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
- package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.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/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -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/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
- 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 +12 -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/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +206 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
- 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/Monitor/NetworkInventoryUtil.js +26 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.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 +102 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- 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/NetworkAutomation/RuleRunResult.js +24 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/Permission.js +41 -0
- package/build/dist/Types/Permission.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/LlmMetricConventions.js +98 -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/AutoImportRuleMatcher.js +149 -0
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- 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 +119 -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
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { TelemetryAttributeService } from "../../../Server/Services/TelemetryAttributeService";
|
|
2
|
+
import { Statement } from "../../../Server/Utils/AnalyticsDatabase/Statement";
|
|
3
|
+
import SecurityEvent from "../../../Models/AnalyticsModels/SecurityEvent";
|
|
4
|
+
import AnalyticsTableName from "../../../Types/AnalyticsDatabase/AnalyticsTableName";
|
|
5
|
+
import TelemetryType from "../../../Types/Telemetry/TelemetryType";
|
|
6
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
7
|
+
import { describe, expect, test } from "@jest/globals";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Security events are the fourth signal to grow an attribute picker, and the
|
|
11
|
+
* one where it matters most: the OCSF schema only has typed columns for the
|
|
12
|
+
* common fields, so everything else a SIEM sent - device.hostname,
|
|
13
|
+
* finding_info.title, metadata.product.name - lives in the `attributes` map.
|
|
14
|
+
* The security events table offers those as columns, and this is the query
|
|
15
|
+
* that tells it which ones exist.
|
|
16
|
+
*
|
|
17
|
+
* TelemetryType.SecurityEvent was in the enum long before there was a source
|
|
18
|
+
* for it, and getTelemetrySource returns null for anything it does not
|
|
19
|
+
* recognise - which makes a missing case a silently empty picker rather than
|
|
20
|
+
* an error. That is what the first test here exists to catch.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
type SourceShape = {
|
|
24
|
+
tableName: string;
|
|
25
|
+
attributesColumn: string;
|
|
26
|
+
attributeKeysColumn?: string | undefined;
|
|
27
|
+
timeColumn: string;
|
|
28
|
+
isMutableMetricSource?: boolean | undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type GetSourceFunction = (telemetryType: TelemetryType) => SourceShape | null;
|
|
32
|
+
|
|
33
|
+
const getSource: GetSourceFunction = (
|
|
34
|
+
telemetryType: TelemetryType,
|
|
35
|
+
): SourceShape | null => {
|
|
36
|
+
return (
|
|
37
|
+
new TelemetryAttributeService() as unknown as {
|
|
38
|
+
getTelemetrySource: (type: TelemetryType) => SourceShape | null;
|
|
39
|
+
}
|
|
40
|
+
).getTelemetrySource(telemetryType);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe("TelemetryAttributeService - security event source", () => {
|
|
44
|
+
test("security events resolve to a source at all", () => {
|
|
45
|
+
expect(getSource(TelemetryType.SecurityEvent)).not.toBeNull();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("points at the security event table", () => {
|
|
49
|
+
const source: SourceShape = getSource(
|
|
50
|
+
TelemetryType.SecurityEvent,
|
|
51
|
+
) as SourceShape;
|
|
52
|
+
|
|
53
|
+
expect(source.tableName).toBe(AnalyticsTableName.SecurityEvent);
|
|
54
|
+
expect(source.tableName).toBe(new SecurityEvent().tableName);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* The names are bound into the statement as identifiers, so a typo here is
|
|
59
|
+
* not a type error anywhere - it is a query that fails at runtime against a
|
|
60
|
+
* table that does exist.
|
|
61
|
+
*/
|
|
62
|
+
test("names columns that are actually on the model", () => {
|
|
63
|
+
const source: SourceShape = getSource(
|
|
64
|
+
TelemetryType.SecurityEvent,
|
|
65
|
+
) as SourceShape;
|
|
66
|
+
|
|
67
|
+
const model: SecurityEvent = new SecurityEvent();
|
|
68
|
+
|
|
69
|
+
expect(model.hasColumn(source.attributesColumn)).toBe(true);
|
|
70
|
+
expect(model.hasColumn(source.attributeKeysColumn as string)).toBe(true);
|
|
71
|
+
expect(model.hasColumn(source.timeColumn)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* The sidecar array column is what keeps this cheap: without it the SQL
|
|
76
|
+
* falls back to mapKeys(attributes), which reads the whole map off every
|
|
77
|
+
* row instead of an already-denormalized array.
|
|
78
|
+
*/
|
|
79
|
+
test("uses the attributeKeys sidecar rather than expanding the map", () => {
|
|
80
|
+
const source: SourceShape = getSource(
|
|
81
|
+
TelemetryType.SecurityEvent,
|
|
82
|
+
) as SourceShape;
|
|
83
|
+
|
|
84
|
+
expect(source.attributeKeysColumn).toBe("attributeKeys");
|
|
85
|
+
expect(source.attributesColumn).toBe("attributes");
|
|
86
|
+
expect(source.timeColumn).toBe("time");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("is not treated as a mutable metric source", () => {
|
|
90
|
+
const source: SourceShape = getSource(
|
|
91
|
+
TelemetryType.SecurityEvent,
|
|
92
|
+
) as SourceShape;
|
|
93
|
+
|
|
94
|
+
expect(source.isMutableMetricSource).toBeFalsy();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// A signal with no source still degrades to "no attributes", not an error.
|
|
98
|
+
test("a telemetry type with no source is still null", () => {
|
|
99
|
+
expect(getSource(TelemetryType.Profile)).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("TelemetryAttributeService - security event attribute keys query", () => {
|
|
104
|
+
type BuildAttributesInput = {
|
|
105
|
+
projectId: ObjectID;
|
|
106
|
+
tableName: string;
|
|
107
|
+
attributesColumn: string;
|
|
108
|
+
attributeKeysColumn?: string | undefined;
|
|
109
|
+
timeColumn: string;
|
|
110
|
+
metricName?: string | undefined;
|
|
111
|
+
isMutableMetricSource?: boolean | undefined;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
type BuildStatementFunction = () => Statement;
|
|
115
|
+
|
|
116
|
+
const buildStatement: BuildStatementFunction = (): Statement => {
|
|
117
|
+
const source: SourceShape = getSource(
|
|
118
|
+
TelemetryType.SecurityEvent,
|
|
119
|
+
) as SourceShape;
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
TelemetryAttributeService as unknown as {
|
|
123
|
+
buildAttributesStatement: (data: BuildAttributesInput) => Statement;
|
|
124
|
+
}
|
|
125
|
+
).buildAttributesStatement({
|
|
126
|
+
projectId: ObjectID.generate(),
|
|
127
|
+
tableName: source.tableName,
|
|
128
|
+
attributesColumn: source.attributesColumn,
|
|
129
|
+
attributeKeysColumn: source.attributeKeysColumn,
|
|
130
|
+
timeColumn: source.timeColumn,
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Table and column names ride in as {pN:Identifier} bindings rather than as
|
|
136
|
+
* SQL text, so everything below asserts against the bound parameters. That
|
|
137
|
+
* is the point of the check as much as a quirk of it: nothing
|
|
138
|
+
* caller-influenced is ever concatenated into these statements.
|
|
139
|
+
*/
|
|
140
|
+
test("aggregates the sidecar array over the security event table", () => {
|
|
141
|
+
const statement: Statement = buildStatement();
|
|
142
|
+
|
|
143
|
+
expect(Object.values(statement.query_params)).toContain(
|
|
144
|
+
AnalyticsTableName.SecurityEvent,
|
|
145
|
+
);
|
|
146
|
+
expect(statement.query).toContain("groupUniqArrayArray(");
|
|
147
|
+
expect(Object.values(statement.query_params)).toContain("attributeKeys");
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* The mapKeys() branch is the fallback for tables with no sidecar column;
|
|
151
|
+
* taking it here would read the whole map off every row.
|
|
152
|
+
*/
|
|
153
|
+
expect(statement.query).not.toContain("mapKeys(");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("scopes to one project, as a bound parameter", () => {
|
|
157
|
+
const statement: Statement = buildStatement();
|
|
158
|
+
|
|
159
|
+
expect(statement.query).toContain("projectId = ");
|
|
160
|
+
/*
|
|
161
|
+
* A tenant id interpolated into SQL would be both an injection surface
|
|
162
|
+
* and a cache-buster; every value in these statements is parameterized.
|
|
163
|
+
*/
|
|
164
|
+
expect(statement.query).toMatch(/projectId = \{p\d+:String\}/);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("bounds the scan by time and by execution budget", () => {
|
|
168
|
+
const statement: Statement = buildStatement();
|
|
169
|
+
|
|
170
|
+
expect(Object.values(statement.query_params)).toContain("time");
|
|
171
|
+
expect(statement.query).toMatch(/>= \{p\d+:DateTime\}/);
|
|
172
|
+
expect(statement.query).toContain("max_execution_time = 45");
|
|
173
|
+
expect(statement.query).toContain("timeout_overflow_mode = 'break'");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("skips rows with no attributes at all", () => {
|
|
177
|
+
const statement: Statement = buildStatement();
|
|
178
|
+
|
|
179
|
+
expect(statement.query).toMatch(/NOT empty\(\{p\d+:Identifier\}\)/);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// metricName is a Metric-only filter; nothing here should mention `name`.
|
|
183
|
+
test("does not filter by metric name", () => {
|
|
184
|
+
const statement: Statement = buildStatement();
|
|
185
|
+
|
|
186
|
+
expect(statement.query).not.toContain("AND name = ");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("TelemetryAttributeService - security event attribute values query", () => {
|
|
191
|
+
type BuildValuesInput = {
|
|
192
|
+
projectId: ObjectID;
|
|
193
|
+
source: SourceShape;
|
|
194
|
+
metricName?: string | undefined;
|
|
195
|
+
attributeKey: string;
|
|
196
|
+
searchText?: string | undefined;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
type BuildValuesFunction = (searchText?: string | undefined) => Statement;
|
|
200
|
+
|
|
201
|
+
const buildValues: BuildValuesFunction = (
|
|
202
|
+
searchText?: string | undefined,
|
|
203
|
+
): Statement => {
|
|
204
|
+
return (
|
|
205
|
+
TelemetryAttributeService as unknown as {
|
|
206
|
+
buildAttributeValuesStatement: (data: BuildValuesInput) => Statement;
|
|
207
|
+
}
|
|
208
|
+
).buildAttributeValuesStatement({
|
|
209
|
+
projectId: ObjectID.generate(),
|
|
210
|
+
source: getSource(TelemetryType.SecurityEvent) as SourceShape,
|
|
211
|
+
attributeKey: "device.hostname",
|
|
212
|
+
...(searchText === undefined ? {} : { searchText }),
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
test("reads the requested key out of the map on the security event table", () => {
|
|
217
|
+
const statement: Statement = buildValues();
|
|
218
|
+
|
|
219
|
+
const params: Array<unknown> = Object.values(statement.query_params);
|
|
220
|
+
|
|
221
|
+
expect(params).toContain(AnalyticsTableName.SecurityEvent);
|
|
222
|
+
expect(params).toContain("attributes");
|
|
223
|
+
expect(params).toContain("device.hostname");
|
|
224
|
+
expect(statement.query).toContain("mapContains(");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/*
|
|
228
|
+
* The key is caller-supplied, so it must never reach the SQL text - an
|
|
229
|
+
* attribute key can contain quotes, brackets, anything a payload had.
|
|
230
|
+
*/
|
|
231
|
+
test("never inlines the attribute key into the SQL text", () => {
|
|
232
|
+
const statement: Statement = buildValues();
|
|
233
|
+
|
|
234
|
+
expect(statement.query).not.toContain("device.hostname");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("narrows server-side when the picker sends search text", () => {
|
|
238
|
+
const statement: Statement = buildValues("web");
|
|
239
|
+
|
|
240
|
+
expect(statement.query).toContain("ILIKE");
|
|
241
|
+
expect(Object.values(statement.query_params)).toContain("%web%");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("whitespace-only search text is not a filter", () => {
|
|
245
|
+
const statement: Statement = buildValues(" ");
|
|
246
|
+
|
|
247
|
+
expect(statement.query).not.toContain("ILIKE");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("caps the number of values returned", () => {
|
|
251
|
+
const statement: Statement = buildValues();
|
|
252
|
+
|
|
253
|
+
expect(statement.query).toContain("ORDER BY attributeValue ASC");
|
|
254
|
+
expect(statement.query).toContain("LIMIT ");
|
|
255
|
+
});
|
|
256
|
+
});
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The runtime component registry reaches the native isolated-vm addon through
|
|
3
|
+
* the JavaScript component's sandbox (Components/JavaScript -> VMRunner).
|
|
4
|
+
* Nothing under test here runs a sandbox, and the prebuilt binary cannot
|
|
5
|
+
* always dlopen in the test environment - so stub the module out before
|
|
6
|
+
* anything imports it.
|
|
7
|
+
*/
|
|
8
|
+
jest.mock("isolated-vm", () => {
|
|
9
|
+
return {};
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
import DatabaseBaseModel from "../../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
13
|
+
import Entities from "../../../../../Models/DatabaseModels/Index";
|
|
14
|
+
import DatabaseService from "../../../../../Server/Services/DatabaseService";
|
|
15
|
+
import Services from "../../../../../Server/Services/Index";
|
|
16
|
+
import ComponentCode from "../../../../../Server/Types/Workflow/ComponentCode";
|
|
17
|
+
import RuntimeComponents from "../../../../../Server/Types/Workflow/Components/Index";
|
|
18
|
+
import BaseService from "../../../../../Server/Services/BaseService";
|
|
19
|
+
import EnableWorkflowOn from "../../../../../Types/BaseDatabase/EnableWorkflowOn";
|
|
20
|
+
import Dictionary from "../../../../../Types/Dictionary";
|
|
21
|
+
import Text from "../../../../../Types/Text";
|
|
22
|
+
import ComponentMetadata from "../../../../../Types/Workflow/Component";
|
|
23
|
+
import NonModelComponents from "../../../../../Types/Workflow/Components";
|
|
24
|
+
import BaseModelComponentFactory from "../../../../../Types/Workflow/Components/BaseModel";
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* A workflow step is resolved twice on the worker, against two registries that
|
|
28
|
+
* are built from two different sources:
|
|
29
|
+
*
|
|
30
|
+
* metadata - App/FeatureSet/Workflow/Utils/ComponentMetadata.ts walks
|
|
31
|
+
* Models/DatabaseModels/Index.ts and emits components for every
|
|
32
|
+
* model carrying @EnableWorkflow. This is also what fills the
|
|
33
|
+
* editor palette, so it decides what a user can drag onto a
|
|
34
|
+
* workflow and save.
|
|
35
|
+
* code - Server/Types/Workflow/Components/Index.ts walks
|
|
36
|
+
* Server/Services/Index.ts and emits components for every
|
|
37
|
+
* REGISTERED DatabaseService whose model carries @EnableWorkflow.
|
|
38
|
+
* This is what RunWorkflow actually executes.
|
|
39
|
+
*
|
|
40
|
+
* A model that is workflow-enabled but whose service was never added to
|
|
41
|
+
* Server/Services/Index.ts satisfies the first registry and not the second. Its
|
|
42
|
+
* components appear in the palette, save into a workflow without complaint, and
|
|
43
|
+
* then fail at execution time with
|
|
44
|
+
* `BadDataException("Component <id> not found")` from RunWorkflow - the failure
|
|
45
|
+
* that Dashboard shipped with, where `dashboard-update-one` was offered by the
|
|
46
|
+
* editor while DashboardService was absent from Server/Services/Index.ts.
|
|
47
|
+
*
|
|
48
|
+
* Nothing in the type system ties the two lists together, so these tests are
|
|
49
|
+
* the thing that keeps them in step.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
interface ModelComponents {
|
|
53
|
+
modelName: string;
|
|
54
|
+
tableName: string;
|
|
55
|
+
enableWorkflowOn: EnableWorkflowOn;
|
|
56
|
+
componentIds: Array<string>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* Every model carrying @EnableWorkflow, whether or not it ends up contributing
|
|
61
|
+
* components. Filtering the empty ones out here instead would make any
|
|
62
|
+
* assertion phrased as "every workflow-enabled model ..." quietly exempt them,
|
|
63
|
+
* which is how MetricType and TeamComplianceSetting - both annotated with all
|
|
64
|
+
* four flags false - stay invisible to the palette while still being
|
|
65
|
+
* workflow-annotated models.
|
|
66
|
+
*/
|
|
67
|
+
const workflowAnnotatedModels: Array<ModelComponents> = [];
|
|
68
|
+
|
|
69
|
+
for (const model of Entities) {
|
|
70
|
+
const instance: DatabaseBaseModel = new model();
|
|
71
|
+
|
|
72
|
+
if (!instance.enableWorkflowOn) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Mirrors loadAllComponentMetadata() in App/FeatureSet/Workflow/Utils/ComponentMetadata.ts.
|
|
77
|
+
const components: Array<ComponentMetadata> =
|
|
78
|
+
BaseModelComponentFactory.getComponents(instance);
|
|
79
|
+
|
|
80
|
+
workflowAnnotatedModels.push({
|
|
81
|
+
modelName: model.name,
|
|
82
|
+
tableName: instance.tableName!,
|
|
83
|
+
enableWorkflowOn: instance.enableWorkflowOn,
|
|
84
|
+
componentIds: components.map((component: ComponentMetadata) => {
|
|
85
|
+
return component.id;
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The subset the editor actually puts in front of a user.
|
|
91
|
+
const editorModelComponents: Array<ModelComponents> =
|
|
92
|
+
workflowAnnotatedModels.filter((entry: ModelComponents) => {
|
|
93
|
+
return entry.componentIds.length > 0;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const allEditorModelComponentIds: Array<string> = editorModelComponents.flatMap(
|
|
97
|
+
(entry: ModelComponents) => {
|
|
98
|
+
return entry.componentIds;
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const registeredDatabaseServices: Array<DatabaseService<DatabaseBaseModel>> =
|
|
103
|
+
Services.filter((service: BaseService) => {
|
|
104
|
+
return service instanceof DatabaseService;
|
|
105
|
+
}) as Array<DatabaseService<DatabaseBaseModel>>;
|
|
106
|
+
|
|
107
|
+
describe("Workflow component registry parity", () => {
|
|
108
|
+
describe("editor palette vs worker runtime", () => {
|
|
109
|
+
test("every model component the editor offers is runnable by the worker", () => {
|
|
110
|
+
/*
|
|
111
|
+
* Reported per model rather than per component id: one unregistered
|
|
112
|
+
* service costs eleven component ids, and the model name is what points
|
|
113
|
+
* at the missing Server/Services/Index.ts entry.
|
|
114
|
+
*/
|
|
115
|
+
const modelsWithMissingComponents: Array<string> = editorModelComponents
|
|
116
|
+
.filter((entry: ModelComponents) => {
|
|
117
|
+
return entry.componentIds.some((id: string) => {
|
|
118
|
+
return !RuntimeComponents[id];
|
|
119
|
+
});
|
|
120
|
+
})
|
|
121
|
+
.map((entry: ModelComponents) => {
|
|
122
|
+
return entry.modelName;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(modelsWithMissingComponents).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("every non-model component the editor offers is runnable by the worker", () => {
|
|
129
|
+
const missing: Array<string> = NonModelComponents.filter(
|
|
130
|
+
(component: ComponentMetadata) => {
|
|
131
|
+
return !RuntimeComponents[component.id];
|
|
132
|
+
},
|
|
133
|
+
).map((component: ComponentMetadata) => {
|
|
134
|
+
return component.id;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(missing).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("the worker runs no model component the editor cannot produce", () => {
|
|
141
|
+
/*
|
|
142
|
+
* The other direction. A component the worker can run but the editor
|
|
143
|
+
* never offers is not a user-visible failure, but it means the two id
|
|
144
|
+
* derivations have drifted apart - which is the same defect pointed the
|
|
145
|
+
* other way, and would hide the next real gap.
|
|
146
|
+
*/
|
|
147
|
+
const editorIds: Set<string> = new Set(allEditorModelComponentIds);
|
|
148
|
+
const nonModelIds: Set<string> = new Set(
|
|
149
|
+
NonModelComponents.map((component: ComponentMetadata) => {
|
|
150
|
+
return component.id;
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const runtimeOnly: Array<string> = Object.keys(RuntimeComponents).filter(
|
|
155
|
+
(id: string) => {
|
|
156
|
+
return !editorIds.has(id) && !nonModelIds.has(id);
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect(runtimeOnly).toEqual([]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("the two registries agree on the exact component id set", () => {
|
|
164
|
+
const editorIds: Array<string> = [
|
|
165
|
+
...allEditorModelComponentIds,
|
|
166
|
+
...NonModelComponents.map((component: ComponentMetadata) => {
|
|
167
|
+
return component.id;
|
|
168
|
+
}),
|
|
169
|
+
].sort();
|
|
170
|
+
|
|
171
|
+
expect(Object.keys(RuntimeComponents).sort()).toEqual(editorIds);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("the palette is actually populated", () => {
|
|
176
|
+
/*
|
|
177
|
+
* Guards the parity assertions above against passing because both sides
|
|
178
|
+
* are empty - an import that silently resolves to nothing would otherwise
|
|
179
|
+
* turn this whole file green.
|
|
180
|
+
*/
|
|
181
|
+
test("the editor offers components for many models", () => {
|
|
182
|
+
expect(editorModelComponents.length).toBeGreaterThan(100);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("the editor offers the non-model components too", () => {
|
|
186
|
+
expect(NonModelComponents.length).toBeGreaterThan(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("a model contributes components exactly when one of its flags is on", () => {
|
|
190
|
+
/*
|
|
191
|
+
* The invariant the palette actually rests on. Phrasing it as "every
|
|
192
|
+
* workflow-enabled model contributes a component" would be false -
|
|
193
|
+
* @EnableWorkflow with every flag false is legal and contributes nothing
|
|
194
|
+
* - and phrasing it over the already-filtered list would make it
|
|
195
|
+
* unfailable. Comparing the two conditions catches both a flag that
|
|
196
|
+
* stops emitting components and a component emitted for a flag that is
|
|
197
|
+
* switched off.
|
|
198
|
+
*/
|
|
199
|
+
const disagreeing: Array<string> = workflowAnnotatedModels
|
|
200
|
+
.filter((entry: ModelComponents) => {
|
|
201
|
+
const anyFlagOn: boolean = Boolean(
|
|
202
|
+
entry.enableWorkflowOn.create ||
|
|
203
|
+
entry.enableWorkflowOn.read ||
|
|
204
|
+
entry.enableWorkflowOn.update ||
|
|
205
|
+
entry.enableWorkflowOn.delete,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const contributesComponents: boolean = entry.componentIds.length > 0;
|
|
209
|
+
|
|
210
|
+
return anyFlagOn !== contributesComponents;
|
|
211
|
+
})
|
|
212
|
+
.map((entry: ModelComponents) => {
|
|
213
|
+
return entry.modelName;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(disagreeing).toEqual([]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("the models annotated but inert are exactly the ones we know about", () => {
|
|
220
|
+
/*
|
|
221
|
+
* @EnableWorkflow with every flag false is legal and contributes nothing,
|
|
222
|
+
* so such a model is outside the parity requirement further down. Naming
|
|
223
|
+
* them rather than looping over whatever happens to be inert keeps that
|
|
224
|
+
* exemption deliberate: a new inert model fails here and has to be
|
|
225
|
+
* acknowledged, instead of quietly joining a list nothing asserts on.
|
|
226
|
+
*/
|
|
227
|
+
const inert: Array<string> = workflowAnnotatedModels
|
|
228
|
+
.filter((entry: ModelComponents) => {
|
|
229
|
+
return entry.componentIds.length === 0;
|
|
230
|
+
})
|
|
231
|
+
.map((entry: ModelComponents) => {
|
|
232
|
+
return entry.modelName;
|
|
233
|
+
})
|
|
234
|
+
.sort();
|
|
235
|
+
|
|
236
|
+
expect(inert).toEqual(["MetricType", "TeamComplianceSetting"]);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe("Dashboard, the model this parity check was written for", () => {
|
|
241
|
+
/*
|
|
242
|
+
* Dashboard is workflow-enabled and exported from
|
|
243
|
+
* Models/DatabaseModels/Index.ts, but DashboardService was missing from
|
|
244
|
+
* Server/Services/Index.ts - only DashboardDomainService was there. Every
|
|
245
|
+
* dashboard component was therefore offered by the editor and unrunnable.
|
|
246
|
+
*/
|
|
247
|
+
test("dashboard-update-one is runnable", () => {
|
|
248
|
+
expect(RuntimeComponents["dashboard-update-one"]).toBeDefined();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("the whole dashboard component family is runnable", () => {
|
|
252
|
+
const dashboard: ModelComponents | undefined = editorModelComponents.find(
|
|
253
|
+
(entry: ModelComponents) => {
|
|
254
|
+
return entry.modelName === "Dashboard";
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
expect(dashboard).toBeDefined();
|
|
259
|
+
|
|
260
|
+
const missing: Array<string> = dashboard!.componentIds.filter(
|
|
261
|
+
(id: string) => {
|
|
262
|
+
return !RuntimeComponents[id];
|
|
263
|
+
},
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
expect(missing).toEqual([]);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("the dashboard components cover create, read, update and delete", () => {
|
|
270
|
+
for (const id of [
|
|
271
|
+
"dashboard-create-one",
|
|
272
|
+
"dashboard-create-many",
|
|
273
|
+
"dashboard-find-one",
|
|
274
|
+
"dashboard-find-many",
|
|
275
|
+
"dashboard-update-one",
|
|
276
|
+
"dashboard-update-many",
|
|
277
|
+
"dashboard-delete-one",
|
|
278
|
+
"dashboard-delete-many",
|
|
279
|
+
"dashboard-on-create",
|
|
280
|
+
"dashboard-on-update",
|
|
281
|
+
"dashboard-on-delete",
|
|
282
|
+
]) {
|
|
283
|
+
expect(RuntimeComponents[id]).toBeDefined();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("the service registry the runtime is built from", () => {
|
|
289
|
+
test("every model the palette offers has a service registered in Server/Services/Index.ts", () => {
|
|
290
|
+
const servicedTableNames: Set<string> = new Set(
|
|
291
|
+
registeredDatabaseServices.map(
|
|
292
|
+
(service: DatabaseService<DatabaseBaseModel>) => {
|
|
293
|
+
return service.getModel().tableName!;
|
|
294
|
+
},
|
|
295
|
+
),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const unserviced: Array<string> = editorModelComponents
|
|
299
|
+
.filter((entry: ModelComponents) => {
|
|
300
|
+
return !servicedTableNames.has(entry.tableName);
|
|
301
|
+
})
|
|
302
|
+
.map((entry: ModelComponents) => {
|
|
303
|
+
return entry.modelName;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
expect(unserviced).toEqual([]);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("no service is registered twice", () => {
|
|
310
|
+
/*
|
|
311
|
+
* Services are singletons, so a duplicated entry is the same object
|
|
312
|
+
* appearing twice - harmless to the registry, but it means an edit landed
|
|
313
|
+
* in the array twice and the second copy will rot.
|
|
314
|
+
*/
|
|
315
|
+
const seen: Set<BaseService> = new Set();
|
|
316
|
+
const duplicated: Array<string> = [];
|
|
317
|
+
|
|
318
|
+
for (const service of Services) {
|
|
319
|
+
if (seen.has(service)) {
|
|
320
|
+
duplicated.push(service.constructor.name);
|
|
321
|
+
}
|
|
322
|
+
seen.add(service);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
expect(duplicated).toEqual([]);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("no two registered services claim the same model", () => {
|
|
329
|
+
const byTableName: Dictionary<Array<string>> = {};
|
|
330
|
+
|
|
331
|
+
for (const service of registeredDatabaseServices) {
|
|
332
|
+
const tableName: string = service.getModel().tableName!;
|
|
333
|
+
byTableName[tableName] = byTableName[tableName] || [];
|
|
334
|
+
byTableName[tableName]!.push(service.constructor.name);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const contested: Array<string> = Object.keys(byTableName).filter(
|
|
338
|
+
(tableName: string) => {
|
|
339
|
+
return byTableName[tableName]!.length > 1;
|
|
340
|
+
},
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
expect(contested).toEqual([]);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe("runtime registry integrity", () => {
|
|
348
|
+
test("every registered component is a ComponentCode", () => {
|
|
349
|
+
const notComponentCode: Array<string> = Object.keys(
|
|
350
|
+
RuntimeComponents,
|
|
351
|
+
).filter((id: string) => {
|
|
352
|
+
return !(RuntimeComponents[id] instanceof ComponentCode);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
expect(notComponentCode).toEqual([]);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("every component is registered under the id its own metadata declares", () => {
|
|
359
|
+
/*
|
|
360
|
+
* Each model component derives its metadata from the service it was
|
|
361
|
+
* constructed with, while the registry key is derived separately at the
|
|
362
|
+
* call site. A component wired to the wrong service still registers
|
|
363
|
+
* cleanly and then acts on the wrong table, so the two have to match.
|
|
364
|
+
*/
|
|
365
|
+
const mismatched: Array<string> = [];
|
|
366
|
+
|
|
367
|
+
for (const id of Object.keys(RuntimeComponents)) {
|
|
368
|
+
const component: ComponentCode | undefined = RuntimeComponents[id];
|
|
369
|
+
const declaredId: string = component!.getMetadata().id;
|
|
370
|
+
|
|
371
|
+
if (declaredId !== id) {
|
|
372
|
+
mismatched.push(`${id} -> ${declaredId}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
expect(mismatched).toEqual([]);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("model component ids are derived from the table name", () => {
|
|
380
|
+
for (const entry of editorModelComponents) {
|
|
381
|
+
const prefix: string = Text.pascalCaseToDashes(entry.tableName);
|
|
382
|
+
|
|
383
|
+
const wrongPrefix: Array<string> = entry.componentIds.filter(
|
|
384
|
+
(id: string) => {
|
|
385
|
+
return !id.startsWith(`${prefix}-`);
|
|
386
|
+
},
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
expect(wrongPrefix).toEqual([]);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("no two models derive the same component id", () => {
|
|
394
|
+
/*
|
|
395
|
+
* Two models whose table names collapse to the same dashed id would
|
|
396
|
+
* silently overwrite each other in the registry, leaving one model's
|
|
397
|
+
* components pointing at the other model's service.
|
|
398
|
+
*/
|
|
399
|
+
const owners: Dictionary<Array<string>> = {};
|
|
400
|
+
|
|
401
|
+
for (const entry of editorModelComponents) {
|
|
402
|
+
for (const id of entry.componentIds) {
|
|
403
|
+
owners[id] = owners[id] || [];
|
|
404
|
+
owners[id]!.push(entry.modelName);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const collisions: Array<string> = Object.keys(owners).filter(
|
|
409
|
+
(id: string) => {
|
|
410
|
+
return owners[id]!.length > 1;
|
|
411
|
+
},
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
expect(collisions).toEqual([]);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
});
|