@oneuptime/common 12.0.23 → 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.
Files changed (294) hide show
  1. package/Models/DatabaseModels/DetectionRule.ts +55 -0
  2. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +27 -7
  3. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
  4. package/Models/DatabaseModels/Project.ts +68 -0
  5. package/Models/DatabaseModels/User.ts +68 -0
  6. package/Server/API/TelemetryAPI.ts +29 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  11. package/Server/Middleware/GzipRequestBody.ts +311 -0
  12. package/Server/Middleware/MultipartFormData.ts +87 -3
  13. package/Server/Services/DetectionRuleService.ts +73 -6
  14. package/Server/Services/Index.ts +205 -0
  15. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
  16. package/Server/Services/NetworkDeviceService.ts +148 -0
  17. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  18. package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
  19. package/Server/Services/ProjectService.ts +29 -19
  20. package/Server/Services/SecurityEventService.ts +90 -13
  21. package/Server/Services/StatusPageResourceService.ts +223 -0
  22. package/Server/Services/TelemetryAttributeService.ts +17 -0
  23. package/Server/Services/UserService.ts +4 -6
  24. package/Server/Types/Markdown.ts +161 -67
  25. package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
  26. package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
  27. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
  28. package/Server/Utils/Attribution.ts +4 -0
  29. package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
  30. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
  31. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
  32. package/Server/Utils/StartServer.ts +12 -32
  33. package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
  34. package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
  35. package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
  36. package/Server/Views/Partials/Head.ejs +2 -1
  37. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
  38. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
  39. package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
  40. package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
  41. package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
  42. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
  43. package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
  44. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
  45. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
  46. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
  47. package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
  48. package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
  49. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
  50. package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
  51. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
  52. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
  53. package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
  54. package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
  55. package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
  56. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
  57. package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
  58. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
  59. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
  60. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
  61. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
  62. package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
  63. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
  64. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
  65. package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
  66. package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
  67. package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
  68. package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
  69. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
  70. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
  71. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
  72. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
  73. package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
  74. package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
  75. package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
  76. package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
  77. package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
  78. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
  79. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
  80. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
  81. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
  82. package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
  83. package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
  84. package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
  85. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
  86. package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
  87. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
  88. package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
  89. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
  90. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
  91. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
  92. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
  93. package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
  94. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
  95. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
  96. package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
  97. package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
  98. package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
  99. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
  100. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
  101. package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
  102. package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
  103. package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
  104. package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
  105. package/Tests/UI/Components/SideOver.test.tsx +1 -1
  106. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
  107. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
  108. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
  109. package/Tests/UI/Components/Toggle.test.tsx +25 -0
  110. package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
  111. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
  112. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
  113. package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
  114. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
  115. package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
  116. package/Types/Code/YamlSyntax.ts +396 -0
  117. package/Types/Exception/ExceptionCode.ts +1 -0
  118. package/Types/Exception/PayloadTooLargeException.ts +8 -0
  119. package/Types/Marketing/Attribution.ts +10 -0
  120. package/Types/Marketing/MarketingEvent.ts +4 -0
  121. package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
  122. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
  123. package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
  124. package/Types/Telemetry/LlmMetricConventions.ts +124 -0
  125. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
  126. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
  127. package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
  128. package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
  129. package/UI/Components/Detail/Detail.tsx +10 -0
  130. package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
  131. package/UI/Components/Forms/Fields/FormField.tsx +31 -0
  132. package/UI/Components/Forms/Types/Field.ts +8 -0
  133. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  134. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
  135. package/UI/Components/Forms/Validation.ts +46 -0
  136. package/UI/Components/Input/Input.tsx +3 -0
  137. package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
  138. package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
  139. package/UI/Components/ModelTable/Column.ts +8 -0
  140. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
  141. package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
  142. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
  143. package/UI/Components/SideOver/SideOver.tsx +5 -5
  144. package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
  145. package/UI/Components/StatusPage/ResourceList.tsx +197 -38
  146. package/UI/Components/Toggle/Toggle.tsx +9 -1
  147. package/UI/Components/Types/FieldType.ts +1 -0
  148. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
  149. package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
  150. package/Utils/StatusPage/ResourceExplorer.ts +258 -0
  151. package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
  152. package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
  153. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  154. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +27 -7
  155. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  156. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
  157. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/Project.js +76 -0
  159. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/User.js +76 -0
  161. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  162. package/build/dist/Server/API/TelemetryAPI.js +17 -0
  163. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  164. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
  165. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
  166. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
  167. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
  168. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
  169. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
  170. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  171. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  172. package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
  173. package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
  174. package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
  175. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  176. package/build/dist/Server/Services/DetectionRuleService.js +49 -4
  177. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  178. package/build/dist/Server/Services/Index.js +204 -0
  179. package/build/dist/Server/Services/Index.js.map +1 -1
  180. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
  181. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  182. package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
  183. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  184. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
  185. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  186. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
  187. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
  188. package/build/dist/Server/Services/ProjectService.js +25 -45
  189. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  190. package/build/dist/Server/Services/SecurityEventService.js +57 -5
  191. package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
  192. package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
  193. package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
  194. package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
  195. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  196. package/build/dist/Server/Services/UserService.js +2 -12
  197. package/build/dist/Server/Services/UserService.js.map +1 -1
  198. package/build/dist/Server/Types/Markdown.js +147 -61
  199. package/build/dist/Server/Types/Markdown.js.map +1 -1
  200. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
  201. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
  202. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
  203. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  204. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
  205. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  206. package/build/dist/Server/Utils/Attribution.js.map +1 -1
  207. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
  208. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
  209. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
  210. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  211. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
  212. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  213. package/build/dist/Server/Utils/StartServer.js +12 -21
  214. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  215. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
  216. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
  217. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
  218. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
  219. package/build/dist/Types/Code/YamlSyntax.js +212 -0
  220. package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
  221. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  222. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  223. package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
  224. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
  225. package/build/dist/Types/Marketing/Attribution.js +10 -0
  226. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  227. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
  228. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  229. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
  230. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  231. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
  232. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  233. package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
  234. package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
  235. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  236. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  237. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
  238. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  239. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
  240. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  241. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
  242. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
  243. package/build/dist/UI/Components/Detail/Detail.js +9 -0
  244. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  245. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
  246. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  247. package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
  248. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  249. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  250. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  251. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  252. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
  253. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  254. package/build/dist/UI/Components/Forms/Validation.js +27 -0
  255. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  256. package/build/dist/UI/Components/Input/Input.js +1 -1
  257. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  258. package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
  259. package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
  260. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
  261. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  262. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
  263. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  264. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
  265. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
  266. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
  267. package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
  268. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
  269. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  270. package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
  271. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  272. package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
  273. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  274. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  275. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  276. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
  277. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  278. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
  279. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
  280. package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
  281. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
  282. package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
  283. package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
  284. package/jest.config.json +2 -0
  285. package/package.json +1 -1
  286. package/tsconfig.json +2 -1
  287. package/Tests/UI/Components/Toast.test.tsx +0 -197
  288. package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
  289. package/UI/Components/Toast/Toast.tsx +0 -189
  290. package/UI/Components/Toast/ToastInit.tsx +0 -110
  291. package/build/dist/UI/Components/Toast/Toast.js +0 -123
  292. package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
  293. package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
  294. package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
