Skip to content

Commit

Permalink
Merge pull request #1688 from OneUptime/sm-templates
Browse files Browse the repository at this point in the history
Scheduled Maintenance Templates
  • Loading branch information
simlarsen authored Sep 9, 2024
2 parents 09370db + 8e74997 commit 6b9a46b
Show file tree
Hide file tree
Showing 62 changed files with 4,635 additions and 437 deletions.
Binary file removed Accounts/public/assets/fonts/camphor/font1.woff2
Binary file not shown.
Binary file removed Accounts/public/assets/fonts/camphor/font2.woff2
Binary file not shown.
Binary file removed Accounts/public/assets/fonts/camphor/font3.woff2
Binary file not shown.
Binary file removed Accounts/public/assets/fonts/camphor/font4.woff2
Binary file not shown.
7 changes: 0 additions & 7 deletions Accounts/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,6 @@
<title>OneUptime Accounts</title>


<!-- Preload light, regular, medium and bold, which are fonts that are used on home -->
<link rel="preload" href="/accounts/assets/fonts/camphor/font1.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="preload" href="/accounts/assets/fonts/camphor/font2.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="preload" href="/accounts/assets/fonts/camphor/font3.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="preload" href="/accounts/assets/fonts/camphor/font4.woff2" as="font" type="font/woff2" crossorigin="">


<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
7 changes: 0 additions & 7 deletions AdminDashboard/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@

<title>OneUptime Admin Dashboard</title>


<!-- Preload light, regular, medium and bold, which are fonts that are used on home
<link rel="preload" href="/admin/assets/fonts/camphor/font1.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="preload" href="/admin/assets/fonts/camphor/font2.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="preload" href="/admin/assets/fonts/camphor/font3.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="preload" href="/admin/assets/fonts/camphor/font4.woff2" as="font" type="font/woff2" crossorigin=""> -->


<!--
manifest.json provides metadata used when your web app is installed on a
Expand Down
48 changes: 48 additions & 0 deletions App/FeatureSet/BaseAPI/Index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,20 @@ import ExceptionInstance from "Common/Models/AnalyticsModels/ExceptionInstance";
import TelemetyException from "Common/Models/DatabaseModels/TelemetryException";
import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority";

// scheduled maintenance template
import ScheduledMaintenanceTemplate from "Common/Models/DatabaseModels/ScheduledMaintenanceTemplate";
import ScheduledMaintenanceTemplateOwnerTeam from "Common/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerTeam";
import ScheduledMaintenanceTemplateOwnerUser from "Common/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser";
import ScheduledMaintenanceTemplateService, {
Service as ScheduledMaintenanceTemplateServiceType,
} from "Common/Server/Services/ScheduledMaintenanceTemplateService";
import ScheduledMaintenanceTemplateOwnerTeamService, {
Service as ScheduledMaintenanceTemplateOwnerTeamServiceType,
} from "Common/Server/Services/ScheduledMaintenanceTemplateOwnerTeamService";
import ScheduledMaintenanceTemplateOwnerUserService, {
Service as ScheduledMaintenanceTemplateOwnerUserServiceType,
} from "Common/Server/Services/ScheduledMaintenanceTemplateOwnerUserService";

const BaseAPIFeatureSet: FeatureSet = {
init: async (): Promise<void> => {
const app: ExpressApplication = Express.getExpressApp();
Expand Down Expand Up @@ -460,6 +474,40 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);

// scheduled maintenance template
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
ScheduledMaintenanceTemplate,
ScheduledMaintenanceTemplateServiceType
>(
ScheduledMaintenanceTemplate,
ScheduledMaintenanceTemplateService,
).getRouter(),
);

app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
ScheduledMaintenanceTemplateOwnerTeam,
ScheduledMaintenanceTemplateOwnerTeamServiceType
>(
ScheduledMaintenanceTemplateOwnerTeam,
ScheduledMaintenanceTemplateOwnerTeamService,
).getRouter(),
);

app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
ScheduledMaintenanceTemplateOwnerUser,
ScheduledMaintenanceTemplateOwnerUserServiceType
>(
ScheduledMaintenanceTemplateOwnerUser,
ScheduledMaintenanceTemplateOwnerUserService,
).getRouter(),
);

