@oneuptime/common 12.0.9 → 12.0.11

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 (262) hide show
  1. package/Models/DatabaseModels/NetworkDevice.ts +44 -0
  2. package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
  3. package/Models/DatabaseModels/RunnerJob.ts +17 -2
  4. package/Server/API/LlmProviderAPI.ts +8 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  7. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
  8. package/Server/Services/NetworkDeviceLinkService.ts +338 -2
  9. package/Server/Services/NetworkDeviceService.ts +265 -12
  10. package/Server/Services/RunnerJobService.ts +24 -1
  11. package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
  12. package/Server/Types/Database/JSONColumnQuery.ts +42 -4
  13. package/Server/Utils/LLM/LLMService.ts +108 -19
  14. package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
  15. package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
  16. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
  17. package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
  18. package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
  19. package/Server/Utils/SSRFProtection.ts +5 -4
  20. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
  21. package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
  22. package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
  23. package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
  24. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
  25. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
  26. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
  27. package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
  28. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
  29. package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
  30. package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
  31. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
  32. package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
  33. package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
  34. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
  35. package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
  36. package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
  37. package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
  38. package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
  39. package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
  40. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
  41. package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
  42. package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
  43. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
  44. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
  45. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
  46. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
  47. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
  48. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
  49. package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
  50. package/Tests/Types/API/URLTelScheme.test.ts +261 -0
  51. package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
  52. package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
  53. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
  54. package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
  55. package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
  56. package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
  57. package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
  58. package/Tests/UI/Components/Button.test.tsx +279 -0
  59. package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
  60. package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
  61. package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
  62. package/Tests/UI/Components/Icon.test.tsx +57 -0
  63. package/Tests/UI/Components/List.test.tsx +20 -3
  64. package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
  65. package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
  66. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
  67. package/Tests/UI/Components/Pagination.test.tsx +721 -146
  68. package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
  69. package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
  70. package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
  71. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
  72. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
  73. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
  74. package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
  75. package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
  76. package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
  77. package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
  78. package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
  79. package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
  80. package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
  81. package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
  82. package/Types/API/Protocol.ts +9 -0
  83. package/Types/API/URL.ts +286 -20
  84. package/Types/Billing/PayAsYouGoPricing.ts +47 -0
  85. package/Types/CustomField/CustomFieldType.ts +28 -0
  86. package/Types/Monitor/MonitorType.ts +11 -0
  87. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
  88. package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
  89. package/Types/Runbook/RunbookStepType.ts +23 -0
  90. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
  91. package/UI/Components/Button/Button.tsx +14 -22
  92. package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
  93. package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
  94. package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
  95. package/UI/Components/Charts/Line/LineChart.tsx +4 -2
  96. package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
  97. package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
  98. package/UI/Components/Dictionary/Dictionary.tsx +4 -39
  99. package/UI/Components/Icon/Icon.tsx +1 -1
  100. package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
  101. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
  102. package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
  103. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
  104. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
  105. package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
  106. package/UI/Components/Pagination/Pagination.tsx +373 -328
  107. package/UI/Components/Pagination/PaginationUtil.ts +263 -0
  108. package/UI/Components/Pagination/Screenshots/README.md +15 -0
  109. package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
  110. package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
  111. package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
  112. package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
  113. package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
  114. package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
  115. package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
  116. package/UI/Components/TextArea/TextArea.tsx +3 -0
  117. package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
  118. package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
  119. package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
  120. package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
  121. package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
  122. package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
  123. package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
  124. package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
  125. package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
  126. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
  127. package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
  128. package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
  129. package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
  130. package/Utils/API.ts +243 -36
  131. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
  132. package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
  133. package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
  134. package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
  135. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  136. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
  137. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
  138. package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
  139. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  140. package/build/dist/Server/API/LlmProviderAPI.js +9 -1
  141. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  142. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
  143. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
  144. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  146. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
  147. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  148. package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
  149. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
  150. package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
  151. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  152. package/build/dist/Server/Services/RunnerJobService.js +20 -2
  153. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  154. package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
  155. package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
  156. package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
  157. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
  158. package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
  159. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  160. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
  161. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  162. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
  163. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  164. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
  165. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  166. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
  167. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  168. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
  169. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
  170. package/build/dist/Server/Utils/SSRFProtection.js +5 -4
  171. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  172. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
  173. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  174. package/build/dist/Types/API/Protocol.js +9 -0
  175. package/build/dist/Types/API/Protocol.js.map +1 -1
  176. package/build/dist/Types/API/URL.js +241 -18
  177. package/build/dist/Types/API/URL.js.map +1 -1
  178. package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
  179. package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
  180. package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
  181. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  182. package/build/dist/Types/Monitor/MonitorType.js +10 -0
  183. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  184. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
  185. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
  186. package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
  187. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  188. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  189. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  190. package/build/dist/UI/Components/Button/Button.js +10 -16
  191. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  192. package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
  193. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  194. package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
  195. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  196. package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
  197. package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
  198. package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
  199. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  200. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
  201. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  202. package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
  203. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  204. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  205. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  206. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
  207. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  208. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
  209. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  210. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
  211. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
  212. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  213. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  214. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
  215. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  216. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
  217. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  218. package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
  219. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  220. package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
  221. package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
  222. package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
  223. package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
  224. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
  225. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
  226. package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
  227. package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
  228. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
  229. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  230. package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
  231. package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
  232. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
  233. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
  234. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
  235. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
  236. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
  237. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
  238. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
  239. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
  240. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
  241. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
  242. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
  243. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
  244. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
  245. package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
  246. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
  247. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
  248. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
  249. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
  250. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
  251. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
  252. package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
  253. package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
  254. package/build/dist/Utils/API.js +175 -34
  255. package/build/dist/Utils/API.js.map +1 -1
  256. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
  257. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
  258. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
  259. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  260. package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
  261. package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
  262. package/package.json +1 -1
