@oneuptime/common 7.0.3517 → 7.0.3538

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.
Files changed (115) hide show
  1. package/Models/DatabaseModels/AlertFeed.ts +12 -4
  2. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +2 -0
  3. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.ts +59 -2
  4. package/Models/DatabaseModels/ScheduledMaintenanceFeed.ts +15 -7
  5. package/Models/DatabaseModels/UserNotificationRule.ts +50 -0
  6. package/Models/DatabaseModels/UserOnCallLog.ts +48 -1
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +49 -2
  8. package/Server/API/BillingInvoiceAPI.ts +2 -32
  9. package/Server/API/UserOnCallLogTimelineAPI.ts +32 -8
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1737141420441-MigrationName.ts +131 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Services/AlertFeedService.ts +44 -37
  13. package/Server/Services/AlertInternalNoteService.ts +73 -0
  14. package/Server/Services/AlertOwnerTeamService.ts +112 -0
  15. package/Server/Services/AlertOwnerUserService.ts +114 -0
  16. package/Server/Services/AlertService.ts +121 -0
  17. package/Server/Services/AlertStateTimelineService.ts +52 -0
  18. package/Server/Services/BillingInvoiceService.ts +170 -0
  19. package/Server/Services/IncidentFeedService.ts +59 -54
  20. package/Server/Services/IncidentInternalNoteService.ts +46 -1
  21. package/Server/Services/IncidentPublicNoteService.ts +47 -2
  22. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +32 -14
  23. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +86 -27
  24. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +30 -12
  25. package/Server/Services/ScheduledMaintenanceFeedService.ts +51 -40
  26. package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +75 -0
  27. package/Server/Services/ScheduledMaintenanceOwnerTeamService.ts +116 -0
  28. package/Server/Services/ScheduledMaintenanceOwnerUserService.ts +118 -0
  29. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +74 -1
  30. package/Server/Services/ScheduledMaintenanceService.ts +68 -0
  31. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +30 -0
  32. package/Server/Services/UserNotificationRuleService.ts +450 -67
  33. package/Server/Services/UserOnCallLogService.ts +61 -18
  34. package/Server/Services/UserOnCallLogTimelineService.ts +25 -11
  35. package/Server/Utils/Logger.ts +14 -0
  36. package/Types/Email/EmailTemplateType.ts +1 -0
  37. package/Types/NotificationRule/NotificationRuleType.ts +1 -1
  38. package/UI/Utils/Project.ts +25 -0
  39. package/build/dist/Models/DatabaseModels/AlertFeed.js +12 -4
  40. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  41. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +2 -0
  42. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  43. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js +60 -2
  44. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js.map +1 -1
  45. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceFeed.js +15 -7
  46. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceFeed.js.map +1 -1
  47. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +49 -0
  48. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  49. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +48 -1
  50. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  51. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +50 -2
  52. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  53. package/build/dist/Server/API/BillingInvoiceAPI.js +2 -19
  54. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  55. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +10 -2
  56. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
  57. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1737141420441-MigrationName.js +50 -0
  58. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1737141420441-MigrationName.js.map +1 -0
  59. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  60. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  61. package/build/dist/Server/Services/AlertFeedService.js +43 -35
  62. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  63. package/build/dist/Server/Services/AlertInternalNoteService.js +56 -0
  64. package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
  65. package/build/dist/Server/Services/AlertOwnerTeamService.js +86 -0
  66. package/build/dist/Server/Services/AlertOwnerTeamService.js.map +1 -1
  67. package/build/dist/Server/Services/AlertOwnerUserService.js +89 -0
  68. package/build/dist/Server/Services/AlertOwnerUserService.js.map +1 -1
  69. package/build/dist/Server/Services/AlertService.js +107 -3
  70. package/build/dist/Server/Services/AlertService.js.map +1 -1
  71. package/build/dist/Server/Services/AlertStateTimelineService.js +44 -1
  72. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  73. package/build/dist/Server/Services/BillingInvoiceService.js +123 -0
  74. package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
  75. package/build/dist/Server/Services/IncidentFeedService.js +45 -39
  76. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  77. package/build/dist/Server/Services/IncidentInternalNoteService.js +37 -0
  78. package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
  79. package/build/dist/Server/Services/IncidentPublicNoteService.js +38 -1
  80. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  81. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +18 -8
  82. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  83. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +70 -24
  84. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  85. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +26 -11
  86. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  87. package/build/dist/Server/Services/ScheduledMaintenanceFeedService.js +45 -37
  88. package/build/dist/Server/Services/ScheduledMaintenanceFeedService.js.map +1 -1
  89. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +56 -0
  90. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
  91. package/build/dist/Server/Services/ScheduledMaintenanceOwnerTeamService.js +86 -0
  92. package/build/dist/Server/Services/ScheduledMaintenanceOwnerTeamService.js.map +1 -1
  93. package/build/dist/Server/Services/ScheduledMaintenanceOwnerUserService.js +89 -0
  94. package/build/dist/Server/Services/ScheduledMaintenanceOwnerUserService.js.map +1 -1
  95. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +56 -0
  96. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  97. package/build/dist/Server/Services/ScheduledMaintenanceService.js +57 -0
  98. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  99. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +24 -0
  100. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  101. package/build/dist/Server/Services/UserNotificationRuleService.js +334 -67
  102. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  103. package/build/dist/Server/Services/UserOnCallLogService.js +66 -27
  104. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  105. package/build/dist/Server/Services/UserOnCallLogTimelineService.js +16 -5
  106. package/build/dist/Server/Services/UserOnCallLogTimelineService.js.map +1 -1
  107. package/build/dist/Server/Utils/Logger.js +11 -0
  108. package/build/dist/Server/Utils/Logger.js.map +1 -1
  109. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  110. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  111. package/build/dist/Types/NotificationRule/NotificationRuleType.js +1 -1
  112. package/build/dist/Types/NotificationRule/NotificationRuleType.js.map +1 -1
  113. package/build/dist/UI/Utils/Project.js +11 -0
  114. package/build/dist/UI/Utils/Project.js.map +1 -1
  115. package/package.json +2 -2
