@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
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/RunnerJob";
5
5
  import RunnerJobStatus from "../../Types/Runbook/RunnerJobStatus";
6
6
  import RunnerJobOrigin from "../../Types/Runbook/RunnerJobOrigin";
7
7
  import RunbookStepType, {
8
+ isPayloadCarryingStepType,
8
9
  isRunnerExecutedStepType,
9
10
  } from "../../Types/Runbook/RunbookStepType";
10
11
  import { AI_COMMAND_STEP_TYPES } from "../../Types/AutoRemediation/AiRemediationCommandPlan";
@@ -106,6 +107,27 @@ export class Service extends DatabaseService<Model> {
106
107
  );
107
108
  }
108
109
 
110
+ /*
111
+ * Each runner-executed type carries its instruction in exactly one place:
112
+ * a script, or a structured payload. Enforced here rather than as a
113
+ * required column because it is per-type — the column check would reject
114
+ * an SSH job's empty script, which is the shape SSH jobs are supposed to
115
+ * have. A job dispatched with neither would reach a Runner that has
116
+ * nothing to do and report success, which looks identical to a step that
117
+ * actually ran.
118
+ */
119
+ if (isPayloadCarryingStepType(data.stepType)) {
120
+ if (!data.payload) {
121
+ throw new BadDataException(
122
+ `A ${data.stepType} job needs structured instructions to send to the Runner.`,
123
+ );
124
+ }
125
+ } else if (!data.script) {
126
+ throw new BadDataException(
127
+ `A ${data.stepType} job needs a script for the Runner to execute.`,
128
+ );
129
+ }
130
+
109
131
  const claimDeadlineAt: Date = OneUptimeDate.addRemoveSeconds(
110
132
  OneUptimeDate.getCurrentDate(),
111
133
  Math.ceil((data.claimTimeoutInMs ?? DEFAULT_CLAIM_TIMEOUT_MS) / 1000),
@@ -118,7 +140,8 @@ export class Service extends DatabaseService<Model> {
118
140
  row.stepId = data.stepId;
119
141
  row.stepType = data.stepType;
120
142
  row.targetAgentId = data.targetAgentId;
121
- row.script = data.script;
143
+ // The column is NOT NULL, so a payload-carrying job stores "" rather than null.
144
+ row.script = data.script || "";
122
145
  if (data.payload) {
123
146
  row.payload = data.payload;
124
147
  }
@@ -4,6 +4,19 @@ import ServerMeteredPlan from "./ServerMeteredPlan";
4
4
  import TelemetryMeteredPlanType from "./TelemetryMeteredPlan";
5
5
  import BadDataException from "../../../../Types/Exception/BadDataException";
6
6
  import ProductType from "../../../../Types/MeteredPlan/ProductType";
7
+ import {
8
+ SESSION_REPLAY_PRICE_IN_USD_PER_GB,
9
+ TELEMETRY_PRICE_IN_USD_PER_GB,
10
+ TELEMETRY_PRICE_RETENTION_IN_DAYS,
11
+ } from "../../../../Types/Billing/PayAsYouGoPricing";
12
+
13
+ /*
14
+ * Per GB per day, derived from the advertised per-GB-per-15-days rate so the
15
+ * price the dashboard quotes a Free plan user and the price we actually meter
16
+ * cannot drift apart.
17
+ */
18
+ const TELEMETRY_UNIT_COST_IN_USD: number =
19
+ TELEMETRY_PRICE_IN_USD_PER_GB / TELEMETRY_PRICE_RETENTION_IN_DAYS;
7
20
 
8
21
  export const ActiveMonitoringMeteredPlan: ActiveMonitoringMeteredPlanType =
9
22
  new ActiveMonitoringMeteredPlanType();
@@ -11,25 +24,25 @@ export const ActiveMonitoringMeteredPlan: ActiveMonitoringMeteredPlanType =
11
24
  export const LogDataIngestMeteredPlan: TelemetryMeteredPlanType =
12
25
  new TelemetryMeteredPlanType({
13
26
  productType: ProductType.Logs,
14
- unitCostInUSD: 0.1 / 15, // 0.10 per 15 days per GB
27
+ unitCostInUSD: TELEMETRY_UNIT_COST_IN_USD, // 0.10 per 15 days per GB
15
28
  });
16
29
 
17
30
  export const MetricsDataIngestMeteredPlan: TelemetryMeteredPlanType =
18
31
  new TelemetryMeteredPlanType({
19
32
  productType: ProductType.Metrics,
20
- unitCostInUSD: 0.1 / 15, // 0.10 per 15 days per GB
33
+ unitCostInUSD: TELEMETRY_UNIT_COST_IN_USD, // 0.10 per 15 days per GB
21
34
  });
22
35
 
23
36
  export const TracesDataIngestMetredPlan: TelemetryMeteredPlanType =
24
37
  new TelemetryMeteredPlanType({
25
38
  productType: ProductType.Traces,
26
- unitCostInUSD: 0.1 / 15, // 0.10 per 15 days per GB
39
+ unitCostInUSD: TELEMETRY_UNIT_COST_IN_USD, // 0.10 per 15 days per GB
27
40
  });
28
41
 
29
42
  export const ProfilesDataIngestMeteredPlan: TelemetryMeteredPlanType =
30
43
  new TelemetryMeteredPlanType({
31
44
  productType: ProductType.Profiles,
32
- unitCostInUSD: 0.1 / 15, // 0.10 per 15 days per GB
45
+ unitCostInUSD: TELEMETRY_UNIT_COST_IN_USD, // 0.10 per 15 days per GB
33
46
  });
34
47
 
35
48
  /*
@@ -43,7 +56,8 @@ export const ProfilesDataIngestMeteredPlan: TelemetryMeteredPlanType =
43
56
  export const SessionReplayDataIngestMeteredPlan: TelemetryMeteredPlanType =
44
57
  new TelemetryMeteredPlanType({
45
58
  productType: ProductType.SessionReplay,
46
- unitCostInUSD: 2.0 / 15, // 2.00 per 15 days per GB
59
+ unitCostInUSD:
60
+ SESSION_REPLAY_PRICE_IN_USD_PER_GB / TELEMETRY_PRICE_RETENTION_IN_DAYS, // 2.00 per 15 days per GB
47
61
  });
48
62
 
49
63
  const AllMeteredPlans: Array<ServerMeteredPlan> = [
@@ -154,6 +154,18 @@ const toScalar: ToScalarFunction = (value: unknown): ScalarValue => {
154
154
  return value.toString();
155
155
  }
156
156
 
157
+ /*
158
+ * A Date only reaches here from a query built server-side: one that arrived
159
+ * over HTTP has already been through JSON, which renders a Date as its ISO
160
+ * string. Both paths must produce the same text, because the ordering
161
+ * comparisons below are lexicographic and only ISO-8601 makes that
162
+ * chronological. The `String(value)` fallback would yield
163
+ * "Sun Aug 17 2026 00:00:00 GMT+0000", which sorts by weekday name.
164
+ */
165
+ if (value instanceof Date) {
166
+ return value.toISOString();
167
+ }
168
+
157
169
  if (value === null || value === undefined) {
158
170
  return "";
159
171
  }
@@ -313,6 +325,32 @@ class KeyPredicateBuilder {
313
325
  };
314
326
  }
315
327
 
328
+ /**
329
+ * An ordering comparison against a value whose type we only learn at runtime.
330
+ *
331
+ * A jsonb key holds whatever the project put there, so `>` has two readings.
332
+ * Numbers compare numerically — "10" must be greater than "9", which text
333
+ * comparison gets backwards. Everything else compares as text, and the case
334
+ * that matters is a date: custom field dates are stored as ISO-8601 strings,
335
+ * and ISO-8601 is designed so that lexicographic order IS chronological
336
+ * order, so `>=` over the raw text is already the right question.
337
+ *
338
+ * Routing dates through compareNumeric — which is what these operators used
339
+ * to do unconditionally — was silently wrong rather than loudly wrong.
340
+ * `Number("2026-08-17T00:00:00.000Z")` is NaN, so the bound parameter became
341
+ * `CAST(NaN AS NUMERIC)`, and `numericExpression` independently returns NULL
342
+ * for text that does not look like a number. The predicate was therefore
343
+ * `NULL > NaN` on every row: no error, no rows, and a lit filter chip over an
344
+ * empty table.
345
+ *
346
+ * InBetween has always branched this way; these four now agree with it.
347
+ */
348
+ private compareOrdered(operator: string, value: unknown): PredicateFragment {
349
+ return isNumericValue(toScalar(value))
350
+ ? this.compareNumeric(operator, value)
351
+ : this.compareText(operator, value);
352
+ }
353
+
316
354
  /**
317
355
  * "No value here" — the key is absent, explicitly JSON null, an empty
318
356
  * string, or an empty array. A multi-select cleared in the UI leaves `[]`
@@ -466,19 +504,19 @@ class KeyPredicateBuilder {
466
504
  }
467
505
 
468
506
  if (value instanceof GreaterThanOrEqual) {
469
- return this.compareNumeric(">=", value.value);
507
+ return this.compareOrdered(">=", value.value);
470
508
  }
471
509
 
472
510
  if (value instanceof GreaterThan) {
473
- return this.compareNumeric(">", value.value);
511
+ return this.compareOrdered(">", value.value);
474
512
  }
475
513
 
476
514
  if (value instanceof LessThanOrEqual) {
477
- return this.compareNumeric("<=", value.value);
515
+ return this.compareOrdered("<=", value.value);
478
516
  }
479
517
 
480
518
  if (value instanceof LessThan) {
481
- return this.compareNumeric("<", value.value);
519
+ return this.compareOrdered("<", value.value);
482
520
  }
483
521
 
484
522
  /*
@@ -49,6 +49,12 @@ export interface LLMCompletionRequest {
49
49
  requestTimeoutInMs?: number | undefined;
50
50
  /** Number of retries after the initial request. */
51
51
  requestRetries?: number | undefined;
52
+ /**
53
+ * Wall-clock budget for the whole retry ladder. Defaults to
54
+ * getRetryDeadlineInMs — raise it for a caller that can afford to wait
55
+ * longer than an interactive chat turn.
56
+ */
57
+ requestRetryDeadlineInMs?: number | undefined;
52
58
  /** Re-apply caller-owned request fields after provider-level overrides. */
53
59
  protectRequestParameters?: boolean | undefined;
54
60
  /**
@@ -121,6 +127,44 @@ interface OpenAIRequestAdaptation {
121
127
  }
122
128
 
123
129
  export default class LLMService {
130
+ /*
131
+ * How many times a provider call is attempted before it is reported as a
132
+ * failure. The provider hop is the flakiest one in the product — hosted
133
+ * endpoints rate limit, self-hosted ones fall behind their own queue, and a
134
+ * cold model can miss the per-attempt timeout while the next attempt sails
135
+ * through — and every one of those used to surface as a dead chat turn
136
+ * after three tries.
137
+ *
138
+ * This is a count of ATTEMPTS; API takes a count of retries, which is one
139
+ * fewer.
140
+ */
141
+ public static readonly DEFAULT_REQUEST_ATTEMPTS: number = 10;
142
+
143
+ /*
144
+ * Ten doublings would put the last wait seventeen minutes out. Capped at
145
+ * eight seconds the whole ladder (2, 4, 8, 8, ...) costs about a minute of
146
+ * sleep before jitter, which is the point: ride out a provider blip without
147
+ * turning one chat turn into a coffee break.
148
+ */
149
+ public static readonly MAX_BACKOFF_IN_MS: number = 8 * 1000;
150
+
151
+ /*
152
+ * Ten attempts is the right answer for failures that fail FAST — a 429, a
153
+ * refused connection, a 502 from a load balancer. Ten TIMEOUTS are a
154
+ * different story: at the 120s default that is twenty minutes, against a
155
+ * ChatAgentRunner budget of five for an entire turn, so the ladder gets a
156
+ * wall clock of its own.
157
+ */
158
+ private static readonly DEFAULT_RETRY_DEADLINE_IN_MS: number = 5 * 60 * 1000;
159
+
160
+ /*
161
+ * The floor keeps the deadline from ever costing a provider attempts it had
162
+ * before this budget existed: three full-timeout attempts is what the old
163
+ * two-retry policy allowed, and a slow provider (Ollama at 300s) still gets
164
+ * exactly that.
165
+ */
166
+ private static readonly MIN_FULL_TIMEOUT_ATTEMPTS_WITHIN_DEADLINE: number = 3;
167
+
124
168
  /*
125
169
  * Provider-level parameters allowed on protected, unattended completions.
126
170
  * This is deliberately a generation-only allowlist: capability fields such
@@ -196,10 +240,10 @@ export default class LLMService {
196
240
 
197
241
  /*
198
242
  * Remembers what a given endpoint actually accepted, so only the first
199
- * completion per provider pays for the discovery. API.post retries 4xx, so
200
- * a rejected request costs several full prompt uploads plus backoff
201
- * re-learning that on every call would be expensive on hot paths like the
202
- * AI agent loop.
243
+ * completion per provider pays for the discovery. A rejected request still
244
+ * costs a full prompt upload and round trip per adaptation re-learning
245
+ * that on every call would be expensive on hot paths like the AI agent
246
+ * loop.
203
247
  *
204
248
  * Keyed by provider + base URL + model, so editing any of them re-probes.
205
249
  * Entries also expire: what a deployment accepts is not fixed forever — the
@@ -650,6 +694,45 @@ export default class LLMService {
650
694
  return undefined;
651
695
  }
652
696
 
697
+ /**
698
+ * The retry policy every provider call runs under.
699
+ *
700
+ * Ten attempts, backed off and jittered, and deliberately spent only on
701
+ * failures a repeat could fix: a provider that rejects the request itself —
702
+ * bad key, unknown model, malformed body — answers the same way every time,
703
+ * and burning the ladder on it only hides the error the operator has to see.
704
+ */
705
+ private static buildRequestPolicy(data: {
706
+ request: LLMCompletionRequest;
707
+ defaultTimeoutInMs: number;
708
+ }): RequestOptions {
709
+ const timeoutInMs: number =
710
+ data.request.requestTimeoutInMs ?? data.defaultTimeoutInMs;
711
+
712
+ return {
713
+ retries: data.request.requestRetries ?? this.DEFAULT_REQUEST_ATTEMPTS - 1,
714
+ exponentialBackoff: true,
715
+ maxBackoffInMs: this.MAX_BACKOFF_IN_MS,
716
+ retryOnlyOnRetryableErrors: true,
717
+ totalTimeoutInMs: this.getRetryDeadlineInMs(data.request, timeoutInMs),
718
+ timeout: timeoutInMs,
719
+ };
720
+ }
721
+
722
+ private static getRetryDeadlineInMs(
723
+ request: LLMCompletionRequest,
724
+ timeoutInMs: number,
725
+ ): number {
726
+ if (request.requestRetryDeadlineInMs !== undefined) {
727
+ return request.requestRetryDeadlineInMs;
728
+ }
729
+
730
+ return Math.max(
731
+ this.DEFAULT_RETRY_DEADLINE_IN_MS,
732
+ timeoutInMs * this.MIN_FULL_TIMEOUT_ATTEMPTS_WITHIN_DEADLINE,
733
+ );
734
+ }
735
+
653
736
  /**
654
737
  * POST an OpenAI-style chat completion, reshaping and retrying when the
655
738
  * endpoint rejects a generation parameter it does not support.
@@ -720,11 +803,13 @@ export default class LLMService {
720
803
  * DNS round trips (and a rebind window between them).
721
804
  */
722
805
  const requestOptions: RequestOptions =
723
- await this.buildGuardedRequestOptions(data.requestUrl, {
724
- retries: data.request.requestRetries ?? 2,
725
- exponentialBackoff: true,
726
- timeout: data.request.requestTimeoutInMs ?? 120000,
727
- });
806
+ await this.buildGuardedRequestOptions(
807
+ data.requestUrl,
808
+ this.buildRequestPolicy({
809
+ request: data.request,
810
+ defaultTimeoutInMs: 120000,
811
+ }),
812
+ );
728
813
 
729
814
  const post: () => Promise<
730
815
  HTTPResponse<JSONObject> | HTTPErrorResponse
@@ -1310,11 +1395,13 @@ export default class LLMService {
1310
1395
  "anthropic-version": "2023-06-01",
1311
1396
  "Content-Type": "application/json",
1312
1397
  },
1313
- options: await this.buildGuardedRequestOptions(anthropicRequestUrl, {
1314
- retries: request.requestRetries ?? 2,
1315
- exponentialBackoff: true,
1316
- timeout: request.requestTimeoutInMs ?? 120000,
1317
- }),
1398
+ options: await this.buildGuardedRequestOptions(
1399
+ anthropicRequestUrl,
1400
+ this.buildRequestPolicy({
1401
+ request: request,
1402
+ defaultTimeoutInMs: 120000,
1403
+ }),
1404
+ ),
1318
1405
  });
