@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,915 @@
1
+ import { mockRouter } from "./Helpers";
2
+ import CommonAPI from "../../../Server/API/CommonAPI";
3
+ import OnCallReadinessAPI from "../../../Server/API/OnCallReadinessAPI";
4
+ import OnCallReadinessService from "../../../Server/Services/OnCallReadinessService";
5
+ import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
6
+ import OnCallSetupReminderService, {
7
+ SetupReminderOutcome,
8
+ SetupReminderResult,
9
+ SetupReminderUserResult,
10
+ } from "../../../Server/Services/OnCallSetupReminderService";
11
+ import {
12
+ ExpressRequest,
13
+ ExpressResponse,
14
+ NextFunction,
15
+ } from "../../../Server/Utils/Express";
16
+ import Response from "../../../Server/Utils/Response";
17
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
18
+ import Dictionary from "../../../Types/Dictionary";
19
+ import BadDataException from "../../../Types/Exception/BadDataException";
20
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
21
+ import { JSONObject } from "../../../Types/JSON";
22
+ import ObjectID from "../../../Types/ObjectID";
23
+ import Permission, {
24
+ UserPermission,
25
+ UserTenantAccessPermission,
26
+ } from "../../../Types/Permission";
27
+ import { beforeEach, describe, expect, test } from "@jest/globals";
28
+
29
+ /*
30
+ * POST /on-call-readiness/send-setup-reminder - the only WRITE on the readiness
31
+ * router, and the only endpoint in this feature that sends email.
32
+ *
33
+ * The route itself is thin by design: OnCallSetupReminderService owns who gets
34
+ * mailed and what the mail says. What is left worth testing here is exactly the
35
+ * part the service cannot do for itself, and it is all about the boundary:
36
+ *
37
+ * WHO. The route is mounted with UserMiddleware.getUserMiddleware, which
38
+ * admits anonymous callers as UserType.Public and takes the project from a
39
+ * caller-supplied `tenantid` header. So the handler is the only gate, and it
40
+ * has to refuse BEFORE any mail-sending work starts - a route that refuses
41
+ * after the send has not refused anything.
42
+ *
43
+ * WITH WHAT STANDING. Membership is the bar for the READS on this router and
44
+ * was, wrongly, the bar for this write too. Every read-only Viewer holds an
45
+ * access entry for the project, so the original gate let anyone who could LOOK
46
+ * at the readiness table make the product mail their colleagues in the
47
+ * project's name. Reading who is unreachable and mailing them about it are
48
+ * different acts, and the tests below pin the second to on-call management.
49
+ *
50
+ * WHICH PROJECT. The project comes from the authenticated caller's tenant,
51
+ * never from the body. A body that carries its own projectId must be ignored
52
+ * entirely, or a member of project A mails project B's responders about
53
+ * project B by sending one extra field.
54
+ *
55
+ * WHAT THE BODY IS ALLOWED TO BE. ObjectID's constructor accepts any string,
56
+ * so a body of arbitrary text would otherwise travel into the service and out
57
+ * the far side as an ordinary-looking "not a member of this project" result.
58
+ *
59
+ * WHAT COMES BACK. Ids flattened to strings, outcomes preserved per user, and
60
+ * the counts carried alongside - because the UI's whole job is to say "5 sent,
61
+ * 2 skipped, 1 failed" instead of showing a tick.
62
+ *
63
+ * The service is stubbed throughout. OnCallSetupReminder.test.ts covers its
64
+ * behaviour with the same rigour; duplicating that here would test the stub.
65
+ */
66
+
67
+ jest.mock("../../../Server/Utils/Express", () => {
68
+ return {
69
+ getRouter: () => {
70
+ return mockRouter;
71
+ },
72
+ };
73
+ });
74
+
75
+ jest.mock("../../../Server/Utils/Response", () => {
76
+ return {
77
+ sendJsonObjectResponse: jest.fn().mockImplementation((...args: []) => {
78
+ return args;
79
+ }),
80
+ sendJsonArrayResponse: jest.fn().mockImplementation((...args: []) => {
81
+ return args;
82
+ }),
83
+ sendEntityArrayResponse: jest.fn().mockImplementation((...args: []) => {
84
+ return args;
85
+ }),
86
+ sendEntityResponse: jest.fn().mockImplementation((...args: []) => {
87
+ return args;
88
+ }),
89
+ sendEmptySuccessResponse: jest.fn(),
90
+ sendErrorResponse: jest.fn().mockImplementation((...args: []) => {
91
+ return args;
92
+ }),
93
+ };
94
+ });
95
+
96
+ const REMINDER_ROUTE: string = "/on-call-readiness/send-setup-reminder";
97
+
98
+ /*
99
+ * The message every authorisation refusal on this router carries. Asserted on
100
+ * the literal so "you are in the wrong project" cannot drift apart from "you are
101
+ * not logged in" - a caller must not be able to tell them apart.
102
+ */
103
+ const REFUSAL: string = "You are not authorized to access this project's data.";
104
+
105
+ interface RegisteredRoute {
106
+ method: string;
107
+ uri: string;
108
+ middleware: unknown;
109
+ handlerFunction: (
110
+ req: ExpressRequest,
111
+ res: ExpressResponse,
112
+ next: NextFunction,
113
+ ) => void | Promise<void>;
114
+ }
115
+
116
+ interface RouteCallResult {
117
+ thrownToNext: unknown;
118
+ nextCallCount: number;
119
+ }
120
+
121
+ let propsSpy: jest.SpyInstance;
122
+ let sendRemindersSpy: jest.SpyInstance;
123
+
124
+ let projectId: ObjectID;
125
+ let otherProjectId: ObjectID;
126
+ let callerUserId: ObjectID;
127
+ let userA: ObjectID;
128
+ let userB: ObjectID;
129
+
130
+ /*
131
+ * A logged-in caller holding exactly the permissions named, in the project
132
+ * named.
133
+ *
134
+ * `isBlockPermission` is set explicitly on every entry because the dictionary
135
+ * these live in holds GRANTS AND DENIALS together and the route's permission
136
+ * read filters on that flag. A fixture that left it undefined would be a grant
137
+ * by accident rather than on purpose, and the denial test below would then be
138
+ * testing nothing.
139
+ */
140
+ function buildProps(data: {
141
+ projectId: ObjectID;
142
+ userId: ObjectID;
143
+ permissions: Array<Permission>;
144
+ blockedPermissions?: Array<Permission>;
145
+ }): DatabaseCommonInteractionProps {
146
+ const granted: Array<UserPermission> = data.permissions.map(
147
+ (permission: Permission): UserPermission => {
148
+ return {
149
+ _type: "UserPermission",
150
+ permission: permission,
151
+ labelIds: [],
152
+ isBlockPermission: false,
153
+ };
154
+ },
155
+ );
156
+
157
+ const blocked: Array<UserPermission> = (data.blockedPermissions || []).map(
158
+ (permission: Permission): UserPermission => {
159
+ return {
160
+ _type: "UserPermission",
161
+ permission: permission,
162
+ labelIds: [],
163
+ isBlockPermission: true,
164
+ };
165
+ },
166
+ );
167
+
168
+ const tenantPermission: UserTenantAccessPermission = {
169
+ _type: "UserTenantAccessPermission",
170
+ projectId: data.projectId,
171
+ permissions: [...granted, ...blocked],
172
+ };
173
+
174
+ const permissionMap: Dictionary<UserTenantAccessPermission> = {};
175
+ permissionMap[data.projectId.toString()] = tenantPermission;
176
+
177
+ return {
178
+ tenantId: data.projectId,
179
+ userId: data.userId,
180
+ userTenantAccessPermission: permissionMap,
181
+ };
182
+ }
183
+
184
+ /*
185
+ * The caller the rest of this file assumes: somebody who administers on-call in
186
+ * this project, which is what the route now requires. Everything about the body
187
+ * and the response shape is tested through this caller, so a regression in the
188
+ * permission gate shows up as those tests failing loudly rather than as one
189
+ * skipped assertion.
190
+ */
191
+ function buildOnCallAdminProps(data: {
192
+ projectId: ObjectID;
193
+ userId: ObjectID;
194
+ }): DatabaseCommonInteractionProps {
195
+ return buildProps({
196
+ projectId: data.projectId,
197
+ userId: data.userId,
198
+ permissions: [Permission.ProjectMember, Permission.OnCallAdmin],
199
+ });
200
+ }
201
+
202
+ /*
203
+ * A member with no on-call standing - the caller the original gate admitted.
204
+ * ProjectMember and Viewer are what an ordinary teammate and a read-only
205
+ * teammate carry, and neither is a licence to send mail as the project.
206
+ */
207
+ function buildMemberProps(data: {
208
+ projectId: ObjectID;
209
+ userId: ObjectID;
210
+ }): DatabaseCommonInteractionProps {
211
+ return buildProps({
212
+ projectId: data.projectId,
213
+ userId: data.userId,
214
+ permissions: [Permission.ProjectMember],
215
+ });
216
+ }
217
+
218
+ function routeFor(uri: string): RegisteredRoute {
219
+ const routes: Array<RegisteredRoute> =
220
+ mockRouter.routes as unknown as Array<RegisteredRoute>;
221
+
222
+ const route: RegisteredRoute | undefined = routes.find(
223
+ (candidate: RegisteredRoute): boolean => {
224
+ return candidate.method === "POST" && candidate.uri === uri;
225
+ },
226
+ );
227
+
228
+ if (!route) {
229
+ throw new Error(`No POST route registered for ${uri}`);
230
+ }
231
+
232
+ return route;
233
+ }
234
+
235
+ async function callRoute(
236
+ body: JSONObject | undefined,
237
+ ): Promise<RouteCallResult> {
238
+ const req: ExpressRequest = {
239
+ params: {},
240
+ query: {},
241
+ body: body,
242
+ headers: {},
243
+ } as unknown as ExpressRequest;
244
+
245
+ const res: ExpressResponse = {
246
+ send: jest.fn(),
247
+ json: jest.fn(),
248
+ status: jest.fn().mockReturnThis(),
249
+ } as unknown as ExpressResponse;
250
+
251
+ const next: jest.Mock = jest.fn();
252
+
253
+ await routeFor(REMINDER_ROUTE).handlerFunction(
254
+ req,
255
+ res,
256
+ next as unknown as NextFunction,
257
+ );
258
+
259
+ return {
260
+ thrownToNext: next.mock.calls[0] ? next.mock.calls[0][0] : undefined,
261
+ nextCallCount: next.mock.calls.length,
262
+ };
263
+ }
264
+
265
+ /*
266
+ * The body the route handed to Response.sendJsonObjectResponse, typed as a bag
267
+ * of unknowns rather than as the wire interface: the point of these assertions
268
+ * is what actually crosses the wire, and typing it as the thing it is supposed
269
+ * to be would let a missing field type-check its way past the test.
270
+ */
271
+ function jsonPayload(): Record<string, unknown> {
272
+ const sender: jest.Mock =
273
+ Response.sendJsonObjectResponse as unknown as jest.Mock;
274
+ const calls: Array<Array<unknown>> = sender.mock.calls;
275
+ const last: Array<unknown> | undefined = calls[calls.length - 1];
276
+
277
+ if (!last) {
278
+ throw new Error("The route sent no JSON response");
279
+ }
280
+
281
+ return last[2] as Record<string, unknown>;
282
+ }
283
+
284
+ function serviceCall(): { projectId: ObjectID; userIds: Array<ObjectID> } {
285
+ const call: Array<unknown> | undefined = sendRemindersSpy.mock.calls[0];
286
+
287
+ if (!call) {
288
+ throw new Error("The route did not call the reminder service");
289
+ }
290
+
291
+ return call[0] as { projectId: ObjectID; userIds: Array<ObjectID> };
292
+ }
293
+
294
+ beforeEach(() => {
295
+ jest.clearAllMocks();
296
+
297
+ projectId = ObjectID.generate();
298
+ otherProjectId = ObjectID.generate();
299
+ callerUserId = ObjectID.generate();
300
+ userA = ObjectID.generate();
301
+ userB = ObjectID.generate();
302
+
303
+ propsSpy = jest
304
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
305
+ .mockResolvedValue(
306
+ buildOnCallAdminProps({ projectId: projectId, userId: callerUserId }),
307
+ );
308
+
309
+ /*
310
+ * The default stub echoes the request back as an all-sent result, so the
311
+ * assertions about WHAT THE ROUTE PASSED IN can be made from the payload it
312
+ * sent out. Tests about the shape of a mixed answer override it.
313
+ */
314
+ const echoAllSent: (data: {
315
+ projectId: ObjectID;
316
+ userIds: Array<ObjectID>;
317
+ }) => Promise<SetupReminderResult> = async (data: {
318
+ projectId: ObjectID;
319
+ userIds: Array<ObjectID>;
320
+ }): Promise<SetupReminderResult> => {
321
+ return {
322
+ projectId: data.projectId,
323
+ requestedCount: data.userIds.length,
324
+ sentCount: data.userIds.length,
325
+ skippedCount: 0,
326
+ failedCount: 0,
327
+ results: data.userIds.map((userId: ObjectID): SetupReminderUserResult => {
328
+ return {
329
+ userId: userId,
330
+ outcome: SetupReminderOutcome.Sent,
331
+ message: "Reminder sent to their account email address.",
332
+ };
333
+ }),
334
+ };
335
+ };
336
+
337
+ sendRemindersSpy = jest
338
+ .spyOn(OnCallSetupReminderService, "sendSetupReminders")
339
+ .mockImplementation(echoAllSent as never);
340
+ });
341
+
342
+ describe("OnCallReadinessAPI POST /send-setup-reminder - registration", () => {
343
+ test("the route is registered as a POST behind the user middleware", () => {
344
+ const route: RegisteredRoute = routeFor(REMINDER_ROUTE);
345
+
346
+ expect(route.method).toBe("POST");
347
+ expect(route.middleware).toBe(UserMiddleware.getUserMiddleware);
348
+ });
349
+
350
+ test("the readiness router still exposes it alongside its three reads", () => {
351
+ /*
352
+ * A guard against the write being registered on some other router by
353
+ * accident: it has to be reachable at the same prefix as the reads it
354
+ * belongs with, or the dashboard's one base URL cannot reach both.
355
+ */
356
+ const uris: Array<string> = (
357
+ mockRouter.routes as unknown as Array<RegisteredRoute>
358
+ ).map((route: RegisteredRoute): string => {
359
+ return route.uri;
360
+ });
361
+
362
+ expect(uris).toContain("/on-call-readiness/project");
363
+ expect(uris).toContain(REMINDER_ROUTE);
364
+ });
365
+ });
366
+
367
+ describe("OnCallReadinessAPI POST /send-setup-reminder - who may call it", () => {
368
+ test("an anonymous caller is refused and nothing is sent", async () => {
369
+ propsSpy.mockResolvedValue({
370
+ tenantId: projectId,
371
+ userTenantAccessPermission: {},
372
+ } as never);
373
+
374
+ const result: RouteCallResult = await callRoute({
375
+ userIds: [userA.toString()],
376
+ });
377
+
378
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
379
+ expect((result.thrownToNext as NotAuthorizedException).message).toBe(
380
+ REFUSAL,
381
+ );
382
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
383
+ });
384
+
385
+ test("a member of a different project is refused and nothing is sent", async () => {
386
+ // The header names THIS project; the caller's permissions name another.
387
+ propsSpy.mockResolvedValue({
388
+ ...buildOnCallAdminProps({
389
+ projectId: otherProjectId,
390
+ userId: callerUserId,
391
+ }),
392
+ tenantId: projectId,
393
+ } as never);
394
+
395
+ const result: RouteCallResult = await callRoute({
396
+ userIds: [userA.toString()],
397
+ });
398
+
399
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
400
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
401
+ });
402
+
403
+ test("a request with no tenant header is refused before the body is even parsed", async () => {
404
+ propsSpy.mockResolvedValue({} as never);
405
+
406
+ const result: RouteCallResult = await callRoute({
407
+ userIds: ["obviously-not-a-uuid"],
408
+ });
409
+
410
+ /*
411
+ * The refusal must be the AUTHORISATION one, not the body-validation one:
412
+ * if the body were parsed first, a caller could probe validation behaviour
413
+ * without being authorised at all.
414
+ */
415
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
416
+ expect((result.thrownToNext as BadDataException).message).toContain(
417
+ "Project ID is required",
418
+ );
419
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
420
+ });
421
+ });
422
+
423
+ /*
424
+ * MEMBERSHIP IS NOT ENOUGH.
425
+ *
426
+ * This block is the whole reason the route's authorisation is not a copy of its
427
+ * read siblings'. Every caller below is a genuine, logged-in member of the right
428
+ * project - assertAuthenticatedProjectMember admits all of them - and the
429
+ * question each test asks is whether they may make the product send branded mail
430
+ * to other people in that project's name.
431
+ *
432
+ * The refusal message is asserted to be the router's single refusal sentence, so
433
+ * "you lack the permission" cannot be told apart from "you are not in this
434
+ * project": a caller who could distinguish them could map out other projects'
435
+ * membership from the outside.
436
+ */
437
+ describe("OnCallReadinessAPI POST /send-setup-reminder - membership is not enough", () => {
438
+ test("an ordinary project member cannot make the product mail their colleagues", async () => {
439
+ propsSpy.mockResolvedValue(
440
+ buildMemberProps({ projectId: projectId, userId: callerUserId }) as never,
441
+ );
442
+
443
+ const result: RouteCallResult = await callRoute({
444
+ userIds: [userA.toString()],
445
+ });
446
+
447
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
448
+ expect((result.thrownToNext as NotAuthorizedException).message).toBe(
449
+ REFUSAL,
450
+ );
451
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
452
+ });
453
+
454
+ test("a read-only viewer is refused", async () => {
455
+ /*
456
+ * The case that made this change necessary. A Viewer can open the readiness
457
+ * page and read every gap on it; that is a disclosure decision already made.
458
+ * It is not a decision to let them mail anybody.
459
+ */
460
+ propsSpy.mockResolvedValue(
461
+ buildProps({
462
+ projectId: projectId,
463
+ userId: callerUserId,
464
+ permissions: [Permission.Viewer],
465
+ }) as never,
466
+ );
467
+
468
+ const result: RouteCallResult = await callRoute({
469
+ userIds: [userA.toString()],
470
+ });
471
+
472
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
473
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
474
+ });
475
+
476
+ test("the on-call tiers below Admin are refused", async () => {
477
+ /*
478
+ * OnCallMember and OnCallViewer are held by people who are ON call, which is
479
+ * the population this endpoint mails. Being a recipient of the reminder is
480
+ * not standing to send it.
481
+ */
482
+ for (const permission of [
483
+ Permission.OnCallMember,
484
+ Permission.OnCallViewer,
485
+ ]) {
486
+ jest.clearAllMocks();
487
+
488
+ propsSpy.mockResolvedValue(
489
+ buildProps({
490
+ projectId: projectId,
491
+ userId: callerUserId,
492
+ permissions: [Permission.ProjectMember, permission],
493
+ }) as never,
494
+ );
495
+
496
+ const result: RouteCallResult = await callRoute({
497
+ userIds: [userA.toString()],
498
+ });
499
+
500
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
501
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
502
+ }
503
+ });
504
+
505
+ test("each permission that means 'manages on-call here' is admitted", async () => {
506
+ /*
507
+ * All four are listed rather than just the granular one, and the reason is
508
+ * migration rather than generosity: OneUptime's teams carry ROLES, so a
509
+ * project that predates this feature has an owner holding ProjectOwner and
510
+ * no granular permission at all. A gate that named only
511
+ * EditProjectOnCallDutyPolicy would lock every existing project's owner out
512
+ * of the button on their own readiness page.
513
+ */
514
+ const admitted: Array<Permission> = [
515
+ Permission.ProjectOwner,
516
+ Permission.ProjectAdmin,
517
+ Permission.OnCallAdmin,
518
+ Permission.EditProjectOnCallDutyPolicy,
519
+ ];
520
+
521
+ for (const permission of admitted) {
522
+ jest.clearAllMocks();
523
+
524
+ propsSpy.mockResolvedValue(
525
+ buildProps({
526
+ projectId: projectId,
527
+ userId: callerUserId,
528
+ permissions: [permission],
529
+ }) as never,
530
+ );
531
+
532
+ const result: RouteCallResult = await callRoute({
533
+ userIds: [userA.toString()],
534
+ });
535
+
536
+ expect(result.thrownToNext).toBeUndefined();
537
+ expect(sendRemindersSpy).toHaveBeenCalledTimes(1);
538
+ }
539
+ });
540
+
541
+ test("a BLOCKED permission is not read as a granted one", async () => {
542
+ /*
543
+ * The dictionary these permissions live in holds grants and denials in one
544
+ * array, told apart only by isBlockPermission. Reading it raw would count a
545
+ * team's explicit "block ProjectAdmin" as a grant of ProjectAdmin - so the
546
+ * admin action that RESTRICTS a team would be the thing that handed it a
547
+ * mail-sending endpoint.
548
+ */
549
+ propsSpy.mockResolvedValue(
550
+ buildProps({
551
+ projectId: projectId,
552
+ userId: callerUserId,
553
+ permissions: [Permission.ProjectMember],
554
+ blockedPermissions: [
555
+ Permission.ProjectAdmin,
556
+ Permission.OnCallAdmin,
557
+ Permission.EditProjectOnCallDutyPolicy,
558
+ ],
559
+ }) as never,
560
+ );
561
+
562
+ const result: RouteCallResult = await callRoute({
563
+ userIds: [userA.toString()],
564
+ });
565
+
566
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
567
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
568
+ });
569
+
570
+ test("the permission held must be held IN THIS PROJECT", async () => {
571
+ /*
572
+ * An on-call admin of project B, sending project A's tenantid header. The
573
+ * membership gate refuses this first; the assertion that matters is that
574
+ * nothing was sent, because the permission read is keyed on the tenant and
575
+ * would otherwise be reading somebody else's project's grants.
576
+ */
577
+ const props: DatabaseCommonInteractionProps = buildOnCallAdminProps({
578
+ projectId: otherProjectId,
579
+ userId: callerUserId,
580
+ });
581
+
582
+ propsSpy.mockResolvedValue({
583
+ ...props,
584
+ tenantId: projectId,
585
+ } as never);
586
+
587
+ const result: RouteCallResult = await callRoute({
588
+ userIds: [userA.toString()],
589
+ });
590
+
591
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
592
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
593
+ });
594
+
595
+ test("the refusal happens before the body is validated", async () => {
596
+ /*
597
+ * An unauthorised caller must not be able to learn what this endpoint
598
+ * accepts by watching which complaint comes back. A body that is nonsense
599
+ * AND a caller who may not send has to fail as a permission problem.
600
+ */
601
+ propsSpy.mockResolvedValue(
602
+ buildMemberProps({ projectId: projectId, userId: callerUserId }) as never,
603
+ );
604
+
605
+ const result: RouteCallResult = await callRoute({
606
+ userIds: "not-even-an-array",
607
+ } as unknown as JSONObject);
608
+
609
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
610
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
611
+ });
612
+
613
+ test("the GET routes are deliberately NOT tightened", async () => {
614
+ /*
615
+ * Only the write moved. The reads exist so that somebody inside the project
616
+ * can see who cannot be paged, and requiring on-call administration to LOOK
617
+ * would take the feature away from the people it was built to inform.
618
+ */
619
+ const readRoutes: Array<RegisteredRoute> = (
620
+ mockRouter.routes as unknown as Array<RegisteredRoute>
621
+ ).filter((route: RegisteredRoute): boolean => {
622
+ return route.method === "GET";
623
+ });
624
+
625
+ expect(readRoutes.length).toBe(3);
626
+
627
+ propsSpy.mockResolvedValue(
628
+ buildMemberProps({ projectId: projectId, userId: callerUserId }) as never,
629
+ );
630
+
631
+ const summarySpy: jest.SpyInstance = jest
632
+ .spyOn(OnCallReadinessService, "getReadinessForProject")
633
+ .mockResolvedValue({
634
+ projectId: projectId,
635
+ readyCount: 0,
636
+ partiallyReadyCount: 0,
637
+ notReachableCount: 0,
638
+ isFallbackEnabled: true,
639
+ isTruncated: false,
640
+ users: [],
641
+ } as never);
642
+
643
+ const projectRoute: RegisteredRoute | undefined = readRoutes.find(
644
+ (route: RegisteredRoute): boolean => {
645
+ return route.uri === "/on-call-readiness/project";
646
+ },
647
+ );
648
+
649
+ const req: ExpressRequest = {
650
+ params: {},
651
+ query: {},
652
+ body: {},
653
+ headers: {},
654
+ } as unknown as ExpressRequest;
655
+
656
+ const res: ExpressResponse = {
657
+ send: jest.fn(),
658
+ json: jest.fn(),
659
+ status: jest.fn().mockReturnThis(),
660
+ } as unknown as ExpressResponse;
661
+
662
+ const next: jest.Mock = jest.fn();
663
+
664
+ await projectRoute!.handlerFunction(
665
+ req,
666
+ res,
667
+ next as unknown as NextFunction,
668
+ );
669
+
670
+ // The bare member got their answer. Nothing was refused.
671
+ expect(next).not.toHaveBeenCalled();
672
+ expect(summarySpy).toHaveBeenCalledTimes(1);
673
+
674
+ summarySpy.mockRestore();
675
+ });
676
+ });
677
+
678
+ describe("OnCallReadinessAPI POST /send-setup-reminder - the body is never trusted", () => {
679
+ test("the project comes from the authenticated tenant, not from the body", async () => {
680
+ await callRoute({
681
+ userIds: [userA.toString()],
682
+ // A caller trying to redirect the send at somebody else's project.
683
+ projectId: otherProjectId.toString(),
684
+ });
685
+
686
+ expect(serviceCall().projectId.toString()).toBe(projectId.toString());
687
+ });
688
+
689
+ test("a missing userIds field is refused", async () => {
690
+ const result: RouteCallResult = await callRoute({});
691
+
692
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
693
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
694
+ });
695
+
696
+ test("an absent body is refused rather than crashing the handler", async () => {
697
+ const result: RouteCallResult = await callRoute(undefined);
698
+
699
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
700
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
701
+ });
702
+
703
+ test("a userIds that is not an array is refused", async () => {
704
+ const result: RouteCallResult = await callRoute({
705
+ userIds: userA.toString(),
706
+ } as unknown as JSONObject);
707
+
708
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
709
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
710
+ });
711
+
712
+ test("a non-string entry is refused", async () => {
713
+ const result: RouteCallResult = await callRoute({
714
+ userIds: [userA.toString(), 42],
715
+ } as unknown as JSONObject);
716
+
717
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
718
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
719
+ });
720
+
721
+ test("an entry that is not a UUID is refused rather than passed through as an id", async () => {
722
+ /*
723
+ * ObjectID's constructor accepts any string. Without this check, arbitrary
724
+ * caller text reaches the mail-sending service and comes back looking like
725
+ * an ordinary stale id.
726
+ */
727
+ const result: RouteCallResult = await callRoute({
728
+ userIds: ["'; DROP TABLE users; --"],
729
+ });
730
+
731
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
732
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
733
+ });
734
+
735
+ test("one bad id refuses the whole request rather than silently dropping it", async () => {
736
+ /*
737
+ * Sending to "everyone except the one we could not parse" would be a
738
+ * partial action reported as a complete one.
739
+ */
740
+ const result: RouteCallResult = await callRoute({
741
+ userIds: [userA.toString(), "not-a-uuid", userB.toString()],
742
+ });
743
+
744
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
745
+ expect(sendRemindersSpy).not.toHaveBeenCalled();
746
+ });
747
+
748
+ test("well-formed ids reach the service in the order they were sent", async () => {
749
+ await callRoute({
750
+ userIds: [userA.toString(), userB.toString()],
751
+ });
752
+
753
+ expect(
754
+ serviceCall().userIds.map((id: ObjectID): string => {
755
+ return id.toString();
756
+ }),
757
+ ).toEqual([userA.toString(), userB.toString()]);
758
+ });
759
+
760
+ test("an empty list is handed to the service, which owns that rule", async () => {
761
+ /*
762
+ * Deliberately NOT rejected here. The "select at least one" rule belongs to
763
+ * the service so it holds for every caller, and duplicating it at the route
764
+ * would let the two drift into disagreeing about what an empty request
765
+ * means.
766
+ */
767
+ sendRemindersSpy.mockRejectedValue(
768
+ new BadDataException(
769
+ "Select at least one responder to send a setup reminder to.",
770
+ ) as never,
771
+ );
772
+
773
+ const result: RouteCallResult = await callRoute({ userIds: [] });
774
+
775
+ expect(sendRemindersSpy).toHaveBeenCalled();
776
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
777
+ });
778
+ });
779
+
780
+ describe("OnCallReadinessAPI POST /send-setup-reminder - what comes back", () => {
781
+ test("the payload carries the counts and one entry per user", async () => {
782
+ sendRemindersSpy.mockResolvedValue({
783
+ projectId: projectId,
784
+ requestedCount: 2,
785
+ sentCount: 1,
786
+ skippedCount: 1,
787
+ failedCount: 0,
788
+ results: [
789
+ {
790
+ userId: userA,
791
+ outcome: SetupReminderOutcome.Sent,
792
+ message: "Reminder sent to their account email address.",
793
+ },
794
+ {
795
+ userId: userB,
796
+ outcome: SetupReminderOutcome.SkippedThrottled,
797
+ message: "Already reminded in the last 24 hours.",
798
+ },
799
+ ],
800
+ } as never);
801
+
802
+ await callRoute({
803
+ userIds: [userA.toString(), userB.toString()],
804
+ });
805
+
806
+ const payload: Record<string, unknown> = jsonPayload();
807
+
808
+ expect(payload["requestedCount"]).toBe(2);
809
+ expect(payload["sentCount"]).toBe(1);
810
+ expect(payload["skippedCount"]).toBe(1);
811
+ expect(payload["failedCount"]).toBe(0);
812
+ expect(payload["results"]).toHaveLength(2);
813
+ });
814
+
815
+ test("every id crosses the wire as a plain string, never as an ObjectID envelope", async () => {
816
+ /*
817
+ * ObjectID.toJSON() serialises to { _type: "ObjectID", value: "..." }, which
818
+ * is right for model payloads and wrong for a hand-rolled body. The browser
819
+ * matches these against row ids it holds as plain strings.
820
+ */
821
+ await callRoute({ userIds: [userA.toString()] });
822
+
823
+ const payload: Record<string, unknown> = jsonPayload();
824
+ const results: Array<Record<string, unknown>> = payload["results"] as Array<
825
+ Record<string, unknown>
826
+ >;
827
+
828
+ expect(typeof payload["projectId"]).toBe("string");
829
+ expect(payload["projectId"]).toBe(projectId.toString());
830
+ expect(typeof results[0]!["userId"]).toBe("string");
831
+ expect(results[0]!["userId"]).toBe(userA.toString());
832
+ });
833
+
834
+ test("the outcome is carried as its own value, not flattened to a boolean", async () => {
835
+ sendRemindersSpy.mockResolvedValue({
836
+ projectId: projectId,
837
+ requestedCount: 1,
838
+ sentCount: 0,
839
+ skippedCount: 0,
840
+ failedCount: 1,
841
+ results: [
842
+ {
843
+ userId: userA,
844
+ outcome: SetupReminderOutcome.Failed,
845
+ message: "We could not send this reminder.",
846
+ },
847
+ ],
848
+ } as never);
849
+
850
+ await callRoute({ userIds: [userA.toString()] });
851
+
852
+ const results: Array<Record<string, unknown>> = jsonPayload()[
853
+ "results"
854
+ ] as Array<Record<string, unknown>>;
855
+
856
+ expect(results[0]!["outcome"]).toBe(SetupReminderOutcome.Failed);
857
+ expect(results[0]!["message"]).toBe("We could not send this reminder.");
858
+ });
859
+
860
+ test("no email address is echoed back in the response", async () => {
861
+ sendRemindersSpy.mockResolvedValue({
862
+ projectId: projectId,
863
+ requestedCount: 1,
864
+ sentCount: 1,
865
+ skippedCount: 0,
866
+ failedCount: 0,
867
+ results: [
868
+ {
869
+ userId: userA,
870
+ outcome: SetupReminderOutcome.Sent,
871
+ message: "Reminder sent to their account email address.",
872
+ },
873
+ ],
874
+ } as never);
875
+
876
+ await callRoute({ userIds: [userA.toString()] });
877
+
878
+ const results: Array<Record<string, unknown>> = jsonPayload()[
879
+ "results"
880
+ ] as Array<Record<string, unknown>>;
881
+
882
+ expect(Object.keys(results[0]!).sort()).toEqual([
883
+ "message",
884
+ "outcome",
885
+ "userId",
886
+ ]);
887
+ });
888
+
889
+ test("a service failure is handed to the error middleware rather than answered as success", async () => {
890
+ sendRemindersSpy.mockRejectedValue(
891
+ new BadDataException("Project not found.") as never,
892
+ );
893
+
894
+ const result: RouteCallResult = await callRoute({
895
+ userIds: [userA.toString()],
896
+ });
897
+
898
+ expect(result.nextCallCount).toBe(1);
899
+ expect(result.thrownToNext).toBeInstanceOf(BadDataException);
900
+ expect(
901
+ (Response.sendJsonObjectResponse as unknown as jest.Mock).mock.calls,
902
+ ).toHaveLength(0);
903
+ });
904
+ });
905
+
906
+ /*
907
+ * Imported for its side effect: OnCallReadinessAPI registers its routes when the
908
+ * MODULE is evaluated (it exports a bare router, not a class), so the reference
909
+ * below is what guarantees the import is not elided as unused.
910
+ */
911
+ describe("OnCallReadinessAPI - the router under test", () => {
912
+ test("the module exports the router the routes were registered on", () => {
913
+ expect(OnCallReadinessAPI).toBe(mockRouter);
914
+ });
915
+ });