@oneuptime/common 9.5.7 → 9.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Alert.ts +8 -9
- package/Models/DatabaseModels/Incident.ts +5 -5
- package/Models/DatabaseModels/IncidentTemplate.ts +4 -3
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -1
- package/Models/DatabaseModels/UserOnCallLog.ts +1 -1
- package/Models/DatabaseModels/UserPush.ts +2 -1
- package/Server/API/UserPushAPI.ts +51 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770833704656-MigrationName.ts +156 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770834237090-MigrationName.ts +119 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +14 -9
- package/Server/Services/AlertEpisodeFeedService.ts +50 -0
- package/Server/Services/AlertEpisodeInternalNoteService.ts +162 -0
- package/Server/Services/AlertEpisodeMemberService.ts +7 -0
- package/Server/Services/AlertEpisodeOwnerTeamService.ts +186 -0
- package/Server/Services/AlertEpisodeOwnerUserService.ts +180 -0
- package/Server/Services/AlertEpisodeService.ts +68 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +5 -0
- package/Server/Services/AlertService.ts +3 -0
- package/Server/Services/IncidentEpisodeFeedService.ts +50 -0
- package/Server/Services/IncidentEpisodeInternalNoteService.ts +163 -0
- package/Server/Services/IncidentEpisodeMemberService.ts +7 -0
- package/Server/Services/IncidentEpisodeOwnerTeamService.ts +189 -0
- package/Server/Services/IncidentEpisodeOwnerUserService.ts +183 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +8 -0
- package/Server/Services/IncidentEpisodeService.ts +91 -12
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +5 -0
- package/Server/Services/IncidentService.ts +5 -0
- package/Server/Services/PushNotificationService.ts +129 -27
- package/Server/Services/UserNotificationRuleService.ts +13 -3
- package/Server/Services/UserPushService.ts +2 -1
- package/Server/Services/WorkspaceNotificationRuleService.ts +20 -0
- package/Server/Utils/PushNotificationUtil.ts +56 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +1 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +7 -6
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +1 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +7 -6
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +17 -0
- package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +27 -12
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +17 -0
- package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +86 -28
- package/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.ts +6 -6
- package/Server/Utils/Workspace/Slack/Slack.ts +49 -0
- package/Server/Utils/Workspace/WorkspaceMessages/Alert.ts +2 -1
- package/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.ts +3 -1
- package/Server/Utils/Workspace/WorkspaceMessages/Incident.ts +2 -1
- package/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.ts +3 -1
- package/Types/Permission.ts +641 -0
- package/Types/PushNotification/PushDeviceType.ts +7 -0
- package/Types/PushNotification/PushNotificationRequest.ts +3 -1
- package/UI/Components/Detail/Detail.tsx +13 -4
- package/UI/Components/Detail/Field.ts +2 -2
- package/UI/Components/Dropdown/Dropdown.tsx +38 -7
- package/UI/Components/Forms/BasicForm.tsx +35 -5
- package/UI/Components/Forms/Fields/PermissionPicker.tsx +261 -0
- package/UI/Components/Forms/Types/Field.ts +5 -3
- package/UI/Components/ModelDelete/ModelDelete.tsx +4 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +4 -0
- package/UI/Components/ModelDetail/ModelDetail.tsx +4 -1
- package/UI/Components/Page/ModelPage.tsx +4 -1
- package/UI/Utils/Permission.ts +29 -6
- package/build/dist/Models/DatabaseModels/Alert.js +8 -8
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +5 -5
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +3 -3
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +2 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Server/API/UserPushAPI.js +34 -3
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770833704656-MigrationName.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770833704656-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237090-MigrationName.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237090-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +10 -4
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeFeedService.js +33 -0
- package/build/dist/Server/Services/AlertEpisodeFeedService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeInternalNoteService.js +132 -0
- package/build/dist/Server/Services/AlertEpisodeInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeMemberService.js +7 -0
- package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerTeamService.js +163 -0
- package/build/dist/Server/Services/AlertEpisodeOwnerTeamService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerUserService.js +156 -0
- package/build/dist/Server/Services/AlertEpisodeOwnerUserService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +53 -0
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +4 -0
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +3 -5
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeFeedService.js +33 -0
- package/build/dist/Server/Services/IncidentEpisodeFeedService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js +132 -0
- package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +7 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js +163 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js +156 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +8 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +72 -10
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +4 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +5 -5
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +77 -21
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +12 -9
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +2 -1
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +16 -0
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +32 -8
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +7 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +7 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +16 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +25 -9
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +16 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +71 -25
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js +6 -6
- package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +40 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Alert.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Incident.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js.map +1 -1
- package/build/dist/Types/Permission.js +637 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/PushNotification/PushDeviceType.js +8 -0
- package/build/dist/Types/PushNotification/PushDeviceType.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +7 -1
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +17 -2
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +17 -3
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/PermissionPicker.js +129 -0
- package/build/dist/UI/Components/Forms/Fields/PermissionPicker.js.map +1 -0
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +2 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +2 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/Page/ModelPage.js +2 -1
- package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
- package/build/dist/UI/Utils/Permission.js +17 -4
- package/build/dist/UI/Utils/Permission.js.map +1 -1
- package/package.json +2 -1
|
@@ -33,6 +33,8 @@ import User from "../../Models/DatabaseModels/User";
|
|
|
33
33
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
34
34
|
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
35
35
|
import WorkspaceType from "../../Types/Workspace/WorkspaceType";
|
|
36
|
+
import IncidentEpisodeWorkspaceMessages from "../Utils/Workspace/WorkspaceMessages/IncidentEpisode";
|
|
37
|
+
import { MessageBlocksByWorkspaceType } from "./WorkspaceNotificationRuleService";
|
|
36
38
|
import IncidentService from "./IncidentService";
|
|
37
39
|
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
38
40
|
import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
@@ -147,6 +149,17 @@ export class Service extends DatabaseService<Model> {
|
|
|
147
149
|
|
|
148
150
|
// Create initial state timeline entry
|
|
149
151
|
Promise.resolve()
|
|
152
|
+
.then(async () => {
|
|
153
|
+
try {
|
|
154
|
+
if (createdItem.projectId && createdItem.id) {
|
|
155
|
+
await this.handleEpisodeWorkspaceOperationsAsync(createdItem);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
logger.error(
|
|
159
|
+
`Workspace operations failed in IncidentEpisodeService.onCreateSuccess: ${error}`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
})
|
|
150
163
|
.then(async () => {
|
|
151
164
|
try {
|
|
152
165
|
await this.changeEpisodeState({
|
|
@@ -193,6 +206,51 @@ export class Service extends DatabaseService<Model> {
|
|
|
193
206
|
return createdItem;
|
|
194
207
|
}
|
|
195
208
|
|
|
209
|
+
@CaptureSpan()
|
|
210
|
+
private async handleEpisodeWorkspaceOperationsAsync(
|
|
211
|
+
createdItem: Model,
|
|
212
|
+
): Promise<void> {
|
|
213
|
+
try {
|
|
214
|
+
if (!createdItem.projectId || !createdItem.id) {
|
|
215
|
+
throw new BadDataException(
|
|
216
|
+
"projectId and id are required for workspace operations",
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const workspaceResult: {
|
|
221
|
+
channelsCreated: Array<NotificationRuleWorkspaceChannel>;
|
|
222
|
+
} | null =
|
|
223
|
+
await IncidentEpisodeWorkspaceMessages.createChannelsAndInviteUsersToChannels(
|
|
224
|
+
{
|
|
225
|
+
projectId: createdItem.projectId,
|
|
226
|
+
incidentEpisodeId: createdItem.id,
|
|
227
|
+
episodeNumber: createdItem.episodeNumber || 0,
|
|
228
|
+
...(createdItem.episodeNumberWithPrefix
|
|
229
|
+
? {
|
|
230
|
+
episodeNumberWithPrefix: createdItem.episodeNumberWithPrefix,
|
|
231
|
+
}
|
|
232
|
+
: {}),
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
if (workspaceResult && workspaceResult.channelsCreated?.length > 0) {
|
|
237
|
+
await this.updateOneById({
|
|
238
|
+
id: createdItem.id,
|
|
239
|
+
data: {
|
|
240
|
+
postUpdatesToWorkspaceChannels:
|
|
241
|
+
workspaceResult.channelsCreated || [],
|
|
242
|
+
},
|
|
243
|
+
props: {
|
|
244
|
+
isRoot: true,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
logger.error(`Error in handleEpisodeWorkspaceOperationsAsync: ${error}`);
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
196
254
|
@CaptureSpan()
|
|
197
255
|
private async createEpisodeCreatedFeed(episode: Model): Promise<void> {
|
|
198
256
|
if (!episode.id || !episode.projectId) {
|
|
@@ -213,6 +271,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
213
271
|
feedInfoInMarkdown += `This episode was manually created.\n\n`;
|
|
214
272
|
}
|
|
215
273
|
|
|
274
|
+
const episodeCreateMessageBlocks: Array<MessageBlocksByWorkspaceType> =
|
|
275
|
+
await IncidentEpisodeWorkspaceMessages.getIncidentEpisodeCreateMessageBlocks(
|
|
276
|
+
{
|
|
277
|
+
incidentEpisodeId: episode.id,
|
|
278
|
+
projectId: episode.projectId,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
|
|
216
282
|
await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
|
|
217
283
|
incidentEpisodeId: episode.id,
|
|
218
284
|
projectId: episode.projectId,
|
|
@@ -220,6 +286,10 @@ export class Service extends DatabaseService<Model> {
|
|
|
220
286
|
displayColor: Red500,
|
|
221
287
|
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
222
288
|
userId: episode.createdByUserId || undefined,
|
|
289
|
+
workspaceNotification: {
|
|
290
|
+
appendMessageBlocks: episodeCreateMessageBlocks,
|
|
291
|
+
sendWorkspaceNotification: true,
|
|
292
|
+
},
|
|
223
293
|
});
|
|
224
294
|
}
|
|
225
295
|
|
|
@@ -952,21 +1022,30 @@ export class Service extends DatabaseService<Model> {
|
|
|
952
1022
|
}
|
|
953
1023
|
|
|
954
1024
|
@CaptureSpan()
|
|
955
|
-
public getWorkspaceChannelForEpisode(
|
|
956
|
-
|
|
957
|
-
workspaceType
|
|
958
|
-
): Array<NotificationRuleWorkspaceChannel
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1025
|
+
public async getWorkspaceChannelForEpisode(data: {
|
|
1026
|
+
episodeId: ObjectID;
|
|
1027
|
+
workspaceType?: WorkspaceType | null;
|
|
1028
|
+
}): Promise<Array<NotificationRuleWorkspaceChannel>> {
|
|
1029
|
+
const episode: Model | null = await this.findOneById({
|
|
1030
|
+
id: data.episodeId,
|
|
1031
|
+
select: {
|
|
1032
|
+
postUpdatesToWorkspaceChannels: true,
|
|
1033
|
+
},
|
|
1034
|
+
props: {
|
|
1035
|
+
isRoot: true,
|
|
1036
|
+
},
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
if (!episode) {
|
|
1040
|
+
throw new BadDataException("Incident Episode not found.");
|
|
965
1041
|
}
|
|
966
1042
|
|
|
967
|
-
return episode.postUpdatesToWorkspaceChannels.filter(
|
|
1043
|
+
return (episode.postUpdatesToWorkspaceChannels || []).filter(
|
|
968
1044
|
(channel: NotificationRuleWorkspaceChannel) => {
|
|
969
|
-
|
|
1045
|
+
if (!data.workspaceType) {
|
|
1046
|
+
return true;
|
|
1047
|
+
}
|
|
1048
|
+
return channel.workspaceType === data.workspaceType;
|
|
970
1049
|
},
|
|
971
1050
|
);
|
|
972
1051
|
}
|
|
@@ -383,6 +383,11 @@ export class Service extends DatabaseService<IncidentEpisodeStateTimeline> {
|
|
|
383
383
|
? `**Cause:** \n${createdItem.rootCause}`
|
|
384
384
|
: undefined,
|
|
385
385
|
userId: createdItem.createdByUserId || onCreate.createBy.props.userId,
|
|
386
|
+
workspaceNotification: {
|
|
387
|
+
sendWorkspaceNotification: true,
|
|
388
|
+
notifyUserId:
|
|
389
|
+
createdItem.createdByUserId || onCreate.createBy.props.userId,
|
|
390
|
+
},
|
|
386
391
|
});
|
|
387
392
|
|
|
388
393
|
return createdItem;
|
|
@@ -843,6 +843,11 @@ export class Service extends DatabaseService<Model> {
|
|
|
843
843
|
projectId: createdItem.projectId,
|
|
844
844
|
incidentId: createdItem.id,
|
|
845
845
|
incidentNumber: createdItem.incidentNumber!,
|
|
846
|
+
...(createdItem.incidentNumberWithPrefix
|
|
847
|
+
? {
|
|
848
|
+
incidentNumberWithPrefix: createdItem.incidentNumberWithPrefix,
|
|
849
|
+
}
|
|
850
|
+
: {}),
|
|
846
851
|
});
|
|
847
852
|
|
|
848
853
|
if (workspaceResult && workspaceResult.channelsCreated?.length > 0) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import PushNotificationRequest from "../../Types/PushNotification/PushNotificationRequest";
|
|
2
2
|
import PushNotificationMessage from "../../Types/PushNotification/PushNotificationMessage";
|
|
3
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
3
4
|
import ObjectID from "../../Types/ObjectID";
|
|
4
5
|
import logger from "../Utils/Logger";
|
|
5
6
|
import UserPushService from "./UserPushService";
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
VapidSubject,
|
|
12
13
|
} from "../EnvironmentConfig";
|
|
13
14
|
import webpush from "web-push";
|
|
15
|
+
import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";
|
|
14
16
|
import PushNotificationUtil from "../Utils/PushNotificationUtil";
|
|
15
17
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
16
18
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
@@ -41,6 +43,7 @@ export interface PushNotificationOptions {
|
|
|
41
43
|
|
|
42
44
|
export default class PushNotificationService {
|
|
43
45
|
public static isWebPushInitialized = false;
|
|
46
|
+
private static expoClient: Expo = new Expo();
|
|
44
47
|
|
|
45
48
|
public static initializeWebPush(): void {
|
|
46
49
|
if (this.isWebPushInitialized) {
|
|
@@ -76,13 +79,8 @@ export default class PushNotificationService {
|
|
|
76
79
|
throw new Error("No devices provided");
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
if (request.deviceType !== "web") {
|
|
80
|
-
logger.error(`Unsupported device type: ${request.deviceType}`);
|
|
81
|
-
throw new Error("Only web push notifications are supported");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
82
|
logger.info(
|
|
85
|
-
`Sending
|
|
83
|
+
`Sending ${request.deviceType} push notifications to ${request.devices.length} devices`,
|
|
86
84
|
);
|
|
87
85
|
logger.info(`Notification message: ${JSON.stringify(request.message)}`);
|
|
88
86
|
|
|
@@ -98,9 +96,25 @@ export default class PushNotificationService {
|
|
|
98
96
|
const promises: Promise<void>[] = [];
|
|
99
97
|
|
|
100
98
|
for (const device of request.devices) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
if (request.deviceType === PushDeviceType.Web) {
|
|
100
|
+
promises.push(
|
|
101
|
+
this.sendWebPushNotification(device.token, request.message, options),
|
|
102
|
+
);
|
|
103
|
+
} else if (
|
|
104
|
+
request.deviceType === PushDeviceType.iOS ||
|
|
105
|
+
request.deviceType === PushDeviceType.Android
|
|
106
|
+
) {
|
|
107
|
+
promises.push(
|
|
108
|
+
this.sendExpoPushNotification(
|
|
109
|
+
device.token,
|
|
110
|
+
request.message,
|
|
111
|
+
request.deviceType,
|
|
112
|
+
options,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
} else {
|
|
116
|
+
logger.error(`Unsupported device type: ${request.deviceType}`);
|
|
117
|
+
}
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
const results: Array<any> = await Promise.allSettled(promises);
|
|
@@ -314,6 +328,81 @@ export default class PushNotificationService {
|
|
|
314
328
|
}
|
|
315
329
|
}
|
|
316
330
|
|
|
331
|
+
private static async sendExpoPushNotification(
|
|
332
|
+
expoPushToken: string,
|
|
333
|
+
message: PushNotificationMessage,
|
|
334
|
+
deviceType: PushDeviceType,
|
|
335
|
+
_options: PushNotificationOptions,
|
|
336
|
+
): Promise<void> {
|
|
337
|
+
if (!Expo.isExpoPushToken(expoPushToken)) {
|
|
338
|
+
throw new Error(
|
|
339
|
+
`Invalid Expo push token for ${deviceType} device: ${expoPushToken}`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const dataPayload: { [key: string]: string } = {};
|
|
345
|
+
if (message.data) {
|
|
346
|
+
for (const key of Object.keys(message.data)) {
|
|
347
|
+
dataPayload[key] = String(message.data[key]);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (message.url || message.clickAction) {
|
|
351
|
+
dataPayload["url"] = message.url || message.clickAction || "";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const channelId: string =
|
|
355
|
+
deviceType === PushDeviceType.Android ? "oncall_high" : "default";
|
|
356
|
+
|
|
357
|
+
const expoPushMessage: ExpoPushMessage = {
|
|
358
|
+
to: expoPushToken,
|
|
359
|
+
title: message.title,
|
|
360
|
+
body: message.body,
|
|
361
|
+
data: dataPayload,
|
|
362
|
+
sound: "default",
|
|
363
|
+
priority: "high",
|
|
364
|
+
channelId: channelId,
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const tickets: ExpoPushTicket[] =
|
|
368
|
+
await this.expoClient.sendPushNotificationsAsync([expoPushMessage]);
|
|
369
|
+
|
|
370
|
+
const ticket: ExpoPushTicket | undefined = tickets[0];
|
|
371
|
+
|
|
372
|
+
if (ticket && ticket.status === "error") {
|
|
373
|
+
const errorTicket: ExpoPushTicket & {
|
|
374
|
+
message?: string;
|
|
375
|
+
details?: { error?: string };
|
|
376
|
+
} = ticket as ExpoPushTicket & {
|
|
377
|
+
message?: string;
|
|
378
|
+
details?: { error?: string };
|
|
379
|
+
};
|
|
380
|
+
logger.error(
|
|
381
|
+
`Expo push notification error for ${deviceType} device: ${errorTicket.message}`,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
if (errorTicket.details?.error === "DeviceNotRegistered") {
|
|
385
|
+
logger.info(
|
|
386
|
+
"Expo push token is no longer valid (DeviceNotRegistered)",
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
throw new Error(
|
|
391
|
+
`Expo push notification failed: ${errorTicket.message}`,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
logger.info(
|
|
396
|
+
`Expo push notification sent successfully to ${deviceType} device`,
|
|
397
|
+
);
|
|
398
|
+
} catch (error: any) {
|
|
399
|
+
logger.error(
|
|
400
|
+
`Failed to send Expo push notification to ${deviceType} device: ${error.message}`,
|
|
401
|
+
);
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
317
406
|
public static async sendPushNotificationToUser(
|
|
318
407
|
userId: ObjectID,
|
|
319
408
|
projectId: ObjectID,
|
|
@@ -342,33 +431,46 @@ export default class PushNotificationService {
|
|
|
342
431
|
|
|
343
432
|
if (userPushDevices.length === 0) {
|
|
344
433
|
logger.info(
|
|
345
|
-
`No verified
|
|
434
|
+
`No verified push devices found for user ${userId.toString()}`,
|
|
346
435
|
);
|
|
347
436
|
return;
|
|
348
437
|
}
|
|
349
438
|
|
|
350
|
-
//
|
|
351
|
-
const
|
|
439
|
+
// Group devices by type
|
|
440
|
+
const devicesByType: Map<
|
|
441
|
+
string,
|
|
442
|
+
Array<{ token: string; name?: string }>
|
|
443
|
+
> = new Map();
|
|
352
444
|
|
|
353
445
|
for (const device of userPushDevices) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
name: device.deviceName || "Unknown Device",
|
|
358
|
-
});
|
|
446
|
+
const type: string = device.deviceType || PushDeviceType.Web;
|
|
447
|
+
if (!devicesByType.has(type)) {
|
|
448
|
+
devicesByType.set(type, []);
|
|
359
449
|
}
|
|
450
|
+
devicesByType.get(type)!.push({
|
|
451
|
+
token: device.deviceToken!,
|
|
452
|
+
name: device.deviceName || "Unknown Device",
|
|
453
|
+
});
|
|
360
454
|
}
|
|
361
455
|
|
|
362
|
-
// Send notifications to
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
456
|
+
// Send notifications to each device type group
|
|
457
|
+
const sendPromises: Promise<void>[] = [];
|
|
458
|
+
|
|
459
|
+
for (const [deviceType, devices] of devicesByType.entries()) {
|
|
460
|
+
if (devices.length > 0) {
|
|
461
|
+
sendPromises.push(
|
|
462
|
+
this.sendPushNotification(
|
|
463
|
+
{
|
|
464
|
+
devices: devices,
|
|
465
|
+
message: message,
|
|
466
|
+
deviceType: deviceType as PushDeviceType,
|
|
467
|
+
},
|
|
468
|
+
options,
|
|
469
|
+
),
|
|
470
|
+
);
|
|
471
|
+
}
|
|
372
472
|
}
|
|
473
|
+
|
|
474
|
+
await Promise.allSettled(sendPromises);
|
|
373
475
|
}
|
|
374
476
|
}
|
|
@@ -29,6 +29,7 @@ import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
|
29
29
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
30
30
|
import NotificationRuleType from "../../Types/NotificationRule/NotificationRuleType";
|
|
31
31
|
import ObjectID from "../../Types/ObjectID";
|
|
32
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
32
33
|
import Phone from "../../Types/Phone";
|
|
33
34
|
import SMS from "../../Types/SMS/SMS";
|
|
34
35
|
import WhatsAppMessage from "../../Types/WhatsApp/WhatsAppMessage";
|
|
@@ -1101,6 +1102,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1101
1102
|
...(alert.alertNumber !== undefined && {
|
|
1102
1103
|
alertNumber: alert.alertNumber,
|
|
1103
1104
|
}),
|
|
1105
|
+
alertId: alert.id!.toString(),
|
|
1106
|
+
projectId: alert.projectId!.toString(),
|
|
1104
1107
|
});
|
|
1105
1108
|
|
|
1106
1109
|
// send push notification.
|
|
@@ -1115,7 +1118,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1115
1118
|
},
|
|
1116
1119
|
],
|
|
1117
1120
|
message: pushMessage,
|
|
1118
|
-
deviceType: notificationRuleItem.userPush
|
|
1121
|
+
deviceType: notificationRuleItem.userPush
|
|
1122
|
+
.deviceType! as PushDeviceType,
|
|
1119
1123
|
},
|
|
1120
1124
|
{
|
|
1121
1125
|
projectId: options.projectId,
|
|
@@ -1175,6 +1179,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1175
1179
|
...(incident.incidentNumber !== undefined && {
|
|
1176
1180
|
incidentNumber: incident.incidentNumber,
|
|
1177
1181
|
}),
|
|
1182
|
+
incidentId: incident.id!.toString(),
|
|
1183
|
+
projectId: incident.projectId!.toString(),
|
|
1178
1184
|
});
|
|
1179
1185
|
|
|
1180
1186
|
// send push notification.
|
|
@@ -1189,7 +1195,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1189
1195
|
},
|
|
1190
1196
|
],
|
|
1191
1197
|
message: pushMessage,
|
|
1192
|
-
deviceType: notificationRuleItem.userPush
|
|
1198
|
+
deviceType: notificationRuleItem.userPush
|
|
1199
|
+
.deviceType! as PushDeviceType,
|
|
1193
1200
|
},
|
|
1194
1201
|
{
|
|
1195
1202
|
projectId: options.projectId,
|
|
@@ -1251,6 +1258,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1251
1258
|
...(alertEpisode.episodeNumberWithPrefix && {
|
|
1252
1259
|
episodeNumberWithPrefix: alertEpisode.episodeNumberWithPrefix,
|
|
1253
1260
|
}),
|
|
1261
|
+
alertEpisodeId: alertEpisode.id!.toString(),
|
|
1262
|
+
projectId: alertEpisode.projectId!.toString(),
|
|
1254
1263
|
});
|
|
1255
1264
|
|
|
1256
1265
|
PushNotificationService.sendPushNotification(
|
|
@@ -1264,7 +1273,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1264
1273
|
},
|
|
1265
1274
|
],
|
|
1266
1275
|
message: pushMessage,
|
|
1267
|
-
deviceType: notificationRuleItem.userPush
|
|
1276
|
+
deviceType: notificationRuleItem.userPush
|
|
1277
|
+
.deviceType! as PushDeviceType,
|
|
1268
1278
|
},
|
|
1269
1279
|
{
|
|
1270
1280
|
projectId: options.projectId,
|
|
@@ -4,6 +4,7 @@ import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
|
4
4
|
import DatabaseService from "./DatabaseService";
|
|
5
5
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
6
6
|
import PositiveNumber from "../../Types/PositiveNumber";
|
|
7
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
7
8
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
8
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
9
10
|
|
|
@@ -25,7 +26,7 @@ export class Service extends DatabaseService<UserPush> {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
// Validate device type
|
|
28
|
-
const validDeviceTypes: string[] =
|
|
29
|
+
const validDeviceTypes: string[] = Object.values(PushDeviceType);
|
|
29
30
|
if (!validDeviceTypes.includes(createBy.data.deviceType)) {
|
|
30
31
|
throw new BadDataException(
|
|
31
32
|
"Device type must be one of: " + validDeviceTypes.join(", "),
|
|
@@ -52,6 +52,7 @@ import WorkspaceNotificationActionType from "../../Types/Workspace/WorkspaceNoti
|
|
|
52
52
|
import ExceptionMessages from "../../Types/Exception/ExceptionMessages";
|
|
53
53
|
import IncidentEpisode from "../../Models/DatabaseModels/IncidentEpisode";
|
|
54
54
|
import IncidentEpisodeService from "./IncidentEpisodeService";
|
|
55
|
+
import AlertEpisodeService from "./AlertEpisodeService";
|
|
55
56
|
|
|
56
57
|
export interface MessageBlocksByWorkspaceType {
|
|
57
58
|
workspaceType: WorkspaceType;
|
|
@@ -735,6 +736,25 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
735
736
|
);
|
|
736
737
|
}
|
|
737
738
|
|
|
739
|
+
// incident episodes
|
|
740
|
+
if (data.notificationFor.incidentEpisodeId) {
|
|
741
|
+
monitorChannels =
|
|
742
|
+
await IncidentEpisodeService.getWorkspaceChannelForEpisode({
|
|
743
|
+
episodeId: data.notificationFor.incidentEpisodeId,
|
|
744
|
+
workspaceType: data.workspaceType,
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// alert episodes
|
|
749
|
+
if (data.notificationFor.alertEpisodeId) {
|
|
750
|
+
monitorChannels = await AlertEpisodeService.getWorkspaceChannelForEpisode(
|
|
751
|
+
{
|
|
752
|
+
episodeId: data.notificationFor.alertEpisodeId,
|
|
753
|
+
workspaceType: data.workspaceType,
|
|
754
|
+
},
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
738
758
|
logger.debug("Workspace channels found:");
|
|
739
759
|
logger.debug(monitorChannels);
|
|
740
760
|
|