@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
@@ -16,6 +16,8 @@ import UpdateBy from "../Types/Database/UpdateBy";
16
16
  import logger, { LogAttributes } from "../Utils/Logger";
17
17
  import Errors from "../Utils/Errors";
18
18
  import ProductAnalytics from "../Utils/ProductAnalytics";
19
+ import MarketingEventUtil from "../Utils/Marketing/MarketingEventUtil";
20
+ import { MarketingEventType } from "../../Types/Marketing/MarketingEvent";
19
21
  import SessionReplayGateCacheStore from "../Utils/SessionReplay/SessionReplayGateCacheStore";
20
22
  import AccessTokenService from "./AccessTokenService";
21
23
  import BillingService from "./BillingService";
@@ -558,10 +560,21 @@ export class ProjectService extends DatabaseService<Model> {
558
560
  trialEndsAt: true,
559
561
  paymentProviderCustomerId: true,
560
562
  createdOwnerEmail: true,
563
+ /*
564
+ * The full attribution set, not just the three UTMs the analytics
565
+ * properties carry: the outbound plan-change conversion is the only
566
+ * place the campaign that won a customer is ever reported, and a
567
+ * column left unselected reaches the receiver as empty rather than as
568
+ * an error.
569
+ */
561
570
  utmSource: true,
562
571
  utmMedium: true,
563
572
  utmCampaign: true,
573
+ utmTerm: true,
574
+ utmContent: true,
575
+ utmUrl: true,
564
576
  clickIds: true,
577
+ firstTouchAttribution: true,
565
578
  },
566
579
  props: {
567
580
  isRoot: true,
@@ -884,6 +897,98 @@ These are no longer recorded against the project and have to be cancelled by han
884
897
  );
885
898
  }
886
899
 
