@oneuptime/common 12.0.18 → 12.0.19

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 (206) 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/UserMicrosoftTeamsAPI.ts +127 -0
  11. package/Server/API/UserSlackAPI.ts +125 -0
  12. package/Server/EnvironmentConfig.ts +12 -107
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  18. package/Server/Infrastructure/Queue.ts +7 -0
  19. package/Server/Services/EnterpriseLicenseService.ts +60 -0
  20. package/Server/Services/Index.ts +6 -2
  21. package/Server/Services/LlmLogService.ts +25 -9
  22. package/Server/Services/OnCallReadinessService.ts +86 -2
  23. package/Server/Services/ProjectService.ts +67 -0
  24. package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
  25. package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
  26. package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
  27. package/Server/Services/UserNotificationRuleService.ts +589 -11
  28. package/Server/Services/UserNotificationSettingService.ts +138 -0
  29. package/Server/Services/UserService.ts +28 -0
  30. package/Server/Services/UserSlackService.ts +218 -0
  31. package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
  32. package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
  33. package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
  34. package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
  35. package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
  36. package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
  37. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
  38. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
  39. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
  40. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
  41. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
  42. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
  43. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
  44. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
  45. package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
  46. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
  47. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
  48. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
  49. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
  50. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
  51. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
  52. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
  53. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
  54. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
  55. package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
  56. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
  57. package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
  58. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
  59. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
  60. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
  61. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
  62. package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
  63. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
  64. package/Tests/Server/Services/UserSlackService.test.ts +407 -0
  65. package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
  66. package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
  67. package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
  68. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
  69. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
  70. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
  71. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
  72. package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
  73. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
  74. package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
  75. package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
  76. package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
  77. package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
  78. package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
  79. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
  80. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
  81. package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
  82. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
  83. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
  84. package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
  85. package/Types/Marketing/MarketingEvent.ts +97 -0
  86. package/UI/Utils/NotificationMethodUtil.ts +41 -1
  87. package/UI/Utils/PermissionGate.ts +56 -0
  88. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
  89. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  90. package/build/dist/Models/DatabaseModels/Index.js +4 -2
  91. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  92. package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
  93. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
  95. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
  96. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
  97. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  98. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
  99. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
  100. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
  101. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  102. package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
  103. package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
  104. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  105. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
  106. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
  107. package/build/dist/Server/API/UserSlackAPI.js +81 -0
  108. package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
  109. package/build/dist/Server/EnvironmentConfig.js +10 -70
  110. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
  112. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
  113. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
  114. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
  118. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
  119. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  121. package/build/dist/Server/Infrastructure/Queue.js +7 -0
  122. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  123. package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
  124. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  125. package/build/dist/Server/Services/Index.js +6 -2
  126. package/build/dist/Server/Services/Index.js.map +1 -1
  127. package/build/dist/Server/Services/LlmLogService.js +25 -9
  128. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  129. package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
  130. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
  131. package/build/dist/Server/Services/ProjectService.js +56 -0
  132. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  133. package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
  134. package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
  135. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
  136. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  137. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
  138. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  139. package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
  140. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  141. package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
  142. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  143. package/build/dist/Server/Services/UserService.js +26 -1
  144. package/build/dist/Server/Services/UserService.js.map +1 -1
  145. package/build/dist/Server/Services/UserSlackService.js +195 -0
  146. package/build/dist/Server/Services/UserSlackService.js.map +1 -0
  147. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
  148. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  149. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
  150. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
  151. package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
  152. package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
  153. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
  154. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
  155. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
  156. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
  157. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
  158. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
  159. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
  160. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
  161. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
  162. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  163. package/build/dist/Types/Marketing/MarketingEvent.js +43 -0
  164. package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
  165. package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
  166. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
  167. package/build/dist/UI/Utils/PermissionGate.js +32 -0
  168. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  169. package/package.json +1 -1
  170. package/Models/DatabaseModels/MarketingConversion.ts +0 -410
  171. package/Server/Services/MarketingConversionService.ts +0 -10
  172. package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
  173. package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
  174. package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
  175. package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
  176. package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
  177. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
  178. package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
  179. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
  180. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
  181. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
  182. package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
  183. package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
  184. package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
  185. package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
  186. package/Types/Marketing/MarketingConversion.ts +0 -53
  187. package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
  188. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
  189. package/build/dist/Server/Services/MarketingConversionService.js +0 -9
  190. package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
  191. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
  192. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
  193. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
  194. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
  195. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
  196. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
  197. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
  198. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
  199. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
  200. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
  201. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
  202. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
  203. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
  204. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
  205. package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
  206. package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
