@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,352 @@
1
+ import "@testing-library/jest-dom";
2
+ import CriteriaFilterUtil from "../../../../App/FeatureSet/Dashboard/src/Utils/Form/Monitor/CriteriaFilter";
3
+ import {
4
+ afterEach,
5
+ beforeEach,
6
+ describe,
7
+ expect,
8
+ jest,
9
+ test,
10
+ } from "@jest/globals";
11
+ import { cleanup, render, screen, waitFor } from "@testing-library/react";
12
+ import * as React from "react";
13
+ import { CheckOn, CriteriaFilter } from "../../../Types/Monitor/CriteriaFilter";
14
+ import MonitorCriteriaInstance from "../../../Types/Monitor/MonitorCriteriaInstance";
15
+ import MonitorStep from "../../../Types/Monitor/MonitorStep";
16
+ import MonitorSteps from "../../../Types/Monitor/MonitorSteps";
17
+ import MonitorType from "../../../Types/Monitor/MonitorType";
18
+ import ObjectID from "../../../Types/ObjectID";
19
+ import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
20
+ import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
21
+ import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
22
+ import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
23
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
24
+ import ProjectUtil from "../../../UI/Utils/Project";
25
+
26
+ /*
27
+ * The wiring test for the monitor-type/criteria alignment.
28
+ *
29
+ * CriteriaFilterMonitorTypeChange.test.ts pins the two pure helpers that
30
+ * decide WHAT the criteria should become. Nothing pinned that the criteria
31
+ * form actually calls them - and that wiring is the entire fix, because the
32
+ * reported defect is not a wrong answer from a helper, it is a form that
33
+ * never asked. Delete the effect in MonitorSteps.tsx, or drop
34
+ * props.monitorType from its dependency array, or drop the seed-id capture
35
+ * that the effect early-returns without, and every one of those helper
36
+ * tests still passes while the bug is fully back.
37
+ *
38
+ * So this test does what the user does: it mounts the criteria form the way
39
+ * the create form remounts it - holding criteria seeded for one monitor
40
+ * type, told it is now showing another - and asserts on what the form hands
41
+ * back to the surrounding form through onChange.
42
+ *
43
+ * MonitorStep is mocked out. What is under test is this component's own
44
+ * seeding and alignment, not the several hundred fields its child renders.
45
+ */
46
+
47
+ jest.mock(
48
+ "../../../../App/FeatureSet/Dashboard/src/Components/Form/Monitor/MonitorStep",
49
+ () => {
50
+ return {
51
+ __esModule: true,
52
+ default: () => {
53
+ return null;
54
+ },
55
+ };
56
+ },
57
+ );
58
+
59
+ jest.mock("../../../../App/FeatureSet/Dashboard/src/Utils/Probe", () => {
60
+ return {
61
+ __esModule: true,
62
+ default: {
63
+ getAllProbes: () => {
64
+ return Promise.resolve([]);
65
+ },
66
+ },
67
+ };
68
+ });
69
+
70
+ jest.mock("../../../../App/FeatureSet/Dashboard/src/Utils/ProjectUser", () => {
71
+ return {
72
+ __esModule: true,
73
+ default: {
74
+ fetchProjectUsersAsDropdownOptions: () => {
75
+ return Promise.resolve([]);
76
+ },
77
+ },
78
+ };
79
+ });
80
+
81
+ import MonitorStepsElement from "../../../../App/FeatureSet/Dashboard/src/Components/Form/Monitor/MonitorSteps";
82
+
83
+ const PROJECT_ID: ObjectID = new ObjectID(
84
+ "11111111-1111-4111-8111-111111111111",
85
+ );
86
+
87
+ const ONLINE_STATUS_ID: string = "22222222-2222-4222-8222-222222222222";
88
+ const OFFLINE_STATUS_ID: string = "33333333-3333-4333-8333-333333333333";
89
+ const INCIDENT_SEVERITY_ID: string = "44444444-4444-4444-8444-444444444444";
90
+ const ALERT_SEVERITY_ID: string = "55555555-5555-4555-8555-555555555555";
91
+
92
+ const MONITOR_NAME: string = "Acme";
93
+
94
+ function listOf<T>(data: Array<T>): unknown {
95
+ return {
96
+ data: data,
97
+ count: data.length,
98
+ skip: 0,
99
+ limit: 50,
100
+ };
101
+ }
102
+
103
+ function monitorStatus(data: {
104
+ id: string;
105
+ name: string;
106
+ isOperationalState: boolean;
107
+ isOfflineState: boolean;
108
+ }): MonitorStatus {
109
+ const status: MonitorStatus = new MonitorStatus();
110
+ status._id = data.id;
111
+ status.name = data.name;
112
+ status.isOperationalState = data.isOperationalState;
113
+ status.isOfflineState = data.isOfflineState;
114
+ return status;
115
+ }
116
+
117
+ function incidentSeverity(): IncidentSeverity {
118
+ const severity: IncidentSeverity = new IncidentSeverity();
119
+ severity._id = INCIDENT_SEVERITY_ID;
120
+ severity.name = "Critical";
121
+ return severity;
122
+ }
123
+
124
+ function alertSeverity(): AlertSeverity {
125
+ const severity: AlertSeverity = new AlertSeverity();
126
+ severity._id = ALERT_SEVERITY_ID;
127
+ severity.name = "Critical";
128
+ return severity;
129
+ }
130
+
131
+ /*
132
+ * The criteria form fetches statuses, severities, on-call policies, labels,
133
+ * teams and incident roles on mount. Only the first three shape what it
134
+ * seeds; the rest just have to answer.
135
+ */
136
+ function mockModelApi(): void {
137
+ jest
138
+ .spyOn(ModelAPI, "getList")
139
+ .mockImplementation((request: { modelType: unknown }): never => {
140
+ const modelType: unknown = request.modelType;
141
+
142
+ if (modelType === MonitorStatus) {
143
+ return listOf([
144
+ monitorStatus({
145
+ id: ONLINE_STATUS_ID,
146
+ name: "Operational",
147
+ isOperationalState: true,
148
+ isOfflineState: false,
149
+ }),
150
+ monitorStatus({
151
+ id: OFFLINE_STATUS_ID,
152
+ name: "Offline",
153
+ isOperationalState: false,
154
+ isOfflineState: true,
155
+ }),
156
+ ]) as never;
157
+ }
158
+
159
+ if (modelType === IncidentSeverity) {
160
+ return listOf([incidentSeverity()]) as never;
161
+ }
162
+
163
+ if (modelType === AlertSeverity) {
164
+ return listOf([alertSeverity()]) as never;
165
+ }
166
+
167
+ return listOf([]) as never;
168
+ });
169
+ }
170
+
171
+ function seededStepsFor(monitorType: MonitorType): MonitorSteps {
172
+ return MonitorSteps.getDefaultMonitorSteps({
173
+ monitorType: monitorType,
174
+ monitorName: MONITOR_NAME,
175
+ defaultMonitorStatusId: new ObjectID(ONLINE_STATUS_ID),
176
+ onlineMonitorStatusId: new ObjectID(ONLINE_STATUS_ID),
177
+ offlineMonitorStatusId: new ObjectID(OFFLINE_STATUS_ID),
178
+ defaultIncidentSeverityId: new ObjectID(INCIDENT_SEVERITY_ID),
179
+ defaultAlertSeverityId: new ObjectID(ALERT_SEVERITY_ID),
180
+ });
181
+ }
182
+
183
+ function checksIn(monitorSteps: MonitorSteps): Array<CheckOn> {
184
+ return (monitorSteps.data?.monitorStepsInstanceArray || []).flatMap(
185
+ (monitorStep: MonitorStep) => {
186
+ return (
187
+ monitorStep.data?.monitorCriteria.data?.monitorCriteriaInstanceArray ||
188
+ []
189
+ ).flatMap((instance: MonitorCriteriaInstance) => {
190
+ return (instance.data?.filters || []).map((filter: CriteriaFilter) => {
191
+ return filter.checkOn;
192
+ });
193
+ });
194
+ },
195
+ );
196
+ }
197
+
198
+ // The checks in `monitorSteps` that this monitor type's form cannot draw.
199
+ function unrenderableChecksIn(
200
+ monitorSteps: MonitorSteps,
201
+ monitorType: MonitorType,
202
+ ): Array<string> {
203
+ const offered: Array<string> =
204
+ CriteriaFilterUtil.getCheckOnOptionsByMonitorType(monitorType).map(
205
+ (option: DropdownOption) => {
206
+ return option.value.toString();
207
+ },
208
+ );
209
+
210
+ return checksIn(monitorSteps)
211
+ .map((checkOn: CheckOn) => {
212
+ return checkOn.toString();
213
+ })
214
+ .filter((checkOn: string) => {
215
+ return !offered.includes(checkOn);
216
+ });
217
+ }
218
+
219
+ interface MountedForm {
220
+ // Everything the form has pushed up through onChange, in order.
221
+ changes: Array<MonitorSteps>;
222
+ latest: () => MonitorSteps;
223
+ }
224
+
225
+ /*
226
+ * Mount the criteria form the way the create form remounts it after the
227
+ * user has been away on another step: holding the criteria it seeded
228
+ * earlier, told the monitor type is now something else.
229
+ *
230
+ * Waits for the form to finish loading rather than for its first onChange.
231
+ * The first onChange is the component handing straight back what it was
232
+ * given, which happens on mount, before the statuses and severities it
233
+ * needs have arrived and therefore before it can align anything - so a
234
+ * test that stops there reads the stale value and would pass whether or
235
+ * not the alignment exists.
236
+ */
237
+ async function mountWith(data: {
238
+ initialValue?: MonitorSteps | undefined;
239
+ monitorType: MonitorType;
240
+ }): Promise<MountedForm> {
241
+ const changes: Array<MonitorSteps> = [];
242
+
243
+ render(
244
+ <MonitorStepsElement
245
+ monitorType={data.monitorType}
246
+ monitorName={MONITOR_NAME}
247
+ {...(data.initialValue ? { initialValue: data.initialValue } : {})}
248
+ onChange={(value: MonitorSteps) => {
249
+ changes.push(value);
250
+ }}
251
+ />,
252
+ );
253
+
254
+ /*
255
+ * Rendered only once isLoading is false - until then the component is a
256
+ * bare loader.
257
+ */
258
+ await waitFor(() => {
259
+ expect(screen.getByText("Default Monitor Status")).toBeInTheDocument();
260
+ });
261
+
262
+ return {
263
+ changes: changes,
264
+ latest: () => {
265
+ return changes[changes.length - 1]!;
266
+ },
267
+ };
268
+ }
269
+
270
+ describe("the criteria form keeps its criteria in step with the monitor type", () => {
271
+ beforeEach(() => {
272
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
273
+ mockModelApi();
274
+ });
275
+
276
+ afterEach(() => {
277
+ cleanup();
278
+ jest.restoreAllMocks();
279
+ });
280
+
281
+ test("criteria seeded for a Website do not survive a switch to External Status Page", async () => {
282
+ /*
283
+ * The reported defect, end to end. The user seeded criteria on a
284
+ * Website monitor, went back a step, picked External Status Page, and
285
+ * came forward again - which unmounts and remounts this component with
286
+ * the criteria it seeded the first time round.
287
+ */
288
+ const form: MountedForm = await mountWith({
289
+ initialValue: seededStepsFor(MonitorType.Website),
290
+ monitorType: MonitorType.ExternalStatusPage,
291
+ });
292
+
293
+ /*
294
+ * Before the fix the criteria still checked "Is Online" and "Response
295
+ * Status Code", which an External Status Page monitor does not offer,
296
+ * so the Filter Type dropdown rendered react-select's empty
297
+ * "Select...". Without the fix this waits out its timeout and reports
298
+ * exactly which checks were left behind.
299
+ */
300
+ await waitFor(() => {
301
+ expect(
302
+ unrenderableChecksIn(form.latest(), MonitorType.ExternalStatusPage),
303
+ ).toEqual([]);
304
+ });
305
+
306
+ /*
307
+ * And the corrected criteria were pushed up to the surrounding form,
308
+ * rather than merely held in this component's own state - that second
309
+ * onChange is what gets saved.
310
+ */
311
+ expect(form.changes.length).toBeGreaterThan(1);
312
+ expect(checksIn(form.latest()).length).toBeGreaterThan(0);
313
+ });
314
+
315
+ test("criteria that already suit the monitor type are handed back unchanged", async () => {
316
+ /*
317
+ * The ordinary path - the user never changed their mind. The form must
318
+ * not rewrite criteria that are already right, or every mount would
319
+ * churn their generated ids and dirty an untouched form.
320
+ */
321
+ const seeded: MonitorSteps = seededStepsFor(MonitorType.Website);
322
+
323
+ const form: MountedForm = await mountWith({
324
+ initialValue: seeded,
325
+ monitorType: MonitorType.Website,
326
+ });
327
+
328
+ expect(form.latest().toJSON()).toEqual(seeded.toJSON());
329
+
330
+ /*
331
+ * Exactly one onChange: the component handing back what it was given.
332
+ * A second one would mean it had rewritten criteria that were already
333
+ * correct.
334
+ */
335
+ expect(form.changes).toHaveLength(1);
336
+ });
337
+
338
+ test("with no criteria to start from, the form still seeds for the monitor type it was given", async () => {
339
+ // The other way into this component: the criteria step, first visit.
340
+ const form: MountedForm = await mountWith({
341
+ initialValue: undefined,
342
+ monitorType: MonitorType.ExternalStatusPage,
343
+ });
344
+
345
+ expect(checksIn(form.latest())).toContain(
346
+ CheckOn.ExternalStatusPageIsOnline,
347
+ );
348
+ expect(
349
+ unrenderableChecksIn(form.latest(), MonitorType.ExternalStatusPage),
350
+ ).toEqual([]);
351
+ });
352
+ });
@@ -0,0 +1,278 @@
1
+ import "@testing-library/jest-dom";
2
+ import { afterEach, describe, expect, test } from "@jest/globals";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import React from "react";
5
+ import LayerCard from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallScheduleLayer/LayerCard";
6
+ import LayerRotationSummary from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallScheduleLayer/LayerRotationSummary";
7
+ import { getLayerPreviewEvents } from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallScheduleLayer/LayerShiftPreview";
8
+ import { OverrideUserInfo } from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallScheduleLayer/ScheduleOverrides";
9
+ import OnCallDutyPolicyScheduleLayer from "../../../Models/DatabaseModels/OnCallDutyPolicyScheduleLayer";
10
+ import OnCallDutyPolicyScheduleLayerUser from "../../../Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser";
11
+ import User from "../../../Models/DatabaseModels/User";
12
+ import Dictionary from "../../../Types/Dictionary";
13
+ import OneUptimeDate from "../../../Types/Date";
14
+ import EventInterval from "../../../Types/Events/EventInterval";
15
+ import Recurring from "../../../Types/Events/Recurring";
16
+ import ObjectID from "../../../Types/ObjectID";
17
+ import RestrictionTimes, {
18
+ RestrictionType,
19
+ } from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
20
+ import { UserOverrideRecord } from "../../../Types/OnCallDutyPolicy/UserOverrideUtil";
21
+
22
+ /*
23
+ * https://github.com/OneUptime/oneuptime/issues/3411
24
+ *
25
+ * The layer card's live "<name> on call now" line and the shift table it
26
+ * expands into are the two places on the Layers tab that make a claim about who
27
+ * is covering right now. Both used to be computed from the raw rotation, so
28
+ * during an override they named the originally-scheduled user while the
29
+ * "Final schedule" calendar at the bottom of the SAME page, and the code that
30
+ * actually rings a phone, named the substitute.
31
+ *
32
+ * LayerShiftPreview's own suite pins the event-level substitution. What these
33
+ * tests add is the half that only the rendered component can prove: a
34
+ * substitute is by construction NOT assigned to the layer, so nothing in the
35
+ * layer's own user list can name them - and a fix that substitutes the id but
36
+ * cannot resolve the name turns "Alice on call now" into "Unknown user on call
37
+ * now", which is not an improvement.
38
+ */
39
+
40
+ const USER_A_ID: string = "aaaaaaaa-1111-4111-8111-111111111111";
41
+ const USER_B_ID: string = "bbbbbbbb-2222-4222-8222-222222222222";
42
+ // A second rotation member. The shift table only renders for a real rotation.
43
+ const USER_C_ID: string = "cccccccc-3333-4333-8333-333333333333";
44
+
45
+ const USER_A_NAME: string = "Alice Scheduled";
46
+ const USER_B_NAME: string = "Bob Covering";
47
+ const USER_C_NAME: string = "Carol Rotation";
48
+
49
+ function makeUser(id: string, name: string): User {
50
+ const user: User = new User();
51
+ user.id = new ObjectID(id);
52
+ user.name = name as any;
53
+ user.email = `${id}@example.com` as any;
54
+ return user;
55
+ }
56
+
57
+ function makeLayerUser(
58
+ id: string,
59
+ name: string,
60
+ order: number = 1,
61
+ ): OnCallDutyPolicyScheduleLayerUser {
62
+ const layerUser: OnCallDutyPolicyScheduleLayerUser =
63
+ new OnCallDutyPolicyScheduleLayerUser();
64
+ layerUser.id = new ObjectID(`6666666${order}-6666-4666-8666-666666666666`);
65
+ layerUser.userId = new ObjectID(id);
66
+ layerUser.user = makeUser(id, name);
67
+ layerUser.order = order;
68
+ return layerUser;
69
+ }
70
+
71
+ /*
72
+ * The rotation summary deliberately renders only prose for a single-user layer
73
+ * ("X is always on call - there is no rotation"): with one person every period
74
+ * is theirs and a shift table says nothing. The turn table, and therefore the
75
+ * "On call now" badge this bug mislabelled, only exists for a real rotation, so
76
+ * these fixtures put two people on the layer.
77
+ */
78
+ function rotationUsers(): Array<OnCallDutyPolicyScheduleLayerUser> {
79
+ return [
80
+ makeLayerUser(USER_A_ID, USER_A_NAME, 1),
81
+ makeLayerUser(USER_C_ID, USER_C_NAME, 2),
82
+ ];
83
+ }
84
+
85
+ function makeLayer(): OnCallDutyPolicyScheduleLayer {
86
+ const layer: OnCallDutyPolicyScheduleLayer =
87
+ new OnCallDutyPolicyScheduleLayer();
88
+ const now: Date = OneUptimeDate.getCurrentDate();
89
+
90
+ layer.id = new ObjectID("33333333-3333-4333-8333-333333333333");
91
+ layer.name = "Primary" as any;
92
+ layer.startsAt = OneUptimeDate.addRemoveDays(now, -30);
93
+ layer.handOffTime = OneUptimeDate.addRemoveDays(now, -30);
94
+ layer.rotation = Recurring.fromJSON({
95
+ _type: "Recurring",
96
+ value: {
97
+ intervalType: EventInterval.Day,
98
+ intervalCount: { _type: "PositiveNumber", value: 1 },
99
+ },
100
+ } as any) as any;
101
+
102
+ const restrictionTimes: RestrictionTimes = new RestrictionTimes();
103
+ restrictionTimes.restictionType = RestrictionType.None;
104
+ restrictionTimes.dayRestrictionTimes = null;
105
+ layer.restrictionTimes = restrictionTimes as any;
106
+
107
+ return layer;
108
+ }
109
+
110
+ function activeGlobalOverride(): UserOverrideRecord {
111
+ const now: Date = OneUptimeDate.getCurrentDate();
112
+ return {
113
+ overrideUserId: USER_A_ID,
114
+ routeAlertsToUserId: USER_B_ID,
115
+ startsAt: OneUptimeDate.addRemoveHours(now, -1),
116
+ endsAt: OneUptimeDate.addRemoveHours(now, 1),
117
+ onCallDutyPolicyId: null,
118
+ };
119
+ }
120
+
121
+ const SUBSTITUTE_INFO: Dictionary<OverrideUserInfo> = {
122
+ [USER_B_ID]: { name: USER_B_NAME, email: `${USER_B_ID}@example.com` },
123
+ };
124
+
125
+ const noop: () => void = (): void => {
126
+ return undefined;
127
+ };
128
+
129
+ function renderCard(options: {
130
+ overrides: Array<UserOverrideRecord>;
131
+ overrideUserInfo: Dictionary<OverrideUserInfo>;
132
+ }): void {
133
+ render(
134
+ <LayerCard
135
+ layer={makeLayer()}
136
+ users={[makeLayerUser(USER_A_ID, USER_A_NAME)]}
137
+ timezone="UTC"
138
+ index={0}
139
+ total={1}
140
+ isExpanded={false}
141
+ actionsDisabled={false}
142
+ isDeleteButtonLoading={false}
143
+ overrides={options.overrides}
144
+ overridePolicyContextId=""
145
+ overrideUserInfo={options.overrideUserInfo}
146
+ onToggleExpand={noop}
147
+ onMoveUp={noop}
148
+ onMoveDown={noop}
149
+ onDeleteLayer={noop}
150
+ onLayerChange={noop}
151
+ onUsersChange={noop}
152
+ />,
153
+ );
154
+ }
155
+
156
+ // The live line reads "<name> on call now", so anchor on that phrase's row.
157
+ function getOnCallNowLineText(): string {
158
+ const label: HTMLElement = screen.getByText(/on call now/i);
159
+ return label.closest("div")?.textContent || label.textContent || "";
160
+ }
161
+
162
+ describe("Layer card names the substitute during an override (issue #3411)", () => {
163
+ afterEach(() => {
164
+ cleanup();
165
+ });
166
+
167
+ test("without an override the rotation user is named - the baseline", () => {
168
+ renderCard({ overrides: [], overrideUserInfo: {} });
169
+
170
+ const line: string = getOnCallNowLineText();
171
+ expect(line).toContain(USER_A_NAME);
172
+ expect(line).not.toContain(USER_B_NAME);
173
+ expect(line).not.toContain("covering via override");
174
+ });
175
+
176
+ test("during an override the SUBSTITUTE is named, and marked as covering", () => {
177
+ renderCard({
178
+ overrides: [activeGlobalOverride()],
179
+ overrideUserInfo: SUBSTITUTE_INFO,
180
+ });
181
+
182
+ const line: string = getOnCallNowLineText();
183
+ expect(line).toContain(USER_B_NAME);
184
+ expect(line).toContain("covering via override");
185
+ expect(line).not.toContain(`${USER_A_NAME} on call now`);
186
+ });
187
+
188
+ /*
189
+ * The substitute's name has to come from the override payload: they are not
190
+ * on this layer, so the layer's user list cannot supply it. Withholding the
191
+ * info must degrade to "Unknown user" rather than silently reverting to the
192
+ * overridden user's name, which would look correct while being wrong.
193
+ */
194
+ test("a substitute with no display info renders as Unknown user, never as the overridden user", () => {
195
+ renderCard({
196
+ overrides: [activeGlobalOverride()],
197
+ overrideUserInfo: {},
198
+ });
199
+
200
+ const line: string = getOnCallNowLineText();
201
+ expect(line).toContain("Unknown user");
202
+ /*
203
+ * Alice legitimately reappears in the same row as "Up next" - she resumes
204
+ * when the override ends - so the assertion is about the CLAIM, not about
205
+ * her name being absent from the row.
206
+ */
207
+ expect(line).not.toContain(`${USER_A_NAME} on call now`);
208
+ });
209
+ });
210
+
211
+ describe("Rotation summary marks a covered turn (issue #3411)", () => {
212
+ afterEach(() => {
213
+ cleanup();
214
+ });
215
+
216
+ function renderSummary(options: {
217
+ overrides: Array<UserOverrideRecord>;
218
+ overrideUserInfo: Dictionary<OverrideUserInfo>;
219
+ }): void {
220
+ const preview: {
221
+ events: Array<any>;
222
+ now: Date;
223
+ } = getLayerPreviewEvents({
224
+ layer: makeLayer(),
225
+ users: rotationUsers(),
226
+ timezone: "UTC",
227
+ numberOfShifts: 6,
228
+ overrides: options.overrides,
229
+ });
230
+
231
+ render(
232
+ <LayerRotationSummary
233
+ layer={makeLayer()}
234
+ users={rotationUsers()}
235
+ timezone="UTC"
236
+ events={preview.events}
237
+ now={preview.now}
238
+ hasLowerPriorityLayer={false}
239
+ overrideUserInfo={options.overrideUserInfo}
240
+ />,
241
+ );
242
+ }
243
+
244
+ test("the covered turn is attributed to the substitute and tagged Covering", () => {
245
+ renderSummary({
246
+ overrides: [activeGlobalOverride()],
247
+ overrideUserInfo: SUBSTITUTE_INFO,
248
+ });
249
+
250
+ expect(screen.getAllByText(USER_B_NAME).length).toBeGreaterThan(0);
251
+ expect(screen.getAllByText("Covering").length).toBeGreaterThan(0);
252
+ });
253
+
254
+ test("without an override no turn is tagged Covering", () => {
255
+ renderSummary({ overrides: [], overrideUserInfo: {} });
256
+
257
+ expect(screen.queryByText("Covering")).toBeNull();
258
+ });
259
+
260
+ /*
261
+ * The "Order" row states the layer's configured rotation, which an override
262
+ * does not change. Substituting there would misrepresent the schedule as
263
+ * having been reconfigured.
264
+ */
265
+ test("the rotation order still lists the assigned user, not the substitute", () => {
266
+ renderSummary({
267
+ overrides: [activeGlobalOverride()],
268
+ overrideUserInfo: SUBSTITUTE_INFO,
269
+ });
270
+
271
+ const orderLabel: HTMLElement = screen.getByText("Order");
272
+ const orderRow: string = orderLabel.parentElement?.textContent || "";
273
+
274
+ expect(orderRow).toContain(USER_A_NAME);
275
+ expect(orderRow).toContain(USER_C_NAME);
276
+ expect(orderRow).not.toContain(USER_B_NAME);
277
+ });
278
+ });