mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
264 lines
9.4 KiB
TypeScript
264 lines
9.4 KiB
TypeScript
import DatabaseService from "./DatabaseService";
|
|
import ObjectID from "../../Types/ObjectID";
|
|
import DatabaseConfig from "../DatabaseConfig";
|
|
import URL from "../../Types/API/URL";
|
|
import OnCallDutyPolicyUserOverride from "../../Models/DatabaseModels/OnCallDutyPolicyUserOverride";
|
|
import CreateBy from "../Types/Database/CreateBy";
|
|
import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
import OneUptimeDate from "../../Types/Date";
|
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
import OnCallDutyPolicyFeedService from "./OnCallDutyPolicyFeedService";
|
|
import { OnCallDutyPolicyFeedEventType } from "../../Models/DatabaseModels/OnCallDutyPolicyFeed";
|
|
import { Gray500 } from "../../Types/BrandColors";
|
|
import UserService from "./UserService";
|
|
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
import Timezone from "../../Types/Timezone";
|
|
import DeleteBy from "../Types/Database/DeleteBy";
|
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
|
|
export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
|
|
public constructor() {
|
|
super(OnCallDutyPolicyUserOverride);
|
|
}
|
|
|
|
@CaptureSpan()
|
|
protected override async onBeforeCreate(
|
|
createBy: CreateBy<OnCallDutyPolicyUserOverride>,
|
|
): Promise<OnCreate<OnCallDutyPolicyUserOverride>> {
|
|
if (!createBy.data.startsAt || !createBy.data.endsAt) {
|
|
throw new BadDataException("Start time and end time are required");
|
|
}
|
|
|
|
// make sure start time is before end time
|
|
if (OneUptimeDate.isAfter(createBy.data.startsAt, createBy.data.endsAt)) {
|
|
throw new BadDataException("Start time must be before end time");
|
|
}
|
|
|
|
// make sure overrideUser and routealertsToUser are not the same
|
|
const overrideUserId: ObjectID | undefined | null =
|
|
createBy.data.overrideUserId || createBy.data.overrideUser?.id;
|
|
|
|
if (!overrideUserId) {
|
|
throw new BadDataException("Override user is required");
|
|
}
|
|
|
|
const routeAlertsToUserId: ObjectID | undefined | null =
|
|
createBy.data.routeAlertsToUserId || createBy.data.routeAlertsToUser?.id;
|
|
|
|
if (!routeAlertsToUserId) {
|
|
throw new BadDataException("Route alerts to user is required");
|
|
}
|
|
|
|
if (overrideUserId.toString() === routeAlertsToUserId.toString()) {
|
|
throw new BadDataException(
|
|
"Override user and route alerts to user cannot be the same",
|
|
);
|
|
}
|
|
|
|
return {
|
|
createBy,
|
|
carryForward: null,
|
|
};
|
|
}
|
|
|
|
protected override async onCreateSuccess(
|
|
_onCreate: OnCreate<OnCallDutyPolicyUserOverride>,
|
|
createdItem: OnCallDutyPolicyUserOverride,
|
|
): Promise<OnCallDutyPolicyUserOverride> {
|
|
// add to on call feed.
|
|
const onCallDutyPolicyId: ObjectID | undefined | null =
|
|
createdItem.onCallDutyPolicyId || createdItem.onCallDutyPolicy?.id;
|
|
|
|
const projectId: ObjectID | undefined | null =
|
|
createdItem.projectId || createdItem.project?.id;
|
|
|
|
const overrideUserId: ObjectID | undefined | null =
|
|
createdItem.overrideUserId || createdItem.overrideUser?.id;
|
|
|
|
const routeAlertsToUserId: ObjectID | undefined | null =
|
|
createdItem.routeAlertsToUserId || createdItem.routeAlertsToUser?.id;
|
|
|
|
if (
|
|
onCallDutyPolicyId &&
|
|
projectId &&
|
|
overrideUserId &&
|
|
routeAlertsToUserId
|
|
) {
|
|
const onCallPolicyName: string | null =
|
|
await OnCallDutyPolicyService.getOnCallDutyPolicyName({
|
|
onCallDutyPolicyId: onCallDutyPolicyId,
|
|
});
|
|
|
|
const overrideUserTimezone: Timezone | null =
|
|
await UserService.getTimezoneForUser(overrideUserId);
|
|
|
|
const routeAlertsToUserTimezone: Timezone | null =
|
|
await UserService.getTimezoneForUser(routeAlertsToUserId);
|
|
|
|
const timezones: Timezone[] = [];
|
|
if (overrideUserTimezone) {
|
|
timezones.push(overrideUserTimezone);
|
|
}
|
|
|
|
if (routeAlertsToUserTimezone) {
|
|
timezones.push(routeAlertsToUserTimezone);
|
|
}
|
|
|
|
await OnCallDutyPolicyFeedService.createOnCallDutyPolicyFeedItem({
|
|
onCallDutyPolicyId: onCallDutyPolicyId,
|
|
projectId: projectId!,
|
|
onCallDutyPolicyFeedEventType:
|
|
OnCallDutyPolicyFeedEventType.UserOverrideAdded,
|
|
displayColor: Gray500,
|
|
feedInfoInMarkdown: `🔁 Added a User Override Rule for user **${await UserService.getUserMarkdownString(
|
|
{
|
|
userId: overrideUserId,
|
|
projectId: projectId!,
|
|
},
|
|
)}** for the [On-Call Policy ${onCallPolicyName}](${(await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId!, onCallDutyPolicyId!)).toString()}). All alerts will be routed to **${await UserService.getUserMarkdownString(
|
|
{
|
|
userId: routeAlertsToUserId,
|
|
projectId: projectId!,
|
|
},
|
|
)}** from **${OneUptimeDate.getDateAsFormattedStringInMultipleTimezones(
|
|
{
|
|
date: createdItem.startsAt!,
|
|
timezones: timezones,
|
|
},
|
|
)}** to **${OneUptimeDate.getDateAsFormattedStringInMultipleTimezones({
|
|
date: createdItem.endsAt!,
|
|
timezones: timezones,
|
|
})}**. `,
|
|
|
|
userId: createdItem.createdByUserId! || undefined,
|
|
workspaceNotification: {
|
|
sendWorkspaceNotification: true,
|
|
notifyUserId: createdItem.createdByUserId! || undefined,
|
|
},
|
|
});
|
|
}
|
|
|
|
return createdItem;
|
|
}
|
|
|
|
protected override async onBeforeDelete(
|
|
deleteBy: DeleteBy<OnCallDutyPolicyUserOverride>,
|
|
): Promise<OnDelete<OnCallDutyPolicyUserOverride>> {
|
|
const itemsToDelete: OnCallDutyPolicyUserOverride[] = await this.findBy({
|
|
query: deleteBy.query,
|
|
select: {
|
|
onCallDutyPolicyId: true,
|
|
projectId: true,
|
|
overrideUserId: true,
|
|
routeAlertsToUserId: true,
|
|
startsAt: true,
|
|
endsAt: true,
|
|
createdByUserId: true,
|
|
},
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
skip: 0,
|
|
limit: LIMIT_PER_PROJECT,
|
|
});
|
|
|
|
for (const item of itemsToDelete) {
|
|
const onCallDutyPolicyId: ObjectID | undefined | null =
|
|
item.onCallDutyPolicyId || item.onCallDutyPolicy?.id;
|
|
|
|
const projectId: ObjectID | undefined | null =
|
|
item.projectId || item.project?.id;
|
|
|
|
const overrideUserId: ObjectID | undefined | null =
|
|
item.overrideUserId || item.overrideUser?.id;
|
|
|
|
const routeAlertsToUserId: ObjectID | undefined | null =
|
|
item.routeAlertsToUserId || item.routeAlertsToUser?.id;
|
|
|
|
if (
|
|
onCallDutyPolicyId &&
|
|
projectId &&
|
|
overrideUserId &&
|
|
routeAlertsToUserId
|
|
) {
|
|
const onCallPolicyName: string | null =
|
|
await OnCallDutyPolicyService.getOnCallDutyPolicyName({
|
|
onCallDutyPolicyId: onCallDutyPolicyId,
|
|
});
|
|
|
|
const overrideUserTimezone: Timezone | null =
|
|
await UserService.getTimezoneForUser(overrideUserId);
|
|
|
|
const routeAlertsToUserTimezone: Timezone | null =
|
|
await UserService.getTimezoneForUser(routeAlertsToUserId);
|
|
|
|
const timezones: Timezone[] = [];
|
|
if (overrideUserTimezone) {
|
|
timezones.push(overrideUserTimezone);
|
|
}
|
|
|
|
if (routeAlertsToUserTimezone) {
|
|
timezones.push(routeAlertsToUserTimezone);
|
|
}
|
|
|
|
const deleteByUserId: ObjectID | undefined | null =
|
|
deleteBy.props.userId;
|
|
|
|
await OnCallDutyPolicyFeedService.createOnCallDutyPolicyFeedItem({
|
|
onCallDutyPolicyId: onCallDutyPolicyId,
|
|
projectId: projectId!,
|
|
onCallDutyPolicyFeedEventType:
|
|
OnCallDutyPolicyFeedEventType.UserOverrideRemoved,
|
|
displayColor: Gray500,
|
|
feedInfoInMarkdown: `❌ Removed a User Override Rule for user **${await UserService.getUserMarkdownString(
|
|
{
|
|
userId: overrideUserId,
|
|
projectId: projectId!,
|
|
},
|
|
)}** for the [On-Call Policy ${onCallPolicyName}](${(await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId!, onCallDutyPolicyId!)).toString()}). All alerts will be routed back to **${await UserService.getUserMarkdownString(
|
|
{
|
|
userId: overrideUserId,
|
|
projectId: projectId!,
|
|
},
|
|
)}**`,
|
|
userId: deleteByUserId || undefined,
|
|
workspaceNotification: {
|
|
sendWorkspaceNotification: true,
|
|
notifyUserId: deleteByUserId || undefined,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
deleteBy,
|
|
carryForward: null,
|
|
};
|
|
}
|
|
|
|
@CaptureSpan()
|
|
public async getOnCallDutyPolicyUserOverrideLinkInDashboard(data: {
|
|
projectId: ObjectID;
|
|
onCallDutyPolicyId?: ObjectID | undefined; // if this is null then this is a global override
|
|
onCallDutyPolicyUserOverrideId: ObjectID;
|
|
}): Promise<URL> {
|
|
const projectId: ObjectID = data.projectId;
|
|
const onCallDutyPolicyId: ObjectID | undefined = data.onCallDutyPolicyId;
|
|
const onCallDutyPolicyUserOverrideId: ObjectID =
|
|
data.onCallDutyPolicyUserOverrideId;
|
|
|
|
const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
|
|
|
|
if (!onCallDutyPolicyId) {
|
|
return URL.fromString(dashboardUrl.toString()).addRoute(
|
|
`/${projectId.toString()}/on-call-duty/user-overrides/${onCallDutyPolicyUserOverrideId.toString()}`,
|
|
);
|
|
}
|
|
return URL.fromString(dashboardUrl.toString()).addRoute(
|
|
`/${projectId.toString()}/on-call-duty/policies/${onCallDutyPolicyId.toString()}/user-overrides/${onCallDutyPolicyUserOverrideId.toString()}`,
|
|
);
|
|
}
|
|
}
|
|
export default new Service();
|