@@ -10,6 +10,11 @@ import Model, {
10
10
  InvoiceStatus,
11
11
  } from "Common/Models/DatabaseModels/BillingInvoice";
12
12
  import Project from "Common/Models/DatabaseModels/Project";
13
+ import SubscriptionStatus from "../../Types/Billing/SubscriptionStatus";
14
+ import ObjectID from "../../Types/ObjectID";
15
+ import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
16
+ import logger from "../Utils/Logger";
17
+ import OneUptimeDate from "../../Types/Date";
13
18
 
14
19
  export class Service extends DatabaseService<Model> {
15
20
  public constructor() {
@@ -17,6 +22,166 @@ export class Service extends DatabaseService<Model> {
17
22
  this.setDoNotAllowDelete(true);
18
23
  }
19
24
 
25
+ public async refreshSubscriptionStatus(data: {
26
+ projectId: ObjectID;
27
+ }): Promise<void> {
28
+ let mutex: SemaphoreMutex | null = null;
29
+
30
+ try {
31
+ mutex = await Semaphore.lock({
32
+ key: data.projectId.toString(),
33
+ namespace: "BillingInoviceService.refreshSubscriptionStatus",
34
+ lockTimeout: 15000,
35
+ acquireTimeout: 20000,
36
+ });
37
+ logger.debug(
38
+ "Mutex acquired - " +
39
+ data.projectId.toString() +
40
+ " at " +
41
+ OneUptimeDate.getCurrentDateAsFormattedString(),
42
+ );
43
+ } catch (err) {
44
+ logger.debug(
45
+ "Mutex acquire failed - " +
46
+ data.projectId.toString() +
47
+ " at " +
48
+ OneUptimeDate.getCurrentDateAsFormattedString(),
49
+ );
50
+ logger.error(err);
51
+ }
52
+
53
+ let project: Project | null = await ProjectService.findOneById({
54
+ id: data.projectId,
55
+ props: {
56
+ isRoot: true,
57
+ },
58
+ select: {
59
+ _id: true,
60
+ paymentProviderCustomerId: true,
61
+ paymentProviderSubscriptionId: true,
62
+ paymentProviderMeteredSubscriptionId: true,
63
+ },
64
+ });
65
+
66
+ // refresh the subscription status. This is a hack to ensure that the subscription status is always up to date.
67
+ // This is because the subscription status can change at any time and we need to ensure that the subscription status is always up to date.
68
+
69
+ if (!project) {
70
+ throw new BadDataException("Project not found");
71
+ }
72
+
73
+ if (!project.paymentProviderCustomerId) {
74
+ throw new BadDataException("Payment provider customer id not found.");
75
+ }
76
+
77
+ let subscriptionState: SubscriptionStatus =
78
+ await BillingService.getSubscriptionStatus(
79
+ project.paymentProviderSubscriptionId as string,
80
+ );
81
+
82
+ let meteredSubscriptionState: SubscriptionStatus =
83
+ await BillingService.getSubscriptionStatus(
84
+ project.paymentProviderMeteredSubscriptionId as string,
85
+ );
86
+
87
+ // update the project.
88
+
89
+ await ProjectService.updateOneById({
90
+ id: project.id!,
91
+ data: {
92
+ paymentProviderSubscriptionStatus: subscriptionState,
93
+ paymentProviderMeteredSubscriptionStatus: meteredSubscriptionState,
94
+ },
95
+ props: {
96
+ isRoot: true,
97
+ ignoreHooks: true,
98
+ },
99
+ });
100
+
101
+ if (
102
+ meteredSubscriptionState === SubscriptionStatus.Canceled ||
103
+ subscriptionState === SubscriptionStatus.Canceled
104
+ ) {
105
+ // check if all invoices are paid. If yes, then reactivate the subscription.
106
+
107
+ const invoices: Array<Invoice> = await BillingService.getInvoices(
108
+ project.paymentProviderCustomerId,
109
+ );
110
+
111
+ let allInvoicesPaid: boolean = true;
112
+
113
+ for (const invoice of invoices) {
114
+ if (
115
+ invoice.status === InvoiceStatus.Open ||
116
+ invoice.status === InvoiceStatus.Uncollectible
117
+ ) {
118
+ allInvoicesPaid = false;
119
+ break;
120
+ }
121
+ }
122
+
123
+ if (allInvoicesPaid) {
124
+ await ProjectService.reactiveSubscription(project.id!);
125
+ project = await ProjectService.findOneById({
126
+ id: data.projectId,
127
+ props: {
128
+ isRoot: true,
129
+ },
130
+ select: {
131
+ _id: true,
132
+ paymentProviderCustomerId: true,
133
+ paymentProviderSubscriptionId: true,
134
+ paymentProviderMeteredSubscriptionId: true,
135
+ },
136
+ });
137
+
138
+ if (!project) {
139
+ throw new BadDataException("Project not found");
140
+ }
141
+
142
+ subscriptionState = await BillingService.getSubscriptionStatus(
143
+ project.paymentProviderSubscriptionId as string,
144
+ );
145
+
146
+ meteredSubscriptionState = await BillingService.getSubscriptionStatus(
147
+ project.paymentProviderMeteredSubscriptionId as string,
148
+ );
149
+
150
+ await ProjectService.updateOneById({
151
+ id: project.id!,
152
+ data: {
153
+ paymentProviderSubscriptionStatus: subscriptionState,
154
+ paymentProviderMeteredSubscriptionStatus: meteredSubscriptionState,
155
+ },
156
+ props: {
157
+ isRoot: true,
158
+ ignoreHooks: true,
159
+ },
160
+ });
161
+ }
162
+ }
163
+
164
+ if (mutex) {
165
+ try {
166
+ await Semaphore.release(mutex);
167
+ logger.debug(
168
+ "Mutex released - " +
169
+ data.projectId.toString() +
170
+ " at " +
171
+ OneUptimeDate.getCurrentDateAsFormattedString(),
172
+ );
173
+ } catch (err) {
174
+ logger.debug(
175
+ "Mutex release failed - " +
176
+ data.projectId.toString() +
177
+ " at " +
178
+ OneUptimeDate.getCurrentDateAsFormattedString(),
179
+ );
180
+ logger.error(err);
181
+ }
182
+ }
183
+ }
184
+
20
185
  protected override async onBeforeFind(
21
186
  findBy: FindBy<Model>,
22
187
  ): Promise<OnFind<Model>> {
@@ -37,6 +202,11 @@ export class Service extends DatabaseService<Model> {
37
202
  },
38
203
  });
39
204
 
205
+ // refresh the subscription status. This is a hack to ensure that the subscription status is always up to date.
206
+ // This is because the subscription status can change at any time and we need to ensure that the subscription status is always up to date.
207
+
208
+ await this.refreshSubscriptionStatus({ projectId: findBy.props.tenantId! });
209
+
40
210
  if (!project) {
41
211
  throw new BadDataException("Project not found");
42
212
  }
@@ -28,61 +28,66 @@ export class Service extends DatabaseService<IncidentFeed> {
28
28
  displayColor?: Color | undefined;
29
29
  userId?: ObjectID | undefined;
30
30
  postedAt?: Date | undefined;
31
- }): Promise<IncidentFeed> {
32
- logger.debug("IncidentFeedService.createIncidentFeed");
33
- logger.debug(data);
34
-
35
- const incidentFeed: IncidentFeed = new IncidentFeed();
36
-
37
- if (!data.incidentId) {
38
- throw new BadDataException("Incident ID is required");
39
- }
40
-
41
- if (!data.feedInfoInMarkdown) {
42
- throw new BadDataException("Log in markdown is required");
43
- }
44
-
45
- if (!data.incidentFeedEventType) {
46
- throw new BadDataException("Incident log event is required");
47
- }
48
-
49
- if (!data.projectId) {
50
- throw new BadDataException("Project ID is required");
51
- }
52
-
53
- if (!data.displayColor) {
54
- data.displayColor = Blue500;
31
+ }): Promise<void> {
32
+ try {
33
+ logger.debug("IncidentFeedService.createIncidentFeed");
34
+ logger.debug(data);
35
+
36
+ const incidentFeed: IncidentFeed = new IncidentFeed();
37
+
38
+ if (!data.incidentId) {
39
+ throw new BadDataException("Incident ID is required");
40
+ }
41
+
42
+ if (!data.feedInfoInMarkdown) {
43
+ throw new BadDataException("Log in markdown is required");
44
+ }
45
+
46
+ if (!data.incidentFeedEventType) {
47
+ throw new BadDataException("Incident log event is required");
48
+ }
49
+
50
+ if (!data.projectId) {
51
+ throw new BadDataException("Project ID is required");
52
+ }
53
+
54
+ if (!data.displayColor) {
55
+ data.displayColor = Blue500;
56
+ }
57
+
58
+ incidentFeed.displayColor = data.displayColor;
59
+ incidentFeed.incidentId = data.incidentId;
60
+ incidentFeed.feedInfoInMarkdown = data.feedInfoInMarkdown;
61
+ incidentFeed.incidentFeedEventType = data.incidentFeedEventType;
62
+ incidentFeed.projectId = data.projectId;
63
+
64
+ if (!data.postedAt) {
65
+ incidentFeed.postedAt = OneUptimeDate.getCurrentDate();
66
+ }
67
+
68
+ if (data.userId) {
69
+ incidentFeed.userId = data.userId;
70
+ }
71
+
72
+ if (data.moreInformationInMarkdown) {
73
+ incidentFeed.moreInformationInMarkdown = data.moreInformationInMarkdown;
74
+ }
75
+
76
+ const createdIncidentFeed: IncidentFeed = await this.create({
77
+ data: incidentFeed,
78
+ props: {
79
+ isRoot: true,
80
+ },
81
+ });
82
+
83
+ logger.debug("Incident Feed created");
84
+ logger.debug(createdIncidentFeed);
85
+ } catch (e) {
86
+ logger.error("Error in creating incident feed");
87
+ logger.error(e);
88
+
89
+ // we dont throw this error as it is not a critical error
55
90
  }
56
-
57
- incidentFeed.displayColor = data.displayColor;
58
- incidentFeed.incidentId = data.incidentId;
59
- incidentFeed.feedInfoInMarkdown = data.feedInfoInMarkdown;
60
- incidentFeed.incidentFeedEventType = data.incidentFeedEventType;
61
- incidentFeed.projectId = data.projectId;
62
-
63
- if (!data.postedAt) {
64
- incidentFeed.postedAt = OneUptimeDate.getCurrentDate();
65
- }
66
-
67
- if (data.userId) {
68
- incidentFeed.userId = data.userId;
69
- }
70
-
71
- if (data.moreInformationInMarkdown) {
72
- incidentFeed.moreInformationInMarkdown = data.moreInformationInMarkdown;
73
- }
74
-
75
- const createdIncidentFeed: IncidentFeed = await this.create({
76
- data: incidentFeed,
77
- props: {
78
- isRoot: true,
79
- },
80
- });
81
-
82
- logger.debug("Incident Feed created");
83
- logger.debug(createdIncidentFeed);
84
-
85
- return createdIncidentFeed;
86
91
  }