@@ -314,6 +314,40 @@ class UserNotificationSetting extends BaseModel {
314
314
  })
315
315
  public alertByTelegram?: boolean = undefined;
316
316
 
317
+ @ColumnAccessControl({
318
+ create: [Permission.CurrentUser],
319
+ read: [Permission.CurrentUser],
320
+ update: [Permission.CurrentUser],
321
+ })
322
+ @TableColumn({
323
+ isDefaultValueColumn: true,
324
+ type: TableColumnType.Boolean,
325
+ defaultValue: false,
326
+ example: false,
327
+ })
328
+ @Column({
329
+ type: ColumnType.Boolean,
330
+ default: false,
331
+ })
332
+ public alertBySlack?: boolean = undefined;
333
+
334
+ @ColumnAccessControl({
335
+ create: [Permission.CurrentUser],
336
+ read: [Permission.CurrentUser],
337
+ update: [Permission.CurrentUser],
338
+ })
339
+ @TableColumn({
340
+ isDefaultValueColumn: true,
341
+ type: TableColumnType.Boolean,
342
+ defaultValue: false,
343
+ example: false,
344
+ })
345
+ @Column({
346
+ type: ColumnType.Boolean,
347
+ default: false,
348
+ })
349
+ public alertByMicrosoftTeams?: boolean = undefined;
350
+
317
351
  @ColumnAccessControl({
318
352
  create: [Permission.CurrentUser],
319
353
  read: [Permission.CurrentUser],
@@ -16,6 +16,8 @@ import UserPush from "./UserPush";
16
16
  import UserOnCallLog from "./UserOnCallLog";
17
17
  import UserSMS from "./UserSMS";
18
18
  import UserTelegram from "./UserTelegram";
19
+ import UserSlack from "./UserSlack";
20
+ import UserMicrosoftTeams from "./UserMicrosoftTeams";
19
21
  import UserWebhook from "./UserWebhook";
20
22
  import UserWhatsApp from "./UserWhatsApp";
21
23
  import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
@@ -1057,6 +1059,99 @@ export default class UserOnCallLogTimeline extends BaseModel {
1057
1059
  })
1058
1060
  public userTelegramId?: ObjectID = undefined;
1059
1061
 
1062
+ @ColumnAccessControl({
1063
+ create: [],
1064
+ read: [Permission.CurrentUser],
1065
+ update: [],
1066
+ })
1067
+ @TableColumn({
1068
+ manyToOneRelationColumn: "userSlackId",
1069
+ type: TableColumnType.Entity,
1070
+ modelType: UserSlack,
1071
+ title: "User Slack",
1072
+ description: "Relation to User Slack Resource in which this object belongs",
1073
+ })
1074
+ @ManyToOne(
1075
+ () => {
1076
+ return UserSlack;
1077
+ },
1078
+ {
1079
+ eager: false,
1080
+ nullable: true,
1081
+ onDelete: "CASCADE",
1082
+ orphanedRowAction: "nullify",
1083
+ },
1084
+ )
1085
+ @JoinColumn({ name: "userSlackId" })
1086
+ public userSlack?: UserSlack = undefined;
1087
+
1088
+ @ColumnAccessControl({
1089
+ create: [],
1090
+ read: [Permission.CurrentUser],
1091
+ update: [],
1092
+ })
1093
+ @Index()
1094
+ @TableColumn({
1095
+ type: TableColumnType.ObjectID,
1096
+ required: false,
1097
+ canReadOnRelationQuery: true,
1098
+ title: "User Slack ID",
1099
+ description: "ID of User Slack in which this object belongs",
1100
+ })
1101
+ @Column({
1102
+ type: ColumnType.ObjectID,
1103
+ nullable: true,
1104
+ transformer: ObjectID.getDatabaseTransformer(),
1105
+ })
1106
+ public userSlackId?: ObjectID = undefined;
1107
+
1108
+ @ColumnAccessControl({
1109
+ create: [],
1110
+ read: [Permission.CurrentUser],
1111
+ update: [],
1112
+ })
1113
+ @TableColumn({
1114
+ manyToOneRelationColumn: "userMicrosoftTeamsId",
1115
+ type: TableColumnType.Entity,
1116
+ modelType: UserMicrosoftTeams,
1117
+ title: "User Microsoft Teams",
1118
+ description:
1119
+ "Relation to User Microsoft Teams Resource in which this object belongs",
1120
+ })
1121
+ @ManyToOne(
1122
+ () => {
1123
+ return UserMicrosoftTeams;
1124
+ },
1125
+ {
1126
+ eager: false,
1127
+ nullable: true,
1128
+ onDelete: "CASCADE",
1129
+ orphanedRowAction: "nullify",
1130
+ },
1131
+ )
1132
+ @JoinColumn({ name: "userMicrosoftTeamsId" })
1133
+ public userMicrosoftTeams?: UserMicrosoftTeams = undefined;
1134
+
1135
+ @ColumnAccessControl({
1136
+ create: [],
1137
+ read: [Permission.CurrentUser],
1138
+ update: [],
1139
+ })
1140
+ @Index()
1141
+ @TableColumn({
1142
+ type: TableColumnType.ObjectID,
1143
+ required: false,
1144
+ canReadOnRelationQuery: true,
1145
+ title: "User Microsoft Teams ID",
1146
+ description: "ID of User Microsoft Teams in which this object belongs",
1147
+ })
1148
+ @Column({
1149
+ type: ColumnType.ObjectID,
1150
+ nullable: true,
1151
+ transformer: ObjectID.getDatabaseTransformer(),
1152
+ })
1153
+ public userMicrosoftTeamsId?: ObjectID = undefined;
1154
+
1060
1155
  @ColumnAccessControl({
1061
1156
  create: [],
1062
1157
  read: [Permission.CurrentUser],
@@ -0,0 +1,341 @@
1
+ import Project from "./Project";
2
+ import User from "./User";
3
+ import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
4
+ import Route from "../../Types/API/Route";
5
+ import AllowAccessIfSubscriptionIsUnpaid from "../../Types/Database/AccessControl/AllowAccessIfSubscriptionIsUnpaid";
6
+ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
7
+ import OwnerOnlyColumn from "../../Types/Database/AccessControl/OwnerOnlyColumn";
8
+ import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
9
+ import ColumnLength from "../../Types/Database/ColumnLength";
10
+ import ColumnType from "../../Types/Database/ColumnType";
11
+ import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
12
+ import CurrentUserCanAccessRecordBy from "../../Types/Database/CurrentUserCanAccessRecordBy";
13
+ import TableColumn from "../../Types/Database/TableColumn";
14
+ import TableColumnType from "../../Types/Database/TableColumnType";
15
+ import TableMetadata from "../../Types/Database/TableMetadata";
16
+ import TenantColumn from "../../Types/Database/TenantColumn";
17
+ import IconProp from "../../Types/Icon/IconProp";
18
+ import ObjectID from "../../Types/ObjectID";
19
+ import Permission from "../../Types/Permission";
20
+ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
21
+
22
+ /*
23
+ * `read` names Permission.CurrentUser and nothing else, exactly like the other
24
+ * notification-method models (UserTelegram has the full essay). That single
25
+ * entry is what keeps every row owner-only: row scoping comes from the TABLE
26
+ * list alone, and widening it would expose the Slack member id every project
27
+ * member's pages are delivered to. Do not add administrator permissions here -
28
+ * OnCallReadinessService returns the masked, admin-sanctioned view instead.
29
+ *
30
+ * Unlike Telegram/SMS/WhatsApp there is no verification code: a row is only
31
+ * ever created from an existing, OAuth-established workspace link
32
+ * (WorkspaceUserAuthToken), so creation IS verification. UserSlackService
33
+ * enforces that and stamps the Slack identifiers server-side.
34
+ */
35
+ @TenantColumn("projectId")
36
+ @AllowAccessIfSubscriptionIsUnpaid()
37
+ @TableAccessControl({
38
+ create: [Permission.CurrentUser],
39
+ read: [Permission.CurrentUser],
40
+ delete: [Permission.CurrentUser],
41
+ update: [Permission.CurrentUser],
42
+ })
43
+ @CrudApiEndpoint(new Route("/user-slack"))
44
+ @Entity({
45
+ name: "UserSlack",
46
+ })
47
+ @TableMetadata({
48
+ tableName: "UserSlack",
49
+ singularName: "Slack Account",
50
+ pluralName: "Slack Accounts",
51
+ icon: IconProp.Slack,
52
+ tableDescription: "Slack accounts used for Slack notifications.",
53
+ })
54
+ @CurrentUserCanAccessRecordBy("userId")
55
+ class UserSlack extends BaseModel {
56
+ @ColumnAccessControl({
57
+ create: [Permission.CurrentUser],
58
+ read: [Permission.CurrentUser],
59
+ update: [],
60
+ })
61
+ @TableColumn({
62
+ manyToOneRelationColumn: "projectId",
63
+ type: TableColumnType.Entity,
64
+ modelType: Project,
65
+ title: "Project",
66
+ description: "Relation to Project Resource in which this object belongs",
67
+ })
68
+ @ManyToOne(
69
+ () => {
70
+ return Project;
71
+ },
72
+ {
73
+ eager: false,
74
+ nullable: true,
75
+ onDelete: "CASCADE",
76
+ orphanedRowAction: "nullify",
77
+ },
78
+ )
79
+ @JoinColumn({ name: "projectId" })
80
+ public project?: Project = undefined;
81
+
82
+ @ColumnAccessControl({
83
+ create: [Permission.CurrentUser],
84
+ read: [Permission.CurrentUser],
85
+ update: [],
86
+ })
87
+ @Index()
88
+ @TableColumn({
89
+ type: TableColumnType.ObjectID,
90
+ required: true,
91
+ canReadOnRelationQuery: true,
92
+ title: "Project ID",
93
+ description: "ID of your OneUptime Project in which this object belongs",
94
+ })
95
+ @Column({
96
+ type: ColumnType.ObjectID,
97
+ nullable: false,
98
+ transformer: ObjectID.getDatabaseTransformer(),
99
+ })
100
+ public projectId?: ObjectID = undefined;
101
+
102
+ @ColumnAccessControl({
103
+ /*
104
+ * CurrentUser here does NOT mean the client may choose the value: the
105
+ * service hook refuses any non-root payload that carries these columns
106
+ * and then stamps them itself, and hooks run BEFORE the column
107
+ * permission check (DatabaseService.create). The entry exists because
108
+ * that same check runs on the STAMPED data - with an empty list the
109
+ * server's own write is refused and no user can add this method at all.
110
+ * Same shape as UserWebAuthn's server-stamped isVerified.
111
+ */
112
+ create: [Permission.CurrentUser],
113
+ read: [Permission.CurrentUser],
114
+ update: [],
115
+ })
116
+ @Index()
117
+ /*
118
+ * The Slack member id (e.g. U0123ABCD) the project's bot delivers direct
119
+ * messages to. Holding it is enough to address bot messages at that person,
120
+ * so it is a delivery target and not a label. Captured server-side from the
121
+ * user's own WorkspaceUserAuthToken - never writable by the client.
122
+ */
123
+ @OwnerOnlyColumn()
124
+ @TableColumn({
125
+ title: "Slack User ID",
126
+ required: false,
127
+ unique: false,
128
+ type: TableColumnType.ShortText,
129
+ canReadOnRelationQuery: true,
130
+ description:
131
+ "Slack member ID captured from your connected Slack account. Populated automatically.",
132
+ })
133
+ @Column({
134
+ type: ColumnType.ShortText,
135
+ length: ColumnLength.ShortText,
136
+ unique: false,
137
+ nullable: true,
138
+ })
139
+ public slackUserId?: string = undefined;
140
+
141
+ @ColumnAccessControl({
142
+ /*
143
+ * CurrentUser here does NOT mean the client may choose the value: the
144
+ * service hook refuses any non-root payload that carries these columns
145
+ * and then stamps them itself, and hooks run BEFORE the column
146
+ * permission check (DatabaseService.create). The entry exists because
147
+ * that same check runs on the STAMPED data - with an empty list the
148
+ * server's own write is refused and no user can add this method at all.
149
+ * Same shape as UserWebAuthn's server-stamped isVerified.
150
+ */
151
+ create: [Permission.CurrentUser],
152
+ read: [Permission.CurrentUser],
153
+ update: [],
154
+ })
155
+ /*
156
+ * The person's Slack display name at the time the method was added. Only a
157
+ * label for the row, but it is still their identity in an outside workspace,
158
+ * so it stays owner-only like the Telegram handle.
159
+ */
160
+ @OwnerOnlyColumn()
161
+ @TableColumn({
162
+ title: "Slack Username",
163
+ required: false,
164
+ unique: false,
165
+ type: TableColumnType.ShortText,
166
+ canReadOnRelationQuery: true,
167
+ description:
168
+ "Slack username captured from your connected Slack account. Populated automatically.",
169
+ })
170
+ @Column({
171
+ type: ColumnType.ShortText,
172
+ length: ColumnLength.ShortText,
173
+ unique: false,
174
+ nullable: true,
175
+ })
176
+ public slackUserName?: string = undefined;
177
+
178
+ @ColumnAccessControl({
179
+ create: [Permission.CurrentUser],
180
+ read: [Permission.CurrentUser],
181
+ update: [],
182
+ })
183
+ @TableColumn({
184
+ manyToOneRelationColumn: "user",
185
+ type: TableColumnType.Entity,
186
+ modelType: User,
187
+ title: "User",
188
+ description: "Relation to User who this Slack account belongs to",
189
+ })
190
+ @ManyToOne(
191
+ () => {
192
+ return User;
193
+ },
194
+ {
195
+ eager: false,
196
+ nullable: true,
197
+ onDelete: "CASCADE",
198
+ orphanedRowAction: "nullify",
199
+ },
200
+ )
201
+ @JoinColumn({ name: "userId" })
202
+ public user?: User = undefined;
203
+
204
+ @ColumnAccessControl({
205
+ create: [Permission.CurrentUser],
206
+ read: [Permission.CurrentUser],
207
+ update: [],
208
+ })
209
+ @TableColumn({
210
+ type: TableColumnType.ObjectID,
211
+ title: "User ID",
212
+ description: "User ID who this Slack account belongs to",
213
+ })
214
+ @Column({
215
+ type: ColumnType.ObjectID,
216
+ nullable: true,
217
+ transformer: ObjectID.getDatabaseTransformer(),
218
+ })
219
+ @Index()
220
+ public userId?: ObjectID = undefined;
221
+
222
+ @ColumnAccessControl({
223
+ create: [Permission.CurrentUser],
224
+ read: [Permission.CurrentUser],
225
+ update: [],
226
+ })
227
+ @TableColumn({
228
+ manyToOneRelationColumn: "createdByUserId",
229
+ type: TableColumnType.Entity,
230
+ modelType: User,
231
+ title: "Created by User",
232
+ description:
233
+ "Relation to User who created this object (if this object was created by a User)",
234
+ })
235
+ @ManyToOne(
236
+ () => {
237
+ return User;
238
+ },
239
+ {
240
+ eager: false,
241
+ nullable: true,
242
+ onDelete: "SET NULL",
243
+ orphanedRowAction: "nullify",
244
+ },
245
+ )
246
+ @JoinColumn({ name: "createdByUserId" })
247
+ public createdByUser?: User = undefined;
248
+
249
+ @ColumnAccessControl({
250
+ create: [Permission.CurrentUser],
251
+ read: [Permission.CurrentUser],
252
+ update: [],
253
+ })
254
+ @TableColumn({
255
+ type: TableColumnType.ObjectID,
256
+ title: "Created by User ID",
257
+ description:
258
+ "User ID who created this object (if this object was created by a User)",
259
+ })
260
+ @Column({
261
+ type: ColumnType.ObjectID,
262
+ nullable: true,
263
+ transformer: ObjectID.getDatabaseTransformer(),
264
+ })
265
+ public createdByUserId?: ObjectID = undefined;
266
+
267
+ @ColumnAccessControl({
268
+ create: [],
269
+ read: [],
270
+ update: [],
271
+ })
272
+ @TableColumn({
273
+ manyToOneRelationColumn: "deletedByUserId",
274
+ type: TableColumnType.Entity,
275
+ title: "Deleted by User",
276
+ modelType: User,
277
+ description:
278
+ "Relation to User who deleted this object (if this object was deleted by a User)",
279
+ })
280
+ @ManyToOne(
281
+ () => {
282
+ return User;
283
+ },
284
+ {
285
+ cascade: false,
286
+ eager: false,
287
+ nullable: true,
288
+ onDelete: "SET NULL",
289
+ orphanedRowAction: "nullify",
290
+ },
291
+ )
292
+ @JoinColumn({ name: "deletedByUserId" })
293
+ public deletedByUser?: User = undefined;
294
+
295
+ @ColumnAccessControl({
296
+ create: [],
297
+ read: [],
298
+ update: [],
299
+ })
300
+ @TableColumn({
301
+ type: TableColumnType.ObjectID,
302
+ title: "Deleted by User ID",
303
+ description:
304
+ "User ID who deleted this object (if this object was deleted by a User)",
305
+ })
306
+ @Column({
307
+ type: ColumnType.ObjectID,
308
+ nullable: true,
309
+ transformer: ObjectID.getDatabaseTransformer(),
310
+ })
311
+ public deletedByUserId?: ObjectID = undefined;
312
+
313
+ @ColumnAccessControl({
314
+ /*
315
+ * CurrentUser here does NOT mean the client may choose the value: the
316
+ * service hook refuses any non-root payload that carries these columns
317
+ * and then stamps them itself, and hooks run BEFORE the column
318
+ * permission check (DatabaseService.create). The entry exists because
319
+ * that same check runs on the STAMPED data - with an empty list the
320
+ * server's own write is refused and no user can add this method at all.
321
+ * Same shape as UserWebAuthn's server-stamped isVerified.
322
+ */
323
+ create: [Permission.CurrentUser],
324
+ read: [Permission.CurrentUser],
325
+ update: [],
326
+ })
327
+ @TableColumn({
328
+ title: "Is Verified",
329
+ description: "Is this Slack account verified?",
330
+ isDefaultValueColumn: true,
331
+ type: TableColumnType.Boolean,
332
+ defaultValue: false,
333
+ })
334
+ @Column({
335
+ type: ColumnType.Boolean,
336
+ default: false,
337
+ })
338
+ public isVerified?: boolean = undefined;
339
+ }
340
+
341
+ export default UserSlack;
@@ -34,6 +34,15 @@ export interface MicrosoftTeamsChat {
34
34
  chatType: MicrosoftTeamsChatType;
35
35
  serviceUrl?: string | undefined; // Bot Framework service URL captured when the bot was added.
36
36
  addedAt?: string | undefined;
37
+ /*
38
+ * Microsoft Entra object ids of the human members, captured from the chat
39
+ * roster. What lets a personal chat be matched back to the OneUptime user
40
+ * it belongs to (WorkspaceUserAuthToken.workspaceUserId is the same id), so
41
+ * user direct-message notifications can reuse the existing chat instead of
42
+ * creating a new conversation. Absent on records captured before this
43
+ * shipped.
44
+ */
45
+ memberAadObjectIds?: Array<string> | undefined;
37
46
  }
38
47
 
39
48
  /*
@@ -0,0 +1,127 @@
1
+ import UserMiddleware from "../Middleware/UserAuthorization";
2
+ import UserMicrosoftTeamsService, {
3
+ Service as UserMicrosoftTeamsServiceType,
4
+ } from "../Services/UserMicrosoftTeamsService";
5
+ import WorkspaceUserNotificationService from "../Services/WorkspaceUserNotificationService";
6
+ import {
7
+ ExpressRequest,
8
+ ExpressResponse,
9
+ NextFunction,
10
+ OneUptimeRequest,
11
+ } from "../Utils/Express";
12
+ import Response from "../Utils/Response";
13
+ import BaseAPI from "./BaseAPI";
14
+ import BadDataException from "../../Types/Exception/BadDataException";
15
+ import UserMicrosoftTeams from "../../Models/DatabaseModels/UserMicrosoftTeams";
16
+ import WorkspaceType from "../../Types/Workspace/WorkspaceType";
17
+ import { WorkspacePayloadMarkdown } from "../../Types/Workspace/WorkspaceMessagePayload";
18
+
19
+ export default class UserMicrosoftTeamsAPI extends BaseAPI<
20
+ UserMicrosoftTeams,
21
+ UserMicrosoftTeamsServiceType
22
+ > {
23
+ public constructor() {
24
+ super(UserMicrosoftTeams, UserMicrosoftTeamsService);
25
+
26
+ /*
27
+ * Sends a test direct message to the caller's own linked Microsoft Teams
28
+ * account. Worth a click on this channel more than any other: a Teams DM
29
+ * needs the OneUptime app installed for the user, and this surfaces the
30
+ * actionable "install the app" error before a real page depends on it.
31
+ */
32
+ this.router.post(
33
+ `${new this.entityType().getCrudApiPath()?.toString()}/test`,
34
+ UserMiddleware.getUserMiddleware,
35
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
36
+ try {
37
+ req = req as OneUptimeRequest;
38
+
39
+ if (!req.body["itemId"]) {
40
+ return Response.sendErrorResponse(
41
+ req,
42
+ res,
43
+ new BadDataException("Invalid item ID"),
44
+ );
45
+ }
46
+
47
+ const item: UserMicrosoftTeams | null =
48
+ await this.service.findOneById({
49
+ id: req.body["itemId"],
50
+ props: {
51
+ isRoot: true,
52
+ },
53
+ select: {
54
+ userId: true,
55
+ projectId: true,
56
+ microsoftTeamsUserId: true,
57
+ isVerified: true,
58
+ },
59
+ });
60
+
61
+ if (!item) {
62
+ return Response.sendErrorResponse(
63
+ req,
64
+ res,
65
+ new BadDataException("Item not found"),
66
+ );
67
+ }
68
+
69
+ if (
70
+ item.userId?.toString() !==
71
+ (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
72
+ ) {
73
+ return Response.sendErrorResponse(
74
+ req,
75
+ res,
76
+ new BadDataException("Invalid user ID"),
77
+ );
78
+ }
79
+
80
+ if (!item.microsoftTeamsUserId || !item.isVerified) {
81
+ return Response.sendErrorResponse(
82
+ req,
83
+ res,
84
+ new BadDataException(
85
+ "This Microsoft Teams account is not verified. Please remove it and add it again.",
86
+ ),
87
+ );
88
+ }
89
+
90
+ const markdownBlock: WorkspacePayloadMarkdown = {
91
+ _type: "WorkspacePayloadMarkdown",
92
+ text: "👋 This is a OneUptime test notification. Your Microsoft Teams account can receive on-call notifications.",
93
+ };
94
+
95
+ try {
96
+ await WorkspaceUserNotificationService.sendDirectMessageToUser({
97
+ projectId: item.projectId!,
98
+ workspaceType: WorkspaceType.MicrosoftTeams,
99
+ workspaceUserId: item.microsoftTeamsUserId,
100
+ messageBlocks: [markdownBlock],
101
+ messageSummary: "Test notification",
102
+ userId: item.userId!,
103
+ });
104
+ } catch (err) {
105
+ const message: string =
106
+ err instanceof Error && err.message
107
+ ? err.message
108
+ : "Unknown error sending test Microsoft Teams message.";
109
+
110
+ return Response.sendJsonObjectResponse(req, res, {
111
+ ok: false,
112
+ statusMessage: message,
113
+ });
114
+ }
115
+
116
+ return Response.sendJsonObjectResponse(req, res, {
117
+ ok: true,
118
+ statusMessage:
119
+ "Test message sent. Check your Microsoft Teams chats.",
120
+ });
121
+ } catch (err) {
122
+ return next(err);
123
+ }
124
+ },
125
+ );
126
+ }
127
+ }