@@ -0,0 +1,436 @@
1
+ import Monitor from "../../../../Models/DatabaseModels/Monitor";
2
+ import StatusPageResource from "../../../../Models/DatabaseModels/StatusPageResource";
3
+ import ResourceList from "../../../../UI/Components/StatusPage/ResourceList";
4
+ import "@testing-library/jest-dom";
5
+ import { fireEvent, render, screen, within } from "@testing-library/react";
6
+ import React, { ReactElement } from "react";
7
+ import { beforeEach, describe, expect, jest, test } from "@jest/globals";
8
+
9
+ /*
10
+ * Contract under test - the checkbox column on a status page group's resource
11
+ * list.
12
+ *
13
+ * A status page group holding twenty seven monitors could only be emptied one
14
+ * monitor at a time: open a row's overflow menu, confirm, wait for the group to
15
+ * reload, and start again on the next one (issue #3419). The list carries a box
16
+ * per row and a box at the top of it now, and the pane above turns whatever is
17
+ * ticked into one removal.
18
+ *
19
+ * The list itself holds no selection state - the pane does, because a selection
20
+ * has to survive the refetch that follows every write. So everything here is
21
+ * about what the list draws for a given selection and what it reports back, and
22
+ * nothing here is about what the pane then does with it.
23
+ */
24
+
25
+ type MakeResourceFunction = (data: {
26
+ id?: string | undefined;
27
+ monitorName?: string | undefined;
28
+ displayName?: string | undefined;
29
+ }) => StatusPageResource;
30
+
31
+ const makeResource: MakeResourceFunction = (data: {
32
+ id?: string | undefined;
33
+ monitorName?: string | undefined;
34
+ displayName?: string | undefined;
35
+ }): StatusPageResource => {
36
+ const resource: StatusPageResource = new StatusPageResource();
37
+
38
+ if (data.id) {
39
+ resource._id = data.id;
40
+ }
41
+
42
+ if (data.monitorName) {
43
+ const monitor: Monitor = new Monitor();
44
+ monitor._id = `monitor-${data.id || "none"}`;
45
+ monitor.name = data.monitorName;
46
+ resource.monitor = monitor;
47
+ }
48
+
49
+ if (data.displayName) {
50
+ resource.displayName = data.displayName;
51
+ }
52
+
53
+ return resource;
54
+ };
55
+
56
+ const API_RESOURCE: StatusPageResource = makeResource({
57
+ id: "resource-api",
58
+ monitorName: "API",
59
+ });
60
+ const DATABASE_RESOURCE: StatusPageResource = makeResource({
61
+ id: "resource-database",
62
+ monitorName: "Database",
63
+ });
64
+ const CACHE_RESOURCE: StatusPageResource = makeResource({
65
+ id: "resource-cache",
66
+ monitorName: "Cache",
67
+ });
68
+
69
+ const onToggleResourceSelected: jest.MockedFunction<any> = jest.fn();
70
+ const onToggleAllSelected: jest.MockedFunction<any> = jest.fn();
71
+ const onReorder: jest.MockedFunction<any> = jest.fn();
72
+ const onShowMore: jest.MockedFunction<any> = jest.fn();
73
+
74
+ interface RenderOptions {
75
+ statusPageResources?: Array<StatusPageResource> | undefined;
76
+ isSelectable?: boolean | undefined;
77
+ selectedResourceIds?: Set<string> | undefined;
78
+ isAllSelected?: boolean | undefined;
79
+ isSelectionIndeterminate?: boolean | undefined;
80
+ isReorderable?: boolean | undefined;
81
+ visibleCount?: number | undefined;
82
+ }
83
+
84
+ type RenderListFunction = (
85
+ options?: RenderOptions,
86
+ ) => ReturnType<typeof render>;
87
+
88
+ const renderList: RenderListFunction = (
89
+ options: RenderOptions = {},
90
+ ): ReturnType<typeof render> => {
91
+ return render(
92
+ <ResourceList
93
+ statusPageResources={
94
+ options.statusPageResources || [
95
+ API_RESOURCE,
96
+ DATABASE_RESOURCE,
97
+ CACHE_RESOURCE,
98
+ ]
99
+ }
100
+ listKey="test"
101
+ getResourceElement={(
102
+ statusPageResource: StatusPageResource,
103
+ ): ReactElement => {
104
+ return <span>{statusPageResource.monitor?.name || "Unknown"}</span>;
105
+ }}
106
+ isEditable={true}
107
+ isDeleteable={true}
108
+ isReorderable={
109
+ options.isReorderable === undefined ? false : options.isReorderable
110
+ }
111
+ isSelectable={
112
+ options.isSelectable === undefined ? true : options.isSelectable
113
+ }
114
+ selectedResourceIds={options.selectedResourceIds || new Set<string>()}
115
+ isAllSelected={Boolean(options.isAllSelected)}
116
+ isSelectionIndeterminate={Boolean(options.isSelectionIndeterminate)}
117
+ onToggleResourceSelected={onToggleResourceSelected}
118
+ onToggleAllSelected={onToggleAllSelected}
119
+ onEdit={() => {}}
120
+ onDelete={() => {}}
121
+ onShowId={() => {}}
122
+ onReorder={onReorder}
123
+ visibleCount={
124
+ options.visibleCount === undefined ? 100 : options.visibleCount
125
+ }
126
+ onShowMore={onShowMore}
127
+ emptyState={<div data-testid="empty-state">Nothing here</div>}
128
+ />,
129
+ );
130
+ };
131
+
132
+ type SelectAllBoxFunction = () => HTMLElement;
133
+
134
+ const selectAllBox: SelectAllBoxFunction = (): HTMLElement => {
135
+ return screen.getByTestId("status-page-resource-list-select-all");
136
+ };
137
+
138
+ type RowByNameFunction = (name: string) => HTMLElement;
139
+
140
+ const rowByName: RowByNameFunction = (name: string): HTMLElement => {
141
+ const row: HTMLElement | undefined = screen
142
+ .queryAllByTestId("status-page-resource-row")
143
+ .find((candidate: HTMLElement) => {
144
+ return (candidate.textContent || "").includes(name);
145
+ });
146
+
147
+ if (!row) {
148
+ throw new Error(`No row called ${name} is on screen`);
149
+ }
150
+
151
+ return row;
152
+ };
153
+
154
+ type RowBoxFunction = (name: string) => HTMLElement;
155
+
156
+ const rowBox: RowBoxFunction = (name: string): HTMLElement => {
157
+ return within(rowByName(name)).getByRole("checkbox");
158
+ };
159
+
160
+ describe("ResourceList - selecting rows", () => {
161
+ beforeEach(() => {
162
+ jest.clearAllMocks();
163
+ });
164
+
165
+ describe("when the operator may remove resources", () => {
166
+ test("every row carries a box", () => {
167
+ renderList();
168
+
169
+ expect(
170
+ screen.queryAllByTestId("status-page-resource-row-select").length,
171
+ ).toBe(3);
172
+ });
173
+
174
+ /*
175
+ * A bare box in a row is announced as "checkbox" and nothing else, so a
176
+ * screen reader user hears the same word once per row with no way to tell
177
+ * which row they are on.
178
+ */
179
+ test("a row's box is named after the monitor it selects", () => {
180
+ renderList();
181
+
182
+ expect(screen.getByLabelText("Select API")).toBeInTheDocument();
183
+ expect(screen.getByLabelText("Select Database")).toBeInTheDocument();
184
+ });
185
+
186
+ test("the list is topped by a box that selects all of it", () => {
187
+ renderList();
188
+
189
+ expect(selectAllBox()).toBeInTheDocument();
190
+ expect(screen.getByLabelText("Select all resources")).toBe(
191
+ selectAllBox(),
192
+ );
193
+ });
194
+
195
+ test("ticking a row reports that row, not its index", () => {
196
+ renderList();
197
+
198
+ fireEvent.click(rowBox("Database"));
199
+
200
+ expect(onToggleResourceSelected).toHaveBeenCalledTimes(1);
201
+ expect(onToggleResourceSelected.mock.calls[0]![0]).toBe(
202
+ DATABASE_RESOURCE,
203
+ );
204
+ });
205
+
206
+ test("un-ticking a selected row reports it too", () => {
207
+ renderList({
208
+ selectedResourceIds: new Set<string>(["resource-database"]),
209
+ });
210
+
211
+ fireEvent.click(rowBox("Database"));
212
+
213
+ expect(onToggleResourceSelected.mock.calls[0]![0]).toBe(
214
+ DATABASE_RESOURCE,
215
+ );
216
+ });
217
+
218
+ test("a selected row is ticked and says so to anything reading the DOM", () => {
219
+ renderList({ selectedResourceIds: new Set<string>(["resource-api"]) });
220
+
221
+ expect(rowBox("API")).toBeChecked();
222
+ expect(rowBox("Database")).not.toBeChecked();
223
+
224
+ expect(rowByName("API")).toHaveAttribute("data-selected", "true");
225
+ expect(rowByName("Database")).toHaveAttribute("data-selected", "false");
226
+ });
227
+
228
+ /*
229
+ * A selection made near the top of a long list has to stay visible from the
230
+ * bottom of it: the bulk bar says how many, and the fill says which.
231
+ */
232
+ test("a selected row is filled rather than left white", () => {
233
+ renderList({ selectedResourceIds: new Set<string>(["resource-api"]) });
234
+
235
+ expect(rowByName("API").className).toContain("bg-indigo-50");
236
+ expect(rowByName("Database").className).toContain("bg-white");
237
+ });
238
+
239
+ test("the box at the top follows the state it is handed", () => {
240
+ const { rerender } = renderList({ isAllSelected: true });
241
+
242
+ expect(selectAllBox()).toBeChecked();
243
+
244
+ rerender(
245
+ <ResourceList
246
+ statusPageResources={[API_RESOURCE]}
247
+ listKey="test"
248
+ getResourceElement={(): ReactElement => {
249
+ return <span>API</span>;
250
+ }}
251
+ isEditable={true}
252
+ isDeleteable={true}
253
+ isReorderable={false}
254
+ isSelectable={true}
255
+ selectedResourceIds={new Set<string>()}
256
+ isAllSelected={false}
257
+ isSelectionIndeterminate={true}
258
+ onToggleResourceSelected={onToggleResourceSelected}
259
+ onToggleAllSelected={onToggleAllSelected}
260
+ onEdit={() => {}}
261
+ onDelete={() => {}}
262
+ onShowId={() => {}}
263
+ onReorder={onReorder}
264
+ visibleCount={100}
265
+ onShowMore={onShowMore}
266
+ emptyState={<div />}
267
+ />,
268
+ );
269
+
270
+ expect(selectAllBox()).not.toBeChecked();
271
+ expect((selectAllBox() as HTMLInputElement).indeterminate).toBe(true);
272
+ });
273
+
274
+ test("ticking the box at the top asks for everything, un-ticking for nothing", () => {
275
+ const { rerender } = renderList();
276
+
277
+ fireEvent.click(selectAllBox());
278
+
279
+ expect(onToggleAllSelected).toHaveBeenCalledTimes(1);
280
+ expect(onToggleAllSelected.mock.calls[0]![0]).toBe(true);
281
+
282
+ rerender(
283
+ <ResourceList
284
+ statusPageResources={[API_RESOURCE]}
285
+ listKey="test"
286
+ getResourceElement={(): ReactElement => {
287
+ return <span>API</span>;
288
+ }}
289
+ isEditable={true}
290
+ isDeleteable={true}
291
+ isReorderable={false}
292
+ isSelectable={true}
293
+ selectedResourceIds={new Set<string>(["resource-api"])}
294
+ isAllSelected={true}
295
+ isSelectionIndeterminate={false}
296
+ onToggleResourceSelected={onToggleResourceSelected}
297
+ onToggleAllSelected={onToggleAllSelected}
298
+ onEdit={() => {}}
299
+ onDelete={() => {}}
300
+ onShowId={() => {}}
301
+ onReorder={onReorder}
302
+ visibleCount={100}
303
+ onShowMore={onShowMore}
304
+ emptyState={<div />}
305
+ />,
306
+ );
307
+
308
+ fireEvent.click(selectAllBox());
309
+
310
+ expect(onToggleAllSelected).toHaveBeenCalledTimes(2);
311
+ expect(onToggleAllSelected.mock.calls[1]![0]).toBe(false);
312
+ });
313
+
314
+ /*
315
+ * The header row is the only place the select-all box can live, and below
316
+ * sm it was hidden because it was labelling columns that are not drawn
317
+ * there. A phone is exactly where removing twenty seven monitors one at a
318
+ * time hurts most, so the row itself is drawn at every width and the column
319
+ * headings are what is held back.
320
+ */
321
+ test("the header is drawn at every width once there is a box in it", () => {
322
+ renderList();
323
+
324
+ const header: HTMLElement = screen.getByTestId(
325
+ "status-page-resource-list-header",
326
+ );
327
+
328
+ expect(header.className).toContain("flex");
329
+ expect(header.className).not.toContain("hidden sm:flex");
330
+ expect(within(header).getByText("Select all")).toBeInTheDocument();
331
+ });
332
+
333
+ /*
334
+ * A focusable control inside an aria-hidden container is in the tab order
335
+ * and out of the accessibility tree at the same time, which is worse than
336
+ * not offering it at all.
337
+ */
338
+ test("the header does not hide its own checkbox from a screen reader", () => {
339
+ renderList();
340
+
341
+ expect(
342
+ screen.getByTestId("status-page-resource-list-header"),
343
+ ).not.toHaveAttribute("aria-hidden");
344
+ });
345
+
346
+ test("a row with no id cannot be ticked", () => {
347
+ renderList({
348
+ statusPageResources: [makeResource({ monitorName: "Orphan" })],
349
+ });
350
+
351
+ expect(rowBox("Orphan")).toBeDisabled();
352
+ });
353
+
354
+ /*
355
+ * The box at the top means "everything this list was handed", and the rows
356
+ * behind "Show more" were handed to it - they are simply not drawn yet. The
357
+ * caller is the one that resolves that to ids, so all the list has to do is
358
+ * ask once.
359
+ */
360
+ test("rows held back behind Show more still have a box waiting for them", () => {
361
+ renderList({ visibleCount: 2 });
362
+
363
+ expect(
364
+ screen.queryAllByTestId("status-page-resource-row-select").length,
365
+ ).toBe(2);
366
+
367
+ fireEvent.click(selectAllBox());
368
+
369
+ expect(onToggleAllSelected).toHaveBeenCalledTimes(1);
370
+ });
371
+
372
+ /*
373
+ * The grip is the drag handle; the box is not. A list that can be both
374
+ * reordered and selected has to keep the two apart, or every attempt to
375
+ * tick a row starts a drag.
376
+ */
377
+ test("a reorderable list keeps its grip and its box apart", () => {
378
+ renderList({ isReorderable: true });
379
+
380
+ const row: HTMLElement = rowByName("API");
381
+
382
+ expect(
383
+ within(row).getByTestId("status-page-resource-row-drag-handle"),
384
+ ).toBeInTheDocument();
385
+ expect(within(row).getByRole("checkbox")).toBeInTheDocument();
386
+ });
387
+
388
+ test("an empty list is still the empty state, not a header over nothing", () => {
389
+ renderList({ statusPageResources: [] });
390
+
391
+ expect(screen.getByTestId("empty-state")).toBeInTheDocument();
392
+ expect(
393
+ screen.queryByTestId("status-page-resource-list-select-all"),
394
+ ).not.toBeInTheDocument();
395
+ });
396
+ });
397
+
398
+ describe("when the operator may not remove resources", () => {
399
+ /*
400
+ * Removing is the only thing a selection is for here, so without it the
401
+ * column would be a row of controls that lead nowhere - and it would cost
402
+ * every row a slice of the width its monitor's name is drawn in.
403
+ */
404
+ test("there is no column of boxes at all", () => {
405
+ renderList({ isSelectable: false });
406
+
407
+ expect(
408
+ screen.queryAllByTestId("status-page-resource-row-select").length,
409
+ ).toBe(0);
410
+ expect(screen.queryAllByRole("checkbox").length).toBe(0);
411
+ });
412
+
413
+ test("the header goes back to being column labels from sm up", () => {
414
+ renderList({ isSelectable: false });
415
+
416
+ const header: HTMLElement = screen.getByTestId(
417
+ "status-page-resource-list-header",
418
+ );
419
+
420
+ expect(header.className).toContain("hidden");
421
+ expect(header.className).toContain("sm:flex");
422
+ expect(within(header).queryByText("Select all")).not.toBeInTheDocument();
423
+ });
424
+
425
+ test("no row claims to be selected", () => {
426
+ renderList({
427
+ isSelectable: false,
428
+ /* Even handed a selection, which the pane would never do. */
429
+ selectedResourceIds: new Set<string>(["resource-api"]),
430
+ });
431
+
432
+ expect(rowByName("API")).toHaveAttribute("data-selected", "false");
433
+ expect(rowByName("API").className).toContain("bg-white");
434
+ });
435
+ });
436
+ });
@@ -142,8 +142,12 @@ describe("Table bulk CSV export", () => {
142
142
  bulkSelectedItems: [data[0]!],
143
143
  });
