Merge pull request #1700 from YinDongFang/feature/json-validation

Feature/json validation
This commit is contained in:
Alexander Holliday
2025-02-11 21:00:20 -08:00
committed by GitHub
7 changed files with 121 additions and 10 deletions
+14
View File
@@ -38,6 +38,20 @@ const MonitorSchema = mongoose.Schema(
"distributed_http",
],
},
jsonPath: {
type: String,
},
expectedValue: {
type: String,
},
matchMethod: {
type: String,
enum: [
"equal",
"include",
"regex",
],
},
url: {
type: String,
required: true,
+7 -1
View File
@@ -144,5 +144,11 @@
"monitorResume": "Monitor resumed successfully",
"statusPageDelete": "Status page deleted successfully",
"statusPageUpdate": "Status page updated successfully",
"statusPageByTeamId": "Got status pages by team id successfully"
"statusPageByTeamId": "Got status pages by team id successfully",
"httpNetworkError": "Network error",
"httpNotJson": "Response data is not json",
"httpJsonPathError": "Failed to parse json data",
"httpEmptyResult": "Result is empty",
"httpMatchSuccess": "Response data match successfully",
"httpMatchFail": "Failed to match response data"
}
+10
View File
@@ -19,6 +19,7 @@
"handlebars": "^4.7.8",
"helmet": "^8.0.0",
"ioredis": "^5.4.2",
"jmespath": "^0.16.0",
"joi": "^17.13.1",
"jsonwebtoken": "9.0.2",
"mailersend": "^2.2.0",
@@ -4441,6 +4442,15 @@
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jmespath": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
"license": "Apache-2.0",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/joi": {
"version": "17.13.3",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
+1
View File
@@ -26,6 +26,7 @@
"handlebars": "^4.7.8",
"helmet": "^8.0.0",
"ioredis": "^5.4.2",
"jmespath": "^0.16.0",
"joi": "^17.13.1",
"jsonwebtoken": "9.0.2",
"mailersend": "^2.2.0",
+59 -9
View File
@@ -1,3 +1,4 @@
import jmespath from 'jmespath';
const SERVICE_NAME = "NetworkService";
const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
@@ -122,20 +123,19 @@ class NetworkService {
*/
async requestHttp(job) {
try {
const url = job.data.url;
const { url, secret, _id, name, teamId, type, jsonPath, matchMethod, expectedValue } = job.data;
const config = {};
job.data.secret !== undefined &&
(config.headers = { Authorization: `Bearer ${job.data.secret}` });
secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
const { response, responseTime, error } = await this.timeRequest(() =>
this.axios.get(url, config)
);
const httpResponse = {
monitorId: job.data._id,
teamId: job.data.teamId,
type: job.data.type,
monitorId: _id,
teamId,
type,
responseTime,
payload: response?.data,
};
@@ -144,12 +144,62 @@ class NetworkService {
const code = error.response?.status || this.NETWORK_ERROR;
httpResponse.code = code;
httpResponse.status = false;
httpResponse.message = this.http.STATUS_CODES[code] || "Network Error";
httpResponse.message = this.http.STATUS_CODES[code] || this.stringService.httpNetworkError;
return httpResponse;
}
httpResponse.status = true;
httpResponse.code = response.status;
httpResponse.message = this.http.STATUS_CODES[response.status];
if (!expectedValue) {
// not configure expected value, return
httpResponse.status = true;
httpResponse.message = this.http.STATUS_CODES[response.status];
return httpResponse;
}
// validate if response data match expected value
let result = response?.data;
this.logger.info({
service: this.SERVICE_NAME,
method: "requestHttp",
message: `Job: [${name}](${_id}) match result with expected value`,
details: { expectedValue, result, jsonPath, matchMethod }
});
if (jsonPath) {
const contentType = response.headers['content-type'];
const isJson = contentType?.includes('application/json');
if (!isJson) {
httpResponse.status = false;
httpResponse.message = this.stringService.httpNotJson;
return httpResponse;
}
try {
result = jmespath.search(result, jsonPath);
} catch (error) {
httpResponse.status = false;
httpResponse.message = this.stringService.httpJsonPathError;
return httpResponse;
}
}
if (result === null || result === undefined) {
httpResponse.status = false;
httpResponse.message = this.stringService.httpEmptyResult;
return httpResponse;
}
let match;
result = typeof result === "object" ? JSON.stringify(result) : result.toString();
if (matchMethod === "include") match = result.includes(expectedValue);
else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
else match = result === expectedValue;
httpResponse.status = match;
httpResponse.message = match ? this.stringService.httpMatchSuccess : this.stringService.httpMatchFail;
return httpResponse;
} catch (error) {
error.service = this.SERVICE_NAME;
+24
View File
@@ -321,6 +321,30 @@ class StringService {
return this.translationService.getTranslation('getAppSettings');
}
get httpNetworkError() {
return this.translationService.getTranslation('httpNetworkError');
}
get httpNotJson() {
return this.translationService.getTranslation('httpNotJson');
}
get httpJsonPathError() {
return this.translationService.getTranslation('httpJsonPathError');
}
get httpEmptyResult() {
return this.translationService.getTranslation('httpEmptyResult');
}
get httpMatchSuccess() {
return this.translationService.getTranslation('httpMatchSuccess');
}
get httpMatchFail() {
return this.translationService.getTranslation('httpMatchFail');
}
get updateAppSettings() {
return this.translationService.getTranslation('updateAppSettings');
}
+6
View File
@@ -194,6 +194,9 @@ const createMonitorBodyValidation = joi.object({
}),
notifications: joi.array().items(joi.object()),
secret: joi.string(),
jsonPath: joi.string(),
expectedValue: joi.string(),
matchMethod: joi.string(),
});
const editMonitorBodyValidation = joi.object({
@@ -202,6 +205,9 @@ const editMonitorBodyValidation = joi.object({
interval: joi.number(),
notifications: joi.array().items(joi.object()),
secret: joi.string(),
jsonPath: joi.string(),
expectedValue: joi.string(),
matchMethod: joi.string(),
});
const pauseMonitorParamValidation = joi.object({