87
92
  }
88
93
 
@@ -1,10 +1,11 @@
1
1
  import ObjectID from "../../Types/ObjectID";
2
2
  import DatabaseService from "./DatabaseService";
3
3
  import Model from "Common/Models/DatabaseModels/IncidentInternalNote";
4
- import { OnCreate } from "../Types/Database/Hooks";
4
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
5
5
  import IncidentFeedService from "./IncidentFeedService";
6
6
  import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
7
7
  import { Blue500 } from "../../Types/BrandColors";
8
+ import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
8
9
 
9
10
  export class Service extends DatabaseService<Model> {
10
11
  public constructor() {
@@ -33,6 +34,50 @@ ${createdItem.note}
33
34
 
34
35
  return createdItem;
35
36
  }
37
+
38
+ public override async onUpdateSuccess(
39
+ onUpdate: OnUpdate<Model>,
40
+ _updatedItemIds: Array<ObjectID>,
41
+ ): Promise<OnUpdate<Model>> {
42
+ if (onUpdate.updateBy.data.note) {
43
+ const updatedItems: Array<Model> = await this.findBy({
44
+ query: onUpdate.updateBy.query,
45
+ limit: LIMIT_PER_PROJECT,
46
+ skip: 0,
47
+ props: {
48
+ isRoot: true,
49
+ },
50
+ select: {
51
+ incidentId: true,
52
+ projectId: true,
53
+ note: true,
54
+ createdByUserId: true,
55
+ createdByUser: {
56
+ _id: true,
57
+ },
58
+ },
59
+ });
60
+
61
+ const userId: ObjectID | null | undefined =
62
+ onUpdate.updateBy.props.userId;
63
+
64
+ for (const updatedItem of updatedItems) {
65
+ await IncidentFeedService.createIncidentFeed({
66
+ incidentId: updatedItem.incidentId!,
67
+ projectId: updatedItem.projectId!,
68
+ incidentFeedEventType: IncidentFeedEventType.PrivateNote,
69
+ displayColor: Blue500,
70
+ userId: userId || undefined,
71
+
72
+ feedInfoInMarkdown: `**Updated Internal / Private Note**
73
+
74
+ ${updatedItem.note}
75
+ `,
76
+ });
77
+ }
78
+ }
79
+ return onUpdate;
80
+ }
36
81
  }
