@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,181 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/NetworkDeviceAutoImportRule";
3
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
4
+ import CreateBy from "../Types/Database/CreateBy";
5
+ import UpdateBy from "../Types/Database/UpdateBy";
6
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
7
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
8
+ import BadDataException from "../../Types/Exception/BadDataException";
9
+ import RulePatternMatchUtil from "../../Utils/Rules/RulePatternMatchUtil";
10
+ import ScanTargetUtil from "../../Utils/NetworkDiscovery/ScanTargetUtil";
11
+
12
+ /*
13
+ * Write-time validation for auto-import rules, following the
14
+ * NetworkSiteAssignmentRuleService contract: a condition that can only ever
15
+ * match nothing is rejected where the user can see it, not logged about by
16
+ * the engine long after they left the form.
17
+ */
18
+
19
+ /*
20
+ * What a sysObjectID condition may contain: an optional leading dot, then
21
+ * digits, dots and '*' wildcards. Hoisted so the literal is not the object
22
+ * of a member expression, which `wrap-regex` and Prettier cannot agree on —
23
+ * same reason as CidrMatchUtil.
24
+ */
25
+ const OID_PATTERN_SHAPE: RegExp = /^\.?[\d.*]+$/;
26
+ export class Service extends DatabaseService<Model> {
27
+ public constructor() {
28
+ super(Model);
29
+ }
30
+
31
+ @CaptureSpan()
32
+ protected override async onBeforeCreate(
33
+ createBy: CreateBy<Model>,
34
+ ): Promise<OnCreate<Model>> {
35
+ this.validateCriteria({
36
+ ipMatchTarget: createBy.data.ipMatchTarget,
37
+ sysNamePattern: createBy.data.sysNamePattern,
38
+ sysDescrPattern: createBy.data.sysDescrPattern,
39
+ sysObjectIdPattern: createBy.data.sysObjectIdPattern,
40
+ });
41
+
42
+ return { createBy, carryForward: null };
43
+ }
44
+
45
+ @CaptureSpan()
46
+ protected override async onBeforeUpdate(
47
+ updateBy: UpdateBy<Model>,
48
+ ): Promise<OnUpdate<Model>> {
49
+ const dataKeys: Array<string> = Object.keys(updateBy.data || {});
50
+
51
+ const isCriteriaChange: boolean =
52
+ dataKeys.includes("ipMatchTarget") ||
53
+ dataKeys.includes("sysNamePattern") ||
54
+ dataKeys.includes("sysDescrPattern") ||
55
+ dataKeys.includes("sysObjectIdPattern");
56
+
57
+ if (!isCriteriaChange) {
58
+ return { updateBy, carryForward: null };
59
+ }
60
+
61
+ /*
62
+ * The update may clear one criterion while another only exists on the
63
+ * stored row, so validate the RESULTING state of every matched row.
64
+ */
65
+ const existingRules: Array<Model> = await this.findBy({
66
+ query: updateBy.query,
67
+ select: {
68
+ _id: true,
69
+ ipMatchTarget: true,
70
+ sysNamePattern: true,
71
+ sysDescrPattern: true,
72
+ sysObjectIdPattern: true,
73
+ },
74
+ limit: LIMIT_MAX,
75
+ skip: 0,
76
+ props: {
77
+ isRoot: true,
78
+ },
79
+ });
80
+
81
+ const data: Record<string, unknown> = updateBy.data as unknown as Record<
82
+ string,
83
+ unknown
84
+ >;
85
+
86
+ for (const existingRule of existingRules) {
87
+ this.validateCriteria({
88
+ ipMatchTarget: dataKeys.includes("ipMatchTarget")
89
+ ? (data["ipMatchTarget"] as string | null)
90
+ : existingRule.ipMatchTarget,
91
+ sysNamePattern: dataKeys.includes("sysNamePattern")
92
+ ? (data["sysNamePattern"] as string | null)
93
+ : existingRule.sysNamePattern,
94
+ sysDescrPattern: dataKeys.includes("sysDescrPattern")
95
+ ? (data["sysDescrPattern"] as string | null)
96
+ : existingRule.sysDescrPattern,
97
+ sysObjectIdPattern: dataKeys.includes("sysObjectIdPattern")
98
+ ? (data["sysObjectIdPattern"] as string | null)
99
+ : existingRule.sysObjectIdPattern,
100
+ });
101
+ }
102
+
103
+ return { updateBy, carryForward: null };
104
+ }
105
+
106
+ private validateCriteria(data: {
107
+ ipMatchTarget?: string | null | undefined;
108
+ sysNamePattern?: string | null | undefined;
109
+ sysDescrPattern?: string | null | undefined;
110
+ sysObjectIdPattern?: string | null | undefined;
111
+ }): void {
112
+ const ipMatchTarget: string = (data.ipMatchTarget || "").trim();
113
+ const sysNamePattern: string = (data.sysNamePattern || "").trim();
114
+ const sysDescrPattern: string = (data.sysDescrPattern || "").trim();
115
+ const sysObjectIdPattern: string = (data.sysObjectIdPattern || "").trim();
116
+
117
+ /*
118
+ * A rule with no conditions matches nothing (see AutoImportRuleMatcher) —
119
+ * and would read as "match everything" to whoever finds it later.
120
+ */
121
+ if (
122
+ !ipMatchTarget &&
123
+ !sysNamePattern &&
124
+ !sysDescrPattern &&
125
+ !sysObjectIdPattern
126
+ ) {
127
+ throw new BadDataException(
128
+ "At least one of Host IP Is In, System Name Pattern, System Description Pattern, or System Object ID Pattern is required.",
129
+ );
130
+ }
131
+
132
+ /*
133
+ * Well-formedness only, deliberately NOT the scan-size ceiling: matching
134
+ * an address against 10.0.0.0/8 is a containment check, not a sweep, so
135
+ * a condition covering a huge block is legitimate here even though a
136
+ * scan target that size is not.
137
+ */
138
+ if (ipMatchTarget && !ScanTargetUtil.isValid(ipMatchTarget)) {
139
+ throw new BadDataException(
140
+ `"${ipMatchTarget}" is not a valid Host IP condition. ${ScanTargetUtil.getSyntaxHint()}`,
141
+ );
142
+ }
143
+
144
+ this.validatePattern("System Name Pattern", sysNamePattern);
145
+ this.validatePattern("System Description Pattern", sysDescrPattern);
146
+ this.validateOidPattern(sysObjectIdPattern);
147
+ }
148
+
149
+ private validatePattern(title: string, pattern: string): void {
150
+ if (!pattern || RulePatternMatchUtil.isSupportedPattern(pattern)) {
151
+ return;
152
+ }
153
+
154
+ throw new BadDataException(
155
+ `${title} is neither a valid regular expression nor a '*' wildcard pattern, so it would never match: ${pattern}`,
156
+ );
157
+ }
158
+
159
+ /*
160
+ * The sysObjectID condition is NOT free-text: an OID is a dotted numeric
161
+ * arc, matched as a literal-dot '*' glob or an arc prefix (see
162
+ * AutoImportRuleMatcher.matchesOidPattern — regex-first matching would make
163
+ * "1.3.6.1.4.1.9.*" match enterprise 94 too). So the only characters that
164
+ * can ever match anything are digits, dots and '*'; anything else is a
165
+ * pattern that silently never fires, which is exactly what this service
166
+ * exists to reject at the write.
167
+ */
168
+ private validateOidPattern(pattern: string): void {
169
+ if (!pattern) {
170
+ return;
171
+ }
172
+
173
+ if (!OID_PATTERN_SHAPE.test(pattern)) {
174
+ throw new BadDataException(
175
+ `System Object ID Pattern must be an OID prefix (1.3.6.1.4.1.9) or a '*' wildcard OID pattern (1.3.6.1.4.1.9.*) — digits, dots and '*' only: ${pattern}`,
176
+ );
177
+ }
178
+ }
179
+ }
180
+
181
+ export default new Service();
@@ -6,6 +6,7 @@ import UpdateBy from "../Types/Database/UpdateBy";
6
6
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
7
7
  import BadDataException from "../../Types/Exception/BadDataException";
