@oneuptime/common 7.0.4007 → 7.0.4019
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/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +243 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +46 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +237 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +210 -0
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +392 -14
- package/Server/Services/UserNotificationSettingService.ts +114 -237
- package/Server/Services/UserService.ts +21 -0
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +2 -2
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +3 -3
- package/Types/Email/EmailTemplateType.ts +6 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +7 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +170 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +40 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +170 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +152 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +266 -10
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +35 -202
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +17 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +2 -2
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +3 -3
- package/build/dist/Types/Email/EmailTemplateType.js +5 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +6 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,9 +1,219 @@
|
|
|
1
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
2
|
+
import ObjectID from "../../Types/ObjectID";
|
|
3
|
+
import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
1
4
|
import DatabaseService from "./DatabaseService";
|
|
2
5
|
import Model from "Common/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser";
|
|
6
|
+
import Dictionary from "../../Types/Dictionary";
|
|
7
|
+
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
8
|
+
import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
9
|
+
import { EmailEnvelope } from "../../Types/Email/EmailMessage";
|
|
10
|
+
import { SMSMessage } from "../../Types/SMS/SMS";
|
|
11
|
+
import UserNotificationSettingService from "./UserNotificationSettingService";
|
|
12
|
+
import NotificationSettingEventType from "../../Types/NotificationSetting/NotificationSettingEventType";
|
|
13
|
+
import { CallRequestMessage } from "../../Types/Call/CallRequest";
|
|
14
|
+
import DeleteBy from "../Types/Database/DeleteBy";
|
|
15
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
3
16
|
|
|
4
17
|
export class Service extends DatabaseService<Model> {
|
|
5
18
|
public constructor() {
|
|
6
19
|
super(Model);
|
|
7
20
|
}
|
|
21
|
+
|
|
22
|
+
protected override async onCreateSuccess(
|
|
23
|
+
_onCreate: OnCreate<Model>,
|
|
24
|
+
createdItem: Model,
|
|
25
|
+
): Promise<Model> {
|
|
26
|
+
const createdItemId: ObjectID = createdItem.id!;
|
|
27
|
+
|
|
28
|
+
if (!createdItemId) {
|
|
29
|
+
throw new BadDataException("Created item does not have an ID");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const createdModel: Model | null = await this.findOneById({
|
|
33
|
+
id: createdItemId,
|
|
34
|
+
select: {
|
|
35
|
+
projectId: true,
|
|
36
|
+
user: {
|
|
37
|
+
timezone: true,
|
|
38
|
+
_id: true,
|
|
39
|
+
},
|
|
40
|
+
onCallDutyPolicyEscalationRule: {
|
|
41
|
+
name: true,
|
|
42
|
+
_id: true,
|
|
43
|
+
order: true,
|
|
44
|
+
},
|
|
45
|
+
onCallDutyPolicy: {
|
|
46
|
+
name: true,
|
|
47
|
+
_id: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
props: {
|
|
51
|
+
isRoot: true,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// send notification to the new current user.
|
|
56
|
+
|
|
57
|
+
const sendEmailToUserId: ObjectID | undefined | null =
|
|
58
|
+
createdModel?.user?.id;
|
|
59
|
+
|
|
60
|
+
if (!sendEmailToUserId) {
|
|
61
|
+
return createdItem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!createdModel) {
|
|
65
|
+
return createdItem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const vars: Dictionary<string> = {
|
|
69
|
+
onCallPolicyName:
|
|
70
|
+
createdModel.onCallDutyPolicy?.name || "No name provided",
|
|
71
|
+
escalationRuleName:
|
|
72
|
+
createdModel.onCallDutyPolicyEscalationRule?.name || "No name provided",
|
|
73
|
+
escalationRuleOrder:
|
|
74
|
+
createdModel.onCallDutyPolicyEscalationRule?.order?.toString() ||
|
|
75
|
+
"No order provided",
|
|
76
|
+
reason: "You have been added to the on-call duty policy escalation rule.",
|
|
77
|
+
onCallPolicyViewLink: (
|
|
78
|
+
await OnCallDutyPolicyService.getOnCallPolicyLinkInDashboard(
|
|
79
|
+
createdModel!.projectId!,
|
|
80
|
+
createdModel.onCallDutyPolicy!.id!,
|
|
81
|
+
)
|
|
82
|
+
).toString(),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// current user changed, send alert the new current user.
|
|
86
|
+
const emailMessage: EmailEnvelope = {
|
|
87
|
+
templateType: EmailTemplateType.UserAddedToOnCallPolicy,
|
|
88
|
+
vars: vars,
|
|
89
|
+
subject: `You have been added to the on-call duty policy ${createdModel.onCallDutyPolicy?.name}`,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const sms: SMSMessage = {
|
|
93
|
+
message: `This is a message from OneUptime. You have been added to the on-call duty policy ${createdModel.onCallDutyPolicy?.name} for escalation rule ${createdModel.onCallDutyPolicyEscalationRule?.name} with order ${createdModel.onCallDutyPolicyEscalationRule?.order}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const callMessage: CallRequestMessage = {
|
|
97
|
+
data: [
|
|
98
|
+
{
|
|
99
|
+
sayMessage: `This is a message from OneUptime. You have been added to the on-call duty policy ${createdModel.onCallDutyPolicy?.name} for escalation rule ${createdModel.onCallDutyPolicyEscalationRule?.name} with order ${createdModel.onCallDutyPolicyEscalationRule?.order}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good Bye`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
await UserNotificationSettingService.sendUserNotification({
|
|
105
|
+
userId: sendEmailToUserId,
|
|
106
|
+
projectId: createdModel!.projectId!,
|
|
107
|
+
emailEnvelope: emailMessage,
|
|
108
|
+
smsMessage: sms,
|
|
109
|
+
callRequestMessage: callMessage,
|
|
110
|
+
eventType:
|
|
111
|
+
NotificationSettingEventType.SEND_WHEN_USER_IS_ADDED_TO_ON_CALL_POLICY,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return createdItem;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected override async onBeforeDelete(
|
|
118
|
+
deleteBy: DeleteBy<Model>,
|
|
119
|
+
): Promise<OnDelete<Model>> {
|
|
120
|
+
const itemsToFetchBeforeDelete: Array<Model> = await this.findBy({
|
|
121
|
+
query: deleteBy.query,
|
|
122
|
+
props: {
|
|
123
|
+
isRoot: true,
|
|
124
|
+
},
|
|
125
|
+
select: {
|
|
126
|
+
projectId: true,
|
|
127
|
+
user: {
|
|
128
|
+
timezone: true,
|
|
129
|
+
_id: true,
|
|
130
|
+
},
|
|
131
|
+
onCallDutyPolicyEscalationRule: {
|
|
132
|
+
name: true,
|
|
133
|
+
_id: true,
|
|
134
|
+
order: true,
|
|
135
|
+
},
|
|
136
|
+
onCallDutyPolicy: {
|
|
137
|
+
name: true,
|
|
138
|
+
_id: true,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
limit: LIMIT_PER_PROJECT,
|
|
142
|
+
skip: 0,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
deleteBy,
|
|
147
|
+
carryForward: {
|
|
148
|
+
deletedItems: itemsToFetchBeforeDelete,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
protected override async onDeleteSuccess(
|
|
154
|
+
onDelete: OnDelete<Model>,
|
|
155
|
+
_itemIdsBeforeDelete: Array<ObjectID>,
|
|
156
|
+
): Promise<OnDelete<Model>> {
|
|
157
|
+
const deletedItems: Array<Model> = onDelete.carryForward.deletedItems;
|
|
158
|
+
|
|
159
|
+
for (const deletedItem of deletedItems) {
|
|
160
|
+
const sendEmailToUserId: ObjectID | undefined | null =
|
|
161
|
+
deletedItem.user?.id;
|
|
162
|
+
|
|
163
|
+
if (!sendEmailToUserId) {
|
|
164
|
+
return onDelete;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const vars: Dictionary<string> = {
|
|
168
|
+
onCallPolicyName:
|
|
169
|
+
deletedItem.onCallDutyPolicy?.name || "No name provided",
|
|
170
|
+
escalationRuleName:
|
|
171
|
+
deletedItem.onCallDutyPolicyEscalationRule?.name ||
|
|
172
|
+
"No name provided",
|
|
173
|
+
escalationRuleOrder:
|
|
174
|
+
deletedItem.onCallDutyPolicyEscalationRule?.order?.toString() ||
|
|
175
|
+
"No order provided",
|
|
176
|
+
reason:
|
|
177
|
+
"You have been removed from the on-call duty policy escalation rule.",
|
|
178
|
+
onCallPolicyViewLink: (
|
|
179
|
+
await OnCallDutyPolicyService.getOnCallPolicyLinkInDashboard(
|
|
180
|
+
deletedItem!.projectId!,
|
|
181
|
+
deletedItem.onCallDutyPolicy!.id!,
|
|
182
|
+
)
|
|
183
|
+
).toString(),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// current user changed, send alert the new current user.
|
|
187
|
+
const emailMessage: EmailEnvelope = {
|
|
188
|
+
templateType: EmailTemplateType.UserRemovedFromOnCallPolicy,
|
|
189
|
+
vars: vars,
|
|
190
|
+
subject: `You have been removed from the on-call duty policy ${deletedItem.onCallDutyPolicy?.name}`,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const sms: SMSMessage = {
|
|
194
|
+
message: `This is a message from OneUptime. You have been removed from the on-call duty policy ${deletedItem.onCallDutyPolicy?.name} for escalation rule ${deletedItem.onCallDutyPolicyEscalationRule?.name} with order ${deletedItem.onCallDutyPolicyEscalationRule?.order}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const callMessage: CallRequestMessage = {
|
|
198
|
+
data: [
|
|
199
|
+
{
|
|
200
|
+
sayMessage: `This is a message from OneUptime. You have been removed from the on-call duty policy ${deletedItem.onCallDutyPolicy?.name} for escalation rule ${deletedItem.onCallDutyPolicyEscalationRule?.name} with order ${deletedItem.onCallDutyPolicyEscalationRule?.order}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good Bye`,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
UserNotificationSettingService.sendUserNotification({
|
|
206
|
+
userId: sendEmailToUserId,
|
|
207
|
+
projectId: deletedItem!.projectId!,
|
|
208
|
+
emailEnvelope: emailMessage,
|
|
209
|
+
smsMessage: sms,
|
|
210
|
+
callRequestMessage: callMessage,
|
|
211
|
+
eventType:
|
|
212
|
+
NotificationSettingEventType.SEND_WHEN_USER_IS_REMOVED_FROM_ON_CALL_POLICY,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return onDelete;
|
|
217
|
+
}
|
|
8
218
|
}
|
|
9
219
|
export default new Service();
|
|
@@ -7,22 +7,37 @@ import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
|
7
7
|
import OneUptimeDate from "../../Types/Date";
|
|
8
8
|
import ObjectID from "../../Types/ObjectID";
|
|
9
9
|
import LayerUtil, { LayerProps } from "../../Types/OnCallDutyPolicy/Layer";
|
|
10
|
-
import Model from "Common/Models/DatabaseModels/OnCallDutyPolicySchedule";
|
|
11
10
|
import OnCallDutyPolicyScheduleLayer from "Common/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer";
|
|
12
11
|
import OnCallDutyPolicyScheduleLayerUser from "Common/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser";
|
|
13
12
|
import User from "Common/Models/DatabaseModels/User";
|
|
14
13
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import OnCallDutyPolicySchedule from "Common/Models/DatabaseModels/OnCallDutyPolicySchedule";
|
|
15
|
+
import OnCallDutyPolicyEscalationRuleSchedule from "../../Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule";
|
|
16
|
+
import OnCallDutyPolicyEscalationRuleScheduleService from "./OnCallDutyPolicyEscalationRuleScheduleService";
|
|
17
|
+
import Dictionary from "../../Types/Dictionary";
|
|
18
|
+
import { EmailEnvelope } from "../../Types/Email/EmailMessage";
|
|
19
|
+
import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
20
|
+
import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
21
|
+
import OnCallDutyPolicyEscalationRule from "../../Models/DatabaseModels/OnCallDutyPolicyEscalationRule";
|
|
22
|
+
import UserService from "./UserService";
|
|
23
|
+
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
24
|
+
import { SMSMessage } from "../../Types/SMS/SMS";
|
|
25
|
+
import { CallRequestMessage } from "../../Types/Call/CallRequest";
|
|
26
|
+
import UserNotificationSettingService from "./UserNotificationSettingService";
|
|
27
|
+
import NotificationSettingEventType from "../../Types/NotificationSetting/NotificationSettingEventType";
|
|
28
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
29
|
+
import Timezone from "../../Types/Timezone";
|
|
30
|
+
|
|
31
|
+
export class Service extends DatabaseService<OnCallDutyPolicySchedule> {
|
|
17
32
|
public constructor() {
|
|
18
|
-
super(
|
|
33
|
+
super(OnCallDutyPolicySchedule);
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
public async getOnCallSchedulesWhereUserIsOnCallDuty(data: {
|
|
22
37
|
projectId: ObjectID;
|
|
23
38
|
userId: ObjectID;
|
|
24
|
-
}): Promise<Array<
|
|
25
|
-
const schedules: Array<
|
|
39
|
+
}): Promise<Array<OnCallDutyPolicySchedule>> {
|
|
40
|
+
const schedules: Array<OnCallDutyPolicySchedule> = await this.findBy({
|
|
26
41
|
query: {
|
|
27
42
|
projectId: data.projectId,
|
|
28
43
|
currentUserIdOnRoster: data.userId,
|
|
@@ -41,6 +56,316 @@ export class Service extends DatabaseService<Model> {
|
|
|
41
56
|
return schedules;
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
private async sendNotificationToUserOnScheduleHandoff(data: {
|
|
60
|
+
scheduleId: ObjectID;
|
|
61
|
+
previousInformation: {
|
|
62
|
+
currentUserIdOnRoster: ObjectID | null;
|
|
63
|
+
rosterHandoffAt: Date | null;
|
|
64
|
+
nextUserIdOnRoster: ObjectID | null;
|
|
65
|
+
nextHandOffTimeAt: Date | null;
|
|
66
|
+
rosterStartAt: Date | null;
|
|
67
|
+
nextRosterStartAt: Date | null;
|
|
68
|
+
};
|
|
69
|
+
newInformation: {
|
|
70
|
+
currentUserIdOnRoster: ObjectID | null;
|
|
71
|
+
rosterHandoffAt: Date | null;
|
|
72
|
+
nextUserIdOnRoster: ObjectID | null;
|
|
73
|
+
nextHandOffTimeAt: Date | null;
|
|
74
|
+
rosterStartAt: Date | null;
|
|
75
|
+
nextRosterStartAt: Date | null;
|
|
76
|
+
};
|
|
77
|
+
}): Promise<void> {
|
|
78
|
+
// Before we send any notification, we need to check if this schedule is attached to any on-call policy.
|
|
79
|
+
|
|
80
|
+
const escalationRulesAttachedToSchedule: Array<OnCallDutyPolicyEscalationRuleSchedule> =
|
|
81
|
+
await OnCallDutyPolicyEscalationRuleScheduleService.findBy({
|
|
82
|
+
query: {
|
|
83
|
+
onCallDutyPolicyScheduleId: data.scheduleId,
|
|
84
|
+
},
|
|
85
|
+
select: {
|
|
86
|
+
projectId: true,
|
|
87
|
+
_id: true,
|
|
88
|
+
onCallDutyPolicy: {
|
|
89
|
+
name: true,
|
|
90
|
+
_id: true,
|
|
91
|
+
},
|
|
92
|
+
onCallDutyPolicyEscalationRule: {
|
|
93
|
+
name: true,
|
|
94
|
+
_id: true,
|
|
95
|
+
order: true,
|
|
96
|
+
},
|
|
97
|
+
onCallDutyPolicySchedule: {
|
|
98
|
+
name: true,
|
|
99
|
+
_id: true,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
props: {
|
|
103
|
+
isRoot: true,
|
|
104
|
+
},
|
|
105
|
+
limit: LIMIT_PER_PROJECT,
|
|
106
|
+
skip: 0,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (escalationRulesAttachedToSchedule.length === 0) {
|
|
110
|
+
// do nothing.
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const escalationRule of escalationRulesAttachedToSchedule) {
|
|
115
|
+
const projectId: ObjectID = escalationRule.projectId!;
|
|
116
|
+
|
|
117
|
+
const onCallSchedule: OnCallDutyPolicySchedule | undefined =
|
|
118
|
+
escalationRule.onCallDutyPolicySchedule;
|
|
119
|
+
|
|
120
|
+
if (!onCallSchedule) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const onCallPolicy: OnCallDutyPolicy | undefined =
|
|
125
|
+
escalationRule.onCallDutyPolicy;
|
|
126
|
+
|
|
127
|
+
if (!onCallPolicy) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const onCallDutyPolicyEscalationRule:
|
|
132
|
+
| OnCallDutyPolicyEscalationRule
|
|
133
|
+
| undefined = escalationRule.onCallDutyPolicyEscalationRule;
|
|
134
|
+
|
|
135
|
+
if (!onCallDutyPolicyEscalationRule) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { previousInformation, newInformation } = data;
|
|
140
|
+
|
|
141
|
+
// if there's a change, witht he current user, send notification to the new current user.
|
|
142
|
+
// Send notificiation to the new current user.
|
|
143
|
+
if (
|
|
144
|
+
previousInformation.currentUserIdOnRoster?.toString() !==
|
|
145
|
+
newInformation.currentUserIdOnRoster?.toString() ||
|
|
146
|
+
previousInformation.rosterHandoffAt?.toString() !==
|
|
147
|
+
newInformation.rosterHandoffAt?.toString()
|
|
148
|
+
) {
|
|
149
|
+
if (
|
|
150
|
+
previousInformation.currentUserIdOnRoster?.toString() !==
|
|
151
|
+
newInformation.currentUserIdOnRoster?.toString() &&
|
|
152
|
+
previousInformation.currentUserIdOnRoster?.toString()
|
|
153
|
+
) {
|
|
154
|
+
// the user has changed. Send notifiction to old user that he has been removed.
|
|
155
|
+
|
|
156
|
+
// send notification to the new current user.
|
|
157
|
+
|
|
158
|
+
const sendEmailToUserId: ObjectID =
|
|
159
|
+
previousInformation.currentUserIdOnRoster;
|
|
160
|
+
|
|
161
|
+
const userTimezone: Timezone | null =
|
|
162
|
+
await UserService.getTimezoneForUser(sendEmailToUserId);
|
|
163
|
+
|
|
164
|
+
const vars: Dictionary<string> = {
|
|
165
|
+
onCallPolicyName: onCallPolicy.name || "No name provided",
|
|
166
|
+
escalationRuleName:
|
|
167
|
+
onCallDutyPolicyEscalationRule.name || "No name provided",
|
|
168
|
+
escalationRuleOrder:
|
|
169
|
+
onCallDutyPolicyEscalationRule.order?.toString() || "-",
|
|
170
|
+
reason:
|
|
171
|
+
"Your on-call roster on schedule " +
|
|
172
|
+
onCallSchedule.name +
|
|
173
|
+
" just ended.",
|
|
174
|
+
rosterStartsAt:
|
|
175
|
+
OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
|
|
176
|
+
date: previousInformation.rosterStartAt!,
|
|
177
|
+
timezones: userTimezone ? [userTimezone] : [],
|
|
178
|
+
}),
|
|
179
|
+
rosterEndsAt:
|
|
180
|
+
OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
|
|
181
|
+
date: OneUptimeDate.isInTheFuture(
|
|
182
|
+
previousInformation.rosterHandoffAt!,
|
|
183
|
+
)
|
|
184
|
+
? OneUptimeDate.getCurrentDate()
|
|
185
|
+
: previousInformation.rosterHandoffAt!,
|
|
186
|
+
timezones: userTimezone ? [userTimezone] : [],
|
|
187
|
+
}),
|
|
188
|
+
onCallPolicyViewLink: (
|
|
189
|
+
await OnCallDutyPolicyService.getOnCallPolicyLinkInDashboard(
|
|
190
|
+
projectId,
|
|
191
|
+
onCallPolicy.id!,
|
|
192
|
+
)
|
|
193
|
+
).toString(),
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// current user changed, send alert the new current user.
|
|
197
|
+
const emailMessage: EmailEnvelope = {
|
|
198
|
+
templateType: EmailTemplateType.UserNoLongerActiveOnOnCallRoster,
|
|
199
|
+
vars: vars,
|
|
200
|
+
subject: "You are no longer on-call for " + onCallPolicy.name!,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const sms: SMSMessage = {
|
|
204
|
+
message: `This is a message from OneUptime. You are no longer on-call for ${onCallPolicy.name!} because your on-call roster on schedule ${onCallSchedule.name} just ended. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const callMessage: CallRequestMessage = {
|
|
208
|
+
data: [
|
|
209
|
+
{
|
|
210
|
+
sayMessage: `This is a message from OneUptime. You are no longer on-call for ${onCallPolicy.name!} because your on-call roster on schedule ${onCallSchedule.name} just ended. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good bye.`,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
await UserNotificationSettingService.sendUserNotification({
|
|
216
|
+
userId: sendEmailToUserId,
|
|
217
|
+
projectId: projectId,
|
|
218
|
+
emailEnvelope: emailMessage,
|
|
219
|
+
smsMessage: sms,
|
|
220
|
+
callRequestMessage: callMessage,
|
|
221
|
+
eventType:
|
|
222
|
+
NotificationSettingEventType.SEND_WHEN_USER_IS_NO_LONGER_ACTIVE_ON_ON_CALL_ROSTER,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (newInformation.currentUserIdOnRoster?.toString()) {
|
|
227
|
+
// send email to the new current user.
|
|
228
|
+
const sendEmailToUserId: ObjectID =
|
|
229
|
+
newInformation.currentUserIdOnRoster;
|
|
230
|
+
const userTimezone: Timezone | null =
|
|
231
|
+
await UserService.getTimezoneForUser(sendEmailToUserId);
|
|
232
|
+
|
|
233
|
+
const vars: Dictionary<string> = {
|
|
234
|
+
onCallPolicyName: onCallPolicy.name || "No name provided",
|
|
235
|
+
escalationRuleName:
|
|
236
|
+
onCallDutyPolicyEscalationRule.name || "No name provided",
|
|
237
|
+
escalationRuleOrder:
|
|
238
|
+
onCallDutyPolicyEscalationRule.order?.toString() || "-",
|
|
239
|
+
reason:
|
|
240
|
+
"You are now on-call for the policy " +
|
|
241
|
+
onCallPolicy.name +
|
|
242
|
+
" because your on-call roster on schedule " +
|
|
243
|
+
onCallSchedule.name,
|
|
244
|
+
rosterStartsAt:
|
|
245
|
+
OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
|
|
246
|
+
date: newInformation.rosterStartAt!,
|
|
247
|
+
timezones: userTimezone ? [userTimezone] : [],
|
|
248
|
+
}),
|
|
249
|
+
rosterEndsAt:
|
|
250
|
+
OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
|
|
251
|
+
date: newInformation.rosterHandoffAt!,
|
|
252
|
+
timezones: userTimezone ? [userTimezone] : [],
|
|
253
|
+
}),
|
|
254
|
+
onCallPolicyViewLink: (
|
|
255
|
+
await OnCallDutyPolicyService.getOnCallPolicyLinkInDashboard(
|
|
256
|
+
projectId,
|
|
257
|
+
onCallPolicy.id!,
|
|
258
|
+
)
|
|
259
|
+
).toString(),
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const emailMessage: EmailEnvelope = {
|
|
263
|
+
templateType: EmailTemplateType.UserCurrentlyOnOnCallRoster,
|
|
264
|
+
vars: vars,
|
|
265
|
+
subject: "You are now on-call for " + onCallPolicy.name!,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const sms: SMSMessage = {
|
|
269
|
+
message: `This is a message from OneUptime. You are now on-call for ${onCallPolicy.name!} because you are now on the roster for schedule ${onCallSchedule.name}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const callMessage: CallRequestMessage = {
|
|
273
|
+
data: [
|
|
274
|
+
{
|
|
275
|
+
sayMessage: `This is a message from OneUptime. You are now on-call for ${onCallPolicy.name!} because you are now on the roster for schedule ${onCallSchedule.name}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good bye.`,
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
await UserNotificationSettingService.sendUserNotification({
|
|
281
|
+
userId: sendEmailToUserId,
|
|
282
|
+
projectId: projectId,
|
|
283
|
+
emailEnvelope: emailMessage,
|
|
284
|
+
smsMessage: sms,
|
|
285
|
+
callRequestMessage: callMessage,
|
|
286
|
+
eventType:
|
|
287
|
+
NotificationSettingEventType.SEND_WHEN_USER_IS_ON_CALL_ROSTER,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// send an email to the next user.
|
|
293
|
+
if (
|
|
294
|
+
previousInformation.nextUserIdOnRoster?.toString() !==
|
|
295
|
+
newInformation.nextUserIdOnRoster?.toString() ||
|
|
296
|
+
previousInformation.nextHandOffTimeAt?.toString() !==
|
|
297
|
+
newInformation.nextHandOffTimeAt?.toString() ||
|
|
298
|
+
previousInformation.nextRosterStartAt?.toString() !==
|
|
299
|
+
newInformation.nextRosterStartAt?.toString()
|
|
300
|
+
) {
|
|
301
|
+
if (newInformation.nextUserIdOnRoster?.toString()) {
|
|
302
|
+
// send email to the next user.
|
|
303
|
+
const sendEmailToUserId: ObjectID = newInformation.nextUserIdOnRoster;
|
|
304
|
+
const userTimezone: Timezone | null =
|
|
305
|
+
await UserService.getTimezoneForUser(sendEmailToUserId);
|
|
306
|
+
|
|
307
|
+
const vars: Dictionary<string> = {
|
|
308
|
+
onCallPolicyName: onCallPolicy.name || "No name provided",
|
|
309
|
+
escalationRuleName:
|
|
310
|
+
onCallDutyPolicyEscalationRule.name || "No name provided",
|
|
311
|
+
escalationRuleOrder:
|
|
312
|
+
onCallDutyPolicyEscalationRule.order?.toString() || "-",
|
|
313
|
+
reason:
|
|
314
|
+
"You are next on-call for the policy " +
|
|
315
|
+
onCallPolicy.name +
|
|
316
|
+
" because your on-call roster on schedule " +
|
|
317
|
+
onCallSchedule.name +
|
|
318
|
+
" will start when the next handoff happens.",
|
|
319
|
+
rosterStartsAt:
|
|
320
|
+
OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
|
|
321
|
+
date: newInformation.nextRosterStartAt!,
|
|
322
|
+
timezones: userTimezone ? [userTimezone] : [],
|
|
323
|
+
}),
|
|
324
|
+
rosterEndsAt:
|
|
325
|
+
OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
|
|
326
|
+
date: newInformation.nextHandOffTimeAt!,
|
|
327
|
+
timezones: userTimezone ? [userTimezone] : [],
|
|
328
|
+
}),
|
|
329
|
+
onCallPolicyViewLink: (
|
|
330
|
+
await OnCallDutyPolicyService.getOnCallPolicyLinkInDashboard(
|
|
331
|
+
projectId,
|
|
332
|
+
onCallPolicy.id!,
|
|
333
|
+
)
|
|
334
|
+
).toString(),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const emailMessage: EmailEnvelope = {
|
|
338
|
+
templateType: EmailTemplateType.UserNextOnOnCallRoster,
|
|
339
|
+
vars: vars,
|
|
340
|
+
subject: "You are next on-call for " + onCallPolicy.name!,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const sms: SMSMessage = {
|
|
344
|
+
message: `This is a message from OneUptime. You are next on-call for ${onCallPolicy.name!} because your on-call roster on schedule ${onCallSchedule.name} will start when the next handoff happens. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const callMessage: CallRequestMessage = {
|
|
348
|
+
data: [
|
|
349
|
+
{
|
|
350
|
+
sayMessage: `This is a message from OneUptime. You are next on-call for ${onCallPolicy.name!} because your on-call roster on schedule ${onCallSchedule.name} will start when the next handoff happens. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good bye.`,
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
await UserNotificationSettingService.sendUserNotification({
|
|
356
|
+
userId: sendEmailToUserId,
|
|
357
|
+
projectId: projectId,
|
|
358
|
+
emailEnvelope: emailMessage,
|
|
359
|
+
smsMessage: sms,
|
|
360
|
+
callRequestMessage: callMessage,
|
|
361
|
+
eventType:
|
|
362
|
+
NotificationSettingEventType.SEND_WHEN_USER_IS_NEXT_ON_CALL_ROSTER,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
44
369
|
public async refreshCurrentUserIdAndHandoffTimeInSchedule(
|
|
45
370
|
scheduleId: ObjectID,
|
|
46
371
|
): Promise<{
|
|
@@ -48,8 +373,47 @@ export class Service extends DatabaseService<Model> {
|
|
|
48
373
|
handOffTimeAt: Date | null;
|
|
49
374
|
nextUserId: ObjectID | null;
|
|
50
375
|
nextHandOffTimeAt: Date | null;
|
|
376
|
+
rosterStartAt: Date | null;
|
|
377
|
+
nextRosterStartAt: Date | null;
|
|
51
378
|
}> {
|
|
52
|
-
|
|
379
|
+
// get previoius result.
|
|
380
|
+
const onCallSchedule: OnCallDutyPolicySchedule | null =
|
|
381
|
+
await this.findOneById({
|
|
382
|
+
id: scheduleId,
|
|
383
|
+
select: {
|
|
384
|
+
currentUserIdOnRoster: true,
|
|
385
|
+
rosterHandoffAt: true,
|
|
386
|
+
nextUserIdOnRoster: true,
|
|
387
|
+
rosterNextHandoffAt: true,
|
|
388
|
+
rosterStartAt: true,
|
|
389
|
+
rosterNextStartAt: true,
|
|
390
|
+
},
|
|
391
|
+
props: {
|
|
392
|
+
isRoot: true,
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
if (!onCallSchedule) {
|
|
397
|
+
throw new BadDataException("Schedule not found");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const previousInformation: {
|
|
401
|
+
currentUserIdOnRoster: ObjectID | null;
|
|
402
|
+
rosterHandoffAt: Date | null;
|
|
403
|
+
nextUserIdOnRoster: ObjectID | null;
|
|
404
|
+
nextHandOffTimeAt: Date | null;
|
|
405
|
+
rosterStartAt: Date | null;
|
|
406
|
+
nextRosterStartAt: Date | null;
|
|
407
|
+
} = {
|
|
408
|
+
currentUserIdOnRoster: onCallSchedule.currentUserIdOnRoster || null,
|
|
409
|
+
rosterHandoffAt: onCallSchedule.rosterHandoffAt || null,
|
|
410
|
+
nextUserIdOnRoster: onCallSchedule.nextUserIdOnRoster || null,
|
|
411
|
+
nextHandOffTimeAt: onCallSchedule.rosterNextHandoffAt || null,
|
|
412
|
+
rosterStartAt: onCallSchedule.rosterStartAt || null,
|
|
413
|
+
nextRosterStartAt: onCallSchedule.rosterNextStartAt || null,
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const newInformation: {
|
|
53
417
|
currentUserId: ObjectID | null;
|
|
54
418
|
handOffTimeAt: Date | null;
|
|
55
419
|
nextUserId: ObjectID | null;
|
|
@@ -61,12 +425,12 @@ export class Service extends DatabaseService<Model> {
|
|
|
61
425
|
await this.updateOneById({
|
|
62
426
|
id: scheduleId!,
|
|
63
427
|
data: {
|
|
64
|
-
currentUserIdOnRoster:
|
|
65
|
-
rosterHandoffAt:
|
|
66
|
-
nextUserIdOnRoster:
|
|
67
|
-
rosterNextHandoffAt:
|
|
68
|
-
rosterStartAt:
|
|
69
|
-
rosterNextStartAt:
|
|
428
|
+
currentUserIdOnRoster: newInformation.currentUserId,
|
|
429
|
+
rosterHandoffAt: newInformation.handOffTimeAt,
|
|
430
|
+
nextUserIdOnRoster: newInformation.nextUserId,
|
|
431
|
+
rosterNextHandoffAt: newInformation.nextHandOffTimeAt,
|
|
432
|
+
rosterStartAt: newInformation.rosterStartAt,
|
|
433
|
+
rosterNextStartAt: newInformation.nextRosterStartAt,
|
|
70
434
|
},
|
|
71
435
|
props: {
|
|
72
436
|
isRoot: true,
|
|
@@ -74,7 +438,21 @@ export class Service extends DatabaseService<Model> {
|
|
|
74
438
|
},
|
|
75
439
|
});
|
|
76
440
|
|
|
77
|
-
|
|
441
|
+
// send notification to the users.
|
|
442
|
+
await this.sendNotificationToUserOnScheduleHandoff({
|
|
443
|
+
scheduleId: scheduleId,
|
|
444
|
+
previousInformation: previousInformation,
|
|
445
|
+
newInformation: {
|
|
446
|
+
currentUserIdOnRoster: newInformation.currentUserId,
|
|
447
|
+
rosterHandoffAt: newInformation.handOffTimeAt,
|
|
448
|
+
nextUserIdOnRoster: newInformation.nextUserId,
|
|
449
|
+
nextHandOffTimeAt: newInformation.nextHandOffTimeAt,
|
|
450
|
+
rosterStartAt: newInformation.rosterStartAt,
|
|
451
|
+
nextRosterStartAt: newInformation.nextRosterStartAt,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
return newInformation;
|
|
78
456
|
}
|
|
79
457
|
|
|
80
458
|
public async getCurrrentUserIdAndHandoffTimeInSchedule(
|