@oneuptime/common 12.0.7 → 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 (873) 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} +184 -9
  9. package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
  10. package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +33 -7
  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/WorkflowLog.ts +38 -0
  27. package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
  28. package/Server/API/AIChatAPI.ts +315 -1
  29. package/Server/API/DashboardAPI.ts +217 -1
  30. package/Server/API/OnCallReadinessAPI.ts +841 -0
  31. package/Server/API/TeamComplianceAPI.ts +69 -17
  32. package/Server/API/TelemetryAPI.ts +220 -12
  33. package/Server/API/UserAPI.ts +16 -1
  34. package/Server/EnvironmentConfig.ts +52 -0
  35. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
  47. package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
  48. package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
  49. package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
  50. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +24 -0
  51. package/Server/Infrastructure/Queue.ts +78 -13
  52. package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
  53. package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
  54. package/Server/Services/AIService.ts +7 -0
  55. package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
  56. package/Server/Services/AlertSeverityService.ts +63 -0
  57. package/Server/Services/DashboardService.ts +9 -10
  58. package/Server/Services/DatabaseService.ts +32 -2
  59. package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
  60. package/Server/Services/IncidentSeverityService.ts +76 -0
  61. package/Server/Services/Index.ts +12 -4
  62. package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
  63. package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +7 -4
  64. package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +203 -21
  65. package/Server/Services/LogAggregationService.ts +45 -8
  66. package/Server/Services/MetricAggregationService.ts +121 -0
  67. package/Server/Services/MetricService.ts +7 -7
  68. package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
  69. package/Server/Services/NetworkDeviceLinkService.ts +84 -0
  70. package/Server/Services/NetworkDeviceService.ts +140 -0
  71. package/Server/Services/NetworkSiteService.ts +77 -25
  72. package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
  73. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
  74. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
  75. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
  76. package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
  77. package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
  78. package/Server/Services/OnCallReadinessService.ts +2803 -0
  79. package/Server/Services/OnCallSetupReminderService.ts +955 -0
  80. package/Server/Services/ProfileAggregationService.ts +123 -0
  81. package/Server/Services/StatusPageService.ts +9 -10
  82. package/Server/Services/TeamComplianceService.ts +429 -252
  83. package/Server/Services/UserCallService.ts +26 -1
  84. package/Server/Services/UserEmailService.ts +26 -1
  85. package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
  86. package/Server/Services/UserNotificationRuleService.ts +3812 -333
  87. package/Server/Services/UserOnCallLogService.ts +561 -48
  88. package/Server/Services/UserPushService.ts +29 -0
  89. package/Server/Services/UserService.ts +11 -0
  90. package/Server/Services/UserSmsService.ts +26 -1
  91. package/Server/Services/UserTelegramService.ts +24 -1
  92. package/Server/Services/UserWebhookService.ts +28 -1
  93. package/Server/Services/UserWhatsAppService.ts +24 -1
  94. package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
  95. package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
  96. package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
  97. package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
  98. package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
  99. package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
  100. package/Server/Types/Workflow/Components/API/Get.ts +1 -1
  101. package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
  102. package/Server/Types/Workflow/Components/API/Post.ts +1 -1
  103. package/Server/Types/Workflow/Components/API/Put.ts +1 -1
  104. package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
  105. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
  106. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
  107. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
  108. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
  109. package/Server/Types/Workflow/Components/Email.ts +25 -7
  110. package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
  111. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
  112. package/Server/Types/Workflow/TriggerCode.ts +12 -0
  113. package/Server/Types/Workflow/Workflow.ts +5 -0
  114. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
  115. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
  116. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
  117. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
  118. package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
  119. package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
  120. package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
  121. package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
  122. package/Server/Utils/AI/Toolbox/Index.ts +48 -0
  123. package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
  124. package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
  125. package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
  126. package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
  127. package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
  128. package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
  129. package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
  130. package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
  131. package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
  132. package/Server/Utils/ClientIp.ts +221 -0
  133. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
  134. package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
  135. package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
  136. package/Server/Utils/Express.ts +12 -17
  137. package/Server/Utils/LLM/LLMService.ts +85 -8
  138. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
  139. package/Server/Utils/SSRFProtection.ts +98 -23
  140. package/Server/Utils/StartServer.ts +12 -3
  141. package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
  142. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
  143. package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
  144. package/Server/Utils/VM/VMAPI.ts +56 -8
  145. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
  146. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
  147. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
  148. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
  149. package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
  150. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
  151. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
  152. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
  153. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
  154. package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
  155. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
  156. package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
  157. package/Tests/Models/InventoryItemModel.test.ts +174 -0
  158. package/Tests/Models/InventoryItemNaming.test.ts +302 -0
  159. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
  160. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
  161. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
  162. package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
  163. package/Tests/Server/API/Helpers.ts +24 -15
  164. package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
  165. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
  166. package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
  167. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
  168. package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
  169. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
  170. package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
  171. package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
  172. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
  173. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
  174. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
  175. package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
  176. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
  177. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +248 -0
  178. package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
  179. package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
  180. package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
  181. package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
  182. package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
  183. package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
  184. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
  185. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
  186. package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
  187. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
  188. package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
  189. package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
  190. package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
  191. package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
  192. package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
  193. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
  194. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
  195. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
  196. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
  197. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
  198. package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
  199. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
  200. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
  201. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
  202. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
  203. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
  204. package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
  205. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
  206. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
  207. package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
  208. package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
  209. package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
  210. package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
  211. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
  212. package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
  213. package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
  214. package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
  215. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
  216. package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
  217. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
  218. package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
  219. package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
  220. package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
  221. package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
  222. package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
  223. package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
  224. package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
  225. package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
  226. package/Tests/Server/Utils/ClientIp.test.ts +438 -0
  227. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
  228. package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
  229. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
  230. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
  231. package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
  232. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
  233. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
  234. package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
  235. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
  236. package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
  237. package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
  238. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
  239. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
  240. package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
  241. package/Tests/Types/IP/IP.test.ts +263 -0
  242. package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
  243. package/Tests/Types/IP/IPv6.test.ts +12 -1
  244. package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
  245. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
  246. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
  247. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
  248. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
  249. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
  250. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
  251. package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
  252. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
  253. package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
  254. package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
  255. package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
  256. package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
  257. package/Tests/Types/Workflow/TemplateSyntax.test.ts +491 -0
  258. package/Tests/Types/Workflow/Templates.test.ts +1218 -0
  259. package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
  260. package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
  261. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
  262. package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
  263. package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
  264. package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
  265. package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
  266. package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
  267. package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
  268. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
  269. package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
  270. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
  271. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
  272. package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
  273. package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
  274. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
  275. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
  276. package/Tests/UI/Components/Workflow/GraphLint.test.ts +893 -0
  277. package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
  278. package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +522 -0
  279. package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +193 -0
  280. package/Tests/UI/Components/Workflow/ModelSchema.test.ts +388 -0
  281. package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +210 -0
  282. package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
  283. package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
  284. package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
  285. package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
  286. package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
  287. package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
  288. package/Tests/UI/EsbuildConfig.test.ts +607 -0
  289. package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
  290. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  291. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
  292. package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
  293. package/Tests/UI/Utils/Platform.test.ts +147 -0
  294. package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
  295. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
  296. package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
  297. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +727 -0
  298. package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
  299. package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
  300. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
  301. package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
  302. package/Tests/__mocks__/bullmq.js +55 -0
  303. package/Types/AI/AIChatMessageStatus.ts +8 -1
  304. package/Types/AI/AIChatTypes.ts +12 -0
  305. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
  306. package/Types/Exception/ExceptionCode.ts +2 -0
  307. package/Types/Exception/ServiceUnavailableException.ts +8 -0
  308. package/Types/Exception/TooManyRequestsException.ts +8 -0
  309. package/Types/IP/IP.ts +93 -47
  310. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +80 -3
  311. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
  312. package/Types/OnCallDutyPolicy/Layer.ts +203 -149
  313. package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
  314. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
  315. package/Types/Permission.ts +193 -0
  316. package/Types/SerializableObjectDictionary.ts +133 -39
  317. package/Types/Telemetry/EntityRelationshipType.ts +1 -1
  318. package/Types/Telemetry/EntitySource.ts +40 -0
  319. package/Types/Telemetry/EntityType.ts +28 -1
  320. package/Types/Telemetry/EntityTypeGroups.ts +65 -0
  321. package/Types/Workflow/Component.ts +29 -0
  322. package/Types/Workflow/Components/API.ts +35 -0
  323. package/Types/Workflow/Components/BaseModel.ts +77 -27
  324. package/Types/Workflow/Components/Discord.ts +1 -0
  325. package/Types/Workflow/Components/Email.ts +12 -3
  326. package/Types/Workflow/Components/JavaScript.ts +7 -0
  327. package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
  328. package/Types/Workflow/Components/Slack.ts +1 -0
  329. package/Types/Workflow/Components/Telegram.ts +1 -0
  330. package/Types/Workflow/StepTrace.ts +181 -0
  331. package/Types/Workflow/TemplateSyntax.ts +543 -0
  332. package/Types/Workflow/Templates.ts +2368 -0
  333. package/UI/Components/Calendar/Calendar.css +43 -0
  334. package/UI/Components/Calendar/Calendar.tsx +8 -0
  335. package/UI/Components/Card/Card.tsx +2 -2
  336. package/UI/Components/Checkbox/Checkbox.tsx +16 -0
  337. package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
  338. package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
  339. package/UI/Components/Dictionary/Dictionary.tsx +71 -15
  340. package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
  341. package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
  342. package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
  343. package/UI/Components/Forms/Types/Field.ts +7 -0
  344. package/UI/Components/Forms/Validation.ts +63 -1
  345. package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
  346. package/UI/Components/Input/Input.tsx +32 -3
  347. package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
  348. package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
  349. package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
  350. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
  351. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
  352. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
  353. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
  354. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
  355. package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
  356. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
  357. package/UI/Components/LogsViewer/types.ts +23 -0
  358. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
  359. package/UI/Components/Modal/Modal.tsx +37 -4
  360. package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
  361. package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
  362. package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
  363. package/UI/Components/Table/Table.tsx +49 -16
  364. package/UI/Components/Table/TableBody.tsx +53 -28
  365. package/UI/Components/Table/TableHeader.tsx +13 -0
  366. package/UI/Components/Table/TableRow.tsx +58 -26
  367. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
  368. package/UI/Components/Workflow/ArgumentsForm.tsx +341 -8
  369. package/UI/Components/Workflow/Component.tsx +28 -26
  370. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
  371. package/UI/Components/Workflow/ComponentSettingsModal.tsx +79 -9
  372. package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
  373. package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
  374. package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
  375. package/UI/Components/Workflow/GraphLint.ts +628 -0
  376. package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
  377. package/UI/Components/Workflow/ModelColumnEditor.tsx +528 -0
  378. package/UI/Components/Workflow/ModelSchema.ts +278 -0
  379. package/UI/Components/Workflow/RunForm.tsx +41 -7
  380. package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
  381. package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
  382. package/UI/Components/Workflow/UseRunWatch.ts +212 -0
  383. package/UI/Components/Workflow/Utils.ts +93 -1
  384. package/UI/Components/Workflow/VariableModal.tsx +6 -2
  385. package/UI/Components/Workflow/Workflow.tsx +126 -7
  386. package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
  387. package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
  388. package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
  389. package/UI/Types/LayeredDismissal.ts +31 -0
  390. package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
  391. package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
  392. package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
  393. package/UI/Utils/Platform.ts +149 -0
  394. package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
  395. package/UI/esbuild-config.js +22 -1
  396. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
  397. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
  398. package/Utils/Monitor/NetworkTopologyUtil.ts +917 -155
  399. package/Utils/Telemetry/CrossSignalScope.ts +502 -0
  400. package/Utils/Telemetry/EntityKey.ts +71 -5
  401. package/Utils/Telemetry/EntityRelationship.ts +1 -1
  402. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
  403. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
  404. package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
  405. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  406. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
  407. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
  408. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
  409. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  410. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
  411. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  412. package/build/dist/Models/DatabaseModels/Index.js +12 -4
  413. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  414. package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +212 -29
  415. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
  416. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
  417. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
  418. package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +52 -25
  419. package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
  420. package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
  421. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  422. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
  423. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
  424. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
  425. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
  426. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
  427. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
  428. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
  429. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
  430. package/build/dist/Models/DatabaseModels/Project.js +80 -0
  431. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  432. package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
  433. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  434. package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
  435. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  436. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
  437. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  438. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
  439. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  440. package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
  441. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  442. package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
  443. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  444. package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
  445. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
  446. package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
  447. package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
  448. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
  449. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  450. package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
  451. package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
  452. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
  453. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  454. package/build/dist/Server/API/AIChatAPI.js +237 -1
  455. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  456. package/build/dist/Server/API/DashboardAPI.js +165 -13
  457. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  458. package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
  459. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
  460. package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
  461. package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
  462. package/build/dist/Server/API/TelemetryAPI.js +129 -18
  463. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  464. package/build/dist/Server/API/UserAPI.js +16 -1
  465. package/build/dist/Server/API/UserAPI.js.map +1 -1
  466. package/build/dist/Server/EnvironmentConfig.js +45 -0
  467. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  468. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
  469. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  470. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
  471. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
  472. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
  473. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
  474. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
  475. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
  476. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
  477. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
  478. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
  479. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
  480. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
  481. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
  482. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
  483. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
  484. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
  485. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
  486. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
  487. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
  488. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
  489. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
  490. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
  491. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
  492. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
  493. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
  494. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
  495. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +24 -0
  496. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  497. package/build/dist/Server/Infrastructure/Queue.js +72 -13
  498. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  499. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
  500. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  501. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
  502. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
  503. package/build/dist/Server/Services/AIService.js +1 -0
  504. package/build/dist/Server/Services/AIService.js.map +1 -1
  505. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
  506. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
  507. package/build/dist/Server/Services/AlertSeverityService.js +54 -0
  508. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  509. package/build/dist/Server/Services/DashboardService.js +10 -9
  510. package/build/dist/Server/Services/DashboardService.js.map +1 -1
  511. package/build/dist/Server/Services/DatabaseService.js +24 -2
  512. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  513. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
  514. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  515. package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
  516. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  517. package/build/dist/Server/Services/Index.js +12 -4
  518. package/build/dist/Server/Services/Index.js.map +1 -1
  519. package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
  520. package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
  521. package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +9 -6
  522. package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
  523. package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +158 -23
  524. package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
  525. package/build/dist/Server/Services/LogAggregationService.js +27 -8
  526. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  527. package/build/dist/Server/Services/MetricAggregationService.js +80 -0
  528. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  529. package/build/dist/Server/Services/MetricService.js +6 -6
  530. package/build/dist/Server/Services/MetricService.js.map +1 -1
  531. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
  532. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
  533. package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
  534. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
  535. package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
  536. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  537. package/build/dist/Server/Services/NetworkSiteService.js +53 -13
  538. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  539. package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
  540. package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
  541. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
  542. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  543. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
  544. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  545. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
  546. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  547. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
  548. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  549. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
  550. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
  551. package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
  552. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
  553. package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
  554. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
  555. package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
  556. package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
  557. package/build/dist/Server/Services/StatusPageService.js +11 -10
  558. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  559. package/build/dist/Server/Services/TeamComplianceService.js +312 -160
  560. package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
  561. package/build/dist/Server/Services/UserCallService.js +24 -1
  562. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  563. package/build/dist/Server/Services/UserEmailService.js +24 -1
  564. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  565. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
  566. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
  567. package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
  568. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  569. package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
  570. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  571. package/build/dist/Server/Services/UserPushService.js +26 -0
  572. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  573. package/build/dist/Server/Services/UserService.js +10 -0
  574. package/build/dist/Server/Services/UserService.js.map +1 -1
  575. package/build/dist/Server/Services/UserSmsService.js +24 -1
  576. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  577. package/build/dist/Server/Services/UserTelegramService.js +22 -1
  578. package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
  579. package/build/dist/Server/Services/UserWebhookService.js +25 -1
  580. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  581. package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
  582. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  583. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
  584. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  585. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
  586. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  587. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
  588. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
  589. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
  590. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
  591. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
  592. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  593. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
  594. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  595. package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
  596. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  597. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
  598. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  599. package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
  600. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  601. package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
  602. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  603. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
  604. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  605. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
  606. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  607. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
  608. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  609. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
  610. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
  611. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
  612. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  613. package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
  614. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  615. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
  616. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  617. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
  618. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  619. package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
  620. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
  621. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  622. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
  623. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  624. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
  625. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  626. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
  627. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  628. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
  629. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  630. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
  631. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
  632. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
  633. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  634. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
  635. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
  636. package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
  637. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  638. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
  639. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
  640. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
  641. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
  642. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
  643. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
  644. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
  645. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
  646. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
  647. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
  648. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
  649. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
  650. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
  651. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
  652. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
  653. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
  654. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
  655. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
  656. package/build/dist/Server/Utils/ClientIp.js +137 -0
  657. package/build/dist/Server/Utils/ClientIp.js.map +1 -0
  658. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
  659. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  660. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
  661. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
  662. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
  663. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
  664. package/build/dist/Server/Utils/Express.js +12 -12
  665. package/build/dist/Server/Utils/Express.js.map +1 -1
  666. package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
  667. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  668. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
  669. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  670. package/build/dist/Server/Utils/SSRFProtection.js +82 -21
  671. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  672. package/build/dist/Server/Utils/StartServer.js +12 -4
  673. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  674. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
  675. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  676. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
  677. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
  678. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
  679. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  680. package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
  681. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  682. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
  683. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  684. package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
  685. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
  686. package/build/dist/Types/AI/AIChatTypes.js +12 -0
  687. package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
  688. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
  689. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
  690. package/build/dist/Types/Exception/ExceptionCode.js +2 -0
  691. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  692. package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
  693. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
  694. package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
  695. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
  696. package/build/dist/Types/IP/IP.js +87 -43
  697. package/build/dist/Types/IP/IP.js.map +1 -1
  698. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
  699. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
  700. package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
  701. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  702. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
  703. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
  704. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
  705. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  706. package/build/dist/Types/Permission.js +174 -0
  707. package/build/dist/Types/Permission.js.map +1 -1
  708. package/build/dist/Types/SerializableObjectDictionary.js +133 -39
  709. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  710. package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
  711. package/build/dist/Types/Telemetry/EntitySource.js +39 -0
  712. package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
  713. package/build/dist/Types/Telemetry/EntityType.js +28 -1
  714. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  715. package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
  716. package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
  717. package/build/dist/Types/Workflow/Component.js +21 -0
  718. package/build/dist/Types/Workflow/Component.js.map +1 -1
  719. package/build/dist/Types/Workflow/Components/API.js +35 -0
  720. package/build/dist/Types/Workflow/Components/API.js.map +1 -1
  721. package/build/dist/Types/Workflow/Components/BaseModel.js +68 -26
  722. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  723. package/build/dist/Types/Workflow/Components/Discord.js +1 -0
  724. package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
  725. package/build/dist/Types/Workflow/Components/Email.js +12 -3
  726. package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
  727. package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
  728. package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
  729. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
  730. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
  731. package/build/dist/Types/Workflow/Components/Slack.js +1 -0
  732. package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
  733. package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
  734. package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
  735. package/build/dist/Types/Workflow/StepTrace.js +104 -0
  736. package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
  737. package/build/dist/Types/Workflow/TemplateSyntax.js +338 -0
  738. package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
  739. package/build/dist/Types/Workflow/Templates.js +2111 -0
  740. package/build/dist/Types/Workflow/Templates.js.map +1 -0
  741. package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
  742. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  743. package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
  744. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  745. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
  746. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
  747. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
  748. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
  749. package/build/dist/UI/Components/Dictionary/Dictionary.js +47 -17
  750. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  751. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  752. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
  753. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
  754. package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
  755. package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
  756. package/build/dist/UI/Components/Forms/Validation.js +44 -0
  757. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  758. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
  759. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  760. package/build/dist/UI/Components/Input/Input.js +13 -4
  761. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  762. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
  763. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
  764. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
  765. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
  766. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
  767. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  768. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
  769. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  770. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
  771. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  772. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
  773. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  774. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
  775. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  776. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
  777. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  778. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
  779. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  780. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
  781. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  782. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  783. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
  784. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  785. package/build/dist/UI/Components/Modal/Modal.js +31 -5
  786. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  787. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
  788. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  789. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
  790. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
  791. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
  792. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
  793. package/build/dist/UI/Components/Table/Table.js +27 -15
  794. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  795. package/build/dist/UI/Components/Table/TableBody.js +24 -18
  796. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  797. package/build/dist/UI/Components/Table/TableHeader.js +9 -1
  798. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  799. package/build/dist/UI/Components/Table/TableRow.js +29 -21
  800. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  801. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
  802. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  803. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +261 -14
  804. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  805. package/build/dist/UI/Components/Workflow/Component.js +20 -19
  806. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  807. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
  808. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  809. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +35 -7
  810. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  811. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
  812. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
  813. package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
  814. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  815. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
  816. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  817. package/build/dist/UI/Components/Workflow/GraphLint.js +425 -0
  818. package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
  819. package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
  820. package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
  821. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +320 -0
  822. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
  823. package/build/dist/UI/Components/Workflow/ModelSchema.js +156 -0
  824. package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
  825. package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
  826. package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
  827. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
  828. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
  829. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
  830. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
  831. package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
  832. package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
  833. package/build/dist/UI/Components/Workflow/Utils.js +73 -1
  834. package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
  835. package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
  836. package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
  837. package/build/dist/UI/Components/Workflow/Workflow.js +92 -7
  838. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  839. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
  840. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
  841. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
  842. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
  843. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
  844. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
  845. package/build/dist/UI/Types/LayeredDismissal.js +21 -0
  846. package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
  847. package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
  848. package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
  849. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
  850. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
  851. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
  852. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  853. package/build/dist/UI/Utils/Platform.js +118 -0
  854. package/build/dist/UI/Utils/Platform.js.map +1 -0
  855. package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
  856. package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
  857. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
  858. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
  859. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
  860. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
  861. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +661 -126
  862. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  863. package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
  864. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
  865. package/build/dist/Utils/Telemetry/EntityKey.js +57 -5
  866. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  867. package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
  868. package/jest.config.json +1 -0
  869. package/package.json +1 -1
  870. package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
  871. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
  872. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
  873. package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
