|
@@ -0,0 +1,103 @@
|
|
|
|
|
+import { Component, Input } from '@angular/core';
|
|
|
|
|
+
|
|
|
|
|
+import { HttpCheckStatus, ServiceCheckData } from '../../../../../common/lib/http-check-data.module';
|
|
|
|
|
+
|
|
|
|
|
+import { ServiceApiService } from 'src/app/services/service-api.service';
|
|
|
|
|
+
|
|
|
|
|
+type StatusTimelineData = {
|
|
|
|
|
+ width: number;
|
|
|
|
|
+ statusText: string;
|
|
|
|
|
+ statusClass: string;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+@Component({
|
|
|
|
|
+ selector: 'app-service-checks-widget',
|
|
|
|
|
+ templateUrl: './service-checks-widget.component.html',
|
|
|
|
|
+ styleUrls: ['./service-checks-widget.component.scss']
|
|
|
|
|
+})
|
|
|
|
|
+export class ServiceChecksWidgetComponent {
|
|
|
|
|
+ @Input() set server(server: ServerConfig) {
|
|
|
|
|
+ this._server = server;
|
|
|
|
|
+
|
|
|
|
|
+ if (!this._serviceChecks) {
|
|
|
|
|
+ this.fetchServiceChecks();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ public get server() {
|
|
|
|
|
+ return this._server;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public get serviceChecks() {
|
|
|
|
|
+ return this._serviceChecks;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private _server!: ServerConfig;
|
|
|
|
|
+ private _serviceChecks?: Array<HttpCheckConfig & { data?: StatusTimelineData[] }>;
|
|
|
|
|
+
|
|
|
|
|
+ constructor(private serviceApi: ServiceApiService) {}
|
|
|
|
|
+
|
|
|
|
|
+ private async fetchServiceChecks() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this._serviceChecks = await this.serviceApi.loadServiceChecks(this.server.id);
|
|
|
|
|
+
|
|
|
|
|
+ const end = new Date();
|
|
|
|
|
+ const start = new Date(end.getTime() - 1000 * 60 * 60 * 24);
|
|
|
|
|
+ const diffMs = end.getTime() - start.getTime();
|
|
|
|
|
+ this.serviceChecks?.forEach(async check => {
|
|
|
|
|
+ // Query status data of last 24h
|
|
|
|
|
+ const rawData = await this.serviceApi.queryServiceData(this.server.id, check.id, start, end);
|
|
|
|
|
+
|
|
|
|
|
+ // Enhance data for displaying as stacked progress bar
|
|
|
|
|
+ const data: Partial<StatusTimelineData>[] = [];
|
|
|
|
|
+ if (rawData?.length) {
|
|
|
|
|
+ let lastEntry: Partial<StatusTimelineData> | undefined = undefined;
|
|
|
|
|
+ const diffPerc = ((rawData[0].time.getTime() - start.getTime()) / diffMs) * 100;
|
|
|
|
|
+ if (diffPerc > 0) {
|
|
|
|
|
+ lastEntry = {
|
|
|
|
|
+ statusText: rawData[0].data.map(dx => dx.message).join(', '),
|
|
|
|
|
+ statusClass: this.mapStatusClass(rawData[0])
|
|
|
|
|
+ };
|
|
|
|
|
+ data.push(lastEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+ let sumwidth = 0;
|
|
|
|
|
+ rawData.forEach((d, i) => {
|
|
|
|
|
+ if (lastEntry) {
|
|
|
|
|
+ lastEntry.width = ((d.time.getTime() - start.getTime()) / diffMs) * 100 - sumwidth;
|
|
|
|
|
+ sumwidth += lastEntry.width;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ lastEntry = {
|
|
|
|
|
+ statusText: d.data.map(dx => dx.message).join(', '),
|
|
|
|
|
+ statusClass: this.mapStatusClass(d)
|
|
|
|
|
+ };
|
|
|
|
|
+ data.push(lastEntry);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (sumwidth < 100 && lastEntry && !lastEntry.width) {
|
|
|
|
|
+ lastEntry.width = 100 - sumwidth;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ data.push({
|
|
|
|
|
+ width: 100,
|
|
|
|
|
+ statusClass: 'bg-progress',
|
|
|
|
|
+ statusText: '- no data -'
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ check.data = data as StatusTimelineData[];
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ // TODO
|
|
|
|
|
+ console.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private mapStatusClass(data: ServiceCheckData) {
|
|
|
|
|
+ const maxStatus = data.data.reduce((res, d) => (res = Math.max(res, d.status)), 0);
|
|
|
|
|
+ switch (maxStatus) {
|
|
|
|
|
+ case HttpCheckStatus.OK:
|
|
|
|
|
+ return 'bg-peak';
|
|
|
|
|
+ default:
|
|
|
|
|
+ return 'bg-max';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|