37
82
 
38
83
  export default new Service();
@@ -1,12 +1,13 @@
1
1
  import CreateBy from "../Types/Database/CreateBy";
2
- import { OnCreate } from "../Types/Database/Hooks";
2
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
3
3
  import DatabaseService from "./DatabaseService";
4
4
  import OneUptimeDate from "../../Types/Date";
5
5
  import Model from "Common/Models/DatabaseModels/IncidentPublicNote";
6
6
  import IncidentFeedService from "./IncidentFeedService";
7
7
  import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
8
- import { Indigo500 } from "../../Types/BrandColors";
8
+ import { Blue500, Indigo500 } from "../../Types/BrandColors";
9
9
  import ObjectID from "../../Types/ObjectID";
10
+ import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
10
11
 
11
12
  export class Service extends DatabaseService<Model> {
12
13
  public constructor() {
@@ -47,6 +48,50 @@ ${createdItem.note}
47
48
 
48
49
  return createdItem;
49
50
  }
51
+
52
+ public override async onUpdateSuccess(
53
+ onUpdate: OnUpdate<Model>,
54
+ _updatedItemIds: Array<ObjectID>,
55
+ ): Promise<OnUpdate<Model>> {
56
+ if (onUpdate.updateBy.data.note) {
57
+ const updatedItems: Array<Model> = await this.findBy({
58
+ query: onUpdate.updateBy.query,
59
+ limit: LIMIT_PER_PROJECT,
60
+ skip: 0,
61
+ props: {
62
+ isRoot: true,
63
+ },
64
+ select: {
65
+ incidentId: true,
66
+ projectId: true,
67
+ note: true,
68
+ createdByUserId: true,
69
+ createdByUser: {
70
+ _id: true,
71
+ },
72
+ },
73
+ });
74
+
75
+ const userId: ObjectID | null | undefined =
76
+ onUpdate.updateBy.props.userId;
77
+
78
+ for (const updatedItem of updatedItems) {
79
+ await IncidentFeedService.createIncidentFeed({
80
+ incidentId: updatedItem.incidentId!,
81
+ projectId: updatedItem.projectId!,
82
+ incidentFeedEventType: IncidentFeedEventType.PublicNote,
83
+ displayColor: Blue500,
84
+ userId: userId || undefined,
85
+
86
+ feedInfoInMarkdown: `**Updated Public Note**
87
+
88
+ ${updatedItem.note}
89
+ `,
90
+ });
91
+ }
92
+ }
93
+ return onUpdate;
94
+ }
50
95
  }
