@oneuptime/common 7.0.4137 → 7.0.4142

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.
@@ -37,7 +37,7 @@ export class Service extends DatabaseService<MonitorStatusTimeline> {
37
37
  try {
38
38
  mutex = await Semaphore.lock({
39
39
  key: createBy.data.monitorId.toString(),
40
- namespace: "MonitorStatusTimeline.onBeforeCreate",
40
+ namespace: "MonitorStatusTimeline.create",
41
41
  });
42
42
  } catch (e) {
43
43
  logger.error(e);
@@ -73,6 +73,13 @@ export class Service extends DatabaseService<MonitorStatusTimeline> {
73
73
  }
74
74
  }
75
75
 
76
+ const monitorStatusId: ObjectID | undefined | null =
77
+ createBy.data.monitorStatusId || createBy.data.monitorStatus?.id;
78
+
79
+ if (!monitorStatusId) {
80
+ throw new BadDataException("monitorStatusId is null");
81
+ }
82
+
76
83
  const stateBeforeThis: MonitorStatusTimeline | null = await this.findOneBy({
77
84
  query: {
78
85
  monitorId: createBy.data.monitorId,
@@ -100,6 +107,20 @@ export class Service extends DatabaseService<MonitorStatusTimeline> {
100
107
  createBy.data.isOwnerNotified = true;
101
108
  }
102
109
 
110
+ // check if this new state and the previous state are same.
111
+ // if yes, then throw bad data exception.
112
+
113
+ if (stateBeforeThis && stateBeforeThis.monitorStatusId && monitorStatusId) {
114
+ if (
115
+ stateBeforeThis.monitorStatusId.toString() ===
116
+ monitorStatusId.toString()
117
+ ) {
118
+ throw new BadDataException(
119
+ "Monitor Status cannot be same as previous status.",
120
+ );
121
+ }
122
+ }
123
+
103
124
  const stateAfterThis: MonitorStatusTimeline | null = await this.findOneBy({
104
125
  query: {
105
126
  monitorId: createBy.data.monitorId,
@@ -123,6 +144,19 @@ export class Service extends DatabaseService<MonitorStatusTimeline> {
123
144
  createBy.data.endsAt = stateAfterThis.startsAt;
124
145
  }
125
146
 
147
+ // check if this new state and the previous state are same.
148
+ // if yes, then throw bad data exception.
149
+
150
+ if (stateAfterThis && stateAfterThis.monitorStatusId && monitorStatusId) {
151
+ if (
152
+ stateAfterThis.monitorStatusId.toString() === monitorStatusId.toString()
153
+ ) {
154
+ throw new BadDataException(
155
+ "Monitor Status cannot be same as next status.",
156
+ );
157
+ }
158
+ }
159
+
126
160
  logger.debug("State After this");
127
161
  logger.debug(stateAfterThis);
128
162
 
@@ -29,6 +29,7 @@ import logger from "../Utils/Logger";
29
29
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
30
30
  import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
31
31
  import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
32
+ import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
32
33
 
33
34
  export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline> {
34
35
  public constructor() {
@@ -93,96 +94,197 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
93
94
  throw new BadDataException("scheduledMaintenanceId is null");
94
95
  }
95
96
 
96
- if (!createBy.data.startsAt) {
97
- createBy.data.startsAt = OneUptimeDate.getCurrentDate();
98
- }
97
+ let mutex: SemaphoreMutex | null = null;
99
98
 
100
- const stateBeforeThis: ScheduledMaintenanceStateTimeline | null =
101
- await this.findOneBy({
102
- query: {
103
- scheduledMaintenanceId: createBy.data.scheduledMaintenanceId,
104
- startsAt: QueryHelper.lessThanEqualTo(createBy.data.startsAt),
105
- },
106
- sort: {
107
- startsAt: SortOrder.Descending,
108
- },
109
- props: {
110
- isRoot: true,
111
- },
112
- select: {
113
- scheduledMaintenanceStateId: true,
114
- startsAt: true,
115
- endsAt: true,
116
- },
117
- });
99
+ try {
100
+ try {
101
+ mutex = await Semaphore.lock({
102
+ key: createBy.data.scheduledMaintenanceId.toString(),
103
+ namespace: "ScheduledMaintenanceStateTimeline.create",
104
+ });
105
+ } catch (err) {
106
+ logger.error(err);
107
+ }
118
108
 
119
- // If this is the first state, then do not notify the owner.
120
- if (!stateBeforeThis) {
121
- // since this is the first status, do not notify the owner.
122
- createBy.data.isOwnerNotified = true;
123
- }
109
+ if (!createBy.data.startsAt) {
110
+ createBy.data.startsAt = OneUptimeDate.getCurrentDate();
111
+ }
124
112
 
125
- const stateAfterThis: ScheduledMaintenanceStateTimeline | null =
126
- await this.findOneBy({
127
- query: {
128
- scheduledMaintenanceId: createBy.data.scheduledMaintenanceId,
129
- startsAt: QueryHelper.greaterThan(createBy.data.startsAt),
130
- },
131
- sort: {
132
- startsAt: SortOrder.Ascending,
133
- },
134
- props: {
135
- isRoot: true,
136
- },
137
- select: {
138
- scheduledMaintenanceStateId: true,
139
- startsAt: true,
140
- endsAt: true,
141
- },
142
- });
113
+ const scheduledMaintenanceStateId: ObjectID | undefined | null =
114
+ createBy.data.scheduledMaintenanceStateId ||
115
+ createBy.data.scheduledMaintenanceState?.id;
143
116
 
144
- // compute ends at. It's the start of the next status.
145
- if (stateAfterThis && stateAfterThis.startsAt) {
146
- createBy.data.endsAt = stateAfterThis.startsAt;
147
- }
117
+ if (!scheduledMaintenanceStateId) {
118
+ throw new BadDataException("scheduledMaintenanceStateId is null");
119
+ }
120
+
121
+ const stateBeforeThis: ScheduledMaintenanceStateTimeline | null =
122
+ await this.findOneBy({
123
+ query: {
124
+ scheduledMaintenanceId: createBy.data.scheduledMaintenanceId,
125
+ startsAt: QueryHelper.lessThanEqualTo(createBy.data.startsAt),
126
+ },
127
+ sort: {
128
+ startsAt: SortOrder.Descending,
129
+ },
130
+ props: {
131
+ isRoot: true,
132
+ },
133
+ select: {
134
+ scheduledMaintenanceStateId: true,
135
+ scheduledMaintenanceState: {
136
+ order: true,
137
+ name: true,
138
+ },
139
+ startsAt: true,
140
+ endsAt: true,
141
+ },
142
+ });
143
+
144
+ // If this is the first state, then do not notify the owner.
145
+ if (!stateBeforeThis) {
146
+ // since this is the first status, do not notify the owner.
147
+ createBy.data.isOwnerNotified = true;
148
+ }
148
149
 
149
- const publicNote: string | undefined = (
150
- createBy.miscDataProps as JSONObject | undefined
151
- )?.["publicNote"] as string | undefined;
152
-
153
- if (publicNote) {
154
- const scheduledMaintenancePublicNote: ScheduledMaintenancePublicNote =
155
- new ScheduledMaintenancePublicNote();
156
- scheduledMaintenancePublicNote.scheduledMaintenanceId =
157
- createBy.data.scheduledMaintenanceId;
158
- scheduledMaintenancePublicNote.note = publicNote;
159
- scheduledMaintenancePublicNote.postedAt = createBy.data.startsAt;
160
- scheduledMaintenancePublicNote.createdAt = createBy.data.startsAt;
161
- scheduledMaintenancePublicNote.projectId = createBy.data.projectId!;
162
- scheduledMaintenancePublicNote.shouldStatusPageSubscribersBeNotifiedOnNoteCreated =
163
- Boolean(createBy.data.shouldStatusPageSubscribersBeNotified);
164
-
165
- // mark status page subscribers as notified for this state change because we dont want to send duplicate (two) emails one for public note and one for state change.
166
150
  if (
167
- scheduledMaintenancePublicNote.shouldStatusPageSubscribersBeNotifiedOnNoteCreated
151
+ stateBeforeThis &&
152
+ stateBeforeThis.scheduledMaintenanceStateId &&
153
+ scheduledMaintenanceStateId
168
154
  ) {
169
- createBy.data.isStatusPageSubscribersNotified = true;
155
+ if (
156
+ stateBeforeThis.scheduledMaintenanceStateId.toString() ===
157
+ scheduledMaintenanceStateId.toString()
158
+ ) {
159
+ throw new BadDataException(
160
+ "Scheduled Maintenance state cannot be same as previous state.",
161
+ );
162
+ }
170
163
  }
171
164
 
172
- await ScheduledMaintenancePublicNoteService.create({
173
- data: scheduledMaintenancePublicNote,
174
- props: createBy.props,
175
- });
176
- }
165
+ if (stateBeforeThis && stateBeforeThis.scheduledMaintenanceState?.order) {
166
+ const newScheduledMaintenanceState: ScheduledMaintenanceState | null =
167
+ await ScheduledMaintenanceStateService.findOneBy({
168
+ query: {
169
+ _id: scheduledMaintenanceStateId,
170
+ },
171
+ select: {
172
+ order: true,
173
+ name: true,
174
+ },
175
+ props: {
176
+ isRoot: true,
177
+ },
178
+ });
177
179
 
178
- return {
179
- createBy,
180
- carryForward: {
181
- statusTimelineBeforeThisStatus: stateBeforeThis || null,
182
- statusTimelineAfterThisStatus: stateAfterThis || null,
183
- publicNote: publicNote,
184
- },
185
- };
180
+ if (
181
+ newScheduledMaintenanceState &&
182
+ newScheduledMaintenanceState.order
183
+ ) {
184
+ // check if the new scheduledMaintenance state is in order is greater than the previous state order
185
+ if (
186
+ stateBeforeThis &&
187
+ stateBeforeThis.scheduledMaintenanceState &&
188
+ stateBeforeThis.scheduledMaintenanceState.order &&
189
+ newScheduledMaintenanceState.order <=
190
+ stateBeforeThis.scheduledMaintenanceState.order
191
+ ) {
192
+ throw new BadDataException(
193
+ `ScheduledMaintenance cannot transition to ${newScheduledMaintenanceState.name} state from ${stateBeforeThis.scheduledMaintenanceState.name} state because ${newScheduledMaintenanceState.name} is before ${stateBeforeThis.scheduledMaintenanceState.name} in the order of scheduledMaintenance states.`,
194
+ );
195
+ }
196
+ }
197
+ }
198
+
199
+ const stateAfterThis: ScheduledMaintenanceStateTimeline | null =
200
+ await this.findOneBy({
201
+ query: {
202
+ scheduledMaintenanceId: createBy.data.scheduledMaintenanceId,
203
+ startsAt: QueryHelper.greaterThan(createBy.data.startsAt),
204
+ },
205
+ sort: {
206
+ startsAt: SortOrder.Ascending,
207
+ },
208
+ props: {
209
+ isRoot: true,
210
+ },
211
+ select: {
212
+ scheduledMaintenanceStateId: true,
213
+ startsAt: true,
214
+ endsAt: true,
215
+ },
216
+ });
217
+
218
+ // compute ends at. It's the start of the next status.
219
+ if (stateAfterThis && stateAfterThis.startsAt) {
220
+ createBy.data.endsAt = stateAfterThis.startsAt;
221
+ }
222
+
223
+ if (
224
+ stateAfterThis &&
225
+ stateAfterThis.scheduledMaintenanceStateId &&
226
+ scheduledMaintenanceStateId
227
+ ) {
228
+ if (
229
+ stateAfterThis.scheduledMaintenanceStateId.toString() ===
230
+ scheduledMaintenanceStateId.toString()
231
+ ) {
232
+ throw new BadDataException(
233
+ "Scheduled Maintenance state cannot be same as next state.",
234
+ );
235
+ }
236
+ }
237
+
238
+ const publicNote: string | undefined = (
239
+ createBy.miscDataProps as JSONObject | undefined
240
+ )?.["publicNote"] as string | undefined;
241
+
242
+ if (publicNote) {
243
+ const scheduledMaintenancePublicNote: ScheduledMaintenancePublicNote =
244
+ new ScheduledMaintenancePublicNote();
245
+ scheduledMaintenancePublicNote.scheduledMaintenanceId =
246
+ createBy.data.scheduledMaintenanceId;
247
+ scheduledMaintenancePublicNote.note = publicNote;
248
+ scheduledMaintenancePublicNote.postedAt = createBy.data.startsAt;
249
+ scheduledMaintenancePublicNote.createdAt = createBy.data.startsAt;
250
+ scheduledMaintenancePublicNote.projectId = createBy.data.projectId!;
251
+ scheduledMaintenancePublicNote.shouldStatusPageSubscribersBeNotifiedOnNoteCreated =
252
+ Boolean(createBy.data.shouldStatusPageSubscribersBeNotified);
253
+
254
+ // mark status page subscribers as notified for this state change because we dont want to send duplicate (two) emails one for public note and one for state change.
255
+ if (
256
+ scheduledMaintenancePublicNote.shouldStatusPageSubscribersBeNotifiedOnNoteCreated
257
+ ) {
258
+ createBy.data.isStatusPageSubscribersNotified = true;
259
+ }
260
+
261
+ await ScheduledMaintenancePublicNoteService.create({
262
+ data: scheduledMaintenancePublicNote,
263
+ props: createBy.props,
264
+ });
265
+ }
266
+
267
+ return {
268
+ createBy,
269
+ carryForward: {
270
+ statusTimelineBeforeThisStatus: stateBeforeThis || null,
271
+ statusTimelineAfterThisStatus: stateAfterThis || null,
272
+ publicNote: publicNote,
273
+ mutex: mutex,
274
+ },
275
+ };
276
+ } catch (error) {
277
+ // release the mutex if it was acquired.
278
+ if (mutex) {
279
+ try {
280
+ await Semaphore.release(mutex);
281
+ } catch (err) {
282
+ logger.error(err);
283
+ }
284
+ }
285
+
286
+ throw error;
287
+ }
186
288
  }
187
289
 
188
290
  @CaptureSpan()
@@ -190,6 +292,8 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
190
292
  onCreate: OnCreate<ScheduledMaintenanceStateTimeline>,
191
293
  createdItem: ScheduledMaintenanceStateTimeline,
192
294
  ): Promise<ScheduledMaintenanceStateTimeline> {
295
+ const mutex: SemaphoreMutex | null = onCreate.carryForward.mutex;
296
+
193
297
  if (!createdItem.scheduledMaintenanceId) {
194
298
  throw new BadDataException("scheduledMaintenanceId is null");
195
299
  }
@@ -266,6 +370,14 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
266
370
  });
267
371
  }
268
372
 
373
+ if (mutex) {
374
+ try {
375
+ await Semaphore.release(mutex);
376
+ } catch (err) {
377
+ logger.error(err);
378
+ }
379
+ }
380
+
269
381
  const scheduledMaintenanceState: ScheduledMaintenanceState | null =
270
382
  await ScheduledMaintenanceStateService.findOneBy({
271
383
  query: {
@@ -44,7 +44,6 @@ import { TelemetryQuery } from "../../../Types/Telemetry/TelemetryQuery";
44
44
  import MonitorIncident from "./MonitorIncident";
45
45
  import MonitorAlert from "./MonitorAlert";
46
46
  import MonitorStatusTimelineUtil from "./MonitorStatusTimeline";
47
- // import Semaphore, { SemaphoreMutex } from "../../Infrastructure/Semaphore";
48
47
  import Metric, {
49
48
  MetricPointType,
50
49
  ServiceType,
@@ -26,6 +26,7 @@ import AlertFeedService from "./AlertFeedService";
26
26
  import { AlertFeedEventType } from "../../Models/DatabaseModels/AlertFeed";
27
27
  import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
28
28
  import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
29
+ import Semaphore from "../Infrastructure/Semaphore";
29
30
  export class Service extends DatabaseService {
30
31
  constructor() {
31
32
  super(AlertStateTimeline);
@@ -52,105 +53,176 @@ export class Service extends DatabaseService {
52
53
  return resolvedState.id;
53
54
  }
54
55
  async onBeforeCreate(createBy) {
55
- var _a, _b;
56
+ var _a, _b, _c, _d;
56
57
  if (!createBy.data.alertId) {
57
58
  throw new BadDataException("alertId is null");
58
59
  }
59
- if (!createBy.data.startsAt) {
60
- createBy.data.startsAt = OneUptimeDate.getCurrentDate();
61
- }
62
- if ((createBy.data.createdByUserId ||
63
- createBy.data.createdByUser ||
64
- createBy.props.userId) &&
65
- !createBy.data.rootCause) {
66
- let userId = createBy.data.createdByUserId;
67
- if (createBy.props.userId) {
68
- userId = createBy.props.userId;
60
+ let mutex = null;
61
+ try {
62
+ if (!createBy.data.startsAt) {
63
+ createBy.data.startsAt = OneUptimeDate.getCurrentDate();
69
64
  }
70
- if (createBy.data.createdByUser && createBy.data.createdByUser.id) {
71
- userId = createBy.data.createdByUser.id;
65
+ try {
66
+ mutex = await Semaphore.lock({
67
+ key: createBy.data.alertId.toString(),
68
+ namespace: "AlertStateTimeline.create",
69
+ });
72
70
  }
73
- if (userId) {
74
- createBy.data.rootCause = `Alert state created by ${await UserService.getUserMarkdownString({
75
- userId: userId,
76
- projectId: createBy.data.projectId || createBy.props.tenantId,
77
- })}`;
71
+ catch (err) {
72
+ logger.error(err);
78
73
  }
79
- }
80
- const stateBeforeThis = await this.findOneBy({
81
- query: {
82
- alertId: createBy.data.alertId,
83
- startsAt: QueryHelper.lessThanEqualTo(createBy.data.startsAt),
84
- },
85
- sort: {
86
- startsAt: SortOrder.Descending,
87
- },
88
- props: {
89
- isRoot: true,
90
- },
91
- select: {
92
- alertStateId: true,
93
- startsAt: true,
94
- endsAt: true,
95
- },
96
- });
97
- logger.debug("State Before this");
98
- logger.debug(stateBeforeThis);
99
- // If this is the first state, then do not notify the owner.
100
- if (!stateBeforeThis) {
101
- // since this is the first status, do not notify the owner.
102
- createBy.data.isOwnerNotified = true;
103
- }
104
- const stateAfterThis = await this.findOneBy({
105
- query: {
106
- alertId: createBy.data.alertId,
107
- startsAt: QueryHelper.greaterThan(createBy.data.startsAt),
108
- },
109
- sort: {
110
- startsAt: SortOrder.Ascending,
111
- },
112
- props: {
113
- isRoot: true,
114
- },
115
- select: {
116
- alertStateId: true,
117
- startsAt: true,
118
- endsAt: true,
119
- },
120
- });
121
- // compute ends at. It's the start of the next status.
122
- if (stateAfterThis && stateAfterThis.startsAt) {
123
- createBy.data.endsAt = stateAfterThis.startsAt;
124
- }
125
- logger.debug("State After this");
126
- logger.debug(stateAfterThis);
127
- const internalNote = (_a = createBy.miscDataProps) === null || _a === void 0 ? void 0 : _a["internalNote"];
128
- if (internalNote) {
129
- const alertNote = new AlertInternalNote();
130
- alertNote.alertId = createBy.data.alertId;
131
- alertNote.note = internalNote;
132
- alertNote.createdAt = createBy.data.startsAt;
133
- alertNote.projectId = createBy.data.projectId;
134
- await AlertInternalNoteService.create({
135
- data: alertNote,
136
- props: createBy.props,
74
+ if ((createBy.data.createdByUserId ||
75
+ createBy.data.createdByUser ||
76
+ createBy.props.userId) &&
77
+ !createBy.data.rootCause) {
78
+ let userId = createBy.data.createdByUserId;
79
+ if (createBy.props.userId) {
80
+ userId = createBy.props.userId;
81
+ }
82
+ if (createBy.data.createdByUser && createBy.data.createdByUser.id) {
83
+ userId = createBy.data.createdByUser.id;
84
+ }
85
+ if (userId) {
86
+ createBy.data.rootCause = `Alert state created by ${await UserService.getUserMarkdownString({
87
+ userId: userId,
88
+ projectId: createBy.data.projectId || createBy.props.tenantId,
89
+ })}`;
90
+ }
91
+ }
92
+ const alertStateId = createBy.data.alertStateId || ((_a = createBy.data.alertState) === null || _a === void 0 ? void 0 : _a.id);
93
+ if (!alertStateId) {
94
+ throw new BadDataException("alertStateId is null");
95
+ }
96
+ const stateBeforeThis = await this.findOneBy({
97
+ query: {
98
+ alertId: createBy.data.alertId,
99
+ startsAt: QueryHelper.lessThanEqualTo(createBy.data.startsAt),
100
+ },
101
+ sort: {
102
+ startsAt: SortOrder.Descending,
103
+ },
104
+ props: {
105
+ isRoot: true,
106
+ },
107
+ select: {
108
+ alertStateId: true,
109
+ alertState: {
110
+ order: true,
111
+ name: true,
112
+ },
113
+ startsAt: true,
114
+ endsAt: true,
115
+ },
116
+ });
117
+ logger.debug("State Before this");
118
+ logger.debug(stateBeforeThis);
119
+ // If this is the first state, then do not notify the owner.
120
+ if (!stateBeforeThis) {
121
+ // since this is the first status, do not notify the owner.
122
+ createBy.data.isOwnerNotified = true;
123
+ }
124
+ // check if this new state and the previous state are same.
125
+ // if yes, then throw bad data exception.
126
+ if (stateBeforeThis && stateBeforeThis.alertStateId && alertStateId) {
127
+ if (stateBeforeThis.alertStateId.toString() === alertStateId.toString()) {
128
+ throw new BadDataException("Alert state cannot be same as previous state.");
129
+ }
130
+ }
131
+ if (stateBeforeThis && ((_b = stateBeforeThis.alertState) === null || _b === void 0 ? void 0 : _b.order)) {
132
+ const newAlertState = await AlertStateService.findOneBy({
133
+ query: {
134
+ _id: alertStateId,
135
+ },
136
+ select: {
137
+ order: true,
138
+ name: true,
139
+ },
140
+ props: {
141
+ isRoot: true,
142
+ },
143
+ });
144
+ if (newAlertState && newAlertState.order) {
145
+ // check if the new alert state is in order is greater than the previous state order
146
+ if (stateBeforeThis &&
147
+ stateBeforeThis.alertState &&
148
+ stateBeforeThis.alertState.order &&
149
+ newAlertState.order <= stateBeforeThis.alertState.order) {
150
+ throw new BadDataException(`Alert cannot transition to ${newAlertState.name} state from ${stateBeforeThis.alertState.name} state because ${newAlertState.name} is before ${stateBeforeThis.alertState.name} in the order of alert states.`);
151
+ }
152
+ }
153
+ }
154
+ const stateAfterThis = await this.findOneBy({
155
+ query: {
156
+ alertId: createBy.data.alertId,
157
+ startsAt: QueryHelper.greaterThan(createBy.data.startsAt),
158
+ },
159
+ sort: {
160
+ startsAt: SortOrder.Ascending,
161
+ },
162
+ props: {
163
+ isRoot: true,
164
+ },
165
+ select: {
166
+ alertStateId: true,
167
+ startsAt: true,
168
+ endsAt: true,
169
+ },
137
170
  });
171
+ // compute ends at. It's the start of the next status.
172
+ if (stateAfterThis && stateAfterThis.startsAt) {
173
+ createBy.data.endsAt = stateAfterThis.startsAt;
174
+ }
175
+ // check if this new state and the previous state are same.
176
+ // if yes, then throw bad data exception.
177
+ if (stateAfterThis && stateAfterThis.alertStateId && alertStateId) {
178
+ if (stateAfterThis.alertStateId.toString() === alertStateId.toString()) {
179
+ throw new BadDataException("Alert state cannot be same as next state.");
180
+ }
181
+ }
182
+ logger.debug("State After this");
183
+ logger.debug(stateAfterThis);
184
+ const internalNote = (_c = createBy.miscDataProps) === null || _c === void 0 ? void 0 : _c["internalNote"];
185
+ if (internalNote) {
186
+ const alertNote = new AlertInternalNote();
187
+ alertNote.alertId = createBy.data.alertId;
188
+ alertNote.note = internalNote;
189
+ alertNote.createdAt = createBy.data.startsAt;
190
+ alertNote.projectId = createBy.data.projectId;
191
+ await AlertInternalNoteService.create({
192
+ data: alertNote,
193
+ props: createBy.props,
194
+ });
195
+ }
196
+ const privateNote = (_d = createBy.miscDataProps) === null || _d === void 0 ? void 0 : _d["privateNote"];
197
+ return {
198
+ createBy,
199
+ carryForward: {
200
+ statusTimelineBeforeThisStatus: stateBeforeThis || null,
201
+ statusTimelineAfterThisStatus: stateAfterThis || null,
202
+ privateNote: privateNote,
203
+ mutex: mutex,
204
+ },
205
+ };
206
+ }
207
+ catch (error) {
208
+ // release the mutex if it was acquired.
209
+ if (mutex) {
210
+ try {
211
+ await Semaphore.release(mutex);
212
+ }
213
+ catch (err) {
214
+ logger.error(err);
215
+ }
216
+ }
217
+ throw error;
138
218
  }
139
- const privateNote = (_b = createBy.miscDataProps) === null || _b === void 0 ? void 0 : _b["privateNote"];
140
- return {
141
- createBy,
142
- carryForward: {
143
- statusTimelineBeforeThisStatus: stateBeforeThis || null,
144
- statusTimelineAfterThisStatus: stateAfterThis || null,
145
- privateNote: privateNote,
146
- },
147
- };
148
219
  }
149
220
  async onCreateSuccess(onCreate, createdItem) {
150
221
  var _a;
151
222
  if (!createdItem.alertId) {
152
223
  throw new BadDataException("alertId is null");
153
224
  }
225
+ const mutex = onCreate.carryForward.mutex;
154
226
  if (!createdItem.alertStateId) {
155
227
  throw new BadDataException("alertStateId is null");
156
228
  }
@@ -215,6 +287,14 @@ export class Service extends DatabaseService {
215
287
  props: onCreate.createBy.props,
216
288
  });
217
289
  }
290
+ if (mutex) {
291
+ try {
292
+ await Semaphore.release(mutex);
293
+ }
294
+ catch (err) {
295
+ logger.error(err);
296
+ }
297
+ }
218
298
  const alertState = await AlertStateService.findOneBy({
219
299
  query: {
220
300
  _id: createdItem.alertStateId.toString(),