From 06d0dba258685ef615eaefd0150e2ed814b869fa Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Tue, 8 Oct 2024 20:26:00 +0100 Subject: [PATCH] add monitor alert --- .../UserNotificationSettingService.ts | 64 +++++++++++++++++++ .../NotificationSettingEventType.ts | 7 ++ .../UI/Components/HeaderAlert/HeaderAlert.tsx | 19 +++++- .../HeaderAlert/HeaderAlertGroup.tsx | 14 ++-- Dashboard/src/Components/Header/Header.tsx | 12 ++-- .../UserSettings/NotificationSettings.tsx | 30 +++++++++ Worker/DataMigrations/Index.ts | 2 + .../RefreshUserNotificationSetting.ts | 60 +++++++++++++++++ .../SendCreatedResourceNotification.ts | 2 +- .../AlertOwners/SendNotePostedNotification.ts | 2 +- .../AlertOwners/SendOwnerAddedNotification.ts | 2 +- .../SendStateChangeNotification.ts | 2 +- 12 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 Worker/DataMigrations/RefreshUserNotificationSetting.ts diff --git a/Common/Server/Services/UserNotificationSettingService.ts b/Common/Server/Services/UserNotificationSettingService.ts index 3d581ddd9c..70bbd5e7ab 100644 --- a/Common/Server/Services/UserNotificationSettingService.ts +++ b/Common/Server/Services/UserNotificationSettingService.ts @@ -282,6 +282,38 @@ export class Service extends DatabaseService { }); } + + const alertCreatedNotificationEvent: PositiveNumber = await this.countBy( + { + query: { + userId, + projectId, + eventType: + NotificationSettingEventType.SEND_ALERT_CREATED_OWNER_NOTIFICATION, + }, + props: { + isRoot: true, + }, + }, + ); + + if (alertCreatedNotificationEvent.toNumber() === 0) { + const item: UserNotificationSetting = new UserNotificationSetting(); + item.userId = userId; + item.projectId = projectId; + item.eventType = + NotificationSettingEventType.SEND_ALERT_CREATED_OWNER_NOTIFICATION; + item.alertByEmail = true; + + await this.create({ + data: item, + props: { + isRoot: true, + }, + }); + } + + // check monitor state changed notification const monitorStateChangedNotificationEvent: PositiveNumber = await this.countBy({ @@ -404,6 +436,38 @@ export class Service extends DatabaseService { }, }); } + + + // check alert state changed notification + const alertStateChangedNotificationEvent: PositiveNumber = + await this.countBy({ + query: { + userId, + projectId, + eventType: + NotificationSettingEventType.SEND_ALERT_STATE_CHANGED_OWNER_NOTIFICATION, + }, + props: { + isRoot: true, + }, + }); + + if (alertStateChangedNotificationEvent.toNumber() === 0) { + const item: UserNotificationSetting = new UserNotificationSetting(); + item.userId = userId; + item.projectId = projectId; + item.eventType = + NotificationSettingEventType.SEND_ALERT_STATE_CHANGED_OWNER_NOTIFICATION; + item.alertByEmail = true; + + await this.create({ + data: item, + props: { + isRoot: true, + }, + }); + } + } protected override async onBeforeCreate( diff --git a/Common/Types/NotificationSetting/NotificationSettingEventType.ts b/Common/Types/NotificationSetting/NotificationSettingEventType.ts index be06cf09b0..7e33383bc0 100644 --- a/Common/Types/NotificationSetting/NotificationSettingEventType.ts +++ b/Common/Types/NotificationSetting/NotificationSettingEventType.ts @@ -5,6 +5,13 @@ enum NotificationSettingEventType { 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", + // Alerts + + SEND_ALERT_CREATED_OWNER_NOTIFICATION = "Send alert created notification when I am the owner of the alert", + SEND_ALERT_NOTE_POSTED_OWNER_NOTIFICATION = "Send alert note posted notification when I am the owner of the alert", + SEND_ALERT_STATE_CHANGED_OWNER_NOTIFICATION = "Send alert state changed notification when I am the owner of the alert", + SEND_ALERT_OWNER_ADDED_NOTIFICATION = "Send notification when I am added as a owner to the alert", + // Monitors SEND_MONITOR_OWNER_ADDED_NOTIFICATION = "Send notification when I am added as a owner to the monitor", SEND_MONITOR_CREATED_OWNER_NOTIFICATION = "Send monitor created notification when I am the owner of the monitor", diff --git a/Common/UI/Components/HeaderAlert/HeaderAlert.tsx b/Common/UI/Components/HeaderAlert/HeaderAlert.tsx index e4644dd604..0e2d996c9d 100644 --- a/Common/UI/Components/HeaderAlert/HeaderAlert.tsx +++ b/Common/UI/Components/HeaderAlert/HeaderAlert.tsx @@ -40,10 +40,27 @@ const HeaderAlert: (props: ComponentProps) => ReactElement = ( break; } + let bgColor: string = "bg-gray"; + + switch (props.alertType) { + case HeaderAlertType.SUCCESS: + bgColor = "bg-green"; + break; + case HeaderAlertType.ERROR: + bgColor = "bg-red"; + break; + case HeaderAlertType.WARNING: + bgColor = "bg-yellow"; + break; + case HeaderAlertType.INFO: + bgColor = "bg-indigo"; + break; + } + const getElement: GetReactElementFunction = (): ReactElement => { return (
{ props.onClick && props.onClick(); }} diff --git a/Common/UI/Components/HeaderAlert/HeaderAlertGroup.tsx b/Common/UI/Components/HeaderAlert/HeaderAlertGroup.tsx index e098614858..6f9a61cc48 100644 --- a/Common/UI/Components/HeaderAlert/HeaderAlertGroup.tsx +++ b/Common/UI/Components/HeaderAlert/HeaderAlertGroup.tsx @@ -26,17 +26,21 @@ const HeaderAlertGroup: (props: ComponentProps) => ReactElement = ( return <>; } + // const className: string = "rounded-lg m-3 h-10 pr-0 pl-0 flex border-2 border-gray-200"; + const className: string = "rounded-lg m-3 mt-5 h-10 pr-0 pl-0 flex" + + return ( -
+
{children.map((child: ReactElement | false, index: number) => { - const isLastElement: boolean = index === props.children.length - 1; + // const isLastElement: boolean = index === props.children.length - 1; return ( -
+
{child} - {!isLastElement && ( + {/* {!isLastElement && (
- )} + )} */}
); })} diff --git a/Dashboard/src/Components/Header/Header.tsx b/Dashboard/src/Components/Header/Header.tsx index 22e797363f..593fb18ce7 100644 --- a/Dashboard/src/Components/Header/Header.tsx +++ b/Dashboard/src/Components/Header/Header.tsx @@ -221,8 +221,8 @@ const DashboardHeader: FunctionComponent = ( hasAcceptedInvitation: false, }} alertType={HeaderAlertType.INFO} - singularName="" - pluralName="" + singularName="Invitation" + pluralName="Invitations" tooltip="Looks like you have pending project invitations. Please click here to review and accept them." requestOptions={{ isMultiTenantRequest: true, @@ -242,8 +242,8 @@ const DashboardHeader: FunctionComponent = ( }, }} refreshToggle={activeIncidentToggleRefresh} - singularName="" - pluralName="" + singularName="Incident" + pluralName="Incidents" tooltip="View all active incidents" requestOptions={{ isMultiTenantRequest: true, @@ -263,8 +263,8 @@ const DashboardHeader: FunctionComponent = ( }, }} refreshToggle={activeAlertToggleRefresh} - singularName="" - pluralName="" + singularName="Alert" + pluralName="Alerts" tooltip="View all active alerts" onClick={() => { Navigation.navigate(RouteMap[PageMap.ALERTS]!); diff --git a/Dashboard/src/Pages/UserSettings/NotificationSettings.tsx b/Dashboard/src/Pages/UserSettings/NotificationSettings.tsx index b1f0a4c06c..a76039003c 100644 --- a/Dashboard/src/Pages/UserSettings/NotificationSettings.tsx +++ b/Dashboard/src/Pages/UserSettings/NotificationSettings.tsx @@ -132,6 +132,22 @@ const Settings: FunctionComponent = (): ReactElement => { return ( + +
+ {getModelTable({ + eventOptions: [ + NotificationSettingEventType.SEND_ALERT_NOTE_POSTED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_OWNER_ADDED_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_CREATED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_STATE_CHANGED_OWNER_NOTIFICATION, + ], + title: "Alert Notifications", + description: + "Here are the list of notification methods we will use when an event happens on an alert.", + })} +
+ +
{getModelTable({ eventOptions: [ @@ -146,6 +162,20 @@ const Settings: FunctionComponent = (): ReactElement => { })}
+
+ {getModelTable({ + eventOptions: [ + NotificationSettingEventType.SEND_INCIDENT_NOTE_POSTED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_INCIDENT_OWNER_ADDED_NOTIFICATION, + NotificationSettingEventType.SEND_INCIDENT_CREATED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_INCIDENT_STATE_CHANGED_OWNER_NOTIFICATION, + ], + title: "Alert Notifications", + description: + "Here are the list of notification methods we will use when an event happens on an incident.", + })} +
+
{getModelTable({ eventOptions: [ diff --git a/Worker/DataMigrations/Index.ts b/Worker/DataMigrations/Index.ts index 16a15de683..2c8438a6e9 100644 --- a/Worker/DataMigrations/Index.ts +++ b/Worker/DataMigrations/Index.ts @@ -36,6 +36,7 @@ import DeleteOldTelemetryTable from "./DeleteOldTelelmetryTable"; import MoveTelemetryServiceTokenToTelemetryIngestionKey from "./MoveTelemetryServiceTokenToTelemetryIngestionKey"; import AddDefaultCopilotActionTypes from "./AddDefaultCopilotActionTypes"; import AddDefaultAlertSeverityAndStateToExistingProjects from "./AddDefaultAlertSeverityAndStateToExistingProjects"; +import RefreshDefaultUserNotificationSetting from "./RefreshUserNotificationSetting"; // This is the order in which the migrations will be run. Add new migrations to the end of the array. @@ -77,6 +78,7 @@ const DataMigrations: Array = [ new MoveTelemetryServiceTokenToTelemetryIngestionKey(), new AddDefaultCopilotActionTypes(), new AddDefaultAlertSeverityAndStateToExistingProjects(), + new RefreshDefaultUserNotificationSetting() ]; export default DataMigrations; diff --git a/Worker/DataMigrations/RefreshUserNotificationSetting.ts b/Worker/DataMigrations/RefreshUserNotificationSetting.ts new file mode 100644 index 0000000000..529d0a80e5 --- /dev/null +++ b/Worker/DataMigrations/RefreshUserNotificationSetting.ts @@ -0,0 +1,60 @@ +import DataMigrationBase from "./DataMigrationBase"; +import LIMIT_MAX from "Common/Types/Database/LimitMax"; +import TeamMemberService from "Common/Server/Services/TeamMemberService"; +import UserNotificationSettingService from "Common/Server/Services/UserNotificationSettingService"; +import UserService from "Common/Server/Services/UserService"; +import TeamMember from "Common/Models/DatabaseModels/TeamMember"; +import User from "Common/Models/DatabaseModels/User"; + +export default class RefreshDefaultUserNotificationSetting extends DataMigrationBase { + public constructor() { + super("RefreshDefaultUserNotificationSetting"); + } + + public override async migrate(): Promise { + // get all the users with email isVerified true. + + const users: Array = await UserService.findBy({ + query: { + isEmailVerified: true, + }, + select: { + email: true, + }, + skip: 0, + limit: LIMIT_MAX, + props: { + isRoot: true, + }, + }); + + for (const user of users) { + // then get all the projects the user belongs to. + const teamMembers: Array = await TeamMemberService.findBy({ + query: { + userId: user.id!, + hasAcceptedInvitation: true, + }, + select: { + projectId: true, + }, + limit: LIMIT_MAX, + skip: 0, + props: { + isRoot: true, + }, + }); + + for (const teamMember of teamMembers) { + await UserNotificationSettingService.addDefaultNotificationSettingsForUser( + user.id!, + teamMember.projectId!, + ); + } + } + } + + public override async rollback(): Promise { + return; + } +} diff --git a/Worker/Jobs/AlertOwners/SendCreatedResourceNotification.ts b/Worker/Jobs/AlertOwners/SendCreatedResourceNotification.ts index c50a227064..96aecb020b 100644 --- a/Worker/Jobs/AlertOwners/SendCreatedResourceNotification.ts +++ b/Worker/Jobs/AlertOwners/SendCreatedResourceNotification.ts @@ -173,7 +173,7 @@ RunCron( smsMessage: sms, callRequestMessage: callMessage, eventType: - NotificationSettingEventType.SEND_INCIDENT_CREATED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_CREATED_OWNER_NOTIFICATION, }); } catch (e) { logger.error("Error in sending alert created resource notification"); diff --git a/Worker/Jobs/AlertOwners/SendNotePostedNotification.ts b/Worker/Jobs/AlertOwners/SendNotePostedNotification.ts index 0df17e92df..a3b7f160db 100644 --- a/Worker/Jobs/AlertOwners/SendNotePostedNotification.ts +++ b/Worker/Jobs/AlertOwners/SendNotePostedNotification.ts @@ -166,7 +166,7 @@ RunCron( smsMessage: sms, callRequestMessage: callMessage, eventType: - NotificationSettingEventType.SEND_INCIDENT_NOTE_POSTED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_NOTE_POSTED_OWNER_NOTIFICATION, }); } } diff --git a/Worker/Jobs/AlertOwners/SendOwnerAddedNotification.ts b/Worker/Jobs/AlertOwners/SendOwnerAddedNotification.ts index f3b5fe1ce6..87fb713159 100644 --- a/Worker/Jobs/AlertOwners/SendOwnerAddedNotification.ts +++ b/Worker/Jobs/AlertOwners/SendOwnerAddedNotification.ts @@ -201,7 +201,7 @@ RunCron( smsMessage: sms, callRequestMessage: callMessage, eventType: - NotificationSettingEventType.SEND_INCIDENT_OWNER_ADDED_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_OWNER_ADDED_NOTIFICATION, }); } } diff --git a/Worker/Jobs/AlertOwners/SendStateChangeNotification.ts b/Worker/Jobs/AlertOwners/SendStateChangeNotification.ts index b56c97e53d..0cc29091c3 100644 --- a/Worker/Jobs/AlertOwners/SendStateChangeNotification.ts +++ b/Worker/Jobs/AlertOwners/SendStateChangeNotification.ts @@ -186,7 +186,7 @@ RunCron( smsMessage: sms, callRequestMessage: callMessage, eventType: - NotificationSettingEventType.SEND_INCIDENT_STATE_CHANGED_OWNER_NOTIFICATION, + NotificationSettingEventType.SEND_ALERT_STATE_CHANGED_OWNER_NOTIFICATION, }); } }