@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
@@ -0,0 +1,250 @@
1
+ import { describe, expect, test } from "@jest/globals";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import SchemaMigrations from "../../../../Server/Infrastructure/Postgres/SchemaMigrations/Index";
5
+ import { AddDeviceRoleAndDeclaredLinkParent1787400000000 } from "../../../../Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent";
6
+
7
+ /*
8
+ * The migration behind #3192: NetworkDevice.deviceRole and
9
+ * NetworkDeviceLink.parentDeviceId.
10
+ *
11
+ * Two things are worth pinning about it, and neither is the SQL — the
12
+ * statements were generated rather than written, and the schema-drift job
13
+ * already proves they match the entities.
14
+ *
15
+ * The first is NULLABILITY, because it carries meaning here rather than
16
+ * just permissiveness. NULL is not "unset pending a backfill": on
17
+ * deviceRole it means "no override, let the classifier decide", and on
18
+ * parentDeviceId it means "peers, infer the direction". They are the
19
+ * values every existing row should hold, and a DEFAULT or a NOT NULL on
20
+ * either would silently restate every device and every link in every
21
+ * project as something an operator had chosen.
22
+ *
23
+ * The second is ORDERING. This is the newest registered migration, so it
24
+ * inherits the guard that used to live in
25
+ * EpisodeMemberNotifyIndexesMigration.test.ts: its timestamp must sort
26
+ * above every other. A migration generated with `generate-postgres-
27
+ * migration` carries a WALL-CLOCK timestamp, and the recent migrations in
28
+ * this repo use hand-picked round numbers that run ahead of it — so the
29
+ * generated file lands below several already-registered migrations unless
30
+ * somebody renumbers it. That is exactly what happened to this one.
31
+ * Whoever adds the next migration should move this test's ordering block
32
+ * to theirs.
33
+ */
34
+
35
+ const MIGRATION_PATH: string = path.join(
36
+ __dirname,
37
+ "../../../../Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts",
38
+ );
39
+
40
+ const SOURCE: string = fs.readFileSync(MIGRATION_PATH, "utf8");
41
+
42
+ interface MigrationClass {
43
+ name: string;
44
+ }
45
+
46
+ function timestampOf(migrationClass: MigrationClass): number | null {
47
+ const match: RegExpMatchArray | null = migrationClass.name.match(/(\d+)$/);
48
+ return match ? Number(match[1]) : null;
49
+ }
50
+
51
+ describe("the columns it adds", () => {
52
+ test("NetworkDevice gains deviceRole", () => {
53
+ expect(SOURCE).toContain(
54
+ `ALTER TABLE "NetworkDevice" ADD "deviceRole" character varying(100)`,
55
+ );
56
+ });
57
+
58
+ test("NetworkDeviceLink gains parentDeviceId", () => {
59
+ expect(SOURCE).toContain(
60
+ `ALTER TABLE "NetworkDeviceLink" ADD "parentDeviceId" uuid`,
61
+ );
62
+ });
63
+
64
+ /*
65
+ * See the header: NULL is a meaningful value on both, so neither may
66
+ * arrive with a DEFAULT or a NOT NULL.
67
+ */
68
+ test("neither column is given a default", () => {
69
+ const addStatements: Array<string> = [
70
+ ...SOURCE.matchAll(/`(ALTER TABLE [^`]*ADD "[^"]+"[^`]*)`/g),
71
+ ].map((match: RegExpMatchArray): string => {
72
+ return match[1] as string;
73
+ });
74
+
75
+ expect(addStatements.length).toBe(2);
76
+ for (const statement of addStatements) {
77
+ expect(statement).not.toContain("DEFAULT");
78
+ expect(statement).not.toContain("NOT NULL");
79
+ }
80
+ });
81
+
82
+ test("the parent is indexed and cascades with the device it names", () => {
83
+ expect(SOURCE).toContain(
84
+ `CREATE INDEX "IDX_01752cd47b773df38b4a54ae53" ON "NetworkDeviceLink" ("parentDeviceId")`,
85
+ );
86
+ expect(SOURCE).toContain(
87
+ `FOREIGN KEY ("parentDeviceId") REFERENCES "NetworkDevice"("_id") ON DELETE CASCADE`,
88
+ );
89
+ });
90
+
91
+ /*
92
+ * The parent is always one of the link's own two ends, and both of those
93
+ * already cascade — so this cascade only ever fires alongside one of
94
+ * them. Anything weaker would leave a link pointing at a deleted device.
95
+ */
96
+ test("it touches nothing beyond those two tables", () => {
97
+ const touchedTables: Set<string> = new Set<string>(
98
+ [...SOURCE.matchAll(/ALTER TABLE "(\w+)"/g)].map(
99
+ (match: RegExpMatchArray): string => {
100
+ return match[1] as string;
101
+ },
102
+ ),
103
+ );
104
+
105
+ expect([...touchedTables].sort()).toEqual([
106
+ "NetworkDevice",
107
+ "NetworkDeviceLink",
108
+ ]);
109
+ });
110
+ });
111
+
112
+ describe("up and down are symmetric", () => {
113
+ test("down drops everything up adds, and nothing else", () => {
114
+ const added: Array<string> = [...SOURCE.matchAll(/ADD "(\w+)"/g)].map(
115
+ (match: RegExpMatchArray): string => {
116
+ return match[1] as string;
117
+ },
118
+ );
119
+ const dropped: Array<string> = [
120
+ ...SOURCE.matchAll(/DROP COLUMN "(\w+)"/g),
121
+ ].map((match: RegExpMatchArray): string => {
122
+ return match[1] as string;
123
+ });
124
+
125
+ expect(added.sort()).toEqual(dropped.sort());
126
+ });
127
+
128
+ test("the index and the constraint are dropped too", () => {
129
+ expect(SOURCE).toContain(
130
+ `DROP INDEX "public"."IDX_01752cd47b773df38b4a54ae53"`,
131
+ );
132
+ expect(SOURCE).toContain(
133
+ `DROP CONSTRAINT "FK_01752cd47b773df38b4a54ae539"`,
134
+ );
135
+ });
136
+
137
+ /*
138
+ * A stray semicolon would split one queryRunner.query() into two.
139
+ *
140
+ * Trailing whitespace is tolerated on purpose: TypeORM's generator ends
141
+ * a CREATE INDEX with no WHERE clause in a single space, and this file
142
+ * is generated. Normalising it here would mean the checked-in SQL no
143
+ * longer matched what `npm run generate-postgres-migration` produces,
144
+ * and the next person to regenerate would see a spurious diff. Newlines
145
+ * are a different matter — one inside a statement means the template
146
+ * literal wrapped, and that is worth catching.
147
+ */
148
+ test("every statement is a complete, single statement", () => {
149
+ for (const statement of [
150
+ ...SOURCE.matchAll(/`((?:ALTER TABLE|CREATE INDEX|DROP INDEX)[^`]+)`/g),
151
+ ]) {
152
+ expect(statement[1]).not.toContain(";");
153
+ expect(statement[1]).not.toContain("\n");
154
+ expect(statement[1]!.trimStart()).toBe(statement[1]);
155
+ }
156
+ });
157
+ });
158
+
159
+ describe("the migration runs", () => {
160
+ test("it is registered", () => {
161
+ const index: string = fs.readFileSync(
162
+ path.join(path.dirname(MIGRATION_PATH), "Index.ts"),
163
+ "utf8",
164
+ );
165
+
166
+ expect(index).toContain(
167
+ 'from "./1787400000000-AddDeviceRoleAndDeclaredLinkParent"',
168
+ );
169
+ expect(index).toContain("AddDeviceRoleAndDeclaredLinkParent1787400000000,");
170
+ });
171
+
172
+ /*
173
+ * ...and registered for real, not just mentioned in the file: an import
174
+ * that never reaches the default-export array leaves the migration
175
+ * unregistered, and it silently never runs.
176
+ */
177
+ test("it is in the exported migration list", () => {
178
+ expect(SchemaMigrations).toContain(
179
+ AddDeviceRoleAndDeclaredLinkParent1787400000000,
180
+ );
181
+ });
182
+
183
+ /*
184
+ * TypeORM records applied migrations by the `name` property, not the file
185
+ * name. A mismatch re-runs the migration on every boot.
186
+ */
187
+ test("its declared name matches its class name", () => {
188
+ expect(new AddDeviceRoleAndDeclaredLinkParent1787400000000().name).toBe(
189
+ "AddDeviceRoleAndDeclaredLinkParent1787400000000",
190
+ );
191
+ });
192
+
193
+ // The guard described in the header. Hand it to the next migration.
194
+ test("its timestamp sorts after every other registered migration", () => {
195
+ const ourTimestamp: number | null = timestampOf(
196
+ AddDeviceRoleAndDeclaredLinkParent1787400000000,
197
+ );
198
+
199
+ expect(ourTimestamp).not.toBeNull();
200
+
201
+ const otherTimestamps: Array<number> = [];
202
+
203
+ for (const migrationClass of SchemaMigrations) {
204
+ if (migrationClass === AddDeviceRoleAndDeclaredLinkParent1787400000000) {
205
+ continue;
206
+ }
207
+
208
+ const timestamp: number | null = timestampOf(
209
+ migrationClass as MigrationClass,
210
+ );
211
+
212
+ if (timestamp !== null) {
213
+ otherTimestamps.push(timestamp);
214
+ }
215
+ }
216
+
217
+ /*
218
+ * Math.max() of an empty list is -Infinity, which every timestamp beats.
219
+ * Prove the list was actually enumerated before leaning on the maximum.
220
+ */
221
+ expect(otherTimestamps.length).toBeGreaterThan(100);
222
+
223
+ expect(ourTimestamp).toBeGreaterThan(Math.max(...otherTimestamps));
224
+ });
225
+
226
+ test("it is registered last, matching that timestamp", () => {
227
+ expect(SchemaMigrations[SchemaMigrations.length - 1]).toBe(
228
+ AddDeviceRoleAndDeclaredLinkParent1787400000000,
229
+ );
230
+ });
231
+
232
+ /*
233
+ * ...and the timestamp in the class name is the one on disk. A class
234
+ * renamed without renaming its file (or two migrations landing on the
235
+ * same timestamp) leaves the ordering above asserting something that is
236
+ * not what actually runs.
237
+ */
238
+ test("exactly one file on disk carries its timestamp, and it is this one", () => {
239
+ const directory: string = path.dirname(MIGRATION_PATH);
240
+ const matching: Array<string> = fs
241
+ .readdirSync(directory)
242
+ .filter((file: string): boolean => {
243
+ return file.startsWith("1787400000000-");
244
+ });
245
+
246
+ expect(matching).toEqual([
247
+ "1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts",
248
+ ]);
249
+ });
250
+ });
@@ -444,10 +444,31 @@ describe("the migration runs", () => {
444
444
  );
445
445
  });
446
446
 
447
- test("it is appended last, matching its timestamp", () => {
448
- expect(SchemaMigrations[SchemaMigrations.length - 1]).toBe(
447
+ /*
448
+ * Registered in timestamp order relative to whatever follows it.
449
+ *
450
+ * This asserted "it is last in the array" until a second migration was
451
+ * added behind it, which is a thing that happens to every migration
452
+ * exactly once and is not what the test was ever about. The invariant
453
+ * worth holding is the one the "matching its timestamp" half named:
454
+ * nothing registered AFTER this may carry an earlier timestamp, because
455
+ * the array is how a reader works out what runs when. A newly generated
456
+ * migration carries a wall-clock timestamp, which can easily fall below
457
+ * the hand-picked round numbers the recent ones use — appending it
458
+ * without renumbering is how the two orders drift apart.
459
+ */
460
+ test("nothing registered after it carries an earlier timestamp", () => {
461
+ const position: number = SchemaMigrations.indexOf(
449
462
  AddEpisodeMemberNotifyIndexes1787300000000,
450
463
  );
464
+ expect(position).toBeGreaterThan(-1);
465
+
466
+ for (const migration of SchemaMigrations.slice(position + 1)) {
467
+ const timestamp: number = Number(
468
+ (migration.name.match(/(\d+)$/) || [])[1],
469
+ );
470
+ expect(timestamp).toBeGreaterThan(1787300000000);
471
+ }
451
472
  });
452
473
 
453
474
  /*
@@ -461,34 +482,41 @@ describe("the migration runs", () => {
461
482
  });
462
483
 
463
484
  /*
464
- * Migrations execute in timestamp order, and TypeORM will not re-order an
465
- * already-applied set: a migration added with a timestamp below one that
466
- * has already run is simply skipped on every existing deployment, so the
467
- * index never appears in production and only ever shows up on fresh
468
- * installs. Both sides of the comparison are therefore read off the
469
- * registered classes themselves rename this migration to a lower
470
- * timestamp, or land another one above it, and this fails.
485
+ * Migrations execute in timestamp order, so one landing BELOW a migration
486
+ * that has already run applies out of order — and on a fresh install runs
487
+ * in a different order than it did in production, which is how a schema
488
+ * that passes drift locally still diverges between deployments.
489
+ *
490
+ * This used to read "above every OTHER registered migration", i.e. that
491
+ * this was the newest. That guard belongs to whichever migration is
492
+ * currently newest — see
493
+ * DeviceRoleAndDeclaredLinkParentMigration.test.ts, which now carries it
494
+ * — and it cannot live here, because it fails by design the moment
495
+ * anything lands above it. What is permanently true of this migration,
496
+ * and is the half that protects an existing deployment, is that nothing
497
+ * registered BEFORE it carries a later timestamp.
471
498
  */
472
- test("its timestamp sorts after every other registered migration", () => {
499
+ test("its timestamp sorts after every migration registered before it", () => {
473
500
  const ourTimestamp: number | null = timestampOf(
474
501
  AddEpisodeMemberNotifyIndexes1787300000000,
475
502
  );
476
503
 
477
504
  expect(ourTimestamp).not.toBeNull();
478
505
 
479
- const otherTimestamps: Array<number> = [];
506
+ const position: number = SchemaMigrations.indexOf(
507
+ AddEpisodeMemberNotifyIndexes1787300000000,
508
+ );
509
+ expect(position).toBeGreaterThan(-1);
480
510
 
481
- for (const migrationClass of SchemaMigrations) {
482
- if (migrationClass === AddEpisodeMemberNotifyIndexes1787300000000) {
483
- continue;
484
- }
511
+ const earlierTimestamps: Array<number> = [];
485
512
 
513
+ for (const migrationClass of SchemaMigrations.slice(0, position)) {
486
514
  const timestamp: number | null = timestampOf(
487
515
  migrationClass as MigrationClass,
488
516
  );
489
517
 
490
518
  if (timestamp !== null) {
491
- otherTimestamps.push(timestamp);
519
+ earlierTimestamps.push(timestamp);
492
520
  }
493
521
  }
494
522
 
@@ -497,9 +525,9 @@ describe("the migration runs", () => {
497
525
  * Prove the list was actually enumerated before leaning on the maximum,
498
526
  * or the assertion below is vacuous again.
499
527
  */
500
- expect(otherTimestamps.length).toBeGreaterThan(100);
528
+ expect(earlierTimestamps.length).toBeGreaterThan(100);
501
529
 
502
- expect(ourTimestamp).toBeGreaterThan(Math.max(...otherTimestamps));
530
+ expect(ourTimestamp).toBeGreaterThan(Math.max(...earlierTimestamps));
503
531
  });
504
532
 
505
533
  /*