mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Add incident member notification system with email and WhatsApp templates
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
{{> Start this}}
|
||||
|
||||
|
||||
{{> Logo this}}
|
||||
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
|
||||
|
||||
{{> InfoBlock info=(concat "You have been assigned as " incidentRole " to this incident.")}}
|
||||
|
||||
{{> InfoBlock info="Here are the details: "}}
|
||||
|
||||
{{> DetailBoxStart this }}
|
||||
{{> DetailBoxField title="Incident Title:" text=incidentTitle }}
|
||||
{{> DetailBoxField title="Your Role: " text=incidentRole }}
|
||||
{{> DetailBoxField title="Current State: " text=currentState }}
|
||||
{{> DetailBoxField title="Resources Affected: " text=resourcesAffected }}
|
||||
{{> DetailBoxField title="Severity: " text=incidentSeverity }}
|
||||
{{> DetailBoxField title="Description: " text=incidentDescription }}
|
||||
{{> DetailBoxEnd this }}
|
||||
|
||||
|
||||
{{> InfoBlock info="You can view this incident by clicking on the button below - "}}
|
||||
|
||||
{{> ButtonBlock buttonUrl=incidentViewLink buttonText="View on Dashboard"}}
|
||||
|
||||
{{> InfoBlock info="You can also copy and paste this link:"}}
|
||||
{{> InfoBlock info=incidentViewLink}}
|
||||
|
||||
{{> InfoBlock info="You will be notified when the status of this incident changes."}}
|
||||
|
||||
{{> Footer this }}
|
||||
|
||||
{{> End this}}
|
||||
@@ -38,6 +38,8 @@ export enum IncidentFeedEventType {
|
||||
OwnerTeamRemoved = "OwnerTeamRemoved",
|
||||
OnCallPolicy = "OnCallPolicy",
|
||||
OnCallNotification = "OnCallNotification",
|
||||
IncidentMemberAdded = "IncidentMemberAdded",
|
||||
IncidentMemberRemoved = "IncidentMemberRemoved",
|
||||
}
|
||||
|
||||
@EnableDocumentation()
|
||||
|
||||
@@ -521,4 +521,24 @@ export default class IncidentMember extends BaseModel {
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Is Member Notified",
|
||||
description: "Has the member been notified of this role assignment?",
|
||||
defaultValue: false,
|
||||
example: false,
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
nullable: false,
|
||||
default: false,
|
||||
})
|
||||
public isMemberNotified?: boolean = undefined;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,209 @@
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import Model from "../../Models/DatabaseModels/IncidentMember";
|
||||
import IncidentFeedService from "./IncidentFeedService";
|
||||
import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
|
||||
import { Gray500, Red500 } from "../../Types/BrandColors";
|
||||
import User from "../../Models/DatabaseModels/User";
|
||||
import UserService from "./UserService";
|
||||
import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
||||
import DeleteBy from "../Types/Database/DeleteBy";
|
||||
import IncidentService from "./IncidentService";
|
||||
import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
|
||||
import NotificationRuleEventType from "../../Types/Workspace/NotificationRules/EventType";
|
||||
import WorkspaceNotificationRule from "../../Models/DatabaseModels/WorkspaceNotificationRule";
|
||||
import logger from "../Utils/Logger";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import IncidentRole from "../../Models/DatabaseModels/IncidentRole";
|
||||
import IncidentRoleService from "./IncidentRoleService";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
protected override async onBeforeDelete(
|
||||
deleteBy: DeleteBy<Model>,
|
||||
): Promise<OnDelete<Model>> {
|
||||
const itemsToDelete: Model[] = await this.findBy({
|
||||
query: deleteBy.query,
|
||||
limit: deleteBy.limit,
|
||||
skip: deleteBy.skip,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
incidentId: true,
|
||||
projectId: true,
|
||||
userId: true,
|
||||
incidentRoleId: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
carryForward: {
|
||||
itemsToDelete: itemsToDelete,
|
||||
},
|
||||
deleteBy: deleteBy,
|
||||
};
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
protected override async onDeleteSuccess(
|
||||
onDelete: OnDelete<Model>,
|
||||
_itemIdsBeforeDelete: Array<ObjectID>,
|
||||
): Promise<OnDelete<Model>> {
|
||||
const deleteByUserId: ObjectID | undefined =
|
||||
onDelete.deleteBy.deletedByUser?.id || onDelete.deleteBy.props.userId;
|
||||
|
||||
const itemsToDelete: Model[] = onDelete.carryForward.itemsToDelete;
|
||||
|
||||
for (const item of itemsToDelete) {
|
||||
const incidentId: ObjectID | undefined = item.incidentId;
|
||||
const projectId: ObjectID | undefined = item.projectId;
|
||||
const userId: ObjectID | undefined = item.userId;
|
||||
const incidentRoleId: ObjectID | undefined = item.incidentRoleId;
|
||||
|
||||
if (incidentId && userId && projectId) {
|
||||
const user: User | null = await UserService.findOneById({
|
||||
id: userId,
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
let roleName: string = "Member";
|
||||
if (incidentRoleId) {
|
||||
const role: IncidentRole | null =
|
||||
await IncidentRoleService.findOneById({
|
||||
id: incidentRoleId,
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
if (role && role.name) {
|
||||
roleName = role.name;
|
||||
}
|
||||
}
|
||||
|
||||
const incidentNumber: number | null =
|
||||
await IncidentService.getIncidentNumber({
|
||||
incidentId: incidentId,
|
||||
});
|
||||
|
||||
if (user && user.name) {
|
||||
await IncidentFeedService.createIncidentFeedItem({
|
||||
incidentId: incidentId,
|
||||
projectId: projectId,
|
||||
incidentFeedEventType: IncidentFeedEventType.IncidentMemberRemoved,
|
||||
displayColor: Red500,
|
||||
feedInfoInMarkdown: `👤 Removed **${user.name.toString()}** (${user.email?.toString()}) as **${roleName}** from [Incident ${incidentNumber}](${(await IncidentService.getIncidentLinkInDashboard(projectId!, incidentId!)).toString()}).`,
|
||||
userId: deleteByUserId || undefined,
|
||||
workspaceNotification: {
|
||||
sendWorkspaceNotification: true,
|
||||
notifyUserId: userId || undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return onDelete;
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public override async onCreateSuccess(
|
||||
onCreate: OnCreate<Model>,
|
||||
createdItem: Model,
|
||||
): Promise<Model> {
|
||||
const incidentId: ObjectID | undefined = createdItem.incidentId;
|
||||
const projectId: ObjectID | undefined = createdItem.projectId;
|
||||
const userId: ObjectID | undefined = createdItem.userId;
|
||||
const incidentRoleId: ObjectID | undefined = createdItem.incidentRoleId;
|
||||
const createdByUserId: ObjectID | undefined =
|
||||
createdItem.createdByUserId || onCreate.createBy.props.userId;
|
||||
|
||||
if (incidentId && userId && projectId) {
|
||||
let roleName: string = "Member";
|
||||
if (incidentRoleId) {
|
||||
const role: IncidentRole | null = await IncidentRoleService.findOneById(
|
||||
{
|
||||
id: incidentRoleId,
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (role && role.name) {
|
||||
roleName = role.name;
|
||||
}
|
||||
}
|
||||
|
||||
const incidentNumber: number | null =
|
||||
await IncidentService.getIncidentNumber({
|
||||
incidentId: incidentId,
|
||||
});
|
||||
|
||||
if (userId) {
|
||||
await IncidentFeedService.createIncidentFeedItem({
|
||||
incidentId: incidentId,
|
||||
projectId: projectId,
|
||||
incidentFeedEventType: IncidentFeedEventType.IncidentMemberAdded,
|
||||
displayColor: Gray500,
|
||||
feedInfoInMarkdown: `👤 Added **${await UserService.getUserMarkdownString(
|
||||
{
|
||||
userId: userId,
|
||||
projectId: projectId,
|
||||
},
|
||||
)}** as **${roleName}** to [Incident ${incidentNumber}](${(await IncidentService.getIncidentLinkInDashboard(projectId!, incidentId!)).toString()}).`,
|
||||
userId: createdByUserId || undefined,
|
||||
workspaceNotification: {
|
||||
sendWorkspaceNotification: true,
|
||||
notifyUserId: userId || undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// get notification rule where inviteOwners is true.
|
||||
const notificationRules: Array<WorkspaceNotificationRule> =
|
||||
await WorkspaceNotificationRuleService.getNotificationRulesWhereInviteOwnersIsTrue(
|
||||
{
|
||||
projectId: projectId!,
|
||||
notificationFor: {
|
||||
incidentId: incidentId,
|
||||
},
|
||||
notificationRuleEventType: NotificationRuleEventType.Incident,
|
||||
},
|
||||
);
|
||||
|
||||
WorkspaceNotificationRuleService.inviteUsersBasedOnRulesAndWorkspaceChannels(
|
||||
{
|
||||
notificationRules: notificationRules,
|
||||
projectId: projectId!,
|
||||
workspaceChannels: await IncidentService.getWorkspaceChannelForIncident(
|
||||
{
|
||||
incidentId: incidentId!,
|
||||
},
|
||||
),
|
||||
userIds: [userId!],
|
||||
},
|
||||
).catch((error: Error) => {
|
||||
logger.error(error);
|
||||
});
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
|
||||
@@ -34,6 +34,7 @@ const templateDashboardLinkVariableMap: Partial<
|
||||
[WhatsAppTemplateIds.IncidentNotePostedOwnerNotification]: "incident_link",
|
||||
[WhatsAppTemplateIds.IncidentStateChangedOwnerNotification]: "incident_link",
|
||||
[WhatsAppTemplateIds.IncidentOwnerAddedNotification]: "incident_link",
|
||||
[WhatsAppTemplateIds.IncidentMemberAddedNotification]: "incident_link",
|
||||
[WhatsAppTemplateIds.MonitorOwnerAddedNotification]: "monitor_link",
|
||||
[WhatsAppTemplateIds.MonitorCreatedOwnerNotification]: "monitor_link",
|
||||
[WhatsAppTemplateIds.MonitorStatusChangedOwnerNotification]: "monitor_link",
|
||||
@@ -75,6 +76,8 @@ const templateIdByEventType: Record<
|
||||
WhatsAppTemplateIds.IncidentStateChangedOwnerNotification,
|
||||
[NotificationSettingEventType.SEND_INCIDENT_OWNER_ADDED_NOTIFICATION]:
|
||||
WhatsAppTemplateIds.IncidentOwnerAddedNotification,
|
||||
[NotificationSettingEventType.SEND_INCIDENT_MEMBER_ADDED_NOTIFICATION]:
|
||||
WhatsAppTemplateIds.IncidentMemberAddedNotification,
|
||||
[NotificationSettingEventType.SEND_ALERT_CREATED_OWNER_NOTIFICATION]:
|
||||
WhatsAppTemplateIds.AlertCreatedOwnerNotification,
|
||||
[NotificationSettingEventType.SEND_ALERT_NOTE_POSTED_OWNER_NOTIFICATION]:
|
||||
|
||||
@@ -36,6 +36,7 @@ enum EmailTemplateType {
|
||||
IncidentOwnerStateChanged = "IncidentOwnerStateChanged.hbs",
|
||||
IncidentOwnerNotePosted = "IncidentOwnerNotePosted.hbs",
|
||||
IncidentOwnerResourceCreated = "IncidentOwnerResourceCreated.hbs",
|
||||
IncidentMemberAdded = "IncidentMemberAdded.hbs",
|
||||
|
||||
AlertOwnerAdded = "AlertOwnerAdded.hbs",
|
||||
AlertOwnerStateChanged = "AlertOwnerStateChanged.hbs",
|
||||
|
||||
@@ -4,6 +4,7 @@ enum NotificationSettingEventType {
|
||||
SEND_INCIDENT_NOTE_POSTED_OWNER_NOTIFICATION = "Send incident note posted notification when I am the owner of the incident",
|
||||
SEND_INCIDENT_STATE_CHANGED_OWNER_NOTIFICATION = "Send incident state changed notification when I am the owner of the incident",
|
||||
SEND_INCIDENT_OWNER_ADDED_NOTIFICATION = "Send notification when I am added as a owner to the incident",
|
||||
SEND_INCIDENT_MEMBER_ADDED_NOTIFICATION = "Send notification when I am assigned to an incident as a member",
|
||||
|
||||
// Alerts
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ type TemplateIdsMap = {
|
||||
readonly IncidentNotePostedOwnerNotification: "oneuptime_incident_note_posted_owner_notification";
|
||||
readonly IncidentStateChangedOwnerNotification: "oneuptime_incident_state_change_owner_notification";
|
||||
readonly IncidentOwnerAddedNotification: "oneuptime_incident_owner_added_notification";
|
||||
readonly IncidentMemberAddedNotification: "oneuptime_incident_member_added_notification";
|
||||
readonly AlertCreatedOwnerNotification: "oneuptime_alert_created_owner_notification";
|
||||
readonly AlertNotePostedOwnerNotification: "oneuptime_alert_note_posted_owner_notification";
|
||||
readonly AlertStateChangedOwnerNotification: "oneuptime_alert_state_changed_owner_notification";
|
||||
@@ -58,6 +59,8 @@ const templateIds: TemplateIdsMap = {
|
||||
IncidentStateChangedOwnerNotification:
|
||||
"oneuptime_incident_state_change_owner_notification",
|
||||
IncidentOwnerAddedNotification: "oneuptime_incident_owner_added_notification",
|
||||
IncidentMemberAddedNotification:
|
||||
"oneuptime_incident_member_added_notification",
|
||||
AlertCreatedOwnerNotification: "oneuptime_alert_created_owner_notification",
|
||||
AlertNotePostedOwnerNotification:
|
||||
"oneuptime_alert_note_posted_owner_notification",
|
||||
@@ -144,6 +147,7 @@ export const WhatsAppTemplateMessages: WhatsAppTemplateMessagesDefinition = {
|
||||
[WhatsAppTemplateIds.IncidentNotePostedOwnerNotification]: `A new note was posted on incident #{{incident_number}} ({{incident_title}}). Review the incident using {{incident_link}} on the OneUptime dashboard for more context.`,
|
||||
[WhatsAppTemplateIds.IncidentStateChangedOwnerNotification]: `Incident #{{incident_number}} ({{incident_title}}) state changed to {{incident_state}}. Track the incident status using {{incident_link}} on the OneUptime dashboard for more context.`,
|
||||
[WhatsAppTemplateIds.IncidentOwnerAddedNotification]: `You have been added as an owner of incident #{{incident_number}} ({{incident_title}}). Manage the incident using {{incident_link}} on the OneUptime dashboard.`,
|
||||
[WhatsAppTemplateIds.IncidentMemberAddedNotification]: `You have been assigned as {{incident_role}} to incident #{{incident_number}} ({{incident_title}}). Manage the incident using {{incident_link}} on the OneUptime dashboard.`,
|
||||
[WhatsAppTemplateIds.AlertCreatedOwnerNotification]: `Alert #{{alert_number}} ({{alert_title}}) has been created for project {{project_name}}. View alert details using {{alert_link}} on the OneUptime dashboard `,
|
||||
[WhatsAppTemplateIds.AlertNotePostedOwnerNotification]: `A new note was posted on alert #{{alert_number}} ({{alert_title}}). Review the alert using {{alert_link}} on the OneUptime dashboard for updates.`,
|
||||
[WhatsAppTemplateIds.AlertStateChangedOwnerNotification]: `Alert #{{alert_number}} ({{alert_title}}) state changed to {{alert_state}}. Track the alert status using {{alert_link}} on the OneUptime dashboard to stay informed.`,
|
||||
@@ -190,6 +194,7 @@ export const WhatsAppTemplateLanguage: Record<WhatsAppTemplateId, string> = {
|
||||
[WhatsAppTemplateIds.IncidentNotePostedOwnerNotification]: "en",
|
||||
[WhatsAppTemplateIds.IncidentStateChangedOwnerNotification]: "en",
|
||||
[WhatsAppTemplateIds.IncidentOwnerAddedNotification]: "en",
|
||||
[WhatsAppTemplateIds.IncidentMemberAddedNotification]: "en",
|
||||
[WhatsAppTemplateIds.AlertCreatedOwnerNotification]: "en",
|
||||
[WhatsAppTemplateIds.AlertNotePostedOwnerNotification]: "en",
|
||||
[WhatsAppTemplateIds.AlertStateChangedOwnerNotification]: "en",
|
||||
|
||||
257
Worker/Jobs/IncidentMembers/SendMemberAddedNotification.ts
Normal file
257
Worker/Jobs/IncidentMembers/SendMemberAddedNotification.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import RunCron from "../../Utils/Cron";
|
||||
import { CallRequestMessage } from "Common/Types/Call/CallRequest";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { EmailEnvelope } from "Common/Types/Email/EmailMessage";
|
||||
import EmailTemplateType from "Common/Types/Email/EmailTemplateType";
|
||||
import NotificationSettingEventType from "Common/Types/NotificationSetting/NotificationSettingEventType";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import { SMSMessage } from "Common/Types/SMS/SMS";
|
||||
import PushNotificationMessage from "Common/Types/PushNotification/PushNotificationMessage";
|
||||
import { EVERY_MINUTE } from "Common/Utils/CronTime";
|
||||
import IncidentMemberService from "Common/Server/Services/IncidentMemberService";
|
||||
import IncidentService from "Common/Server/Services/IncidentService";
|
||||
import IncidentRoleService from "Common/Server/Services/IncidentRoleService";
|
||||
import UserNotificationSettingService from "Common/Server/Services/UserNotificationSettingService";
|
||||
import PushNotificationUtil from "Common/Server/Utils/PushNotificationUtil";
|
||||
import { createWhatsAppMessageFromTemplate } from "Common/Server/Utils/WhatsAppTemplateUtil";
|
||||
import Markdown, { MarkdownContentType } from "Common/Server/Types/Markdown";
|
||||
import Incident from "Common/Models/DatabaseModels/Incident";
|
||||
import IncidentMember from "Common/Models/DatabaseModels/IncidentMember";
|
||||
import IncidentRole from "Common/Models/DatabaseModels/IncidentRole";
|
||||
import Monitor from "Common/Models/DatabaseModels/Monitor";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import { WhatsAppMessagePayload } from "Common/Types/WhatsApp/WhatsAppMessage";
|
||||
|
||||
RunCron(
|
||||
"IncidentMember:SendMemberAddedEmail",
|
||||
{ schedule: EVERY_MINUTE, runOnStartup: false },
|
||||
async () => {
|
||||
const incidentMembers: Array<IncidentMember> =
|
||||
await IncidentMemberService.findAllBy({
|
||||
query: {
|
||||
isMemberNotified: false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
skip: 0,
|
||||
select: {
|
||||
_id: true,
|
||||
incidentId: true,
|
||||
userId: true,
|
||||
incidentRoleId: true,
|
||||
user: {
|
||||
email: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const incidentMembersMap: Dictionary<
|
||||
Array<{ user: User; roleName: string }>
|
||||
> = {};
|
||||
|
||||
for (const incidentMember of incidentMembers) {
|
||||
const incidentId: ObjectID = incidentMember.incidentId!;
|
||||
const user: User = incidentMember.user!;
|
||||
const incidentRoleId: ObjectID | undefined = incidentMember.incidentRoleId;
|
||||
|
||||
let roleName: string = "Member";
|
||||
if (incidentRoleId) {
|
||||
const role: IncidentRole | null = await IncidentRoleService.findOneById(
|
||||
{
|
||||
id: incidentRoleId,
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (role && role.name) {
|
||||
roleName = role.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (incidentMembersMap[incidentId.toString()] === undefined) {
|
||||
incidentMembersMap[incidentId.toString()] = [];
|
||||
}
|
||||
|
||||
(
|
||||
incidentMembersMap[incidentId.toString()] as Array<{
|
||||
user: User;
|
||||
roleName: string;
|
||||
}>
|
||||
).push({ user, roleName });
|
||||
|
||||
// mark this as notified.
|
||||
await IncidentMemberService.updateOneById({
|
||||
id: incidentMember.id!,
|
||||
data: {
|
||||
isMemberNotified: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// send email to all of these users.
|
||||
|
||||
for (const incidentId in incidentMembersMap) {
|
||||
if (!incidentMembersMap[incidentId]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
incidentMembersMap[incidentId] as Array<{
|
||||
user: User;
|
||||
roleName: string;
|
||||
}>
|
||||
).length === 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const members: Array<{ user: User; roleName: string }> =
|
||||
incidentMembersMap[incidentId] as Array<{
|
||||
user: User;
|
||||
roleName: string;
|
||||
}>;
|
||||
|
||||
// get incident details
|
||||
const incident: Incident | null = await IncidentService.findOneById({
|
||||
id: new ObjectID(incidentId),
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
|
||||
select: {
|
||||
_id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
projectId: true,
|
||||
project: {
|
||||
name: true,
|
||||
},
|
||||
currentIncidentState: {
|
||||
name: true,
|
||||
},
|
||||
incidentSeverity: {
|
||||
name: true,
|
||||
},
|
||||
monitors: {
|
||||
name: true,
|
||||
},
|
||||
incidentNumber: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!incident) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const incidentNumber: string = incident.incidentNumber
|
||||
? `#${incident.incidentNumber}`
|
||||
: "";
|
||||
|
||||
for (const member of members) {
|
||||
const user: User = member.user;
|
||||
const roleName: string = member.roleName;
|
||||
|
||||
const vars: Dictionary<string> = {
|
||||
incidentTitle: incident.title!,
|
||||
incidentNumber: incidentNumber,
|
||||
projectName: incident.project!.name!,
|
||||
currentState: incident.currentIncidentState!.name!,
|
||||
incidentRole: roleName,
|
||||
incidentDescription: await Markdown.convertToHTML(
|
||||
incident.description! || "",
|
||||
MarkdownContentType.Email,
|
||||
),
|
||||
resourcesAffected:
|
||||
incident
|
||||
.monitors!.map((monitor: Monitor) => {
|
||||
return monitor.name!;
|
||||
})
|
||||
.join(", ") || "None",
|
||||
incidentSeverity: incident.incidentSeverity!.name!,
|
||||
incidentViewLink: (
|
||||
await IncidentService.getIncidentLinkInDashboard(
|
||||
incident.projectId!,
|
||||
incident.id!,
|
||||
)
|
||||
).toString(),
|
||||
};
|
||||
|
||||
const incidentIdentifier: string =
|
||||
incident.incidentNumber !== undefined
|
||||
? `#${incident.incidentNumber} (${incident.title})`
|
||||
: incident.title!;
|
||||
|
||||
const emailMessage: EmailEnvelope = {
|
||||
templateType: EmailTemplateType.IncidentMemberAdded,
|
||||
vars: vars,
|
||||
subject: `You have been assigned as ${roleName} to Incident ${incidentNumber} - ${incident.title}`,
|
||||
};
|
||||
|
||||
const sms: SMSMessage = {
|
||||
message: `This is a message from OneUptime. You have been assigned as ${roleName} to the incident ${incidentIdentifier}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
|
||||
};
|
||||
|
||||
const callMessage: CallRequestMessage = {
|
||||
data: [
|
||||
{
|
||||
sayMessage: `This is a message from OneUptime. You have been assigned as ${roleName} to the incident ${incidentIdentifier}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good bye.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const pushMessage: PushNotificationMessage =
|
||||
PushNotificationUtil.createGenericNotification({
|
||||
title: `Assigned as ${roleName} to Incident ${incidentNumber}`,
|
||||
body: `You have been assigned as ${roleName} to the incident ${incidentIdentifier}. Click to view details.`,
|
||||
clickAction: (
|
||||
await IncidentService.getIncidentLinkInDashboard(
|
||||
incident.projectId!,
|
||||
incident.id!,
|
||||
)
|
||||
).toString(),
|
||||
tag: "incident-member-added",
|
||||
requireInteraction: false,
|
||||
});
|
||||
|
||||
const eventType: NotificationSettingEventType =
|
||||
NotificationSettingEventType.SEND_INCIDENT_MEMBER_ADDED_NOTIFICATION;
|
||||
|
||||
const whatsAppMessage: WhatsAppMessagePayload =
|
||||
createWhatsAppMessageFromTemplate({
|
||||
eventType,
|
||||
templateVariables: {
|
||||
incident_title: incident.title!,
|
||||
incident_number:
|
||||
incident.incidentNumber !== undefined
|
||||
? incident.incidentNumber.toString()
|
||||
: "",
|
||||
incident_role: roleName,
|
||||
incident_link: vars["incidentViewLink"] || "",
|
||||
},
|
||||
});
|
||||
|
||||
await UserNotificationSettingService.sendUserNotification({
|
||||
userId: user.id!,
|
||||
projectId: incident.projectId!,
|
||||
emailEnvelope: emailMessage,
|
||||
smsMessage: sms,
|
||||
callRequestMessage: callMessage,
|
||||
pushNotificationMessage: pushMessage,
|
||||
whatsAppMessage,
|
||||
incidentId: incident.id!,
|
||||
eventType,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -13,6 +13,9 @@ import "./Jobs/IncidentOwners/SendNotePostedNotification";
|
||||
import "./Jobs/IncidentOwners/SendOwnerAddedNotification";
|
||||
import "./Jobs/IncidentOwners/SendStateChangeNotification";
|
||||
|
||||
// Incident Members
|
||||
import "./Jobs/IncidentMembers/SendMemberAddedNotification";
|
||||
|
||||
// Monitor Jobs.
|
||||
import "./Jobs/Monitor/KeepCurrentStateConsistent";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user