@oneuptime/common 12.0.18 → 12.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/Models/DatabaseModels/EnterpriseLicense.ts +23 -0
  2. package/Models/DatabaseModels/Index.ts +4 -2
  3. package/Models/DatabaseModels/Monitor.ts +39 -9
  4. package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
  5. package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
  6. package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
  8. package/Models/DatabaseModels/UserSlack.ts +341 -0
  9. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
  10. package/Server/API/EnterpriseLicenseAPI.ts +94 -19
  11. package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
  12. package/Server/API/UserSlackAPI.ts +125 -0
  13. package/Server/EnvironmentConfig.ts +29 -107
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  19. package/Server/Infrastructure/Queue.ts +7 -0
  20. package/Server/Services/EnterpriseLicenseService.ts +60 -0
  21. package/Server/Services/Index.ts +6 -2
  22. package/Server/Services/LlmLogService.ts +25 -9
  23. package/Server/Services/OnCallReadinessService.ts +86 -2
  24. package/Server/Services/ProjectService.ts +171 -0
  25. package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
  26. package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
  27. package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
  28. package/Server/Services/UserNotificationRuleService.ts +589 -11
  29. package/Server/Services/UserNotificationSettingService.ts +138 -0
  30. package/Server/Services/UserService.ts +28 -0
  31. package/Server/Services/UserSlackService.ts +218 -0
  32. package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
  33. package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
  34. package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
  35. package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
  36. package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
  37. package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
  38. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
  39. package/Server/Utils/Response.ts +2 -2
  40. package/Server/Utils/StartServer.ts +2 -2
  41. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
  42. package/Server/Views/Partials/AnalyticsConsent.ejs +533 -0
  43. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
  44. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
  45. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +465 -0
  46. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +302 -0
  47. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +408 -0
  48. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +474 -0
  49. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +368 -0
  50. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
  51. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
  52. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
  53. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
  54. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +763 -0
  55. package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
  56. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
  57. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
  58. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
  59. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
  60. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
  61. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
  62. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
  63. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
  64. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
  65. package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
  66. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +138 -0
  67. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +516 -0
  68. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
  69. package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
  70. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
  71. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
  72. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
  73. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
  74. package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
  75. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
  76. package/Tests/Server/Services/UserSlackService.test.ts +407 -0
  77. package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
  78. package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
  79. package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
  80. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
  81. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
  82. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
  83. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
  84. package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
  85. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
  86. package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
  87. package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
  88. package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
  89. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +53 -0
  90. package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
  91. package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
  92. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
  93. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
  94. package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
  95. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
  96. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +241 -0
  97. package/Tests/UI/Components/Charts/ChartTooltipSorted.test.tsx +129 -0
  98. package/Tests/UI/Components/Charts/TooltipEntries.test.ts +155 -0
  99. package/Tests/UI/Components/Detail/DetailFieldErrors.test.tsx +347 -0
  100. package/Tests/UI/Components/Forms/BasicFormStepValidation.test.tsx +392 -0
  101. package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +549 -0
  102. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
  103. package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
  104. package/Tests/Utils/EnterpriseLicenseSync.test.ts +562 -0
  105. package/Types/Analytics/RevenueEvent.ts +1 -0
  106. package/Types/Marketing/MarketingEvent.ts +140 -0
  107. package/UI/Components/Charts/Area/AreaChart.tsx +8 -1
  108. package/UI/Components/Charts/Bar/BarChart.tsx +8 -1
  109. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +19 -4
  110. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +20 -2
  111. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +19 -4
  112. package/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.ts +105 -0
  113. package/UI/Components/Charts/Line/LineChart.tsx +8 -1
  114. package/UI/Components/Detail/Detail.tsx +18 -1
  115. package/UI/Utils/NotificationMethodUtil.ts +41 -1
  116. package/UI/Utils/PermissionGate.ts +56 -0
  117. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +244 -0
  118. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
  119. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  120. package/build/dist/Models/DatabaseModels/Index.js +4 -2
  121. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  122. package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
  123. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  124. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
  125. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
  126. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
  127. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  128. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
  129. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
  130. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
  131. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  132. package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
  133. package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
  134. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  135. package/build/dist/Server/API/EnterpriseLicenseAPI.js +66 -12
  136. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  137. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
  138. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
  139. package/build/dist/Server/API/UserSlackAPI.js +81 -0
  140. package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
  141. package/build/dist/Server/EnvironmentConfig.js +25 -70
  142. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  143. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
  144. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
  146. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
  147. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
  148. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
  149. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
  150. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
  151. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  152. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  153. package/build/dist/Server/Infrastructure/Queue.js +7 -0
  154. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  155. package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
  156. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  157. package/build/dist/Server/Services/Index.js +6 -2
  158. package/build/dist/Server/Services/Index.js.map +1 -1
  159. package/build/dist/Server/Services/LlmLogService.js +25 -9
  160. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  161. package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
  162. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
  163. package/build/dist/Server/Services/ProjectService.js +144 -0
  164. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  165. package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
  166. package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
  167. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
  168. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  169. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
  170. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  171. package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
  172. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  173. package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
  174. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  175. package/build/dist/Server/Services/UserService.js +26 -1
  176. package/build/dist/Server/Services/UserService.js.map +1 -1
  177. package/build/dist/Server/Services/UserSlackService.js +195 -0
  178. package/build/dist/Server/Services/UserSlackService.js.map +1 -0
  179. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
  180. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  181. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
  182. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
  183. package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
  184. package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
  185. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
  186. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
  187. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
  188. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
  189. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
  190. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
  192. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
  193. package/build/dist/Server/Utils/Response.js +2 -2
  194. package/build/dist/Server/Utils/Response.js.map +1 -1
  195. package/build/dist/Server/Utils/StartServer.js +2 -2
  196. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  197. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
  198. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  199. package/build/dist/Types/Analytics/RevenueEvent.js +1 -0
  200. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -1
  201. package/build/dist/Types/Marketing/MarketingEvent.js +82 -0
  202. package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
  203. package/build/dist/UI/Components/Charts/Area/AreaChart.js +9 -1
  204. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  205. package/build/dist/UI/Components/Charts/Bar/BarChart.js +9 -1
  206. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  207. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +22 -13
  208. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  209. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +27 -15
  210. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  211. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +27 -18
  212. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  213. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js +71 -0
  214. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js.map +1 -0
  215. package/build/dist/UI/Components/Charts/Line/LineChart.js +9 -1
  216. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  217. package/build/dist/UI/Components/Detail/Detail.js +17 -1
  218. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  219. package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
  220. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
  221. package/build/dist/UI/Utils/PermissionGate.js +32 -0
  222. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  223. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +174 -0
  224. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +1 -0
  225. package/package.json +1 -1
  226. package/Models/DatabaseModels/MarketingConversion.ts +0 -410
  227. package/Server/Services/MarketingConversionService.ts +0 -10
  228. package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
  229. package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
  230. package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
  231. package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
  232. package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
  233. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
  234. package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
  235. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
  236. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
  237. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
  238. package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
  239. package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
  240. package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
  241. package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
  242. package/Types/Marketing/MarketingConversion.ts +0 -53
  243. package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
  244. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
  245. package/build/dist/Server/Services/MarketingConversionService.js +0 -9
  246. package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
  247. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
  248. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
  249. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
  250. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
  251. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
  252. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
  253. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
  254. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
  255. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
  256. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
  257. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
  258. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
  259. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
  260. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
  261. package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
  262. package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
