@oneuptime/common 12.0.8 → 12.0.9

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 (769) hide show
  1. package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
  2. package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
  3. package/Models/DatabaseModels/AIConversation.ts +32 -0
  4. package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
  5. package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
  6. package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
  7. package/Models/DatabaseModels/Index.ts +12 -4
  8. package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +139 -9
  9. package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
  10. package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +8 -8
  11. package/Models/DatabaseModels/NetworkDevice.ts +141 -0
  12. package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
  13. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
  14. package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
  15. package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
  16. package/Models/DatabaseModels/Project.ts +78 -0
  17. package/Models/DatabaseModels/UserCall.ts +42 -0
  18. package/Models/DatabaseModels/UserEmail.ts +44 -0
  19. package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
  20. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
  21. package/Models/DatabaseModels/UserPush.ts +49 -0
  22. package/Models/DatabaseModels/UserSMS.ts +43 -0
  23. package/Models/DatabaseModels/UserTelegram.ts +59 -0
  24. package/Models/DatabaseModels/UserWebhook.ts +52 -0
  25. package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
  26. package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
  27. package/Server/API/AIChatAPI.ts +315 -1
  28. package/Server/API/DashboardAPI.ts +217 -1
  29. package/Server/API/OnCallReadinessAPI.ts +841 -0
  30. package/Server/API/TeamComplianceAPI.ts +69 -17
  31. package/Server/API/TelemetryAPI.ts +220 -12
  32. package/Server/EnvironmentConfig.ts +52 -0
  33. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
  47. package/Server/Infrastructure/Queue.ts +78 -13
  48. package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
  49. package/Server/Services/AIService.ts +7 -0
  50. package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
  51. package/Server/Services/AlertSeverityService.ts +63 -0
  52. package/Server/Services/DashboardService.ts +9 -10
  53. package/Server/Services/DatabaseService.ts +32 -2
  54. package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
  55. package/Server/Services/IncidentSeverityService.ts +76 -0
  56. package/Server/Services/Index.ts +12 -4
  57. package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
  58. package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +4 -4
  59. package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +89 -20
  60. package/Server/Services/LogAggregationService.ts +45 -8
  61. package/Server/Services/MetricAggregationService.ts +121 -0
  62. package/Server/Services/MetricService.ts +7 -7
  63. package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
  64. package/Server/Services/NetworkDeviceLinkService.ts +84 -0
  65. package/Server/Services/NetworkDeviceService.ts +140 -0
  66. package/Server/Services/NetworkSiteService.ts +77 -25
  67. package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
  69. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
  70. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
  71. package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
  72. package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
  73. package/Server/Services/OnCallReadinessService.ts +2803 -0
  74. package/Server/Services/OnCallSetupReminderService.ts +955 -0
  75. package/Server/Services/ProfileAggregationService.ts +123 -0
  76. package/Server/Services/StatusPageService.ts +9 -10
  77. package/Server/Services/TeamComplianceService.ts +429 -252
  78. package/Server/Services/UserCallService.ts +26 -1
  79. package/Server/Services/UserEmailService.ts +26 -1
  80. package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
  81. package/Server/Services/UserNotificationRuleService.ts +3812 -333
  82. package/Server/Services/UserOnCallLogService.ts +561 -48
  83. package/Server/Services/UserPushService.ts +29 -0
  84. package/Server/Services/UserService.ts +11 -0
  85. package/Server/Services/UserSmsService.ts +26 -1
  86. package/Server/Services/UserTelegramService.ts +24 -1
  87. package/Server/Services/UserWebhookService.ts +28 -1
  88. package/Server/Services/UserWhatsAppService.ts +24 -1
  89. package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
  90. package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
  91. package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
  92. package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
  93. package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
  94. package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
  95. package/Server/Types/Workflow/Components/API/Get.ts +1 -1
  96. package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
  97. package/Server/Types/Workflow/Components/API/Post.ts +1 -1
  98. package/Server/Types/Workflow/Components/API/Put.ts +1 -1
  99. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
  100. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
  101. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
  102. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
  103. package/Server/Types/Workflow/Components/Email.ts +25 -7
  104. package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
  105. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
  106. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
  107. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
  108. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
  109. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
  110. package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
  111. package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
  112. package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
  113. package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
  114. package/Server/Utils/AI/Toolbox/Index.ts +48 -0
  115. package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
  116. package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
  117. package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
  118. package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
  119. package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
  120. package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
  121. package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
  122. package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
  123. package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
  124. package/Server/Utils/ClientIp.ts +221 -0
  125. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
  126. package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
  127. package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
  128. package/Server/Utils/Express.ts +12 -17
  129. package/Server/Utils/LLM/LLMService.ts +85 -8
  130. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
  131. package/Server/Utils/SSRFProtection.ts +98 -23
  132. package/Server/Utils/StartServer.ts +12 -3
  133. package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
  134. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +25 -25
  135. package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
  136. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
  137. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
  138. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
  139. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
  140. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
  141. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
  142. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
  143. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
  144. package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
  145. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
  146. package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
  147. package/Tests/Models/InventoryItemModel.test.ts +174 -0
  148. package/Tests/Models/InventoryItemNaming.test.ts +302 -0
  149. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
  150. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
  151. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
  152. package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
  153. package/Tests/Server/API/Helpers.ts +24 -15
  154. package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
  155. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
  156. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
  157. package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
  158. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
  159. package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
  160. package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
  161. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
  162. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
  163. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
  164. package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
  165. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
  166. package/Tests/Server/Services/{TelemetryEntityManualCreate.test.ts → InventoryItemManualCreate.test.ts} +27 -27
  167. package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
  168. package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
  169. package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
  170. package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
  171. package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
  172. package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
  173. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
  174. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
  175. package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
  176. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
  177. package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
  178. package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
  179. package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
  180. package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
  181. package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
  182. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
  183. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
  184. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
  185. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
  186. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
  187. package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
  188. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
  189. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
  190. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
  191. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
  192. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
  193. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
  194. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
  195. package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
  196. package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
  197. package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
  198. package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
  199. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
  200. package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
  201. package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
  202. package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
  203. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
  204. package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
  205. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
  206. package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
  207. package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
  208. package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
  209. package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
  210. package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
  211. package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
  212. package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
  213. package/Tests/Server/Utils/ClientIp.test.ts +438 -0
  214. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
  215. package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
  216. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
  217. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
  218. package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
  219. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
  220. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
  221. package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
  222. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
  223. package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
  224. package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
  225. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +15 -15
  226. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
  227. package/Tests/Types/IP/IP.test.ts +263 -0
  228. package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
  229. package/Tests/Types/IP/IPv6.test.ts +12 -1
  230. package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
  231. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
  232. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
  233. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
  234. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
  235. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
  236. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
  237. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +6 -6
  238. package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
  239. package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
  240. package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
  241. package/Tests/Types/Workflow/TemplateSyntax.test.ts +112 -0
  242. package/Tests/Types/Workflow/Templates.test.ts +916 -101
  243. package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
  244. package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
  245. package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
  246. package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
  247. package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
  248. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
  249. package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
  250. package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
  251. package/Tests/UI/Components/Workflow/GraphLint.test.ts +72 -0
  252. package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
  253. package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +242 -7
  254. package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +15 -7
  255. package/Tests/UI/Components/Workflow/ModelSchema.test.ts +242 -28
  256. package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +59 -1
  257. package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
  258. package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
  259. package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
  260. package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
  261. package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
  262. package/Tests/UI/EsbuildConfig.test.ts +607 -0
  263. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  264. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
  265. package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
  266. package/Tests/UI/Utils/Platform.test.ts +147 -0
  267. package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
  268. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
  269. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +487 -0
  270. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
  271. package/Tests/__mocks__/bullmq.js +55 -0
  272. package/Types/AI/AIChatMessageStatus.ts +8 -1
  273. package/Types/AI/AIChatTypes.ts +12 -0
  274. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
  275. package/Types/Exception/ExceptionCode.ts +2 -0
  276. package/Types/Exception/ServiceUnavailableException.ts +8 -0
  277. package/Types/Exception/TooManyRequestsException.ts +8 -0
  278. package/Types/IP/IP.ts +93 -47
  279. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +49 -3
  280. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
  281. package/Types/OnCallDutyPolicy/Layer.ts +203 -149
  282. package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
  283. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
  284. package/Types/Permission.ts +193 -0
  285. package/Types/Telemetry/EntityRelationshipType.ts +1 -1
  286. package/Types/Telemetry/EntitySource.ts +1 -1
  287. package/Types/Telemetry/EntityType.ts +1 -1
  288. package/Types/Telemetry/EntityTypeGroups.ts +1 -1
  289. package/Types/Workflow/Components/BaseModel.ts +75 -29
  290. package/Types/Workflow/Components/Discord.ts +1 -0
  291. package/Types/Workflow/Components/Email.ts +12 -3
  292. package/Types/Workflow/Components/JavaScript.ts +7 -0
  293. package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
  294. package/Types/Workflow/Components/Slack.ts +1 -0
  295. package/Types/Workflow/Components/Telegram.ts +1 -0
  296. package/Types/Workflow/TemplateSyntax.ts +44 -0
  297. package/Types/Workflow/Templates.ts +2097 -45
  298. package/UI/Components/Calendar/Calendar.css +43 -0
  299. package/UI/Components/Calendar/Calendar.tsx +8 -0
  300. package/UI/Components/Card/Card.tsx +2 -2
  301. package/UI/Components/Checkbox/Checkbox.tsx +16 -0
  302. package/UI/Components/Dictionary/Dictionary.tsx +48 -9
  303. package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
  304. package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
  305. package/UI/Components/Input/Input.tsx +1 -0
  306. package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
  307. package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
  308. package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
  309. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
  310. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
  311. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
  312. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
  313. package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
  314. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
  315. package/UI/Components/LogsViewer/types.ts +23 -0
  316. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
  317. package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
  318. package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
  319. package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
  320. package/UI/Components/Table/Table.tsx +49 -16
  321. package/UI/Components/Table/TableBody.tsx +53 -28
  322. package/UI/Components/Table/TableHeader.tsx +13 -0
  323. package/UI/Components/Table/TableRow.tsx +58 -26
  324. package/UI/Components/Workflow/ArgumentsForm.tsx +155 -9
  325. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
  326. package/UI/Components/Workflow/ComponentSettingsModal.tsx +10 -1
  327. package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
  328. package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
  329. package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
  330. package/UI/Components/Workflow/GraphLint.ts +42 -5
  331. package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
  332. package/UI/Components/Workflow/ModelColumnEditor.tsx +154 -22
  333. package/UI/Components/Workflow/ModelSchema.ts +115 -33
  334. package/UI/Components/Workflow/RunStatusWatcher.ts +1 -1
  335. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  336. package/UI/Components/Workflow/UseRunWatch.ts +212 -0
  337. package/UI/Components/Workflow/VariableModal.tsx +6 -2
  338. package/UI/Components/Workflow/Workflow.tsx +49 -3
  339. package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
  340. package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
  341. package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
  342. package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
  343. package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
  344. package/UI/Utils/Platform.ts +149 -0
  345. package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
  346. package/UI/esbuild-config.js +22 -1
  347. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
  348. package/Utils/Monitor/NetworkTopologyUtil.ts +888 -164
  349. package/Utils/Telemetry/CrossSignalScope.ts +502 -0
  350. package/Utils/Telemetry/EntityKey.ts +5 -5
  351. package/Utils/Telemetry/EntityRelationship.ts +1 -1
  352. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
  353. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
  354. package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
  355. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  356. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
  357. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
  358. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
  359. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  360. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
  361. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  362. package/build/dist/Models/DatabaseModels/Index.js +12 -4
  363. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  364. package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +167 -31
  365. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
  366. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
  367. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
  368. package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +27 -27
  369. package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
  370. package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
  371. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  372. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
  373. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
  374. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
  375. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
  376. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
  377. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
  378. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
  379. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
  380. package/build/dist/Models/DatabaseModels/Project.js +80 -0
  381. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  382. package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
  383. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  384. package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
  385. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  386. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
  387. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  388. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
  389. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  390. package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
  391. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
  393. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
  395. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
  397. package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
  398. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
  399. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  400. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
  401. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  402. package/build/dist/Server/API/AIChatAPI.js +237 -1
  403. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  404. package/build/dist/Server/API/DashboardAPI.js +165 -13
  405. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  406. package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
  407. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
  408. package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
  409. package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
  410. package/build/dist/Server/API/TelemetryAPI.js +129 -18
  411. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  412. package/build/dist/Server/EnvironmentConfig.js +45 -0
  413. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  414. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
  415. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  416. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
  417. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
  418. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
  419. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
  420. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
  421. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
  422. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
  423. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
  424. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
  425. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
  426. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
  427. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
  428. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
  429. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
  430. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
  431. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
  432. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
  433. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
  434. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
  435. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
  436. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
  437. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
  438. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  439. package/build/dist/Server/Infrastructure/Queue.js +72 -13
  440. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  441. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
  442. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
  443. package/build/dist/Server/Services/AIService.js +1 -0
  444. package/build/dist/Server/Services/AIService.js.map +1 -1
  445. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
  446. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
  447. package/build/dist/Server/Services/AlertSeverityService.js +54 -0
  448. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  449. package/build/dist/Server/Services/DashboardService.js +10 -9
  450. package/build/dist/Server/Services/DashboardService.js.map +1 -1
  451. package/build/dist/Server/Services/DatabaseService.js +24 -2
  452. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  453. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
  454. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  455. package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
  456. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  457. package/build/dist/Server/Services/Index.js +12 -4
  458. package/build/dist/Server/Services/Index.js.map +1 -1
  459. package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
  460. package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
  461. package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +6 -6
  462. package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
  463. package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +68 -23
  464. package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
  465. package/build/dist/Server/Services/LogAggregationService.js +27 -8
  466. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  467. package/build/dist/Server/Services/MetricAggregationService.js +80 -0
  468. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  469. package/build/dist/Server/Services/MetricService.js +6 -6
  470. package/build/dist/Server/Services/MetricService.js.map +1 -1
  471. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
  472. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
  473. package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
  474. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
  475. package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
  476. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  477. package/build/dist/Server/Services/NetworkSiteService.js +53 -13
  478. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  479. package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
  480. package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
  481. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
  482. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  483. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
  484. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  485. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
  486. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  487. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
  488. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  489. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
  490. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
  491. package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
  492. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
  493. package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
  494. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
  495. package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
  496. package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
  497. package/build/dist/Server/Services/StatusPageService.js +11 -10
  498. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  499. package/build/dist/Server/Services/TeamComplianceService.js +312 -160
  500. package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
  501. package/build/dist/Server/Services/UserCallService.js +24 -1
  502. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  503. package/build/dist/Server/Services/UserEmailService.js +24 -1
  504. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  505. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
  506. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
  507. package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
  508. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  509. package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
  510. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  511. package/build/dist/Server/Services/UserPushService.js +26 -0
  512. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  513. package/build/dist/Server/Services/UserService.js +10 -0
  514. package/build/dist/Server/Services/UserService.js.map +1 -1
  515. package/build/dist/Server/Services/UserSmsService.js +24 -1
  516. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  517. package/build/dist/Server/Services/UserTelegramService.js +22 -1
  518. package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
  519. package/build/dist/Server/Services/UserWebhookService.js +25 -1
  520. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  521. package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
  522. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  523. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
  524. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  525. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
  526. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  527. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
  528. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
  529. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
  530. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
  531. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
  532. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  533. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
  534. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  535. package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
  536. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  537. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
  538. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  539. package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
  540. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  541. package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
  542. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  543. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
  544. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  545. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
  546. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  547. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
  548. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
  549. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
  550. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  551. package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
  552. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  553. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
  554. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  555. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
  556. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  557. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
  558. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  559. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
  560. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  561. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
  562. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  563. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
  564. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  565. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
  566. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  567. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
  568. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
  569. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
  570. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  571. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
  572. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
  573. package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
  574. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  575. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
  576. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
  577. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
  578. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
  579. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
  580. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
  581. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
  582. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
  583. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
  584. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
  585. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
  586. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
  587. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
  588. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
  589. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
  590. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
  591. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
  592. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
  593. package/build/dist/Server/Utils/ClientIp.js +137 -0
  594. package/build/dist/Server/Utils/ClientIp.js.map +1 -0
  595. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
  596. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  597. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
  598. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
  599. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
  600. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
  601. package/build/dist/Server/Utils/Express.js +12 -12
  602. package/build/dist/Server/Utils/Express.js.map +1 -1
  603. package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
  604. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  605. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
  606. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  607. package/build/dist/Server/Utils/SSRFProtection.js +82 -21
  608. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  609. package/build/dist/Server/Utils/StartServer.js +12 -4
  610. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  611. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
  612. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  613. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +11 -11
  614. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  615. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
  616. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  617. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
  618. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  619. package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
  620. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
  621. package/build/dist/Types/AI/AIChatTypes.js +12 -0
  622. package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
  623. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
  624. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
  625. package/build/dist/Types/Exception/ExceptionCode.js +2 -0
  626. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  627. package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
  628. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
  629. package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
  630. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
  631. package/build/dist/Types/IP/IP.js +87 -43
  632. package/build/dist/Types/IP/IP.js.map +1 -1
  633. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
  634. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
  635. package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
  636. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  637. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
  638. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
  639. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
  640. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  641. package/build/dist/Types/Permission.js +174 -0
  642. package/build/dist/Types/Permission.js.map +1 -1
  643. package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
  644. package/build/dist/Types/Telemetry/EntitySource.js +1 -1
  645. package/build/dist/Types/Telemetry/EntityType.js +1 -1
  646. package/build/dist/Types/Telemetry/EntityTypeGroups.js +1 -1
  647. package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -1
  648. package/build/dist/Types/Workflow/Components/BaseModel.js +66 -28
  649. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  650. package/build/dist/Types/Workflow/Components/Discord.js +1 -0
  651. package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
  652. package/build/dist/Types/Workflow/Components/Email.js +12 -3
  653. package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
  654. package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
  655. package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
  656. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
  657. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
  658. package/build/dist/Types/Workflow/Components/Slack.js +1 -0
  659. package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
  660. package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
  661. package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
  662. package/build/dist/Types/Workflow/TemplateSyntax.js +12 -0
  663. package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -1
  664. package/build/dist/Types/Workflow/Templates.js +1931 -38
  665. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  666. package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
  667. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  668. package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
  669. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  670. package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -11
  671. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  672. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  673. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
  674. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  675. package/build/dist/UI/Components/Input/Input.js +1 -0
  676. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  677. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
  678. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
  679. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
  680. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
  681. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
  682. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  683. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
  684. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  685. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
  686. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  687. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
  688. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  689. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
  690. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  691. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
  692. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  693. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
  694. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  695. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  696. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
  697. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  698. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
  699. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  700. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
  701. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
  702. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
  703. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
  704. package/build/dist/UI/Components/Table/Table.js +27 -15
  705. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  706. package/build/dist/UI/Components/Table/TableBody.js +24 -18
  707. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  708. package/build/dist/UI/Components/Table/TableHeader.js +9 -1
  709. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  710. package/build/dist/UI/Components/Table/TableRow.js +29 -21
  711. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  712. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +130 -12
  713. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  714. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
  715. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  716. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +4 -4
  717. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  718. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
  719. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
  720. package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
  721. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  722. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
  723. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  724. package/build/dist/UI/Components/Workflow/GraphLint.js +33 -4
  725. package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -1
  726. package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
  727. package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
  728. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +116 -21
  729. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
  730. package/build/dist/UI/Components/Workflow/ModelSchema.js +72 -34
  731. package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -1
  732. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +1 -1
  733. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -1
  734. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  735. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  736. package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
  737. package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
  738. package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
  739. package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
  740. package/build/dist/UI/Components/Workflow/Workflow.js +41 -1
  741. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  742. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
  743. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
  744. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
  745. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
  746. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
  747. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
  748. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
  749. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
  750. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
  751. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  752. package/build/dist/UI/Utils/Platform.js +118 -0
  753. package/build/dist/UI/Utils/Platform.js.map +1 -0
  754. package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
  755. package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
  756. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
  757. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
  758. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +642 -136
  759. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  760. package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
  761. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
  762. package/build/dist/Utils/Telemetry/EntityKey.js +5 -5
  763. package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
  764. package/jest.config.json +1 -0
  765. package/package.json +1 -1
  766. package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
  767. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
  768. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
  769. package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