900
+ /*
901
+ * The first-subscription conversion.
902
+ *
903
+ * A project created directly on a paid plan never passes through changePlan,
904
+ * so the plan-change pass below never sees it — and could not report it if
905
+ * it did, because direction is decided by plan ORDER and a first plan has no
906
+ * previous tier to compare against. That left the most bid-worthy self-serve
907
+ * conversion there is emitting nothing at all: a visitor could click an ad,
908
+ * sign up, pick Growth and pay, and no channel carried a single event.
909
+ *
910
+ * It is its own event rather than an upgrade with an empty old_plan because
911
+ * this is new business and an upgrade is expansion. Mixing them is a
912
+ * distinction nothing downstream could restore.
913
+ *
914
+ * Free projects reach here too — a project on the free plan is still
915
+ * subscribed at the payment provider — so the paid check is what keeps this
916
+ * a revenue event.
917
+ */
918
+ private captureSubscriptionStartedAnalytics(data: {
919
+ project: Model;
920
+ plan: SubscriptionPlan;
921
+ planId: string;
922
+ seats: number;
923
+ }): void {
924
+ if (!data.project.createdOwnerEmail) {
925
+ return;
926
+ }
927
+
928
+ // Per-month amount, or null when unknown (custom pricing / unknown plan).
929
+ const monthlyAmountInUSD: number | null = data.plan.isCustomPricing()
930
+ ? null
931
+ : data.plan.getYearlyPlanId() === data.planId
932
+ ? data.plan.getYearlySubscriptionAmountInUSD()
933
+ : data.plan.getMonthlySubscriptionAmountInUSD();
934
+
935
+ const planIsPaid: boolean =
936
+ data.plan.isCustomPricing() ||
937
+ (monthlyAmountInUSD !== null && monthlyAmountInUSD > 0);
938
+
939
+ if (!planIsPaid) {
940
+ return;
941
+ }
942
+
943
+ const properties: JSONObject = {
944
+ project_id: data.project.id?.toString() || "",
945
+ new_plan: data.plan.getName(),
946
+ seats: data.seats,
947
+ /*
948
+ * Always true. The project went straight from not existing to paying,
949
+ * which is the acquisition this event exists to report.
950
+ */
951
+ is_paid_conversion: true,
952
+ has_custom_pricing: data.plan.isCustomPricing(),
953
+ utm_source: data.project.utmSource || "",
954
+ utm_medium: data.project.utmMedium || "",
955
+ utm_campaign: data.project.utmCampaign || "",
956
+ utm_term: data.project.utmTerm || "",
957
+ utm_content: data.project.utmContent || "",
958
+ click_ids: data.project.clickIds || {},
959
+ };
960
+
961
+ if (monthlyAmountInUSD !== null) {
962
+ properties["new_monthly_amount_in_usd"] = monthlyAmountInUSD;
963
+ // Value for ROAS reporting: monthly recurring revenue at the start.
964
+ properties["value"] = monthlyAmountInUSD * data.seats;
965
+ properties["currency"] = "USD";
966
+ }
967
+
968
+ ProductAnalytics.capture({
969
+ event: "server/subscription_started",
970
+ distinctId: data.project.createdOwnerEmail.toString(),
971
+ properties: properties,
972
+ });
973
+
974
+ MarketingEventUtil.emitInBackground(
975
+ MarketingEventUtil.buildEvent({
976
+ eventType: MarketingEventType.SubscriptionStarted,
977
+ /*
978
+ * Naturally unique: a project has exactly one first subscription, so a
979
+ * queue retry carries the identical id and the receiver dedupes it.
980
+ * Unlike a plan change, this needs no timestamp to separate it from
981
+ * the next one — there is no next one.
982
+ */
983
+ eventId: `${MarketingEventType.SubscriptionStarted}:${data.project.id?.toString()}`,
984
+ occurredAt: data.project.createdAt || new Date(),
985
+ email: data.project.createdOwnerEmail.toString(),
986
+ attributionSource: data.project,
987
+ data: properties,
988
+ }),
989
+ );
990
+ }
991
+
887
992
  /*
888
993
  * The paid-conversion event for ad platforms: fires server-side (immune to
889
994
  * ad blockers) whenever a project's subscription plan changes, with enough
@@ -982,6 +1087,60 @@ These are no longer recorded against the project and have to be cancelled by han
982
1087
  distinctId: data.project.createdOwnerEmail.toString(),
983
1088
  properties: properties,
984
1089
  });
1090
+
1091
+ this.emitPlanChangeMarketingEvent({
1092
+ project: data.project,
1093
+ properties: properties,
1094
+ isUpgrade: properties["is_upgrade"] === true,
1095
+ isDowngrade: properties["is_downgrade"] === true,
1096
+ });
1097
+ }
1098
+
1099
+ /*
1100
+ * The outbound subscription_upgraded / subscription_downgraded conversions.
1101
+ *
1102
+ * Rides on the analytics pass above rather than on the plan-change path
1103
+ * directly, because that is where "is this an upgrade" is already decided —
1104
+ * by plan ORDER, not price, so a monthly-to-yearly switch at the same tier
1105
+ * is neither. Duplicating that comparison here is how the two would
1106
+ * eventually disagree about what a customer did.
1107
+ *
1108
+ * Only real tier movements are emitted. A first plan on a project with no
1109
+ * previous one has no direction (both flags read false, since oldPlanOrder
1110
+ * is null), and an interval change is not a conversion — sending either as
1111
+ * an upgrade would inflate exactly the number this exists to report.
1112
+ */
1113
+ private emitPlanChangeMarketingEvent(data: {
1114
+ project: Model;
1115
+ properties: JSONObject;
1116
+ isUpgrade: boolean;
1117
+ isDowngrade: boolean;
1118
+ }): void {
1119
+ if (!data.isUpgrade && !data.isDowngrade) {
1120
+ return;
1121
+ }
1122
+
1123
+ const eventType: MarketingEventType = data.isUpgrade
1124
+ ? MarketingEventType.SubscriptionUpgraded
1125
+ : MarketingEventType.SubscriptionDowngraded;
1126
+
1127
+ const occurredAt: Date = new Date();
1128
+
1129
+ MarketingEventUtil.emitInBackground(
1130
+ MarketingEventUtil.buildEvent({
1131
+ eventType: eventType,
1132
+ /*
1133
+ * A project can legitimately upgrade, downgrade and upgrade again, so
1134
+ * unlike a signup or a booking there is no naturally unique key —
1135
+ * the instant is what separates one change from the next.
1136
+ */
1137
+ eventId: `${eventType}:${data.project.id?.toString()}:${occurredAt.toISOString()}`,
1138
+ occurredAt: occurredAt,
1139
+ email: data.project.createdOwnerEmail?.toString(),
1140
+ attributionSource: data.project,
1141
+ data: data.properties,
1142
+ }),
1143
+ );
985
1144
  }
