@oneuptime/common 9.4.12 → 9.4.13

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 (256) hide show
  1. package/Models/DatabaseModels/Incident.ts +77 -0
  2. package/Models/DatabaseModels/IncidentEpisode.ts +1223 -0
  3. package/Models/DatabaseModels/IncidentEpisodeFeed.ts +533 -0
  4. package/Models/DatabaseModels/IncidentEpisodeInternalNote.ts +456 -0
  5. package/Models/DatabaseModels/IncidentEpisodeMember.ts +587 -0
  6. package/Models/DatabaseModels/IncidentEpisodeOwnerTeam.ts +421 -0
  7. package/Models/DatabaseModels/IncidentEpisodeOwnerUser.ts +419 -0
  8. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +524 -0
  9. package/Models/DatabaseModels/IncidentGroupingRule.ts +1430 -0
  10. package/Models/DatabaseModels/Index.ts +18 -0
  11. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +70 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.ts +59 -0
  13. package/Models/DatabaseModels/UserOnCallLog.ts +48 -0
  14. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +49 -0
  15. package/Models/DatabaseModels/WorkspaceNotificationLog.ts +57 -0
  16. package/Server/API/IncidentEpisodeAPI.ts +150 -0
  17. package/Server/API/SlackAPI.ts +23 -0
  18. package/Server/API/UserOnCallLogTimelineAPI.ts +24 -4
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1769626069479-MigrationName.ts +729 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1769629928240-MigrationName.ts +261 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1769676117342-RenameEvaluateOverTimeInCriteriaFilter.ts +28 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  23. package/Server/Services/BillingService.ts +1 -3
  24. package/Server/Services/CallService.ts +1 -0
  25. package/Server/Services/IncidentEpisodeFeedService.ts +94 -0
  26. package/Server/Services/IncidentEpisodeInternalNoteService.ts +71 -0
  27. package/Server/Services/IncidentEpisodeMemberService.ts +321 -0
  28. package/Server/Services/IncidentEpisodeOwnerTeamService.ts +10 -0
  29. package/Server/Services/IncidentEpisodeOwnerUserService.ts +10 -0
  30. package/Server/Services/IncidentEpisodeService.ts +1045 -0
  31. package/Server/Services/IncidentEpisodeStateTimelineService.ts +566 -0
  32. package/Server/Services/IncidentGroupingEngineService.ts +1047 -0
  33. package/Server/Services/IncidentGroupingRuleService.ts +14 -0
  34. package/Server/Services/IncidentService.ts +11 -0
  35. package/Server/Services/Index.ts +18 -0
  36. package/Server/Services/MailService.ts +1 -0
  37. package/Server/Services/MonitorService.ts +9 -0
  38. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +18 -0
  39. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +64 -2
  40. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +26 -1
  41. package/Server/Services/OnCallDutyPolicyService.ts +15 -0
  42. package/Server/Services/SmsService.ts +1 -0
  43. package/Server/Services/UserNotificationRuleService.ts +48 -2
  44. package/Server/Services/UserNotificationSettingService.ts +23 -0
  45. package/Server/Services/UserOnCallLogService.ts +41 -4
  46. package/Server/Services/WhatsAppService.ts +1 -0
  47. package/Server/Services/WorkspaceNotificationLogService.ts +16 -0
  48. package/Server/Services/WorkspaceNotificationRuleService.ts +116 -0
  49. package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +490 -0
  50. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +1 -1
  51. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  52. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +1 -1
  53. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +1 -1
  54. package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +2 -2
  55. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +182 -0
  56. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
  57. package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +1 -1
  58. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +37 -0
  59. package/Server/Utils/PushNotificationUtil.ts +31 -0
  60. package/Server/Utils/WhatsAppTemplateUtil.ts +14 -0
  61. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.ts +18 -0
  62. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +702 -0
  63. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +20 -0
  64. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +11 -0
  65. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +918 -0
  66. package/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.ts +120 -0
  67. package/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.ts +74 -0
  68. package/Types/Email/EmailTemplateType.ts +6 -0
  69. package/Types/Monitor/CriteriaFilter.ts +24 -4
  70. package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
  71. package/Types/Monitor/MonitorStep.ts +37 -0
  72. package/Types/Monitor/MonitorStepSnmpMonitor.ts +102 -0
  73. package/Types/Monitor/MonitorType.ts +15 -2
  74. package/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.ts +8 -0
  75. package/Types/Monitor/SnmpMonitor/SnmpDataType.ts +21 -0
  76. package/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.ts +16 -0
  77. package/Types/Monitor/SnmpMonitor/SnmpOid.ts +60 -0
  78. package/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.ts +7 -0
  79. package/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.ts +7 -0
  80. package/Types/Monitor/SnmpMonitor/SnmpV3Auth.ts +12 -0
  81. package/Types/Monitor/SnmpMonitor/SnmpVersion.ts +7 -0
  82. package/Types/NotificationSetting/NotificationSettingEventType.ts +7 -0
  83. package/Types/Permission.ts +311 -0
  84. package/Types/Probe/ProbeMonitorResponse.ts +2 -0
  85. package/Types/UserNotification/UserNotificationEventType.ts +1 -0
  86. package/Types/WhatsApp/WhatsAppTemplates.ts +24 -0
  87. package/Types/Workspace/NotificationRules/EventType.ts +1 -0
  88. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +38 -1
  89. package/Utils/Monitor/MonitorMetricType.ts +2 -1
  90. package/build/dist/Models/DatabaseModels/Incident.js +78 -0
  91. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  92. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1250 -0
  93. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -0
  94. package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js +555 -0
  95. package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js.map +1 -0
  96. package/build/dist/Models/DatabaseModels/IncidentEpisodeInternalNote.js +467 -0
  97. package/build/dist/Models/DatabaseModels/IncidentEpisodeInternalNote.js.map +1 -0
  98. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +607 -0
  99. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -0
  100. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerTeam.js +437 -0
  101. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerTeam.js.map +1 -0
  102. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerUser.js +436 -0
  103. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerUser.js.map +1 -0
  104. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +546 -0
  105. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -0
  106. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +1437 -0
  107. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -0
  108. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  109. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  110. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +69 -0
  111. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  112. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js +58 -0
  113. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js.map +1 -1
  114. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +47 -0
  115. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  116. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +48 -0
  117. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  118. package/build/dist/Models/DatabaseModels/WorkspaceNotificationLog.js +58 -0
  119. package/build/dist/Models/DatabaseModels/WorkspaceNotificationLog.js.map +1 -1
  120. package/build/dist/Server/API/IncidentEpisodeAPI.js +97 -0
  121. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -0
  122. package/build/dist/Server/API/SlackAPI.js +18 -0
  123. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  124. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +30 -10
  125. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
  126. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769626069479-MigrationName.js +256 -0
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769626069479-MigrationName.js.map +1 -0
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769629928240-MigrationName.js +96 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769629928240-MigrationName.js.map +1 -0
  130. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769676117342-RenameEvaluateOverTimeInCriteriaFilter.js +25 -0
  131. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769676117342-RenameEvaluateOverTimeInCriteriaFilter.js.map +1 -0
  132. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  133. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  134. package/build/dist/Server/Services/BillingService.js +1 -2
  135. package/build/dist/Server/Services/BillingService.js.map +1 -1
  136. package/build/dist/Server/Services/CallService.js.map +1 -1
  137. package/build/dist/Server/Services/IncidentEpisodeFeedService.js +83 -0
  138. package/build/dist/Server/Services/IncidentEpisodeFeedService.js.map +1 -0
  139. package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js +70 -0
  140. package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js.map +1 -0
  141. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +298 -0
  142. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -0
  143. package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js +9 -0
  144. package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js.map +1 -0
  145. package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js +9 -0
  146. package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js.map +1 -0
  147. package/build/dist/Server/Services/IncidentEpisodeService.js +933 -0
  148. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -0
  149. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +498 -0
  150. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -0
  151. package/build/dist/Server/Services/IncidentGroupingEngineService.js +799 -0
  152. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -0
  153. package/build/dist/Server/Services/IncidentGroupingRuleService.js +13 -0
  154. package/build/dist/Server/Services/IncidentGroupingRuleService.js.map +1 -0
  155. package/build/dist/Server/Services/IncidentService.js +10 -0
  156. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  157. package/build/dist/Server/Services/Index.js +16 -0
  158. package/build/dist/Server/Services/Index.js.map +1 -1
  159. package/build/dist/Server/Services/MailService.js.map +1 -1
  160. package/build/dist/Server/Services/MonitorService.js +9 -1
  161. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  162. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +10 -0
  163. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  164. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +48 -2
  165. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  166. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +20 -1
  167. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  168. package/build/dist/Server/Services/OnCallDutyPolicyService.js +8 -0
  169. package/build/dist/Server/Services/OnCallDutyPolicyService.js.map +1 -1
  170. package/build/dist/Server/Services/SmsService.js.map +1 -1
  171. package/build/dist/Server/Services/UserNotificationRuleService.js +39 -2
  172. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  173. package/build/dist/Server/Services/UserNotificationSettingService.js +9 -0
  174. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  175. package/build/dist/Server/Services/UserOnCallLogService.js +35 -3
  176. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  177. package/build/dist/Server/Services/WhatsAppService.js.map +1 -1
  178. package/build/dist/Server/Services/WorkspaceNotificationLogService.js +12 -0
  179. package/build/dist/Server/Services/WorkspaceNotificationLogService.js.map +1 -1
  180. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +95 -1
  181. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  182. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +402 -0
  183. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -0
  184. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +1 -1
  185. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  186. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +1 -1
  187. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +1 -1
  188. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +2 -2
  189. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +135 -0
  190. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -0
  191. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
  192. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  193. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +1 -1
  194. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +26 -0
  195. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  196. package/build/dist/Server/Utils/PushNotificationUtil.js +20 -0
  197. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  198. package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +8 -0
  199. package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
  200. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js +17 -0
  201. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js.map +1 -1
  202. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +547 -0
  203. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -0
  204. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +15 -0
  205. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  206. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +10 -0
  207. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  208. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +651 -0
  209. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -0
  210. package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js +100 -0
  211. package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js.map +1 -0
  212. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js +70 -0
  213. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js.map +1 -0
  214. package/build/dist/Types/Email/EmailTemplateType.js +5 -0
  215. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  216. package/build/dist/Types/Monitor/CriteriaFilter.js +16 -3
  217. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  218. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
  219. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  220. package/build/dist/Types/Monitor/MonitorStep.js +26 -0
  221. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  222. package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js +77 -0
  223. package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js.map +1 -0
  224. package/build/dist/Types/Monitor/MonitorType.js +13 -2
  225. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  226. package/build/dist/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.js +9 -0
  227. package/build/dist/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.js.map +1 -0
  228. package/build/dist/Types/Monitor/SnmpMonitor/SnmpDataType.js +22 -0
  229. package/build/dist/Types/Monitor/SnmpMonitor/SnmpDataType.js.map +1 -0
  230. package/build/dist/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.js +2 -0
  231. package/build/dist/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.js.map +1 -0
  232. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOid.js +55 -0
  233. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOid.js.map +1 -0
  234. package/build/dist/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.js +8 -0
  235. package/build/dist/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.js.map +1 -0
  236. package/build/dist/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.js +8 -0
  237. package/build/dist/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.js.map +1 -0
  238. package/build/dist/Types/Monitor/SnmpMonitor/SnmpV3Auth.js +2 -0
  239. package/build/dist/Types/Monitor/SnmpMonitor/SnmpV3Auth.js.map +1 -0
  240. package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js +8 -0
  241. package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js.map +1 -0
  242. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +5 -0
  243. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  244. package/build/dist/Types/Permission.js +264 -0
  245. package/build/dist/Types/Permission.js.map +1 -1
  246. package/build/dist/Types/UserNotification/UserNotificationEventType.js +1 -0
  247. package/build/dist/Types/UserNotification/UserNotificationEventType.js.map +1 -1
  248. package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +15 -0
  249. package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
  250. package/build/dist/Types/Workspace/NotificationRules/EventType.js +1 -0
  251. package/build/dist/Types/Workspace/NotificationRules/EventType.js.map +1 -1
  252. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +33 -1
  253. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
  254. package/build/dist/Utils/Monitor/MonitorMetricType.js +2 -1
  255. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  256. package/package.json +1 -1
