This commit is contained in:
Simon Larsen 2023-10-05 15:59:10 +01:00
parent 58cfe477c2
commit c85d9b8372
No known key found for this signature in database
GPG Key ID: AB45983AA9C81CDE
4 changed files with 124 additions and 96 deletions

View File

@ -105,8 +105,7 @@ export default class ProbeMonitorResponseService {
// get last log. We do this because there are many monitoring steps and we need to store those.
logger.info(
`${dataToProcess.monitorId.toString()} - monitor type ${
monitor.monitorType
`${dataToProcess.monitorId.toString()} - monitor type ${monitor.monitorType
}`
);
@ -133,6 +132,7 @@ export default class ProbeMonitorResponseService {
},
});
if (!monitorProbe) {
throw new BadDataException(
'Probe is not assigned to this monitor'
@ -229,7 +229,7 @@ export default class ProbeMonitorResponseService {
if (incidentTemplate.autoResolveIncident) {
if (
!autoResolveCriteriaInstanceIdIncidentIdsDictionary[
criteriaInstance.data.id.toString()
criteriaInstance.data.id.toString()
]
) {
autoResolveCriteriaInstanceIdIncidentIdsDictionary[
@ -294,13 +294,11 @@ export default class ProbeMonitorResponseService {
if (response.criteriaMetId && response.rootCause) {
logger.info(
`${dataToProcess.monitorId.toString()} - Criteria met: ${
response.criteriaMetId
`${dataToProcess.monitorId.toString()} - Criteria met: ${response.criteriaMetId
}`
);
logger.info(
`${dataToProcess.monitorId.toString()} - Root cause: ${
response.rootCause
`${dataToProcess.monitorId.toString()} - Root cause: ${response.rootCause
}`
);
@ -315,7 +313,7 @@ export default class ProbeMonitorResponseService {
!response.criteriaMetId &&
monitorSteps.data.defaultMonitorStatusId &&
monitor.currentMonitorStatusId?.toString() !==
monitorSteps.data.defaultMonitorStatusId.toString()
monitorSteps.data.defaultMonitorStatusId.toString()
) {
logger.info(
`${dataToProcess.monitorId.toString()} - No criteria met. Change to default status.`
@ -422,7 +420,7 @@ export default class ProbeMonitorResponseService {
input.criteriaInstance.data?.changeMonitorStatus &&
input.criteriaInstance.data?.monitorStatusId &&
input.criteriaInstance.data?.monitorStatusId.toString() !==
input.monitor.currentMonitorStatusId?.toString()
input.monitor.currentMonitorStatusId?.toString()
) {
logger.info(
`${input.monitor.id?.toString()} - Change monitor status to ${input.criteriaInstance.data?.monitorStatusId.toString()}`
@ -476,9 +474,9 @@ export default class ProbeMonitorResponseService {
openIncidents.find((incident: Incident) => {
return (
incident.createdCriteriaId ===
input.criteriaInstance.data?.id.toString() &&
input.criteriaInstance.data?.id.toString() &&
incident.createdIncidentTemplateId ===
criteriaIncident.id.toString()
criteriaIncident.id.toString()
);
});
@ -644,7 +642,7 @@ export default class ProbeMonitorResponseService {
if (
input.autoResolveCriteriaInstanceIdIncidentIdsDictionary[
input.openIncident.createdCriteriaId?.toString()
input.openIncident.createdCriteriaId?.toString()
]
) {
if (
@ -766,7 +764,7 @@ export default class ProbeMonitorResponseService {
if (
FilterCondition.Any ===
input.criteriaInstance.data?.filterCondition &&
input.criteriaInstance.data?.filterCondition &&
didMeetCriteria === true
) {
finalResult = rootCause;
@ -774,7 +772,7 @@ export default class ProbeMonitorResponseService {
if (
FilterCondition.All ===
input.criteriaInstance.data?.filterCondition &&
input.criteriaInstance.data?.filterCondition &&
didMeetCriteria === false
) {
finalResult = null;
@ -783,7 +781,7 @@ export default class ProbeMonitorResponseService {
if (
FilterCondition.All ===
input.criteriaInstance.data?.filterCondition &&
input.criteriaInstance.data?.filterCondition &&
didMeetCriteria &&
rootCause
) {
@ -807,25 +805,45 @@ export default class ProbeMonitorResponseService {
let value: number | string | undefined = input.criteriaFilter.value;
//check is online filter
if (input.criteriaFilter.checkOn === CheckOn.JavaScriptExpression) {
debugger;
let storageMap: JSONObject = {
}
if(input.monitor.monitorType === MonitorType.API || input.monitor.monitorType === MonitorType.Website){
if (input.monitor.monitorType === MonitorType.API || input.monitor.monitorType === MonitorType.Website) {
// try to parse json
let responseBody: JSONObject | null = null;
try {
responseBody = JSON.parse((input.dataToProcess as ProbeMonitorResponse).responseBody as string || '{}');
} catch (err) {
responseBody = (input.dataToProcess as ProbeMonitorResponse).responseBody as JSONObject;
}
if (typeof responseBody === Typeof.String && (responseBody?.toString()) === '') {
// if empty string then set to empty object.
responseBody = {};
}
storageMap = {
responseBody: (input.dataToProcess as ProbeMonitorResponse).responseBody,
responseHeaders: (input.dataToProcess as ProbeMonitorResponse).responseHeaders,
responseStatusCode: (input.dataToProcess as ProbeMonitorResponse).responseCode,
responseTimeInMs: (input.dataToProcess as ProbeMonitorResponse).responseTimeInMs,
isOnline: (input.dataToProcess as ProbeMonitorResponse).isOnline,
responseBody: responseBody,
responseHeaders: (input.dataToProcess as ProbeMonitorResponse).responseHeaders,
responseStatusCode: (input.dataToProcess as ProbeMonitorResponse).responseCode,
responseTimeInMs: (input.dataToProcess as ProbeMonitorResponse).responseTimeInMs,
isOnline: (input.dataToProcess as ProbeMonitorResponse).isOnline,
};
}
if(input.monitor.monitorType === MonitorType.IncomingRequest){
if (input.monitor.monitorType === MonitorType.IncomingRequest) {
storageMap = {
requestBody: (input.dataToProcess as IncomingMonitorRequest).requestBody,
requestHeaders: (input.dataToProcess as IncomingMonitorRequest).requestHeaders,
requestBody: (input.dataToProcess as IncomingMonitorRequest).requestBody,
requestHeaders: (input.dataToProcess as IncomingMonitorRequest).requestHeaders,
};
}
@ -834,16 +852,22 @@ export default class ProbeMonitorResponseService {
expression = VMUtil.replaceValueInPlace(storageMap, expression, false); // now pass this to the VM.
let code: string = `return Boolean(${expression});`;
let result: any = null;
try {
result = await VMUtil.runCodeInSandbox(code, {
timeout: 1000,
allowAsync: false,
args: {},
includeHttpPackage: false
});
} catch (err) {
logger.error(err);
return null;
}
const result = await VMUtil.runCodeInSandbox(code, {
timeout: 1000,
allowAsync: false,
args: {},
includeHttpPackage: false
});
if(result === false){
return `JavaScript Expression - ${expression} - returned false.`;
if (result) {
return `JavaScript Expression - ${expression} - evaluated to true.`;
}
return null; // if true then return null.
@ -895,10 +919,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs! > (value as number)
) {
return `Response time is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is greater than the criteria value of ${value} ms.`;
return `Response time is ${(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is greater than the criteria value of ${value} ms.`;
}
return null;
}
@ -910,10 +933,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs! < (value as number)
) {
return `Response time is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is less than the criteria value of ${value} ms.`;
return `Response time is ${(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is less than the criteria value of ${value} ms.`;
}
return null;
}
@ -925,10 +947,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs === (value as number)
) {
return `Response time is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms.`;
return `Response time is ${(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms.`;
}
return null;
}
@ -940,10 +961,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs !== (value as number)
) {
return `Response time is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is not equal to the criteria value of ${value} ms.`;
return `Response time is ${(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is not equal to the criteria value of ${value} ms.`;
}
return null;
}
@ -958,10 +978,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs! >= (value as number)
) {
return `Response time is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is greater than or equal to the criteria value of ${value} ms.`;
return `Response time is ${(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is greater than or equal to the criteria value of ${value} ms.`;
}
return null;
}
@ -975,10 +994,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs! <= (value as number)
) {
return `Response time is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is less than or equal to the criteria value of ${value} ms.`;
return `Response time is ${(input.dataToProcess as ProbeMonitorResponse)
.responseTimeInMs
} ms which is less than or equal to the criteria value of ${value} ms.`;
}
return null;
}
@ -1010,10 +1028,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseCode! > (value as number)
) {
return `Response status code is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is greater than the criteria value of ${value}.`;
return `Response status code is ${(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is greater than the criteria value of ${value}.`;
}
return null;
}
@ -1025,10 +1042,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseCode! < (value as number)
) {
return `Response status code is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is less than the criteria value of ${value}.`;
return `Response status code is ${(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is less than the criteria value of ${value}.`;
}
return null;
}
@ -1040,10 +1056,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseCode === (value as number)
) {
return `Response status code is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseCode
}.`;
return `Response status code is ${(input.dataToProcess as ProbeMonitorResponse)
.responseCode
}.`;
}
return null;
}
@ -1055,10 +1070,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseCode !== (value as number)
) {
return `Response status code is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is not equal to the criteria value of ${value}.`;
return `Response status code is ${(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is not equal to the criteria value of ${value}.`;
}
return null;
}
@ -1073,10 +1087,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseCode! >= (value as number)
) {
return `Response status code is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is greater than or equal to the criteria value of ${value}.`;
return `Response status code is ${(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is greater than or equal to the criteria value of ${value}.`;
}
return null;
}
@ -1090,10 +1103,9 @@ export default class ProbeMonitorResponseService {
(input.dataToProcess as ProbeMonitorResponse)
.responseCode! <= (value as number)
) {
return `Response status code is ${
(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is less than or equal to the criteria value of ${value}.`;
return `Response status code is ${(input.dataToProcess as ProbeMonitorResponse)
.responseCode
} which is less than or equal to the criteria value of ${value}.`;
}
return null;
}
@ -1139,7 +1151,7 @@ export default class ProbeMonitorResponseService {
if (input.criteriaFilter.checkOn === CheckOn.ResponseHeader) {
const headerKeys: Array<string> = Object.keys(
(input.dataToProcess as ProbeMonitorResponse).responseHeaders ||
{}
{}
).map((key: string) => {
return key.toLowerCase();
});
@ -1171,7 +1183,7 @@ export default class ProbeMonitorResponseService {
if (input.criteriaFilter.checkOn === CheckOn.ResponseHeaderValue) {
const headerValues: Array<string> = Object.values(
(input.dataToProcess as ProbeMonitorResponse).responseHeaders ||
{}
{}
).map((key: string) => {
return key.toLowerCase();
});

View File

@ -11,8 +11,10 @@ import Dropdown, {
DropdownValue,
} from 'CommonUI/src/Components/Dropdown/Dropdown';
import Input from 'CommonUI/src/Components/Input/Input';
import Link from 'CommonUI/src/Components/Link/Link';
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
import URL from 'Common/Types/API/URL';
export interface ComponentProps {
initialValue: CriteriaFilter | undefined;
@ -150,7 +152,12 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
return i.value === FilterType.EvaluatesToTrue;
});
setValuePlaceholder('{{response.body.result}} === true');
if (props.monitorType === MonitorType.IncomingRequest) {
setValuePlaceholder('{{requestBody.result}} === true');
} else {
setValuePlaceholder('{{responseBody.result}} === true');
}
}
if (criteriaFilter?.checkOn === CheckOn.ResponseStatusCode) {
@ -267,7 +274,7 @@ const CriteriaFilterElement: FunctionComponent<ComponentProps> = (
</div>
</div>
{criteriaFilter?.checkOn === CheckOn.JavaScriptExpression ? (
<div>Some documentation</div>
<div className="mt-1 text-sm text-gray-500 underline"><Link to={URL.fromString("https://github.com/OneUptime/oneuptime/blob/master/Docs/Monitor/JavaScriptExpression.md")} openInNewTab={true} > Read documentation for using JavaScript exporession here. </Link> </div>
) : (
<></>
)}

View File

@ -20,24 +20,28 @@ The following variables are available in the context of the monitored object:
The following example shows how to use a JavaScript expression to monitor a website for a specific string in the response body:
```javascript
responseBody.item === "hello"
"{{responseBody.item}}" === "hello"
// or you can use response headers
responseHeaders.contentType === "text/html"
"{{responseHeaders.contentType}} === "text/html"
// you can also use regular expressions
responseBody.match(/hello/)
"{{responseBody.item}}".match(/hello/)
// you can also use response status code
responseStatusCode === 200
{{responseStatusCode}} === 200
// you can combine multiple expressions using logical operators
responseBody.item === "hello" && responseStatusCode === 200
"{{responseBody.item}}" === "hello" && {{responseStatusCode}} === 200
// for arrays you can use the following
"{{responseBody.items[0].name}}" === "hello"
```
### Incoming Request monitors
@ -55,21 +59,26 @@ The following variables are available in the context of the monitored object:
The following example shows how to use a JavaScript expression to monitor an incoming request for a specific string in the request body:
```javascript
requestBody.item === "hello"
"{{requestBody.item}}" === "hello"
// or you can use request headers
requestHeaders.contentType === "text/html"
"{{requestHeaders.contentType}}" === "text/html"
// you can also use regular expressions
requestBody.match(/hello/)
"{{requestBody.item}}".match(/hello/)
// you can combine multiple expressions using logical operators
requestBody.item === "hello" && requestHeaders.contentType === "text/html"
"{{requestBody.item}"} === "hello" && "{{requestHeaders.contentType}}" === "text/html"
// you can use the following for arrays
"{{requestBody.items[0].name}}" === "hello"
```
### Things to consider
* scripts have a timeout of 1 second, it will return `false` if the script takes longer than 1 second to execute.
* scripts have a timeout of 1 second, it will return `false` if the script takes longer than 1 second to execute.
* `{{var}}` will replace the variable with the value, so if you want to compare a string, you need to wrap it in quotes, e.g. `"{{responseBody.item}}" === "hello"` and if you want to compare a number, you don't need to wrap it in quotes, e.g. `{{responseStatusCode}} === 200`

View File

@ -95,7 +95,7 @@ export default class MonitorUtil {
criteria.data?.filters;
for (const filter of filters) {
if (filter.checkOn === CheckOn.ResponseBody) {
if (filter.checkOn === CheckOn.ResponseBody || filter.checkOn === CheckOn.JavaScriptExpression) {
return false;
}
}