986
1145
 
987
1146
  private async sendSubscriptionChangeWebhookSlackNotification(
@@ -1219,6 +1378,18 @@ These are no longer recorded against the project and have to be cancelled by han
1219
1378
  },
1220
1379
  });
1221
1380
 
1381
+ /*
1382
+ * Raised after the row is written, mirroring changePlan: the write is
1383
+ * what makes the subscription real to OneUptime, and the conversion
1384
+ * describes a subscription that exists.
1385
+ */
1386
+ this.captureSubscriptionStartedAnalytics({
1387
+ project: createdItem,
1388
+ plan: plan,
1389
+ planId: createdItem.paymentProviderPlanId!,
1390
+ seats: 1,
1391
+ });
1392
+
1222
1393
  // mark the promo code as used it it exists.
1223
1394
 
1224
1395
  if (createdItem.paymentProviderPromoCode) {
@@ -0,0 +1,208 @@
1
+ import CreateBy from "../Types/Database/CreateBy";
2
+ import DeleteBy from "../Types/Database/DeleteBy";
3
+ import { OnCreate, OnDelete } from "../Types/Database/Hooks";
4
+ import DatabaseService from "./DatabaseService";
5
+ import UserNotificationRuleService, {
6
+ NotificationDeletionImpact,
7
+ NotificationMethodChannel,
8
+ } from "./UserNotificationRuleService";
9
+ import WorkspaceProjectAuthTokenService from "./WorkspaceProjectAuthTokenService";
10
+ import WorkspaceUserAuthTokenService from "./WorkspaceUserAuthTokenService";
11
+ import logger from "../Utils/Logger";
12
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
13
+ import BadDataException from "../../Types/Exception/BadDataException";
14
+ import { JSONObject } from "../../Types/JSON";
15
+ import ObjectID from "../../Types/ObjectID";
16
+ import WorkspaceType from "../../Types/Workspace/WorkspaceType";
17
+ import WorkspaceProjectAuthToken from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
18
+ import WorkspaceUserAuthToken from "../../Models/DatabaseModels/WorkspaceUserAuthToken";
19
+ import Model from "../../Models/DatabaseModels/UserMicrosoftTeams";
20
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
21
+
22
+ export class Service extends DatabaseService<Model> {
23
+ public constructor() {
24
+ super(Model);
25
+ }
26
+
27
+ @CaptureSpan()
28
+ protected override async onBeforeDelete(
29
+ deleteBy: DeleteBy<Model>,
30
+ ): Promise<OnDelete<Model>> {
31
+ const itemsToDelete: Array<Model> = await this.findBy({
32
+ query: deleteBy.query,
33
+ select: {
34
+ _id: true,
35
+ projectId: true,
36
+ },
37
+ skip: 0,
38
+ limit: LIMIT_MAX,
39
+ props: {
40
+ isRoot: true,
41
+ },
42
+ });
43
+
44
+ for (const item of itemsToDelete) {
45
+ await UserNotificationRuleService.deleteBy({
46
+ query: {
47
+ userMicrosoftTeamsId: item.id!,
48
+ projectId: item.projectId!,
49
+ },
50
+ limit: LIMIT_MAX,
51
+ skip: 0,
52
+ props: {
53
+ isRoot: true,
54
+ },
55
+ });
56
+ }
57
+
58
+ return {
59
+ deleteBy,
60
+ carryForward: null,
61
+ };
62
+ }
63
+
64
+ /**
65
+ * What this user would lose if this Microsoft Teams account were deleted.
66
+ * Ask BEFORE calling delete; nothing here refuses anything.
67
+ *
68
+ * The hook directly above deletes every UserNotificationRule that points at
69
+ * this account, and the foreign key is onDelete: "CASCADE" so the rows would
70
+ * go even if it did not.
71
+ */
72
+ @CaptureSpan()
73
+ public async getDeletionImpact(data: {
74
+ itemId: ObjectID;
75
+ projectId: ObjectID;
76
+ }): Promise<NotificationDeletionImpact> {
77
+ return UserNotificationRuleService.getNotificationMethodDeletionImpact({
78
+ projectId: data.projectId,
79
+ methodType: NotificationMethodChannel.MicrosoftTeams,
80
+ methodId: data.itemId,
81
+ });
82
+ }
83
+
84
+ /*
85
+ * A Microsoft Teams notification method is a POINTER at the user's existing
86
+ * workspace link, not a hand-typed address: the Microsoft Entra user id is
87
+ * resolved from the user's own WorkspaceUserAuthToken and never accepted
88
+ * from the request. That is why creation IS verification — the OAuth dance
89
+ * that wrote the auth-token row already proved the person controls that
90
+ * Microsoft Teams account.
91
+ */
92
+ @CaptureSpan()
93
+ protected override async onBeforeCreate(
94
+ createBy: CreateBy<Model>,
95
+ ): Promise<OnCreate<Model>> {
96
+ if (!createBy.props.isRoot && createBy.data.isVerified) {
97
+ throw new BadDataException("isVerified cannot be set to true");
98
+ }
99
+
100
+ if (!createBy.props.isRoot && createBy.data.microsoftTeamsUserId) {
101
+ throw new BadDataException("microsoftTeamsUserId cannot be set directly");
102
+ }
103
+
104
+ if (!createBy.data.projectId || !createBy.data.userId) {
105
+ throw new BadDataException("projectId and userId are required");
106
+ }
107
+
108
+ const projectId: ObjectID = new ObjectID(
109
+ createBy.data.projectId.toString(),
110
+ );
111
+ const userId: ObjectID = new ObjectID(createBy.data.userId.toString());
112
+
113
+ /*
114
+ * One workspace link per (user, project) means a second row would be an
115
+ * exact duplicate of the first.
116
+ */
117
+ const existingCount: number = (
118
+ await this.countBy({
119
+ query: {
120
+ projectId: projectId,
121
+ userId: userId,
122
+ },
123
+ props: {
124
+ isRoot: true,
125
+ },
126
+ })
127
+ ).toNumber();
128
+
129
+ if (existingCount > 0) {
130
+ throw new BadDataException(
131
+ "Microsoft Teams is already added as a notification method for this project.",
132
+ );
133
+ }
134
+
135
+ const projectAuth: WorkspaceProjectAuthToken | null =
136
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
137
+ projectId: projectId,
138
+ workspaceType: WorkspaceType.MicrosoftTeams,
139
+ });
140
+
141
+ if (!projectAuth || !projectAuth.authToken) {
142
+ throw new BadDataException(
143
+ "This project is not connected to Microsoft Teams. Please ask a project admin to connect Microsoft Teams in Project Settings > Microsoft Teams Integration.",
144
+ );
145
+ }
146
+
147
+ const userAuth: WorkspaceUserAuthToken | null =
148
+ await WorkspaceUserAuthTokenService.getUserAuth({
149
+ projectId: projectId,
150
+ userId: userId,
151
+ workspaceType: WorkspaceType.MicrosoftTeams,
152
+ });
153
+
154
+ if (!userAuth || !userAuth.workspaceUserId) {
155
+ throw new BadDataException(
156
+ "Your Microsoft Teams account is not connected to OneUptime for this project. Please go to User Settings > Microsoft Teams Integration and connect your Microsoft Teams account first.",
157
+ );
158
+ }
159
+
160
+ createBy.data.microsoftTeamsUserId = userAuth.workspaceUserId;
161
+ createBy.data.isVerified = true;
162
+
163
+ /* Display label only — captured when the user linked their account. */
164
+ const displayName: string | undefined = (
165
+ userAuth.miscData as JSONObject | undefined
166
+ )?.["displayName"] as string | undefined;
167
+
168
+ if (displayName) {
169
+ createBy.data.microsoftTeamsUserName = displayName;
170
+ }
171
+
172
+ return {
173
+ createBy,
174
+ carryForward: null,
175
+ };
176
+ }
177
+
178
+ @CaptureSpan()
179
+ protected override async onCreateSuccess(
180
+ _onCreate: OnCreate<Model>,
181
+ createdItem: Model,
182
+ ): Promise<Model> {
183
+ /*
184
+ * The row is born verified (creation is gated on the live workspace
185
+ * link), so default on-call rules are seeded at create time — same as
186
+ * webhooks.
187
+ */
188
+ if (createdItem.projectId && createdItem.userId && createdItem.id) {
189
+ try {
190
+ await UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod(
191
+ {
192
+ projectId: createdItem.projectId,
193
+ userId: createdItem.userId,
194
+ notificationMethod: {
195
+ userMicrosoftTeamsId: createdItem.id,
196
+ },
197
+ },
198
+ );
199
+ } catch (err) {
200
+ logger.error(err);
201
+ }
202
+ }
203
+
204
+ return createdItem;
205
+ }
206
+ }
207
+
208
+ export default new Service();
@@ -17,6 +17,8 @@ import UserPushService from "./UserPushService";
17
17
  import UserService from "./UserService";