app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAnalyticsAPI<Log, LogServiceType>(Log, LogService).getRouter(),
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions App/FeatureSet/Workers/Index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import "./Jobs/ScheduledMaintenance/ChangeStateToEnded";
// Scheduled Event
import "./Jobs/ScheduledMaintenance/ChangeStateToOngoing";
import "./Jobs/ScheduledMaintenance/SendNotificationToSubscribers";
import "./Jobs/ScheduledMaintenance/ScheduleRecurringEvents";
// Scheduled Event Owners
import "./Jobs/ScheduledMaintenanceOwners/SendCreatedResourceNotification";
import "./Jobs/ScheduledMaintenanceOwners/SendNotePostedNotification";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import RunCron from "../../Utils/Cron";
import LIMIT_MAX from "Common/Types/Database/LimitMax";
import OneUptimeDate from "Common/Types/Date";
import { EVERY_MINUTE } from "Common/Utils/CronTime";
import ScheduledMaintenanceService from "Common/Server/Services/ScheduledMaintenanceService";
import QueryHelper from "Common/Server/Types/Database/QueryHelper";
import ScheduledMaintenanceTemplate from "Common/Models/DatabaseModels/ScheduledMaintenanceTemplate";
import ScheduledMaintenanceTemplateService from "Common/Server/Services/ScheduledMaintenanceTemplateService";
import ScheduledMaintenanceTemplateOwnerUserService from "Common/Server/Services/ScheduledMaintenanceTemplateOwnerUserService";
import ScheduledMaintenanceOwnerUser from "Common/Models/DatabaseModels/ScheduledMaintenanceOwnerUser";
import ScheduledMaintenanceTemplateOwnerUser from "Common/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser";
import ScheduledMaintenanceOwnerTeamService from "Common/Server/Services/ScheduledMaintenanceOwnerTeamService";
import ScheduledMaintenanceTemplateOwnerTeamService from "Common/Server/Services/ScheduledMaintenanceTemplateOwnerTeamService";
import ScheduledMaintenance from "Common/Models/DatabaseModels/ScheduledMaintenance";
import ScheduledMaintenanceOwnerUserService from "Common/Server/Services/ScheduledMaintenanceOwnerUserService";
import ScheduledMaintenanceOwnerTeam from "Common/Models/DatabaseModels/ScheduledMaintenanceOwnerTeam";
import logger from "Common/Server/Utils/Logger";
import Recurring from "Common/Types/Events/Recurring";

RunCron(
"ScheduledMaintenance:ScheduleRecurringEvents",
{ schedule: EVERY_MINUTE, runOnStartup: false },
async () => {
// get all scheduled events of all the projects.
const recurringTemplates: Array<ScheduledMaintenanceTemplate> =
await ScheduledMaintenanceTemplateService.findBy({
query: {
isRecurringEvent: true,
scheduleNextEventAt: QueryHelper.lessThanEqualTo(
OneUptimeDate.getCurrentDate(),
),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
projectId: true,
changeMonitorStatusToId: true,
shouldStatusPageSubscribersBeNotifiedWhenEventChangedToEnded: true,
shouldStatusPageSubscribersBeNotifiedOnEventCreated: true,
shouldStatusPageSubscribersBeNotifiedWhenEventChangedToOngoing: true,
monitors: true,
statusPages: true,
scheduleNextEventAt: true,
firstEventStartsAt: true,
firstEventEndsAt: true,
firstEventScheduledAt: true,
title: true,
description: true,
labels: true,
isRecurringEvent: true,
recurringInterval: true,
},
});

// change their state to Ongoing.

for (const recurringTemplate of recurringTemplates) {
try {
if (recurringTemplate.recurringInterval === undefined) {
continue;
}

// update the next scheduled time for this event.
const recurringInterval: Recurring =
recurringTemplate.recurringInterval!;
const nextScheduledTime: Date =
ScheduledMaintenanceTemplateService.getNextEventTime({
dateAndTime: recurringTemplate.scheduleNextEventAt!,
recurringInterval,
});

await ScheduledMaintenanceTemplateService.updateOneById({
id: recurringTemplate.id!,
data: {
scheduleNextEventAt: nextScheduledTime,
},
props: {
isRoot: true,
},
});

// get owner users for this template.
const ownerUsers: Array<ScheduledMaintenanceTemplateOwnerUser> =
await ScheduledMaintenanceTemplateOwnerUserService.findBy({
query: {
scheduledMaintenanceTemplateId: recurringTemplate.id!,
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
userId: true,
},
});

// owner teams.
const ownerTeams: Array<ScheduledMaintenanceOwnerTeam> =
await ScheduledMaintenanceTemplateOwnerTeamService.findBy({
query: {
scheduledMaintenanceTemplateId: recurringTemplate.id!,
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
teamId: true,
},
});

// now create a new scheduled maintenance event for this template.
let scheduledMaintenanceEvent: ScheduledMaintenance =
new ScheduledMaintenance();
scheduledMaintenanceEvent.projectId = recurringTemplate.projectId!;
scheduledMaintenanceEvent.changeMonitorStatusToId =
recurringTemplate.changeMonitorStatusToId!;
scheduledMaintenanceEvent.shouldStatusPageSubscribersBeNotifiedWhenEventChangedToEnded =
recurringTemplate.shouldStatusPageSubscribersBeNotifiedWhenEventChangedToEnded!;
scheduledMaintenanceEvent.shouldStatusPageSubscribersBeNotifiedOnEventCreated =
recurringTemplate.shouldStatusPageSubscribersBeNotifiedOnEventCreated!;
scheduledMaintenanceEvent.shouldStatusPageSubscribersBeNotifiedWhenEventChangedToOngoing =
recurringTemplate.shouldStatusPageSubscribersBeNotifiedWhenEventChangedToOngoing!;
scheduledMaintenanceEvent.monitors = recurringTemplate.monitors!;
scheduledMaintenanceEvent.statusPages = recurringTemplate.statusPages!;
scheduledMaintenanceEvent.title = recurringTemplate.title!;
scheduledMaintenanceEvent.description = recurringTemplate.description!;
scheduledMaintenanceEvent.labels = recurringTemplate.labels!;

const eventscheduledTime: Date = recurringTemplate.scheduleNextEventAt!;

const firstScheduledTime: Date =
recurringTemplate.firstEventScheduledAt!;
const firstStartTime: Date = recurringTemplate.firstEventStartsAt!;
const firstEndTime: Date = recurringTemplate.firstEventEndsAt!;

const minutesBetwenScheduledAndStartTime: number =
OneUptimeDate.getMinutesBetweenTwoDates(
eventscheduledTime,
firstStartTime,
);
const minutesBetweenScheduledAndEndTime: number =
OneUptimeDate.getMinutesBetweenTwoDates(
eventscheduledTime,
firstEndTime,
);

// set the scheduled time for this event.
scheduledMaintenanceEvent.createdAt = eventscheduledTime!;
scheduledMaintenanceEvent.startsAt = OneUptimeDate.addRemoveMinutes(
firstScheduledTime,
minutesBetwenScheduledAndStartTime,
);
scheduledMaintenanceEvent.endsAt = OneUptimeDate.addRemoveMinutes(
firstScheduledTime,
minutesBetweenScheduledAndEndTime,
);

// now create this event.

scheduledMaintenanceEvent = await ScheduledMaintenanceService.create({
data: scheduledMaintenanceEvent,
props: {
isRoot: true,
},
});

// now add owners and teams to this event.

for (const ownerUser of ownerUsers) {
const scheduledMaintenanceOwnerUser: ScheduledMaintenanceOwnerUser =
new ScheduledMaintenanceOwnerUser();
scheduledMaintenanceOwnerUser.scheduledMaintenanceId =
scheduledMaintenanceEvent.id!;
scheduledMaintenanceOwnerUser.projectId =
scheduledMaintenanceEvent.projectId!;
scheduledMaintenanceOwnerUser.userId = ownerUser.userId!;
await ScheduledMaintenanceOwnerUserService.create({
data: scheduledMaintenanceOwnerUser,
props: {
isRoot: true,
},
});
}

// now do the same for owner teams.

for (const ownerTeam of ownerTeams) {
const scheduledMaintenanceOwnerTeam: ScheduledMaintenanceOwnerTeam =
new ScheduledMaintenanceOwnerTeam();
scheduledMaintenanceOwnerTeam.scheduledMaintenanceId =
scheduledMaintenanceEvent.id!;
scheduledMaintenanceOwnerTeam.projectId =
scheduledMaintenanceEvent.projectId!;
scheduledMaintenanceOwnerTeam.teamId = ownerTeam.teamId!;
await ScheduledMaintenanceOwnerTeamService.create({
data: scheduledMaintenanceOwnerTeam,
props: {
isRoot: true,
},
});
}
} catch (e) {
logger.error(e);
}
}
},
);
8 changes: 8 additions & 0 deletions Common/Models/DatabaseModels/IncidentNoteTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
import { PlanType } from "../../Types/Billing/SubscriptionPlan";