@@ -0,0 +1,1272 @@
1
+ import DatabaseConfig from "../../../Server/DatabaseConfig";
2
+ import GlobalCache from "../../../Server/Infrastructure/GlobalCache";
3
+ import MailService from "../../../Server/Services/MailService";
4
+ import OnCallReadinessService, {
5
+ ReadinessCoverageCell,
6
+ ReadinessStatus,
7
+ ResponderSource,
8
+ UserReadiness,
9
+ } from "../../../Server/Services/OnCallReadinessService";
10
+ import OnCallSetupReminderService, {
11
+ OnCallSetupReminderService as OnCallSetupReminderServiceClass,
12
+ SetupReminderOutcome,
13
+ SetupReminderResult,
14
+ SetupReminderUserResult,
15
+ SETUP_REMINDER_MAX_RECIPIENTS,
16
+ } from "../../../Server/Services/OnCallSetupReminderService";
17
+ import ProjectService from "../../../Server/Services/ProjectService";
18
+ import TeamMemberService from "../../../Server/Services/TeamMemberService";
19
+ import logger from "../../../Server/Utils/Logger";
20
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
21
+ import Protocol from "../../../Types/API/Protocol";
22
+ import URL from "../../../Types/API/URL";
23
+ import Hostname from "../../../Types/API/Hostname";
24
+ import BadDataException from "../../../Types/Exception/BadDataException";
25
+ import NotificationRuleType from "../../../Types/NotificationRule/NotificationRuleType";
26
+ import ObjectID from "../../../Types/ObjectID";
27
+ import { beforeEach, describe, expect, test } from "@jest/globals";
28
+
29
+ /*
30
+ * The setup reminder: the one action in the on-call readiness feature that
31
+ * leaves the product and lands in somebody's inbox.
32
+ *
33
+ * Its first cut was a button wired to a function that threw, which is why the
34
+ * bar for this file is not "does it call MailService". The failure modes that
35
+ * matter are all failures of HONESTY or of RESTRAINT, and each section below
36
+ * pins one of them:
37
+ *
38
+ * (A) NEVER TRUST THE BODY. User is a global model, so a user id posted by a
39
+ * member of project A is a perfectly valid id in project B. Without a
40
+ * membership re-check inside the sending service, this endpoint mails
41
+ * strangers about a project they have never heard of - with that
42
+ * project's name in the message.
43
+ *
44
+ * (B) NEVER CLAIM MORE THAN IS TRUE. A Ready responder gets no mail, because
45
+ * a mail telling somebody they are missing something they are not missing
46
+ * is a claim they can disprove in ten seconds - and the next mail from
47
+ * this sender might be a page. Likewise the "you are on an on-call
48
+ * rotation" sentence appears only for a responder some policy actually
49
+ * reaches.
50
+ *
51
+ * (C) NEVER REPORT SENT FOR A MAIL THAT DID NOT GO. MailService goes through
52
+ * API.post, which RESOLVES with an error response for a non-200 rather
53
+ * than throwing. A try/catch on its own therefore reports success for
54
+ * every mail the notification service refuses, which is the exact green
55
+ * tick over nothing this whole phase exists to remove.
56
+ *
57
+ * (D) THE THROTTLE IS A LOCK, NOT A NOTE. It is claimed atomically before the
58
+ * send (a check-then-send lets two concurrent clicks both send), and it
59
+ * is RELEASED when the send fails (or an SMTP blip silences a responder
60
+ * for a day).
61
+ *
62
+ * (E) THE ESCAPING. SimpleMessage.hbs renders the message body through a
63
+ * triple stache, so the escaping in this service is the only escaping
64
+ * there is between an admin-typed project name and a recipient's inbox.
65
+ *
66
+ * Nothing here touches a database, a cache or a mail server: every collaborator
67
+ * is a jest.spyOn. What is under test is this service's own decision-making.
68
+ *
69
+ * Each test builds its own service instance rather than using the module
70
+ * singleton, because the 24h throttle is in-process state and a singleton
71
+ * carries one test's claims into the next. The singleton is asserted to be an
72
+ * instance of the same class at the end, so nothing here is testing a different
73
+ * object from the one the API mounts.
74
+ */
75
+
76
+ const PROJECT_ID: ObjectID = new ObjectID(
77
+ "11111111-1111-4111-8111-111111111111",
78
+ );
79
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
80
+ "1e1e1e1e-1e1e-4e1e-8e1e-1e1e1e1e1e1e",
81
+ );
82
+ const USER_A: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
83
+ const USER_B: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
84
+ const USER_C: ObjectID = new ObjectID("44444444-4444-4444-8444-444444444444");
85
+ const SEVERITY_ID: ObjectID = new ObjectID(
86
+ "55555555-5555-4555-8555-555555555555",
87
+ );
88
+
89
+ const DASHBOARD_URL: URL = new URL(
90
+ Protocol.HTTPS,
91
+ new Hostname("oneuptime.example.com"),
92
+ );
93
+
94
+ interface SentMail {
95
+ toEmail: string;
96
+ subject: string;
97
+ message: string;
98
+ templateType: string;
99
+ options: {
100
+ projectId?: ObjectID | undefined;
101
+ userId?: ObjectID | undefined;
102
+ };
103
+ }
104
+
105
+ let readinessSpy: jest.SpyInstance;
106
+ let projectSpy: jest.SpyInstance;
107
+ let teamMemberSpy: jest.SpyInstance;
108
+ let dashboardUrlSpy: jest.SpyInstance;
109
+ let mailSpy: jest.SpyInstance;
110
+ let cacheClaimSpy: jest.SpyInstance;
111
+ let cacheReleaseSpy: jest.SpyInstance;
112
+
113
+ function buildCoverage(data: {
114
+ ruleType: NotificationRuleType;
115
+ hasRule: boolean;
116
+ }): ReadinessCoverageCell {
117
+ return {
118
+ ruleType: data.ruleType,
119
+ severityId: SEVERITY_ID,
120
+ severityName: "Sev1",
121
+ hasRule: data.hasRule,
122
+ isOptOut: false,
123
+ };
124
+ }
125
+
126
+ function buildReadiness(data: {
127
+ userId: ObjectID;
128
+ status?: ReadinessStatus | undefined;
129
+ email?: string | undefined;
130
+ reasons?: Array<string> | undefined;
131
+ reachedVia?: Array<ResponderSource> | undefined;
132
+ coverage?: Array<ReadinessCoverageCell> | undefined;
133
+ }): UserReadiness {
134
+ return {
135
+ userId: data.userId,
136
+ userName: "Jane Responder",
137
+ userEmail: data.email === undefined ? "jane@example.com" : data.email,
138
+ status: data.status || ReadinessStatus.NotReachable,
139
+ methods: [],
140
+ coverage: data.coverage || [],
141
+ reasons:
142
+ data.reasons === undefined
143
+ ? ["No verified notification method - cannot be paged"]
144
+ : data.reasons,
145
+ reachedVia:
146
+ data.reachedVia === undefined
147
+ ? [ResponderSource.Direct]
148
+ : data.reachedVia,
149
+ teams: [],
150
+ };
151
+ }
152
+
153
+ /** A fresh service, with an empty in-process throttle. */
154
+ function newService(): OnCallSetupReminderServiceClass {
155
+ return new OnCallSetupReminderServiceClass();
156
+ }
157
+
158
+ function sentMails(): Array<SentMail> {
159
+ return mailSpy.mock.calls.map((args: Array<unknown>): SentMail => {
160
+ const mail: {
161
+ toEmail: { toString: () => string };
162
+ subject: string;
163
+ templateType: string;
164
+ vars: Record<string, string>;
165
+ } = args[0] as never;
166
+
167
+ return {
168
+ toEmail: mail.toEmail.toString(),
169
+ subject: mail.subject,
170
+ message: mail.vars["message"] || "",
171
+ templateType: mail.templateType,
172
+ options: (args[1] || {}) as SentMail["options"],
173
+ };
174
+ });
175
+ }
176
+
177
+ function onlyMail(): SentMail {
178
+ const mails: Array<SentMail> = sentMails();
179
+
180
+ if (mails.length !== 1) {
181
+ throw new Error(`Expected exactly one mail, saw ${mails.length}`);
182
+ }
183
+
184
+ return mails[0]!;
185
+ }
186
+
187
+ /*
188
+ * One case per severity-scoped rule type: the settings tab a gap of that type
189
+ * should link to. All four are covered because sending somebody to the wrong tab
190
+ * produces a screen with nothing wrong on it, which reads as a false alarm - and
191
+ * the mapping is four near-identical lines of the kind that get copy-pasted with
192
+ * one value left unchanged.
193
+ */
194
+ interface GapLinkCase {
195
+ ruleType: NotificationRuleType;
196
+ path: string;
197
+ }
198
+
199
+ const GAP_LINK_CASES: Array<GapLinkCase> = [
200
+ {
201
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
202
+ path: "incident-on-call-rules",
203
+ },
204
+ {
205
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT_EPISODE,
206
+ path: "incident-episode-on-call-rules",
207
+ },
208
+ {
209
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
210
+ path: "alert-on-call-rules",
211
+ },
212
+ {
213
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT_EPISODE,
214
+ path: "alert-episode-on-call-rules",
215
+ },
216
+ ];
217
+
218
+ /*
219
+ * Declared out here rather than inline in the loop that calls it: a test body
220
+ * closing over a loop variable AND over the mutable `readinessSpy` is the shape
221
+ * `no-loop-func` exists to catch, and the lint rule is right that it is easy to
222
+ * get subtly wrong.
223
+ */
224
+ function registerGapLinkTest(gapCase: GapLinkCase): void {
225
+ test(`a ${gapCase.ruleType} gap links to ${gapCase.path}`, async () => {
226
+ readinessSpy.mockResolvedValue([
227
+ buildReadiness({
228
+ userId: USER_A,
229
+ status: ReadinessStatus.PartiallyReady,
230
+ coverage: [
231
+ buildCoverage({ ruleType: gapCase.ruleType, hasRule: false }),
232
+ ],
233
+ }),
234
+ ] as never);
235
+
236
+ await newService().sendSetupReminders({
237
+ projectId: PROJECT_ID,
238
+ userIds: [USER_A],
239
+ });
240
+
241
+ expect(onlyMail().message).toContain(
242
+ `/${PROJECT_ID.toString()}/user-settings/${gapCase.path}`,
243
+ );
244
+ });
245
+ }
246
+
247
+ function outcomeFor(
248
+ result: SetupReminderResult,
249
+ userId: ObjectID,
250
+ ): SetupReminderOutcome {
251
+ const found: SetupReminderUserResult | undefined = result.results.find(
252
+ (one: SetupReminderUserResult): boolean => {
253
+ return one.userId.toString() === userId.toString();
254
+ },
255
+ );
256
+
257
+ if (!found) {
258
+ throw new Error(`No result was returned for user ${userId.toString()}`);
259
+ }
260
+
261
+ return found.outcome;
262
+ }
263
+
264
+ beforeEach(() => {
265
+ jest.restoreAllMocks();
266
+ jest.clearAllMocks();
267
+
268
+ jest.spyOn(logger, "error").mockImplementation((): void => {});
269
+ jest.spyOn(logger, "warn").mockImplementation((): void => {});
270
+ jest.spyOn(logger, "debug").mockImplementation((): void => {});
271
+
272
+ projectSpy = jest
273
+ .spyOn(ProjectService, "findOneById")
274
+ .mockResolvedValue({ id: PROJECT_ID, name: "Acme Payments" } as never);
275
+
276
+ readinessSpy = jest
277
+ .spyOn(OnCallReadinessService, "getReadinessForUsers")
278
+ .mockResolvedValue([buildReadiness({ userId: USER_A })] as never);
279
+
280
+ teamMemberSpy = jest
281
+ .spyOn(TeamMemberService, "findBy")
282
+ .mockResolvedValue([] as never);
283
+
284
+ dashboardUrlSpy = jest
285
+ .spyOn(DatabaseConfig, "getDashboardUrl")
286
+ .mockResolvedValue(DASHBOARD_URL as never);
287
+
288
+ mailSpy = jest
289
+ .spyOn(MailService, "sendMail")
290
+ .mockResolvedValue(new HTTPResponse(200, {}, {}) as never);
291
+
292
+ cacheClaimSpy = jest
293
+ .spyOn(GlobalCache, "setStringIfNotExists")
294
+ .mockResolvedValue(true as never);
295
+
296
+ cacheReleaseSpy = jest
297
+ .spyOn(GlobalCache, "deleteKeyIfValue")
298
+ .mockResolvedValue(true as never);
299
+ });
300
+
301
+ describe("OnCallSetupReminderService - (A) never trust the body", () => {
302
+ test("a user id that is not a member of the project is skipped and never mailed", async () => {
303
+ /*
304
+ * The readiness read omits non-members, and the follow-up membership read
305
+ * confirms there are no rows for them at all.
306
+ */
307
+ readinessSpy.mockResolvedValue([] as never);
308
+ teamMemberSpy.mockResolvedValue([] as never);
309
+
310
+ const result: SetupReminderResult = await newService().sendSetupReminders({
311
+ projectId: PROJECT_ID,
312
+ userIds: [USER_A],
313
+ });
314
+
315
+ expect(outcomeFor(result, USER_A)).toBe(
316
+ SetupReminderOutcome.SkippedNotAMember,
317
+ );
318
+ expect(mailSpy).not.toHaveBeenCalled();
319
+ expect(result.sentCount).toBe(0);
320
+ expect(result.skippedCount).toBe(1);
321
+ });
322
+
323
+ test("the membership re-check is scoped to the caller's project and reads as root", async () => {
324
+ readinessSpy.mockResolvedValue([] as never);
325
+
326
+ await newService().sendSetupReminders({
327
+ projectId: PROJECT_ID,
328
+ userIds: [USER_A],
329
+ });
330
+
331
+ const call: {
332
+ query: Record<string, unknown>;
333
+ props: { isRoot?: boolean | undefined };
334
+ } = teamMemberSpy.mock.calls[0]![0] as never;
335
+
336
+ expect(call.query["projectId"]).toBe(PROJECT_ID);
337
+ expect(call.props.isRoot).toBe(true);
338
+ });
339
+
340
+ test("the readiness read is scoped to the caller's project, not to anything in the request body", async () => {
341
+ await newService().sendSetupReminders({
342
+ projectId: PROJECT_ID,
343
+ userIds: [USER_A],
344
+ });
345
+
346
+ expect(readinessSpy).toHaveBeenCalledWith([USER_A], PROJECT_ID);
347
+ });
348
+
349
+ test("a member the readiness read could not answer for is reported as failed, not as a stranger", async () => {
350
+ /*
351
+ * "Not in this project" and "we could not work out what they are missing"
352
+ * need two different sentences: the first is a list an admin should fix,
353
+ * the second is a bug they should report.
354
+ */
355
+ readinessSpy.mockResolvedValue([] as never);
356
+ teamMemberSpy.mockResolvedValue([{ _id: "tm-1", userId: USER_A }] as never);
357
+
358
+ const result: SetupReminderResult = await newService().sendSetupReminders({
359
+ projectId: PROJECT_ID,
360
+ userIds: [USER_A],
361
+ });
362
+
363
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
364
+ expect(result.failedCount).toBe(1);
365
+ expect(mailSpy).not.toHaveBeenCalled();
366
+ });
367
+
368
+ test("no membership query is issued when readiness answered for everybody", async () => {
369
+ // The common case must not pay for the disambiguation of a rare one.
370
+ await newService().sendSetupReminders({
371
+ projectId: PROJECT_ID,
372
+ userIds: [USER_A],
373
+ });
374
+
375
+ expect(teamMemberSpy).not.toHaveBeenCalled();
376
+ });
377
+ });
378
+
379
+ describe("OnCallSetupReminderService - (B) never claim more than is true", () => {
380
+ test("a Ready responder is skipped rather than nagged", async () => {
381
+ readinessSpy.mockResolvedValue([
382
+ buildReadiness({
383
+ userId: USER_A,
384
+ status: ReadinessStatus.Ready,
385
+ reasons: [],
386
+ }),
387
+ ] as never);
388
+
389
+ const result: SetupReminderResult = await newService().sendSetupReminders({
390
+ projectId: PROJECT_ID,
391
+ userIds: [USER_A],
392
+ });
393
+
394
+ expect(outcomeFor(result, USER_A)).toBe(
395
+ SetupReminderOutcome.SkippedNothingMissing,
396
+ );
397
+ expect(mailSpy).not.toHaveBeenCalled();
398
+ });
399
+
400
+ test("a Ready responder does not consume their 24h window either", async () => {
401
+ /*
402
+ * The claim must come after the "is there anything to say" decision.
403
+ * Burning the window on a mail that was never going to be sent would
404
+ * silence a responder who becomes genuinely unreachable an hour later.
405
+ */
406
+ readinessSpy.mockResolvedValue([
407
+ buildReadiness({ userId: USER_A, status: ReadinessStatus.Ready }),
408
+ ] as never);
409
+
410
+ await newService().sendSetupReminders({
411
+ projectId: PROJECT_ID,
412
+ userIds: [USER_A],
413
+ });
414
+
415
+ expect(cacheClaimSpy).not.toHaveBeenCalled();
416
+ });
417
+
418
+ test("the mail says they are on a rotation only when a policy actually reaches them", async () => {
419
+ readinessSpy.mockResolvedValue([
420
+ buildReadiness({ userId: USER_A, reachedVia: [ResponderSource.Team] }),
421
+ ] as never);
422
+
423
+ await newService().sendSetupReminders({
424
+ projectId: PROJECT_ID,
425
+ userIds: [USER_A],
426
+ });
427
+
428
+ expect(onlyMail().message).toContain("You are on an on-call rotation");
429
+ });
430
+
431
+ test("a responder on no policy is not told they are on one", async () => {
432
+ readinessSpy.mockResolvedValue([
433
+ buildReadiness({ userId: USER_A, reachedVia: [] }),
434
+ ] as never);
435
+
436
+ await newService().sendSetupReminders({
437
+ projectId: PROJECT_ID,
438
+ userIds: [USER_A],
439
+ });
440
+
441
+ const message: string = onlyMail().message;
442
+
443
+ expect(message).not.toContain("You are on an on-call rotation");
444
+ expect(message).toContain("can be added to an on-call rotation");
445
+ });
446
+
447
+ test("the mail names the real gap, using the readiness service's own words", async () => {
448
+ readinessSpy.mockResolvedValue([
449
+ buildReadiness({
450
+ userId: USER_A,
451
+ status: ReadinessStatus.PartiallyReady,
452
+ reasons: [
453
+ "No rules for Sev1, Sev2 incidents - pages fall back to Email, Push",
454
+ ],
455
+ coverage: [
456
+ buildCoverage({
457
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
458
+ hasRule: false,
459
+ }),
460
+ ],
461
+ }),
462
+ ] as never);
463
+
464
+ await newService().sendSetupReminders({
465
+ projectId: PROJECT_ID,
466
+ userIds: [USER_A],
467
+ });
468
+
469
+ expect(onlyMail().message).toContain(
470
+ "No rules for Sev1, Sev2 incidents - pages fall back to Email, Push",
471
+ );
472
+ });
473
+
474
+ test("an unreachable responder gets the urgent subject; a partially ready one does not", async () => {
475
+ readinessSpy.mockResolvedValue([
476
+ buildReadiness({ userId: USER_A, status: ReadinessStatus.NotReachable }),
477
+ ] as never);
478
+
479
+ await newService().sendSetupReminders({
480
+ projectId: PROJECT_ID,
481
+ userIds: [USER_A],
482
+ });
483
+
484
+ expect(onlyMail().subject).toBe(
485
+ "Action needed: nothing can page you in Acme Payments",
486
+ );
487
+
488
+ mailSpy.mockClear();
489
+
490
+ readinessSpy.mockResolvedValue([
491
+ buildReadiness({
492
+ userId: USER_A,
493
+ status: ReadinessStatus.PartiallyReady,
494
+ }),
495
+ ] as never);
496
+
497
+ await newService().sendSetupReminders({
498
+ projectId: PROJECT_ID,
499
+ userIds: [USER_A],
500
+ });
501
+
502
+ expect(onlyMail().subject).toBe(
503
+ "Finish your on-call notification setup in Acme Payments",
504
+ );
505
+ });
506
+
507
+ test("the mail goes to the responder's own account email, tagged with project and user", async () => {
508
+ readinessSpy.mockResolvedValue([
509
+ buildReadiness({ userId: USER_A, email: "jane@example.com" }),
510
+ ] as never);
511
+
512
+ await newService().sendSetupReminders({
513
+ projectId: PROJECT_ID,
514
+ userIds: [USER_A],
515
+ });
516
+
517
+ const mail: SentMail = onlyMail();
518
+
519
+ expect(mail.toEmail).toBe("jane@example.com");
520
+ expect(mail.options.projectId).toBe(PROJECT_ID);
521
+ expect(mail.options.userId).toBe(USER_A);
522
+ });
523
+ });
524
+
525
+ describe("OnCallSetupReminderService - the deep link points at the broken thing", () => {
526
+ test("an unreachable responder is sent to notification methods", async () => {
527
+ readinessSpy.mockResolvedValue([
528
+ buildReadiness({
529
+ userId: USER_A,
530
+ status: ReadinessStatus.NotReachable,
531
+ /*
532
+ * A coverage gap is present and must NOT win: with no verified method,
533
+ * adding a rule changes nothing.
534
+ */
535
+ coverage: [
536
+ buildCoverage({
537
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
538
+ hasRule: false,
539
+ }),
540
+ ],
541
+ }),
542
+ ] as never);
543
+
544
+ await newService().sendSetupReminders({
545
+ projectId: PROJECT_ID,
546
+ userIds: [USER_A],
547
+ });
548
+
549
+ expect(onlyMail().message).toContain(
550
+ `/${PROJECT_ID.toString()}/user-settings/notification-methods`,
551
+ );
552
+ });
553
+
554
+ for (const gapCase of GAP_LINK_CASES) {
555
+ registerGapLinkTest(gapCase);
556
+ }
557
+
558
+ test("a covered cell is not mistaken for a gap", async () => {
559
+ readinessSpy.mockResolvedValue([
560
+ buildReadiness({
561
+ userId: USER_A,
562
+ status: ReadinessStatus.PartiallyReady,
563
+ coverage: [
564
+ buildCoverage({
565
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
566
+ hasRule: true,
567
+ }),
568
+ buildCoverage({
569
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
570
+ hasRule: false,
571
+ }),
572
+ ],
573
+ }),
574
+ ] as never);
575
+
576
+ await newService().sendSetupReminders({
577
+ projectId: PROJECT_ID,
578
+ userIds: [USER_A],
579
+ });
580
+
581
+ expect(onlyMail().message).toContain(
582
+ `/${PROJECT_ID.toString()}/user-settings/alert-on-call-rules`,
583
+ );
584
+ });
585
+
586
+ test("a muted cell is not a gap either", async () => {
587
+ readinessSpy.mockResolvedValue([
588
+ buildReadiness({
589
+ userId: USER_A,
590
+ status: ReadinessStatus.PartiallyReady,
591
+ coverage: [
592
+ {
593
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
594
+ severityId: SEVERITY_ID,
595
+ severityName: "Sev4",
596
+ hasRule: false,
597
+ isOptOut: true,
598
+ },
599
+ buildCoverage({
600
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT_EPISODE,
601
+ hasRule: false,
602
+ }),
603
+ ],
604
+ }),
605
+ ] as never);
606
+
607
+ await newService().sendSetupReminders({
608
+ projectId: PROJECT_ID,
609
+ userIds: [USER_A],
610
+ });
611
+
612
+ expect(onlyMail().message).toContain(
613
+ `/${PROJECT_ID.toString()}/user-settings/alert-episode-on-call-rules`,
614
+ );
615
+ });
616
+ });
617
+
618
+ describe("OnCallSetupReminderService - (E) escaping", () => {
619
+ test("an admin-typed project name cannot inject markup into the message body", async () => {
620
+ projectSpy.mockResolvedValue({
621
+ id: PROJECT_ID,
622
+ name: '<script>alert("x")</script> & Co',
623
+ } as never);
624
+
625
+ await newService().sendSetupReminders({
626
+ projectId: PROJECT_ID,
627
+ userIds: [USER_A],
628
+ });
629
+
630
+ const message: string = onlyMail().message;
631
+
632
+ expect(message).not.toContain("<script>");
633
+ expect(message).toContain("&lt;script&gt;");
634
+ expect(message).toContain("&amp; Co");
635
+ });
636
+
637
+ test("a reason sentence carrying a crafted severity name is escaped too", async () => {
638
+ readinessSpy.mockResolvedValue([
639
+ buildReadiness({
640
+ userId: USER_A,
641
+ reasons: ['No rules for <img src=x onerror="steal()"> incidents'],
642
+ }),
643
+ ] as never);
644
+
645
+ await newService().sendSetupReminders({
646
+ projectId: PROJECT_ID,
647
+ userIds: [USER_A],
648
+ });
649
+
650
+ const message: string = onlyMail().message;
651
+
652
+ expect(message).not.toContain("<img src=x");
653
+ expect(message).toContain("&lt;img src=x");
654
+ expect(message).toContain("&quot;steal()&quot;");
655
+ });
656
+
657
+ test("the subject is NOT escaped - Handlebars escapes it, and the SMTP header must stay raw", async () => {
658
+ projectSpy.mockResolvedValue({
659
+ id: PROJECT_ID,
660
+ name: "Bob's & Co",
661
+ } as never);
662
+
663
+ await newService().sendSetupReminders({
664
+ projectId: PROJECT_ID,
665
+ userIds: [USER_A],
666
+ });
667
+
668
+ expect(onlyMail().subject).toContain("Bob's & Co");
669
+ expect(onlyMail().subject).not.toContain("&amp;");
670
+ });
671
+
672
+ test("the structural markup this service writes itself survives", async () => {
673
+ readinessSpy.mockResolvedValue([
674
+ buildReadiness({
675
+ userId: USER_A,
676
+ reasons: ["No verified notification method - cannot be paged"],
677
+ }),
678
+ ] as never);
679
+
680
+ await newService().sendSetupReminders({
681
+ projectId: PROJECT_ID,
682
+ userIds: [USER_A],
683
+ });
684
+
685
+ const message: string = onlyMail().message;
686
+
687
+ expect(message).toContain("<ul>");
688
+ expect(message).toContain(
689
+ "<li>No verified notification method - cannot be paged</li>",
690
+ );
691
+ expect(message).toContain("<a href=");
692
+ });
693
+
694
+ test("a long reason list is summarised rather than printed in full", async () => {
695
+ const manyReasons: Array<string> = [];
696
+
697
+ for (let index: number = 0; index < 10; index++) {
698
+ manyReasons.push(`Reason number ${index}`);
699
+ }
700
+
701
+ readinessSpy.mockResolvedValue([
702
+ buildReadiness({ userId: USER_A, reasons: manyReasons }),
703
+ ] as never);
704
+
705
+ await newService().sendSetupReminders({
706
+ projectId: PROJECT_ID,
707
+ userIds: [USER_A],
708
+ });
709
+
710
+ const message: string = onlyMail().message;
711
+
712
+ expect(message).toContain("Reason number 5");
713
+ expect(message).not.toContain("Reason number 6");
714
+ expect(message).toContain("and 4 more, listed in your settings");
715
+ });
716
+
717
+ test("a status with no reasons still produces a message with a link", async () => {
718
+ readinessSpy.mockResolvedValue([
719
+ buildReadiness({ userId: USER_A, reasons: [] }),
720
+ ] as never);
721
+
722
+ await newService().sendSetupReminders({
723
+ projectId: PROJECT_ID,
724
+ userIds: [USER_A],
725
+ });
726
+
727
+ const message: string = onlyMail().message;
728
+
729
+ expect(message).not.toContain("<ul>");
730
+ expect(message).toContain("Open your settings to see what is missing");
731
+ expect(message).toContain("<a href=");
732
+ });
733
+ });
734
+
735
+ describe("OnCallSetupReminderService - (D) the throttle is a lock", () => {
736
+ test("the window is claimed BEFORE the send, atomically, for 24 hours", async () => {
737
+ await newService().sendSetupReminders({
738
+ projectId: PROJECT_ID,
739
+ userIds: [USER_A],
740
+ });
741
+
742
+ expect(cacheClaimSpy).toHaveBeenCalledTimes(1);
743
+
744
+ const args: Array<unknown> = cacheClaimSpy.mock.calls[0]!;
745
+
746
+ expect(args[1]).toBe(`${PROJECT_ID.toString()}:${USER_A.toString()}`);
747
+ expect(args[3]).toEqual({ expiresInSeconds: 24 * 60 * 60 });
748
+
749
+ // Claim first, then send - not the other way round.
750
+ expect(cacheClaimSpy.mock.invocationCallOrder[0]!).toBeLessThan(
751
+ mailSpy.mock.invocationCallOrder[0]!,
752
+ );
753
+ });
754
+
755
+ test("a second reminder for the same responder in the same process is skipped without a round trip", async () => {
756
+ const service: OnCallSetupReminderServiceClass = newService();
757
+
758
+ await service.sendSetupReminders({
759
+ projectId: PROJECT_ID,
760
+ userIds: [USER_A],
761
+ });
762
+
763
+ cacheClaimSpy.mockClear();
764
+
765
+ const second: SetupReminderResult = await service.sendSetupReminders({
766
+ projectId: PROJECT_ID,
767
+ userIds: [USER_A],
768
+ });
769
+
770
+ expect(outcomeFor(second, USER_A)).toBe(
771
+ SetupReminderOutcome.SkippedThrottled,
772
+ );
773
+ expect(mailSpy).toHaveBeenCalledTimes(1);
774
+ expect(cacheClaimSpy).not.toHaveBeenCalled();
775
+ });
776
+
777
+ test("a window already held by another pod skips the send", async () => {
778
+ cacheClaimSpy.mockResolvedValue(false as never);
779
+
780
+ const result: SetupReminderResult = await newService().sendSetupReminders({
781
+ projectId: PROJECT_ID,
782
+ userIds: [USER_A],
783
+ });
784
+
785
+ expect(outcomeFor(result, USER_A)).toBe(
786
+ SetupReminderOutcome.SkippedThrottled,
787
+ );
788
+ expect(mailSpy).not.toHaveBeenCalled();
789
+ });
790
+
791
+ test("a window held elsewhere is remembered locally, so the next click costs no round trip", async () => {
792
+ const service: OnCallSetupReminderServiceClass = newService();
793
+
794
+ cacheClaimSpy.mockResolvedValue(false as never);
795
+
796
+ await service.sendSetupReminders({
797
+ projectId: PROJECT_ID,
798
+ userIds: [USER_A],
799
+ });
800
+
801
+ cacheClaimSpy.mockClear();
802
+
803
+ await service.sendSetupReminders({
804
+ projectId: PROJECT_ID,
805
+ userIds: [USER_A],
806
+ });
807
+
808
+ expect(cacheClaimSpy).not.toHaveBeenCalled();
809
+ });
810
+
811
+ test("two different responders do not share a window", async () => {
812
+ readinessSpy.mockResolvedValue([
813
+ buildReadiness({ userId: USER_A }),
814
+ buildReadiness({ userId: USER_B }),
815
+ ] as never);
816
+
817
+ const result: SetupReminderResult = await newService().sendSetupReminders({
818
+ projectId: PROJECT_ID,
819
+ userIds: [USER_A, USER_B],
820
+ });
821
+
822
+ expect(result.sentCount).toBe(2);
823
+ expect(mailSpy).toHaveBeenCalledTimes(2);
824
+ });
825
+
826
+ test("the same responder in two different projects does not share a window", async () => {
827
+ const service: OnCallSetupReminderServiceClass = newService();
828
+
829
+ await service.sendSetupReminders({
830
+ projectId: PROJECT_ID,
831
+ userIds: [USER_A],
832
+ });
833
+
834
+ const second: SetupReminderResult = await service.sendSetupReminders({
835
+ projectId: OTHER_PROJECT_ID,
836
+ userIds: [USER_A],
837
+ });
838
+
839
+ expect(outcomeFor(second, USER_A)).toBe(SetupReminderOutcome.Sent);
840
+ expect(mailSpy).toHaveBeenCalledTimes(2);
841
+ });
842
+
843
+ test("a failed send gives the window back, using its own token", async () => {
844
+ mailSpy.mockRejectedValue(new Error("smtp is down") as never);
845
+
846
+ const result: SetupReminderResult = await newService().sendSetupReminders({
847
+ projectId: PROJECT_ID,
848
+ userIds: [USER_A],
849
+ });
850
+
851
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
852
+ expect(cacheReleaseSpy).toHaveBeenCalledTimes(1);
853
+
854
+ const claimArgs: Array<unknown> = cacheClaimSpy.mock.calls[0]!;
855
+ const releaseArgs: Array<unknown> = cacheReleaseSpy.mock.calls[0]!;
856
+
857
+ /*
858
+ * Same namespace, same key, and crucially the SAME token: a release must
859
+ * never be able to delete a claim somebody else is holding.
860
+ */
861
+ expect(releaseArgs[0]).toBe(claimArgs[0]);
862
+ expect(releaseArgs[1]).toBe(claimArgs[1]);
863
+ expect(releaseArgs[2]).toBe(claimArgs[2]);
864
+ });
865
+
866
+ test("a released window can be claimed again by the very next request", async () => {
867
+ const service: OnCallSetupReminderServiceClass = newService();
868
+
869
+ mailSpy.mockRejectedValueOnce(new Error("smtp blip") as never);
870
+
871
+ await service.sendSetupReminders({
872
+ projectId: PROJECT_ID,
873
+ userIds: [USER_A],
874
+ });
875
+
876
+ const second: SetupReminderResult = await service.sendSetupReminders({
877
+ projectId: PROJECT_ID,
878
+ userIds: [USER_A],
879
+ });
880
+
881
+ expect(outcomeFor(second, USER_A)).toBe(SetupReminderOutcome.Sent);
882
+ });
883
+
884
+ test("a successful send never releases the window", async () => {
885
+ await newService().sendSetupReminders({
886
+ projectId: PROJECT_ID,
887
+ userIds: [USER_A],
888
+ });
889
+
890
+ expect(cacheReleaseSpy).not.toHaveBeenCalled();
891
+ });
892
+
893
+ test("an unreachable cache fails OPEN - the reminder still goes, with a warning", async () => {
894
+ cacheClaimSpy.mockRejectedValue(
895
+ new Error("cache is not connected") as never,
896
+ );
897
+
898
+ const result: SetupReminderResult = await newService().sendSetupReminders({
899
+ projectId: PROJECT_ID,
900
+ userIds: [USER_A],
901
+ });
902
+
903
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Sent);
904
+ expect(mailSpy).toHaveBeenCalledTimes(1);
905
+ expect(logger.warn).toHaveBeenCalled();
906
+ });
907
+
908
+ test("with the cache down, the in-process throttle still stops a repeat click", async () => {
909
+ const service: OnCallSetupReminderServiceClass = newService();
910
+
911
+ cacheClaimSpy.mockRejectedValue(
912
+ new Error("cache is not connected") as never,
913
+ );
914
+
915
+ await service.sendSetupReminders({
916
+ projectId: PROJECT_ID,
917
+ userIds: [USER_A],
918
+ });
919
+
920
+ const second: SetupReminderResult = await service.sendSetupReminders({
921
+ projectId: PROJECT_ID,
922
+ userIds: [USER_A],
923
+ });
924
+
925
+ expect(outcomeFor(second, USER_A)).toBe(
926
+ SetupReminderOutcome.SkippedThrottled,
927
+ );
928
+ expect(mailSpy).toHaveBeenCalledTimes(1);
929
+ });
930
+
931
+ test("a release that cannot reach the cache does not turn a failed send into a thrown request", async () => {
932
+ mailSpy.mockRejectedValue(new Error("smtp is down") as never);
933
+ cacheReleaseSpy.mockRejectedValue(
934
+ new Error("cache is not connected") as never,
935
+ );
936
+
937
+ const result: SetupReminderResult = await newService().sendSetupReminders({
938
+ projectId: PROJECT_ID,
939
+ userIds: [USER_A],
940
+ });
941
+
942
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
943
+ });
944
+ });
945
+
946
+ describe("OnCallSetupReminderService - (C) never report sent for a mail that did not go", () => {
947
+ test("a non-200 answer from the notification service is reported as failed", async () => {
948
+ /*
949
+ * The highest-value assertion in this file. MailService goes through
950
+ * API.post, which RESOLVES with an error response rather than throwing, so
951
+ * a bare try/catch would call this a success.
952
+ */
953
+ mailSpy.mockResolvedValue(
954
+ new HTTPResponse(500, { message: "smtp refused" }, {}) as never,
955
+ );
956
+
957
+ const result: SetupReminderResult = await newService().sendSetupReminders({
958
+ projectId: PROJECT_ID,
959
+ userIds: [USER_A],
960
+ });
961
+
962
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
963
+ expect(result.sentCount).toBe(0);
964
+ expect(result.failedCount).toBe(1);
965
+ });
966
+
967
+ test("a non-200 answer also gives the throttle window back", async () => {
968
+ mailSpy.mockResolvedValue(new HTTPResponse(502, {}, {}) as never);
969
+
970
+ await newService().sendSetupReminders({
971
+ projectId: PROJECT_ID,
972
+ userIds: [USER_A],
973
+ });
974
+
975
+ expect(cacheReleaseSpy).toHaveBeenCalledTimes(1);
976
+ });
977
+
978
+ test("a thrown send is reported as failed", async () => {
979
+ mailSpy.mockRejectedValue(new Error("connect ECONNREFUSED") as never);
980
+
981
+ const result: SetupReminderResult = await newService().sendSetupReminders({
982
+ projectId: PROJECT_ID,
983
+ userIds: [USER_A],
984
+ });
985
+
986
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
987
+ });
988
+
989
+ test("the failure message given to the admin is prose, not a stack trace", async () => {
990
+ mailSpy.mockRejectedValue(
991
+ new Error("connect ECONNREFUSED 10.0.0.4:587") as never,
992
+ );
993
+
994
+ const result: SetupReminderResult = await newService().sendSetupReminders({
995
+ projectId: PROJECT_ID,
996
+ userIds: [USER_A],
997
+ });
998
+
999
+ const message: string = result.results[0]!.message;
1000
+
1001
+ expect(message).not.toContain("ECONNREFUSED");
1002
+ expect(message).toContain("Nothing was delivered");
1003
+ });
1004
+
1005
+ test("a responder with no usable account email is failed, not silently dropped", async () => {
1006
+ readinessSpy.mockResolvedValue([
1007
+ buildReadiness({ userId: USER_A, email: "" }),
1008
+ ] as never);
1009
+
1010
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1011
+ projectId: PROJECT_ID,
1012
+ userIds: [USER_A],
1013
+ });
1014
+
1015
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
1016
+ expect(mailSpy).not.toHaveBeenCalled();
1017
+ // And it does not burn a window it was never going to use.
1018
+ expect(cacheClaimSpy).not.toHaveBeenCalled();
1019
+ });
1020
+
1021
+ test("a malformed account email is failed rather than thrown for", async () => {
1022
+ readinessSpy.mockResolvedValue([
1023
+ buildReadiness({ userId: USER_A, email: "not-an-email" }),
1024
+ ] as never);
1025
+
1026
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1027
+ projectId: PROJECT_ID,
1028
+ userIds: [USER_A],
1029
+ });
1030
+
1031
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Failed);
1032
+ expect(mailSpy).not.toHaveBeenCalled();
1033
+ });
1034
+ });
1035
+
1036
+ describe("OnCallSetupReminderService - the batch answer is per user", () => {
1037
+ test("one bad recipient does not cancel the rest of the batch", async () => {
1038
+ readinessSpy.mockResolvedValue([
1039
+ buildReadiness({ userId: USER_A }),
1040
+ buildReadiness({ userId: USER_B, email: "" }),
1041
+ buildReadiness({
1042
+ userId: USER_C,
1043
+ status: ReadinessStatus.PartiallyReady,
1044
+ }),
1045
+ ] as never);
1046
+
1047
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1048
+ projectId: PROJECT_ID,
1049
+ userIds: [USER_A, USER_B, USER_C],
1050
+ });
1051
+
1052
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Sent);
1053
+ expect(outcomeFor(result, USER_B)).toBe(SetupReminderOutcome.Failed);
1054
+ expect(outcomeFor(result, USER_C)).toBe(SetupReminderOutcome.Sent);
1055
+ expect(mailSpy).toHaveBeenCalledTimes(2);
1056
+ });
1057
+
1058
+ test("the counts add up and describe the whole request", async () => {
1059
+ readinessSpy.mockResolvedValue([
1060
+ buildReadiness({ userId: USER_A }),
1061
+ buildReadiness({
1062
+ userId: USER_B,
1063
+ status: ReadinessStatus.Ready,
1064
+ }),
1065
+ ] as never);
1066
+ teamMemberSpy.mockResolvedValue([] as never);
1067
+
1068
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1069
+ projectId: PROJECT_ID,
1070
+ userIds: [USER_A, USER_B, USER_C],
1071
+ });
1072
+
1073
+ expect(result.requestedCount).toBe(3);
1074
+ expect(result.sentCount).toBe(1);
1075
+ // USER_B is Ready, USER_C is not a member.
1076
+ expect(result.skippedCount).toBe(2);
1077
+ expect(result.failedCount).toBe(0);
1078
+ expect(result.results).toHaveLength(3);
1079
+ expect(result.sentCount + result.skippedCount + result.failedCount).toBe(
1080
+ result.requestedCount,
1081
+ );
1082
+ });
1083
+
1084
+ test("results come back in the order the caller asked, so a UI can line them up with its rows", async () => {
1085
+ readinessSpy.mockResolvedValue([
1086
+ // Deliberately returned in the opposite order to the request.
1087
+ buildReadiness({ userId: USER_C }),
1088
+ buildReadiness({ userId: USER_B }),
1089
+ buildReadiness({ userId: USER_A }),
1090
+ ] as never);
1091
+
1092
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1093
+ projectId: PROJECT_ID,
1094
+ userIds: [USER_A, USER_B, USER_C],
1095
+ });
1096
+
1097
+ expect(
1098
+ result.results.map((one: SetupReminderUserResult): string => {
1099
+ return one.userId.toString();
1100
+ }),
1101
+ ).toEqual([USER_A.toString(), USER_B.toString(), USER_C.toString()]);
1102
+ });
1103
+
1104
+ test("a repeated id is one person, one mail and one result", async () => {
1105
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1106
+ projectId: PROJECT_ID,
1107
+ userIds: [USER_A, USER_A, USER_A],
1108
+ });
1109
+
1110
+ expect(result.requestedCount).toBe(1);
1111
+ expect(result.results).toHaveLength(1);
1112
+ expect(mailSpy).toHaveBeenCalledTimes(1);
1113
+ expect(outcomeFor(result, USER_A)).toBe(SetupReminderOutcome.Sent);
1114
+ });
1115
+
1116
+ test("every result carries a sentence a human can act on", async () => {
1117
+ readinessSpy.mockResolvedValue([
1118
+ buildReadiness({ userId: USER_A }),
1119
+ buildReadiness({ userId: USER_B, status: ReadinessStatus.Ready }),
1120
+ ] as never);
1121
+ teamMemberSpy.mockResolvedValue([] as never);
1122
+
1123
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1124
+ projectId: PROJECT_ID,
1125
+ userIds: [USER_A, USER_B, USER_C],
1126
+ });
1127
+
1128
+ for (const one of result.results) {
1129
+ expect(one.message.length).toBeGreaterThan(10);
1130
+ }
1131
+ });
1132
+
1133
+ test("no result echoes an email address back to the caller", async () => {
1134
+ readinessSpy.mockResolvedValue([
1135
+ buildReadiness({ userId: USER_A, email: "jane@example.com" }),
1136
+ ] as never);
1137
+
1138
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1139
+ projectId: PROJECT_ID,
1140
+ userIds: [USER_A],
1141
+ });
1142
+
1143
+ expect(JSON.stringify(result.results)).not.toContain("jane@example.com");
1144
+ });
1145
+ });
1146
+
1147
+ describe("OnCallSetupReminderService - requests that are wrong as a whole", () => {
1148
+ test("an empty selection is refused", async () => {
1149
+ await expect(
1150
+ newService().sendSetupReminders({
1151
+ projectId: PROJECT_ID,
1152
+ userIds: [],
1153
+ }),
1154
+ ).rejects.toThrow(BadDataException);
1155
+
1156
+ expect(readinessSpy).not.toHaveBeenCalled();
1157
+ });
1158
+
1159
+ test("more than the ceiling is REFUSED rather than quietly truncated", async () => {
1160
+ const tooMany: Array<ObjectID> = [];
1161
+
1162
+ for (
1163
+ let index: number = 0;
1164
+ index <= SETUP_REMINDER_MAX_RECIPIENTS;
1165
+ index++
1166
+ ) {
1167
+ tooMany.push(ObjectID.generate());
1168
+ }
1169
+
1170
+ await expect(
1171
+ newService().sendSetupReminders({
1172
+ projectId: PROJECT_ID,
1173
+ userIds: tooMany,
1174
+ }),
1175
+ ).rejects.toThrow(
1176
+ new RegExp(`at most ${SETUP_REMINDER_MAX_RECIPIENTS} responders`),
1177
+ );
1178
+
1179
+ expect(mailSpy).not.toHaveBeenCalled();
1180
+ });
1181
+
1182
+ test("exactly the ceiling is allowed", async () => {
1183
+ const exactly: Array<ObjectID> = [];
1184
+
1185
+ for (
1186
+ let index: number = 0;
1187
+ index < SETUP_REMINDER_MAX_RECIPIENTS;
1188
+ index++
1189
+ ) {
1190
+ exactly.push(ObjectID.generate());
1191
+ }
1192
+
1193
+ readinessSpy.mockResolvedValue([] as never);
1194
+ teamMemberSpy.mockResolvedValue([] as never);
1195
+
1196
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1197
+ projectId: PROJECT_ID,
1198
+ userIds: exactly,
1199
+ });
1200
+
1201
+ expect(result.requestedCount).toBe(SETUP_REMINDER_MAX_RECIPIENTS);
1202
+ });
1203
+
1204
+ test("duplicates are collapsed before the ceiling is applied", async () => {
1205
+ const withDuplicates: Array<ObjectID> = [];
1206
+
1207
+ for (
1208
+ let index: number = 0;
1209
+ index <= SETUP_REMINDER_MAX_RECIPIENTS;
1210
+ index++
1211
+ ) {
1212
+ withDuplicates.push(USER_A);
1213
+ }
1214
+
1215
+ const result: SetupReminderResult = await newService().sendSetupReminders({
1216
+ projectId: PROJECT_ID,
1217
+ userIds: withDuplicates,
1218
+ });
1219
+
1220
+ expect(result.requestedCount).toBe(1);
1221
+ });
1222
+
1223
+ test("a project that does not exist fails the whole request rather than mailing 'your project'", async () => {
1224
+ projectSpy.mockResolvedValue(null as never);
1225
+
1226
+ await expect(
1227
+ newService().sendSetupReminders({
1228
+ projectId: PROJECT_ID,
1229
+ userIds: [USER_A],
1230
+ }),
1231
+ ).rejects.toThrow(BadDataException);
1232
+
1233
+ expect(mailSpy).not.toHaveBeenCalled();
1234
+ });
1235
+
1236
+ test("the dashboard URL is resolved once for the whole batch", async () => {
1237
+ readinessSpy.mockResolvedValue([
1238
+ buildReadiness({ userId: USER_A }),
1239
+ buildReadiness({ userId: USER_B }),
1240
+ buildReadiness({ userId: USER_C }),
1241
+ ] as never);
1242
+
1243
+ await newService().sendSetupReminders({
1244
+ projectId: PROJECT_ID,
1245
+ userIds: [USER_A, USER_B, USER_C],
1246
+ });
1247
+
1248
+ expect(dashboardUrlSpy).toHaveBeenCalledTimes(1);
1249
+ });
1250
+
1251
+ test("the project is read once, as root, for its name", async () => {
1252
+ await newService().sendSetupReminders({
1253
+ projectId: PROJECT_ID,
1254
+ userIds: [USER_A],
1255
+ });
1256
+
1257
+ expect(projectSpy).toHaveBeenCalledTimes(1);
1258
+
1259
+ const call: { props: { isRoot?: boolean | undefined } } = projectSpy.mock
1260
+ .calls[0]![0] as never;
1261
+
1262
+ expect(call.props.isRoot).toBe(true);
1263
+ });
1264
+ });
1265
+
1266
+ describe("OnCallSetupReminderService - the exported singleton", () => {
1267
+ test("the default export is an instance of the class the tests exercise", () => {
1268
+ expect(OnCallSetupReminderService).toBeInstanceOf(
1269
+ OnCallSetupReminderServiceClass,
1270
+ );
1271
+ });
1272
+ });