18
18
  import UserSmsService from "./UserSmsService";
19
19
  import UserTelegramService from "./UserTelegramService";
20
+ import UserSlackService from "./UserSlackService";
21
+ import UserMicrosoftTeamsService from "./UserMicrosoftTeamsService";
20
22
  import UserWebhookService from "./UserWebhookService";
21
23
  import UserWhatsAppService from "./UserWhatsAppService";
22
24
  import type AuditLogServiceType from "./AuditLogService";
@@ -42,6 +44,8 @@ import UserEmail from "../../Models/DatabaseModels/UserEmail";
42
44
  import UserPush from "../../Models/DatabaseModels/UserPush";
43
45
  import UserSMS from "../../Models/DatabaseModels/UserSMS";
44
46
  import UserTelegram from "../../Models/DatabaseModels/UserTelegram";
47
+ import UserSlack from "../../Models/DatabaseModels/UserSlack";
48
+ import UserMicrosoftTeams from "../../Models/DatabaseModels/UserMicrosoftTeams";
45
49
  import UserWebhook from "../../Models/DatabaseModels/UserWebhook";
46
50
  import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
47
51
 
@@ -66,7 +70,8 @@ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
66
70
  * filters, sort columns appended after the guard had run. The models are still
67
71
  * CurrentUser-only. Nothing in this file changes that, and an administrator's