@@ -0,0 +1,918 @@
1
+ import BadDataException from "../../../../../Types/Exception/BadDataException";
2
+ import ObjectID from "../../../../../Types/ObjectID";
3
+ import IncidentEpisodeService from "../../../../Services/IncidentEpisodeService";
4
+ import { ExpressRequest, ExpressResponse } from "../../../Express";
5
+ import SlackUtil from "../Slack";
6
+ import SlackActionType, { PrivateNoteEmojis } from "./ActionTypes";
7
+ import { SlackAction, SlackRequest } from "./Auth";
8
+ import Response from "../../../Response";
9
+ import {
10
+ WorkspaceDropdownBlock,
11
+ WorkspaceModalBlock,
12
+ WorkspacePayloadMarkdown,
13
+ WorkspaceTextAreaBlock,
14
+ } from "../../../../../Types/Workspace/WorkspaceMessagePayload";
15
+ import IncidentEpisodeInternalNoteService from "../../../../Services/IncidentEpisodeInternalNoteService";
16
+ import OnCallDutyPolicy from "../../../../../Models/DatabaseModels/OnCallDutyPolicy";
17
+ import OnCallDutyPolicyService from "../../../../Services/OnCallDutyPolicyService";
18
+ import { LIMIT_PER_PROJECT } from "../../../../../Types/Database/LimitMax";
19
+ import { DropdownOption } from "../../../../../UI/Components/Dropdown/Dropdown";
20
+ import UserNotificationEventType from "../../../../../Types/UserNotification/UserNotificationEventType";
21
+ import IncidentState from "../../../../../Models/DatabaseModels/IncidentState";
22
+ import IncidentStateService from "../../../../Services/IncidentStateService";
23
+ import logger from "../../../Logger";
24
+ import AccessTokenService from "../../../../Services/AccessTokenService";
25
+ import CaptureSpan from "../../../Telemetry/CaptureSpan";
26
+ import WorkspaceNotificationLogService from "../../../../Services/WorkspaceNotificationLogService";
27
+ import WorkspaceUserAuthTokenService from "../../../../Services/WorkspaceUserAuthTokenService";
28
+ import WorkspaceType from "../../../../../Types/Workspace/WorkspaceType";
29
+ import WorkspaceProjectAuthTokenService from "../../../../Services/WorkspaceProjectAuthTokenService";
30
+ import WorkspaceNotificationLog from "../../../../../Models/DatabaseModels/WorkspaceNotificationLog";
31
+ import WorkspaceProjectAuthToken from "../../../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
32
+ import WorkspaceUserAuthToken from "../../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
33
+
34
+ export default class SlackIncidentEpisodeActions {
35
+ @CaptureSpan()
36
+ public static isIncidentEpisodeAction(data: {
37
+ actionType: SlackActionType;
38
+ }): boolean {
39
+ const { actionType } = data;
40
+
41
+ switch (actionType) {
42
+ case SlackActionType.AcknowledgeIncidentEpisode:
43
+ case SlackActionType.ResolveIncidentEpisode:
44
+ case SlackActionType.ViewAddIncidentEpisodeNote:
45
+ case SlackActionType.SubmitIncidentEpisodeNote:
46
+ case SlackActionType.ViewChangeIncidentEpisodeState:
47
+ case SlackActionType.SubmitChangeIncidentEpisodeState:
48
+ case SlackActionType.ViewExecuteIncidentEpisodeOnCallPolicy:
49
+ case SlackActionType.SubmitExecuteIncidentEpisodeOnCallPolicy:
50
+ case SlackActionType.ViewIncidentEpisode:
51
+ return true;
52
+ default:
53
+ return false;
54
+ }
55
+ }
56
+
57
+ @CaptureSpan()
58
+ public static async acknowledgeIncidentEpisode(data: {
59
+ slackRequest: SlackRequest;
60
+ action: SlackAction;
61
+ req: ExpressRequest;
62
+ res: ExpressResponse;
63
+ }): Promise<void> {
64
+ const { slackRequest, req, res } = data;
65
+ const { botUserId, userId, projectAuthToken, slackUsername } = slackRequest;
66
+
67
+ const { actionValue } = data.action;
68
+
69
+ if (!actionValue) {
70
+ return Response.sendErrorResponse(
71
+ req,
72
+ res,
73
+ new BadDataException("Invalid Incident Episode ID"),
74
+ );
75
+ }
76
+
77
+ if (!userId) {
78
+ return Response.sendErrorResponse(
79
+ req,
80
+ res,
81
+ new BadDataException("Invalid User ID"),
82
+ );
83
+ }
84
+
85
+ if (!projectAuthToken) {
86
+ return Response.sendErrorResponse(
87
+ req,
88
+ res,
89
+ new BadDataException("Invalid Project Auth Token"),
90
+ );
91
+ }
92
+
93
+ if (!botUserId) {
94
+ return Response.sendErrorResponse(
95
+ req,
96
+ res,
97
+ new BadDataException("Invalid Bot User ID"),
98
+ );
99
+ }
100
+
101
+ if (data.action.actionType === SlackActionType.AcknowledgeIncidentEpisode) {
102
+ const episodeId: ObjectID = new ObjectID(actionValue);
103
+
104
+ // We send this early let slack know we're ok. We'll do the rest in the background.
105
+ Response.sendJsonObjectResponse(req, res, {
106
+ response_action: "clear",
107
+ });
108
+
109
+ const isAlreadyAcknowledged: boolean =
110
+ await IncidentEpisodeService.isEpisodeAcknowledged({
111
+ episodeId: episodeId,
112
+ });
113
+
114
+ if (isAlreadyAcknowledged) {
115
+ // send a message to the channel visible to user, that the episode has already been acknowledged.
116
+ const markdwonPayload: WorkspacePayloadMarkdown = {
117
+ _type: "WorkspacePayloadMarkdown",
118
+ text: `@${slackUsername}, unfortunately you cannot acknowledge the **[Incident Episode](${await IncidentEpisodeService.getEpisodeLinkInDashboard(slackRequest.projectId!, episodeId)})**. It has already been acknowledged.`,
119
+ };
120
+
121
+ await SlackUtil.sendDirectMessageToUser({
122
+ messageBlocks: [markdwonPayload],
123
+ authToken: projectAuthToken,
124
+ workspaceUserId: slackRequest.slackUserId!,
125
+ });
126
+
127
+ return;
128
+ }
129
+
130
+ await IncidentEpisodeService.acknowledgeEpisode(episodeId, userId);
131
+
132
+ // Log the button interaction
133
+ if (slackRequest.projectId) {
134
+ try {
135
+ const logData: {
136
+ projectId: ObjectID;
137
+ workspaceType: WorkspaceType;
138
+ channelId?: string;
139
+ userId: ObjectID;
140
+ buttonAction: string;
141
+ incidentEpisodeId?: ObjectID;
142
+ } = {
143
+ projectId: slackRequest.projectId,
144
+ workspaceType: WorkspaceType.Slack,
145
+ userId: userId,
146
+ buttonAction: "acknowledge_incident_episode",
147
+ };
148
+
149
+ if (slackRequest.slackChannelId) {
150
+ logData.channelId = slackRequest.slackChannelId;
151
+ }
152
+ logData.incidentEpisodeId = episodeId;
153
+
154
+ await WorkspaceNotificationLogService.logButtonPressed(logData, {
155
+ isRoot: true,
156
+ });
157
+ } catch (err) {
158
+ logger.error("Error logging button interaction:");
159
+ logger.error(err);
160
+ // Don't throw the error, just log it so the main flow continues
161
+ }
162
+ }
163
+
164
+ return;
165
+ }
166
+
167
+ // invalid action type.
168
+ return Response.sendErrorResponse(
169
+ req,
170
+ res,
171
+ new BadDataException("Invalid Action Type"),
172
+ );
173
+ }
174
+
175
+ @CaptureSpan()
176
+ public static async resolveIncidentEpisode(data: {
177
+ slackRequest: SlackRequest;
178
+ action: SlackAction;
179
+ req: ExpressRequest;
180
+ res: ExpressResponse;
181
+ }): Promise<void> {
182
+ const { slackRequest, req, res } = data;
183
+ const { botUserId, userId, projectAuthToken, slackUsername } = slackRequest;
184
+
185
+ const { actionValue } = data.action;
186
+
187
+ if (!actionValue) {
188
+ return Response.sendErrorResponse(
189
+ req,
190
+ res,
191
+ new BadDataException("Invalid Incident Episode ID"),
192
+ );
193
+ }
194
+
195
+ if (!userId) {
196
+ return Response.sendErrorResponse(
197
+ req,
198
+ res,
199
+ new BadDataException("Invalid User ID"),
200
+ );
201
+ }
202
+
203
+ if (!projectAuthToken) {
204
+ return Response.sendErrorResponse(
205
+ req,
206
+ res,
207
+ new BadDataException("Invalid Project Auth Token"),
208
+ );
209
+ }
210
+
211
+ if (!botUserId) {
212
+ return Response.sendErrorResponse(
213
+ req,
214
+ res,
215
+ new BadDataException("Invalid Bot User ID"),
216
+ );
217
+ }
218
+
219
+ if (data.action.actionType === SlackActionType.ResolveIncidentEpisode) {
220
+ const episodeId: ObjectID = new ObjectID(actionValue);
221
+
222
+ // We send this early let slack know we're ok. We'll do the rest in the background.
223
+ Response.sendJsonObjectResponse(req, res, {
224
+ response_action: "clear",
225
+ });
226
+
227
+ const isAlreadyResolved: boolean =
228
+ await IncidentEpisodeService.isEpisodeResolved(episodeId);
229
+
230
+ if (isAlreadyResolved) {
231
+ // send a message to the channel visible to user, that the episode has already been Resolved.
232
+ const markdwonPayload: WorkspacePayloadMarkdown = {
233
+ _type: "WorkspacePayloadMarkdown",
234
+ text: `@${slackUsername}, unfortunately you cannot resolve the **[Incident Episode](${await IncidentEpisodeService.getEpisodeLinkInDashboard(slackRequest.projectId!, episodeId)})**. It has already been resolved.`,
235
+ };
236
+
237
+ await SlackUtil.sendDirectMessageToUser({
238
+ messageBlocks: [markdwonPayload],
239
+ authToken: projectAuthToken,
240
+ workspaceUserId: slackRequest.slackUserId!,
241
+ });
242
+
243
+ return;
244
+ }
245
+
246
+ await IncidentEpisodeService.resolveEpisode(episodeId, userId);
247
+
248
+ return;
249
+ }
250
+
251
+ // invalid action type.
252
+ return Response.sendErrorResponse(
253
+ req,
254
+ res,
255
+ new BadDataException("Invalid Action Type"),
256
+ );
257
+ }
258
+
259
+ @CaptureSpan()
260
+ public static async viewExecuteOnCallPolicy(data: {
261
+ slackRequest: SlackRequest;
262
+ action: SlackAction;
263
+ req: ExpressRequest;
264
+ res: ExpressResponse;
265
+ }): Promise<void> {
266
+ const { req, res } = data;
267
+ const { actionValue } = data.action;
268
+
269
+ if (!actionValue) {
270
+ return Response.sendErrorResponse(
271
+ req,
272
+ res,
273
+ new BadDataException("Invalid Incident Episode ID"),
274
+ );
275
+ }
276
+
277
+ // We send this early let slack know we're ok. We'll do the rest in the background.
278
+ Response.sendJsonObjectResponse(req, res, {
279
+ response_action: "clear",
280
+ });
281
+
282
+ const onCallPolicies: Array<OnCallDutyPolicy> =
283
+ await OnCallDutyPolicyService.findBy({
284
+ query: {
285
+ projectId: data.slackRequest.projectId!,
286
+ },
287
+ select: {
288
+ name: true,
289
+ },
290
+ props: {
291
+ isRoot: true,
292
+ },
293
+ limit: LIMIT_PER_PROJECT,
294
+ skip: 0,
295
+ });
296
+
297
+ const dropdownOption: Array<DropdownOption> = onCallPolicies
298
+ .map((policy: OnCallDutyPolicy) => {
299
+ return {
300
+ label: policy.name || "",
301
+ value: policy._id?.toString() || "",
302
+ };
303
+ })
304
+ .filter((option: DropdownOption) => {
305
+ return option.label !== "" || option.value !== "";
306
+ });
307
+
308
+ const onCallPolicyDropdown: WorkspaceDropdownBlock = {
309
+ _type: "WorkspaceDropdownBlock",
310
+ label: "On Call Policy",
311
+ blockId: "onCallPolicy",
312
+ placeholder: "Select On Call Policy",
313
+ options: dropdownOption,
314
+ };
315
+
316
+ const modalBlock: WorkspaceModalBlock = {
317
+ _type: "WorkspaceModalBlock",
318
+ title: "Execute On Call Policy",
319
+ submitButtonTitle: "Submit",
320
+ cancelButtonTitle: "Cancel",
321
+ actionId: SlackActionType.SubmitExecuteIncidentEpisodeOnCallPolicy,
322
+ actionValue: actionValue,
323
+ blocks: [onCallPolicyDropdown],
324
+ };
325
+
326
+ await SlackUtil.showModalToUser({
327
+ authToken: data.slackRequest.projectAuthToken!,
328
+ modalBlock: modalBlock,
329
+ triggerId: data.slackRequest.triggerId!,
330
+ });
331
+ }
332
+
333
+ @CaptureSpan()
334
+ public static async viewChangeIncidentEpisodeState(data: {
335
+ slackRequest: SlackRequest;
336
+ action: SlackAction;
337
+ req: ExpressRequest;
338
+ res: ExpressResponse;
339
+ }): Promise<void> {
340
+ const { req, res } = data;
341
+ const { actionValue } = data.action;
342
+
343
+ if (!actionValue) {
344
+ return Response.sendErrorResponse(
345
+ req,
346
+ res,
347
+ new BadDataException("Invalid Incident Episode ID"),
348
+ );
349
+ }
350
+
351
+ // We send this early let slack know we're ok. We'll do the rest in the background.
352
+ Response.sendJsonObjectResponse(req, res, {
353
+ response_action: "clear",
354
+ });
355
+
356
+ // Incident Episodes use incident states
357
+ const incidentStates: Array<IncidentState> =
358
+ await IncidentStateService.getAllIncidentStates({
359
+ projectId: data.slackRequest.projectId!,
360
+ props: {
361
+ isRoot: true,
362
+ },
363
+ });
364
+
365
+ const dropdownOptions: Array<DropdownOption> = incidentStates
366
+ .map((state: IncidentState) => {
367
+ return {
368
+ label: state.name || "",
369
+ value: state._id?.toString() || "",
370
+ };
371
+ })
372
+ .filter((option: DropdownOption) => {
373
+ return option.label !== "" || option.value !== "";
374
+ });
375
+
376
+ const statePickerDropdown: WorkspaceDropdownBlock = {
377
+ _type: "WorkspaceDropdownBlock",
378
+ label: "Episode State",
379
+ blockId: "episodeState",
380
+ placeholder: "Select Episode State",
381
+ options: dropdownOptions,
382
+ };
383
+
384
+ const modalBlock: WorkspaceModalBlock = {
385
+ _type: "WorkspaceModalBlock",
386
+ title: "Change Episode State",
387
+ submitButtonTitle: "Submit",
388
+ cancelButtonTitle: "Cancel",
389
+ actionId: SlackActionType.SubmitChangeIncidentEpisodeState,
390
+ actionValue: actionValue,
391
+ blocks: [statePickerDropdown],
392
+ };
393
+
394
+ await SlackUtil.showModalToUser({
395
+ authToken: data.slackRequest.projectAuthToken!,
396
+ modalBlock: modalBlock,
397
+ triggerId: data.slackRequest.triggerId!,
398
+ });
399
+ }
400
+
401
+ @CaptureSpan()
402
+ public static async submitChangeIncidentEpisodeState(data: {
403
+ slackRequest: SlackRequest;
404
+ action: SlackAction;
405
+ req: ExpressRequest;
406
+ res: ExpressResponse;
407
+ }): Promise<void> {
408
+ const { req, res } = data;
409
+ const { actionValue } = data.action;
410
+
411
+ if (!actionValue) {
412
+ return Response.sendErrorResponse(
413
+ req,
414
+ res,
415
+ new BadDataException("Invalid Incident Episode ID"),
416
+ );
417
+ }
418
+
419
+ // We send this early let slack know we're ok. We'll do the rest in the background.
420
+ Response.sendJsonObjectResponse(req, res, {
421
+ response_action: "clear",
422
+ });
423
+
424
+ if (
425
+ !data.slackRequest.viewValues ||
426
+ !data.slackRequest.viewValues["episodeState"]
427
+ ) {
428
+ return Response.sendErrorResponse(
429
+ req,
430
+ res,
431
+ new BadDataException("Invalid View Values"),
432
+ );
433
+ }
434
+
435
+ const episodeId: ObjectID = new ObjectID(actionValue);
436
+ const stateString: string =
437
+ data.slackRequest.viewValues["episodeState"].toString();
438
+
439
+ const stateId: ObjectID = new ObjectID(stateString);
440
+
441
+ await IncidentEpisodeService.updateOneById({
442
+ id: episodeId,
443
+ data: {
444
+ currentIncidentStateId: stateId,
445
+ },
446
+ props:
447
+ await AccessTokenService.getDatabaseCommonInteractionPropsByUserAndProject(
448
+ {
449
+ userId: data.slackRequest.userId!,
450
+ projectId: data.slackRequest.projectId!,
451
+ },
452
+ ),
453
+ });
454
+
455
+ // Log the button interaction
456
+ if (data.slackRequest.projectId && data.slackRequest.userId) {
457
+ try {
458
+ const logData: {
459
+ projectId: ObjectID;
460
+ workspaceType: WorkspaceType;
461
+ channelId?: string;
462
+ userId: ObjectID;
463
+ buttonAction: string;
464
+ incidentEpisodeId?: ObjectID;
465
+ } = {
466
+ projectId: data.slackRequest.projectId,
467
+ workspaceType: WorkspaceType.Slack,
468
+ userId: data.slackRequest.userId,
469
+ buttonAction: "change_incident_episode_state",
470
+ };
471
+
472
+ if (data.slackRequest.slackChannelId) {
473
+ logData.channelId = data.slackRequest.slackChannelId;
474
+ }
475
+ logData.incidentEpisodeId = episodeId;
476
+
477
+ await WorkspaceNotificationLogService.logButtonPressed(logData, {
478
+ isRoot: true,
479
+ });
480
+ } catch (err) {
481
+ logger.error("Error logging button interaction:");
482
+ logger.error(err);
483
+ // Don't throw the error, just log it so the main flow continues
484
+ }
485
+ }
486
+ }
487
+
488
+ @CaptureSpan()
489
+ public static async executeOnCallPolicy(data: {
490
+ slackRequest: SlackRequest;
491
+ action: SlackAction;
492
+ req: ExpressRequest;
493
+ res: ExpressResponse;
494
+ }): Promise<void> {
495
+ const { slackRequest, req, res } = data;
496
+ const { botUserId, userId, projectAuthToken, slackUsername } = slackRequest;
497
+
498
+ const { actionValue } = data.action;
499
+
500
+ if (!actionValue) {
501
+ return Response.sendErrorResponse(
502
+ req,
503
+ res,
504
+ new BadDataException("Invalid Incident Episode ID"),
505
+ );
506
+ }
507
+
508
+ if (!userId) {
509
+ return Response.sendErrorResponse(
510
+ req,
511
+ res,
512
+ new BadDataException("Invalid User ID"),
513
+ );
514
+ }
515
+
516
+ if (!projectAuthToken) {
517
+ return Response.sendErrorResponse(
518
+ req,
519
+ res,
520
+ new BadDataException("Invalid Project Auth Token"),
521
+ );
522
+ }
523
+
524
+ if (!botUserId) {
525
+ return Response.sendErrorResponse(
526
+ req,
527
+ res,
528
+ new BadDataException("Invalid Bot User ID"),
529
+ );
530
+ }
531
+
532
+ if (
533
+ data.action.actionType ===
534
+ SlackActionType.SubmitExecuteIncidentEpisodeOnCallPolicy
535
+ ) {
536
+ const episodeId: ObjectID = new ObjectID(actionValue);
537
+
538
+ // We send this early let slack know we're ok. We'll do the rest in the background.
539
+ Response.sendJsonObjectResponse(req, res, {
540
+ response_action: "clear",
541
+ });
542
+
543
+ const isAlreadyResolved: boolean =
544
+ await IncidentEpisodeService.isEpisodeResolved(episodeId);
545
+
546
+ if (isAlreadyResolved) {
547
+ // send a message to the channel visible to user, that the episode has already been Resolved.
548
+ const markdwonPayload: WorkspacePayloadMarkdown = {
549
+ _type: "WorkspacePayloadMarkdown",
550
+ text: `@${slackUsername}, unfortunately you cannot execute the on-call policy for **[Incident Episode](${await IncidentEpisodeService.getEpisodeLinkInDashboard(slackRequest.projectId!, episodeId)})**. It has already been resolved.`,
551
+ };
552
+
553
+ await SlackUtil.sendDirectMessageToUser({
554
+ messageBlocks: [markdwonPayload],
555
+ authToken: projectAuthToken,
556
+ workspaceUserId: slackRequest.slackUserId!,
557
+ });
558
+
559
+ return;
560
+ }
561
+
562
+ if (
563
+ !data.slackRequest.viewValues ||
564
+ !data.slackRequest.viewValues["onCallPolicy"]
565
+ ) {
566
+ return Response.sendErrorResponse(
567
+ req,
568
+ res,
569
+ new BadDataException("Invalid View Values"),
570
+ );
571
+ }
572
+
573
+ const onCallPolicyString: string =
574
+ data.slackRequest.viewValues["onCallPolicy"].toString();
575
+
576
+ // get the on-call policy id.
577
+ const onCallPolicyId: ObjectID = new ObjectID(onCallPolicyString);
578
+
579
+ await OnCallDutyPolicyService.executePolicy(onCallPolicyId, {
580
+ triggeredByIncidentEpisodeId: episodeId,
581
+ userNotificationEventType:
582
+ UserNotificationEventType.IncidentEpisodeCreated,
583
+ });
584
+ }
585
+ }
586
+
587
+ @CaptureSpan()
588
+ public static async submitIncidentEpisodeNote(data: {
589
+ slackRequest: SlackRequest;
590
+ action: SlackAction;
591
+ req: ExpressRequest;
592
+ res: ExpressResponse;
593
+ }): Promise<void> {
594
+ const { req, res } = data;
595
+ const { actionValue } = data.action;
596
+
597
+ if (!actionValue) {
598
+ return Response.sendErrorResponse(
599
+ req,
600
+ res,
601
+ new BadDataException("Invalid Incident Episode ID"),
602
+ );
603
+ }
604
+
605
+ if (!data.slackRequest.viewValues) {
606
+ return Response.sendErrorResponse(
607
+ req,
608
+ res,
609
+ new BadDataException("Invalid View Values"),
610
+ );
611
+ }
612
+
613
+ if (!data.slackRequest.viewValues["note"]) {
614
+ return Response.sendErrorResponse(
615
+ req,
616
+ res,
617
+ new BadDataException("Invalid Note"),
618
+ );
619
+ }
620
+
621
+ const episodeId: ObjectID = new ObjectID(actionValue);
622
+ const note: string = data.slackRequest.viewValues["note"].toString();
623
+
624
+ // send empty response.
625
+ Response.sendJsonObjectResponse(req, res, {
626
+ response_action: "clear",
627
+ });
628
+
629
+ await IncidentEpisodeInternalNoteService.addNote({
630
+ incidentEpisodeId: episodeId!,
631
+ note: note || "",
632
+ projectId: data.slackRequest.projectId!,
633
+ userId: data.slackRequest.userId!,
634
+ });
635
+ }
636
+
637
+ @CaptureSpan()
638
+ public static async viewAddIncidentEpisodeNote(data: {
639
+ slackRequest: SlackRequest;
640
+ action: SlackAction;
641
+ req: ExpressRequest;
642
+ res: ExpressResponse;
643
+ }): Promise<void> {
644
+ const { req, res } = data;
645
+ const { actionValue } = data.action;
646
+
647
+ if (!actionValue) {
648
+ return Response.sendErrorResponse(
649
+ req,
650
+ res,
651
+ new BadDataException("Invalid Incident Episode ID"),
652
+ );
653
+ }
654
+
655
+ // We send this early let slack know we're ok. We'll do the rest in the background.
656
+ Response.sendJsonObjectResponse(req, res, {
657
+ response_action: "clear",
658
+ });
659
+
660
+ const noteTextArea: WorkspaceTextAreaBlock = {
661
+ _type: "WorkspaceTextAreaBlock",
662
+ label: "Note",
663
+ blockId: "note",
664
+ placeholder: "Note",
665
+ description: "Please type in plain text or markdown.",
666
+ };
667
+
668
+ const modalBlock: WorkspaceModalBlock = {
669
+ _type: "WorkspaceModalBlock",
670
+ title: "Add Note",
671
+ submitButtonTitle: "Submit",
672
+ cancelButtonTitle: "Cancel",
673
+ actionId: SlackActionType.SubmitIncidentEpisodeNote,
674
+ actionValue: actionValue,
675
+ blocks: [noteTextArea],
676
+ };
677
+
678
+ await SlackUtil.showModalToUser({
679
+ authToken: data.slackRequest.projectAuthToken!,
680
+ modalBlock: modalBlock,
681
+ triggerId: data.slackRequest.triggerId!,
682
+ });
683
+ }
684
+
685
+ @CaptureSpan()
686
+ public static async handleIncidentEpisodeAction(data: {
687
+ slackRequest: SlackRequest;
688
+ action: SlackAction;
689
+ req: ExpressRequest;
690
+ res: ExpressResponse;
691
+ }): Promise<void> {
692
+ const actionType: SlackActionType | undefined = data.action.actionType;
693
+
694
+ if (actionType === SlackActionType.AcknowledgeIncidentEpisode) {
695
+ return await this.acknowledgeIncidentEpisode(data);
696
+ }
697
+
698
+ if (actionType === SlackActionType.ResolveIncidentEpisode) {
699
+ return await this.resolveIncidentEpisode(data);
700
+ }
701
+
702
+ if (actionType === SlackActionType.ViewAddIncidentEpisodeNote) {
703
+ return await this.viewAddIncidentEpisodeNote(data);
704
+ }
705
+
706
+ if (actionType === SlackActionType.SubmitIncidentEpisodeNote) {
707
+ return await this.submitIncidentEpisodeNote(data);
708
+ }
709
+
710
+ if (actionType === SlackActionType.ViewExecuteIncidentEpisodeOnCallPolicy) {
711
+ return await this.viewExecuteOnCallPolicy(data);
712
+ }
713
+
714
+ if (
715
+ actionType === SlackActionType.SubmitExecuteIncidentEpisodeOnCallPolicy
716
+ ) {
717
+ return await this.executeOnCallPolicy(data);
718
+ }
719
+
720
+ if (actionType === SlackActionType.ViewChangeIncidentEpisodeState) {
721
+ return await this.viewChangeIncidentEpisodeState(data);
722
+ }
723
+
724
+ if (actionType === SlackActionType.SubmitChangeIncidentEpisodeState) {
725
+ return await this.submitChangeIncidentEpisodeState(data);
726
+ }
727
+
728
+ if (actionType === SlackActionType.ViewIncidentEpisode) {
729
+ // do nothing. This is just a view episode action.
730
+ return Response.sendJsonObjectResponse(data.req, data.res, {
731
+ response_action: "clear",
732
+ });
733
+ }
734
+
735
+ // invalid action type.
736
+ return Response.sendErrorResponse(
737
+ data.req,
738
+ data.res,
739
+ new BadDataException("Invalid Action Type"),
740
+ );
741
+ }
742
+
743
+ @CaptureSpan()
744
+ public static async handleEmojiReaction(data: {
745
+ teamId: string;
746
+ reaction: string;
747
+ userId: string;
748
+ channelId: string;
749
+ messageTs: string;
750
+ }): Promise<void> {
751
+ logger.debug("Handling emoji reaction for Incident Episode with data:");
752
+ logger.debug(data);
753
+
754
+ const { teamId, reaction, userId, channelId, messageTs } = data;
755
+
756
+ // Incident Episodes only support private notes
757
+ const isPrivateNoteEmoji: boolean = PrivateNoteEmojis.includes(reaction);
758
+
759
+ if (!isPrivateNoteEmoji) {
760
+ logger.debug(
761
+ `Emoji "${reaction}" is not a supported private note emoji for incident episodes. Ignoring.`,
762
+ );
763
+ return;
764
+ }
765
+
766
+ // Get the project auth token using the team ID
767
+ const projectAuth: WorkspaceProjectAuthToken | null =
768
+ await WorkspaceProjectAuthTokenService.findOneBy({
769
+ query: {
770
+ workspaceProjectId: teamId,
771
+ },
772
+ select: {
773
+ projectId: true,
774
+ authToken: true,
775
+ },
776
+ props: {
777
+ isRoot: true,
778
+ },
779
+ });
780
+
781
+ if (!projectAuth || !projectAuth.projectId || !projectAuth.authToken) {
782
+ logger.debug(
783
+ "No project auth found for team ID. Ignoring emoji reaction.",
784
+ );
785
+ return;
786
+ }
787
+
788
+ const projectId: ObjectID = projectAuth.projectId;
789
+ const authToken: string = projectAuth.authToken;
790
+
791
+ // Find the incident episode linked to this channel
792
+ const workspaceLog: WorkspaceNotificationLog | null =
793
+ await WorkspaceNotificationLogService.findOneBy({
794
+ query: {
795
+ channelId: channelId,
796
+ workspaceType: WorkspaceType.Slack,
797
+ projectId: projectId,
798
+ },
799
+ select: {
800
+ incidentEpisodeId: true,
801
+ },
802
+ props: {
803
+ isRoot: true,
804
+ },
805
+ });
806
+
807
+ if (!workspaceLog || !workspaceLog.incidentEpisodeId) {
808
+ logger.debug(
809
+ "No incident episode found linked to this channel. Ignoring emoji reaction.",
810
+ );
811
+ return;
812
+ }
813
+
814
+ const episodeId: ObjectID = workspaceLog.incidentEpisodeId;
815
+
816
+ // Get the user ID in OneUptime based on Slack user ID
817
+ const userAuth: WorkspaceUserAuthToken | null =
818
+ await WorkspaceUserAuthTokenService.findOneBy({
819
+ query: {
820
+ workspaceUserId: userId,
821
+ workspaceType: WorkspaceType.Slack,
822
+ projectId: projectId,
823
+ },
824
+ select: {
825
+ userId: true,
826
+ },
827
+ props: {
828
+ isRoot: true,
829
+ },
830
+ });
831
+
832
+ if (!userAuth || !userAuth.userId) {
833
+ logger.debug(
834
+ "No OneUptime user found for Slack user. Ignoring emoji reaction.",
835
+ );
836
+ return;
837
+ }
838
+
839
+ const oneUptimeUserId: ObjectID = userAuth.userId;
840
+
841
+ // Fetch the message text using the timestamp
842
+ let messageText: string | null = null;
843
+ try {
844
+ messageText = await SlackUtil.getMessageByTimestamp({
845
+ authToken: authToken,
846
+ channelId: channelId,
847
+ messageTs: messageTs,
848
+ });
849
+ } catch (err) {
850
+ logger.error("Error fetching message text:");
851
+ logger.error(err);
852
+ return;
853
+ }
854
+
855
+ if (!messageText) {
856
+ logger.debug("No message text found. Ignoring emoji reaction.");
857
+ return;
858
+ }
859
+
860
+ // Create a unique identifier for this Slack message to prevent duplicate notes
861
+ const postedFromSlackMessageId: string = `${channelId}:${messageTs}`;
862
+
863
+ // Check if a note from this Slack message already exists
864
+ const hasExistingNote: boolean =
865
+ await IncidentEpisodeInternalNoteService.hasNoteFromSlackMessage({
866
+ incidentEpisodeId: episodeId,
867
+ postedFromSlackMessageId: postedFromSlackMessageId,
868
+ });
869
+
870
+ if (hasExistingNote) {
871
+ logger.debug(
872
+ "Private note from this Slack message already exists. Skipping duplicate.",
873
+ );
874
+ return;
875
+ }
876
+
877
+ // Save as private note
878
+ try {
879
+ await IncidentEpisodeInternalNoteService.addNote({
880
+ incidentEpisodeId: episodeId,
881
+ note: messageText,
882
+ projectId: projectId,
883
+ userId: oneUptimeUserId,
884
+ postedFromSlackMessageId: postedFromSlackMessageId,
885
+ });
886
+ logger.debug("Private note added to incident episode successfully.");
887
+ } catch (err) {
888
+ logger.error("Error saving note:");
889
+ logger.error(err);
890
+ return;
891
+ }
892
+
893
+ // Send confirmation message as a reply to the original message thread
894
+ try {
895
+ const episodeLink: string = (
896
+ await IncidentEpisodeService.getEpisodeLinkInDashboard(
897
+ projectId,
898
+ episodeId,
899
+ )
900
+ ).toString();
901
+
902
+ const confirmationMessage: string = `Message saved as *private note* to <${episodeLink}|Incident Episode>.`;
903
+
904
+ await SlackUtil.sendMessageToThread({
905
+ authToken: authToken,
906
+ channelId: channelId,
907
+ threadTs: messageTs,
908
+ text: confirmationMessage,
909
+ });
910
+
911
+ logger.debug("Confirmation message sent successfully.");
912
+ } catch (err) {
913
+ logger.error("Error sending confirmation message:");
914
+ logger.error(err);
915
+ // Don't throw - note was saved successfully, confirmation is best effort
916
+ }
917
+ }
918
+ }