@@ -646,6 +646,50 @@ export default class NetworkDevice extends BaseModel {
646
646
  })
647
647
  public monitoringMethod?: string = undefined;
648
648
 
649
+ @ColumnAccessControl({
650
+ create: [
651
+ Permission.ProjectOwner,
652
+ Permission.ProjectAdmin,
653
+ Permission.ProjectMember,
654
+ Permission.SettingsAdmin,
655
+ Permission.SettingsMember,
656
+ Permission.CreateNetworkDevice,
657
+ ],
658
+ read: [
659
+ Permission.ProjectOwner,
660
+ Permission.ProjectAdmin,
661
+ Permission.ProjectMember,
662
+ Permission.Viewer,
663
+ Permission.SettingsAdmin,
664
+ Permission.SettingsMember,
665
+ Permission.SettingsViewer,
666
+ Permission.ReadNetworkDevice,
667
+ ],
668
+ update: [
669
+ Permission.ProjectOwner,
670
+ Permission.ProjectAdmin,
671
+ Permission.ProjectMember,
672
+ Permission.SettingsAdmin,
673
+ Permission.SettingsMember,
674
+ Permission.EditNetworkDevice,
675
+ ],
676
+ })
677
+ @TableColumn({
678
+ required: false,
679
+ type: TableColumnType.ShortText,
680
+ canReadOnRelationQuery: true,
681
+ title: "Device Role",
682
+ description:
683
+ "What this device does on the network — router, switch, access point and so on. Left empty, the role is worked out from the device's own SNMP identity. Set it when there is no SNMP to read: a ping-only device has no identity to classify, and the role decides both the shape it is drawn with and where it sits in the topology hierarchy.",
684
+ example: "switch",
685
+ })
686
+ @Column({
687
+ nullable: true,
688
+ type: ColumnType.ShortText,
689
+ length: ColumnLength.ShortText,
690
+ })
691
+ public deviceRole?: string = undefined;
692
+
649
693
  @ColumnAccessControl({
650
694
  create: [
651
695
  Permission.ProjectOwner,
@@ -376,6 +376,122 @@ export default class NetworkDeviceLink extends BaseModel {
376
376
  })
377
377
  public toDeviceId?: ObjectID = undefined;
378
378
 
379
+ /*
380
+ * --- Which end is up ---
381
+ *
382
+ * A link says two devices are cabled together. It does not say which one
383
+ * the other hangs off, and the topology map has to decide: the
384
+ * parent-child view draws an actual tree, so somebody is the parent.
385
+ * Left to itself it infers one from device roles and connection counts,
386
+ * which is a good guess on a network that speaks SNMP and no guess at
387
+ * all on one that does not — the case issue #3192 is about, where the
388
+ * child is a ping-only device whose role nothing can classify.
389
+ *
390
+ * This column is the operator answering that question outright. It is
391
+ * NULL by default and on every row written before it existed, and NULL
392
+ * means exactly what the map did then: a peer link, direction inferred.
393
+ * So no existing map moves, and a hierarchy only becomes declared when
394
+ * somebody declares it.
395
+ *
396
+ * The value must be one of this link's own two ends — a parent that is
397
+ * not on the link is not a statement about the link — which the service
398
+ * enforces on create and on update, including when an update moves an
399
+ * end out from under a parent that was already set.
400
+ */
401
+ @ColumnAccessControl({
402
+ create: [
403
+ Permission.ProjectOwner,
404
+ Permission.ProjectAdmin,
405
+ Permission.ProjectMember,
406
+ Permission.SettingsAdmin,
407
+ Permission.SettingsMember,
408
+ Permission.CreateNetworkDeviceLink,
409
+ ],
410
+ read: [
411
+ Permission.ProjectOwner,
412
+ Permission.ProjectAdmin,
413
+ Permission.ProjectMember,
414
+ Permission.Viewer,
415
+ Permission.SettingsAdmin,
416
+ Permission.SettingsMember,
417
+ Permission.SettingsViewer,
418
+ Permission.ReadNetworkDeviceLink,
419
+ ],
420
+ update: [
421
+ Permission.ProjectOwner,
422
+ Permission.ProjectAdmin,
423
+ Permission.ProjectMember,
424
+ Permission.SettingsAdmin,
425
+ Permission.SettingsMember,
426
+ Permission.EditNetworkDeviceLink,
427
+ ],
428
+ })
429
+ @TableColumn({
430
+ manyToOneRelationColumn: "parentDeviceId",
431
+ type: TableColumnType.Entity,
432
+ modelType: NetworkDevice,
433
+ title: "Parent Device",
434
+ description:
435
+ "Relation to whichever end of this link is the parent, if the hierarchy is declared rather than inferred",
436
+ })
437
+ @ManyToOne(
438
+ () => {
439
+ return NetworkDevice;
440
+ },
441
+ {
442
+ eager: false,
443
+ nullable: true,
444
+ onDelete: "CASCADE",
445
+ orphanedRowAction: "nullify",
446
+ },
447
+ )
448
+ @JoinColumn({ name: "parentDeviceId" })
449
+ public parentDevice?: NetworkDevice = undefined;
450
+
451
+ @ColumnAccessControl({
452
+ create: [
453
+ Permission.ProjectOwner,
454
+ Permission.ProjectAdmin,
455
+ Permission.ProjectMember,
456
+ Permission.SettingsAdmin,
457
+ Permission.SettingsMember,
458
+ Permission.CreateNetworkDeviceLink,
459
+ ],
460
+ read: [
461
+ Permission.ProjectOwner,
462
+ Permission.ProjectAdmin,
463
+ Permission.ProjectMember,
464
+ Permission.Viewer,
465
+ Permission.SettingsAdmin,
466
+ Permission.SettingsMember,
467
+ Permission.SettingsViewer,
468
+ Permission.ReadNetworkDeviceLink,
469
+ ],
470
+ update: [
471
+ Permission.ProjectOwner,
472
+ Permission.ProjectAdmin,
473
+ Permission.ProjectMember,
474
+ Permission.SettingsAdmin,
475
+ Permission.SettingsMember,
476
+ Permission.EditNetworkDeviceLink,
477
+ ],
478
+ })
479
+ @Index()
480
+ @TableColumn({
481
+ type: TableColumnType.ObjectID,
482
+ required: false,
483
+ canReadOnRelationQuery: true,
484
+ title: "Parent Device ID",
485
+ description:
486
+ "ID of whichever end of this link is the parent. Must be the From Device or the To Device. Empty means the two are peers and the map infers the hierarchy.",
487
+ })
488
+ @Column({
489
+ type: ColumnType.ObjectID,
490
+ nullable: true,
491
+ transformer: ObjectID.getDatabaseTransformer(),
492
+ })
493
+ public parentDeviceId?: ObjectID = undefined;
494
+
379
495
  @ColumnAccessControl({
380
496
  create: [
381
497
  Permission.ProjectOwner,
@@ -32,7 +32,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
32
32
  pluralName: "Runner Jobs",
33
33
  icon: IconProp.Logs,
34
34
  tableDescription:
35
- "One row per Bash or JavaScript step dispatched to a specific Runner. Tracks claim, execution, and result. Managed by the Worker and the agents; not user-writable.",
35
+ "One row per step dispatched to a specific Runner — Bash and JavaScript, which carry a script, and SSH and Kubernetes, which carry structured instructions instead. Tracks claim, execution, and result. Managed by the Worker and the agents; not user-writable.",
36
36
  })
37
37
  @TableAccessControl({
38
38
  create: [],
@@ -449,6 +449,21 @@ export default class RunnerJob extends BaseModel {
449
449
  })
450
450
  public status?: RunnerJobStatus = undefined;
451
451
 
452
+ /*
453
+ * NOT a required column, even though the database keeps it NOT NULL.
454
+ *
455
+ * `required` feeds getRequiredColumns(), which
456
+ * DatabaseService.checkRequiredFields rejects on any falsy value — and an
457
+ * empty string is falsy. SSH and Kubernetes jobs carry their instruction in
458
+ * `payload` and an empty script by design, so marking this required made
459
+ * every one of them fail at create() with "script is required" before a
460
+ * Runner ever saw the job. The NOT NULL constraint still holds:
461
+ * RunnerJobService always writes a string, empty or not.
462
+ *
463
+ * Which step types must carry a script and which must carry a payload is a
464
+ * per-type rule that column metadata cannot express, so it is enforced in
465
+ * RunnerJobService.enqueue instead.
466
+ */
452
467
  @ColumnAccessControl({
453
468
  create: [],
454
469
  read: [
@@ -464,7 +479,7 @@ export default class RunnerJob extends BaseModel {
464
479
  })
465
480
  @TableColumn({
466
481
  type: TableColumnType.VeryLongText,
467
- required: true,
482
+ required: false,
468
483
  title: "Script",
469
484
  description:
470
485
  "The script the Runner must execute. Empty for step types that carry structured instructions in the payload instead.",
@@ -191,6 +191,14 @@ export default class LlmProviderAPI extends BaseAPI<
191
191
  ],
192
192
  temperature: 0,
193
193
  maxTokens: 16,
194
+ /*
195
+ * A connection test is a person waiting on a verdict, not a
196
+ * chat turn worth saving. The default ten-attempt ladder would
197
+ * make "your base URL is unreachable" — the answer this button
198
+ * exists to give — take the better part of a minute to arrive.
199
+ * One retry still absorbs a single blip.
200
+ */
201
+ requestRetries: 1,
194
202
  llmProviderConfig: llmProviderConfig,
195
203
  ...(provider.additionalParams
196
204
  ? { additionalParams: provider.additionalParams }
@@ -0,0 +1,52 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ /*
4
+ * Two nullable columns, both of them an operator stating something the
5
+ * network cannot be asked:
6
+ *
7
+ * NetworkDevice.deviceRole — what this box actually is, for devices
8
+ * with no SNMP identity to classify.
9
+ * NetworkDeviceLink.parentDeviceId — which end of a declared link is up.
10
+ *
11
+ * Nullable on purpose, with no backfill. NULL is a meaningful value in
12
+ * both cases and it is the value every existing row should hold: no role
13
+ * override (so the classifier keeps deciding) and no declared hierarchy
14
+ * (so the map keeps inferring one). Defaulting either would silently
15
+ * restate every device and every link in every project as something an
16
+ * operator had chosen, which is the one thing neither column may mean.
17
+ */
18
+ export class AddDeviceRoleAndDeclaredLinkParent1787400000000
19
+ implements MigrationInterface
20
+ {
21
+ public name = "AddDeviceRoleAndDeclaredLinkParent1787400000000";
22
+
23
+ public async up(queryRunner: QueryRunner): Promise<void> {
24
+ await queryRunner.query(
25
+ `ALTER TABLE "NetworkDevice" ADD "deviceRole" character varying(100)`,
26
+ );
27
+ await queryRunner.query(
28
+ `ALTER TABLE "NetworkDeviceLink" ADD "parentDeviceId" uuid`,
29
+ );
30
+ await queryRunner.query(
31
+ `CREATE INDEX "IDX_01752cd47b773df38b4a54ae53" ON "NetworkDeviceLink" ("parentDeviceId") `,
32
+ );
33
+ await queryRunner.query(
34
+ `ALTER TABLE "NetworkDeviceLink" ADD CONSTRAINT "FK_01752cd47b773df38b4a54ae539" FOREIGN KEY ("parentDeviceId") REFERENCES "NetworkDevice"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
35
+ );
36
+ }
37
+
38
+ public async down(queryRunner: QueryRunner): Promise<void> {
39
+ await queryRunner.query(
40
+ `ALTER TABLE "NetworkDeviceLink" DROP CONSTRAINT "FK_01752cd47b773df38b4a54ae539"`,
41
+ );
42
+ await queryRunner.query(
43
+ `DROP INDEX "public"."IDX_01752cd47b773df38b4a54ae53"`,
44
+ );
45
+ await queryRunner.query(
46
+ `ALTER TABLE "NetworkDeviceLink" DROP COLUMN "parentDeviceId"`,
47
+ );
48
+ await queryRunner.query(
49
+ `ALTER TABLE "NetworkDevice" DROP COLUMN "deviceRole"`,
50
+ );
51
+ }
52
+ }
@@ -527,6 +527,7 @@ import { AddOnCallNotificationFallbackColumns1787000000000 } from "./17870000000
527
527
  import { AddAIConversationPageContext1787100000000 } from "./1787100000000-AddAIConversationPageContext";
528
528
  import { AddAIChatMessageFeedback1787200000000 } from "./1787200000000-AddAIChatMessageFeedback";
529
529
  import { AddEpisodeMemberNotifyIndexes1787300000000 } from "./1787300000000-AddEpisodeMemberNotifyIndexes";
530
+ import { AddDeviceRoleAndDeclaredLinkParent1787400000000 } from "./1787400000000-AddDeviceRoleAndDeclaredLinkParent";
530
531
 
531
532
  export default [
532
533
  InitialMigration,
@@ -1058,4 +1059,5 @@ export default [
1058
1059
  AddAIConversationPageContext1787100000000,
1059
1060
  AddAIChatMessageFeedback1787200000000,
1060
1061
  AddEpisodeMemberNotifyIndexes1787300000000,
1062
+ AddDeviceRoleAndDeclaredLinkParent1787400000000,
1061
1063
  ];
@@ -3,11 +3,38 @@ import NetworkDevice from "../../Models/DatabaseModels/NetworkDevice";
3
3
  import NetworkDeviceLabelRule from "../../Models/DatabaseModels/NetworkDeviceLabelRule";
4
4
  import NetworkDeviceLabelRuleService from "./NetworkDeviceLabelRuleService";
5
5
  import NetworkDeviceService from "./NetworkDeviceService";
6
+ import BadDataException from "../../Types/Exception/BadDataException";
6
7
  import ObjectID from "../../Types/ObjectID";
8
+ import SortOrder from "../../Types/BaseDatabase/SortOrder";
9
+ import { LabelRuleRunResult } from "../../Types/NetworkAutomation/RuleRunResult";
7
10
  import RulePatternMatchUtil from "../../Utils/Rules/RulePatternMatchUtil";
8
11
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
9
12
  import logger, { LogAttributes } from "../Utils/Logger";
10
13
 
14
+ /*
15
+ * Bounds on one manual "Run now" of a label rule. The automatic path only
16
+ * ever sees a single freshly created device; a retroactive run walks the
17
+ * whole estate, so it reads devices in pages and stops at a cap, reporting
18
+ * isTruncated instead of holding an HTTP request open over a full fleet.
19
+ */
20
+ export const MAX_DEVICES_PER_LABEL_RULE_RUN: number = 10000;
21
+ const LABEL_RULE_RUN_PAGE_SIZE: number = 1000;
22
+
23
+ /*
24
+ * How many (device, label) pairs one INSERT attaches. TypeORM expands
25
+ * `.of(ids).add(labelId)` into a parameter per id, and Postgres refuses a
26
+ * statement with more than 65535 of them.
27
+ */
28
+ const LABEL_ATTACH_CHUNK_SIZE: number = 500;
29
+
30
+ function chunk<T>(items: Array<T>, size: number): Array<Array<T>> {
31
+ const chunks: Array<Array<T>> = [];
32
+ for (let index: number = 0; index < items.length; index += size) {
33
+ chunks.push(items.slice(index, index + size));
34
+ }
35
+ return chunks;
36
+ }
37
+
11
38
  class NetworkDeviceLabelRuleEngineServiceClass {
12
39
  /**
13
40
  * Evaluates NetworkDeviceLabelRule rows for the given network device and attaches matched
@@ -133,6 +160,219 @@ class NetworkDeviceLabelRuleEngineServiceClass {
133
160
  }
134
161
  }
135
162
 
163
+ /*
164
+ * Runs ONE label rule against the network devices that already exist.
165
+ *
166
+ * The automatic engine above only fires on device creation, so a rule
167
+ * written after an estate was imported or discovered would never reach a
168
+ * single one of those devices (OneUptime/oneuptime#3191). This is the
169
+ * manual counterpart: same matcher, same "already-attached labels are not
170
+ * duplicated" guarantee, applied across the project instead of to one row.
171
+ *
172
+ * Safe to run repeatedly — attaching labels is additive and idempotent, so
173
+ * a second run reports devicesMatched with devicesLabeled at zero.
174
+ */
175
+ @CaptureSpan()
176
+ public async applyRuleToExistingNetworkDevices(data: {
177
+ ruleId: ObjectID;
178
+ projectId: ObjectID;
179
+ }): Promise<LabelRuleRunResult> {
180
+ const rule: NetworkDeviceLabelRule | null =
181
+ await NetworkDeviceLabelRuleService.findOneBy({
182
+ query: {
183
+ _id: data.ruleId,
184
+ projectId: data.projectId,
185
+ },
186
+ select: {
187
+ _id: true,
188
+ name: true,
189
+ isEnabled: true,
190
+ networkDeviceLabels: { _id: true },
191
+ networkDeviceNamePattern: true,
192
+ networkDeviceDescriptionPattern: true,
193
+ labelsToAdd: { _id: true },
194
+ },
195
+ props: { isRoot: true },
196
+ });
197
+
198
+ if (!rule) {
199
+ throw new BadDataException("Label rule not found.");
200
+ }
201
+
202
+ /*
203
+ * A disabled rule is one the user has switched off; running it by hand
204
+ * would contradict the toggle they can see right next to the button.
205
+ */
206
+ if (!rule.isEnabled) {
207
+ throw new BadDataException(
208
+ "This label rule is disabled. Enable it before running it.",
209
+ );
210
+ }
211
+
212
+ const labelIdsToAdd: Array<string> = (rule.labelsToAdd || [])
213
+ .map((label: Label) => {
214
+ return label.id?.toString() || "";
215
+ })
216
+ .filter((id: string) => {
217
+ return id !== "";
218
+ });
219
+
220
+ if (labelIdsToAdd.length === 0) {
221
+ throw new BadDataException(
222
+ "This label rule has no labels to add, so running it would do nothing.",
223
+ );
224
+ }
225
+
226
+ const result: LabelRuleRunResult = {
227
+ devicesEvaluated: 0,
228
+ devicesMatched: 0,
229
+ devicesLabeled: 0,
230
+ labelsAttached: 0,
231
+ labelsFailed: 0,
232
+ isTruncated: false,
233
+ };
234
+
235
+ // Counted across pages so a device is never counted twice.
236
+ const labeledDeviceIds: Set<string> = new Set();
237
+
238
+ let skip: number = 0;
239
+
240
+ for (;;) {
241
+ const devices: Array<NetworkDevice> = await NetworkDeviceService.findBy({
242
+ query: {
243
+ projectId: data.projectId,
244
+ },
245
+ select: {
246
+ _id: true,
247
+ name: true,
248
+ description: true,
249
+ labels: { _id: true },
250
+ },
251
+ /*
252
+ * Sorted by id so paging stays stable across the writes this run
253
+ * makes: attaching a label never changes a device's id, so no row
254
+ * can shift between pages and be skipped or seen twice.
255
+ */
256
+ sort: {
257
+ _id: SortOrder.Ascending,
258
+ },
259
+ limit: LABEL_RULE_RUN_PAGE_SIZE,
260
+ skip: skip,
261
+ props: { isRoot: true },
262
+ });
263
+
264
+ if (devices.length === 0) {
265
+ break;
266
+ }
267
+
268
+ /*
269
+ * One INSERT per label rather than per device: every matched device
270
+ * needs the same label set, so the work collapses into a handful of
271
+ * statements no matter how large the estate is.
272
+ */
273
+ const deviceIdsByLabelId: Map<string, Array<string>> = new Map();
274
+
275
+ for (const device of devices) {
276
+ result.devicesEvaluated++;
277
+
278
+ if (!this.doesNetworkDeviceMatchRule(device, rule)) {
279
+ continue;
280
+ }
281
+
282
+ result.devicesMatched++;
283
+
284
+ const deviceId: string | undefined = device.id?.toString();
285
+
286
+ if (!deviceId) {
287
+ continue;
288
+ }
289
+
290
+ const existingLabelIds: Set<string> = new Set(
291
+ (device.labels || []).map((label: Label) => {
292
+ return label.id?.toString() || "";
293
+ }),
294
+ );
295
+
296
+ for (const labelId of labelIdsToAdd) {
297
+ if (existingLabelIds.has(labelId)) {
298
+ continue;
299
+ }
300
+
301
+ const deviceIds: Array<string> =
302
+ deviceIdsByLabelId.get(labelId) || [];
303
+ deviceIds.push(deviceId);
304
+ deviceIdsByLabelId.set(labelId, deviceIds);
305
+ }
306
+ }
307
+
308
+ for (const [labelId, deviceIds] of deviceIdsByLabelId) {
309
+ for (const deviceIdChunk of chunk(deviceIds, LABEL_ATTACH_CHUNK_SIZE)) {
310
+ try {
311
+ await NetworkDeviceService.getRepository()
312
+ .createQueryBuilder()
313
+ .relation(NetworkDevice, "labels")
314
+ .of(deviceIdChunk)
315
+ .add(labelId);
316
+
317
+ result.labelsAttached += deviceIdChunk.length;
318
+
319
+ for (const deviceId of deviceIdChunk) {
320
+ labeledDeviceIds.add(deviceId);
321
+ }
322
+ } catch (error) {
323
+ /*
324
+ * A failed batch must not abandon the rest of the estate — the
325
+ * run reports it and carries on.
326
+ */
327
+ result.labelsFailed += deviceIdChunk.length;
328
+ logger.error(
329
+ `Error attaching label ${labelId} from network device label rule ${data.ruleId.toString()}: ${error}`,
330
+ {
331
+ projectId: data.projectId.toString(),
332
+ } as LogAttributes,
333
+ );
334
+ }
335
+ }
336
+ }
337
+
338
+ skip += devices.length;
339
+
340
+ if (devices.length < LABEL_RULE_RUN_PAGE_SIZE) {
341
+ break;
342
+ }
343
+
344
+ if (skip >= MAX_DEVICES_PER_LABEL_RULE_RUN) {
345
+ /*
346
+ * A full last page is not proof that more devices exist — an estate
347
+ * of exactly the cap would report a truncation that never happened.
348
+ * One row settles it.
349
+ */
350
+ const nextDevice: Array<NetworkDevice> =
351
+ await NetworkDeviceService.findBy({
352
+ query: {
353
+ projectId: data.projectId,
354
+ },
355
+ select: {
356
+ _id: true,
357
+ },
358
+ sort: {
359
+ _id: SortOrder.Ascending,
360
+ },
361
+ limit: 1,
362
+ skip: skip,
363
+ props: { isRoot: true },
364
+ });
365
+
366
+ result.isTruncated = nextDevice.length > 0;
367
+ break;
368
+ }
369
+ }
370
+
371
+ result.devicesLabeled = labeledDeviceIds.size;
372
+
373
+ return result;
374
+ }
375
+
136
376
  private doesNetworkDeviceMatchRule(
137
377
  networkDevice: NetworkDevice,
138
378
  rule: NetworkDeviceLabelRule,