68
72
  * every direct read or write of UserEmail, UserSMS, UserCall, UserWhatsApp,
69
- * UserTelegram, UserPush and UserWebhook is still refused.
73
+ * UserTelegram, UserSlack, UserMicrosoftTeams, UserPush and UserWebhook is
74
+ * still refused.
70
75
  *
71
76
  * What this file does instead is a NARROW, SERVER-SIDE, AUDITED capability
72
77
  * that runs as root on the admin's behalf and never hands them the row:
@@ -88,7 +93,7 @@ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
88
93
  * notification methods page as an unverified method they did not add — on
89
94
  * top of the mail this service sends them.
90
95
  *
91
- * - REMOVE is allowed on all seven channels, because offboarding a stale
96
+ * - REMOVE is allowed on all nine channels, because offboarding a stale
92
97
  * device is a real administrative job and the owner may well be gone. It is
93
98
  * the destructive direction, so it is the one with a preview attached:
94
99
  * UserNotificationRuleService.getNotificationMethodDeletionImpact says how
@@ -100,7 +105,10 @@ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
100
105
  * omissions are not oversights. Push is a device token minted by a browser
101
106
  * or a phone at registration time; there is nothing an admin could type.
102
107
  * Telegram needs the account holder to message the bot before a chat id
103
- * exists. Webhook is the one that is deliberately refused rather than
108
+ * exists. Slack and Microsoft Teams are pointers at the owner's own OAuth
109
+ * workspace link, which only the owner can establish — and an admin-created
110
+ * row would be live immediately, since creation is verification for those
111
+ * two. Webhook is the one that is deliberately refused rather than
104
112
  * merely impractical: UserWebhook has no isVerified column at all, so an