51
96
 
52
97
  export default new Service();
@@ -30,6 +30,7 @@ import OnCallDutyPolicyEscalationRuleTeam from "Common/Models/DatabaseModels/OnC
30
30
  import OnCallDutyPolicyEscalationRuleUser from "Common/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser";
31
31
  import OnCallDutyPolicyExecutionLogTimeline from "Common/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline";
32
32
  import User from "Common/Models/DatabaseModels/User";
33
+ import logger from "../Utils/Logger";
33
34
 
34
35
  export class Service extends DatabaseService<Model> {
35
36
  public async startRuleExecution(
@@ -37,12 +38,13 @@ export class Service extends DatabaseService<Model> {
37
38
  options: {
38
39
  projectId: ObjectID;
39
40
  triggeredByIncidentId?: ObjectID | undefined;
41
+ triggeredByAlertId?: ObjectID | undefined;
40
42
  userNotificationEventType: UserNotificationEventType;
41
43
  onCallPolicyExecutionLogId: ObjectID;
42
44
  onCallPolicyId: ObjectID;
43
45
  },
44
46
  ): Promise<void> {
45
- // add log timeline.
47
+ logger.debug(`Starting rule execution for ruleId: ${ruleId.toString()}`);
46
48
 
47
49
  const rule: Model | null = await this.findOneById({
48
50
  id: ruleId,
@@ -62,6 +64,8 @@ export class Service extends DatabaseService<Model> {
62
64
  );
63
65
  }
64
66
 
67
+ logger.debug(`Found rule: ${JSON.stringify(rule)}`);
68
+
65
69
  await OnCallDutyPolicyExecutionLogService.updateOneById({
66
70
  id: options.onCallPolicyExecutionLogId,
67
71
  data: {
@@ -75,6 +79,8 @@ export class Service extends DatabaseService<Model> {
75
79
  },
76
80
  });
77
81
 
82
+ logger.debug(`Updated execution log for ruleId: ${ruleId.toString()}`);
83
+
78
84
  type GetNewLogFunction = () => OnCallDutyPolicyExecutionLogTimeline;
79
85
 
80
86
  const getNewLog: GetNewLogFunction =
@@ -92,6 +98,10 @@ export class Service extends DatabaseService<Model> {
92
98
  log.triggeredByIncidentId = options.triggeredByIncidentId;
93
99
  }
94
100
 
101
+ if (options.triggeredByAlertId) {
102
+ log.triggeredByAlertId = options.triggeredByAlertId;
103
+ }
104
+
95
105
  return log;
96
106
  };
97
107
 
@@ -105,6 +115,16 @@ export class Service extends DatabaseService<Model> {
105
115
  );
106
116
  }
107
117
 
118
+ if (
119
+ UserNotificationEventType.AlertCreated ===
120
+ options.userNotificationEventType &&
121
+ !options.triggeredByAlertId
122
+ ) {
123
+ throw new BadDataException(
124
+ "triggeredByAlertId is required when userNotificationEventType is IncidentCreated",
125
+ );
126
+ }
127
+
108
128
  const usersInRule: Array<OnCallDutyPolicyEscalationRuleUser> =
109
129
  await OnCallDutyPolicyEscalationRuleUserService.findBy({
110
130
  query: {
@@ -120,6 +140,8 @@ export class Service extends DatabaseService<Model> {
120
140
  },
121
141
  });
122
142
 
143
+ logger.debug(`Found users in rule: ${JSON.stringify(usersInRule)}`);
144
+
123
145
  const teamsInRule: Array<OnCallDutyPolicyEscalationRuleTeam> =
124
146
  await OnCallDutyPolicyEscalationRuleTeamService.findBy({
125
147
  query: {
@@ -135,6 +157,8 @@ export class Service extends DatabaseService<Model> {
135
157
  },
136
158
  });
137
159
 
160
+ logger.debug(`Found teams in rule: ${JSON.stringify(teamsInRule)}`);
161
+
138
162
  const schedulesInRule: Array<OnCallDutyPolicyEscalationRuleSchedule> =
139
163
  await OnCallDutyPolicyEscalationRuleScheduleService.findBy({
140
164
  query: {
@@ -150,7 +174,7 @@ export class Service extends DatabaseService<Model> {
150
174
  },
151
175
  });
152
176
 
153
- // get unique users and notify all the users.
177
+ logger.debug(`Found schedules in rule: ${JSON.stringify(schedulesInRule)}`);
154
178
 
155
179
  type StartUserNotificationRuleExecutionFunction = (
156
180
  userId: ObjectID,
@@ -164,7 +188,9 @@ export class Service extends DatabaseService<Model> {
164
188
  teamId: ObjectID | null,
165
189
  scheduleId: ObjectID | null,
166
190
  ): Promise<void> => {
167
- // no users in this rule. Skipping.
191
+ logger.debug(
192
+ `Starting notification rule execution for userId: ${userId.toString()}`,
193
+ );
168
194
  let log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
169
195
  log.statusMessage = "Sending notification to user.";
170
196
  log.status = OnCallDutyExecutionLogTimelineStatus.Executing;
@@ -189,6 +215,7 @@ export class Service extends DatabaseService<Model> {
189
215
  {
190
216
  userNotificationEventType: options.userNotificationEventType!,
191
217
  triggeredByIncidentId: options.triggeredByIncidentId || undefined,
218
+ triggeredByAlertId: options.triggeredByAlertId || undefined,
192
219
  onCallPolicyExecutionLogId: options.onCallPolicyExecutionLogId,
193
220
  onCallPolicyId: options.onCallPolicyId,
194
221
  onCallPolicyEscalationRuleId: ruleId,
@@ -220,7 +247,6 @@ export class Service extends DatabaseService<Model> {
220
247
  null,
221
248
  );
222
249
  } else {
223
- // no users in this rule. Skipping.
224
250
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
225
251
  log.statusMessage =
226
252
  "Skipped because notification sent to this user already.";
@@ -247,7 +273,6 @@ export class Service extends DatabaseService<Model> {
247
273
  uniqueUserIds.push(userRule.userId!);
248
274
  await startUserNotificationRuleExecution(userRule.userId!, null, null);
249
275
  } else {
250
- // no users in this rule. Skipping.
251
276
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
252
277
  log.statusMessage =
253
278
  "Skipped because notification sent to this user already.";
@@ -264,23 +289,16 @@ export class Service extends DatabaseService<Model> {
264
289
  }
265
290
 
266
291
  for (const scheduleRule of schedulesInRule) {
267
- // get layers and users in this schedule and find a user to notify.
268
-
269
292
  const userIdInSchedule: ObjectID | null =
270
293
  await OnCallDutyPolicyScheduleService.getCurrentUserIdInSchedule(
271
294
  scheduleRule.onCallDutyPolicyScheduleId!,
272
295
  );
273
296
 
274
297
  if (!userIdInSchedule) {
275
- // no user active in this schedule. Skipping.
276
-
277
298
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
278
-
279
299
  log.statusMessage =
280
300
  "Skipped because no active users are found in this schedule.";
281
-
282
301
  log.status = OnCallDutyExecutionLogTimelineStatus.Skipped;
283
-
284
302
  log.onCallDutyScheduleId = scheduleRule.onCallDutyPolicyScheduleId!;
285
303
 
286
304
  await OnCallDutyPolicyExecutionLogTimelineService.create({
@@ -305,7 +323,6 @@ export class Service extends DatabaseService<Model> {
305
323
  scheduleRule.onCallDutyPolicyScheduleId!,
306
324
  );
307
325
  } else {
308
- // no users in this rule. Skipping.
309
326
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
310
327
  log.statusMessage =
311
328
  "Skipped because notification sent to this user already.";
@@ -323,7 +340,6 @@ export class Service extends DatabaseService<Model> {
323
340
  }
324
341
 
325
342
  if (uniqueUserIds.length === 0) {
326
- // no users in this rule. Skipping.
327
343
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
328
344
  log.statusMessage = "Skipped because no users in this rule.";
329
345
  log.status = OnCallDutyExecutionLogTimelineStatus.Skipped;
@@ -335,6 +351,8 @@ export class Service extends DatabaseService<Model> {
335
351
  },
336
352
  });
337
353
  }
354
+
355
+ logger.debug(`Completed rule execution for ruleId: ${ruleId.toString()}`);
338
356
  }
339
357
 
340
358
  public constructor() {