@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.
Files changed (345) hide show
  1. package/Models/DatabaseModels/DetectionRule.ts +55 -0
  2. package/Models/DatabaseModels/Index.ts +2 -0
  3. package/Models/DatabaseModels/NetworkDevice.ts +44 -0
  4. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
  5. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
  6. package/Models/DatabaseModels/Project.ts +68 -0
  7. package/Models/DatabaseModels/User.ts +68 -0
  8. package/Server/API/TelemetryAPI.ts +29 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  16. package/Server/Middleware/GzipRequestBody.ts +311 -0
  17. package/Server/Middleware/MultipartFormData.ts +87 -3
  18. package/Server/Services/DetectionRuleService.ts +73 -6
  19. package/Server/Services/Index.ts +207 -0
  20. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
  21. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -0
  22. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
  23. package/Server/Services/NetworkDeviceService.ts +148 -0
  24. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  25. package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
  26. package/Server/Services/ProjectService.ts +29 -19
  27. package/Server/Services/SecurityEventService.ts +90 -13
  28. package/Server/Services/StatusPageResourceService.ts +223 -0
  29. package/Server/Services/TelemetryAttributeService.ts +17 -0
  30. package/Server/Services/UserService.ts +4 -6
  31. package/Server/Types/Markdown.ts +161 -67
  32. package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
  33. package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
  34. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
  35. package/Server/Utils/Attribution.ts +4 -0
  36. package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
  37. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +39 -1
  38. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
  39. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
  40. package/Server/Utils/StartServer.ts +12 -32
  41. package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
  42. package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
  43. package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
  44. package/Server/Views/Partials/Head.ejs +2 -1
  45. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
  46. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
  47. package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
  48. package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
  49. package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
  50. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
  51. package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
  52. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
  53. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
  54. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
  55. package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
  56. package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
  57. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
  58. package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
  59. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
  60. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
  61. package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
  62. package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
  63. package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
  64. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
  65. package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
  66. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
  67. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
  68. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
  69. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
  70. package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
  71. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
  72. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
  73. package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
  74. package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
  75. package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
  76. package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -0
  77. package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
  78. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
  79. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
  80. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -0
  81. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
  82. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
  83. package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
  84. package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
  85. package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
  86. package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
  87. package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
  88. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
  89. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
  91. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +97 -1
  92. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
  93. package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
  94. package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
  95. package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
  96. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
  97. package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
  98. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
  99. package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
  100. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
  101. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
  102. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
  103. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
  104. package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
  105. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
  106. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
  107. package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
  108. package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
  109. package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
  110. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
  111. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
  112. package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
  113. package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
  114. package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
  115. package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
  116. package/Tests/UI/Components/SideOver.test.tsx +1 -1
  117. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
  118. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
  119. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
  120. package/Tests/UI/Components/Toggle.test.tsx +25 -0
  121. package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
  122. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
  123. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +516 -0
  124. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
  125. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
  126. package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
  127. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
  128. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
  129. package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
  130. package/Types/Code/YamlSyntax.ts +396 -0
  131. package/Types/Exception/ExceptionCode.ts +1 -0
  132. package/Types/Exception/PayloadTooLargeException.ts +8 -0
  133. package/Types/Marketing/Attribution.ts +10 -0
  134. package/Types/Marketing/MarketingEvent.ts +4 -0
  135. package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
  136. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
  137. package/Types/NetworkAutomation/RuleRunResult.ts +84 -0
  138. package/Types/Permission.ts +47 -0
  139. package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
  140. package/Types/Telemetry/LlmMetricConventions.ts +124 -0
  141. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
  142. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
  143. package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
  144. package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
  145. package/UI/Components/Detail/Detail.tsx +10 -0
  146. package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
  147. package/UI/Components/Forms/Fields/FormField.tsx +31 -0
  148. package/UI/Components/Forms/Types/Field.ts +8 -0
  149. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  150. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
  151. package/UI/Components/Forms/Validation.ts +46 -0
  152. package/UI/Components/Input/Input.tsx +3 -0
  153. package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
  154. package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
  155. package/UI/Components/ModelTable/Column.ts +8 -0
  156. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
  157. package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
  158. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
  159. package/UI/Components/SideOver/SideOver.tsx +5 -5
  160. package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
  161. package/UI/Components/StatusPage/ResourceList.tsx +197 -38
  162. package/UI/Components/Toggle/Toggle.tsx +9 -1
  163. package/UI/Components/Types/FieldType.ts +1 -0
  164. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
  165. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +257 -0
  166. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
  167. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
  168. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
  169. package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
  170. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
  171. package/Utils/StatusPage/ResourceExplorer.ts +258 -0
  172. package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
  173. package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
  174. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  176. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
  178. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
  180. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
  181. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -0
  182. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/Project.js +76 -0
  184. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/User.js +76 -0
  186. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  187. package/build/dist/Server/API/TelemetryAPI.js +17 -0
  188. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
  190. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
  191. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
  192. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
  193. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
  194. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
  195. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
  196. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
  197. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
  198. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
  199. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
  200. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
  201. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  202. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  203. package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
  204. package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
  205. package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
  206. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  207. package/build/dist/Server/Services/DetectionRuleService.js +49 -4
  208. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  209. package/build/dist/Server/Services/Index.js +206 -0
  210. package/build/dist/Server/Services/Index.js.map +1 -1
  211. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
  212. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
  213. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
  214. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
  215. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
  216. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  217. package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
  218. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  219. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
  220. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  221. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
  222. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
  223. package/build/dist/Server/Services/ProjectService.js +25 -45
  224. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  225. package/build/dist/Server/Services/SecurityEventService.js +57 -5
  226. package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
  227. package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
  228. package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
  229. package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
  230. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  231. package/build/dist/Server/Services/UserService.js +2 -12
  232. package/build/dist/Server/Services/UserService.js.map +1 -1
  233. package/build/dist/Server/Types/Markdown.js +147 -61
  234. package/build/dist/Server/Types/Markdown.js.map +1 -1
  235. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
  236. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
  237. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
  238. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  239. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
  240. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  241. package/build/dist/Server/Utils/Attribution.js.map +1 -1
  242. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
  243. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +26 -0
  245. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  246. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
  247. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  248. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
  249. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  250. package/build/dist/Server/Utils/StartServer.js +12 -21
  251. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  252. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
  253. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
  254. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
  255. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
  256. package/build/dist/Types/Code/YamlSyntax.js +212 -0
  257. package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
  258. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  259. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  260. package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
  261. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
  262. package/build/dist/Types/Marketing/Attribution.js +10 -0
  263. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
  265. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  266. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
  267. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  268. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +24 -0
  269. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  270. package/build/dist/Types/Permission.js +41 -0
  271. package/build/dist/Types/Permission.js.map +1 -1
  272. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
  273. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  274. package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
  275. package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
  276. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  277. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  278. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
  279. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  280. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
  281. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  282. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
  283. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
  284. package/build/dist/UI/Components/Detail/Detail.js +9 -0
  285. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  286. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
  287. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  288. package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
  289. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  290. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  291. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  292. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  293. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
  294. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  295. package/build/dist/UI/Components/Forms/Validation.js +27 -0
  296. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  297. package/build/dist/UI/Components/Input/Input.js +1 -1
  298. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  299. package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
  300. package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
  301. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
  302. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  303. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
  304. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  305. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
  306. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
  307. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
  308. package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
  309. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
  310. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  311. package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
  312. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  313. package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
  314. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  315. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  316. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  317. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
  318. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  319. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +149 -0
  320. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
  321. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
  322. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
  323. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
  324. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
  325. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
  326. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
  327. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
  328. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
  329. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
  330. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  331. package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
  332. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
  333. package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
  334. package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
  335. package/jest.config.json +2 -0
  336. package/package.json +1 -1
  337. package/tsconfig.json +2 -1
  338. package/Tests/UI/Components/Toast.test.tsx +0 -197
  339. package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
  340. package/UI/Components/Toast/Toast.tsx +0 -189
  341. package/UI/Components/Toast/ToastInit.tsx +0 -110
  342. package/build/dist/UI/Components/Toast/Toast.js +0 -123
  343. package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
  344. package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
  345. package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