1319
1406
 
1320
1407
  const anthropicLogAttributes: LogAttributes = {
@@ -1467,11 +1554,13 @@ export default class LLMService {
1467
1554
  headers: {
1468
1555
  "Content-Type": "application/json",
1469
1556
  },
1470
- options: await this.buildGuardedRequestOptions(ollamaRequestUrl, {
1471
- retries: request.requestRetries ?? 2,
1472
- exponentialBackoff: true,
1473
- timeout: request.requestTimeoutInMs ?? 300000, // Ollama may be slower
1474
- }),
1557
+ options: await this.buildGuardedRequestOptions(
1558
+ ollamaRequestUrl,
1559
+ this.buildRequestPolicy({
1560
+ request: request,
1561
+ defaultTimeoutInMs: 300000, // Ollama may be slower
1562
+ }),
1563
+ ),
1475
1564
  });
1476
1565
 
1477
1566
  const ollamaLogAttributes: LogAttributes = {
@@ -1,15 +1,9 @@
1
1
  import Alert from "../../../Models/DatabaseModels/Alert";
2
2
  import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
3
3
  import AlertStateTimeline from "../../../Models/DatabaseModels/AlertStateTimeline";
4
- import CephCluster from "../../../Models/DatabaseModels/CephCluster";
5
- import DockerSwarmCluster from "../../../Models/DatabaseModels/DockerSwarmCluster";
6
- import Host from "../../../Models/DatabaseModels/Host";
7
4
  import Label from "../../../Models/DatabaseModels/Label";
8
5
  import Monitor from "../../../Models/DatabaseModels/Monitor";
9
6
  import OnCallDutyPolicy from "../../../Models/DatabaseModels/OnCallDutyPolicy";
10
- import ProxmoxCluster from "../../../Models/DatabaseModels/ProxmoxCluster";
11
- import IoTFleet from "../../../Models/DatabaseModels/IoTFleet";
12
- import Service from "../../../Models/DatabaseModels/Service";
13
7
  import SortOrder from "../../../Types/BaseDatabase/SortOrder";
14
8
  import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
15
9
  import Dictionary from "../../../Types/Dictionary";
@@ -24,11 +18,9 @@ import AlertService from "../../Services/AlertService";
24
18
  import AlertSeverityService from "../../Services/AlertSeverityService";
25
19
  import ProjectScopedReferenceValidator from "../Database/ProjectScopedReferenceValidator";
26
20
  import AlertStateTimelineService from "../../Services/AlertStateTimelineService";
27
- import HostService from "../../Services/HostService";
28
21
  import NetworkDeviceOwnerUserService, {
29
22
  NetworkDeviceOwners,
30
23
  } from "../../Services/NetworkDeviceOwnerUserService";
31
- import ServiceService from "../../Services/ServiceService";
32
24
  import logger, { LogAttributes } from "../Logger";
33
25
  import CaptureSpan from "../Telemetry/CaptureSpan";
34
26
  import DataToProcess from "./DataToProcess";
@@ -36,6 +28,7 @@ import MonitorClusterContextUtil, {
36
28
  MonitorClusterContext,
37
29
  } from "./MonitorClusterContext";
38
30
  import MonitorTemplateUtil from "./MonitorTemplateUtil";
31
+ import SeriesResourceLinker from "./SeriesResourceLinker";
39
32
  import MonitorDependencySuppression, {
40
33
  DependencySuppressionResult,
41
34
  } from "./MonitorDependencySuppression";
@@ -553,118 +546,32 @@ export default class MonitorAlert {
553
546
  alert.seriesLabels = seriesLabels;
554
547
 
555
548
  /*
556
- * Link the alert to the Host that emitted this series, if
557
- * the metric carried a `host.name` resource attribute. The
558
- * Host record was auto-discovered during OTel ingestion.
559
- * Metric attributes are stored with the `resource.` prefix in
560
- * ClickHouse, so the group-by dropdown surfaces
561
- * `resource.host.name` — but accept the bare `host.name` too
562
- * for any non-OTel ingest paths.
549
+ * Attach every resource this series identifies host, docker
550
+ * host, podman host, k8s cluster, service, and the Proxmox /
551
+ * Ceph / Swarm / IoT clusters — resolved from the shared label
552
+ * key map. Same call the incident path makes, so the two can't
553
+ * drift apart again.
563
554
  */
564
- const hostName: string | undefined =
565
- typeof seriesLabels["resource.host.name"] === "string"
566
- ? (seriesLabels["resource.host.name"] as string)
567
- : typeof seriesLabels["host.name"] === "string"
568
- ? (seriesLabels["host.name"] as string)
569
- : undefined;
570
-
571
- if (hostName) {
572
- const host: Host | null = await HostService.findOneBy({
573
- query: {
574
- projectId: input.monitor.projectId!,
575
- hostIdentifier: hostName,
576
- },
577
- select: {
578
- _id: true,
579
- },
580
- props: {
581
- isRoot: true,
582
- },
583
- });
584
-
585
- if (host) {
586
- alert.hosts = [host];
587
- }
588
- }
589
-
590
- /*
591
- * Same idea for Service — OTel ingest stamps `service.name` and
592
- * auto-creates a Service row keyed by that name (see
593
- * OpenTelemetryIngestService.telemetryServiceFromName). When the
594
- * breaching series carries that attribute, link the alert to the
595
- * emitting service so the on-call/labels pipeline can fan out.
596
- */
597
- const serviceName: string | undefined =
598
- typeof seriesLabels["resource.service.name"] === "string"
599
- ? (seriesLabels["resource.service.name"] as string)
600
- : typeof seriesLabels["service.name"] === "string"
601
- ? (seriesLabels["service.name"] as string)
602
- : undefined;
603
-
604
- if (serviceName) {
605
- const service: Service | null = await ServiceService.findOneBy({
606
- query: {
607
- projectId: input.monitor.projectId!,
608
- name: serviceName,
609
- },
610
- select: {
611
- _id: true,
612
- },
613
- props: {
614
- isRoot: true,
615
- },
616
- });
617
-
618
- if (service) {
619
- alert.services = [service];
620
- }
621
- }
555
+ await SeriesResourceLinker.linkSeriesResourcesToModel({
556
+ model: alert,
557
+ seriesLabels,
558
+ projectId: input.monitor.projectId!,
559
+ });
622
560
  }
623
561
 
624
562
  /*
625
- * Deterministic Proxmox/Ceph cluster link from the monitor's
626
- * step config (resolved once above). Series labels never carry
627
- * cluster identity for these monitor types, so this not the
628
- * label path above is what makes the per-cluster Activity
629
- * tabs and badge counts see monitor-created alerts. Runs for
630
- * both grouped and ungrouped alerts.
563
+ * Deterministic Proxmox/Ceph/Swarm/IoT cluster link from the
564
+ * monitor's step config (resolved once above). The shipped
565
+ * templates for those monitor types group by datapoint labels
566
+ * that carry no cluster identity, so this — not the label path
567
+ * above is what makes the per-cluster Activity tabs and badge
568
+ * counts see monitor-created alerts. Runs for both grouped and
569
+ * ungrouped alerts, and merges with whatever the labels resolved.
631
570
  */
632
- if (clusterContext.proxmoxClusterIds.length > 0) {
633
- alert.proxmoxClusters = clusterContext.proxmoxClusterIds.map(
634
- (id: string): ProxmoxCluster => {
635
- const cluster: ProxmoxCluster = new ProxmoxCluster();
636
- cluster._id = id;
637
- return cluster;
638
- },
639
- );
640
- }
641
- if (clusterContext.cephClusterIds.length > 0) {
642
- alert.cephClusters = clusterContext.cephClusterIds.map(
643
- (id: string): CephCluster => {
644
- const cluster: CephCluster = new CephCluster();
645
- cluster._id = id;
646
- return cluster;
647
- },
648
- );
649
- }
650
- if (clusterContext.dockerSwarmClusterIds.length > 0) {
651
- alert.dockerSwarmClusters = clusterContext.dockerSwarmClusterIds.map(
652
- (id: string): DockerSwarmCluster => {
653
- const cluster: DockerSwarmCluster = new DockerSwarmCluster();
654
- cluster._id = id;
655
- return cluster;
656
- },
657
- );
658
- }
659
- if (clusterContext.iotFleetIds.length > 0) {
660
- alert.iotFleets = clusterContext.iotFleetIds.map(
661
- (id: string): IoTFleet => {
662
- const fleet: IoTFleet = new IoTFleet();
663
- fleet._id = id;
664
- return fleet;
665
- },
666
- );
667
- }
571
+ SeriesResourceLinker.attachClusterContext({
572
+ model: alert,
573
+ clusterContext,
574
+ });
668
575
 
669
576
  alert.onCallDutyPolicies =
670
577
  criteriaAlert.onCallPolicyIds?.map((id: ObjectID) => {