]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Fix RGW user/bucket quota issues
authorVolker Theile <vtheile@suse.com>
Mon, 20 May 2019 09:03:23 +0000 (11:03 +0200)
committerVolker Theile <vtheile@suse.com>
Mon, 27 May 2019 13:40:22 +0000 (15:40 +0200)
* RGW user/bucket "max. size" should be hidden when "user/bucket quota" is not enabled
* Only execute validators if necessary

Fixes: https://tracker.ceph.com/issues/39964
Signed-off-by: Volker Theile <vtheile@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-validators.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-validators.ts

index c41346c6bb064493c76cf627dd3a5f75b12337b0..12dd06c55941aff5cc7e9f779a156f2fb6080be2 100644 (file)
           <!-- Maximum size -->
           <div class="form-group"
                [ngClass]="{'has-error': userForm.showError('user_quota_max_size', frm)}"
-               *ngIf="!userForm.getValue('user_quota_max_size_unlimited')">
+               *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_size_unlimited')">
             <label class="control-label col-sm-3"
                    for="user_quota_max_size">
               <ng-container i18n>Max. size</ng-container>
           <!-- Maximum objects -->
           <div class="form-group"
                [ngClass]="{'has-error': userForm.showError('user_quota_max_objects', frm)}"
-               *ngIf="!userForm.getValue('user_quota_max_objects_unlimited')">
+               *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_objects_unlimited')">
             <label class="control-label col-sm-3"
                    for="user_quota_max_objects">
               <ng-container i18n>Max. objects</ng-container>
           <!-- Maximum size -->
           <div class="form-group"
                [ngClass]="{'has-error': userForm.showError('bucket_quota_max_size', frm)}"
-               *ngIf="!userForm.getValue('bucket_quota_max_size_unlimited')">
+               *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_size_unlimited')">
             <label class="control-label col-sm-3"
                    for="bucket_quota_max_size">
               <ng-container i18n>Max. size</ng-container>
           <!-- Maximum objects -->
           <div class="form-group"
                [ngClass]="{'has-error': userForm.showError('bucket_quota_max_objects', frm)}"
-               *ngIf="!userForm.getValue('bucket_quota_max_objects_unlimited')">
+               *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_objects_unlimited')">
             <label class="control-label col-sm-3"
                    for="bucket_quota_max_objects">
               <ng-container i18n>Max. objects</ng-container>
index 770bc09854a4f2e57cad665e8bf1b075fea5da16..eb92b3e5c7693dd5bccb0996e8e22023d94b3138 100644 (file)
@@ -90,11 +90,13 @@ export class RgwUserFormComponent implements OnInit {
       user_quota_max_size: [
         null,
         [
-          CdValidators.requiredIf({
-            user_quota_enabled: true,
-            user_quota_max_size_unlimited: false
-          }),
-          this.quotaMaxSizeValidator
+          CdValidators.composeIf(
+            {
+              user_quota_enabled: true,
+              user_quota_max_size_unlimited: false
+            },
+            [Validators.required, this.quotaMaxSizeValidator]
+          )
         ]
       ],
       user_quota_max_objects_unlimited: [true],
@@ -114,11 +116,13 @@ export class RgwUserFormComponent implements OnInit {
       bucket_quota_max_size: [
         null,
         [
-          CdValidators.requiredIf({
-            bucket_quota_enabled: true,
-            bucket_quota_max_size_unlimited: false
-          }),
-          this.quotaMaxSizeValidator
+          CdValidators.composeIf(
+            {
+              bucket_quota_enabled: true,
+              bucket_quota_max_size_unlimited: false
+            },
+            [Validators.required, this.quotaMaxSizeValidator]
+          )
         ]
       ],
       bucket_quota_max_objects_unlimited: [true],
index e2fe10e236050698de303052f76074ca71789abe..d6b2b00454686631fd5f3d21a087d86dc3d59a1e 100644 (file)
@@ -475,4 +475,55 @@ describe('CdValidators', () => {
       formHelper.expectError('x', 'notUnique');
     }));
   });
+
+  describe('composeIf', () => {
+    beforeEach(() => {
+      form = new CdFormGroup({
+        x: new FormControl(true),
+        y: new FormControl('abc'),
+        z: new FormControl('')
+      });
+      formHelper = new FormHelper(form);
+    });
+
+    it('should not error because all conditions are fulfilled', () => {
+      formHelper.setValue('z', 'zyx');
+      const validatorFn = CdValidators.composeIf(
+        {
+          x: true,
+          y: 'abc'
+        },
+        [Validators.required]
+      );
+      expect(validatorFn(form.get('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.composeIf(
+        {
+          x: false,
+          y: 'xyz'
+        },
+        [Validators.required]
+      );
+      // The validator must succeed because the prereqs do not match, so the
+      // validation of the 'z' control will be skipped.
+      expect(validatorFn(form.get('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.composeIf(
+        {
+          x: true,
+          y: 'abc'
+        },
+        [Validators.required]
+      );
+      // The validator must fail because the value of control 'z' is empty.
+      expect(validatorFn(form.get('z'))).toEqual({ required: true });
+    });
+  });
 });
index dff17050b0129573848d4180d7ed2ec69885cb5f..50fd21c220c5fd64ce42c7937e5c4fef2df09cdb 100644 (file)
@@ -113,7 +113,7 @@ export class CdValidators {
         isWatched = true;
       }
 
-      // Check if all prerequisites matches.
+      // Check if all prerequisites met.
       if (
         !Object.keys(prerequisites).every((key) => {
           return control.parent && control.parent.get(key).value === prerequisites[key];
@@ -128,6 +128,47 @@ export class CdValidators {
     };
   }
 
+  /**
+   * Compose multiple validators into a single function that returns the union of
+   * the individual error maps for the provided control when the given prerequisites
+   * are fulfilled.
+   *
+   * @param {Object} prerequisites An object containing the prerequisites as
+   *   key/value pairs.
+   *   ### Example
+   *   ```typescript
+   *   {
+   *     'generate_key': true,
+   *     'username': 'Max Mustermann'
+   *   }
+   *   ```
+   * @param {ValidatorFn[]} validators List of validators that should be taken
+   *   into action when the prerequisites are met.
+   * @return {ValidatorFn} Returns the validator function.
+   */
+  static composeIf(prerequisites: Object, validators: ValidatorFn[]): ValidatorFn {
+    let isWatched = false;
+    return (control: AbstractControl): ValidationErrors | null => {
+      if (!isWatched && control.parent) {
+        Object.keys(prerequisites).forEach((key) => {
+          control.parent.get(key).valueChanges.subscribe(() => {
+            control.updateValueAndValidity({ emitEvent: false });
+          });
+        });
+        isWatched = true;
+      }
+      // Check if all prerequisites are met.
+      if (
+        !Object.keys(prerequisites).every((key) => {
+          return control.parent && control.parent.get(key).value === prerequisites[key];
+        })
+      ) {
+        return null;
+      }
+      return Validators.compose(validators)(control);
+    };
+  }
+
   /**
    * Custom validation by passing a name for the error and a function as error condition.
    *