@@ -0,0 +1,460 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import { cleanup, render, screen, waitFor } from "@testing-library/react";
11
+ import React from "react";
12
+ import getJestMockFunction, { MockFunction } from "../../MockType";
13
+
14
+ /*
15
+ * https://github.com/OneUptime/oneuptime/issues/3411
16
+ *
17
+ * The schedule preview (the "Final schedule" calendar and the "On call right
18
+ * now" card above it) is the screen an on-call engineer opens to answer "who is
19
+ * covering?". Alert routing answers the same question through
20
+ * OnCallDutyPolicyEscalationRuleService -> getCurrentUserIdInSchedule, which is
21
+ * handed the escalating policy's id and therefore applies BOTH global and
22
+ * policy-scoped user overrides.
23
+ *
24
+ * The preview used to hard-code `onCallDutyPolicyId: IsNull()` on its override
25
+ * fetch, so an override created from the on-call policy's own "User Overrides"
26
+ * tab - the obvious place to add a shift override, and the one that stamps
27
+ * onCallDutyPolicyId - never reached the calendar. The page then contradicted
28
+ * itself: the banner at the top of the same screen reads the persisted roster,
29
+ * which the server resolves in the single attached policy's context and which
30
+ * therefore DID show the substitute, while the calendar underneath kept showing
31
+ * the original user.
32
+ *
33
+ * These tests drive the real component with a mocked ModelAPI that filters the
34
+ * override list exactly the way the server's query would, so an assertion here
35
+ * fails if the component stops asking for the right rows OR stops passing the
36
+ * policy context into UserOverrideUtil - the two independent halves of the bug.
37
+ */
38
+
39
+ const getListMock: MockFunction = getJestMockFunction();
40
+
41
+ /*
42
+ * The arrow wrappers matter: jest.mock is hoisted above the compiled requires,
43
+ * so the consts above are still in their temporal dead zone when the factory
44
+ * body runs. Dereferencing lazily, at call time, is what works.
45
+ */
46
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
47
+ return {
48
+ __esModule: true,
49
+ default: {
50
+ getList: (...args: Array<any>) => {
51
+ return getListMock(...args);
52
+ },
53
+ },
54
+ };
55
+ });
56
+
57
+ jest.mock("../../../UI/Utils/Project", () => {
58
+ return {
59
+ __esModule: true,
60
+ default: {
61
+ getCurrentProjectId: () => {
62
+ return {
63
+ toString: (): string => {
64
+ return PROJECT_ID;
65
+ },
66
+ };
67
+ },
68
+ },
69
+ };
70
+ });
71
+
72
+ /*
73
+ * react-big-calendar renders a grid that is awkward to assert against and slow
74
+ * to mount. What matters here is the event list the preview hands it, so the
75
+ * calendar is replaced by a plain list of event titles. The substitution is
76
+ * visible in the DOM either way, so a regression that stops relabelling events
77
+ * still fails.
78
+ */
79
+ jest.mock("../../../UI/Components/Calendar/Calendar", () => {
80
+ return {
81
+ __esModule: true,
82
+ default: (props: any) => {
83
+ return (
84
+ <ul data-testid="calendar-events">
85
+ {(props.events || []).map((event: any, index: number) => {
86
+ return (
87
+ <li key={index} data-testid="calendar-event">
88
+ {event.title}
89
+ </li>
90
+ );
91
+ })}
92
+ </ul>
93
+ );
94
+ },
95
+ DefaultCalendarView: {
96
+ Month: "month",
97
+ Week: "week",
98
+ Day: "day",
99
+ Agenda: "agenda",
100
+ },
101
+ };
102
+ });
103
+
104
+ import LayersPreview from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallScheduleLayer/LayersPreview";
105
+ import OnCallDutyPolicyEscalationRuleSchedule from "../../../Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule";
106
+ import OnCallDutyPolicyScheduleLayer from "../../../Models/DatabaseModels/OnCallDutyPolicyScheduleLayer";
107
+ import OnCallDutyPolicyScheduleLayerUser from "../../../Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser";
108
+ import OnCallDutyPolicyUserOverride from "../../../Models/DatabaseModels/OnCallDutyPolicyUserOverride";
109
+ import User from "../../../Models/DatabaseModels/User";
110
+ import IsNull from "../../../Types/BaseDatabase/IsNull";
111
+ import EqualToOrNull from "../../../Types/BaseDatabase/EqualToOrNull";
112
+ import Dictionary from "../../../Types/Dictionary";
113
+ import OneUptimeDate from "../../../Types/Date";
114
+ import EventInterval from "../../../Types/Events/EventInterval";
115
+ import Recurring from "../../../Types/Events/Recurring";
116
+ import ObjectID from "../../../Types/ObjectID";
117
+ import RestrictionTimes, {
118
+ RestrictionType,
119
+ } from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
120
+
121
+ const PROJECT_ID: string = "11111111-1111-4111-8111-111111111111";
122
+ const SCHEDULE_ID: string = "22222222-2222-4222-8222-222222222222";
123
+ const LAYER_ID: string = "33333333-3333-4333-8333-333333333333";
124
+ const POLICY_ID: string = "44444444-4444-4444-8444-444444444444";
125
+ const OTHER_POLICY_ID: string = "55555555-5555-4555-8555-555555555555";
126
+
127
+ const USER_A_ID: string = "aaaaaaaa-1111-4111-8111-111111111111";
128
+ const USER_B_ID: string = "bbbbbbbb-2222-4222-8222-222222222222";
129
+ const USER_C_ID: string = "cccccccc-3333-4333-8333-333333333333";
130
+
131
+ const USER_A_NAME: string = "Alice Scheduled";
132
+ const USER_B_NAME: string = "Bob Covering";
133
+ const USER_C_NAME: string = "Carol Elsewhere";
134
+
135
+ const objectId: (id: string) => ObjectID = (id: string): ObjectID => {
136
+ return new ObjectID(id);
137
+ };
138
+
139
+ function makeUser(id: string, name: string): User {
140
+ const user: User = new User();
141
+ user.id = objectId(id);
142
+ user.name = name as any;
143
+ user.email = `${id}@example.com` as any;
144
+ return user;
145
+ }
146
+
147
+ /*
148
+ * A single 24/7 layer that started well in the past and rotates daily, so
149
+ * exactly one user - the only one assigned - is on call at "now" no matter when
150
+ * the suite runs. That makes "who does the preview show right now" a question
151
+ * with one correct answer, which is the whole point.
152
+ */
153
+ function makeLayer(): OnCallDutyPolicyScheduleLayer {
154
+ const layer: OnCallDutyPolicyScheduleLayer =
155
+ new OnCallDutyPolicyScheduleLayer();
156
+ layer.id = objectId(LAYER_ID);
157
+ layer.projectId = objectId(PROJECT_ID);
158
+ layer.onCallDutyPolicyScheduleId = objectId(SCHEDULE_ID);
159
+ layer.order = 1;
160
+ layer.name = "Primary" as any;
161
+
162
+ const now: Date = OneUptimeDate.getCurrentDate();
163
+ layer.startsAt = OneUptimeDate.addRemoveDays(now, -30);
164
+ layer.handOffTime = OneUptimeDate.addRemoveDays(now, -30);
165
+
166
+ layer.rotation = Recurring.fromJSON({
167
+ _type: "Recurring",
168
+ value: {
169
+ intervalType: EventInterval.Day,
170
+ intervalCount: { _type: "PositiveNumber", value: 1 },
171
+ },
172
+ } as any) as any;
173
+
174
+ const restrictionTimes: RestrictionTimes = new RestrictionTimes();
175
+ restrictionTimes.restictionType = RestrictionType.None;
176
+ restrictionTimes.dayRestrictionTimes = null;
177
+ layer.restrictionTimes = restrictionTimes as any;
178
+
179
+ return layer;
180
+ }
181
+
182
+ function makeLayerUsers(): Dictionary<
183
+ Array<OnCallDutyPolicyScheduleLayerUser>
184
+ > {
185
+ const layerUser: OnCallDutyPolicyScheduleLayerUser =
186
+ new OnCallDutyPolicyScheduleLayerUser();
187
+ layerUser.id = objectId("66666666-6666-4666-8666-666666666666");
188
+ layerUser.onCallDutyPolicyScheduleLayerId = objectId(LAYER_ID);
189
+ layerUser.onCallDutyPolicyScheduleId = objectId(SCHEDULE_ID);
190
+ layerUser.projectId = objectId(PROJECT_ID);
191
+ layerUser.order = 1;
192
+ layerUser.userId = objectId(USER_A_ID);
193
+ layerUser.user = makeUser(USER_A_ID, USER_A_NAME);
194
+
195
+ return { [LAYER_ID]: [layerUser] };
196
+ }
197
+
198
+ interface OverrideFixture {
199
+ overrideUserId: string;
200
+ routeAlertsToUserId: string;
201
+ routeAlertsToUserName: string;
202
+ onCallDutyPolicyId: string | null;
203
+ startsAt: Date;
204
+ endsAt: Date;
205
+ }
206
+
207
+ function activeOverride(
208
+ onCallDutyPolicyId: string | null,
209
+ routeAlertsToUserId: string = USER_B_ID,
210
+ routeAlertsToUserName: string = USER_B_NAME,
211
+ ): OverrideFixture {
212
+ const now: Date = OneUptimeDate.getCurrentDate();
213
+ return {
214
+ overrideUserId: USER_A_ID,
215
+ routeAlertsToUserId,
216
+ routeAlertsToUserName,
217
+ onCallDutyPolicyId,
218
+ startsAt: OneUptimeDate.addRemoveHours(now, -1),
219
+ endsAt: OneUptimeDate.addRemoveHours(now, 1),
220
+ };
221
+ }
222
+
223
+ function toModel(fixture: OverrideFixture): OnCallDutyPolicyUserOverride {
224
+ const model: OnCallDutyPolicyUserOverride =
225
+ new OnCallDutyPolicyUserOverride();
226
+ model.id = objectId("77777777-7777-4777-8777-777777777777");
227
+ model.projectId = objectId(PROJECT_ID);
228
+ model.overrideUserId = objectId(fixture.overrideUserId);
229
+ model.routeAlertsToUserId = objectId(fixture.routeAlertsToUserId);
230
+ model.startsAt = fixture.startsAt;
231
+ model.endsAt = fixture.endsAt;
232
+ model.onCallDutyPolicyId = fixture.onCallDutyPolicyId
233
+ ? objectId(fixture.onCallDutyPolicyId)
234
+ : (null as any);
235
+ model.overrideUser = makeUser(fixture.overrideUserId, USER_A_NAME);
236
+ model.routeAlertsToUser = makeUser(
237
+ fixture.routeAlertsToUserId,
238
+ fixture.routeAlertsToUserName,
239
+ );
240
+ return model;
241
+ }
242
+
243
+ /*
244
+ * Stands in for the server's WHERE clause on onCallDutyPolicyId. Without this
245
+ * the mock would hand back every override regardless of what the component
246
+ * asked for, and the test could not tell a component that asks for the right
247
+ * rows apart from one that asks for the wrong rows and gets lucky.
248
+ */
249
+ function matchesPolicyQuery(
250
+ fixture: OverrideFixture,
251
+ queryValue: unknown,
252
+ ): boolean {
253
+ if (queryValue instanceof IsNull) {
254
+ return fixture.onCallDutyPolicyId === null;
255
+ }
256
+
257
+ if (queryValue instanceof EqualToOrNull) {
258
+ return (
259
+ fixture.onCallDutyPolicyId === null ||
260
+ fixture.onCallDutyPolicyId === queryValue.toString()
261
+ );
262
+ }
263
+
264
+ if (queryValue === undefined || queryValue === null) {
265
+ // No filter at all - the server would return every row in the project.
266
+ return true;
267
+ }
268
+
269
+ return fixture.onCallDutyPolicyId === String(queryValue);
270
+ }
271
+
272
+ interface ScenarioOptions {
273
+ overrides: Array<OverrideFixture>;
274
+ attachedPolicyIds: Array<string>;
275
+ }
276
+
277
+ function setupApi(options: ScenarioOptions): void {
278
+ getListMock.mockImplementation((args: any) => {
279
+ const modelName: string = args?.modelType?.name || "";
280
+
281
+ if (modelName === "OnCallDutyPolicyUserOverride") {
282
+ const matched: Array<OverrideFixture> = options.overrides.filter(
283
+ (fixture: OverrideFixture) => {
284
+ return matchesPolicyQuery(fixture, args?.query?.onCallDutyPolicyId);
285
+ },
286
+ );
287
+
288
+ return Promise.resolve({
289
+ data: matched.map(toModel),
290
+ count: matched.length,
291
+ skip: 0,
292
+ limit: matched.length,
293
+ });
294
+ }
295
+
296
+ if (modelName === "OnCallDutyPolicyEscalationRuleSchedule") {
297
+ const joins: Array<OnCallDutyPolicyEscalationRuleSchedule> =
298
+ options.attachedPolicyIds.map((policyId: string, index: number) => {
299
+ const join: OnCallDutyPolicyEscalationRuleSchedule =
300
+ new OnCallDutyPolicyEscalationRuleSchedule();
301
+ join.id = objectId(
302
+ `8888888${index}-8888-4888-8888-888888888888`.slice(0, 36),
303
+ );
304
+ join.projectId = objectId(PROJECT_ID);
305
+ join.onCallDutyPolicyScheduleId = objectId(SCHEDULE_ID);
306
+ join.onCallDutyPolicyId = objectId(policyId);
307
+ return join;
308
+ });
309
+
310
+ return Promise.resolve({
311
+ data: joins,
312
+ count: joins.length,
313
+ skip: 0,
314
+ limit: joins.length,
315
+ });
316
+ }
317
+
318
+ return Promise.resolve({ data: [], count: 0, skip: 0, limit: 0 });
319
+ });
320
+ }
321
+
322
+ function renderPreview(): void {
323
+ render(
324
+ <LayersPreview
325
+ layers={[makeLayer()]}
326
+ allLayerUsers={makeLayerUsers()}
327
+ timezone="UTC"
328
+ onCallDutyPolicyScheduleId={objectId(SCHEDULE_ID)}
329
+ />,
330
+ );
331
+ }
332
+
333
+ /*
334
+ * The "On call right now" hero card. Reading the name out of that specific card
335
+ * (rather than out of the whole document) is what makes the assertion about the
336
+ * question the user asked - "who is covering right now" - and not about whether
337
+ * a name appears anywhere on a screen that also lists a whole week of shifts.
338
+ */
339
+ async function getOnCallNowText(): Promise<string> {
340
+ const heading: HTMLElement = await screen.findByText(/On call right now/i);
341
+ const card: HTMLElement | null = heading.closest("div.rounded-xl");
342
+ if (!card) {
343
+ throw new Error("Could not find the 'On call right now' card");
344
+ }
345
+ return card.textContent || "";
346
+ }
347
+
348
+ describe("Schedule preview reflects the overrides that alert routing applies (issue #3411)", () => {
349
+ beforeEach(() => {
350
+ getListMock.mockReset();
351
+ });
352
+
353
+ afterEach(() => {
354
+ cleanup();
355
+ });
356
+
357
+ test("a POLICY-SCOPED override is shown when the schedule is attached to exactly one policy", async () => {
358
+ setupApi({
359
+ overrides: [activeOverride(POLICY_ID)],
360
+ attachedPolicyIds: [POLICY_ID],
361
+ });
362
+
363
+ renderPreview();
364
+
365
+ await waitFor(
366
+ async () => {
367
+ expect(await getOnCallNowText()).toContain(USER_B_NAME);
368
+ },
369
+ { timeout: 10000 },
370
+ );
371
+
372
+ expect(await getOnCallNowText()).not.toContain(USER_A_NAME);
373
+ });
374
+
375
+ test("the calendar grid relabels the overridden window to the substitute", async () => {
376
+ setupApi({
377
+ overrides: [activeOverride(POLICY_ID)],
378
+ attachedPolicyIds: [POLICY_ID],
379
+ });
380
+
381
+ renderPreview();
382
+
383
+ /*
384
+ * A longer budget than the 1s default on purpose. The grid is painted from a
385
+ * useEffect, so it lands one commit after the summary (a useMemo) - plus two
386
+ * awaited round trips before that, for the policy context and the overrides.
387
+ */
388
+ await waitFor(
389
+ () => {
390
+ const titles: Array<string> = screen
391
+ .getAllByTestId("calendar-event")
392
+ .map((node: HTMLElement) => {
393
+ return node.textContent || "";
394
+ });
395
+
396
+ expect(
397
+ titles.some((title: string) => {
398
+ return (
399
+ title.includes(USER_B_NAME) &&
400
+ title.includes(`covering ${USER_A_NAME}`)
401
+ );
402
+ }),
403
+ ).toBe(true);
404
+ },
405
+ { timeout: 10000 },
406
+ );
407
+ });
408
+
409
+ test("a GLOBAL override is still shown (the behaviour that already worked must not regress)", async () => {
410
+ setupApi({
411
+ overrides: [activeOverride(null)],
412
+ attachedPolicyIds: [POLICY_ID],
413
+ });
414
+
415
+ renderPreview();
416
+
417
+ await waitFor(
418
+ async () => {
419
+ expect(await getOnCallNowText()).toContain(USER_B_NAME);
420
+ },
421
+ { timeout: 10000 },
422
+ );
423
+ });
424
+
425
+ test("an override scoped to a DIFFERENT policy is not shown", async () => {
426
+ setupApi({
427
+ overrides: [activeOverride(OTHER_POLICY_ID, USER_C_ID, USER_C_NAME)],
428
+ attachedPolicyIds: [POLICY_ID],
429
+ });
430
+
431
+ renderPreview();
432
+
433
+ await waitFor(
434
+ async () => {
435
+ expect(await getOnCallNowText()).toContain(USER_A_NAME);
436
+ },
437
+ { timeout: 10000 },
438
+ );
439
+
440
+ expect(await getOnCallNowText()).not.toContain(USER_C_NAME);
441
+ });
442
+
443
+ test("with the schedule attached to TWO policies only global overrides apply, matching the persisted roster", async () => {
444
+ setupApi({
445
+ overrides: [activeOverride(POLICY_ID, USER_C_ID, USER_C_NAME)],
446
+ attachedPolicyIds: [POLICY_ID, OTHER_POLICY_ID],
447
+ });
448
+
449
+ renderPreview();
450
+
451
+ await waitFor(
452
+ async () => {
453
+ expect(await getOnCallNowText()).toContain(USER_A_NAME);
454
+ },
455
+ { timeout: 10000 },
456
+ );
457
+
458
+ expect(await getOnCallNowText()).not.toContain(USER_C_NAME);
459
+ });
460
+ });
@@ -0,0 +1,171 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+
10
+ import SecurityEventAttributeUtil from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventAttributeUtil";
11
+ import API from "../../../UI/Utils/API/API";
12
+ import AnalyticsModelAPI from "../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
13
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
14
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
15
+ import URL from "../../../Types/API/URL";
16
+ import { JSONObject } from "../../../Types/JSON";
17
+ import Dictionary from "../../../Types/Dictionary";
18
+
19
+ /*
20
+ * The security events table's "Add Attribute Column" search is fed from this
21
+ * one request. Three things about it are worth pinning, because each fails
22
+ * quietly rather than loudly:
23
+ *
24
+ * - the route. A wrong path 404s, which the picker renders as an error the
25
+ * user cannot act on.
26
+ * - the tenant headers. AnalyticsModelAPI.getCommonHeaders carries the
27
+ * project id these bespoke telemetry routes read the tenant from; without
28
+ * it the request is rejected as having no project.
29
+ * - the shape of what comes back. It is JSON off the wire, so a non-array
30
+ * (or an array with junk in it) has to degrade to "no attributes" rather
31
+ * than generating columns titled "null" that read nothing off any row.
32
+ */
33
+
34
+ type PostArgs = {
35
+ url: URL;
36
+ data: JSONObject;
37
+ headers?: Dictionary<string> | undefined;
38
+ };
39
+
40
+ type MockPostFunction = (
41
+ response: HTTPResponse<JSONObject> | HTTPErrorResponse,
42
+ ) => jest.SpiedFunction<typeof API.post>;
43
+
44
+ const mockPost: MockPostFunction = (
45
+ response: HTTPResponse<JSONObject> | HTTPErrorResponse,
46
+ ): jest.SpiedFunction<typeof API.post> => {
47
+ return jest
48
+ .spyOn(API, "post")
49
+ .mockResolvedValue(response as never) as unknown as jest.SpiedFunction<
50
+ typeof API.post
51
+ >;
52
+ };
53
+
54
+ type FirstPostArgsFunction = (
55
+ spy: jest.SpiedFunction<typeof API.post>,
56
+ ) => PostArgs;
57
+
58
+ const firstPostArgs: FirstPostArgsFunction = (
59
+ spy: jest.SpiedFunction<typeof API.post>,
60
+ ): PostArgs => {
61
+ return (spy.mock.calls[0] as Array<unknown>)[0] as PostArgs;
62
+ };
63
+
64
+ type OkFunction = (data: JSONObject) => HTTPResponse<JSONObject>;
65
+
66
+ const ok: OkFunction = (data: JSONObject): HTTPResponse<JSONObject> => {
67
+ return new HTTPResponse<JSONObject>(200, data, {});
68
+ };
69
+
70
+ describe("SecurityEventAttributeUtil.getAttributeKeys", () => {
71
+ beforeEach(() => {
72
+ jest
73
+ .spyOn(AnalyticsModelAPI, "getCommonHeaders")
74
+ .mockReturnValue({ tenantid: "project-1" } as Dictionary<string>);
75
+ });
76
+
77
+ afterEach(() => {
78
+ jest.restoreAllMocks();
79
+ });
80
+
81
+ test("posts to the security events attribute route", async () => {
82
+ const spy: jest.SpiedFunction<typeof API.post> = mockPost(
83
+ ok({ attributes: [] }),
84
+ );
85
+
86
+ await SecurityEventAttributeUtil.getAttributeKeys();
87
+
88
+ expect(firstPostArgs(spy).url.toString()).toContain(
89
+ "/telemetry/security-events/get-attributes",
90
+ );
91
+ });
92
+
93
+ test("carries the tenant headers the route reads the project from", async () => {
94
+ const spy: jest.SpiedFunction<typeof API.post> = mockPost(
95
+ ok({ attributes: [] }),
96
+ );
97
+
98
+ await SecurityEventAttributeUtil.getAttributeKeys();
99
+
100
+ expect(firstPostArgs(spy).headers).toMatchObject({
101
+ tenantid: "project-1",
102
+ });
103
+ });
104
+
105
+ test("returns the keys the server sent", async () => {
106
+ mockPost(
107
+ ok({
108
+ attributes: ["device.hostname", "class_uid", "finding_info.title"],
109
+ }),
110
+ );
111
+
112
+ await expect(
113
+ SecurityEventAttributeUtil.getAttributeKeys(),
114
+ ).resolves.toEqual(["device.hostname", "class_uid", "finding_info.title"]);
115
+ });
116
+
117
+ test("an empty list is an empty list, not an error", async () => {
118
+ mockPost(ok({ attributes: [] }));
119
+
120
+ await expect(
121
+ SecurityEventAttributeUtil.getAttributeKeys(),
122
+ ).resolves.toEqual([]);
123
+ });
124
+
125
+ test("a missing attributes field degrades to no attributes", async () => {
126
+ mockPost(ok({ someOtherField: true }));
127
+
128
+ await expect(
129
+ SecurityEventAttributeUtil.getAttributeKeys(),
130
+ ).resolves.toEqual([]);
131
+ });
132
+
133
+ test("a non-array attributes field degrades to no attributes", async () => {
134
+ mockPost(ok({ attributes: "device.hostname" }));
135
+
136
+ await expect(
137
+ SecurityEventAttributeUtil.getAttributeKeys(),
138
+ ).resolves.toEqual([]);
139
+ });
140
+
141
+ test("non-string entries are dropped rather than turned into columns", async () => {
142
+ mockPost(
143
+ ok({
144
+ attributes: ["device.hostname", null, 42, { a: 1 }, "class_uid"],
145
+ } as unknown as JSONObject),
146
+ );
147
+
148
+ await expect(
149
+ SecurityEventAttributeUtil.getAttributeKeys(),
150
+ ).resolves.toEqual(["device.hostname", "class_uid"]);
151
+ });
152
+
153
+ /*
154
+ * The caller turns a rejection into the picker's error message. Swallowing
155
+ * it here would show an empty search box instead, which reads as "this
156
+ * project has no attributes".
157
+ */
158
+ test("an error response is thrown, not swallowed", async () => {
159
+ const error: HTTPErrorResponse = new HTTPErrorResponse(
160
+ 500,
161
+ { message: "clickhouse said no" },
162
+ {},
163
+ );
164
+
165
+ mockPost(error);
166
+
167
+ await expect(SecurityEventAttributeUtil.getAttributeKeys()).rejects.toBe(
168
+ error,
169
+ );
170
+ });
171
+ });