@@ -0,0 +1,197 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ /*
4
+ * Slack and Microsoft Teams as user notification methods.
5
+ *
6
+ * Two new per-user method tables (UserSlack / UserMicrosoftTeams — pointers at
7
+ * the user's OAuth workspace link, created verified), the notification-rule
8
+ * and on-call timeline foreign keys that let rules route pages to them, and
9
+ * the two per-event toggles on UserNotificationSetting.
10
+ */
11
+ export class AddUserSlackAndMicrosoftTeams1789000000000
12
+ implements MigrationInterface
13
+ {
14
+ public name = "AddUserSlackAndMicrosoftTeams1789000000000";
15
+
16
+ public async up(queryRunner: QueryRunner): Promise<void> {
17
+ await queryRunner.query(
18
+ `CREATE TABLE "UserSlack" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "slackUserId" character varying(100), "slackUserName" character varying(100), "userId" uuid, "createdByUserId" uuid, "deletedByUserId" uuid, "isVerified" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_86de4cd76b41a08811fc403e23a" PRIMARY KEY ("_id"))`,
19
+ );
20
+ await queryRunner.query(
21
+ `CREATE INDEX "IDX_d5dd2503bf422c6a19946216e1" ON "UserSlack" ("projectId") `,
22
+ );
23
+ await queryRunner.query(
24
+ `CREATE INDEX "IDX_abe4265455b18cd43a94bb84ed" ON "UserSlack" ("slackUserId") `,
25
+ );
26
+ await queryRunner.query(
27
+ `CREATE INDEX "IDX_9c8fa391e949f53f670dabe222" ON "UserSlack" ("userId") `,
28
+ );
29
+ await queryRunner.query(
30
+ `CREATE TABLE "UserMicrosoftTeams" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "microsoftTeamsUserId" character varying(100), "microsoftTeamsUserName" character varying(100), "userId" uuid, "createdByUserId" uuid, "deletedByUserId" uuid, "isVerified" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_9e2c27aadbc24a991c68e55c130" PRIMARY KEY ("_id"))`,
31
+ );
32
+ await queryRunner.query(
33
+ `CREATE INDEX "IDX_cebf1edfa5f30a7e597633bddf" ON "UserMicrosoftTeams" ("projectId") `,
34
+ );
35
+ await queryRunner.query(
36
+ `CREATE INDEX "IDX_a746ef4fd29b03ac5abe8f4e4c" ON "UserMicrosoftTeams" ("microsoftTeamsUserId") `,
37
+ );
38
+ await queryRunner.query(
39
+ `CREATE INDEX "IDX_e4405f9dacd3d1e3c363e8dbd3" ON "UserMicrosoftTeams" ("userId") `,
40
+ );
41
+ await queryRunner.query(
42
+ `ALTER TABLE "UserNotificationRule" ADD "userSlackId" uuid`,
43
+ );
44
+ await queryRunner.query(
45
+ `ALTER TABLE "UserNotificationRule" ADD "userMicrosoftTeamsId" uuid`,
46
+ );
47
+ await queryRunner.query(
48
+ `ALTER TABLE "UserOnCallLogTimeline" ADD "userSlackId" uuid`,
49
+ );
50
+ await queryRunner.query(
51
+ `ALTER TABLE "UserOnCallLogTimeline" ADD "userMicrosoftTeamsId" uuid`,
52
+ );
53
+ await queryRunner.query(
54
+ `ALTER TABLE "UserNotificationSetting" ADD "alertBySlack" boolean NOT NULL DEFAULT false`,
55
+ );
56
+ await queryRunner.query(
57
+ `ALTER TABLE "UserNotificationSetting" ADD "alertByMicrosoftTeams" boolean NOT NULL DEFAULT false`,
58
+ );
59
+ await queryRunner.query(
60
+ `CREATE INDEX "IDX_0c24badba9b152d08700f7d8aa" ON "UserNotificationRule" ("userSlackId") `,
61
+ );
62
+ await queryRunner.query(
63
+ `CREATE INDEX "IDX_3dc4e1c367eab9fd36b7983e44" ON "UserNotificationRule" ("userMicrosoftTeamsId") `,
64
+ );
65
+ await queryRunner.query(
66
+ `CREATE INDEX "IDX_d51ef7b2a8b813d37e94890ff7" ON "UserOnCallLogTimeline" ("userSlackId") `,
67
+ );
68
+ await queryRunner.query(
69
+ `CREATE INDEX "IDX_a7206c19df5cdfe02cf3215a64" ON "UserOnCallLogTimeline" ("userMicrosoftTeamsId") `,
70
+ );
71
+ await queryRunner.query(
72
+ `ALTER TABLE "UserSlack" ADD CONSTRAINT "FK_d5dd2503bf422c6a19946216e19" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
73
+ );
74
+ await queryRunner.query(
75
+ `ALTER TABLE "UserSlack" ADD CONSTRAINT "FK_9c8fa391e949f53f670dabe2220" FOREIGN KEY ("userId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
76
+ );
77
+ await queryRunner.query(
78
+ `ALTER TABLE "UserSlack" ADD CONSTRAINT "FK_fd38a9ee050702437ed323f21fb" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
79
+ );
80
+ await queryRunner.query(
81
+ `ALTER TABLE "UserSlack" ADD CONSTRAINT "FK_2f058862227400121f90925d65e" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
82
+ );
83
+ await queryRunner.query(
84
+ `ALTER TABLE "UserMicrosoftTeams" ADD CONSTRAINT "FK_cebf1edfa5f30a7e597633bddf9" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
85
+ );
86
+ await queryRunner.query(
87
+ `ALTER TABLE "UserMicrosoftTeams" ADD CONSTRAINT "FK_e4405f9dacd3d1e3c363e8dbd30" FOREIGN KEY ("userId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
88
+ );
89
+ await queryRunner.query(
90
+ `ALTER TABLE "UserMicrosoftTeams" ADD CONSTRAINT "FK_81443b1ab79d63b7700f3d16c84" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
91
+ );
92
+ await queryRunner.query(
93
+ `ALTER TABLE "UserMicrosoftTeams" ADD CONSTRAINT "FK_1d7cfdf5a4e314cf4b4cc7873e0" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
94
+ );
95
+ await queryRunner.query(
96
+ `ALTER TABLE "UserNotificationRule" ADD CONSTRAINT "FK_0c24badba9b152d08700f7d8aa8" FOREIGN KEY ("userSlackId") REFERENCES "UserSlack"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
97
+ );
98
+ await queryRunner.query(
99
+ `ALTER TABLE "UserNotificationRule" ADD CONSTRAINT "FK_3dc4e1c367eab9fd36b7983e444" FOREIGN KEY ("userMicrosoftTeamsId") REFERENCES "UserMicrosoftTeams"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
100
+ );
101
+ await queryRunner.query(
102
+ `ALTER TABLE "UserOnCallLogTimeline" ADD CONSTRAINT "FK_d51ef7b2a8b813d37e94890ff77" FOREIGN KEY ("userSlackId") REFERENCES "UserSlack"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
103
+ );
104
+ await queryRunner.query(
105
+ `ALTER TABLE "UserOnCallLogTimeline" ADD CONSTRAINT "FK_a7206c19df5cdfe02cf3215a64c" FOREIGN KEY ("userMicrosoftTeamsId") REFERENCES "UserMicrosoftTeams"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
106
+ );
107
+ }
108
+
109
+ public async down(queryRunner: QueryRunner): Promise<void> {
110
+ await queryRunner.query(
111
+ `ALTER TABLE "UserOnCallLogTimeline" DROP CONSTRAINT "FK_a7206c19df5cdfe02cf3215a64c"`,
112
+ );
113
+ await queryRunner.query(
114
+ `ALTER TABLE "UserOnCallLogTimeline" DROP CONSTRAINT "FK_d51ef7b2a8b813d37e94890ff77"`,
115
+ );
116
+ await queryRunner.query(
117
+ `ALTER TABLE "UserNotificationRule" DROP CONSTRAINT "FK_3dc4e1c367eab9fd36b7983e444"`,
118
+ );
119
+ await queryRunner.query(
120
+ `ALTER TABLE "UserNotificationRule" DROP CONSTRAINT "FK_0c24badba9b152d08700f7d8aa8"`,
121
+ );
122
+ await queryRunner.query(
123
+ `ALTER TABLE "UserMicrosoftTeams" DROP CONSTRAINT "FK_1d7cfdf5a4e314cf4b4cc7873e0"`,
124
+ );
125
+ await queryRunner.query(
126
+ `ALTER TABLE "UserMicrosoftTeams" DROP CONSTRAINT "FK_81443b1ab79d63b7700f3d16c84"`,
127
+ );
128
+ await queryRunner.query(
129
+ `ALTER TABLE "UserMicrosoftTeams" DROP CONSTRAINT "FK_e4405f9dacd3d1e3c363e8dbd30"`,
130
+ );
131
+ await queryRunner.query(
132
+ `ALTER TABLE "UserMicrosoftTeams" DROP CONSTRAINT "FK_cebf1edfa5f30a7e597633bddf9"`,
133
+ );
134
+ await queryRunner.query(
135
+ `ALTER TABLE "UserSlack" DROP CONSTRAINT "FK_2f058862227400121f90925d65e"`,
136
+ );
137
+ await queryRunner.query(
138
+ `ALTER TABLE "UserSlack" DROP CONSTRAINT "FK_fd38a9ee050702437ed323f21fb"`,
139
+ );
140
+ await queryRunner.query(
141
+ `ALTER TABLE "UserSlack" DROP CONSTRAINT "FK_9c8fa391e949f53f670dabe2220"`,
142
+ );
143
+ await queryRunner.query(
144
+ `ALTER TABLE "UserSlack" DROP CONSTRAINT "FK_d5dd2503bf422c6a19946216e19"`,
145
+ );
146
+ await queryRunner.query(
147
+ `DROP INDEX "public"."IDX_a7206c19df5cdfe02cf3215a64"`,
148
+ );
149
+ await queryRunner.query(
150
+ `DROP INDEX "public"."IDX_d51ef7b2a8b813d37e94890ff7"`,
151
+ );
152
+ await queryRunner.query(
153
+ `DROP INDEX "public"."IDX_3dc4e1c367eab9fd36b7983e44"`,
154
+ );
155
+ await queryRunner.query(
156
+ `DROP INDEX "public"."IDX_0c24badba9b152d08700f7d8aa"`,
157
+ );
158
+ await queryRunner.query(
159
+ `ALTER TABLE "UserNotificationSetting" DROP COLUMN "alertByMicrosoftTeams"`,
160
+ );
161
+ await queryRunner.query(
162
+ `ALTER TABLE "UserNotificationSetting" DROP COLUMN "alertBySlack"`,
163
+ );
164
+ await queryRunner.query(
165
+ `ALTER TABLE "UserOnCallLogTimeline" DROP COLUMN "userMicrosoftTeamsId"`,
166
+ );
167
+ await queryRunner.query(
168
+ `ALTER TABLE "UserOnCallLogTimeline" DROP COLUMN "userSlackId"`,
169
+ );
170
+ await queryRunner.query(
171
+ `ALTER TABLE "UserNotificationRule" DROP COLUMN "userMicrosoftTeamsId"`,
172
+ );
173
+ await queryRunner.query(
174
+ `ALTER TABLE "UserNotificationRule" DROP COLUMN "userSlackId"`,
175
+ );
176
+ await queryRunner.query(
177
+ `DROP INDEX "public"."IDX_e4405f9dacd3d1e3c363e8dbd3"`,
178
+ );
179
+ await queryRunner.query(
180
+ `DROP INDEX "public"."IDX_a746ef4fd29b03ac5abe8f4e4c"`,
181
+ );
182
+ await queryRunner.query(
183
+ `DROP INDEX "public"."IDX_cebf1edfa5f30a7e597633bddf"`,
184
+ );
185
+ await queryRunner.query(`DROP TABLE "UserMicrosoftTeams"`);
186
+ await queryRunner.query(
187
+ `DROP INDEX "public"."IDX_9c8fa391e949f53f670dabe222"`,
188
+ );
189
+ await queryRunner.query(
190
+ `DROP INDEX "public"."IDX_abe4265455b18cd43a94bb84ed"`,
191
+ );
192
+ await queryRunner.query(
193
+ `DROP INDEX "public"."IDX_d5dd2503bf422c6a19946216e1"`,
194
+ );
195
+ await queryRunner.query(`DROP TABLE "UserSlack"`);
196
+ }
197
+ }
@@ -540,6 +540,10 @@ import { AddLlmModelPrice1788300000000 } from "./1788300000000-AddLlmModelPrice"
540
540
  import { AddMarketingConversionAttribution1788400000000 } from "./1788400000000-AddMarketingConversionAttribution";
541
541
  import { RemoveLlmCostBudgetAlertColumns1788500000000 } from "./1788500000000-RemoveLlmCostBudgetAlertColumns";
542
542
  import { AddDetectionRuleIncidentColumns1788600000000 } from "./1788600000000-AddDetectionRuleIncidentColumns";
543
+ import { RemoveMarketingConversionUploadState1788700000000 } from "./1788700000000-RemoveMarketingConversionUploadState";
544
+ import { DropMarketingConversionAddEnterpriseLicenseEmail1788800000000 } from "./1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail";
545
+ import { RedactStoredMonitorIngestSecrets1788900000000 } from "./1788900000000-RedactStoredMonitorIngestSecrets";
546
+ import { AddUserSlackAndMicrosoftTeams1789000000000 } from "./1789000000000-AddUserSlackAndMicrosoftTeams";
543
547
  import { MigrationName1787142779538 } from "./1787142779538-MigrationName";
544
548
  import { MigrationName1787156982416 } from "./1787156982416-MigrationName";
545
549
 
@@ -1088,4 +1092,8 @@ export default [
1088
1092
  AddMarketingConversionAttribution1788400000000,
1089
1093
  RemoveLlmCostBudgetAlertColumns1788500000000,
1090
1094
  AddDetectionRuleIncidentColumns1788600000000,
1095
+ RemoveMarketingConversionUploadState1788700000000,
1096
+ DropMarketingConversionAddEnterpriseLicenseEmail1788800000000,
1097
+ RedactStoredMonitorIngestSecrets1788900000000,
1098
+ AddUserSlackAndMicrosoftTeams1789000000000,
1091
1099
  ];
@@ -17,6 +17,13 @@ export enum QueueName {
17
17
  Worker = "Worker",
18
18
  Telemetry = "Telemetry",
19
19
  Runbook = "Runbook",
20
+ /*
21
+ * Outbound marketing conversion webhooks. Data-carrying (the job holds the
22
+ * whole event), unlike Worker, which looks its function up by name. Nothing
23
+ * stores these events, so this queue is the only thing that survives a
24
+ * receiver being briefly unreachable.
25
+ */
26
+ MarketingEvent = "MarketingEvent",
20
27
  }
21
28
 
22
29
  export type QueueJob = Job;
@@ -1,10 +1,70 @@
1
1
  import DatabaseService from "./DatabaseService";
2
2
  import EnterpriseLicense from "../../Models/DatabaseModels/EnterpriseLicense";
3
+ import MarketingEventUtil from "../Utils/Marketing/MarketingEventUtil";
4
+ import { MarketingEventType } from "../../Types/Marketing/MarketingEvent";
5
+ import { OnCreate } from "../Types/Database/Hooks";
6
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
3
7
 
4
8
  export class Service extends DatabaseService<EnterpriseLicense> {
5
9
  public constructor() {
6
10
  super(EnterpriseLicense);
7
11
  }
12
+
13
+ @CaptureSpan()
14
+ protected override async onCreateSuccess(
15
+ _onCreate: OnCreate<EnterpriseLicense>,
16
+ createdItem: EnterpriseLicense,
17
+ ): Promise<EnterpriseLicense> {
18
+ /*
19
+ * The outbound enterprise_license_issued conversion.
20
+ *
21
+ * This is where sales-led revenue enters the funnel. Everything else
22
+ * OneUptime emits is self-serve and knows its own value from the plan
23
+ * table; an enterprise contract is negotiated, so annualContractValue is
24
+ * the only place the number exists at all.
25
+ *
26
+ * Emitted even with no email and no ACV. A licence issued without either
27
+ * is still a licence issued, and a receiver that sees the event can chase
28
+ * the missing detail — one that never sees it cannot. The two nulls are
29
+ * reported honestly rather than defaulted to zero, which would quietly
30
+ * drag reported contract value down.
31
+ */
32
+ MarketingEventUtil.emitInBackground(
33
+ MarketingEventUtil.buildEvent({
34
+ eventType: MarketingEventType.EnterpriseLicenseIssued,
35
+ eventId: `${MarketingEventType.EnterpriseLicenseIssued}:${createdItem.id?.toString()}`,
36
+ occurredAt: createdItem.createdAt || new Date(),
37
+ email: createdItem.email?.toString(),
38
+ /*
39
+ * No attribution source. A licence row is typed in by a human and
40
+ * carries no session, so its campaign is whatever the meeting_booked
41
+ * sharing its email carried — the join the receiver makes on
42
+ * emailHash, not something OneUptime can restate here.
43
+ */
44
+ data: {
45
+ enterpriseLicenseId: createdItem.id?.toString() || "",
46
+ companyName: createdItem.companyName || "",
47
+ annualContractValueInUSD:
48
+ createdItem.annualContractValue === undefined ||
49
+ createdItem.annualContractValue === null
50
+ ? null
51
+ : createdItem.annualContractValue,
52
+ currency: "USD",
53
+ isEvaluationLicense: Boolean(createdItem.isEvaluationLicense),
54
+ userLimit:
55
+ createdItem.userLimit === undefined ||
56
+ createdItem.userLimit === null
57
+ ? null
58
+ : createdItem.userLimit,
59
+ expiresAt: createdItem.expiresAt
60
+ ? createdItem.expiresAt.toISOString()
61
+ : null,
62
+ },
63
+ }),
64
+ );
65
+
66
+ return createdItem;
67
+ }
8
68
  }
9
69
 
10
70
  export default new Service();
@@ -145,7 +145,6 @@ import GlobalOidcProjectService from "./GlobalOidcProjectService";
145
145
  import PromoCodeService from "./PromoCodeService";
146
146
  import EnterpriseLicenseService from "./EnterpriseLicenseService";
147
147
  import EnterpriseLicenseInstanceService from "./EnterpriseLicenseInstanceService";
148
- import MarketingConversionService from "./MarketingConversionService";
149
148
  import OpenSourceDeploymentService from "./OpenSourceDeploymentService";
150
149
  import RecommendationDismissalService from "./RecommendationDismissalService";
151
150
  import ResellerPlanService from "./ResellerPlanService";
@@ -213,6 +212,8 @@ import UserSmsService from "./UserSmsService";
213
212
  import UserIncomingCallNumberService from "./UserIncomingCallNumberService";
214
213
  import UserWhatsAppService from "./UserWhatsAppService";
215
214
  import UserTelegramService from "./UserTelegramService";
215
+ import UserSlackService from "./UserSlackService";
216
+ import UserMicrosoftTeamsService from "./UserMicrosoftTeamsService";
216
217
  import WorkflowLogService from "./WorkflowLogService";
217
218
  // Workflows.
218
219
  import WorkflowService from "./WorkflowService";
@@ -292,6 +293,7 @@ import WorkspaceSettingService from "./WorkspaceSettingService";
292
293
  import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
293
294
  import WorkspaceNotificationLogService from "./WorkspaceNotificationLogService";
294
295
  import WorkspaceNotificationSummaryService from "./WorkspaceNotificationSummaryService";
296
+ import WorkspaceUserNotificationService from "./WorkspaceUserNotificationService";
295
297
  import OnCallDutyPolicyUserOverrideService from "./OnCallDutyPolicyUserOverrideService";
296
298
 
297
299
  import MonitorLogService from "./MonitorLogService";
@@ -309,7 +311,6 @@ const services: Array<BaseService> = [
309
311
  PromoCodeService,
310
312
  EnterpriseLicenseService,
311
313
  EnterpriseLicenseInstanceService,
312
- MarketingConversionService,
313
314
  OpenSourceDeploymentService,
314
315
 
315
316
  ResellerService,
@@ -490,6 +491,8 @@ const services: Array<BaseService> = [
490
491
  UserIncomingCallNumberService,
491
492
  UserWhatsAppService,
492
493
  UserTelegramService,
494
+ UserSlackService,
495
+ UserMicrosoftTeamsService,
493
496
  UserTotpAuthService,
494
497
  UserWebAuthnService,
495
498
 
@@ -587,6 +590,7 @@ const services: Array<BaseService> = [
587
590
  WorkspaceNotificationRuleService,
588
591
  WorkspaceNotificationLogService,
589
592
  WorkspaceNotificationSummaryService,
593
+ WorkspaceUserNotificationService,
590
594
 
591
595
  ProjectSCIMLogService,
592
596
  StatusPageSCIMLogService,
@@ -57,24 +57,40 @@ export class Service extends DatabaseService<Model> {
57
57
  const incidentRunMembership: string = `EXISTS (SELECT 1 FROM "AIRun" AS "run" WHERE "run"."_id" = "log"."aiRunId" AND "run"."triggeredByIncidentId" IS NOT NULL AND "run"."triggeredByAlertId" IS NULL)`;
58
58
  const alertRunMembership: string = `EXISTS (SELECT 1 FROM "AIRun" AS "run" WHERE "run"."_id" = "log"."aiRunId" AND "run"."triggeredByIncidentId" IS NULL AND "run"."triggeredByAlertId" IS NOT NULL)`;
59
59
 
60
- let subjectClause: string = `AND "log"."incidentId" IS NULL AND "log"."alertId" IS NULL AND NOT ("log"."feature" = ANY($4)) AND NOT ("log"."feature" = ANY($5)) AND NOT (${incidentRunMembership}) AND NOT (${alertRunMembership})`;
60
+ /*
61
+ * Placeholders and bindings are built together, per branch, because
62
+ * PostgreSQL derives a prepared statement's parameter count from the
63
+ * HIGHEST $n present in the text — not from what the driver sends. A
64
+ * branch that binds an argument it never references is rejected outright
65
+ * at Bind (bind message supplies 5 parameters, but prepared statement
66
+ * requires 4), and a branch that skips a placeholder is rejected at Parse
67
+ * (could not determine data type of parameter $4). Both are runtime-only
68
+ * failures no compiler catches, so the parameter array is grown next to
69
+ * the clause that reads it and never assembled apart from it.
70
+ */
71
+ const params: Array<unknown> = [
72
+ data.projectId.toString(), // $1
73
+ data.since, // $2
74
+ data.features, // $3
75
+ ];
76
+
77
+ let subjectClause: string;
61
78
 
62
79
  if (data.incidentId) {
80
+ params.push(legacyIncidentFeatures); // $4
63
81
  subjectClause = `AND (("log"."incidentId" IS NOT NULL AND "log"."alertId" IS NULL) OR ("log"."incidentId" IS NULL AND "log"."alertId" IS NULL AND (("log"."feature" = ANY($4)) OR ${incidentRunMembership})))`;
64
82
  } else if (data.alertId) {
65
- subjectClause = `AND (("log"."incidentId" IS NULL AND "log"."alertId" IS NOT NULL) OR ("log"."incidentId" IS NULL AND "log"."alertId" IS NULL AND (("log"."feature" = ANY($5)) OR ${alertRunMembership})))`;
83
+ params.push(legacyAlertFeatures); // $4
84
+ subjectClause = `AND (("log"."incidentId" IS NULL AND "log"."alertId" IS NOT NULL) OR ("log"."incidentId" IS NULL AND "log"."alertId" IS NULL AND (("log"."feature" = ANY($4)) OR ${alertRunMembership})))`;
85
+ } else {
86
+ params.push(legacyIncidentFeatures, legacyAlertFeatures); // $4, $5
87
+ subjectClause = `AND "log"."incidentId" IS NULL AND "log"."alertId" IS NULL AND NOT ("log"."feature" = ANY($4)) AND NOT ("log"."feature" = ANY($5)) AND NOT (${incidentRunMembership}) AND NOT (${alertRunMembership})`;
66
88
  }
67
89
 
68
90
  const rows: Array<{ total: string | number | null }> =
69
91
  await this.getRepository().manager.query(
70
92
  `SELECT COALESCE(SUM("log"."totalTokens"), 0) AS "total" FROM "LlmLog" AS "log" WHERE "log"."projectId" = $1 AND "log"."createdAt" >= $2 AND "log"."feature" = ANY($3) ${subjectClause} AND "log"."deletedAt" IS NULL`,
71
- [
72
- data.projectId.toString(),
73
- data.since,
74
- data.features,
75
- legacyIncidentFeatures,
76
- legacyAlertFeatures,
77
- ],
93
+ params,
78
94
  );
79
95
 
80
96
  return Number(rows[0]?.total || 0);
@@ -16,6 +16,8 @@ import UserPushService from "./UserPushService";
16
16
  import UserService from "./UserService";
17
17
  import UserSmsService from "./UserSmsService";
18
18
  import UserTelegramService from "./UserTelegramService";
19
+ import UserSlackService from "./UserSlackService";
20
+ import UserMicrosoftTeamsService from "./UserMicrosoftTeamsService";
19
21
  import UserWebhookService from "./UserWebhookService";
20
22
  import UserWhatsAppService from "./UserWhatsAppService";
21
23
  import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
@@ -51,6 +53,8 @@ import UserNotificationRule from "../../Models/DatabaseModels/UserNotificationRu
51
53
  import UserPush from "../../Models/DatabaseModels/UserPush";
52
54
  import UserSMS from "../../Models/DatabaseModels/UserSMS";
53
55
  import UserTelegram from "../../Models/DatabaseModels/UserTelegram";
56
+ import UserSlack from "../../Models/DatabaseModels/UserSlack";
57
+ import UserMicrosoftTeams from "../../Models/DatabaseModels/UserMicrosoftTeams";
54
58
  import UserWebhook from "../../Models/DatabaseModels/UserWebhook";
55
59
  import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
56
60
 
@@ -116,7 +120,7 @@ export enum ResponderSource {
116
120
  }
117
121
 
118
122
  /**
119
- * The seven channels a page can be delivered on. These strings are the same literals the
123
+ * The nine channels a page can be delivered on. These strings are the same literals the
120
124
  * fallback uses for `channelsUsed` (UserNotificationRuleService.chooseFallbackChannels),
121
125
  * deliberately: an operator reading "notified via fallback (Push, Email)" in an execution
122
126
  * log and "Push, Email" in the readiness table must not have to translate between two
@@ -129,6 +133,8 @@ export enum ReadinessMethodType {
129
133
  Call = "Call",
130
134
  WhatsApp = "WhatsApp",
131
135
  Telegram = "Telegram",
136
+ Slack = "Slack",
137
+ MicrosoftTeams = "Microsoft Teams",
132
138
  Webhook = "Webhook",
133
139
  }
134
140
 
@@ -436,6 +442,8 @@ const RULE_TYPE_SCOPES: Array<RuleTypeScope> = [
436
442
  const METHOD_DISPLAY_ORDER: Array<ReadinessMethodType> = [
437
443
  ReadinessMethodType.Push,
438
444
  ReadinessMethodType.Email,
445
+ ReadinessMethodType.Slack,
446
+ ReadinessMethodType.MicrosoftTeams,
439
447
  ReadinessMethodType.SMS,
440
448
  ReadinessMethodType.Call,
441
449
  ReadinessMethodType.WhatsApp,
@@ -2194,6 +2202,74 @@ export default class OnCallReadinessService {
2194
2202
  },
2195
2203
  });
2196
2204
 
2205
+ await this.readEveryPage<UserSlack>({
2206
+ description: "Slack notification methods",
2207
+ projectId: projectId,
2208
+ completeness: completeness,
2209
+ service: UserSlackService,
2210
+ query: {
2211
+ projectId: projectId,
2212
+ userId: new Includes(userIds),
2213
+ },
2214
+ /*
2215
+ * The username only — never slackUserId. The member id is the
2216
+ * addressable target the bot sends to; the username is the human-facing
2217
+ * label, and it is the one a user can recognise as theirs.
2218
+ */
2219
+ select: {
2220
+ _id: true,
2221
+ userId: true,
2222
+ slackUserName: true,
2223
+ isVerified: true,
2224
+ },
2225
+ consumePage: (rows: Array<UserSlack>): void => {
2226
+ for (const row of rows) {
2227
+ addMethod(row, {
2228
+ methodType: ReadinessMethodType.Slack,
2229
+ maskedIdentifier: maskIdentifier(
2230
+ row.slackUserName,
2231
+ MaskedIdentifierKind.Handle,
2232
+ ),
2233
+ isVerified: Boolean(row.isVerified),
2234
+ });
2235
+ }
2236
+ },
2237
+ });
2238
+
2239
+ await this.readEveryPage<UserMicrosoftTeams>({
2240
+ description: "Microsoft Teams notification methods",
2241
+ projectId: projectId,
2242
+ completeness: completeness,
2243
+ service: UserMicrosoftTeamsService,
2244
+ query: {
2245
+ projectId: projectId,
2246
+ userId: new Includes(userIds),
2247
+ },
2248
+ /*
2249
+ * The display name only — never microsoftTeamsUserId. The Entra id is
2250
+ * the addressable target the bot sends to; the display name is the
2251
+ * human-facing label, and it is the one a user can recognise as theirs.
2252
+ */
2253
+ select: {
2254
+ _id: true,
2255
+ userId: true,
2256
+ microsoftTeamsUserName: true,
2257
+ isVerified: true,
2258
+ },
2259
+ consumePage: (rows: Array<UserMicrosoftTeams>): void => {
2260
+ for (const row of rows) {
2261
+ addMethod(row, {
2262
+ methodType: ReadinessMethodType.MicrosoftTeams,
2263
+ maskedIdentifier: maskIdentifier(
2264
+ row.microsoftTeamsUserName,
2265
+ MaskedIdentifierKind.Handle,
2266
+ ),
2267
+ isVerified: Boolean(row.isVerified),
2268
+ });
2269
+ }
2270
+ },
2271
+ });
2272
+
2197
2273
  await this.readEveryPage<UserWebhook>({
2198
2274
  description: "webhook notification methods",
2199
2275
  projectId: projectId,
@@ -2677,7 +2753,7 @@ export default class OnCallReadinessService {
2677
2753
  * the real thing rather than a hopeful "some verified method".
2678
2754
  *
2679
2755
  * This mirrors UserNotificationRuleService.chooseFallbackChannels exactly, including
2680
- * the project switches: zero-cost channels first and BOTH of them if present, then one
2756
+ * the project switches: zero-cost channels first and ALL of them if present, then one
2681
2757
  * paid channel in escalating-intrusiveness order, then webhook. If that function's
2682
2758
  * order ever changes, this one has to change with it — a readiness surface promising
2683
2759
  * "falls back to SMS" for a project that has SMS switched off is worse than saying
@@ -2709,6 +2785,14 @@ export default class OnCallReadinessService {
2709
2785
  zeroCost.push(ReadinessMethodType.Email);
2710
2786
  }
2711
2787
 
2788
+ if (has(ReadinessMethodType.Slack)) {
2789
+ zeroCost.push(ReadinessMethodType.Slack);
2790
+ }
2791
+
2792
+ if (has(ReadinessMethodType.MicrosoftTeams)) {
2793
+ zeroCost.push(ReadinessMethodType.MicrosoftTeams);
2794
+ }
2795
+
2712
2796
  if (zeroCost.length > 0) {
2713
2797
  return zeroCost;
2714
2798
  }