]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add custom validators. 21041/head
authorVolker Theile <vtheile@suse.com>
Mon, 26 Mar 2018 09:24:29 +0000 (11:24 +0200)
committerVolker Theile <vtheile@suse.com>
Wed, 11 Apr 2018 06:26:40 +0000 (08:26 +0200)
Signed-off-by: Volker Theile <vtheile@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.ts [new file with mode: 0644]

diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.spec.ts
new file mode 100644 (file)
index 0000000..509932a
--- /dev/null
@@ -0,0 +1,89 @@
+import { FormControl, FormGroup } from '@angular/forms';
+
+import { CdValidators } from './cd-validators';
+
+describe('CdValidators', () => {
+  describe('email', () => {
+    it('should not error on an empty email address', () => {
+      const control = new FormControl('');
+      expect(CdValidators.email(control)).toBeNull();
+    });
+
+    it('should not error on valid email address', () => {
+      const control = new FormControl('dashboard@ceph.com');
+      expect(CdValidators.email(control)).toBeNull();
+    });
+
+    it('should error on invalid email address', () => {
+      const control = new FormControl('xyz');
+      expect(CdValidators.email(control)).toEqual({'email': true});
+    });
+  });
+
+  describe('requiredIf', () => {
+    let form: FormGroup;
+
+    beforeEach(() => {
+      form = new FormGroup({
+        x: new FormControl(true),
+        y: new FormControl('abc'),
+        z: new FormControl('')
+      });
+    });
+
+    it('should not error because all conditions are fulfilled', () => {
+      form.get('z').setValue('zyx');
+      const validatorFn = CdValidators.requiredIf({
+        'x': true,
+        'y': 'abc'
+      });
+      expect(validatorFn(form.controls['z'])).toBeNull();
+    });
+
+    it('should not error because of unmet prerequisites', () => {
+      // Define prereqs that do not match the current values of the form fields.
+      const validatorFn = CdValidators.requiredIf({
+        'x': false,
+        'y': 'xyz'
+      });
+      // The validator must succeed because the prereqs do not match, so the
+      // validation of the 'z' control will be skipped.
+      expect(validatorFn(form.controls['z'])).toBeNull();
+    });
+
+    it('should error because of an empty value', () => {
+      // Define prereqs that force the validator to validate the value of
+      // the 'z' control.
+      const validatorFn = CdValidators.requiredIf({
+        'x': true,
+        'y': 'abc'
+      });
+      // The validator must fail because the value of control 'z' is empty.
+      expect(validatorFn(form.controls['z'])).toEqual({'required': true});
+    });
+
+    it('should not error because of unsuccessful condition', () => {
+      form.get('z').setValue('zyx');
+      // Define prereqs that force the validator to validate the value of
+      // the 'z' control.
+      const validatorFn = CdValidators.requiredIf({
+        'x': true,
+        'z': 'zyx'
+      }, () => false);
+      expect(validatorFn(form.controls['z'])).toBeNull();
+    });
+
+    it('should error because of successful condition', () => {
+      const conditionFn = (value) => {
+        return value === 'abc';
+      };
+      // Define prereqs that force the validator to validate the value of
+      // the 'y' control.
+      const validatorFn = CdValidators.requiredIf({
+        'x': true,
+        'z': ''
+      }, conditionFn);
+      expect(validatorFn(form.controls['y'])).toEqual({'required': true});
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/validators/cd-validators.ts
new file mode 100644 (file)
index 0000000..88edeb8
--- /dev/null
@@ -0,0 +1,65 @@
+import {
+  AbstractControl,
+  ValidationErrors,
+  ValidatorFn,
+  Validators
+} from '@angular/forms';
+
+import * as _ from 'lodash';
+
+type Prerequisites = { // tslint:disable-line
+  [key: string]: any
+};
+
+export function isEmptyInputValue(value: any): boolean {
+  return value == null || value.length === 0;
+}
+
+export class CdValidators {
+  /**
+   * Validator that performs email validation. In contrast to the Angular
+   * email validator an empty email will not be handled as invalid.
+   */
+  static email(control: AbstractControl): ValidationErrors | null {
+    // Exit immediately if value is empty.
+    if (isEmptyInputValue(control.value)) {
+      return null;
+    }
+    return Validators.email(control);
+  }
+
+  /**
+   * Validator that requires controls to fulfill the specified condition if
+   * the specified prerequisites matches. If the prerequisites are fulfilled,
+   * then the given function is executed and if it succeeds, the 'required'
+   * validation error will be returned, otherwise null.
+   * @param {Prerequisites} prerequisites An object containing the prerequisites.
+   *   ### Example
+   *   ```typescript
+   *   {
+   *     'generate_key': true,
+   *     'username': 'Max Mustermann'
+   *   }
+   *   ```
+   *   Only if all prerequisites are fulfilled, then the validation of the
+   *   control will be triggered.
+   * @param {Function | undefined} condition The function to be executed when all
+   *   prerequisites are fulfilled. If not set, then the {@link isEmptyInputValue}
+   *   function will be used by default. The control's value is used as function
+   *   argument. The function must return true to set the validation error.
+   * @return {ValidatorFn} Returns the validator function.
+   */
+  static requiredIf(prerequisites: Prerequisites, condition?: Function | undefined): ValidatorFn {
+    return (control: AbstractControl): ValidationErrors | null => {
+      // Check if all prerequisites matches.
+      if (!Object.keys(prerequisites).every((key) => {
+        return (control.parent && control.parent.get(key).value === prerequisites[key]);
+      })) {
+        return null;
+      }
+      const success = _.isFunction(condition) ? condition.call(condition, control.value) :
+        isEmptyInputValue(control.value);
+      return success ? {'required': true} : null;
+    };
+  }
+}