Merge pull request #1084 from bluewave-labs/feat/be/hardware-monitor-type

feat/be/hardware monitor type
This commit is contained in:
Alexander Holliday
2024-10-30 09:08:05 +08:00
committed by GitHub
8 changed files with 132 additions and 41 deletions

View File

@@ -36,11 +36,31 @@ const HardwareCheckSchema = mongoose.Schema(
type: mongoose.Schema.Types.ObjectId,
ref: "Monitor",
immutable: true,
index: true,
},
status: {
type: Boolean,
index: true,
},
responseTime: {
type: Number,
},
statusCode: {
type: Number,
index: true,
},
message: {
type: String,
},
expiry: {
type: Date,
default: Date.now,
expires: 60 * 60 * 24 * 30, // 30 days
},
cpu: {
type: cpuSchema,
default: () => ({}),

View File

@@ -61,6 +61,9 @@ const MonitorSchema = mongoose.Schema(
ref: "Notification",
},
],
secret: {
type: String,
},
},
{
timestamps: true,

View File

@@ -163,7 +163,6 @@ class JobQueue {
// Get the current status
const networkResponse = await this.networkService.getStatus(job);
// Handle status change
const { monitor, statusChanged, prevStatus } =
await this.statusService.updateStatus(networkResponse);

View File

@@ -1,5 +1,4 @@
import { errorMessages, successMessages } from "../utils/messages.js";
/**
* Constructs a new NetworkService instance.
*
@@ -94,6 +93,7 @@ class NetworkService {
* @param {Object} job.data - The data object within the job.
* @param {string} job.data.url - The URL to send the HTTP GET request to.
* @param {string} job.data._id - The monitor ID for the HTTP request.
* @param {string} [job.data.secret] - Secret for authorization if provided.
* @returns {Promise<Object>} An object containing the HTTP response details.
* @property {string} monitorId - The monitor ID for the HTTP request.
* @property {string} type - The type of request, which is "http".
@@ -105,13 +105,18 @@ class NetworkService {
*/
async requestHttp(job) {
const url = job.data.url;
const config = {};
job.data.secret !== undefined &&
(config.headers = { Authorization: `Bearer ${job.data.secret}` });
const { response, responseTime, error } = await this.timeRequest(() =>
this.axios.get(url)
this.axios.get(url, config)
);
const httpResponse = {
monitorId: job.data._id,
type: "http",
type: job.data.type,
responseTime,
payload: response?.data,
};
@@ -147,34 +152,15 @@ class NetworkService {
*/
async requestPagespeed(job) {
const url = job.data.url;
const { response, responseTime, error } = await this.timeRequest(() =>
this.axios.get(
`https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`
)
);
const pagespeedResponse = {
monitorId: job.data._id,
type: "pagespeed",
responseTime,
payload: response?.data,
};
if (error) {
const code = error.response?.status || this.NETWORK_ERROR;
pagespeedResponse.code = code;
pagespeedResponse.status = false;
pagespeedResponse.message = this.http.STATUS_CODES[code] || "Network Error";
return pagespeedResponse;
}
pagespeedResponse.status = true;
pagespeedResponse.code = response.status;
pagespeedResponse.message = this.http.STATUS_CODES[response.status];
return pagespeedResponse;
const updatedJob = { ...job };
const pagespeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`;
updatedJob.data.url = pagespeedUrl;
return this.requestHttp(updatedJob);
}
async requestHandleHardware(job) {}
async requestHardware(job) {
return this.requestHttp(job);
}
/**
* Gets the status of a job based on its type and returns the appropriate response.
@@ -194,7 +180,7 @@ class NetworkService {
case this.TYPE_PAGESPEED:
return await this.requestPagespeed(job);
case this.TYPE_HARDWARE:
return await this.requestHandleHardware(job);
return await this.requestHardware(job);
default:
this.logger.error({
message: `Unsupported type: ${job.data.type}`,

View File

@@ -99,6 +99,13 @@ class StatusService {
check.performance = (categories.performance?.score || 0) * 100;
check.audits = { cls, si, fcp, lcp, tbt };
}
if (type === "hardware") {
check.cpu = payload?.cpu ?? {};
check.memory = payload?.memory ?? {};
check.disk = payload?.disk ?? {};
check.host = payload?.host ?? {};
}
return check;
};
@@ -121,6 +128,7 @@ class StatusService {
http: this.db.createCheck,
ping: this.db.createCheck,
pagespeed: this.db.createPageSpeedCheck,
hardware: this.db.createHardwareCheck,
};
const operation = operationMap[networkResponse.type];
const check = this.buildCheck(networkResponse);
@@ -130,7 +138,7 @@ class StatusService {
message: error.message,
service: this.SERVICE_NAME,
method: "insertCheck",
details: `Error inserting check for monitor: ${networkResponse.monitorId}`,
details: `Error inserting check for monitor: ${networkResponse?.monitorId}`,
stack: error.stack,
});
}

View File

@@ -2,7 +2,6 @@ import sinon from "sinon";
import NetworkService from "../../service/networkService.js";
import { expect } from "chai";
import http from "http";
import exp from "constants";
describe("Network Service", () => {
let axios, ping, logger, networkService;
@@ -74,7 +73,7 @@ describe("Network Service", () => {
});
describe("requestHttp", () => {
it("should return a response object if http successful", async () => {
const job = { data: { url: "http://test.com", _id: "123" } };
const job = { data: { url: "http://test.com", _id: "123", type: "http" } };
const httpResult = await networkService.requestHttp(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("http");
@@ -87,7 +86,7 @@ describe("Network Service", () => {
networkService.timeRequest = sinon
.stub()
.resolves({ response: null, responseTime: 1, error });
const job = { data: { url: "http://test.com", _id: "123" } };
const job = { data: { url: "http://test.com", _id: "123", type: "http" } };
const httpResult = await networkService.requestHttp(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("http");
@@ -101,7 +100,7 @@ describe("Network Service", () => {
networkService.timeRequest = sinon
.stub()
.resolves({ response: null, responseTime: 1, error });
const job = { data: { url: "http://test.com", _id: "123" } };
const job = { data: { url: "http://test.com", _id: "123", type: "http" } };
const httpResult = await networkService.requestHttp(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("http");
@@ -113,7 +112,7 @@ describe("Network Service", () => {
describe("requestPagespeed", () => {
it("should return a response object if pagespeed successful", async () => {
const job = { data: { url: "http://test.com", _id: "123" } };
const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } };
const pagespeedResult = await networkService.requestPagespeed(job);
expect(pagespeedResult.monitorId).to.equal("123");
expect(pagespeedResult.type).to.equal("pagespeed");
@@ -126,7 +125,7 @@ describe("Network Service", () => {
networkService.timeRequest = sinon
.stub()
.resolves({ response: null, responseTime: 1, error });
const job = { data: { url: "http://test.com", _id: "123" } };
const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } };
const pagespeedResult = await networkService.requestPagespeed(job);
expect(pagespeedResult.monitorId).to.equal("123");
expect(pagespeedResult.type).to.equal("pagespeed");
@@ -140,7 +139,7 @@ describe("Network Service", () => {
networkService.timeRequest = sinon
.stub()
.resolves({ response: null, responseTime: 1, error });
const job = { data: { url: "http://test.com", _id: "123" } };
const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } };
const pagespeedResult = await networkService.requestPagespeed(job);
expect(pagespeedResult.monitorId).to.equal("123");
expect(pagespeedResult.type).to.equal("pagespeed");
@@ -150,6 +149,60 @@ describe("Network Service", () => {
});
});
describe("requestHardware", () => {
it("should return a response object if hardware successful", async () => {
const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } };
const httpResult = await networkService.requestHardware(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("hardware");
expect(httpResult.responseTime).to.be.a("number");
expect(httpResult.status).to.be.true;
});
it("should return a response object if hardware successful and job has a secret", async () => {
const job = {
data: {
url: "http://test.com",
_id: "123",
type: "hardware",
secret: "my_secret",
},
};
const httpResult = await networkService.requestHardware(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("hardware");
expect(httpResult.responseTime).to.be.a("number");
expect(httpResult.status).to.be.true;
});
it("should return a response object if hardware unsuccessful", async () => {
const error = new Error("Test error");
error.response = { status: 404 };
networkService.timeRequest = sinon
.stub()
.resolves({ response: null, responseTime: 1, error });
const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } };
const httpResult = await networkService.requestHardware(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("hardware");
expect(httpResult.responseTime).to.be.a("number");
expect(httpResult.status).to.be.false;
expect(httpResult.code).to.equal(404);
});
it("should return a response object if hardware unsuccessful with unknown code", async () => {
const error = new Error("Test error");
error.response = {};
networkService.timeRequest = sinon
.stub()
.resolves({ response: null, responseTime: 1, error });
const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } };
const httpResult = await networkService.requestHardware(job);
expect(httpResult.monitorId).to.equal("123");
expect(httpResult.type).to.equal("hardware");
expect(httpResult.responseTime).to.be.a("number");
expect(httpResult.status).to.be.false;
expect(httpResult.code).to.equal(networkService.NETWORK_ERROR);
});
});
describe("getStatus", () => {
beforeEach(() => {
networkService.requestPing = sinon.stub();
@@ -179,10 +232,10 @@ describe("Network Service", () => {
expect(networkService.requestHttp.notCalled).to.be.true;
expect(networkService.requestPagespeed.calledOnce).to.be.true;
});
it("should call requestHandleHardware if type is hardware", () => {
it("should call requestHardware if type is hardware", () => {
networkService.getStatus({ data: { type: "hardware" } });
expect(networkService.requestHardware.calledOnce).to.be.true;
expect(networkService.requestPing.notCalled).to.be.true;
expect(networkService.requestHttp.notCalled).to.be.true;
expect(networkService.requestPagespeed.notCalled).to.be.true;
});
it("should log an error if an unknown type is provided", () => {

View File

@@ -173,6 +173,26 @@ describe("StatusService", () => {
tbt: 0,
});
});
it("should build a check for hardware type", () => {
const check = statusService.buildCheck({
monitorId: "test",
type: "hardware",
status: true,
responseTime: 100,
code: 200,
message: "Test message",
payload: { cpu: "cpu", memory: "memory", disk: "disk", host: "host" },
});
expect(check.monitorId).to.equal("test");
expect(check.status).to.be.true;
expect(check.statusCode).to.equal(200);
expect(check.responseTime).to.equal(100);
expect(check.message).to.equal("Test message");
expect(check.cpu).to.equal("cpu");
expect(check.memory).to.equal("memory");
expect(check.disk).to.equal("disk");
expect(check.host).to.equal("host");
});
});
describe("insertCheck", () => {
it("should log an error if one is thrown", async () => {

View File

@@ -228,6 +228,7 @@ const createMonitorBodyValidation = joi.object({
usage_disk: joi.number(),
}),
notifications: joi.array().items(joi.object()),
secret: joi.string(),
});
const editMonitorBodyValidation = joi.object({
@@ -235,6 +236,7 @@ const editMonitorBodyValidation = joi.object({
description: joi.string(),
interval: joi.number(),
notifications: joi.array().items(joi.object()),
secret: joi.string(),
});
const pauseMonitorParamValidation = joi.object({