@oneuptime/common 7.0.5085 → 7.0.5093
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/OnCallDutyPolicyEscalationRule.ts +1 -1
- package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.ts +1 -1
- package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.ts +1 -1
- package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.ts +1 -1
- package/Models/DatabaseModels/Project.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1756821449686-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/AlertService.ts +1 -1
- package/Server/Services/MonitorService.ts +27 -7
- package/Server/Utils/Monitor/MonitorAlert.ts +20 -4
- package/Server/Utils/Monitor/MonitorIncident.ts +23 -5
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +246 -0
- package/Server/Utils/VM/VMAPI.ts +11 -2
- package/UI/Components/Graphs/ServiceDependencyGraph.tsx +10 -5
- package/UI/Components/Input/Input.tsx +5 -4
- package/UI/Components/Workflow/ArgumentsForm.tsx +5 -3
- package/UI/Components/Workflow/Utils.ts +3 -8
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRule.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +34 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1756821449686-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1756821449686-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +1 -1
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +22 -7
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +17 -3
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -3
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +180 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js +9 -1
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/UI/Components/Graphs/ServiceDependencyGraph.js +1 -1
- package/build/dist/UI/Components/Graphs/ServiceDependencyGraph.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +4 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Utils.js +1 -4
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -46,7 +46,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
46
46
|
Permission.EditProjectOnCallDutyPolicyEscalationRule,
|
|
47
47
|
],
|
|
48
48
|
})
|
|
49
|
-
@CrudApiEndpoint(new Route("/on-call-duty-policy-
|
|
49
|
+
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule"))
|
|
50
50
|
@Entity({
|
|
51
51
|
name: "OnCallDutyPolicyEscalationRule",
|
|
52
52
|
})
|
|
@@ -47,7 +47,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
47
47
|
Permission.EditProjectOnCallDutyPolicyEscalationRuleSchedule,
|
|
48
48
|
],
|
|
49
49
|
})
|
|
50
|
-
@CrudApiEndpoint(new Route("/on-call-duty-policy-
|
|
50
|
+
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule-schedule"))
|
|
51
51
|
@Entity({
|
|
52
52
|
name: "OnCallDutyPolicyEscalationRuleSchedule",
|
|
53
53
|
})
|
|
@@ -47,7 +47,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
47
47
|
Permission.EditProjectOnCallDutyPolicyEscalationRuleTeam,
|
|
48
48
|
],
|
|
49
49
|
})
|
|
50
|
-
@CrudApiEndpoint(new Route("/on-call-duty-policy-
|
|
50
|
+
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule-team"))
|
|
51
51
|
@Entity({
|
|
52
52
|
name: "OnCallDutyPolicyEscalationRuleTeam",
|
|
53
53
|
})
|
|
@@ -46,7 +46,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
46
46
|
Permission.EditProjectOnCallDutyPolicyEscalationRuleUser,
|
|
47
47
|
],
|
|
48
48
|
})
|
|
49
|
-
@CrudApiEndpoint(new Route("/on-call-duty-policy-
|
|
49
|
+
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule-user"))
|
|
50
50
|
@Entity({
|
|
51
51
|
name: "OnCallDutyPolicyEscalationRuleUser",
|
|
52
52
|
})
|
|
@@ -1291,4 +1291,37 @@ export default class Project extends TenantModel {
|
|
|
1291
1291
|
type: ColumnType.Boolean,
|
|
1292
1292
|
})
|
|
1293
1293
|
public letCustomerSupportAccessProject?: boolean = undefined;
|
|
1294
|
+
|
|
1295
|
+
@ColumnAccessControl({
|
|
1296
|
+
create: [],
|
|
1297
|
+
read: [
|
|
1298
|
+
Permission.ProjectOwner,
|
|
1299
|
+
Permission.ProjectAdmin,
|
|
1300
|
+
Permission.ProjectMember,
|
|
1301
|
+
Permission.ReadProject,
|
|
1302
|
+
Permission.UnAuthorizedSsoUser,
|
|
1303
|
+
Permission.ProjectUser,
|
|
1304
|
+
],
|
|
1305
|
+
update: [
|
|
1306
|
+
Permission.ProjectOwner,
|
|
1307
|
+
Permission.ProjectAdmin,
|
|
1308
|
+
Permission.EditProject,
|
|
1309
|
+
],
|
|
1310
|
+
})
|
|
1311
|
+
@TableColumn({
|
|
1312
|
+
required: false,
|
|
1313
|
+
type: TableColumnType.Boolean,
|
|
1314
|
+
isDefaultValueColumn: false,
|
|
1315
|
+
title: "Do NOT auto-add Global Probes to new monitors",
|
|
1316
|
+
description:
|
|
1317
|
+
"If enabled, global probes will NOT be automatically added to new monitors. Enable this only if you are using ONLY custom probes to monitor your resources.",
|
|
1318
|
+
defaultValue: false,
|
|
1319
|
+
})
|
|
1320
|
+
@Column({
|
|
1321
|
+
type: ColumnType.Boolean,
|
|
1322
|
+
nullable: false,
|
|
1323
|
+
unique: false,
|
|
1324
|
+
default: false,
|
|
1325
|
+
})
|
|
1326
|
+
public doNotAddGlobalProbesByDefaultOnNewMonitors?: boolean = undefined;
|
|
1294
1327
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1756821449686 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1756821449686";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "Project" ADD "doNotAddGlobalProbesByDefaultOnNewMonitors" boolean NOT NULL DEFAULT false`,
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "Project" DROP COLUMN "doNotAddGlobalProbesByDefaultOnNewMonitors"`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -164,6 +164,7 @@ import { MigrationName1755778934927 } from "./1755778934927-MigrationName";
|
|
|
164
164
|
import { MigrationName1756293325324 } from "./1756293325324-MigrationName";
|
|
165
165
|
import { MigrationName1756296282627 } from "./1756296282627-MigrationName";
|
|
166
166
|
import { MigrationName1756300358095 } from "./1756300358095-MigrationName";
|
|
167
|
+
import { MigrationName1756821449686 } from "./1756821449686-MigrationName";
|
|
167
168
|
|
|
168
169
|
export default [
|
|
169
170
|
InitialMigration,
|
|
@@ -332,4 +333,5 @@ export default [
|
|
|
332
333
|
MigrationName1756293325324,
|
|
333
334
|
MigrationName1756296282627,
|
|
334
335
|
MigrationName1756300358095,
|
|
336
|
+
MigrationName1756821449686,
|
|
335
337
|
];
|
|
@@ -440,7 +440,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
440
440
|
|
|
441
441
|
${createdItem.description || "No description provided."}
|
|
442
442
|
|
|
443
|
-
|
|
443
|
+
`;
|
|
444
444
|
|
|
445
445
|
if (alert.currentAlertState?.name) {
|
|
446
446
|
feedInfoInMarkdown += `🔴 **Alert State**: ${alert.currentAlertState.name} \n\n`;
|
|
@@ -68,6 +68,7 @@ import { FindWhere } from "../../Types/BaseDatabase/Query";
|
|
|
68
68
|
import logger from "../Utils/Logger";
|
|
69
69
|
import PushNotificationUtil from "../Utils/PushNotificationUtil";
|
|
70
70
|
import ExceptionMessages from "../../Types/Exception/ExceptionMessages";
|
|
71
|
+
import Project from "../../Models/DatabaseModels/Project";
|
|
71
72
|
|
|
72
73
|
export class Service extends DatabaseService<Model> {
|
|
73
74
|
public constructor() {
|
|
@@ -791,21 +792,40 @@ ${createdItem.description?.trim() || "No description provided."}
|
|
|
791
792
|
projectId: ObjectID,
|
|
792
793
|
monitorId: ObjectID,
|
|
793
794
|
): Promise<void> {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
shouldAutoEnableProbeOnNewMonitors: true,
|
|
798
|
-
},
|
|
795
|
+
// Fetch project to see if global probes should be added automatically.
|
|
796
|
+
const project: Project | null = await ProjectService.findOneById({
|
|
797
|
+
id: projectId,
|
|
799
798
|
select: {
|
|
800
799
|
_id: true,
|
|
800
|
+
doNotAddGlobalProbesByDefaultOnNewMonitors: true,
|
|
801
801
|
},
|
|
802
|
-
skip: 0,
|
|
803
|
-
limit: LIMIT_PER_PROJECT,
|
|
804
802
|
props: {
|
|
805
803
|
isRoot: true,
|
|
806
804
|
},
|
|
807
805
|
});
|
|
808
806
|
|
|
807
|
+
const shouldSkipGlobalProbes: boolean =
|
|
808
|
+
project?.doNotAddGlobalProbesByDefaultOnNewMonitors === true;
|
|
809
|
+
|
|
810
|
+
let globalProbes: Array<Probe> = [];
|
|
811
|
+
|
|
812
|
+
if (!shouldSkipGlobalProbes) {
|
|
813
|
+
globalProbes = await ProbeService.findBy({
|
|
814
|
+
query: {
|
|
815
|
+
isGlobalProbe: true,
|
|
816
|
+
shouldAutoEnableProbeOnNewMonitors: true,
|
|
817
|
+
},
|
|
818
|
+
select: {
|
|
819
|
+
_id: true,
|
|
820
|
+
},
|
|
821
|
+
skip: 0,
|
|
822
|
+
limit: LIMIT_PER_PROJECT,
|
|
823
|
+
props: {
|
|
824
|
+
isRoot: true,
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
809
829
|
const projectProbes: Array<Probe> = await ProbeService.findBy({
|
|
810
830
|
query: {
|
|
811
831
|
isGlobalProbe: false,
|
|
@@ -19,6 +19,8 @@ import AlertStateTimelineService from "../../Services/AlertStateTimelineService"
|
|
|
19
19
|
import logger from "../Logger";
|
|
20
20
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
21
21
|
import DataToProcess from "./DataToProcess";
|
|
22
|
+
import MonitorTemplateUtil from "./MonitorTemplateUtil";
|
|
23
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
22
24
|
|
|
23
25
|
export default class MonitorAlert {
|
|
24
26
|
@CaptureSpan()
|
|
@@ -130,9 +132,20 @@ export default class MonitorAlert {
|
|
|
130
132
|
logger.debug(`${input.monitor.id?.toString()} - Create alert.`);
|
|
131
133
|
|
|
132
134
|
const alert: Alert = new Alert();
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
const storageMap: JSONObject =
|
|
136
|
+
MonitorTemplateUtil.buildTemplateStorageMap({
|
|
137
|
+
monitorType: input.monitor.monitorType!,
|
|
138
|
+
dataToProcess: input.dataToProcess,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
alert.title = MonitorTemplateUtil.processTemplateString({
|
|
142
|
+
value: criteriaAlert.title,
|
|
143
|
+
storageMap,
|
|
144
|
+
});
|
|
145
|
+
alert.description = MonitorTemplateUtil.processTemplateString({
|
|
146
|
+
value: criteriaAlert.description,
|
|
147
|
+
storageMap,
|
|
148
|
+
});
|
|
136
149
|
|
|
137
150
|
if (!criteriaAlert.alertSeverityId) {
|
|
138
151
|
// pick the critical criteria.
|
|
@@ -194,7 +207,10 @@ export default class MonitorAlert {
|
|
|
194
207
|
}
|
|
195
208
|
|
|
196
209
|
if (criteriaAlert.remediationNotes) {
|
|
197
|
-
alert.remediationNotes =
|
|
210
|
+
alert.remediationNotes = MonitorTemplateUtil.processTemplateString({
|
|
211
|
+
value: criteriaAlert.remediationNotes,
|
|
212
|
+
storageMap,
|
|
213
|
+
});
|
|
198
214
|
}
|
|
199
215
|
|
|
200
216
|
if (DisableAutomaticAlertCreation) {
|
|
@@ -19,6 +19,8 @@ import IncidentStateTimelineService from "../../Services/IncidentStateTimelineSe
|
|
|
19
19
|
import logger from "../Logger";
|
|
20
20
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
21
21
|
import DataToProcess from "./DataToProcess";
|
|
22
|
+
import MonitorTemplateUtil from "./MonitorTemplateUtil";
|
|
23
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
22
24
|
|
|
23
25
|
export default class MonitorIncident {
|
|
24
26
|
@CaptureSpan()
|
|
@@ -34,7 +36,7 @@ export default class MonitorIncident {
|
|
|
34
36
|
// check active incidents and if there are open incidents, do not cretae anothr incident.
|
|
35
37
|
const openIncidents: Array<Incident> = await IncidentService.findBy({
|
|
36
38
|
query: {
|
|
37
|
-
monitors: [input.monitorId]
|
|
39
|
+
monitors: [input.monitorId],
|
|
38
40
|
currentIncidentState: {
|
|
39
41
|
isResolvedState: false,
|
|
40
42
|
},
|
|
@@ -136,9 +138,20 @@ export default class MonitorIncident {
|
|
|
136
138
|
logger.debug(`${input.monitor.id?.toString()} - Create incident.`);
|
|
137
139
|
|
|
138
140
|
const incident: Incident = new Incident();
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
const storageMap: JSONObject =
|
|
142
|
+
MonitorTemplateUtil.buildTemplateStorageMap({
|
|
143
|
+
monitorType: input.monitor.monitorType!,
|
|
144
|
+
dataToProcess: input.dataToProcess,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
incident.title = MonitorTemplateUtil.processTemplateString({
|
|
148
|
+
value: criteriaIncident.title,
|
|
149
|
+
storageMap,
|
|
150
|
+
});
|
|
151
|
+
incident.description = MonitorTemplateUtil.processTemplateString({
|
|
152
|
+
value: criteriaIncident.description,
|
|
153
|
+
storageMap,
|
|
154
|
+
});
|
|
142
155
|
|
|
143
156
|
if (!criteriaIncident.incidentSeverityId) {
|
|
144
157
|
// pick the critical criteria.
|
|
@@ -204,7 +217,12 @@ export default class MonitorIncident {
|
|
|
204
217
|
}
|
|
205
218
|
|
|
206
219
|
if (criteriaIncident.remediationNotes) {
|
|
207
|
-
incident.remediationNotes =
|
|
220
|
+
incident.remediationNotes = MonitorTemplateUtil.processTemplateString(
|
|
221
|
+
{
|
|
222
|
+
value: criteriaIncident.remediationNotes,
|
|
223
|
+
storageMap,
|
|
224
|
+
},
|
|
225
|
+
);
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
if (DisableAutomaticIncidentCreation) {
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
2
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
3
|
+
import ProbeMonitorResponse from "../../../Types/Probe/ProbeMonitorResponse";
|
|
4
|
+
import IncomingMonitorRequest from "../../../Types/Monitor/IncomingMonitor/IncomingMonitorRequest";
|
|
5
|
+
import ServerMonitorResponse, {
|
|
6
|
+
ServerProcess,
|
|
7
|
+
} from "../../../Types/Monitor/ServerMonitor/ServerMonitorResponse";
|
|
8
|
+
import BasicInfrastructureMetrics, {
|
|
9
|
+
BasicDiskMetrics,
|
|
10
|
+
} from "../../../Types/Infrastructure/BasicMetrics";
|
|
11
|
+
import SslMonitorResponse from "../../../Types/Monitor/SSLMonitor/SslMonitorResponse";
|
|
12
|
+
import CustomCodeMonitorResponse from "../../../Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse";
|
|
13
|
+
import SyntheticMonitorResponse from "../../../Types/Monitor/SyntheticMonitors/SyntheticMonitorResponse";
|
|
14
|
+
import Typeof from "../../../Types/Typeof";
|
|
15
|
+
import VMUtil from "../VM/VMAPI";
|
|
16
|
+
import DataToProcess from "./DataToProcess";
|
|
17
|
+
import logger from "../Logger";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Utility for building template variable storage map and processing dynamic placeholders
|
|
21
|
+
* shared between Incident and Alert auto-creation.
|
|
22
|
+
*/
|
|
23
|
+
export default class MonitorTemplateUtil {
|
|
24
|
+
/**
|
|
25
|
+
* Build a storage map of variables available for templating based on monitor type.
|
|
26
|
+
*/
|
|
27
|
+
public static buildTemplateStorageMap(data: {
|
|
28
|
+
monitorType: MonitorType;
|
|
29
|
+
dataToProcess: DataToProcess;
|
|
30
|
+
}): JSONObject {
|
|
31
|
+
let storageMap: JSONObject = {};
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
if (
|
|
35
|
+
data.monitorType === MonitorType.API ||
|
|
36
|
+
data.monitorType === MonitorType.Website
|
|
37
|
+
) {
|
|
38
|
+
let responseBody: JSONObject | null = null;
|
|
39
|
+
try {
|
|
40
|
+
responseBody = JSON.parse(
|
|
41
|
+
((data.dataToProcess as ProbeMonitorResponse)
|
|
42
|
+
.responseBody as string) || "{}",
|
|
43
|
+
);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
logger.error(err);
|
|
46
|
+
responseBody = (data.dataToProcess as ProbeMonitorResponse)
|
|
47
|
+
.responseBody as JSONObject;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
typeof responseBody === Typeof.String &&
|
|
52
|
+
responseBody?.toString() === ""
|
|
53
|
+
) {
|
|
54
|
+
responseBody = {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
storageMap = {
|
|
58
|
+
responseBody: responseBody,
|
|
59
|
+
responseHeaders: (data.dataToProcess as ProbeMonitorResponse)
|
|
60
|
+
.responseHeaders,
|
|
61
|
+
responseStatusCode: (data.dataToProcess as ProbeMonitorResponse)
|
|
62
|
+
.responseCode,
|
|
63
|
+
responseTimeInMs: (data.dataToProcess as ProbeMonitorResponse)
|
|
64
|
+
.responseTimeInMs,
|
|
65
|
+
isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
|
|
66
|
+
} as JSONObject;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (data.monitorType === MonitorType.IncomingRequest) {
|
|
70
|
+
storageMap = {
|
|
71
|
+
requestBody: (data.dataToProcess as IncomingMonitorRequest)
|
|
72
|
+
.requestBody,
|
|
73
|
+
requestHeaders: (data.dataToProcess as IncomingMonitorRequest)
|
|
74
|
+
.requestHeaders,
|
|
75
|
+
requestMethod: (data.dataToProcess as IncomingMonitorRequest)
|
|
76
|
+
.requestMethod,
|
|
77
|
+
incomingRequestReceivedAt: (
|
|
78
|
+
data.dataToProcess as IncomingMonitorRequest
|
|
79
|
+
).incomingRequestReceivedAt,
|
|
80
|
+
} as JSONObject;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
data.monitorType === MonitorType.Ping ||
|
|
85
|
+
data.monitorType === MonitorType.IP ||
|
|
86
|
+
data.monitorType === MonitorType.Port
|
|
87
|
+
) {
|
|
88
|
+
storageMap = {
|
|
89
|
+
isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
|
|
90
|
+
responseTimeInMs: (data.dataToProcess as ProbeMonitorResponse)
|
|
91
|
+
.responseTimeInMs,
|
|
92
|
+
failureCause: (data.dataToProcess as ProbeMonitorResponse)
|
|
93
|
+
.failureCause,
|
|
94
|
+
isTimeout: (data.dataToProcess as ProbeMonitorResponse).isTimeout,
|
|
95
|
+
} as JSONObject;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (data.monitorType === MonitorType.SSLCertificate) {
|
|
99
|
+
const sslResponse: SslMonitorResponse | undefined = (
|
|
100
|
+
data.dataToProcess as ProbeMonitorResponse
|
|
101
|
+
).sslResponse;
|
|
102
|
+
storageMap = {
|
|
103
|
+
isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
|
|
104
|
+
isSelfSigned: sslResponse?.isSelfSigned,
|
|
105
|
+
createdAt: sslResponse?.createdAt,
|
|
106
|
+
expiresAt: sslResponse?.expiresAt,
|
|
107
|
+
commonName: sslResponse?.commonName,
|
|
108
|
+
organizationalUnit: sslResponse?.organizationalUnit,
|
|
109
|
+
organization: sslResponse?.organization,
|
|
110
|
+
locality: sslResponse?.locality,
|
|
111
|
+
state: sslResponse?.state,
|
|
112
|
+
country: sslResponse?.country,
|
|
113
|
+
serialNumber: sslResponse?.serialNumber,
|
|
114
|
+
fingerprint: sslResponse?.fingerprint,
|
|
115
|
+
fingerprint256: sslResponse?.fingerprint256,
|
|
116
|
+
failureCause: (data.dataToProcess as ProbeMonitorResponse)
|
|
117
|
+
.failureCause,
|
|
118
|
+
} as JSONObject;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (data.monitorType === MonitorType.Server) {
|
|
122
|
+
const serverResponse: ServerMonitorResponse =
|
|
123
|
+
data.dataToProcess as ServerMonitorResponse;
|
|
124
|
+
const infraMetrics: BasicInfrastructureMetrics | undefined =
|
|
125
|
+
serverResponse.basicInfrastructureMetrics;
|
|
126
|
+
|
|
127
|
+
storageMap = {
|
|
128
|
+
hostname: serverResponse.hostname,
|
|
129
|
+
requestReceivedAt: serverResponse.requestReceivedAt,
|
|
130
|
+
failureCause: serverResponse.failureCause,
|
|
131
|
+
} as JSONObject;
|
|
132
|
+
|
|
133
|
+
// Add CPU metrics if available
|
|
134
|
+
if (infraMetrics?.cpuMetrics) {
|
|
135
|
+
storageMap["cpuUsagePercent"] = infraMetrics.cpuMetrics.percentUsed;
|
|
136
|
+
storageMap["cpuCores"] = infraMetrics.cpuMetrics.cores;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add memory metrics if available
|
|
140
|
+
if (infraMetrics?.memoryMetrics) {
|
|
141
|
+
storageMap["memoryUsagePercent"] =
|
|
142
|
+
infraMetrics.memoryMetrics.percentUsed;
|
|
143
|
+
storageMap["memoryFreePercent"] =
|
|
144
|
+
infraMetrics.memoryMetrics.percentFree;
|
|
145
|
+
storageMap["memoryTotalBytes"] = infraMetrics.memoryMetrics.total;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Add disk metrics if available
|
|
149
|
+
if (infraMetrics?.diskMetrics) {
|
|
150
|
+
storageMap["diskMetrics"] = infraMetrics.diskMetrics.map(
|
|
151
|
+
(disk: BasicDiskMetrics) => {
|
|
152
|
+
return {
|
|
153
|
+
diskPath: disk.diskPath,
|
|
154
|
+
usagePercent: disk.percentUsed,
|
|
155
|
+
freePercent: disk.percentFree,
|
|
156
|
+
totalBytes: disk.total,
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Add processes if available
|
|
163
|
+
if (serverResponse.processes) {
|
|
164
|
+
storageMap["processes"] = serverResponse.processes.map(
|
|
165
|
+
(process: ServerProcess) => {
|
|
166
|
+
return {
|
|
167
|
+
pid: process.pid,
|
|
168
|
+
name: process.name,
|
|
169
|
+
command: process.command,
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
data.monitorType === MonitorType.SyntheticMonitor ||
|
|
178
|
+
data.monitorType === MonitorType.CustomJavaScriptCode
|
|
179
|
+
) {
|
|
180
|
+
const customCodeResponse: CustomCodeMonitorResponse | undefined = (
|
|
181
|
+
data.dataToProcess as ProbeMonitorResponse
|
|
182
|
+
).customCodeMonitorResponse;
|
|
183
|
+
const syntheticResponse: SyntheticMonitorResponse[] | undefined = (
|
|
184
|
+
data.dataToProcess as ProbeMonitorResponse
|
|
185
|
+
).syntheticMonitorResponse;
|
|
186
|
+
|
|
187
|
+
storageMap = {
|
|
188
|
+
executionTimeInMs: customCodeResponse?.executionTimeInMS,
|
|
189
|
+
result: customCodeResponse?.result,
|
|
190
|
+
scriptError: customCodeResponse?.scriptError,
|
|
191
|
+
logMessages: customCodeResponse?.logMessages || [],
|
|
192
|
+
failureCause: (data.dataToProcess as ProbeMonitorResponse)
|
|
193
|
+
.failureCause,
|
|
194
|
+
} as JSONObject;
|
|
195
|
+
|
|
196
|
+
// Add synthetic monitor specific fields if available
|
|
197
|
+
if (syntheticResponse && syntheticResponse.length > 0) {
|
|
198
|
+
const firstResponse: SyntheticMonitorResponse = syntheticResponse[0]!;
|
|
199
|
+
if (firstResponse) {
|
|
200
|
+
storageMap["screenshots"] = firstResponse.screenshots;
|
|
201
|
+
storageMap["browserType"] = firstResponse.browserType;
|
|
202
|
+
storageMap["screenSizeType"] = firstResponse.screenSizeType;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
logger.error(err);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
logger.debug(`Storage Map: ${JSON.stringify(storageMap, null, 2)}`);
|
|
211
|
+
|
|
212
|
+
return storageMap;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Replace {{var}} placeholders in the given string with values from the storage map.
|
|
217
|
+
*/
|
|
218
|
+
public static processTemplateString(data: {
|
|
219
|
+
value: string | undefined;
|
|
220
|
+
storageMap: JSONObject;
|
|
221
|
+
}): string {
|
|
222
|
+
try {
|
|
223
|
+
const { value, storageMap } = data;
|
|
224
|
+
|
|
225
|
+
if (!value) {
|
|
226
|
+
return "";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let replaced: string = VMUtil.replaceValueInPlace(
|
|
230
|
+
storageMap,
|
|
231
|
+
value,
|
|
232
|
+
false,
|
|
233
|
+
);
|
|
234
|
+
replaced =
|
|
235
|
+
replaced !== undefined && replaced !== null ? `${replaced}` : "";
|
|
236
|
+
|
|
237
|
+
logger.debug(`Original Value: ${data.value}`);
|
|
238
|
+
logger.debug(`Replaced Value: ${replaced}`);
|
|
239
|
+
|
|
240
|
+
return replaced;
|
|
241
|
+
} catch (err) {
|
|
242
|
+
logger.error(err);
|
|
243
|
+
return data.value || "";
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
package/Server/Utils/VM/VMAPI.ts
CHANGED
|
@@ -82,10 +82,19 @@ export default class VMUtil {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
for (const variable of variablesInArgument) {
|
|
85
|
-
const
|
|
85
|
+
const foundValue: JSONValue = VMUtil.deepFind(
|
|
86
86
|
storageMap as any,
|
|
87
87
|
variable as any,
|
|
88
|
-
)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
let valueToReplaceInPlace: string;
|
|
91
|
+
|
|
92
|
+
// Properly serialize objects to JSON strings
|
|
93
|
+
if (typeof foundValue === "object" && foundValue !== null) {
|
|
94
|
+
valueToReplaceInPlace = JSON.stringify(foundValue, null, 2);
|
|
95
|
+
} else {
|
|
96
|
+
valueToReplaceInPlace = foundValue as string;
|
|
97
|
+
}
|
|
89
98
|
|
|
90
99
|
if (valueToReplaceInPlaceCopy.trim() === "{{" + variable + "}}") {
|
|
91
100
|
valueToReplaceInPlaceCopy = valueToReplaceInPlace;
|
|
@@ -14,9 +14,14 @@ import ReactFlow, {
|
|
|
14
14
|
Position,
|
|
15
15
|
} from "reactflow";
|
|
16
16
|
import "reactflow/dist/style.css";
|
|
17
|
-
import type { ElkExtendedEdge, ElkNode } from "elkjs";
|
|
17
|
+
import type { ElkExtendedEdge, ElkNode, LayoutOptions } from "elkjs";
|
|
18
18
|
import ELK from "elkjs/lib/elk.bundled.js";
|
|
19
19
|
|
|
20
|
+
// Minimal interface for the ELK layout engine we rely on.
|
|
21
|
+
interface ElkLayoutEngine {
|
|
22
|
+
layout: (graph: ElkNode) => Promise<ElkNode>;
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
export interface ServiceNodeData {
|
|
21
26
|
id: string;
|
|
22
27
|
name: string;
|
|
@@ -96,7 +101,7 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
|
|
96
101
|
const [rfEdges, setRfEdges] = useState<Edge[]>([]);
|
|
97
102
|
|
|
98
103
|
useEffect((): void => {
|
|
99
|
-
const elk:
|
|
104
|
+
const elk: ElkLayoutEngine = new ELK() as unknown as ElkLayoutEngine;
|
|
100
105
|
// fixed node dimensions for layout (px)
|
|
101
106
|
const NODE_WIDTH: number = 220;
|
|
102
107
|
const NODE_HEIGHT: number = 56;
|
|
@@ -123,7 +128,7 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
|
|
123
128
|
"elk.layered.spacing.nodeNodeBetweenLayers": "120",
|
|
124
129
|
"elk.spacing.nodeNode": "60",
|
|
125
130
|
"elk.edgeRouting": "POLYLINE",
|
|
126
|
-
},
|
|
131
|
+
} as LayoutOptions,
|
|
127
132
|
children: sortedServices.map((svc: ServiceNodeData): ElkNode => {
|
|
128
133
|
return {
|
|
129
134
|
id: svc.id,
|
|
@@ -142,9 +147,9 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
|
|
142
147
|
|
|
143
148
|
const layout: () => Promise<void> = async (): Promise<void> => {
|
|
144
149
|
try {
|
|
145
|
-
const res:
|
|
150
|
+
const res: ElkNode = (await elk.layout(elkGraph)) as ElkNode; // casting to bundled ElkNode shape
|
|
146
151
|
const placedNodes: Node[] = (res.children || []).map(
|
|
147
|
-
(child:
|
|
152
|
+
(child: ElkNode): Node => {
|
|
148
153
|
const svc: ServiceNodeData | undefined = sortedServices.find(
|
|
149
154
|
(s: ServiceNodeData): boolean => {
|
|
150
155
|
return s.id === child.id;
|
|
@@ -64,7 +64,8 @@ const Input: FunctionComponent<ComponentProps> = (
|
|
|
64
64
|
|
|
65
65
|
const [value, setValue] = useState<string | Date>("");
|
|
66
66
|
const [displayValue, setDisplayValue] = useState<string>("");
|
|
67
|
-
const ref:
|
|
67
|
+
const ref: React.MutableRefObject<HTMLInputElement | null> =
|
|
68
|
+
useRef<HTMLInputElement | null>(null);
|
|
68
69
|
|
|
69
70
|
useEffect(() => {
|
|
70
71
|
if (
|
|
@@ -120,9 +121,9 @@ const Input: FunctionComponent<ComponentProps> = (
|
|
|
120
121
|
}, [value]);
|
|
121
122
|
|
|
122
123
|
useEffect(() => {
|
|
123
|
-
const input:
|
|
124
|
+
const input: HTMLInputElement | null = ref.current;
|
|
124
125
|
if (input) {
|
|
125
|
-
|
|
126
|
+
input.value = displayValue;
|
|
126
127
|
}
|
|
127
128
|
}, [ref, displayValue]);
|
|
128
129
|
|
|
@@ -195,7 +196,7 @@ const Input: FunctionComponent<ComponentProps> = (
|
|
|
195
196
|
tabIndex={props.tabIndex}
|
|
196
197
|
onKeyDown={
|
|
197
198
|
props.onEnterPress
|
|
198
|
-
? (event:
|
|
199
|
+
? (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
199
200
|
if (event.key === "Enter") {
|
|
200
201
|
props.onEnterPress?.();
|
|
201
202
|
}
|
|
@@ -27,7 +27,9 @@ export interface ComponentProps {
|
|
|
27
27
|
const ArgumentsForm: FunctionComponent<ComponentProps> = (
|
|
28
28
|
props: ComponentProps,
|
|
29
29
|
): ReactElement => {
|
|
30
|
-
const formRef:
|
|
30
|
+
const formRef: React.MutableRefObject<FormProps<
|
|
31
|
+
FormValues<JSONObject>
|
|
32
|
+
> | null> = useRef<FormProps<FormValues<JSONObject>> | null>(null);
|
|
31
33
|
const [component, setComponent] = useState<NodeDataProp>(props.component);
|
|
32
34
|
const [showVariableModal, setShowVariableModal] = useState<boolean>(false);
|
|
33
35
|
const [showComponentPickerModal, setShowComponentPickerModal] =
|
|
@@ -143,7 +145,7 @@ const ArgumentsForm: FunctionComponent<ComponentProps> = (
|
|
|
143
145
|
}}
|
|
144
146
|
onSave={(variableId: string) => {
|
|
145
147
|
setShowVariableModal(false);
|
|
146
|
-
formRef.current
|
|
148
|
+
formRef.current?.setFieldValue(
|
|
147
149
|
selectedArgId,
|
|
148
150
|
(component.arguments && component.arguments[selectedArgId]
|
|
149
151
|
? component.arguments[selectedArgId]
|
|
@@ -161,7 +163,7 @@ const ArgumentsForm: FunctionComponent<ComponentProps> = (
|
|
|
161
163
|
}}
|
|
162
164
|
onSave={(returnValuePath: string) => {
|
|
163
165
|
setShowComponentPickerModal(false);
|
|
164
|
-
formRef.current
|
|
166
|
+
formRef.current?.setFieldValue(
|
|
165
167
|
selectedArgId,
|
|
166
168
|
(component.arguments && component.arguments[selectedArgId]
|
|
167
169
|
? component.arguments[selectedArgId]
|