@oneuptime/common 11.7.1 → 11.7.3

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 (285) hide show
  1. package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
  2. package/Models/DatabaseModels/DashboardDomain.ts +1 -0
  3. package/Models/DatabaseModels/Incident.ts +2 -0
  4. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  5. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  7. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  8. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  9. package/Models/DatabaseModels/Monitor.ts +1 -0
  10. package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
  11. package/Models/DatabaseModels/NetworkSite.ts +1 -0
  12. package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
  13. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
  14. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
  15. package/Models/DatabaseModels/Probe.ts +13 -13
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  17. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  18. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  19. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
  20. package/Models/DatabaseModels/StatusPage.ts +1 -0
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  22. package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
  23. package/Models/DatabaseModels/TableView.ts +40 -0
  24. package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
  25. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
  26. package/Server/API/BaseAPI.ts +17 -1
  27. package/Server/API/CommonAPI.ts +32 -0
  28. package/Server/API/MicrosoftTeamsAPI.ts +99 -2
  29. package/Server/API/ProbeAPI.ts +2 -0
  30. package/Server/API/SlackAPI.ts +62 -0
  31. package/Server/API/StatusPageAPI.ts +157 -192
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  37. package/Server/Services/DatabaseService.ts +11 -3
  38. package/Server/Services/MonitorGroupService.ts +145 -0
  39. package/Server/Services/MonitorService.ts +231 -30
  40. package/Server/Services/ProbeService.ts +319 -16
  41. package/Server/Services/StatusPageService.ts +72 -42
  42. package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
  43. package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
  44. package/Server/Utils/Monitor/MonitorResource.ts +88 -35
  45. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
  46. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
  47. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
  48. package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
  49. package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
  50. package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
  51. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
  52. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
  53. package/Tests/Server/API/ProbeAPI.test.ts +6 -0
  54. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
  55. package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
  56. package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
  57. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
  58. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
  59. package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
  60. package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
  61. package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
  62. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
  63. package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
  64. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
  65. package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
  66. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
  67. package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
  68. package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
  69. package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
  70. package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
  71. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
  72. package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
  73. package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
  74. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
  75. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
  76. package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
  77. package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
  78. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
  79. package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
  80. package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
  81. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
  82. package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
  83. package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
  84. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
  85. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
  86. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
  87. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
  88. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
  89. package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
  90. package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
  91. package/Tests/Types/JSONFunctions.test.ts +103 -1
  92. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
  93. package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
  94. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
  95. package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
  96. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
  97. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
  98. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +572 -0
  99. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
  100. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
  101. package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
  102. package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
  103. package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
  104. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
  105. package/Tests/UI/Utils/Project.test.ts +809 -0
  106. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
  107. package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
  108. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
  109. package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
  110. package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
  111. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
  112. package/Tests/Utils/UserPreferences.test.ts +563 -0
  113. package/Types/JSONFunctions.ts +7 -2
  114. package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
  115. package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
  116. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
  117. package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
  118. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
  119. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  120. package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
  121. package/UI/Components/Forms/ModelForm.tsx +41 -18
  122. package/UI/Components/MasterPage/MasterPage.tsx +65 -0
  123. package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
  124. package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
  125. package/UI/Components/ModelTable/BaseModelTable.tsx +687 -34
  126. package/UI/Components/ModelTable/Column.ts +29 -0
  127. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +420 -0
  128. package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
  129. package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
  130. package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
  131. package/UI/Components/ModelTable/TableView.tsx +24 -2
  132. package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
  133. package/UI/Components/Navbar/NavBar.tsx +66 -1
  134. package/UI/Components/SideMenu/SideMenu.tsx +24 -4
  135. package/UI/Components/Table/Table.tsx +66 -14
  136. package/UI/Components/Table/TableRow.tsx +180 -175
  137. package/UI/Components/Table/Types/Column.ts +20 -0
  138. package/UI/Utils/Project.ts +214 -18
  139. package/UI/Utils/TableColumnsToCsv.ts +86 -6
  140. package/UI/esbuild-config.js +34 -0
  141. package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
  142. package/Utils/UserPreferences.ts +53 -0
  143. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
  144. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
  145. package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
  146. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  147. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  148. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  149. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  150. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  152. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  154. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  155. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  156. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  157. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  158. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  159. package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
  160. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  161. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
  162. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  163. package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
  164. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  165. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
  166. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
  167. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
  168. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  169. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
  170. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  171. package/build/dist/Models/DatabaseModels/Probe.js +13 -13
  172. package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
  173. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  174. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  176. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  178. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
  180. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
  182. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  184. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
  186. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/TableView.js +40 -0
  188. package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
  190. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  192. package/build/dist/Server/API/BaseAPI.js +12 -1
  193. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  194. package/build/dist/Server/API/CommonAPI.js +21 -0
  195. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  196. package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
  197. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  198. package/build/dist/Server/API/ProbeAPI.js +2 -0
  199. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  200. package/build/dist/Server/API/SlackAPI.js +44 -0
  201. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  202. package/build/dist/Server/API/StatusPageAPI.js +98 -152
  203. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  204. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
  205. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
  206. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
  207. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
  208. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
  209. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
  210. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
  211. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
  212. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  213. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  214. package/build/dist/Server/Services/DatabaseService.js +10 -2
  215. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  216. package/build/dist/Server/Services/MonitorGroupService.js +118 -0
  217. package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
  218. package/build/dist/Server/Services/MonitorService.js +198 -24
  219. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  220. package/build/dist/Server/Services/ProbeService.js +238 -9
  221. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  222. package/build/dist/Server/Services/StatusPageService.js +46 -39
  223. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  224. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
  225. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  226. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
  227. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
  228. package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
  229. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  230. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
  231. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  232. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
  233. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
  234. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
  235. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  236. package/build/dist/Types/JSONFunctions.js +8 -3
  237. package/build/dist/Types/JSONFunctions.js.map +1 -1
  238. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
  239. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
  240. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
  241. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
  242. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  243. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
  244. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  245. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
  246. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
  247. package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
  248. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  249. package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
  250. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  251. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
  252. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  253. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
  254. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  255. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +472 -27
  256. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  257. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +189 -0
  258. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
  259. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
  260. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
  261. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
  262. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
  263. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
  264. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
  265. package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
  266. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  267. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
  268. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
  269. package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
  270. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  271. package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
  272. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  273. package/build/dist/UI/Components/Table/Table.js +46 -13
  274. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  275. package/build/dist/UI/Components/Table/TableRow.js +11 -6
  276. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  277. package/build/dist/UI/Utils/Project.js +161 -15
  278. package/build/dist/UI/Utils/Project.js.map +1 -1
  279. package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
  280. package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
  281. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
  282. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
  283. package/build/dist/Utils/UserPreferences.js +33 -0
  284. package/build/dist/Utils/UserPreferences.js.map +1 -1
  285. package/package.json +4 -3
