Ver Fonte

admin-panel: validate check-editor for empties before save

Christian Kahlau há 2 anos atrás
pai
commit
b8b84ab1fb

+ 1 - 1
ng/src/app/components/confirm-modal/confirm-modal.component.html

@@ -7,7 +7,7 @@
     <p>{{ strings.message }}</p>
   </div>
   <div class="modal-footer">
-    <button type="button" class="btn btn-outline-danger" (click)="modal.close(false)">{{ strings.no }}</button>
+    <button *ngIf="strings.no !== null" type="button" class="btn btn-outline-danger" (click)="modal.close(false)">{{ strings.no }}</button>
     <button type="button" class="btn btn-outline-primary" (click)="modal.close(true)">{{ strings.yes }}</button>
   </div>
 </ng-template>

+ 2 - 2
ng/src/app/components/confirm-modal/confirm-modal.component.ts

@@ -5,7 +5,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 export type ConfirmModalOptions = {
   buttonTitles?: {
     yes?: string;
-    no?: string;
+    no?: string | null;
   };
   modalContent?: {
     title?: string;
@@ -23,7 +23,7 @@ export class ConfirmModalComponent {
 
   @Output() ref: EventEmitter<ConfirmModalComponent> = new EventEmitter();
 
-  private defaults = {
+  private defaults: { yes: string; no: string | null; title: string; message: string } = {
     yes: 'Yes',
     no: 'No',
     title: 'Please confirm:',

+ 9 - 7
ng/src/app/components/service-check-editor/service-check-adapter/service-check-adapter.component.ts

@@ -23,14 +23,8 @@ export class ServiceCheckAdapterComponent extends ServiceCheckEditorComponent {
         this.disjunction = model as CheckDisjunction;
       }
     }
-
-    this._model = model;
-  }
-  public get model() {
-    return this._model;
   }
 
-  private _model!: string | CheckDisjunction | CheckConjunction;
   public stringModel?: string;
   public disjunction?: CheckDisjunction;
   public conjunction?: CheckConjunction;
@@ -40,6 +34,14 @@ export class ServiceCheckAdapterComponent extends ServiceCheckEditorComponent {
     else if (this.conCmp) return this.conCmp.collect();
     else if (this.disCmp) return this.disCmp.collect();
 
-    throw new Error('Collect in adapter failed: not type detected');
+    throw new Error('Collect in adapter failed: no type detected');
+  }
+
+  public validate() {
+    if (this.expCmp) return this.expCmp.validate();
+    else if (this.conCmp) return this.conCmp.validate();
+    else if (this.disCmp) return this.disCmp.validate();
+
+    throw new Error('Validate in adapter failed: no type detected');
   }
 }

+ 10 - 0
ng/src/app/components/service-check-editor/service-check-conjunction/service-check-conjunction.component.ts

@@ -1,4 +1,5 @@
 import { Component, Input, QueryList, ViewChildren } from '@angular/core';
+import { ValidationErrors } from '@angular/forms';
 import { faCaretDown, faCaretUp, faTimes } from '@fortawesome/free-solid-svg-icons';
 
 import { ServiceCheckAdapterComponent } from 'src/app/components/service-check-editor/service-check-adapter/service-check-adapter.component';
@@ -19,6 +20,15 @@ export class ServiceCheckConjunctionComponent extends ServiceCheckEditorComponen
     return { and: this.adapters.map(adpt => adpt.collect()) };
   }
 
+  public validate(): ValidationErrors | null {
+    if (!this.model?.and.length) return { 'empty-and': 'There is an empty AND-expression. Please remove.' };
+    let subValid = {};
+    for (const adp of this.adapters) {
+      subValid = { ...subValid, ...adp.validate() };
+    }
+    return Object.keys(subValid).length > 0 ? subValid : null;
+  }
+
   public onSubSort(idx: number, dir: SortDirection) {
     if (!this.model?.and) return;
 

+ 10 - 0
ng/src/app/components/service-check-editor/service-check-disjunction/service-check-disjunction.component.ts

@@ -1,4 +1,5 @@
 import { Component, Input, QueryList, ViewChildren } from '@angular/core';
+import { ValidationErrors } from '@angular/forms';
 import { faCaretDown, faCaretUp, faTimes } from '@fortawesome/free-solid-svg-icons';
 
 import { ServiceCheckAdapterComponent } from 'src/app/components/service-check-editor/service-check-adapter/service-check-adapter.component';
@@ -20,6 +21,15 @@ export class ServiceCheckDisjunctionComponent extends ServiceCheckEditorComponen
     return this.adapters.map(adpt => adpt.collect());
   }
 
+  public validate(): ValidationErrors | null {
+    if (!this.model?.length) return { 'empty-or': 'There is an empty OR-expression. Please remove.' };
+    let subValid = {};
+    for (const adp of this.adapters) {
+      subValid = { ...subValid, ...adp.validate() };
+    }
+    return Object.keys(subValid).length > 0 ? subValid : null;
+  }
+
   public onSubSort(idx: number, dir: SortDirection) {
     if (!this.model) return;
 

+ 4 - 0
ng/src/app/components/service-check-editor/service-check-editor.component.ts

@@ -1,4 +1,5 @@
 import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { ValidationErrors } from '@angular/forms';
 
 export type SortDirection = 'up' | 'down';
 
@@ -7,4 +8,7 @@ export abstract class ServiceCheckEditorComponent {
   @Input() sortVisible: { up: boolean; down: boolean } = { up: true, down: true };
   @Output() sort: EventEmitter<SortDirection> = new EventEmitter();
   @Output() remove: EventEmitter<void> = new EventEmitter();
+
+  abstract collect(): string | CheckDisjunction | CheckConjunction;
+  abstract validate(): ValidationErrors | null;
 }

+ 6 - 0
ng/src/app/components/service-check-editor/service-check-string/service-check-string.component.ts

@@ -1,4 +1,5 @@
 import { Component, Input } from '@angular/core';
+import { ValidationErrors } from '@angular/forms';
 import { faCaretDown, faCaretUp, faTerminal, faTimes } from '@fortawesome/free-solid-svg-icons';
 
 import { ServiceCheckEditorComponent } from '../service-check-editor.component';
@@ -16,4 +17,9 @@ export class ServiceCheckStringComponent extends ServiceCheckEditorComponent {
   public collect() {
     return this.model ?? '';
   }
+
+  public validate(): ValidationErrors | null {
+    if (!this.model?.trim().length) return { 'empty-expr': 'There is an empty regular expression. Please remove.' };
+    return null;
+  }
 }

+ 6 - 0
ng/src/app/components/service-check-form/service-check-form.component.ts

@@ -38,6 +38,12 @@ export class ServiceCheckFormComponent implements OnInit {
   async save() {
     this.serviceCheckForm.updateValueAndValidity();
     const copy = deepCopy(this.serviceCheckForm.value as HttpCheckConfig);
+
+    const invalid = this.checkEditor.validate();
+    if (null !== invalid) {
+      throw { type: 'validation', errors: invalid };
+    }
+
     copy.checks = this.checkEditor.collect();
 
     const savedCheck = await this.serviceApi.saveServiceCheck(this.serviceCheck.serverId as number, copy);

+ 8 - 1
ng/src/app/pages/admin-panel/admin-service-checks-page/admin-service-checks-page.component.ts

@@ -7,6 +7,7 @@ import { ServiceCheckFormComponent } from 'src/app/components/service-check-form
 import { ComponentService } from 'src/app/services/component.service';
 import { ServiceApiService } from 'src/app/services/service-api.service';
 import { ServerApiService } from 'src/app/services/server-api.service';
+import { ValidationErrors } from '@angular/forms';
 
 @Component({
   selector: 'app-admin-service-checks-page',
@@ -123,7 +124,13 @@ export class AdminServiceChecksPageComponent {
         const idx = this.serviceChecks.findIndex(c => savedCheck.id === c.id);
         if (idx >= 0) this.serviceChecks[idx] = savedCheck;
       }
-    } catch (error) {
+    } catch (error: any) {
+      if (error.type === 'validation') {
+        const invalid: ValidationErrors = error.errors;
+        Object.entries(invalid).forEach(entry => {
+          this.cmpService.openConfirmModal({ buttonTitles: { no: null, yes: 'OK' }, modalContent: { title: entry[0], message: entry[1] } });
+        });
+      }
       console.error(error);
     }
   }