144
144
 
145
- // The bulk action bar is visible with the selected count.
146
- expect(getByText("1 Monitors Selected")).toBeTruthy();
145
+ /*
146
+ * The bulk action bar is visible with the selected count - in the singular
147
+ * for one row, because that badge is always on screen and "1 Monitors
148
+ * Selected" reads as a bug in the count.
149
+ */
150
+ expect(getByText("1 Monitor Selected")).toBeTruthy();
147
151
 
148
152
  // Open the Bulk Actions menu.
149
153
  fireEvent.click(getByText("Bulk Actions"));
@@ -46,6 +46,31 @@ describe("Toggle", () => {
46
46
  expect(onChange).toHaveBeenCalledWith(true);
47
47
  });
48
48
 
49
+ test("calls onChange exactly once per click", () => {
50
+ /*
51
+ * The click handler used to call handleChange (which already calls
52
+ * props.onChange) and then props.onChange again, running every consumer's
53
+ * handler twice per click. Idempotent handlers never noticed; the monitor
54
+ * criteria switches, which seed a blank incident / alert row on the way
55
+ * on, were saved from a second row only by their own "is the array still
56
+ * empty" guard.
57
+ */
58
+ const onChange: MockFunction = getJestMockFunction();
59
+
60
+ const { getByRole } = render(
61
+ <Toggle onChange={onChange} initialValue={false} />,
62
+ );
63
+ const toggle: HTMLElement = getByRole("switch");
64
+
65
+ fireEvent.click(toggle);
66
+ expect(onChange).toHaveBeenCalledTimes(1);
67
+ expect(onChange).toHaveBeenNthCalledWith(1, true);
68
+
69
+ fireEvent.click(toggle);
70
+ expect(onChange).toHaveBeenCalledTimes(2);
71
+ expect(onChange).toHaveBeenNthCalledWith(2, false);
72
+ });
73
+
49
74
  test("calls onFocus", () => {
50
75
  const onFocus: MockFunction = getJestMockFunction();
51
76