105
113
  * admin-created webhook would be live the moment it was written, which is
106
114
  * exactly the silent-redirect this whole design is built to prevent.
@@ -139,7 +147,7 @@ export interface AdminNotificationMethodView {
139
147
 
140
148
  /**
141
149
  * The four channels an administrator may CREATE. See the header for why the
142
- * other three are absent; this enum is the single place that decides, and both
150
+ * other five are absent; this enum is the single place that decides, and both
143
151
  * the API's validation and the service's own dispatch read it.
144
152
  */
145
153
  export enum AdminAddableChannel {
@@ -751,6 +759,160 @@ export class UserNotificationMethodAdminService extends BaseService {
751
759
  });
752
760
  },
753
761
  },
762
+ {
763
+ methodType: ReadinessMethodType.Slack,
764
+ isAdminAddable: false,
765
+ modelType: UserSlack,
766
+ list: async (data: {
767
+ projectId: ObjectID;
768
+ userId: ObjectID;
769
+ }): Promise<Array<AdminNotificationMethodView>> => {
770
+ const rows: Array<UserSlack> = await UserSlackService.findBy({
771
+ query: { projectId: data.projectId, userId: data.userId },
772
+ /*
773
+ * The username only — never slackUserId, which is the addressable
774
+ * target the bot sends to. Same rule OnCallReadinessService
775
+ * follows.
776
+ */
777
+ select: {
778
+ _id: true,
779
+ slackUserName: true,
780
+ isVerified: true,
781
+ createdAt: true,
782
+ },
783
+ sort: { createdAt: SortOrder.Ascending },
784
+ skip: 0,
785
+ limit: LIMIT_PER_PROJECT,
786
+ props: { isRoot: true },
787
+ });
788
+
789
+ return rows.map((row: UserSlack): AdminNotificationMethodView => {
790
+ return this.toView({
791
+ row: row,
792
+ methodType: ReadinessMethodType.Slack,
793
+ isAdminAddable: false,
794
+ identifier: row.slackUserName,
795
+ kind: MaskedIdentifierKind.Handle,
796
+ isVerified: row.isVerified,
797
+ });
798
+ });
799
+ },
800
+ findOwnedRow: async (data: {
801
+ methodId: ObjectID;
802
+ projectId: ObjectID;
803
+ userId: ObjectID;
804
+ }): Promise<AdminNotificationMethodView | null> => {
805
+ const row: UserSlack | null = await UserSlackService.findOneBy({
806
+ query: {
807
+ _id: data.methodId,
808
+ projectId: data.projectId,
809
+ userId: data.userId,
810
+ },
811
+ select: {
812
+ _id: true,
813
+ slackUserName: true,
814
+ isVerified: true,
815
+ },
816
+ props: { isRoot: true },
817
+ });
818
+
819
+ return row
820
+ ? this.toView({
821
+ row: row,
822
+ methodType: ReadinessMethodType.Slack,
823
+ isAdminAddable: false,
824
+ identifier: row.slackUserName,
825
+ kind: MaskedIdentifierKind.Handle,
826
+ isVerified: row.isVerified,
827
+ })
828
+ : null;
829
+ },
830
+ deleteRow: async (data: { methodId: ObjectID }): Promise<void> => {
831
+ await UserSlackService.deleteOneById({
832
+ id: data.methodId,
833
+ props: { isRoot: true },
834
+ });
835
+ },
836
+ },
837
+ {
838
+ methodType: ReadinessMethodType.MicrosoftTeams,
839
+ isAdminAddable: false,
840
+ modelType: UserMicrosoftTeams,
841
+ list: async (data: {
842
+ projectId: ObjectID;
843
+ userId: ObjectID;
844
+ }): Promise<Array<AdminNotificationMethodView>> => {
845
+ const rows: Array<UserMicrosoftTeams> =
846
+ await UserMicrosoftTeamsService.findBy({
847
+ query: { projectId: data.projectId, userId: data.userId },
848
+ /*
849
+ * The display name only — never microsoftTeamsUserId, which is
850
+ * the addressable target the bot sends to. Same rule
851
+ * OnCallReadinessService follows.
852
+ */
853
+ select: {
854
+ _id: true,
855
+ microsoftTeamsUserName: true,
856
+ isVerified: true,
857
+ createdAt: true,
858
+ },
859
+ sort: { createdAt: SortOrder.Ascending },
860
+ skip: 0,
861
+ limit: LIMIT_PER_PROJECT,
862
+ props: { isRoot: true },
863
+ });
864
+
865
+ return rows.map(
866
+ (row: UserMicrosoftTeams): AdminNotificationMethodView => {
867
+ return this.toView({
868
+ row: row,
869
+ methodType: ReadinessMethodType.MicrosoftTeams,
870
+ isAdminAddable: false,
871
+ identifier: row.microsoftTeamsUserName,
872
+ kind: MaskedIdentifierKind.Handle,
873
+ isVerified: row.isVerified,
874
+ });
875
+ },
876
+ );
877
+ },
878
+ findOwnedRow: async (data: {
879
+ methodId: ObjectID;
880
+ projectId: ObjectID;
881
+ userId: ObjectID;
882
+ }): Promise<AdminNotificationMethodView | null> => {
883
+ const row: UserMicrosoftTeams | null =
884
+ await UserMicrosoftTeamsService.findOneBy({
885
+ query: {
886
+ _id: data.methodId,
887
+ projectId: data.projectId,
888
+ userId: data.userId,
889
+ },
890
+ select: {
891
+ _id: true,
892
+ microsoftTeamsUserName: true,
893
+ isVerified: true,
894
+ },
895
+ props: { isRoot: true },
896
+ });
897
+
898
+ return row
899
+ ? this.toView({
900
+ row: row,
901
+ methodType: ReadinessMethodType.MicrosoftTeams,
902
+ isAdminAddable: false,
903
+ identifier: row.microsoftTeamsUserName,
904
+ kind: MaskedIdentifierKind.Handle,
905
+ isVerified: row.isVerified,
906
+ })
907
+ : null;
908
+ },
909
+ deleteRow: async (data: { methodId: ObjectID }): Promise<void> => {
910
+ await UserMicrosoftTeamsService.deleteOneById({
911
+ id: data.methodId,
912
+ props: { isRoot: true },
913
+ });
914
+ },
915
+ },
754
916
  {
755
917
  methodType: ReadinessMethodType.Webhook,
756
918
  isAdminAddable: false,
@@ -966,7 +1128,7 @@ export class UserNotificationMethodAdminService extends BaseService {
966
1128
  * the feature to somebody probing it.
967
1129
  */
968
1130
  throw new BadDataException(
969
- `A project administrator can only add Email, SMS, Call and WhatsApp notification methods for another user. Push, Telegram and webhook methods have to be added by the person who owns the device.`,
1131
+ `A project administrator can only add Email, SMS, Call and WhatsApp notification methods for another user. Push, Telegram, Slack, Microsoft Teams and webhook methods have to be added by the person who owns the device or account.`,
970
1132
  );
971
1133
  }
972
1134