8
8
  import ScanTargetUtil from "../../Utils/NetworkDiscovery/ScanTargetUtil";
9
+ import ScanNameUtil from "../../Utils/NetworkDiscovery/ScanNameUtil";
9
10
 
10
11
  export class Service extends DatabaseService<Model> {
11
12
  public constructor() {
@@ -33,6 +34,25 @@ export class Service extends DatabaseService<Model> {
33
34
  ): Promise<OnCreate<Model>> {
34
35
  this.validateScanTarget(createBy.data.cidr);
35
36
 
37
+ /*
38
+ * The name is normalized as well as validated — collapsed onto one line,
39
+ * trimmed, and a blank one dropped entirely — so the column holds either a
40
+ * name or nothing. An empty string stored here would read as a name to
41
+ * every `scan.name ?` in the product and render as a blank line above the
42
+ * scan target. See ScanNameUtil.
43
+ */
44
+ this.validateScanName(createBy.data.name);
45
+
46
+ /*
47
+ * Written through a cast because `exactOptionalPropertyTypes` forbids
48
+ * assigning `undefined` to an optional property — and dropping a blank
49
+ * name is exactly that assignment. Undefined rather than null: a model
50
+ * instance starts with every column undefined, so this leaves the row in
51
+ * the state a scan created without a name would already have been in.
52
+ */
53
+ (createBy.data as unknown as Record<string, unknown>)["name"] =
54
+ ScanNameUtil.normalize(createBy.data.name) ?? undefined;
55
+
36
56
  return { createBy, carryForward: null };
37
57
  }
38
58
 
@@ -54,6 +74,27 @@ export class Service extends DatabaseService<Model> {
54
74
  );
55
75
  }
56
76
 
77
+ /*
78
+ * The name IS user-updatable — renaming a scan is the whole point of
79
+ * letting it be named — so this path is the one the form actually takes,
80
+ * not just a guard against root writers.
81
+ *
82
+ * Cleared with null rather than left as "": the form sends back an empty
83
+ * box as an empty string, and the operator who emptied it meant "this scan
84
+ * has no name", which is what NULL says. `undefined` would not do — the
85
+ * key is present in the update, and an undefined value is a column TypeORM
86
+ * would try to write.
87
+ */
88
+ if (dataKeys.includes("name")) {
89
+ const data: Record<string, unknown> = updateBy.data as Record<
90
+ string,
91
+ unknown
92
+ >;
93
+
94
+ this.validateScanName(data["name"]);
95
+ data["name"] = ScanNameUtil.normalize(data["name"]);
96
+ }
97
+
57
98
  return { updateBy, carryForward: null };
58
99
  }
