@oneuptime/common 7.0.3515 → 7.0.3526
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/Incident.ts +6 -1
- package/Models/DatabaseModels/ScheduledMaintenanceFeed.ts +15 -7
- package/Server/API/BillingInvoiceAPI.ts +2 -32
- package/Server/Services/AlertFeedService.ts +44 -37
- package/Server/Services/BillingInvoiceService.ts +170 -0
- package/Server/Services/IncidentFeedService.ts +59 -54
- package/Server/Services/IncidentInternalNoteService.ts +46 -1
- package/Server/Services/IncidentPublicNoteService.ts +47 -2
- package/Server/Services/IncidentService.ts +18 -0
- package/Server/Services/ScheduledMaintenanceFeedService.ts +51 -40
- package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +75 -0
- package/Server/Services/ScheduledMaintenanceOwnerTeamService.ts +116 -0
- package/Server/Services/ScheduledMaintenanceOwnerUserService.ts +118 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +74 -1
- package/Server/Services/ScheduledMaintenanceService.ts +68 -0
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +30 -0
- package/Server/Utils/Logger.ts +14 -0
- package/UI/Utils/Project.ts +25 -0
- package/build/dist/Models/DatabaseModels/Incident.js +6 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceFeed.js +15 -7
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceFeed.js.map +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js +2 -19
- package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
- package/build/dist/Server/Services/AlertFeedService.js +43 -35
- package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
- package/build/dist/Server/Services/BillingInvoiceService.js +123 -0
- package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFeedService.js +45 -39
- package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
- package/build/dist/Server/Services/IncidentInternalNoteService.js +37 -0
- package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +38 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +15 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceFeedService.js +45 -37
- package/build/dist/Server/Services/ScheduledMaintenanceFeedService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +56 -0
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerTeamService.js +86 -0
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerTeamService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerUserService.js +89 -0
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerUserService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +56 -0
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +57 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +24 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Utils/Logger.js +11 -0
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/UI/Utils/Project.js +11 -0
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/package.json +2 -2
|
@@ -853,7 +853,12 @@ export default class Incident extends BaseModel {
|
|
|
853
853
|
Permission.ProjectMember,
|
|
854
854
|
Permission.ReadProjectIncident,
|
|
855
855
|
],
|
|
856
|
-
update: [
|
|
856
|
+
update: [
|
|
857
|
+
Permission.ProjectOwner,
|
|
858
|
+
Permission.ProjectAdmin,
|
|
859
|
+
Permission.ProjectMember,
|
|
860
|
+
Permission.EditProjectIncident,
|
|
861
|
+
],
|
|
857
862
|
})
|
|
858
863
|
@TableColumn({
|
|
859
864
|
type: TableColumnType.Markdown,
|
|
@@ -23,12 +23,20 @@ import Color from "../../Types/Color";
|
|
|
23
23
|
|
|
24
24
|
export enum ScheduledMaintenanceFeedEventType {
|
|
25
25
|
PublicNote = "PublicNote",
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
SubscriberNotificationSent = "SubscriberNotificationSent",
|
|
27
|
+
OwnerNotificationSent = "OwnerNotificationSent",
|
|
28
|
+
OwnerUserAdded = "OwnerUserAdded",
|
|
29
|
+
OwnerTeamAdded = "OwnerTeamAdded",
|
|
28
30
|
ScheduledMaintenanceCreated = "ScheduledMaintenanceCreated",
|
|
29
|
-
|
|
30
|
-
ScheduledMaintenanceResolved = "ScheduledMaintenanceResolved",
|
|
31
|
+
ScheduledMaintenanceStateChanged = "ScheduledMaintenanceStateChanged",
|
|
31
32
|
PrivateNote = "PrivateNote",
|
|
33
|
+
ScheduledMaintenanceUpdated = "ScheduledMaintenanceUpdated",
|
|
34
|
+
RootCause = "RootCause",
|
|
35
|
+
RemediationNotes = "RemediationNotes",
|
|
36
|
+
OwnerUserRemoved = "OwnerUserRemoved",
|
|
37
|
+
OwnerTeamRemoved = "OwnerTeamRemoved",
|
|
38
|
+
OnCallPolicy = "OnCallPolicy",
|
|
39
|
+
OnCallNotification = "OnCallNotification",
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
@EnableDocumentation()
|
|
@@ -332,7 +340,7 @@ export default class ScheduledMaintenanceFeed extends BaseModel {
|
|
|
332
340
|
required: true,
|
|
333
341
|
title: "Log (in Markdown)",
|
|
334
342
|
description:
|
|
335
|
-
"Log of the entire
|
|
343
|
+
"Log of the entire scheduled maintenance state change in Markdown",
|
|
336
344
|
})
|
|
337
345
|
@Column({
|
|
338
346
|
type: ColumnType.Markdown,
|
|
@@ -403,13 +411,13 @@ export default class ScheduledMaintenanceFeed extends BaseModel {
|
|
|
403
411
|
Permission.ProjectOwner,
|
|
404
412
|
Permission.ProjectAdmin,
|
|
405
413
|
Permission.ProjectMember,
|
|
406
|
-
Permission.
|
|
414
|
+
Permission.CreateScheduledMaintenanceFeed,
|
|
407
415
|
],
|
|
408
416
|
read: [
|
|
409
417
|
Permission.ProjectOwner,
|
|
410
418
|
Permission.ProjectAdmin,
|
|
411
419
|
Permission.ProjectMember,
|
|
412
|
-
Permission.
|
|
420
|
+
Permission.ReadScheduledMaintenanceFeed,
|
|
413
421
|
],
|
|
414
422
|
update: [],
|
|
415
423
|
})
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
import Response from "../Utils/Response";
|
|
15
15
|
import BaseAPI from "./BaseAPI";
|
|
16
16
|
import BaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
17
|
-
import SubscriptionStatus from "Common/Types/Billing/SubscriptionStatus";
|
|
18
17
|
import BadDataException from "Common/Types/Exception/BadDataException";
|
|
19
18
|
import { JSONObject } from "Common/Types/JSON";
|
|
20
19
|
import Permission, { UserPermission } from "Common/Types/Permission";
|
|
@@ -127,37 +126,8 @@ export default class UserAPI extends BaseAPI<
|
|
|
127
126
|
},
|
|
128
127
|
});
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await BillingService.getSubscriptionStatus(
|
|
133
|
-
project.paymentProviderSubscriptionId as string,
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const meteredSubscriptionState: SubscriptionStatus =
|
|
137
|
-
await BillingService.getSubscriptionStatus(
|
|
138
|
-
project.paymentProviderMeteredSubscriptionId as string,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
// if subscription is cancelled, create a new subscription and update project.
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
meteredSubscriptionState === SubscriptionStatus.Canceled ||
|
|
145
|
-
subscriptionState === SubscriptionStatus.Canceled
|
|
146
|
-
) {
|
|
147
|
-
await ProjectService.reactiveSubscription(project.id!);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
await ProjectService.updateOneById({
|
|
151
|
-
id: project.id!,
|
|
152
|
-
data: {
|
|
153
|
-
paymentProviderSubscriptionStatus: subscriptionState,
|
|
154
|
-
paymentProviderMeteredSubscriptionStatus:
|
|
155
|
-
meteredSubscriptionState,
|
|
156
|
-
},
|
|
157
|
-
props: {
|
|
158
|
-
isRoot: true,
|
|
159
|
-
ignoreHooks: true,
|
|
160
|
-
},
|
|
129
|
+
await BillingInvoiceService.refreshSubscriptionStatus({
|
|
130
|
+
projectId: project.id!,
|
|
161
131
|
});
|
|
162
132
|
|
|
163
133
|
return Response.sendEmptySuccessResponse(req, res);
|
|
@@ -4,6 +4,7 @@ import OneUptimeDate from "../../Types/Date";
|
|
|
4
4
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
5
5
|
import ObjectID from "../../Types/ObjectID";
|
|
6
6
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
7
|
+
import logger from "../Utils/Logger";
|
|
7
8
|
import DatabaseService from "./DatabaseService";
|
|
8
9
|
import Model, {
|
|
9
10
|
AlertFeedEventType,
|
|
@@ -27,54 +28,60 @@ export class Service extends DatabaseService<Model> {
|
|
|
27
28
|
displayColor?: Color | undefined;
|
|
28
29
|
userId?: ObjectID | undefined;
|
|
29
30
|
postedAt?: Date | undefined;
|
|
30
|
-
}): Promise<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
}): Promise<void> {
|
|
32
|
+
try {
|
|
33
|
+
if (!data.alertId) {
|
|
34
|
+
throw new BadDataException("Alert ID is required");
|
|
35
|
+
}
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
if (!data.feedInfoInMarkdown) {
|
|
38
|
+
throw new BadDataException("Log in markdown is required");
|
|
39
|
+
}
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
if (!data.alertFeedEventType) {
|
|
42
|
+
throw new BadDataException("Alert log event is required");
|
|
43
|
+
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
if (!data.projectId) {
|
|
46
|
+
throw new BadDataException("Project ID is required");
|
|
47
|
+
}
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
const alertFeed: Model = new Model();
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
if (!data.displayColor) {
|
|
52
|
+
data.displayColor = Blue500;
|
|
53
|
+
}
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
if (data.userId) {
|
|
56
|
+
alertFeed.userId = data.userId;
|
|
57
|
+
}
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
alertFeed.displayColor = data.displayColor;
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
alertFeed.alertId = data.alertId;
|
|
62
|
+
alertFeed.feedInfoInMarkdown = data.feedInfoInMarkdown;
|
|
63
|
+
alertFeed.alertFeedEventType = data.alertFeedEventType;
|
|
64
|
+
alertFeed.projectId = data.projectId;
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
if (!data.postedAt) {
|
|
67
|
+
alertFeed.postedAt = OneUptimeDate.getCurrentDate();
|
|
68
|
+
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
if (data.moreInformationInMarkdown) {
|
|
71
|
+
alertFeed.moreInformationInMarkdown = data.moreInformationInMarkdown;
|
|
72
|
+
}
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
await this.create({
|
|
75
|
+
data: alertFeed,
|
|
76
|
+
props: {
|
|
77
|
+
isRoot: true,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.error("AlertFeedService.createAlertFeed");
|
|
82
|
+
logger.error(error);
|
|
83
|
+
// we dont want to throw the error here, as this is a non-critical operation
|
|
84
|
+
}
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
87
|
|
|
@@ -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<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
data.displayColor
|
|
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();
|
|
@@ -662,6 +662,24 @@ ${onUpdate.updateBy.data.title || "No title provided."}
|
|
|
662
662
|
});
|
|
663
663
|
}
|
|
664
664
|
|
|
665
|
+
if (onUpdate.updateBy.data.rootCause) {
|
|
666
|
+
// add incident feed.
|
|
667
|
+
const createdByUserId: ObjectID | undefined | null =
|
|
668
|
+
onUpdate.updateBy.props.userId;
|
|
669
|
+
|
|
670
|
+
await IncidentFeedService.createIncidentFeed({
|
|
671
|
+
incidentId: incidentId,
|
|
672
|
+
projectId: onUpdate.updateBy.props.tenantId as ObjectID,
|
|
673
|
+
incidentFeedEventType: IncidentFeedEventType.IncidentUpdated,
|
|
674
|
+
displayColor: Gray500,
|
|
675
|
+
feedInfoInMarkdown: `**Incident root cause was updated.** Here's the new root cause.
|
|
676
|
+
|
|
677
|
+
${onUpdate.updateBy.data.rootCause || "No root cause provided."}
|
|
678
|
+
`,
|
|
679
|
+
userId: createdByUserId || undefined,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
665
683
|
if (onUpdate.updateBy.data.description) {
|
|
666
684
|
// add incident feed.
|
|
667
685
|
const createdByUserId: ObjectID | undefined | null =
|