@oneuptime/common 10.5.3 → 10.5.6
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/Models/DatabaseModels/AlertEpisodeMember.ts +29 -0
- package/Models/DatabaseModels/AlertGroupingRule.ts +76 -0
- package/Models/DatabaseModels/GlobalConfig.ts +28 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +29 -0
- package/Models/DatabaseModels/IncidentGroupingRule.ts +76 -0
- package/Models/DatabaseModels/ProjectSmtpConfig.ts +62 -6
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779971548393-AddLabelGroupByToGroupingRules.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779975064262-AddTransportTypeToProjectSmtpConfig.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779976190561-AddSmtpTransportTypeToGlobalConfig.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779980428744-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Services/AlertEpisodeMemberService.ts +23 -0
- package/Server/Services/AlertGroupingEngineService.ts +83 -0
- package/Server/Services/IncidentEpisodeMemberService.ts +23 -0
- package/Server/Services/IncidentGroupingEngineService.ts +99 -0
- package/Server/Services/MailService.ts +15 -3
- package/Server/Services/ProjectSmtpConfigService.ts +30 -10
- package/Server/Services/StatusPagePrivateUserService.ts +1 -0
- package/Server/Services/StatusPageSubscriberService.ts +3 -0
- package/Server/Services/UserNotificationSettingService.ts +12 -0
- package/Server/Utils/WhatsAppTemplateUtil.ts +4 -0
- package/Tests/Server/Services/AlertGroupingEngineService.test.ts +28 -0
- package/Tests/Server/Services/AlertGroupingRuleService.test.ts +14 -0
- package/Types/Email/EmailServer.ts +22 -4
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/Email/MailTransportType.ts +18 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +2 -0
- package/Types/WhatsApp/WhatsAppTemplates.ts +10 -0
- package/UI/Components/Accordion/Accordion.tsx +12 -4
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +30 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertGroupingRule.js +78 -0
- package/build/dist/Models/DatabaseModels/AlertGroupingRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +28 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +30 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +78 -0
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSmtpConfig.js +59 -6
- package/build/dist/Models/DatabaseModels/ProjectSmtpConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779971548393-AddLabelGroupByToGroupingRules.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779971548393-AddLabelGroupByToGroupingRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779975064262-AddTransportTypeToProjectSmtpConfig.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779975064262-AddTransportTypeToProjectSmtpConfig.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779976190561-AddSmtpTransportTypeToGlobalConfig.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779976190561-AddSmtpTransportTypeToGlobalConfig.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779980428744-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779980428744-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeMemberService.js +20 -0
- package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/AlertGroupingEngineService.js +85 -0
- package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +20 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +95 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/MailService.js +15 -3
- package/build/dist/Server/Services/MailService.js.map +1 -1
- package/build/dist/Server/Services/ProjectSmtpConfigService.js +24 -8
- package/build/dist/Server/Services/ProjectSmtpConfigService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +2 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +2 -0
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
- package/build/dist/Tests/Server/Services/AlertGroupingEngineService.test.js +21 -0
- package/build/dist/Tests/Server/Services/AlertGroupingEngineService.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/AlertGroupingRuleService.test.js +12 -0
- package/build/dist/Tests/Server/Services/AlertGroupingRuleService.test.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +2 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Email/MailTransportType.js +19 -0
- package/build/dist/Types/Email/MailTransportType.js.map +1 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +2 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +6 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
- package/build/dist/UI/Components/Accordion/Accordion.js +4 -4
- package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
|
4
4
|
import DatabaseService from "./DatabaseService";
|
|
5
5
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
6
6
|
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import PositiveNumber from "../../Types/PositiveNumber";
|
|
7
8
|
import Model from "../../Models/DatabaseModels/IncidentEpisodeMember";
|
|
8
9
|
import Incident from "../../Models/DatabaseModels/Incident";
|
|
9
10
|
import IncidentEpisode from "../../Models/DatabaseModels/IncidentEpisode";
|
|
@@ -64,6 +65,28 @@ export class Service extends DatabaseService<Model> {
|
|
|
64
65
|
createBy.data.addedAt = OneUptimeDate.getCurrentDate();
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
/*
|
|
69
|
+
* If this is the very first incident in the episode (the founder), the
|
|
70
|
+
* "episode created" notification already covers it. Mark the member as
|
|
71
|
+
* "already notified" so the IncidentAdded-to-episode cron skips it. This
|
|
72
|
+
* avoids double-notifying owners when an episode is born with its first
|
|
73
|
+
* incident.
|
|
74
|
+
*/
|
|
75
|
+
if (createBy.data.isOwnerNotifiedOfIncidentAdded === undefined) {
|
|
76
|
+
const existingMemberCount: PositiveNumber = await this.countBy({
|
|
77
|
+
query: {
|
|
78
|
+
incidentEpisodeId: createBy.data.incidentEpisodeId,
|
|
79
|
+
},
|
|
80
|
+
props: {
|
|
81
|
+
isRoot: true,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (existingMemberCount.toNumber() === 0) {
|
|
86
|
+
createBy.data.isOwnerNotifiedOfIncidentAdded = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
67
90
|
return { createBy, carryForward: null };
|
|
68
91
|
}
|
|
69
92
|
|
|
@@ -17,6 +17,7 @@ import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
|
17
17
|
import OneUptimeDate from "../../Types/Date";
|
|
18
18
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
19
19
|
import IncidentGroupingRuleService from "./IncidentGroupingRuleService";
|
|
20
|
+
import IncidentService from "./IncidentService";
|
|
20
21
|
import IncidentEpisodeService from "./IncidentEpisodeService";
|
|
21
22
|
import IncidentEpisodeMemberService from "./IncidentEpisodeMemberService";
|
|
22
23
|
import IncidentEpisodeOwnerUserService from "./IncidentEpisodeOwnerUserService";
|
|
@@ -93,6 +94,8 @@ class IncidentGroupingEngineServiceClass {
|
|
|
93
94
|
groupByMonitor: true,
|
|
94
95
|
groupBySeverity: true,
|
|
95
96
|
groupByIncidentTitle: true,
|
|
97
|
+
groupByIncidentLabels: true,
|
|
98
|
+
groupByMonitorLabels: true,
|
|
96
99
|
// Time settings
|
|
97
100
|
enableTimeWindow: true,
|
|
98
101
|
timeWindowMinutes: true,
|
|
@@ -613,10 +616,100 @@ class IncidentGroupingEngineServiceClass {
|
|
|
613
616
|
parts.push(`title:${normalizedTitle}`);
|
|
614
617
|
}
|
|
615
618
|
|
|
619
|
+
// Group by incident labels (exact set match) - only if explicitly enabled
|
|
620
|
+
if (rule.groupByIncidentLabels && incident.id) {
|
|
621
|
+
const incidentLabels: Array<Label> =
|
|
622
|
+
await this.getIncidentLabels(incident);
|
|
623
|
+
const sortedLabelIds: Array<string> = incidentLabels
|
|
624
|
+
.map((l: Label) => {
|
|
625
|
+
return l.id?.toString() || "";
|
|
626
|
+
})
|
|
627
|
+
.filter((id: string) => {
|
|
628
|
+
return id.length > 0;
|
|
629
|
+
})
|
|
630
|
+
.sort();
|
|
631
|
+
parts.push(`incidentLabels:${sortedLabelIds.join(",")}`);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/*
|
|
635
|
+
* Group by monitor labels (exact set match) - only if explicitly enabled
|
|
636
|
+
* Incidents can be associated with multiple monitors; we union and dedupe labels across all of them.
|
|
637
|
+
*/
|
|
638
|
+
if (
|
|
639
|
+
rule.groupByMonitorLabels &&
|
|
640
|
+
incident.monitors &&
|
|
641
|
+
incident.monitors.length > 0
|
|
642
|
+
) {
|
|
643
|
+
const monitorLabelIdSet: Set<string> = new Set<string>();
|
|
644
|
+
for (const incidentMonitor of incident.monitors) {
|
|
645
|
+
if (!incidentMonitor || !incidentMonitor.id) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
const monitorLabels: Array<Label> = await this.getMonitorLabels(
|
|
649
|
+
incidentMonitor.id,
|
|
650
|
+
);
|
|
651
|
+
for (const label of monitorLabels) {
|
|
652
|
+
const labelIdStr: string = label.id?.toString() || "";
|
|
653
|
+
if (labelIdStr.length > 0) {
|
|
654
|
+
monitorLabelIdSet.add(labelIdStr);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const sortedLabelIds: Array<string> =
|
|
659
|
+
Array.from(monitorLabelIdSet).sort();
|
|
660
|
+
parts.push(`monitorLabels:${sortedLabelIds.join(",")}`);
|
|
661
|
+
}
|
|
662
|
+
|
|
616
663
|
// If no group by options are enabled, all matching incidents go into a single episode
|
|
617
664
|
return parts.join("|") || "default";
|
|
618
665
|
}
|
|
619
666
|
|
|
667
|
+
@CaptureSpan()
|
|
668
|
+
private async getIncidentLabels(incident: Incident): Promise<Array<Label>> {
|
|
669
|
+
// If labels are already loaded on the incident, use them
|
|
670
|
+
if (incident.labels && Array.isArray(incident.labels)) {
|
|
671
|
+
return incident.labels;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (!incident.id) {
|
|
675
|
+
return [];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Re-load incident with labels
|
|
679
|
+
const reloadedIncident: Incident | null = await IncidentService.findOneById(
|
|
680
|
+
{
|
|
681
|
+
id: incident.id,
|
|
682
|
+
select: {
|
|
683
|
+
labels: {
|
|
684
|
+
_id: true,
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
props: {
|
|
688
|
+
isRoot: true,
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
return reloadedIncident?.labels || [];
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@CaptureSpan()
|
|
697
|
+
private async getMonitorLabels(monitorId: ObjectID): Promise<Array<Label>> {
|
|
698
|
+
const monitor: Monitor | null = await MonitorService.findOneById({
|
|
699
|
+
id: monitorId,
|
|
700
|
+
select: {
|
|
701
|
+
labels: {
|
|
702
|
+
_id: true,
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
props: {
|
|
706
|
+
isRoot: true,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
return monitor?.labels || [];
|
|
711
|
+
}
|
|
712
|
+
|
|
620
713
|
@CaptureSpan()
|
|
621
714
|
private async findMatchingActiveEpisode(
|
|
622
715
|
projectId: ObjectID,
|
|
@@ -887,6 +980,12 @@ class IncidentGroupingEngineServiceClass {
|
|
|
887
980
|
if (rule.groupByIncidentTitle) {
|
|
888
981
|
groupByParts.push("Incident Title");
|
|
889
982
|
}
|
|
983
|
+
if (rule.groupByIncidentLabels) {
|
|
984
|
+
groupByParts.push("Incident Labels");
|
|
985
|
+
}
|
|
986
|
+
if (rule.groupByMonitorLabels) {
|
|
987
|
+
groupByParts.push("Monitor Labels");
|
|
988
|
+
}
|
|
890
989
|
|
|
891
990
|
const groupByDescription: string =
|
|
892
991
|
groupByParts.length > 0
|
|
@@ -45,12 +45,24 @@ export class MailService extends BaseService {
|
|
|
45
45
|
|
|
46
46
|
if (options && options.mailServer) {
|
|
47
47
|
body["SMTP_ID"] = options.mailServer.id?.toString();
|
|
48
|
+
// host/port/secure are optional on EmailServer because HTTP-API
|
|
49
|
+
// transports (e.g. Microsoft Graph) don't use them. Only serialize the
|
|
50
|
+
// SMTP-only fields when they're actually present.
|
|
51
|
+
body["SMTP_TRANSPORT_TYPE"] =
|
|
52
|
+
options.mailServer.transportType || undefined;
|
|
48
53
|
body["SMTP_USERNAME"] = options.mailServer.username || undefined;
|
|
49
54
|
body["SMTP_EMAIL"] = options.mailServer.fromEmail.toString();
|
|
50
55
|
body["SMTP_FROM_NAME"] = options.mailServer.fromName;
|
|
51
|
-
body["SMTP_IS_SECURE"] =
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
body["SMTP_IS_SECURE"] =
|
|
57
|
+
options.mailServer.secure === undefined
|
|
58
|
+
? undefined
|
|
59
|
+
: options.mailServer.secure;
|
|
60
|
+
body["SMTP_PORT"] = options.mailServer.port
|
|
61
|
+
? options.mailServer.port.toNumber()
|
|
62
|
+
: undefined;
|
|
63
|
+
body["SMTP_HOST"] = options.mailServer.host
|
|
64
|
+
? options.mailServer.host.toString()
|
|
65
|
+
: undefined;
|
|
54
66
|
body["SMTP_PASSWORD"] = options.mailServer.password || undefined;
|
|
55
67
|
body["SMTP_AUTH_TYPE"] = options.mailServer.authType || undefined;
|
|
56
68
|
body["SMTP_CLIENT_ID"] = options.mailServer.clientId || undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import DatabaseService from "./DatabaseService";
|
|
2
2
|
import EmailServer from "../../Types/Email/EmailServer";
|
|
3
|
+
import MailTransportType from "../../Types/Email/MailTransportType";
|
|
3
4
|
import SMTPAuthenticationType from "../../Types/Email/SMTPAuthenticationType";
|
|
4
5
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
5
6
|
import URL from "../../Types/API/URL";
|
|
@@ -21,17 +22,28 @@ export class Service extends DatabaseService<Model> {
|
|
|
21
22
|
throw new BadDataException("Project SMTP config id is not set");
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!projectSmtpConfig.port) {
|
|
29
|
-
throw new BadDataException("Project SMTP config port is not set");
|
|
30
|
-
}
|
|
25
|
+
const transportType: MailTransportType =
|
|
26
|
+
projectSmtpConfig.transportType || MailTransportType.SMTP;
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
/*
|
|
29
|
+
* Get auth type, default to UsernamePassword for backward compatibility.
|
|
30
|
+
* Microsoft Graph always uses OAuth (Client Credentials) regardless of what
|
|
31
|
+
* the user picked — but we still let the existing OAuth validation apply.
|
|
32
|
+
*/
|
|
33
33
|
const authType: SMTPAuthenticationType =
|
|
34
|
-
|
|
34
|
+
transportType === MailTransportType.MicrosoftGraph
|
|
35
|
+
? SMTPAuthenticationType.OAuth
|
|
36
|
+
: projectSmtpConfig.authType || SMTPAuthenticationType.UsernamePassword;
|
|
37
|
+
|
|
38
|
+
if (transportType === MailTransportType.SMTP) {
|
|
39
|
+
if (!projectSmtpConfig.hostname) {
|
|
40
|
+
throw new BadDataException("Project SMTP config host is not set");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!projectSmtpConfig.port) {
|
|
44
|
+
throw new BadDataException("Project SMTP config port is not set");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
35
47
|
|
|
36
48
|
// Validate based on auth type
|
|
37
49
|
if (authType === SMTPAuthenticationType.UsernamePassword) {
|
|
@@ -43,7 +55,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
43
55
|
throw new BadDataException("Project SMTP config password is not set");
|
|
44
56
|
}
|
|
45
57
|
} else if (authType === SMTPAuthenticationType.OAuth) {
|
|
46
|
-
|
|
58
|
+
/*
|
|
59
|
+
* For Microsoft Graph, username is optional — we fall back to fromEmail
|
|
60
|
+
* as the sender mailbox. For SMTP+XOAUTH2, username is required.
|
|
61
|
+
*/
|
|
62
|
+
if (
|
|
63
|
+
transportType === MailTransportType.SMTP &&
|
|
64
|
+
!projectSmtpConfig.username
|
|
65
|
+
) {
|
|
47
66
|
throw new BadDataException(
|
|
48
67
|
"Project SMTP config username (email address) is not set for OAuth",
|
|
49
68
|
);
|
|
@@ -85,6 +104,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
85
104
|
|
|
86
105
|
return {
|
|
87
106
|
id: projectSmtpConfig.id!,
|
|
107
|
+
transportType: transportType,
|
|
88
108
|
host: projectSmtpConfig.hostname,
|
|
89
109
|
port: projectSmtpConfig.port,
|
|
90
110
|
username: projectSmtpConfig.username,
|
|
@@ -684,6 +684,7 @@ Stay informed about service availability! 🚀`;
|
|
|
684
684
|
name: true,
|
|
685
685
|
smtpConfig: {
|
|
686
686
|
_id: true,
|
|
687
|
+
transportType: true,
|
|
687
688
|
hostname: true,
|
|
688
689
|
port: true,
|
|
689
690
|
username: true,
|
|
@@ -915,6 +916,7 @@ Stay informed about service availability! 🚀`;
|
|
|
915
916
|
name: true,
|
|
916
917
|
smtpConfig: {
|
|
917
918
|
_id: true,
|
|
919
|
+
transportType: true,
|
|
918
920
|
hostname: true,
|
|
919
921
|
port: true,
|
|
920
922
|
username: true,
|
|
@@ -1347,6 +1349,7 @@ Stay informed about service availability! 🚀`;
|
|
|
1347
1349
|
allowSubscribersToChooseEventTypes: true,
|
|
1348
1350
|
smtpConfig: {
|
|
1349
1351
|
_id: true,
|
|
1352
|
+
transportType: true,
|
|
1350
1353
|
hostname: true,
|
|
1351
1354
|
port: true,
|
|
1352
1355
|
username: true,
|
|
@@ -706,6 +706,12 @@ export class Service extends DatabaseService<UserNotificationSetting> {
|
|
|
706
706
|
projectId,
|
|
707
707
|
NotificationSettingEventType.SEND_ALERT_EPISODE_STATE_CHANGED_OWNER_NOTIFICATION,
|
|
708
708
|
);
|
|
709
|
+
|
|
710
|
+
await this.addNotificationSettingIfNotExists(
|
|
711
|
+
userId,
|
|
712
|
+
projectId,
|
|
713
|
+
NotificationSettingEventType.SEND_ALERT_ADDED_TO_EPISODE_OWNER_NOTIFICATION,
|
|
714
|
+
);
|
|
709
715
|
}
|
|
710
716
|
|
|
711
717
|
private async addIncidentEpisodeNotificationSettings(
|
|
@@ -723,6 +729,12 @@ export class Service extends DatabaseService<UserNotificationSetting> {
|
|
|
723
729
|
projectId,
|
|
724
730
|
NotificationSettingEventType.SEND_INCIDENT_EPISODE_STATE_CHANGED_OWNER_NOTIFICATION,
|
|
725
731
|
);
|
|
732
|
+
|
|
733
|
+
await this.addNotificationSettingIfNotExists(
|
|
734
|
+
userId,
|
|
735
|
+
projectId,
|
|
736
|
+
NotificationSettingEventType.SEND_INCIDENT_ADDED_TO_EPISODE_OWNER_NOTIFICATION,
|
|
737
|
+
);
|
|
726
738
|
}
|
|
727
739
|
|
|
728
740
|
private async addNotificationSettingIfNotExists(
|
|
@@ -94,6 +94,8 @@ const templateIdByEventType: Record<
|
|
|
94
94
|
WhatsAppTemplateIds.AlertEpisodeStateChangedOwnerNotification,
|
|
95
95
|
[NotificationSettingEventType.SEND_ALERT_EPISODE_OWNER_ADDED_NOTIFICATION]:
|
|
96
96
|
WhatsAppTemplateIds.AlertEpisodeOwnerAddedNotification,
|
|
97
|
+
[NotificationSettingEventType.SEND_ALERT_ADDED_TO_EPISODE_OWNER_NOTIFICATION]:
|
|
98
|
+
WhatsAppTemplateIds.AlertAddedToEpisodeOwnerNotification,
|
|
97
99
|
[NotificationSettingEventType.SEND_INCIDENT_EPISODE_CREATED_OWNER_NOTIFICATION]:
|
|
98
100
|
WhatsAppTemplateIds.IncidentEpisodeCreatedOwnerNotification,
|
|
99
101
|
[NotificationSettingEventType.SEND_INCIDENT_EPISODE_NOTE_POSTED_OWNER_NOTIFICATION]:
|
|
@@ -102,6 +104,8 @@ const templateIdByEventType: Record<
|
|
|
102
104
|
WhatsAppTemplateIds.IncidentEpisodeStateChangedOwnerNotification,
|
|
103
105
|
[NotificationSettingEventType.SEND_INCIDENT_EPISODE_OWNER_ADDED_NOTIFICATION]:
|
|
104
106
|
WhatsAppTemplateIds.IncidentEpisodeOwnerAddedNotification,
|
|
107
|
+
[NotificationSettingEventType.SEND_INCIDENT_ADDED_TO_EPISODE_OWNER_NOTIFICATION]:
|
|
108
|
+
WhatsAppTemplateIds.IncidentAddedToEpisodeOwnerNotification,
|
|
105
109
|
[NotificationSettingEventType.SEND_MONITOR_OWNER_ADDED_NOTIFICATION]:
|
|
106
110
|
WhatsAppTemplateIds.MonitorOwnerAddedNotification,
|
|
107
111
|
[NotificationSettingEventType.SEND_MONITOR_CREATED_OWNER_NOTIFICATION]:
|
|
@@ -232,6 +232,34 @@ describe("AlertGroupingEngineService Models", () => {
|
|
|
232
232
|
);
|
|
233
233
|
expect(groupingKey).toContain(`title:${mockAlert.title}`);
|
|
234
234
|
});
|
|
235
|
+
|
|
236
|
+
test("should produce identical label key regardless of label order (exact set match)", () => {
|
|
237
|
+
// Simulates buildGroupingKey's label-sorting logic.
|
|
238
|
+
const labelIdsA: string[] = ["lbl-c", "lbl-a", "lbl-b"];
|
|
239
|
+
const labelIdsB: string[] = ["lbl-b", "lbl-c", "lbl-a"];
|
|
240
|
+
|
|
241
|
+
const keyA: string = `alertLabels:${[...labelIdsA].sort().join(",")}`;
|
|
242
|
+
const keyB: string = `alertLabels:${[...labelIdsB].sort().join(",")}`;
|
|
243
|
+
|
|
244
|
+
expect(keyA).toBe(keyB);
|
|
245
|
+
expect(keyA).toBe("alertLabels:lbl-a,lbl-b,lbl-c");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("should produce different label keys for different label sets (exact set match)", () => {
|
|
249
|
+
// Alerts with [A,B] and [A,C] must NOT group together under exact set match.
|
|
250
|
+
const keyAB: string = `alertLabels:${["lbl-a", "lbl-b"].sort().join(",")}`;
|
|
251
|
+
const keyAC: string = `alertLabels:${["lbl-a", "lbl-c"].sort().join(",")}`;
|
|
252
|
+
|
|
253
|
+
expect(keyAB).not.toBe(keyAC);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("should emit empty label key when alert has no labels", () => {
|
|
257
|
+
// Alerts with no labels should produce the same (empty) label key.
|
|
258
|
+
const labelIds: string[] = [];
|
|
259
|
+
const key: string = `alertLabels:${[...labelIds].sort().join(",")}`;
|
|
260
|
+
|
|
261
|
+
expect(key).toBe("alertLabels:");
|
|
262
|
+
});
|
|
235
263
|
});
|
|
236
264
|
|
|
237
265
|
describe("Time Window Configuration", () => {
|
|
@@ -143,14 +143,28 @@ describe("AlertGroupingRule Model", () => {
|
|
|
143
143
|
expect(rule.groupByAlertTitle).toBe(true);
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
+
test("should set and get groupByAlertLabels correctly", () => {
|
|
147
|
+
rule.groupByAlertLabels = true;
|
|
148
|
+
expect(rule.groupByAlertLabels).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("should set and get groupByMonitorLabels correctly", () => {
|
|
152
|
+
rule.groupByMonitorLabels = true;
|
|
153
|
+
expect(rule.groupByMonitorLabels).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
146
156
|
test("should handle all groupBy options as false", () => {
|
|
147
157
|
rule.groupByMonitor = false;
|
|
148
158
|
rule.groupBySeverity = false;
|
|
149
159
|
rule.groupByAlertTitle = false;
|
|
160
|
+
rule.groupByAlertLabels = false;
|
|
161
|
+
rule.groupByMonitorLabels = false;
|
|
150
162
|
|
|
151
163
|
expect(rule.groupByMonitor).toBe(false);
|
|
152
164
|
expect(rule.groupBySeverity).toBe(false);
|
|
153
165
|
expect(rule.groupByAlertTitle).toBe(false);
|
|
166
|
+
expect(rule.groupByAlertLabels).toBe(false);
|
|
167
|
+
expect(rule.groupByMonitorLabels).toBe(false);
|
|
154
168
|
});
|
|
155
169
|
|
|
156
170
|
test("should handle combination of groupBy options", () => {
|
|
@@ -3,20 +3,38 @@ import URL from "../API/URL";
|
|
|
3
3
|
import Email from "../Email";
|
|
4
4
|
import ObjectID from "../ObjectID";
|
|
5
5
|
import Port from "../Port";
|
|
6
|
+
import MailTransportType from "./MailTransportType";
|
|
6
7
|
import OAuthProviderType from "./OAuthProviderType";
|
|
7
8
|
import SMTPAuthenticationType from "./SMTPAuthenticationType";
|
|
8
9
|
|
|
9
10
|
export default interface EmailServer {
|
|
10
11
|
id?: ObjectID | undefined; // If this is custom SMTP, this is the ID of the SMTP config. Otherwise, it's undefined
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Transport selection.
|
|
15
|
+
* Defaults to SMTP when undefined for backwards compatibility with existing
|
|
16
|
+
* EmailServer constructions.
|
|
17
|
+
*/
|
|
18
|
+
transportType?: MailTransportType | undefined;
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* SMTP transport fields. Required when transportType === SMTP (or undefined).
|
|
22
|
+
* Not used for HTTP-API transports like MicrosoftGraph.
|
|
23
|
+
*/
|
|
24
|
+
host?: Hostname | undefined;
|
|
25
|
+
port?: Port | undefined;
|
|
26
|
+
secure?: boolean | undefined;
|
|
27
|
+
|
|
13
28
|
username: string | undefined;
|
|
14
29
|
password: string | undefined;
|
|
15
|
-
secure: boolean;
|
|
16
30
|
fromEmail: Email;
|
|
17
31
|
fromName: string;
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
/*
|
|
34
|
+
* OAuth 2.0 fields. Used by:
|
|
35
|
+
* - SMTP transport when authType === OAuth (XOAUTH2)
|
|
36
|
+
* - HTTP-API transports (always — they always authenticate via OAuth2)
|
|
37
|
+
*/
|
|
20
38
|
authType?: SMTPAuthenticationType | undefined;
|
|
21
39
|
clientId?: string | undefined; // OAuth Application Client ID
|
|
22
40
|
clientSecret?: string | undefined; // OAuth Application Client Secret
|
|
@@ -47,11 +47,13 @@ enum EmailTemplateType {
|
|
|
47
47
|
AlertEpisodeOwnerStateChanged = "AlertEpisodeOwnerStateChanged.hbs",
|
|
48
48
|
AlertEpisodeOwnerNotePosted = "AlertEpisodeOwnerNotePosted.hbs",
|
|
49
49
|
AlertEpisodeOwnerResourceCreated = "AlertEpisodeOwnerResourceCreated.hbs",
|
|
50
|
+
AlertEpisodeOwnerAlertAdded = "AlertEpisodeOwnerAlertAdded.hbs",
|
|
50
51
|
|
|
51
52
|
IncidentEpisodeOwnerAdded = "IncidentEpisodeOwnerAdded.hbs",
|
|
52
53
|
IncidentEpisodeOwnerStateChanged = "IncidentEpisodeOwnerStateChanged.hbs",
|
|
53
54
|
IncidentEpisodeOwnerNotePosted = "IncidentEpisodeOwnerNotePosted.hbs",
|
|
54
55
|
IncidentEpisodeOwnerResourceCreated = "IncidentEpisodeOwnerResourceCreated.hbs",
|
|
56
|
+
IncidentEpisodeOwnerIncidentAdded = "IncidentEpisodeOwnerIncidentAdded.hbs",
|
|
55
57
|
|
|
56
58
|
SubscriberEpisodeCreated = "SubscriberEpisodeCreated.hbs",
|
|
57
59
|
SubscriberEpisodeStateChanged = "SubscriberEpisodeStateChanged.hbs",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport type for delivering email.
|
|
3
|
+
*
|
|
4
|
+
* - SMTP: traditional SMTP server (with optional XOAUTH2 / username+password / no auth)
|
|
5
|
+
* - MicrosoftGraph: Microsoft 365 mailbox via Microsoft Graph API
|
|
6
|
+
* (POST /v1.0/users/{sender}/sendMail). Required when the tenant has SMTP AUTH
|
|
7
|
+
* disabled — the Azure AD app only needs Mail.Send (application) permission.
|
|
8
|
+
*
|
|
9
|
+
* Adding a new HTTP-API provider (Gmail, SES, etc.) means adding a new enum value
|
|
10
|
+
* and a matching MailProvider implementation. See
|
|
11
|
+
* App/FeatureSet/Notification/Services/MailProviders.
|
|
12
|
+
*/
|
|
13
|
+
enum MailTransportType {
|
|
14
|
+
SMTP = "SMTP",
|
|
15
|
+
MicrosoftGraph = "Microsoft Graph",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default MailTransportType;
|
|
@@ -19,6 +19,7 @@ enum NotificationSettingEventType {
|
|
|
19
19
|
SEND_ALERT_EPISODE_NOTE_POSTED_OWNER_NOTIFICATION = "Send alert episode note posted notification when I am the owner of the alert episode",
|
|
20
20
|
SEND_ALERT_EPISODE_STATE_CHANGED_OWNER_NOTIFICATION = "Send alert episode state changed notification when I am the owner of the alert episode",
|
|
21
21
|
SEND_ALERT_EPISODE_OWNER_ADDED_NOTIFICATION = "Send notification when I am added as a owner to the alert episode",
|
|
22
|
+
SEND_ALERT_ADDED_TO_EPISODE_OWNER_NOTIFICATION = "Send notification when a new alert is added to an alert episode I own",
|
|
22
23
|
|
|
23
24
|
// Incident Episodes
|
|
24
25
|
|
|
@@ -26,6 +27,7 @@ enum NotificationSettingEventType {
|
|
|
26
27
|
SEND_INCIDENT_EPISODE_NOTE_POSTED_OWNER_NOTIFICATION = "Send incident episode note posted notification when I am the owner of the incident episode",
|
|
27
28
|
SEND_INCIDENT_EPISODE_STATE_CHANGED_OWNER_NOTIFICATION = "Send incident episode state changed notification when I am the owner of the incident episode",
|
|
28
29
|
SEND_INCIDENT_EPISODE_OWNER_ADDED_NOTIFICATION = "Send notification when I am added as a owner to the incident episode",
|
|
30
|
+
SEND_INCIDENT_ADDED_TO_EPISODE_OWNER_NOTIFICATION = "Send notification when a new incident is added to an incident episode I own",
|
|
29
31
|
|
|
30
32
|
// Monitors
|
|
31
33
|
SEND_MONITOR_OWNER_ADDED_NOTIFICATION = "Send notification when I am added as a owner to the monitor",
|
|
@@ -18,10 +18,12 @@ type TemplateIdsMap = {
|
|
|
18
18
|
readonly AlertEpisodeNotePostedOwnerNotification: "oneuptime_alert_episode_note_posted_owner_notification";
|
|
19
19
|
readonly AlertEpisodeStateChangedOwnerNotification: "oneuptime_alert_episode_state_changed_owner_notification";
|
|
20
20
|
readonly AlertEpisodeOwnerAddedNotification: "oneuptime_alert_episode_owner_added_notification";
|
|
21
|
+
readonly AlertAddedToEpisodeOwnerNotification: "oneuptime_alert_added_to_episode_owner_notification";
|
|
21
22
|
readonly IncidentEpisodeCreatedOwnerNotification: "oneuptime_incident_episode_created_owner_notification";
|
|
22
23
|
readonly IncidentEpisodeNotePostedOwnerNotification: "oneuptime_incident_episode_note_posted_owner_notification";
|
|
23
24
|
readonly IncidentEpisodeStateChangedOwnerNotification: "oneuptime_incident_episode_state_changed_owner_notification";
|
|
24
25
|
readonly IncidentEpisodeOwnerAddedNotification: "oneuptime_incident_episode_owner_added_notification";
|
|
26
|
+
readonly IncidentAddedToEpisodeOwnerNotification: "oneuptime_incident_added_to_episode_owner_notification";
|
|
25
27
|
readonly MonitorOwnerAddedNotification: "oneuptime_monitor_owner_added_notification";
|
|
26
28
|
readonly MonitorCreatedOwnerNotification: "oneuptime_monitor_created_owner_notification";
|
|
27
29
|
readonly MonitorStatusChangedOwnerNotification: "oneuptime_monitor_status_changed_owner_notification";
|
|
@@ -75,6 +77,8 @@ const templateIds: TemplateIdsMap = {
|
|
|
75
77
|
"oneuptime_alert_episode_state_changed_owner_notification",
|
|
76
78
|
AlertEpisodeOwnerAddedNotification:
|
|
77
79
|
"oneuptime_alert_episode_owner_added_notification",
|
|
80
|
+
AlertAddedToEpisodeOwnerNotification:
|
|
81
|
+
"oneuptime_alert_added_to_episode_owner_notification",
|
|
78
82
|
IncidentEpisodeCreatedOwnerNotification:
|
|
79
83
|
"oneuptime_incident_episode_created_owner_notification",
|
|
80
84
|
IncidentEpisodeNotePostedOwnerNotification:
|
|
@@ -83,6 +87,8 @@ const templateIds: TemplateIdsMap = {
|
|
|
83
87
|
"oneuptime_incident_episode_state_changed_owner_notification",
|
|
84
88
|
IncidentEpisodeOwnerAddedNotification:
|
|
85
89
|
"oneuptime_incident_episode_owner_added_notification",
|
|
90
|
+
IncidentAddedToEpisodeOwnerNotification:
|
|
91
|
+
"oneuptime_incident_added_to_episode_owner_notification",
|
|
86
92
|
MonitorOwnerAddedNotification: "oneuptime_monitor_owner_added_notification",
|
|
87
93
|
MonitorCreatedOwnerNotification:
|
|
88
94
|
"oneuptime_monitor_created_owner_notification",
|
|
@@ -156,10 +162,12 @@ export const WhatsAppTemplateMessages: WhatsAppTemplateMessagesDefinition = {
|
|
|
156
162
|
[WhatsAppTemplateIds.AlertEpisodeNotePostedOwnerNotification]: `A new note was posted on alert episode #{{episode_number}} ({{episode_title}}). Review the alert episode using {{episode_link}} on the OneUptime dashboard for updates.`,
|
|
157
163
|
[WhatsAppTemplateIds.AlertEpisodeStateChangedOwnerNotification]: `Alert Episode #{{episode_number}} ({{episode_title}}) state changed to {{episode_state}}. Track the alert episode status using {{episode_link}} on the OneUptime dashboard.`,
|
|
158
164
|
[WhatsAppTemplateIds.AlertEpisodeOwnerAddedNotification]: `You have been added as an owner of alert episode #{{episode_number}} ({{episode_title}}). Manage the alert episode using {{episode_link}} on the OneUptime dashboard.`,
|
|
165
|
+
[WhatsAppTemplateIds.AlertAddedToEpisodeOwnerNotification]: `{{alert_count}} new alert(s) were added to alert episode #{{episode_number}} ({{episode_title}}). Review the alert episode using {{episode_link}} on the OneUptime dashboard.`,
|
|
159
166
|
[WhatsAppTemplateIds.IncidentEpisodeCreatedOwnerNotification]: `Incident Episode #{{episode_number}} ({{episode_title}}) has been created for project {{project_name}}. View incident episode details using {{episode_link}} on the OneUptime dashboard.`,
|
|
160
167
|
[WhatsAppTemplateIds.IncidentEpisodeNotePostedOwnerNotification]: `A new note was posted on incident episode #{{episode_number}} ({{episode_title}}). Review the incident episode using {{episode_link}} on the OneUptime dashboard for updates.`,
|
|
161
168
|
[WhatsAppTemplateIds.IncidentEpisodeStateChangedOwnerNotification]: `Incident Episode #{{episode_number}} ({{episode_title}}) state changed to {{episode_state}}. Track the incident episode status using {{episode_link}} on the OneUptime dashboard.`,
|
|
162
169
|
[WhatsAppTemplateIds.IncidentEpisodeOwnerAddedNotification]: `You have been added as an owner of incident episode #{{episode_number}} ({{episode_title}}). Manage the incident episode using {{episode_link}} on the OneUptime dashboard.`,
|
|
170
|
+
[WhatsAppTemplateIds.IncidentAddedToEpisodeOwnerNotification]: `{{incident_count}} new incident(s) were added to incident episode #{{episode_number}} ({{episode_title}}). Review the incident episode using {{episode_link}} on the OneUptime dashboard.`,
|
|
163
171
|
[WhatsAppTemplateIds.MonitorOwnerAddedNotification]: `You have been added as an owner of monitor {{monitor_name}}. Manage the monitor using {{monitor_link}} on the OneUptime dashboard to keep things running.`,
|
|
164
172
|
[WhatsAppTemplateIds.MonitorCreatedOwnerNotification]: `Monitor {{monitor_name}} has been created. Check monitor {{monitor_link}} on the OneUptime dashboard `,
|
|
165
173
|
[WhatsAppTemplateIds.MonitorStatusChangedOwnerNotification]: `Monitor {{monitor_name}} status changed to {{monitor_status}}. Check the monitor status using {{monitor_link}} on the OneUptime dashboard to stay informed.`,
|
|
@@ -203,10 +211,12 @@ export const WhatsAppTemplateLanguage: Record<WhatsAppTemplateId, string> = {
|
|
|
203
211
|
[WhatsAppTemplateIds.AlertEpisodeNotePostedOwnerNotification]: "en",
|
|
204
212
|
[WhatsAppTemplateIds.AlertEpisodeStateChangedOwnerNotification]: "en",
|
|
205
213
|
[WhatsAppTemplateIds.AlertEpisodeOwnerAddedNotification]: "en",
|
|
214
|
+
[WhatsAppTemplateIds.AlertAddedToEpisodeOwnerNotification]: "en",
|
|
206
215
|
[WhatsAppTemplateIds.IncidentEpisodeCreatedOwnerNotification]: "en",
|
|
207
216
|
[WhatsAppTemplateIds.IncidentEpisodeNotePostedOwnerNotification]: "en",
|
|
208
217
|
[WhatsAppTemplateIds.IncidentEpisodeStateChangedOwnerNotification]: "en",
|
|
209
218
|
[WhatsAppTemplateIds.IncidentEpisodeOwnerAddedNotification]: "en",
|
|
219
|
+
[WhatsAppTemplateIds.IncidentAddedToEpisodeOwnerNotification]: "en",
|
|
210
220
|
[WhatsAppTemplateIds.MonitorOwnerAddedNotification]: "en",
|
|
211
221
|
[WhatsAppTemplateIds.MonitorCreatedOwnerNotification]: "en",
|
|
212
222
|
[WhatsAppTemplateIds.MonitorStatusChangedOwnerNotification]: "en",
|
|
@@ -75,7 +75,9 @@ const Accordion: FunctionComponent<ComponentProps> = (
|
|
|
75
75
|
<div className={className}>
|
|
76
76
|
<div>
|
|
77
77
|
<div
|
|
78
|
-
className={`flex justify-between
|
|
78
|
+
className={`flex justify-between ${
|
|
79
|
+
props.description ? "items-start" : "items-center"
|
|
80
|
+
} gap-3 cursor-pointer group/accordion-header rounded-lg -mx-2 px-2 py-2 transition-colors ${
|
|
79
81
|
isOpen ? "" : "hover:bg-gray-50/80"
|
|
80
82
|
}`}
|
|
81
83
|
role="button"
|
|
@@ -87,10 +89,16 @@ const Accordion: FunctionComponent<ComponentProps> = (
|
|
|
87
89
|
}}
|
|
88
90
|
onKeyDown={handleKeyDown}
|
|
89
91
|
>
|
|
90
|
-
<div
|
|
92
|
+
<div
|
|
93
|
+
className={`flex ${
|
|
94
|
+
props.description ? "items-start" : "items-center"
|
|
95
|
+
} min-w-0 flex-1`}
|
|
96
|
+
>
|
|
91
97
|
{props.title && (
|
|
92
98
|
<div
|
|
93
|
-
className={`flex-shrink-0
|
|
99
|
+
className={`flex-shrink-0 ${
|
|
100
|
+
props.description ? "mt-0.5" : ""
|
|
101
|
+
} flex items-center justify-center w-6 h-6 rounded-md transition-all duration-200 ${
|
|
94
102
|
isOpen
|
|
95
103
|
? "bg-gray-900/5 text-gray-700"
|
|
96
104
|
: "text-gray-400 group-hover/accordion-header:bg-gray-900/5 group-hover/accordion-header:text-gray-700"
|
|
@@ -126,7 +134,7 @@ const Accordion: FunctionComponent<ComponentProps> = (
|
|
|
126
134
|
)}
|
|
127
135
|
</div>
|
|
128
136
|
{!isOpen && props.rightElement && (
|
|
129
|
-
<div className="flex-shrink-0
|
|
137
|
+
<div className="flex-shrink-0">{props.rightElement}</div>
|
|
130
138
|
)}
|
|
131
139
|
</div>
|
|
132
140
|
{isOpen && (
|
|
@@ -54,6 +54,7 @@ let AlertEpisodeMember = class AlertEpisodeMember extends BaseModel {
|
|
|
54
54
|
this.matchedRuleId = undefined;
|
|
55
55
|
this.createdByUser = undefined;
|
|
56
56
|
this.createdByUserId = undefined;
|
|
57
|
+
this.isOwnerNotifiedOfAlertAdded = undefined;
|
|
57
58
|
this.deletedByUser = undefined;
|
|
58
59
|
this.deletedByUserId = undefined;
|
|
59
60
|
}
|
|
@@ -588,6 +589,35 @@ __decorate([
|
|
|
588
589
|
}),
|
|
589
590
|
__metadata("design:type", ObjectID)
|
|
590
591
|
], AlertEpisodeMember.prototype, "createdByUserId", void 0);
|
|
592
|
+
__decorate([
|
|
593
|
+
ColumnAccessControl({
|
|
594
|
+
create: [],
|
|
595
|
+
read: [
|
|
596
|
+
Permission.ProjectOwner,
|
|
597
|
+
Permission.ProjectAdmin,
|
|
598
|
+
Permission.ProjectMember,
|
|
599
|
+
Permission.Viewer,
|
|
600
|
+
Permission.AlertAdmin,
|
|
601
|
+
Permission.AlertMember,
|
|
602
|
+
Permission.AlertViewer,
|
|
603
|
+
Permission.ReadAlertEpisodeMember,
|
|
604
|
+
],
|
|
605
|
+
update: [],
|
|
606
|
+
}),
|
|
607
|
+
TableColumn({
|
|
608
|
+
isDefaultValueColumn: true,
|
|
609
|
+
required: true,
|
|
610
|
+
type: TableColumnType.Boolean,
|
|
611
|
+
title: "Is Owner Notified of Alert Added to Episode",
|
|
612
|
+
description: "Has the owner been notified that this alert was added to the episode?",
|
|
613
|
+
}),
|
|
614
|
+
Column({
|
|
615
|
+
type: ColumnType.Boolean,
|
|
616
|
+
nullable: false,
|
|
617
|
+
default: false,
|
|
618
|
+
}),
|
|
619
|
+
__metadata("design:type", Boolean)
|
|
620
|
+
], AlertEpisodeMember.prototype, "isOwnerNotifiedOfAlertAdded", void 0);
|
|
591
621
|
__decorate([
|
|
592
622
|
ColumnAccessControl({
|
|
593
623
|
create: [],
|