59
100
 
@@ -75,6 +116,24 @@ export class Service extends DatabaseService<Model> {
75
116
  throw new BadDataException(validationError);
76
117
  }
77
118
  }
119
+
120
+ /*
121
+ * Same contract as validateScanTarget above: the value is passed through
122
+ * untouched, because it arrives straight from the request JSON and may be a
123
+ * number, an object or an array rather than a string. ScanNameUtil type-
124
+ * guards internally and reports a non-string as such.
125
+ *
126
+ * An ABSENT or blank name is not an error — the column is optional, and a
127
+ * scan with no name reads as it always did, by its target.
128
+ */
129
+ private validateScanName(name: unknown): void {
130
+ const validationError: string | null =
131
+ ScanNameUtil.getValidationError(name);
132
+
133
+ if (validationError) {
134
+ throw new BadDataException(validationError);
135
+ }
136
+ }
78
137
  }
79
138
 
80
139
  export default new Service();
@@ -303,6 +303,24 @@ export class Service extends DatabaseService<Model> {
303
303
  await this.applySiteAssignmentRulesToDevice(createdItem.id!);
304
304
  }
305
305
  })
306
+ .then(async () => {
307
+ /*
308
+ * A device created monitor-backed adopts its monitor's CURRENT
309
+ * status right away rather than waiting for that monitor's next
310
+ * status CHANGE. Skipped for SNMP devices, which are judged by
311
+ * their own walk.
312
+ */
313
+ if (
314
+ NetworkDeviceMonitoringMethodUtil.isMonitorBacked(
315
+ createdItem.monitoringMethod,
316
+ )
317
+ ) {
318
+ await this.refreshStampedMonitorStatus({
319
+ deviceId: createdItem.id!,
320
+ clearWhenNotMonitorBacked: false,
321
+ });
322
+ }
323
+ })
306
324
  .catch((error: Error) => {
307
325
  logger.error(
308
326
  `Error applying network device rules in NetworkDeviceService.onCreateSuccess: ${error}`,
@@ -453,6 +471,109 @@ export class Service extends DatabaseService<Model> {
453
471
  };
454
472
  }
455
473
 
474
+ /*
475
+ * Re-derives one device's stamped `currentMonitorStatusId` from its
476
+ * binding, and refreshes its site chain if the stamp moved.
477
+ *
478
+ * The stamp is what every monitor-backed surface reads — the device list
479
+ * pill, the site rollup, the topology node — and until this existed the
480
+ * ONLY thing that ever wrote it was
481
+ * `NetworkSiteService.onMonitorStatusChanged`, which fires on a monitor's
482
+ * next status CHANGE. Bind a device to a Ping monitor that is already Up
483
+ * and staying Up and nothing writes the stamp at all, so the device sits
484
+ * on "Pending" until the monitor happens to go down — which is
485
+ * OneUptime/oneuptime#3392. Binding is itself an event that decides the
486
+ * device's status, so it stamps here.
487
+ *
488
+ * `clearWhenNotMonitorBacked` is for the write that moves a device OFF
489
+ * monitor-backed: the ping monitor's verdict must not outlive the binding
490
+ * (DeviceHealthStateUtil lets a stamped status beat reachability, so a
491
+ * stale one would poison the site rollup of a device that is now walked).
492
+ * It is deliberately NOT set when a write only touches the monitor
493
+ * binding of an SNMP device, because an SNMP device's stamp comes from
494
+ * the Network Device monitor that watches it, and that binding lives in
495
+ * the monitor's step data rather than in this column.
496
+ */
497
+ @CaptureSpan()
498
+ public async refreshStampedMonitorStatus(data: {
499
+ deviceId: ObjectID;
500
+ clearWhenNotMonitorBacked: boolean;
501
+ }): Promise<void> {
502
+ const device: Model | null = await this.findOneById({
503
+ id: data.deviceId,
504
+ select: {
505
+ _id: true,
506
+ projectId: true,
507
+ siteId: true,
508
+ monitoringMethod: true,
509
+ monitorId: true,
510
+ currentMonitorStatusId: true,
511
+ },
512
+ props: {
513
+ isRoot: true,
514
+ },
515
+ });
516
+
517
+ if (!device || !device.projectId) {
518
+ return;
519
+ }
520
+
521
+ const isMonitorBacked: boolean =
522
+ NetworkDeviceMonitoringMethodUtil.isMonitorBacked(
523
+ device.monitoringMethod,
524
+ );
525
+
526
+ if (!isMonitorBacked && !data.clearWhenNotMonitorBacked) {
527
+ return;
528
+ }
529
+
530
+ let monitorStatusId: ObjectID | null = null;
531
+
532
+ if (isMonitorBacked && device.monitorId) {
533
+ /*
534
+ * Scoped to the device's project on the read as well as on the write
535
+ * path in onBeforeCreate/onBeforeUpdate: a monitor from another
536
+ * tenant must never be able to stamp a status here.
537
+ */
538
+ const monitor: Monitor | null = await MonitorService.findOneBy({
539
+ query: {
540
+ _id: device.monitorId,
541
+ projectId: device.projectId,
542
+ },
543
+ select: {
544
+ _id: true,
545
+ currentMonitorStatusId: true,
546
+ },
547
+ props: {
548
+ isRoot: true,
549
+ },
550
+ });
551
+
552
+ monitorStatusId = monitor?.currentMonitorStatusId || null;
553
+ }
554
+
555
+ const currentStampId: string | null =
556
+ device.currentMonitorStatusId?.toString() || null;
557
+ const nextStampId: string | null = monitorStatusId?.toString() || null;
558
+
559
+ if (currentStampId === nextStampId) {
560
+ return;
561
+ }
562
+
563
+ await this.updateColumnsByIdWithoutHooks({
564
+ id: data.deviceId,
565
+ data: {
566
+ currentMonitorStatusId: monitorStatusId,
567
+ },
568
+ });
569
+
570
+ if (device.siteId) {
571
+ await NetworkSiteService.recomputeRollupForSiteAndAncestors(
572
+ device.siteId,
573
+ );
574
+ }
575
+ }
576
+
456
577
  /*
457
578
  * Site maintenance after device updates. Resilient by design: a rollup
458
579
  * or rule-engine failure must never fail the device update itself.
@@ -462,6 +583,33 @@ export class Service extends DatabaseService<Model> {
462
583
  onUpdate: OnUpdate<Model>,
463
584
  updatedItemIds: Array<ObjectID>,
464
585
  ): Promise<OnUpdate<Model>> {
586
+ /*
587
+ * Re-stamp before the site maintenance below, and in its own try: the
588
+ * two are independent, and a rollup failure must not cost the device
589
+ * its status (nor the other way round).
590
+ */
591
+ try {
592
+ const dataKeys: Array<string> = Object.keys(onUpdate.updateBy.data || {});
593
+ const isMethodWrite: boolean = dataKeys.includes("monitoringMethod");
594
+ const isMonitorWrite: boolean = RelationIdUtil.isWritten(
595
+ dataKeys,
596
+ MONITOR_KEYS,
597
+ );
598
+
599
+ if (isMethodWrite || isMonitorWrite) {
600
+ for (const deviceId of updatedItemIds) {
601
+ await this.refreshStampedMonitorStatus({
602
+ deviceId: deviceId,
603
+ clearWhenNotMonitorBacked: isMethodWrite,
604
+ });
605
+ }
606
+ }
607
+ } catch (error) {
608
+ logger.error(
609
+ `Error in NetworkDeviceService.onUpdateSuccess monitor status refresh: ${error}`,
610
+ );
611
+ }
612
+
465
613
  try {
466
614
  const dataKeys: Array<string> = Object.keys(onUpdate.updateBy.data || {});
467
615
 
@@ -31,6 +31,48 @@ export class Service extends DatabaseService<Model> {
31
31
  super(Model);
32
32
  }
33
33
 
34
+ /**
35
+ * Re-resolve and persist the schedule's roster after it is attached to, or
36
+ * detached from, an on-call policy.
37
+ *
38
+ * The persisted roster columns are derived state, and one of their inputs is
39
+ * WHICH policies escalate to the schedule: a schedule attached to exactly one
40
+ * policy is resolved in that policy's context, so its policy-scoped user
41
+ * overrides count, and a schedule attached to none or to several is resolved
42
+ * policy-blind (OnCallDutyPolicyScheduleService.getSingleAttachedPolicyId).
43
+ * Attaching or detaching therefore changes the correct answer, and every other
44
+ * input — layers, layer users, overrides — already refreshes on change.
45
+ *
46
+ * Without this the roster stayed on the pre-attach answer until the next
47
+ * natural hand-off, so the schedule page's "X is currently on the roster"
48
+ * banner and the "On Call Now" column named the originally-scheduled user for
49
+ * the whole override window while alert routing paged the substitute.
50
+ * https://github.com/OneUptime/oneuptime/issues/3411
51
+ *
52
+ * Best-effort: an attach must not fail because a roster could not be
53
+ * recomputed, and the refresh is idempotent (an unaffected schedule simply
54
+ * resolves to the same roster and diffs to "no change", so no duplicate
55
+ * hand-off notification is sent).
56
+ */
57
+ private async refreshScheduleRoster(
58
+ onCallDutyPolicyScheduleId: ObjectID | null | undefined,
59
+ ): Promise<void> {
60
+ if (!onCallDutyPolicyScheduleId) {
61
+ return;
62
+ }
63
+
64
+ try {
65
+ await OnCallDutyPolicyScheduleService.refreshCurrentUserIdAndHandoffTimeInSchedule(
66
+ onCallDutyPolicyScheduleId,
67
+ );
68
+ } catch (err) {
69
+ logger.error(
70
+ "Error refreshing the roster after an on-call schedule was attached to or detached from a policy (best-effort).",
71
+ );
72
+ logger.error(err);
73
+ }
74
+ }
75
+
34
76
  protected override async onCreateSuccess(
35
77
  _onCreate: OnCreate<Model>,
36
78
  createdItem: Model,
@@ -75,6 +117,12 @@ export class Service extends DatabaseService<Model> {
75
117
  );
76
118
  }
77
119
 
120
+ /*
121
+ * Bring the persisted roster in line with the new policy attachment before
122
+ * anything below can return early. See refreshScheduleRoster.
123
+ */
124
+ await this.refreshScheduleRoster(createdModel.onCallDutyPolicyScheduleId);
125
+
78
126
  // send notification to the new current user.
79
127
 
80
128
  const userOnSchedule: ObjectID | null =
@@ -324,6 +372,23 @@ export class Service extends DatabaseService<Model> {
324
372
  ): Promise<OnDelete<Model>> {
325
373
  const deletedItems: Array<Model> = onDelete.carryForward.deletedItems;
326
374
 
375
+ /*
376
+ * Detaching changes the schedule's policy context just as attaching does, so
377
+ * its roster has to be re-resolved here too. Deduped by schedule: removing a
378
+ * schedule from several escalation rules of one policy is one context
379
+ * change, not several. See refreshScheduleRoster.
380
+ */
381
+ const refreshedScheduleIds: Set<string> = new Set<string>();
382
+ for (const deletedItem of deletedItems) {
383
+ const scheduleId: string | undefined =
384
+ deletedItem.onCallDutyPolicyScheduleId?.toString();
385
+ if (!scheduleId || refreshedScheduleIds.has(scheduleId)) {
386
+ continue;
387
+ }
388
+ refreshedScheduleIds.add(scheduleId);
389
+ await this.refreshScheduleRoster(deletedItem.onCallDutyPolicyScheduleId);
390
+ }
391
+
327
392
  for (const deletedItem of deletedItems) {
328
393
  const userOnSchedule: ObjectID | null =
329
394
  await OnCallDutyPolicyScheduleService.getCurrentUserIdInSchedule(
@@ -19,7 +19,7 @@ export class Service extends DatabaseService<OnCallDutyPolicyFeed> {
19
19
  super(OnCallDutyPolicyFeed);
20
20
 
21
21
  if (IsBillingEnabled) {
22
- this.hardDeleteItemsOlderThanInDays("createdAt", 90);
22
+ this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
23
23
  }
24
24
  }
25
25
 
@@ -16,8 +16,14 @@ import UpdateBy from "../Types/Database/UpdateBy";
16
16
  import logger, { LogAttributes } from "../Utils/Logger";
17
17
  import Errors from "../Utils/Errors";
18
18
  import ProductAnalytics from "../Utils/ProductAnalytics";
19
- import MarketingEventUtil from "../Utils/Marketing/MarketingEventUtil";
19
+ import MarketingEventUtil, {
20
+ utmAnalyticsProperties,
21
+ } from "../Utils/Marketing/MarketingEventUtil";
20
22
  import { MarketingEventType } from "../../Types/Marketing/MarketingEvent";
23
+ import {
24
+ UtmPropertyKeys,
25
+ UtmUrlPropertyKey,
26
+ } from "../../Types/Marketing/Attribution";
21
27
  import SessionReplayGateCacheStore from "../Utils/SessionReplay/SessionReplayGateCacheStore";
22
28
  import AccessTokenService from "./AccessTokenService";
23
29
  import BillingService from "./BillingService";
@@ -198,6 +204,10 @@ export class ProjectService extends DatabaseService<Model> {
198
204
  utmMedium: true,
199
205
  utmTerm: true,
200
206
  utmContent: true,
207
+ utmId: true,
208
+ utmSourcePlatform: true,
209
+ utmCreativeFormat: true,
210
+ utmMarketingTactic: true,
201
211
  utmUrl: true,
202
212
  clickIds: true,
203
213
  firstTouchAttribution: true,
@@ -348,13 +358,17 @@ export class ProjectService extends DatabaseService<Model> {
348
358
  data.data.createdOwnerPhone = user.companyPhoneNumber!;
349
359
  data.data.createdOwnerCompanyName = user.companyName!;
350
360
 
351
- // UTM info.
352
- data.data.utmCampaign = user.utmCampaign!;
353
- data.data.utmSource = user.utmSource!;
354
- data.data.utmMedium = user.utmMedium!;
355
- data.data.utmTerm = user.utmTerm!;
356
- data.data.utmContent = user.utmContent!;
357
- data.data.utmUrl = user.utmUrl!;
361
+ /*
362
+ * UTM info, copied field by field off the shared contract rather than
363
+ * hand-listed. Hand-listing is how Project ended up with four columns
364
+ * (utmId and its GA4 siblings) that the schema had but nothing ever wrote.
365
+ */
366
+ const projectRow: JSONObject = data.data as unknown as JSONObject;
367
+ const userRow: JSONObject = user as unknown as JSONObject;
368
+
369
+ for (const propertyKey of [...UtmPropertyKeys, UtmUrlPropertyKey]) {
370
+ projectRow[propertyKey] = userRow[propertyKey];
371
+ }
358
372
 
359
373
  // Ad attribution info (click IDs + first touch).
360
374
  data.data.clickIds = user.clickIds!;
@@ -572,6 +586,10 @@ export class ProjectService extends DatabaseService<Model> {
572
586
  utmCampaign: true,
573
587
  utmTerm: true,
574
588
  utmContent: true,
589
+ utmId: true,
590
+ utmSourcePlatform: true,
591
+ utmCreativeFormat: true,
592
+ utmMarketingTactic: true,
575
593
  utmUrl: true,
576
594
  clickIds: true,
577
595
  firstTouchAttribution: true,
@@ -950,11 +968,7 @@ These are no longer recorded against the project and have to be cancelled by han
950
968
  */
951
969
  is_paid_conversion: true,
952
970
  has_custom_pricing: data.plan.isCustomPricing(),
953
- utm_source: data.project.utmSource || "",
954
- utm_medium: data.project.utmMedium || "",
955
- utm_campaign: data.project.utmCampaign || "",
956
- utm_term: data.project.utmTerm || "",
957
- utm_content: data.project.utmContent || "",
971
+ ...utmAnalyticsProperties(data.project as unknown as JSONObject),
958
972
  click_ids: data.project.clickIds || {},
959
973
  };
960
974
 
@@ -1065,9 +1079,7 @@ These are no longer recorded against the project and have to be cancelled by han
1065
1079
  oldPlanOrder !== null && newPlanOrder === oldPlanOrder,
1066
1080
  is_paid_conversion: oldMonthlyAmountInUSD === 0 && newPlanIsPaid,
1067
1081
  has_custom_pricing: data.newPlan.isCustomPricing(),
1068
- utm_source: data.project.utmSource || "",
1069
- utm_medium: data.project.utmMedium || "",
1070
- utm_campaign: data.project.utmCampaign || "",
1082
+ ...utmAnalyticsProperties(data.project as unknown as JSONObject),
1071
1083
  click_ids: data.project.clickIds || {},
1072
1084
  };
1073
1085
 
@@ -1436,9 +1448,7 @@ These are no longer recorded against the project and have to be cancelled by han
1436
1448
  project_id: createdItem.id?.toString() || "",
1437
1449
  project_name: createdItem.name?.toString() || "",
1438
1450
  plan: createdItem.planName?.toString() || "",
1439
- utm_source: createdItem.utmSource || "",
1440
- utm_medium: createdItem.utmMedium || "",
1441
- utm_campaign: createdItem.utmCampaign || "",
1451
+ ...utmAnalyticsProperties(createdItem as unknown as JSONObject),
1442
1452
  click_ids: createdItem.clickIds || {},
1443
1453
  },
1444
1454
  });