Bladeren bron

Server: Endpoint GET /services/{:serverId}/{:serviceId}/data; Docs: Postman collection update

Christian Kahlau 3 jaren geleden
bovenliggende
commit
5c2ca9c6b1

+ 10 - 0
common/lib/http-check-data.module.ts

@@ -13,3 +13,13 @@ export type HttpCheckData = {
   status: HttpCheckStatus;
   message: string;
 };
+
+export type ServiceCheckData = {
+  time: Date;
+  data: Array<ServiceCheckDataEntry>;
+};
+
+export type ServiceCheckDataEntry = {
+  status: HttpCheckStatus;
+  message: string;
+};

+ 1 - 1
common/types/query-response.d.ts

@@ -1,5 +1,5 @@
 type QueryResponse<T> = {
   start: Date;
   end: Date;
-  data: T[];
+  data: T;
 };

+ 1 - 1
ng/src/app/services/server-api.service.ts

@@ -41,7 +41,7 @@ export class ServerApiService {
   public queryServerData(serverID: number, type: string, start: Date, end: Date) {
     return firstValueFrom(
       this.http
-        .get<QueryResponse<ServerData>>(`${environment.apiBaseUrl}server/${serverID}/data`, {
+        .get<QueryResponse<ServerData[]>>(`${environment.apiBaseUrl}server/${serverID}/data`, {
           params: { type, start: start.toString(), end: end.toString() }
         })
         .pipe(map(resp => resp.data.map(data => ({ ...data, time: new Date(data.time) }))))

+ 35 - 0
server/docs/Monitoring.postman_collection.json

@@ -175,6 +175,41 @@
 						}
 					},
 					"response": []
+				},
+				{
+					"name": "/services/{:serverId}/{:serviceId}/data - Query Service Check Data",
+					"request": {
+						"method": "GET",
+						"header": [],
+						"url": {
+							"raw": "http://10.8.0.1:8880/services/2/3/data?start=2022-11-20T00:00:00.000Z&end=2022-11-20T23:59:59.999Z",
+							"protocol": "http",
+							"host": [
+								"10",
+								"8",
+								"0",
+								"1"
+							],
+							"port": "8880",
+							"path": [
+								"services",
+								"2",
+								"3",
+								"data"
+							],
+							"query": [
+								{
+									"key": "start",
+									"value": "2022-11-20T00:00:00.000Z"
+								},
+								{
+									"key": "end",
+									"value": "2022-11-20T23:59:59.999Z"
+								}
+							]
+						}
+					},
+					"response": []
 				}
 			]
 		}

+ 36 - 1
server/src/ctrl/database.class.ts

@@ -6,7 +6,7 @@ import { Database as SQLiteDB, OPEN_CREATE, OPEN_READWRITE } from 'sqlite3';
 
 import defaults from '../../../common/defaults.module';
 import { ServiceConfig, validateParamType } from '../../../common/interfaces/service-config.interface';
-import { HttpCheckData, HttpCheckStatus } from '../../../common/lib/http-check-data.module';
+import { HttpCheckData, HttpCheckStatus, ServiceCheckData, ServiceCheckDataEntry } from '../../../common/lib/http-check-data.module';
 import { Logger } from '../../../common/util/logger.class';
 
 import { ValidationException } from '../lib/validation-exception.class';
@@ -415,6 +415,41 @@ export class Database extends SQLiteController {
     } as HttpCheckData;
   }
 
+  async queryServiceCheckData(serverID: number, confID: number, from: Date, to: Date) {
+    const result = await this.stmt(
+      `
+      SELECT 
+        HealthCheckDataEntry.*
+      FROM HealthCheckDataEntry
+      JOIN HealthCheckConfig ON HealthCheckConfig.ID = HealthCheckDataEntry.ConfigID
+      WHERE HealthCheckConfig.ServerID = ?
+        AND HealthCheckDataEntry.ConfigID = ?
+        AND HealthCheckDataEntry.Timestamp BETWEEN ? AND ?
+      ORDER BY Timestamp, ID;
+    `,
+      [serverID, confID, from.getTime(), to.getTime()]
+    );
+
+    const mapByTimestamp = result.rows.reduce((res: Map<number, ServiceCheckDataEntry[]>, row) => {
+      const time: number = row['Timestamp'];
+      if (!res.has(time)) res.set(time, []);
+      res.get(time)?.push({
+        status: row['Status'] as number,
+        message: row['Message']
+      });
+      return res;
+    }, new Map()) as Map<number, ServiceCheckDataEntry[]>;
+
+    const arr: ServiceCheckData[] = [];
+    for (const entry of mapByTimestamp.entries()) {
+      arr.push({
+        time: new Date(entry[0]),
+        data: entry[1]
+      });
+    }
+    return arr;
+  }
+
   private configFromResultRows(rows: any[]) {
     return rows.reduce((res: ServiceConfig[], line, i) => {
       const configID = line['ID'];

+ 1 - 1
server/src/webhdl/server-api-handler.class.ts

@@ -60,7 +60,7 @@ export class ServerAPIHandler extends WebHandler {
           start,
           end,
           data
-        } as QueryResponse<ServerData>);
+        } as QueryResponse<ServerData[]>);
       } catch (err) {
         next(err);
       }

+ 28 - 0
server/src/webhdl/services-api-handler.class.ts

@@ -1,5 +1,6 @@
 import { RouterOptions, json } from 'express';
 
+import { ServiceCheckData } from '../../../common/lib/http-check-data.module';
 import { HttpStatusException } from '../../../common/lib/http-status.exception';
 
 import { ControllerPool } from '../ctrl/controller-pool.interface';
@@ -56,6 +57,33 @@ export class ServicesAPIHandler extends WebHandler {
         next(err);
       }
     });
+
+    this.router.get('/:serverID/:serviceID/data', async (req, res, next) => {
+      try {
+        const serverID = this.validateNumber(req.params.serverID, 'server id');
+        const serviceID = this.validateNumber(req.params.serviceID, 'service id');
+
+        const qStart = (req.query.start || '').toString();
+        const qEnd = (req.query.end || '').toString();
+
+        if (!qStart || !qEnd) throw new HttpStatusException("QueryParams 'start' and 'end' are mandatory.", 400);
+
+        const start = new Date(qStart);
+        const end = new Date(qEnd);
+        if ([start.toString(), end.toString()].includes('Invalid Date')) {
+          throw new HttpStatusException("QueryParams 'start' and 'end' must be parseable dates or unix epoch timestamps (ms).", 400);
+        }
+
+        const data = await this.ctrlPool.db.queryServiceCheckData(serverID, serviceID, start, end);
+        res.send({
+          start,
+          end,
+          data
+        } as QueryResponse<ServiceCheckData[]>);
+      } catch (err) {
+        next(err);
+      }
+    });
   }
 
   private validateNumber(id: string, field: string) {