@@ -0,0 +1,563 @@
1
+ import UserPreferences, {
2
+ UserPreferenceType,
3
+ } from "../../Utils/UserPreferences";
4
+ import { JSONObject } from "../../Types/JSON";
5
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
6
+
7
+ /*
8
+ * UserPreferences is the only thing standing between a table's saved layout and
9
+ * real browser storage, so the two properties worth pinning are:
10
+ *
11
+ * 1. Namespacing. Every entry lands under `${UserPreferenceType}.${key}`. Two
12
+ * tables, and two kinds of preference on the same table, must never land in
13
+ * the same slot - a collision there means changing a page size silently
14
+ * wipes a column layout.
15
+ * 2. The read guard. localStorage is user-editable and can be left half
16
+ * written by a closing tab, so anything that comes back as the wrong type
17
+ * has to degrade to "no preference" instead of being handed to the caller,
18
+ * which would spread the bad value through the column-preference code.
19
+ *
20
+ * These run against the real jsdom localStorage rather than a mock, so the raw
21
+ * keys asserted below are exactly what ships in a browser. Jest runs with
22
+ * --runInBand and storage leaks between files, hence the clear() below.
23
+ */
24
+
25
+ const TABLE_KEY: string = "monitors-table";
26
+ const OTHER_TABLE_KEY: string = "incidents-table";
27
+
28
+ type ReadRawFn = (key: string) => string | null;
29
+
30
+ const readRaw: ReadRawFn = (key: string): string | null => {
31
+ return window.localStorage.getItem(key);
32
+ };
33
+
34
+ describe("UserPreferences", () => {
35
+ beforeEach(() => {
36
+ window.localStorage.clear();
37
+ });
38
+
39
+ afterEach(() => {
40
+ window.localStorage.clear();
41
+ });
42
+
43
+ describe("UserPreferenceType", () => {
44
+ /*
45
+ * These strings are baked into every user's localStorage. Renaming one does
46
+ * not migrate anything - it just makes every saved preference unreachable,
47
+ * so everyone's table silently resets to the default layout.
48
+ */
49
+ test("exposes the stable persisted key for column layouts", () => {
50
+ expect(UserPreferenceType.BaseModelTableColumns).toBe(
51
+ "BaseModelTableColumns",
52
+ );
53
+ });
54
+
55
+ test("keeps the pre-existing page size key unchanged", () => {
56
+ expect(UserPreferenceType.BaseModelTablePageSize).toBe(
57
+ "BaseModelTablePageSize",
58
+ );
59
+ });
60
+ });
61
+
62
+ describe("key namespacing", () => {
63
+ test("stores a number under `${UserPreferenceType}.${key}`", () => {
64
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
65
+ key: TABLE_KEY,
66
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
67
+ value: 25,
68
+ });
69
+
70
+ expect(readRaw("BaseModelTablePageSize.monitors-table")).toBe("25");
71
+ });
72
+
73
+ test("stores an object under `${UserPreferenceType}.${key}`", () => {
74
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
75
+ key: TABLE_KEY,
76
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
77
+ value: { order: ["name"], hidden: [] },
78
+ });
79
+
80
+ const raw: string | null = readRaw(
81
+ "BaseModelTableColumns.monitors-table",
82
+ );
83
+
84
+ expect(raw).not.toBeNull();
85
+ expect(JSON.parse(raw as string)).toEqual({
86
+ order: ["name"],
87
+ hidden: [],
88
+ });
89
+ });
90
+
91
+ // Two tables on the same page must not overwrite each other's layout.
92
+ test("two tables with different keys do not collide", () => {
93
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
94
+ key: TABLE_KEY,
95
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
96
+ value: { order: ["monitor-name"], hidden: ["monitor-status"] },
97
+ });
98
+
99
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
100
+ key: OTHER_TABLE_KEY,
101
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
102
+ value: { order: ["incident-title"], hidden: [] },
103
+ });
104
+
105
+ expect(readRaw("BaseModelTableColumns.monitors-table")).not.toBe(
106
+ readRaw("BaseModelTableColumns.incidents-table"),
107
+ );
108
+
109
+ expect(
110
+ UserPreferences.getUserPreferenceByTypeAsJSON({
111
+ key: TABLE_KEY,
112
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
113
+ }),
114
+ ).toEqual({ order: ["monitor-name"], hidden: ["monitor-status"] });
115
+
116
+ expect(
117
+ UserPreferences.getUserPreferenceByTypeAsJSON({
118
+ key: OTHER_TABLE_KEY,
119
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
120
+ }),
121
+ ).toEqual({ order: ["incident-title"], hidden: [] });
122
+ });
123
+
124
+ /*
125
+ * One table holds both a page size and a column layout under the same
126
+ * userPreferencesKey. The type prefix is the only thing keeping them apart.
127
+ */
128
+ test("two preference types on the same table key do not collide", () => {
129
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
130
+ key: TABLE_KEY,
131
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
132
+ value: 50,
133
+ });
134
+
135
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
136
+ key: TABLE_KEY,
137
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
138
+ value: { order: ["name"], hidden: ["description"] },
139
+ });
140
+
141
+ expect(readRaw("BaseModelTablePageSize.monitors-table")).toBe("50");
142
+ expect(readRaw("BaseModelTableColumns.monitors-table")).not.toBeNull();
143
+
144
+ expect(
145
+ UserPreferences.getUserPreferenceByTypeAsNumber({
146
+ key: TABLE_KEY,
147
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
148
+ }),
149
+ ).toBe(50);
150
+
151
+ expect(
152
+ UserPreferences.getUserPreferenceByTypeAsJSON({
153
+ key: TABLE_KEY,
154
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
155
+ }),
156
+ ).toEqual({ order: ["name"], hidden: ["description"] });
157
+ });
158
+
159
+ test("a value saved under one type is invisible to another type", () => {
160
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
161
+ key: TABLE_KEY,
162
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
163
+ value: 25,
164
+ });
165
+
166
+ expect(
167
+ UserPreferences.getUserPreferenceByTypeAsNumber({
168
+ key: TABLE_KEY,
169
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
170
+ }),
171
+ ).toBeNull();
172
+
173
+ expect(
174
+ UserPreferences.getUserPreferenceByTypeAsJSON({
175
+ key: TABLE_KEY,
176
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
177
+ }),
178
+ ).toBeNull();
179
+ });
180
+ });
181
+
182
+ describe("number preferences", () => {
183
+ test("saves and reads back a page size", () => {
184
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
185
+ key: TABLE_KEY,
186
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
187
+ value: 25,
188
+ });
189
+
190
+ expect(
191
+ UserPreferences.getUserPreferenceByTypeAsNumber({
192
+ key: TABLE_KEY,
193
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
194
+ }),
195
+ ).toBe(25);
196
+ });
197
+
198
+ // A number must not come back as the string localStorage actually holds.
199
+ test("reads back a number, not the stored string", () => {
200
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
201
+ key: TABLE_KEY,
202
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
203
+ value: 10,
204
+ });
205
+
206
+ expect(
207
+ typeof UserPreferences.getUserPreferenceByTypeAsNumber({
208
+ key: TABLE_KEY,
209
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
210
+ }),
211
+ ).toBe("number");
212
+ });
213
+
214
+ test("the newest value wins", () => {
215
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
216
+ key: TABLE_KEY,
217
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
218
+ value: 10,
219
+ });
220
+
221
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
222
+ key: TABLE_KEY,
223
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
224
+ value: 50,
225
+ });
226
+
227
+ expect(
228
+ UserPreferences.getUserPreferenceByTypeAsNumber({
229
+ key: TABLE_KEY,
230
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
231
+ }),
232
+ ).toBe(50);
233
+ });
234
+
235
+ test("returns null when nothing was ever saved", () => {
236
+ expect(
237
+ UserPreferences.getUserPreferenceByTypeAsNumber({
238
+ key: "never-rendered-table",
239
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
240
+ }),
241
+ ).toBeNull();
242
+ });
243
+ });
244
+
245
+ describe("JSON preferences", () => {
246
+ test("round trips a column preference", () => {
247
+ const preference: JSONObject = {
248
+ order: ["name", "currentMonitorStatus", "labels"],
249
+ hidden: ["description"],
250
+ };
251
+
252
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
253
+ key: TABLE_KEY,
254
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
255
+ value: preference,
256
+ });
257
+
258
+ expect(
259
+ UserPreferences.getUserPreferenceByTypeAsJSON({
260
+ key: TABLE_KEY,
261
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
262
+ }),
263
+ ).toEqual(preference);
264
+ });
265
+
266
+ /*
267
+ * The column preference is nothing but arrays of ids, and LocalStorage runs
268
+ * everything through a serializer on the way in and out - so the arrays,
269
+ * their order, and empty arrays all have to survive intact.
270
+ */
271
+ test("arrays keep their contents and their order", () => {
272
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
273
+ key: TABLE_KEY,
274
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
275
+ value: {
276
+ order: ["c", "a", "b"],
277
+ hidden: [],
278
+ },
279
+ });
280
+
281
+ const preference: JSONObject | null =
282
+ UserPreferences.getUserPreferenceByTypeAsJSON({
283
+ key: TABLE_KEY,
284
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
285
+ });
286
+
287
+ expect(preference?.["order"]).toEqual(["c", "a", "b"]);
288
+ expect(preference?.["hidden"]).toEqual([]);
289
+ });
290
+
291
+ test("arrays nested inside a nested object survive", () => {
292
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
293
+ key: TABLE_KEY,
294
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
295
+ value: {
296
+ columns: {
297
+ order: ["name", "customField.severity"],
298
+ hidden: ["createdAt"],
299
+ },
300
+ version: 1,
301
+ },
302
+ });
303
+
304
+ expect(
305
+ UserPreferences.getUserPreferenceByTypeAsJSON({
306
+ key: TABLE_KEY,
307
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
308
+ }),
309
+ ).toEqual({
310
+ columns: {
311
+ order: ["name", "customField.severity"],
312
+ hidden: ["createdAt"],
313
+ },
314
+ version: 1,
315
+ });
316
+ });
317
+
318
+ /*
319
+ * An empty object is a real, deliberately saved preference (the caller
320
+ * wrote it), not the absence of one - collapsing it to null would resurrect
321
+ * the default layout the user just cleared.
322
+ */
323
+ test("an empty object is a preference, not an absent one", () => {
324
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
325
+ key: TABLE_KEY,
326
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
327
+ value: {},
328
+ });
329
+
330
+ expect(
331
+ UserPreferences.getUserPreferenceByTypeAsJSON({
332
+ key: TABLE_KEY,
333
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
334
+ }),
335
+ ).toEqual({});
336
+ });
337
+
338
+ test("returns null when nothing was ever saved", () => {
339
+ expect(
340
+ UserPreferences.getUserPreferenceByTypeAsJSON({
341
+ key: "never-rendered-table",
342
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
343
+ }),
344
+ ).toBeNull();
345
+ });
346
+ });
347
+
348
+ /*
349
+ * Everything below writes straight into localStorage, which is what a
350
+ * hand-edited entry, an older build, or a tab that died mid-write leaves
351
+ * behind. None of it may reach the caller as anything but null: the column
352
+ * code expects an object and would happily read `.order` off a string.
353
+ */
354
+ describe("JSON preferences reject anything that is not an object", () => {
355
+ const columnsKey: string = "BaseModelTableColumns.monitors-table";
356
+
357
+ type ReadPreferenceFn = () => JSONObject | null;
358
+
359
+ const readPreference: ReadPreferenceFn = (): JSONObject | null => {
360
+ return UserPreferences.getUserPreferenceByTypeAsJSON({
361
+ key: TABLE_KEY,
362
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
363
+ });
364
+ };
365
+
366
+ test("a bare string that is not JSON at all", () => {
367
+ window.localStorage.setItem(columnsKey, "just-a-string");
368
+
369
+ expect(readPreference()).toBeNull();
370
+ });
371
+
372
+ test("a quoted string that parses cleanly into a string", () => {
373
+ window.localStorage.setItem(columnsKey, JSON.stringify("name,status"));
374
+
375
+ expect(readPreference()).toBeNull();
376
+ });
377
+
378
+ test("a number", () => {
379
+ // e.g. a build where this key used to hold a page size.
380
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
381
+ key: TABLE_KEY,
382
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
383
+ value: 25,
384
+ });
385
+
386
+ expect(readPreference()).toBeNull();
387
+ });
388
+
389
+ // An array of ids is not a preference object, even though it parses.
390
+ test("an array", () => {
391
+ window.localStorage.setItem(
392
+ columnsKey,
393
+ JSON.stringify(["name", "status"]),
394
+ );
395
+
396
+ expect(readPreference()).toBeNull();
397
+ });
398
+
399
+ test("malformed JSON left behind by a half-finished write", () => {
400
+ window.localStorage.setItem(columnsKey, '{"order": ["name",');
401
+
402
+ expect(readPreference()).toBeNull();
403
+ });
404
+
405
+ test("a stored null", () => {
406
+ window.localStorage.setItem(columnsKey, "null");
407
+
408
+ expect(readPreference()).toBeNull();
409
+ });
410
+
411
+ test("a bad entry never throws and never returns a string", () => {
412
+ window.localStorage.setItem(columnsKey, '{"order": ["name",');
413
+
414
+ const preference: JSONObject | null = readPreference();
415
+
416
+ expect(typeof preference).not.toBe("string");
417
+ expect(preference).toBeNull();
418
+ });
419
+
420
+ // A rejected entry must not poison the neighbouring page size.
421
+ test("a bad column entry leaves the page size readable", () => {
422
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
423
+ key: TABLE_KEY,
424
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
425
+ value: 50,
426
+ });
427
+
428
+ window.localStorage.setItem(columnsKey, "not json");
429
+
430
+ expect(readPreference()).toBeNull();
431
+ expect(
432
+ UserPreferences.getUserPreferenceByTypeAsNumber({
433
+ key: TABLE_KEY,
434
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
435
+ }),
436
+ ).toBe(50);
437
+ });
438
+ });
439
+
440
+ describe("removeUserPreferenceByType", () => {
441
+ test("removes the value it owns", () => {
442
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
443
+ key: TABLE_KEY,
444
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
445
+ value: { order: ["name"], hidden: [] },
446
+ });
447
+
448
+ UserPreferences.removeUserPreferenceByType({
449
+ key: TABLE_KEY,
450
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
451
+ });
452
+
453
+ expect(readRaw("BaseModelTableColumns.monitors-table")).toBeNull();
454
+ expect(
455
+ UserPreferences.getUserPreferenceByTypeAsJSON({
456
+ key: TABLE_KEY,
457
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
458
+ }),
459
+ ).toBeNull();
460
+ });
461
+
462
+ // "Reset columns" on one table must not reset every other table.
463
+ test("leaves the same preference type on another table alone", () => {
464
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
465
+ key: TABLE_KEY,
466
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
467
+ value: { order: ["name"], hidden: [] },
468
+ });
469
+
470
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
471
+ key: OTHER_TABLE_KEY,
472
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
473
+ value: { order: ["title"], hidden: [] },
474
+ });
475
+
476
+ UserPreferences.removeUserPreferenceByType({
477
+ key: TABLE_KEY,
478
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
479
+ });
480
+
481
+ expect(
482
+ UserPreferences.getUserPreferenceByTypeAsJSON({
483
+ key: OTHER_TABLE_KEY,
484
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
485
+ }),
486
+ ).toEqual({ order: ["title"], hidden: [] });
487
+ });
488
+
489
+ // ...nor the page size the same table stored under the same key.
490
+ test("leaves the other preference type on the same table alone", () => {
491
+ UserPreferences.saveUserPreferenceByTypeAsNumber({
492
+ key: TABLE_KEY,
493
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
494
+ value: 25,
495
+ });
496
+
497
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
498
+ key: TABLE_KEY,
499
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
500
+ value: { order: ["name"], hidden: [] },
501
+ });
502
+
503
+ UserPreferences.removeUserPreferenceByType({
504
+ key: TABLE_KEY,
505
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
506
+ });
507
+
508
+ expect(readRaw("BaseModelTableColumns.monitors-table")).toBeNull();
509
+ expect(
510
+ UserPreferences.getUserPreferenceByTypeAsNumber({
511
+ key: TABLE_KEY,
512
+ userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
513
+ }),
514
+ ).toBe(25);
515
+ });
516
+
517
+ test("removing something that was never saved is a no-op", () => {
518
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
519
+ key: TABLE_KEY,
520
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
521
+ value: { order: ["name"], hidden: [] },
522
+ });
523
+
524
+ UserPreferences.removeUserPreferenceByType({
525
+ key: "never-rendered-table",
526
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
527
+ });
528
+
529
+ expect(
530
+ UserPreferences.getUserPreferenceByTypeAsJSON({
531
+ key: TABLE_KEY,
532
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
533
+ }),
534
+ ).toEqual({ order: ["name"], hidden: [] });
535
+ });
536
+
537
+ test("a removed preference can be saved again", () => {
538
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
539
+ key: TABLE_KEY,
540
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
541
+ value: { order: ["name"], hidden: [] },
542
+ });
543
+
544
+ UserPreferences.removeUserPreferenceByType({
545
+ key: TABLE_KEY,
546
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
547
+ });
548
+
549
+ UserPreferences.saveUserPreferenceByTypeAsJSON({
550
+ key: TABLE_KEY,
551
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
552
+ value: { order: ["status"], hidden: ["name"] },
553
+ });
554
+
555
+ expect(
556
+ UserPreferences.getUserPreferenceByTypeAsJSON({
557
+ key: TABLE_KEY,
558
+ userPreferenceType: UserPreferenceType.BaseModelTableColumns,
559
+ }),
560
+ ).toEqual({ order: ["status"], hidden: ["name"] });
561
+ });
562
+ });
563
+ });
@@ -342,9 +342,12 @@ export default class JSONFunctions {
342
342
  ].fromJSON(val);