@TableBillingAccessControl({
create: PlanType.Growth,
read: PlanType.Growth,
update: PlanType.Growth,
delete: PlanType.Growth,
})
@EnableDocumentation()
@TenantColumn("projectId")
@TableAccessControl({
Expand Down
8 changes: 8 additions & 0 deletions Common/Models/DatabaseModels/IncidentTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ import {
ManyToMany,
ManyToOne,
} from "typeorm";
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
import { PlanType } from "../../Types/Billing/SubscriptionPlan";

@TableBillingAccessControl({
create: PlanType.Growth,
read: PlanType.Growth,
update: PlanType.Growth,
delete: PlanType.Growth,
})
@EnableDocumentation()
@AccessControlColumn("labels")
@TenantColumn("projectId")
Expand Down
6 changes: 6 additions & 0 deletions Common/Models/DatabaseModels/Index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ import TelemetryIngestionKey from "./TelemetryIngestionKey";

import TelemetryException from "./TelemetryException";
import CopilotActionTypePriority from "./CopilotActionTypePriority";
import ScheduledMaintenanceTemplate from "./ScheduledMaintenanceTemplate";
import ScheduledMaintenanceTemplateOwnerTeam from "./ScheduledMaintenanceTemplateOwnerTeam";
import ScheduledMaintenanceTemplateOwnerUser from "./ScheduledMaintenanceTemplateOwnerUser";

export default [
User,
Expand Down Expand Up @@ -244,6 +247,9 @@ export default [

IncidentNoteTemplate,

ScheduledMaintenanceTemplate,
ScheduledMaintenanceTemplateOwnerTeam,
ScheduledMaintenanceTemplateOwnerUser,
ScheduledMaintenanceNoteTemplate,

Reseller,
Expand Down
Loading

0 comments on commit 6b9a46b

Please sign in to comment.