@@ -0,0 +1,1119 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import { cleanup, render, waitFor } from "@testing-library/react";
11
+ import React, { FunctionComponent, ReactElement } from "react";
12
+ import getJestMockFunction, { MockFunction } from "../../MockType";
13
+
14
+ /*
15
+ * The four on-call rule pages - incident, alert, incident episode, alert
16
+ * episode - were four ~370-line copies of one another and are now four sets of
17
+ * props on one shared component. Everything a copy-paste page got wrong loudly
18
+ * (a compile error, a missing import) a props object gets wrong silently, and
19
+ * two of those silent failures page real people at the wrong time:
20
+ *
21
+ * - the severity FOREIGN KEY COLUMN. A rule is tied to its severity band by
22
+ * `incidentSeverityId` or `alertSeverityId`. Write one and filter on the
23
+ * other and nothing throws: the table lists rules for EVERY severity, so a
24
+ * Sev 4 pages exactly like a Sev 1, and rules created from that table are
25
+ * stamped with a column nobody reads back.
26
+ *
27
+ * - the severity MODEL. It does NOT follow from the rule type the way the
28
+ * names suggest. The alert episode page enumerates AlertSeverity; the
29
+ * incident episode page enumerates IncidentSeverity. Anything that derives
30
+ * one of the two axes from the other gets one of those pages wrong.
31
+ *
32
+ * The third pin here is the stored-preferences key. It has to carry the
33
+ * severity id because one table is rendered per severity on a single route;
34
+ * drop the id and paging one table repaginates every other table on the page.
35
+ * That is a pure-UX bug, invisible to the type checker, and it was already
36
+ * explained in a comment in the original source - which is exactly why it is
37
+ * asserted rather than trusted to survive the next edit.
38
+ *
39
+ * The fourth is where the "Notification Method" dropdown comes from, which is
40
+ * now two different places depending on whose rules are on screen. For the
41
+ * signed-in user it is still seven reads of the seven method models, and that
42
+ * path is the four settings pages every existing user relies on. For anybody
43
+ * else those reads are REFUSED - the models are scoped to the person who owns
44
+ * the device, because the columns behind them are the raw phone number, the
45
+ * webhook bearer url, the push device token, the telegram chat id and the
46
+ * verification code - so the caller hands the list over instead, masked. Both
47
+ * halves are asserted here: that the self-serve path still reads what it always
48
+ * read, and that pointing the table at somebody else reads nothing at all.
49
+ */
50
+
51
+ const PROJECT_ID_STRING: string = "00000000-0000-4000-8000-0000000000a1";
52
+ const CURRENT_USER_ID_STRING: string = "00000000-0000-4000-8000-0000000000b1";
53
+ const OTHER_USER_ID_STRING: string = "00000000-0000-4000-8000-0000000000b2";
54
+ const SEVERITY_ONE_ID: string = "00000000-0000-4000-8000-0000000000c1";
55
+ const SEVERITY_TWO_ID: string = "00000000-0000-4000-8000-0000000000c2";
56
+
57
+ /*
58
+ * The supplied methods. Each carries the id of its own row - which is what a
59
+ * rule's userSmsId / userEmailId actually reference - alongside a mask in
60
+ * exactly the shape OnCallReadinessService.maskIdentifier emits, and a raw value
61
+ * that must never appear in a dropdown label.
62
+ */
63
+ const SUPPLIED_SMS_ID: string = "00000000-0000-4000-8000-0000000000d1";
64
+ const SUPPLIED_EMAIL_ID: string = "00000000-0000-4000-8000-0000000000d2";
65
+ const SUPPLIED_TELEGRAM_ID: string = "00000000-0000-4000-8000-0000000000d3";
66
+ const UNKNOWN_METHOD_ID: string = "00000000-0000-4000-8000-0000000000d9";
67
+
68
+ const MASKED_SMS: string = "+1 ••• ••• 4821";
69
+ const MASKED_EMAIL: string = "j•••@example.com";
70
+ const MASKED_TELEGRAM: string = "@ja•••";
71
+
72
+ const RAW_PHONE: string = "+15551234821";
73
+
74
+ /*
75
+ * Restated rather than imported, so that changing the wording in the component
76
+ * fails here. This string is what distinguishes "deliberately never page me for
77
+ * this" from a row that merely looks broken, and the whole risk it guards
78
+ * against is somebody deleting the row because the cell was blank.
79
+ */
80
+ const OPT_OUT_LABEL: string = "Muted - notifications turned off for this rule";
81
+
82
+ /* The signed-in user's own email, which their own page IS allowed to read. */
83
+ const OWN_EMAIL_ID: string = "00000000-0000-4000-8000-0000000000e1";
84
+ const OWN_EMAIL_ADDRESS: string = "self.serve@example.com";
85
+
86
+ const getListMock: MockFunction = getJestMockFunction();
87
+
88
+ /*
89
+ * The arrow wrappers are load bearing: jest.mock is hoisted above the compiled
90
+ * requires, so the consts above are still in their temporal dead zone when the
91
+ * factory body runs. Dereferencing them lazily, at call time, is what works.
92
+ */
93
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
94
+ return {
95
+ __esModule: true,
96
+ default: {
97
+ getList: (...args: Array<any>) => {
98
+ return getListMock(...args);
99
+ },
100
+ },
101
+ };
102
+ });
103
+
104
+ jest.mock("../../../UI/Utils/User", () => {
105
+ return {
106
+ __esModule: true,
107
+ default: {
108
+ getUserId: () => {
109
+ return currentUserId;
110
+ },
111
+ /*
112
+ * The delete guard asks whether the viewer is a master admin before it
113
+ * offers a Delete control at all. A stub missing this method does not
114
+ * make the guard fail an assertion - it throws
115
+ * "isMasterAdmin is not a function" at render, taking every test in the
116
+ * file with it, which reads as 33 unrelated failures rather than one
117
+ * missing stub.
118
+ */
119
+ isMasterAdmin: () => {
120
+ return false;
121
+ },
122
+ },
123
+ };
124
+ });
125
+
126
+ /*
127
+ * A stand-in ModelTable that records the props it was handed. Rendering the
128
+ * real one would drag in the pager, the facet bar and the URL state, none of
129
+ * which this file is about - what matters is the query the table is given, the
130
+ * row it builds on create, and the key it stores preferences under.
131
+ */
132
+ interface CapturedFormField {
133
+ overrideFieldKey?: string | undefined;
134
+ dropdownOptions?: Array<{ label: string; value: string }> | undefined;
135
+ }
136
+
137
+ /*
138
+ * A declared column. `field` is captured because ModelTable folds it into the
139
+ * projection it sends alongside `selectMoreFields` - so a column is a REQUEST,
140
+ * not merely a renderer, and asserting only on selectMoreFields would miss half
141
+ * of what goes on the wire.
142
+ */
143
+ interface CapturedColumn {
144
+ title: string;
145
+ field: Record<string, unknown>;
146
+ getElement?: ((item: UserNotificationRule) => ReactElement) | undefined;
147
+ }
148
+
149
+ interface CapturedTableProps {
150
+ userPreferencesKey: string;
151
+ query: Record<string, ObjectID | NotificationRuleType | undefined>;
152
+ onBeforeCreate: (
153
+ model: UserNotificationRule,
154
+ miscDataProps: Record<string, unknown>,
155
+ ) => Promise<UserNotificationRule>;
156
+ isCreateable: boolean;
157
+ isDeleteable: boolean;
158
+ isEditable: boolean;
159
+ formFields: Array<CapturedFormField>;
160
+ selectMoreFields: Record<string, unknown>;
161
+ columns: Array<CapturedColumn>;
162
+ cardProps: { title: string; description: string };
163
+ }
164
+
165
+ let capturedTables: Array<CapturedTableProps> = [];
166
+
167
+ jest.mock("../../../UI/Components/ModelTable/ModelTable", () => {
168
+ return {
169
+ __esModule: true,
170
+ default: (props: CapturedTableProps) => {
171
+ capturedTables.push(props);
172
+ return null;
173
+ },
174
+ };
175
+ });
176
+
177
+ import OnCallRulesTable, {
178
+ NotificationMethodChoice,
179
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationRule/OnCallRulesTable";
180
+ import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
181
+ import AlertOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/AlertOnCallRules";
182
+ import EpisodeOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/EpisodeOnCallRules";
183
+ import IncidentEpisodeOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/IncidentEpisodeOnCallRules";
184
+ import IncidentOnCallRules from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/IncidentOnCallRules";
185
+ import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
186
+ import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
187
+ import UserCall from "../../../Models/DatabaseModels/UserCall";
188
+ import UserEmail from "../../../Models/DatabaseModels/UserEmail";
189
+ import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
190
+ import UserPush from "../../../Models/DatabaseModels/UserPush";
191
+ import UserSMS from "../../../Models/DatabaseModels/UserSMS";
192
+ import UserTelegram from "../../../Models/DatabaseModels/UserTelegram";
193
+ import UserWebhook from "../../../Models/DatabaseModels/UserWebhook";
194
+ import UserWhatsApp from "../../../Models/DatabaseModels/UserWhatsApp";
195
+ import Route from "../../../Types/API/Route";
196
+ import Email from "../../../Types/Email";
197
+ import NotificationRuleType from "../../../Types/NotificationRule/NotificationRuleType";
198
+ import ObjectID from "../../../Types/ObjectID";
199
+ import ProjectUtil from "../../../UI/Utils/Project";
200
+
201
+ /*
202
+ * The seven models whose rows only their owner may read. They are listed
203
+ * together because the assertion that matters is about the SET: an admin
204
+ * surface that reads any one of them is reading a row it is not entitled to,
205
+ * and which one hardly matters.
206
+ */
207
+ const NOTIFICATION_METHOD_MODELS: Array<unknown> = [
208
+ UserEmail,
209
+ UserSMS,
210
+ UserCall,
211
+ UserPush,
212
+ UserWhatsApp,
213
+ UserTelegram,
214
+ UserWebhook,
215
+ ];
216
+
217
+ /*
218
+ * What the admin surface hands over in place of those reads: a channel name, the
219
+ * id of the row, and a mask. Telegram is unverified on purpose - the self-serve
220
+ * path filters `isVerified: true` in six of its seven queries, so a supplied
221
+ * list that offered unverified methods would be the two paths disagreeing about
222
+ * what is pickable.
223
+ */
224
+ const SUPPLIED_METHODS: Array<NotificationMethodChoice> = [
225
+ {
226
+ methodType: "SMS",
227
+ methodId: SUPPLIED_SMS_ID,
228
+ maskedIdentifier: MASKED_SMS,
229
+ isVerified: true,
230
+ },
231
+ {
232
+ methodType: "Email",
233
+ methodId: SUPPLIED_EMAIL_ID,
234
+ maskedIdentifier: MASKED_EMAIL,
235
+ isVerified: true,
236
+ },
237
+ {
238
+ methodType: "Telegram",
239
+ methodId: SUPPLIED_TELEGRAM_ID,
240
+ maskedIdentifier: MASKED_TELEGRAM,
241
+ isVerified: false,
242
+ },
243
+ ];
244
+
245
+ /* Every foreign key a rule can point a method at. */
246
+ const METHOD_FOREIGN_KEYS: Array<keyof UserNotificationRule> = [
247
+ "userEmailId",
248
+ "userSmsId",
249
+ "userCallId",
250
+ "userPushId",
251
+ "userWhatsAppId",
252
+ "userTelegramId",
253
+ "userWebhookId",
254
+ ];
255
+
256
+ const PROJECT_ID: ObjectID = new ObjectID(PROJECT_ID_STRING);
257
+ const currentUserId: ObjectID = new ObjectID(CURRENT_USER_ID_STRING);
258
+ const OTHER_USER_ID: ObjectID = new ObjectID(OTHER_USER_ID_STRING);
259
+
260
+ type SeverityModelType = { new (): IncidentSeverity | AlertSeverity };
261
+
262
+ type BuildSeverities = (
263
+ modelType: SeverityModelType,
264
+ ) => Array<IncidentSeverity | AlertSeverity>;
265
+
266
+ const buildSeverities: BuildSeverities = (
267
+ modelType: SeverityModelType,
268
+ ): Array<IncidentSeverity | AlertSeverity> => {
269
+ return [
270
+ { id: SEVERITY_ONE_ID, name: "Sev 1" },
271
+ { id: SEVERITY_TWO_ID, name: "Sev 2" },
272
+ ].map((spec: { id: string; name: string }) => {
273
+ const severity: IncidentSeverity | AlertSeverity = new modelType();
274
+ severity._id = spec.id;
275
+ severity.name = spec.name;
276
+ return severity;
277
+ });
278
+ };
279
+
280
+ const pageProps: PageComponentProps = {
281
+ pageRoute: new Route("/user-settings/on-call-rules"),
282
+ currentProject: null,
283
+ hasPaymentMethod: false,
284
+ };
285
+
286
+ /*
287
+ * Which model each getList call asked for. The severity fetch is the only one
288
+ * that varies between the four pages, so this is how "the right severity model
289
+ * is used" gets asserted without reaching inside the component.
290
+ */
291
+ type GetRequestedModelTypes = () => Array<unknown>;
292
+
293
+ const getRequestedModelTypes: GetRequestedModelTypes = (): Array<unknown> => {
294
+ return getListMock.mock.calls.map((call: Array<any>) => {
295
+ return call[0].modelType;
296
+ });
297
+ };
298
+
299
+ type GetCapturedTables = () => Array<CapturedTableProps>;
300
+
301
+ /*
302
+ * The captured list is REPLACED between tests rather than emptied in place, so a
303
+ * closure built inside the `for` loop below would otherwise read whichever array
304
+ * happened to be bound when the loop ran rather than the one the test is
305
+ * filling. Going through a stable function is also what stops eslint's
306
+ * no-loop-func from being right about it.
307
+ */
308
+ const getCapturedTables: GetCapturedTables = (): Array<CapturedTableProps> => {
309
+ return capturedTables;
310
+ };
311
+
312
+ type GetQueriedUserIds = () => Array<string | undefined>;
313
+
314
+ const getQueriedUserIds: GetQueriedUserIds = (): Array<string | undefined> => {
315
+ return getListMock.mock.calls.map((call: Array<any>) => {
316
+ return call[0].query.userId?.toString();
317
+ });
318
+ };
319
+
320
+ /*
321
+ * The options the create form's "Notification Method" dropdown is offering. It
322
+ * is an override field - its value travels in `miscDataProps` rather than as a
323
+ * column - so it is found by its override key rather than by a field name.
324
+ */
325
+ type GetMethodOptions = (
326
+ table: CapturedTableProps,
327
+ ) => Array<{ label: string; value: string }>;
328
+
329
+ const getMethodOptions: GetMethodOptions = (
330
+ table: CapturedTableProps,
331
+ ): Array<{ label: string; value: string }> => {
332
+ const field: CapturedFormField | undefined = table.formFields.find(
333
+ (candidate: CapturedFormField): boolean => {
334
+ return candidate.overrideFieldKey === "notificationMethod";
335
+ },
336
+ );
337
+
338
+ if (!field) {
339
+ throw new Error("no notification method form field");
340
+ }
341
+
342
+ return field.dropdownOptions || [];
343
+ };
344
+
345
+ /*
346
+ * The seven RELATION spellings. Selecting any of these on a rule pulls columns
347
+ * out of the method models themselves - the raw address, the raw number, the
348
+ * telegram handle - through a table an administrator is allowed to read.
349
+ *
350
+ * That route is not refused, and it is worth being precise about why, because
351
+ * "the model is owner-scoped" is the reason everybody assumes covers it and it
352
+ * does not. SelectPermission checks the nested key against UserNotificationRule's
353
+ * own `userEmail` column, whose read list is [Permission.CurrentUser] - and
354
+ * CurrentUser is auto-granted to every authenticated caller. Then
355
+ * QueryPermission.checkRelationQueryPermission sees `canReadOnRelationQuery:
356
+ * true` on UserEmail.email and returns early without consulting UserEmail's own
357
+ * read list at all. And a read of UserNotificationRule is deliberately NOT
358
+ * row-scoped for an administrator, since that is what the admin surface is for.
359
+ * Three permissive steps compose into a leak that no single one of them looks
360
+ * like.
361
+ */
362
+ const METHOD_RELATIONS: Array<string> = [
363
+ "userEmail",
364
+ "userSms",
365
+ "userCall",
366
+ "userPush",
367
+ "userWhatsApp",
368
+ "userTelegram",
369
+ "userWebhook",
370
+ ];
371
+
372
+ /*
373
+ * Everything the table asks the server for about a method, from BOTH of the
374
+ * places ModelTable builds its projection out of. Asserting on the union is the
375
+ * point: the two are unioned on the wire, so a guard applied to one of them is
376
+ * no guard at all.
377
+ */
378
+ type GetMethodProjectionKeys = (table: CapturedTableProps) => Array<string>;
379
+
380
+ const getMethodProjectionKeys: GetMethodProjectionKeys = (
381
+ table: CapturedTableProps,
382
+ ): Array<string> => {
383
+ const keys: Array<string> = Object.keys(table.selectMoreFields || {});
384
+
385
+ for (const column of table.columns || []) {
386
+ keys.push(...Object.keys(column.field || {}));
387
+ }
388
+
389
+ return keys;
390
+ };
391
+
392
+ /* The rendered "Notification Method" cell for one rule, as plain text. */
393
+ type RenderMethodCell = (
394
+ table: CapturedTableProps,
395
+ rule: UserNotificationRule,
396
+ ) => string;
397
+
398
+ const renderMethodCell: RenderMethodCell = (
399
+ table: CapturedTableProps,
400
+ rule: UserNotificationRule,
401
+ ): string => {
402
+ const column: CapturedColumn | undefined = (table.columns || []).find(
403
+ (candidate: CapturedColumn): boolean => {
404
+ return candidate.title === "Notification Method";
405
+ },
406
+ );
407
+
408
+ if (!column || !column.getElement) {
409
+ throw new Error("no notification method column");
410
+ }
411
+
412
+ const { container }: { container: HTMLElement } = render(
413
+ column.getElement(rule),
414
+ );
415
+
416
+ return container.textContent || "";
417
+ };
418
+
419
+ type GetMethodColumnsSet = (
420
+ rule: UserNotificationRule,
421
+ ) => Array<{ column: string; value: string }>;
422
+
423
+ /*
424
+ * Which of the seven method foreign keys a created rule ended up carrying. The
425
+ * assertion is always about the whole set, never about one column: a rule that
426
+ * sets the wrong one pages the wrong device, and a rule that sets two is a shape
427
+ * the create path has no meaning for.
428
+ */
429
+ const getMethodColumnsSet: GetMethodColumnsSet = (
430
+ rule: UserNotificationRule,
431
+ ): Array<{ column: string; value: string }> => {
432
+ const columns: Array<{ column: string; value: string }> = [];
433
+
434
+ for (const foreignKey of METHOD_FOREIGN_KEYS) {
435
+ const value: unknown = rule[foreignKey];
436
+
437
+ if (value) {
438
+ columns.push({
439
+ column: foreignKey as string,
440
+ value: (value as ObjectID).toString(),
441
+ });
442
+ }
443
+ }
444
+
445
+ return columns;
446
+ };
447
+
448
+ type RenderAndSettle = (element: ReactElement) => Promise<void>;
449
+
450
+ const renderAndSettle: RenderAndSettle = async (
451
+ element: ReactElement,
452
+ ): Promise<void> => {
453
+ render(element);
454
+
455
+ await waitFor(() => {
456
+ expect(capturedTables.length).toBeGreaterThan(0);
457
+ });
458
+ };
459
+
460
+ interface RuleTypeCase {
461
+ label: string;
462
+ Page: FunctionComponent<PageComponentProps>;
463
+ severityModelType: SeverityModelType;
464
+ wrongSeverityModelType: SeverityModelType;
465
+ foreignKeyColumn: "incidentSeverityId" | "alertSeverityId";
466
+ wrongForeignKeyColumn: "incidentSeverityId" | "alertSeverityId";
467
+ ruleType: NotificationRuleType;
468
+ }
469
+
470
+ /*
471
+ * Note the crossed pair in the middle two rows: the ALERT episode page reads
472
+ * AlertSeverity while the INCIDENT episode page reads IncidentSeverity. That
473
+ * crossing is the whole reason the severity model and the foreign key column
474
+ * are two separate props, and a case table is the only shape that makes an
475
+ * accidental re-derivation fail on exactly one row instead of passing on all
476
+ * four.
477
+ */
478
+ const RULE_TYPE_CASES: Array<RuleTypeCase> = [
479
+ {
480
+ label: "incident",
481
+ Page: IncidentOnCallRules,
482
+ severityModelType: IncidentSeverity,
483
+ wrongSeverityModelType: AlertSeverity,
484
+ foreignKeyColumn: "incidentSeverityId",
485
+ wrongForeignKeyColumn: "alertSeverityId",
486
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
487
+ },
488
+ {
489
+ label: "alert",
490
+ Page: AlertOnCallRules,
491
+ severityModelType: AlertSeverity,
492
+ wrongSeverityModelType: IncidentSeverity,
493
+ foreignKeyColumn: "alertSeverityId",
494
+ wrongForeignKeyColumn: "incidentSeverityId",
495
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
496
+ },
497
+ {
498
+ label: "alert episode",
499
+ Page: EpisodeOnCallRules,
500
+ severityModelType: AlertSeverity,
501
+ wrongSeverityModelType: IncidentSeverity,
502
+ foreignKeyColumn: "alertSeverityId",
503
+ wrongForeignKeyColumn: "incidentSeverityId",
504
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT_EPISODE,
505
+ },
506
+ {
507
+ label: "incident episode",
508
+ Page: IncidentEpisodeOnCallRules,
509
+ severityModelType: IncidentSeverity,
510
+ wrongSeverityModelType: AlertSeverity,
511
+ foreignKeyColumn: "incidentSeverityId",
512
+ wrongForeignKeyColumn: "alertSeverityId",
513
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT_EPISODE,
514
+ },
515
+ ];
516
+
517
+ describe("OnCallRulesTable", () => {
518
+ beforeEach(() => {
519
+ capturedTables = [];
520
+ getListMock.mockReset();
521
+
522
+ /*
523
+ * Whichever severity model the page asks for is the one it gets back. A
524
+ * page that asked for the wrong one would still render two tables, so the
525
+ * assertions below check the REQUEST rather than inferring it from what
526
+ * came back.
527
+ */
528
+ getListMock.mockImplementation((data: any) => {
529
+ if (
530
+ data.modelType === IncidentSeverity ||
531
+ data.modelType === AlertSeverity
532
+ ) {
533
+ return Promise.resolve({
534
+ data: buildSeverities(data.modelType),
535
+ count: 2,
536
+ skip: 0,
537
+ limit: 2,
538
+ });
539
+ }
540
+
541
+ /*
542
+ * One real row on the self-serve path, so "the dropdown is built from the
543
+ * models" is asserted against a label rather than against an empty list -
544
+ * which is what every method model returned before, and which a component
545
+ * that had stopped reading them entirely would also produce.
546
+ */
547
+ if (data.modelType === UserEmail) {
548
+ const userEmail: UserEmail = new UserEmail();
549
+ userEmail._id = OWN_EMAIL_ID;
550
+ userEmail.email = new Email(OWN_EMAIL_ADDRESS);
551
+
552
+ return Promise.resolve({
553
+ data: [userEmail],
554
+ count: 1,
555
+ skip: 0,
556
+ limit: 1,
557
+ });
558
+ }
559
+
560
+ return Promise.resolve({ data: [], count: 0, skip: 0, limit: 0 });
561
+ });
562
+
563
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
564
+ });
565
+
566
+ afterEach(() => {
567
+ cleanup();
568
+ jest.restoreAllMocks();
569
+ });
570
+
571
+ for (const testCase of RULE_TYPE_CASES) {
572
+ const Page: FunctionComponent<PageComponentProps> = testCase.Page;
573
+
574
+ describe(`${testCase.label} rules`, () => {
575
+ test("enumerates its own severity model and never the other one", async () => {
576
+ await renderAndSettle(<Page {...pageProps} />);
577
+
578
+ const requested: Array<unknown> = getRequestedModelTypes();
579
+
580
+ expect(requested).toContain(testCase.severityModelType);
581
+ expect(requested).not.toContain(testCase.wrongSeverityModelType);
582
+ });
583
+
584
+ test("filters each table on its own severity foreign key column", async () => {
585
+ await renderAndSettle(<Page {...pageProps} />);
586
+
587
+ expect(getCapturedTables()).toHaveLength(2);
588
+
589
+ const severityIds: Array<string> = [SEVERITY_ONE_ID, SEVERITY_TWO_ID];
590
+
591
+ getCapturedTables().forEach((table: CapturedTableProps, i: number) => {
592
+ expect(table.query["ruleType"]).toBe(testCase.ruleType);
593
+ expect(table.query["projectId"]?.toString()).toBe(PROJECT_ID_STRING);
594
+ expect(table.query["userId"]?.toString()).toBe(
595
+ CURRENT_USER_ID_STRING,
596
+ );
597
+
598
+ expect(table.query[testCase.foreignKeyColumn]?.toString()).toBe(
599
+ severityIds[i],
600
+ );
601
+
602
+ /*
603
+ * Absent, not merely undefined. A query that carries the other column
604
+ * at all is the shape that reads as "no severity filter" once it is
605
+ * serialised onto the wire.
606
+ */
607
+ expect(Object.keys(table.query)).not.toContain(
608
+ testCase.wrongForeignKeyColumn,
609
+ );
610
+ });
611
+ });
612
+
613
+ test("stamps the same severity foreign key column on a newly created rule", async () => {
614
+ await renderAndSettle(<Page {...pageProps} />);
615
+
616
+ const created: UserNotificationRule =
617
+ await getCapturedTables()[0]!.onBeforeCreate(
618
+ new UserNotificationRule(),
619
+ {
620
+ notificationMethod: undefined,
621
+ },
622
+ );
623
+
624
+ expect(created.ruleType).toBe(testCase.ruleType);
625
+ expect(created.projectId?.toString()).toBe(PROJECT_ID_STRING);
626
+ expect(created.userId?.toString()).toBe(CURRENT_USER_ID_STRING);
627
+
628
+ expect(created[testCase.foreignKeyColumn]?.toString()).toBe(
629
+ SEVERITY_ONE_ID,
630
+ );
631
+ expect(created[testCase.wrongForeignKeyColumn]).toBeUndefined();
632
+ });
633
+
634
+ test("keeps the severity id in each table's user preferences key", async () => {
635
+ await renderAndSettle(<Page {...pageProps} />);
636
+
637
+ const keys: Array<string> = getCapturedTables().map(
638
+ (table: CapturedTableProps) => {
639
+ return table.userPreferencesKey;
640
+ },
641
+ );
642
+
643
+ expect(keys[0]).toContain(SEVERITY_ONE_ID);
644
+ expect(keys[1]).toContain(SEVERITY_TWO_ID);
645
+ expect(keys[0]).not.toBe(keys[1]);
646
+
647
+ keys.forEach((key: string) => {
648
+ expect(key).toContain(testCase.ruleType);
649
+ });
650
+ });
651
+ });
652
+ }
653
+
654
+ test("gives every table across all four rule types a distinct preferences key", async () => {
655
+ const keys: Array<string> = [];
656
+
657
+ for (const testCase of RULE_TYPE_CASES) {
658
+ const Page: FunctionComponent<PageComponentProps> = testCase.Page;
659
+
660
+ capturedTables = [];
661
+ await renderAndSettle(<Page {...pageProps} />);
662
+
663
+ capturedTables.forEach((table: CapturedTableProps) => {
664
+ keys.push(table.userPreferencesKey);
665
+ });
666
+
667
+ cleanup();
668
+ }
669
+
670
+ /*
671
+ * Four pages, two severities each. If any two keys collided the distinct
672
+ * count would drop, and the two tables sharing a key would share a stored
673
+ * page size and a slice of URL state.
674
+ */
675
+ expect(keys).toHaveLength(8);
676
+ expect(new Set(keys).size).toBe(8);
677
+ });
678
+
679
+ /*
680
+ * Pointed at somebody else - the admin surface.
681
+ *
682
+ * The rules still move with the user, but the METHODS no longer do: a read of
683
+ * UserSMS for a colleague is refused, because the row behind the label holds
684
+ * their raw number. Widening that scope so this dropdown could be filled was
685
+ * tried and could not be contained, so the list is handed in already masked
686
+ * and the table reads nothing.
687
+ */
688
+ type RenderForOtherUser = (
689
+ notificationMethods?: Array<NotificationMethodChoice> | undefined,
690
+ ) => Promise<void>;
691
+
692
+ const renderForOtherUser: RenderForOtherUser = async (
693
+ notificationMethods?: Array<NotificationMethodChoice> | undefined,
694
+ ): Promise<void> => {
695
+ await renderAndSettle(
696
+ <OnCallRulesTable
697
+ severityModelType={IncidentSeverity}
698
+ severityForeignKeyColumn="incidentSeverityId"
699
+ ruleType={NotificationRuleType.ON_CALL_EXECUTED_INCIDENT}
700
+ userPreferencesKeyPrefix="admin-notification-rules-table"
701
+ userId={OTHER_USER_ID}
702
+ notificationMethods={notificationMethods}
703
+ getTitle={(severityName: string): string => {
704
+ return severityName;
705
+ }}
706
+ getDescription={(severityName: string): string => {
707
+ return severityName;
708
+ }}
709
+ />,
710
+ );
711
+ };
712
+
713
+ test("scopes rules to the user it is given, and reads no method model for them", async () => {
714
+ await renderForOtherUser(SUPPLIED_METHODS);
715
+
716
+ expect(capturedTables[0]!.query["userId"]?.toString()).toBe(
717
+ OTHER_USER_ID_STRING,
718
+ );
719
+
720
+ /*
721
+ * The whole point of the redesign, in one assertion. Not "reads them and
722
+ * shows fewer columns" - does not read them. Each of these seven models is
723
+ * scoped to the person who owns the device, and the columns behind them are
724
+ * the raw phone number, the webhook bearer url, the push device token, the
725
+ * telegram chat id and the verification code. A request here is a request
726
+ * the server refuses, and a component that issues it renders an error page
727
+ * where the rules should be.
728
+ */
729
+ const requested: Array<unknown> = getRequestedModelTypes();
730
+
731
+ for (const methodModel of NOTIFICATION_METHOD_MODELS) {
732
+ expect(requested).not.toContain(methodModel);
733
+ }
734
+
735
+ expect(requested).toEqual([IncidentSeverity]);
736
+
737
+ // Nor may it quietly fall back to reading the ADMIN's own methods instead.
738
+ expect(getQueriedUserIds()).not.toContain(CURRENT_USER_ID_STRING);
739
+
740
+ const created: UserNotificationRule =
741
+ await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {});
742
+
743
+ expect(created.userId?.toString()).toBe(OTHER_USER_ID_STRING);
744
+ });
745
+
746
+ test("reads no method model even when it is handed no methods at all", async () => {
747
+ /*
748
+ * The readiness payload the admin page sources these from is fetched
749
+ * asynchronously and can fail outright, so an absent list is a real state
750
+ * rather than a caller mistake. Treating it as "then fetch them yourself"
751
+ * would fire seven refused requests and turn this component's error state on
752
+ * - taking the rule repair away because a DIFFERENT read failed.
753
+ */
754
+ await renderForOtherUser(undefined);
755
+
756
+ const requested: Array<unknown> = getRequestedModelTypes();
757
+
758
+ for (const methodModel of NOTIFICATION_METHOD_MODELS) {
759
+ expect(requested).not.toContain(methodModel);
760
+ }
761
+
762
+ expect(capturedTables).toHaveLength(2);
763
+ expect(getMethodOptions(capturedTables[0]!)).toEqual([]);
764
+ });
765
+
766
+ test("builds the dropdown from the masked methods it is handed", async () => {
767
+ await renderForOtherUser(SUPPLIED_METHODS);
768
+
769
+ const options: Array<{ label: string; value: string }> = getMethodOptions(
770
+ capturedTables[0]!,
771
+ );
772
+
773
+ /*
774
+ * Label from the mask, value from the method id. That pairing is the entire
775
+ * trick: the viewer picks "SMS: +1 ••• ••• 4821" and the form submits a
776
+ * foreign key, so the number itself is never in the page to begin with.
777
+ */
778
+ expect(options).toEqual([
779
+ { label: `SMS: ${MASKED_SMS}`, value: SUPPLIED_SMS_ID },
780
+ { label: `Email: ${MASKED_EMAIL}`, value: SUPPLIED_EMAIL_ID },
781
+ ]);
782
+
783
+ for (const option of options) {
784
+ expect(option.label).not.toContain(RAW_PHONE);
785
+ }
786
+
787
+ /*
788
+ * The unverified Telegram method is absent, which mirrors the
789
+ * `isVerified: true` filter on six of the seven self-serve queries. A rule
790
+ * pointed at an unverified device is a rule that pages nothing.
791
+ */
792
+ expect(
793
+ options.map((option: { label: string; value: string }) => {
794
+ return option.value;
795
+ }),
796
+ ).not.toContain(SUPPLIED_TELEGRAM_ID);
797
+ });
798
+
799
+ test("points the created rule at the column the chosen method belongs to", async () => {
800
+ await renderForOtherUser(SUPPLIED_METHODS);
801
+
802
+ const created: UserNotificationRule =
803
+ await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
804
+ notificationMethod: SUPPLIED_SMS_ID,
805
+ });
806
+
807
+ /*
808
+ * The seven foreign keys are mutually exclusive and nothing validates that
809
+ * client-side, so this asserts the whole set: an SMS id written to
810
+ * `userEmailId` would page an address rather than a phone, and would not
811
+ * error anywhere on the way.
812
+ */
813
+ expect(getMethodColumnsSet(created)).toEqual([
814
+ { column: "userSmsId", value: SUPPLIED_SMS_ID },
815
+ ]);
816
+ });
817
+
818
+ test("sets no method at all for an id it was never handed", async () => {
819
+ await renderForOtherUser(SUPPLIED_METHODS);
820
+
821
+ const created: UserNotificationRule =
822
+ await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
823
+ notificationMethod: UNKNOWN_METHOD_ID,
824
+ });
825
+
826
+ /*
827
+ * A rule with no method is refused by the server, which is the right
828
+ * failure. The wrong one would be guessing a column for an unrecognised id -
829
+ * that produces a rule which pages some other device and never errors.
830
+ */
831
+ expect(getMethodColumnsSet(created)).toEqual([]);
832
+ });
833
+
834
+ test("sets no method for an id the dropdown deliberately withheld", async () => {
835
+ await renderForOtherUser(SUPPLIED_METHODS);
836
+
837
+ const created: UserNotificationRule =
838
+ await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
839
+ notificationMethod: SUPPLIED_TELEGRAM_ID,
840
+ });
841
+
842
+ /*
843
+ * The unverified method IS in the supplied list - it is shown on the methods
844
+ * card - and is only kept out of the dropdown. Matching on the list alone
845
+ * would let a value that was never offered become a rule anyway, so the
846
+ * offered set and the accepted set are one predicate rather than two.
847
+ */
848
+ expect(getMethodColumnsSet(created)).toEqual([]);
849
+ });
850
+
851
+ test("asks for no method column of any method model when the table is about somebody else", async () => {
852
+ await renderForOtherUser(SUPPLIED_METHODS);
853
+
854
+ /*
855
+ * The SECOND door, and the one that was open.
856
+ *
857
+ * "Reads no method model" is usually taken to mean "issues no getList over
858
+ * UserEmail", which the test above already pins. But the rules table also
859
+ * asked for `{ userEmail: { email: true }, userSms: { phone: true }, ... }`
860
+ * as a nested select ON THE RULE, to render its "Notification Method" cell.
861
+ * That reaches the same columns through a table an administrator may read,
862
+ * it is not row-scoped for them, and the permission machinery lets it
863
+ * through - see METHOD_RELATIONS above for the three steps. The admin page
864
+ * masked every identifier on its readiness card and then printed the raw
865
+ * ones in a table one card below.
866
+ */
867
+ const projection: Array<string> = getMethodProjectionKeys(
868
+ capturedTables[0]!,
869
+ );
870
+
871
+ for (const relation of METHOD_RELATIONS) {
872
+ expect(projection).not.toContain(relation);
873
+ }
874
+
875
+ /*
876
+ * The ids are asked for instead, and are the mechanism rather than a
877
+ * consolation prize: an id carries nothing, it is admin-readable by design,
878
+ * and matching it against the masked payload is what lets the cell name a
879
+ * method whose row was never read.
880
+ */
881
+ for (const foreignKey of METHOD_FOREIGN_KEYS) {
882
+ expect(projection).toContain(foreignKey as string);
883
+ }
884
+
885
+ // Not vacuous - it still asks for the things the other cells need.
886
+ expect(projection).toContain("isOptOut");
887
+ expect(projection).toContain("notifyAfterMinutes");
888
+ });
889
+
890
+ test("labels somebody else's rule from the mask, never from the row", async () => {
891
+ await renderForOtherUser(SUPPLIED_METHODS);
892
+
893
+ const rule: UserNotificationRule = new UserNotificationRule();
894
+ rule.userSmsId = new ObjectID(SUPPLIED_SMS_ID);
895
+
896
+ const cell: string = renderMethodCell(capturedTables[0]!, rule);
897
+
898
+ expect(cell).toBe(`SMS: ${MASKED_SMS}`);
899
+ expect(cell).not.toContain(RAW_PHONE);
900
+ });
901
+
902
+ test("names the method behind a rule even once that method stops being verified", async () => {
903
+ await renderForOtherUser(SUPPLIED_METHODS);
904
+
905
+ const rule: UserNotificationRule = new UserNotificationRule();
906
+ rule.userTelegramId = new ObjectID(SUPPLIED_TELEGRAM_ID);
907
+
908
+ /*
909
+ * The dropdown withholds this method, and the cell must NOT. A rule still
910
+ * pointing at a method that has since become unverified is precisely the
911
+ * misconfiguration an administrator opened this page to find, so hiding it
912
+ * behind the same filter that governs the dropdown would blank out the one
913
+ * row worth looking at.
914
+ */
915
+ expect(renderMethodCell(capturedTables[0]!, rule)).toBe(
916
+ `Telegram: ${MASKED_TELEGRAM}`,
917
+ );
918
+ });
919
+
920
+ test("a rule whose method is not in the payload is labelled, not left blank", async () => {
921
+ await renderForOtherUser(SUPPLIED_METHODS);
922
+
923
+ const rule: UserNotificationRule = new UserNotificationRule();
924
+ rule.userCallId = new ObjectID(UNKNOWN_METHOD_ID);
925
+
926
+ const cell: string = renderMethodCell(capturedTables[0]!, rule);
927
+
928
+ /*
929
+ * Readiness can be down, or the method deleted, and then the cell cannot
930
+ * name it. Rendering nothing is the one genuinely wrong answer: a blank
931
+ * method cell is exactly what an OPT-OUT row looks like, and the two mean
932
+ * opposite things - "never page me here" against "paged on a device this
933
+ * screen cannot name". An admin who reads the second as the first deletes a
934
+ * working rule and silently stops somebody's pages.
935
+ */
936
+ expect(cell.length).toBeGreaterThan(0);
937
+ expect(cell).not.toBe(OPT_OUT_LABEL);
938
+ });
939
+
940
+ test("an opt-out row is still labelled as muted for somebody else", async () => {
941
+ await renderForOtherUser(SUPPLIED_METHODS);
942
+
943
+ const rule: UserNotificationRule = new UserNotificationRule();
944
+ (rule as unknown as Record<string, unknown>)["isOptOut"] = true;
945
+
946
+ expect(renderMethodCell(capturedTables[0]!, rule)).toBe(OPT_OUT_LABEL);
947
+ });
948
+
949
+ test("the signed-in user's own table still asks for the method relations", async () => {
950
+ /*
951
+ * The other half of the seam, and the regression that would actually be
952
+ * noticed: the four self-serve pages render this same cell from the nested
953
+ * relations, and reading your own address unmasked is both allowed and the
954
+ * more useful thing to show. Narrowing the projection for everybody would
955
+ * blank the method column on every settings page in the product.
956
+ */
957
+ await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
958
+
959
+ const projection: Array<string> = getMethodProjectionKeys(
960
+ capturedTables[0]!,
961
+ );
962
+
963
+ for (const relation of METHOD_RELATIONS) {
964
+ expect(projection).toContain(relation);
965
+ }
966
+ });
967
+
968
+ test("the signed-in user's own table still reads the method models itself", async () => {
969
+ /*
970
+ * The regression surface. Four settings pages every existing user already
971
+ * relies on read their own seven models to fill this dropdown, and reading
972
+ * your own rows is exactly what those models allow. Nothing about the admin
973
+ * surface is a reason to change it - and an unmasked label is more use to
974
+ * somebody looking at their own phone number than a mask of it.
975
+ */
976
+ await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
977
+
978
+ const requested: Array<unknown> = getRequestedModelTypes();
979
+
980
+ for (const methodModel of NOTIFICATION_METHOD_MODELS) {
981
+ expect(requested).toContain(methodModel);
982
+ }
983
+
984
+ expect(
985
+ getQueriedUserIds().filter((userId: string | undefined) => {
986
+ return userId === CURRENT_USER_ID_STRING;
987
+ }),
988
+ ).toHaveLength(7);
989
+
990
+ expect(getMethodOptions(capturedTables[0]!)).toEqual([
991
+ { label: `Email: ${OWN_EMAIL_ADDRESS}`, value: OWN_EMAIL_ID },
992
+ ]);
993
+
994
+ const created: UserNotificationRule =
995
+ await capturedTables[0]!.onBeforeCreate(new UserNotificationRule(), {
996
+ notificationMethod: OWN_EMAIL_ID,
997
+ });
998
+
999
+ expect(getMethodColumnsSet(created)).toEqual([
1000
+ { column: "userEmailId", value: OWN_EMAIL_ID },
1001
+ ]);
1002
+ });
1003
+
1004
+ test("a table handed methods for the signed-in user still reads their own rows", async () => {
1005
+ /*
1006
+ * The fourth corner of the seam, stated so it cannot be read the other way:
1007
+ * WHOSE rules these are decides where the methods come from, not whether a
1008
+ * list happened to be passed. The admin page renders this component for the
1009
+ * viewer's own id too, and there the supplied masks are the wrong thing to
1010
+ * show somebody about their own devices.
1011
+ */
1012
+ await renderAndSettle(
1013
+ <OnCallRulesTable
1014
+ severityModelType={IncidentSeverity}
1015
+ severityForeignKeyColumn="incidentSeverityId"
1016
+ ruleType={NotificationRuleType.ON_CALL_EXECUTED_INCIDENT}
1017
+ userPreferencesKeyPrefix="admin-notification-rules-table"
1018
+ userId={currentUserId}
1019
+ notificationMethods={SUPPLIED_METHODS}
1020
+ getTitle={(severityName: string): string => {
1021
+ return severityName;
1022
+ }}
1023
+ getDescription={(severityName: string): string => {
1024
+ return severityName;
1025
+ }}
1026
+ />,
1027
+ );
1028
+
1029
+ expect(getMethodOptions(capturedTables[0]!)).toEqual([
1030
+ { label: `Email: ${OWN_EMAIL_ADDRESS}`, value: OWN_EMAIL_ID },
1031
+ ]);
1032
+ });
1033
+
1034
+ test("drops the create and delete affordances when it is not editable", async () => {
1035
+ await renderAndSettle(
1036
+ <OnCallRulesTable
1037
+ severityModelType={AlertSeverity}
1038
+ severityForeignKeyColumn="alertSeverityId"
1039
+ ruleType={NotificationRuleType.ON_CALL_EXECUTED_ALERT}
1040
+ userPreferencesKeyPrefix="admin-notification-rules-table"
1041
+ userId={OTHER_USER_ID}
1042
+ isEditable={false}
1043
+ getTitle={(severityName: string): string => {
1044
+ return severityName;
1045
+ }}
1046
+ getDescription={(severityName: string): string => {
1047
+ return severityName;
1048
+ }}
1049
+ />,
1050
+ );
1051
+
1052
+ expect(capturedTables).toHaveLength(2);
1053
+
1054
+ capturedTables.forEach((table: CapturedTableProps) => {
1055
+ expect(table.isCreateable).toBe(false);
1056
+ expect(table.isDeleteable).toBe(false);
1057
+
1058
+ /*
1059
+ * Including the row editor. Leaving it on for a viewer who may not create
1060
+ * or delete would draw the one control that rewrites an existing rule for
1061
+ * exactly the person the server will refuse.
1062
+ */
1063
+ expect(table.isEditable).toBe(false);
1064
+ });
1065
+ });
1066
+
1067
+ test("turns the row editor on for a viewer who may edit, along with add and remove", async () => {
1068
+ /*
1069
+ * All three affordances answer ONE question - may this person rewrite how
1070
+ * somebody gets paged? - and the server answers it once, so they move
1071
+ * together.
1072
+ *
1073
+ * The editor used to be hardcoded off, on the assumption that a notification
1074
+ * rule is only ever added and removed. That made the whole edit capability
1075
+ * inert: no PATCH was ever issued from this table, so opening
1076
+ * `notifyAfterMinutes` to update bought nothing anybody could click. The
1077
+ * delay is the single most common repair on this screen, and doing it by
1078
+ * deleting the rule and adding it back is a window during which the person
1079
+ * is not paged at all.
1080
+ */
1081
+ await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
1082
+
1083
+ capturedTables.forEach((table: CapturedTableProps) => {
1084
+ expect(table.isCreateable).toBe(true);
1085
+ /*
1086
+ * Off by design, and NOT the same thing as "cannot delete". Deleting a
1087
+ * rule goes through the impact confirmation instead - the guard supplies
1088
+ * its own action button, because ModelTable's built-in dialog cannot say
1089
+ * how many rules disappear or which severity loses its last delivering
1090
+ * rule. "Remove" in this test's name is that button, not this flag.
1091
+ */
1092
+ expect(table.isDeleteable).toBe(false);
1093
+ expect(table.isEditable).toBe(true);
1094
+ });
1095
+ });
1096
+
1097
+ test("renders the per-severity card copy the self-serve pages had before the extraction", async () => {
1098
+ await renderAndSettle(<IncidentOnCallRules {...pageProps} />);
1099
+
1100
+ expect(capturedTables[0]!.cardProps.title).toBe(
1101
+ "Sev 1 Severity: When I am on call and Sev 1 is assigned to me...",
1102
+ );
1103
+ expect(capturedTables[0]!.cardProps.description).toBe(
1104
+ "Here are the rules when you are on call and Sev 1 is assigned to you.",
1105
+ );
1106
+
1107
+ cleanup();
1108
+ capturedTables = [];
1109
+
1110
+ await renderAndSettle(<EpisodeOnCallRules {...pageProps} />);
1111
+
1112
+ expect(capturedTables[0]!.cardProps.title).toBe(
1113
+ "Sev 1 Severity Episode: When I am on call and Sev 1 severity episode is assigned to me...",
1114
+ );
1115
+ expect(capturedTables[0]!.cardProps.description).toBe(
1116
+ "Here are the rules when you are on call and Sev 1 Severity episode is assigned to you.",
1117
+ );
1118
+ });
1119
+ });