mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-04-25 18:40:59 -05:00
Merge pull request #1700 from YinDongFang/feature/json-validation
Feature/json validation
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Generated
+10
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user