343
343
  } else if (val instanceof Date) {
344
344
  return val;
345
- } else if (typeof val === Typeof.Object) {
346
- return this.deserialize(val as JSONObject);
347
345
  } else if (Array.isArray(val)) {
346
+ /*
347
+ * This has to be checked before the plain-object branch below: arrays are
348
+ * typeof "object", so falling through to deserialize() would walk them by
349
+ * key and hand back { "0": ..., "1": ... } instead of an array.
350
+ */
348
351
  const arr: Array<JSONValue> = [];
349
352
 
350
353
  for (const v of val) {
@@ -352,6 +355,8 @@ export default class JSONFunctions {
352
355
  }
353
356
 
354
357
  return arr;
358
+ } else if (typeof val === Typeof.Object) {
359
+ return this.deserialize(val as JSONObject);
355
360
  }
356
361
 
357
362
  return val;
@@ -34,14 +34,30 @@ export interface KubernetesCostAllocationIngestRow {
34
34
  providerId?: string | undefined;
35
35
  labels?: Record<string, string> | undefined;
36
36
 
37
- // Usage / request measures.
37
+ // Usage / request / limit measures.
38
38
  cpuCoreHours?: number | undefined;
39
39
  cpuCoreRequestAverage?: number | undefined;
40
40
  cpuCoreUsageAverage?: number | undefined;
41
+ cpuCoreLimitAverage?: number | undefined;
41
42
  gpuHours?: number | undefined;
42
43
  ramByteHours?: number | undefined;
43
44
  ramBytesRequestAverage?: number | undefined;
44
45
  ramBytesUsageAverage?: number | undefined;
46
+ ramBytesLimitAverage?: number | undefined;
47
+
48
+ /*
49
+ * Peak working set over the window, sourced from Prometheus rather than
50
+ * the cost engine — the Allocation API only reports averages, and an
51
+ * hourly mean hides the burst that OOMKills a container, so a memory
52
+ * recommendation built on the average is actively dangerous.
53
+ *
54
+ * 0 when the agent has no Prometheus configured (external-engine installs
55
+ * have no bundled one), when the scrape had no data for the window, or
56
+ * when the agent predates this field. The server cannot tell those apart
57
+ * from a real 0, so recommendations must require a positive value.
58
+ */
59
+ ramBytesUsageMax?: number | undefined;
60
+
45
61
  pvByteHours?: number | undefined;
46
62
 
47
63
  // Pre-priced cost components (in `currency` of the payload).
@@ -69,6 +85,26 @@ export interface KubernetesCostIngestPayload {
69
85
  clusterName: string;
70
86
  /** Currency code of all cost figures (e.g. "USD"). Optional. */
71
87
  currency?: string | undefined;
88
+
89
+ /*
90
+ * Identity of one shipment — the agent's delivery of ONE window — and this
91
+ * request's position within it.
92
+ *
93
+ * A window wider than the agent's batch size arrives as several requests,
94
+ * each becoming its own ingest job. Without an identity the server cannot
95
+ * tell "another chunk of the shipment I have already started" (accept)
96
+ * from "a window an earlier shipment already delivered in full" (drop, or
97
+ * a restart double-counts spend), so it dropped both and lost every row
98
+ * past the first chunk on clusters above SHIP_BATCH_SIZE containers.
99
+ *
100
+ * shipmentId is a content hash over the window's row identities, so the
101
+ * same window re-shipped after a restart hashes the same and its chunks
102
+ * dedup individually. Both are absent on agents older than this contract;
103
+ * the server then falls back to the whole-window guard.
104
+ */
105
+ shipmentId?: string | undefined;
106
+ shipmentChunk?: number | undefined;
107
+
72
108
  allocations: Array<KubernetesCostAllocationIngestRow>;
73
109
  }
74
110
 
@@ -10,4 +10,8 @@ export default interface BaseNotificationRule {
10
10
  shouldPostToExistingChannel: boolean;
11
11
  existingChannelNames: string; // separate by comma
12
12
  existingTeam?: string; // team to post to (for MS Teams only)
13
+
14
+ // Post to group / personal chats (for MS Teams only).
15
+ shouldPostToExistingChat?: boolean;
16
+ existingChatIds?: Array<string>; // Teams chat ids the OneUptime app has been added to.
13
17
  }