]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Every keystroke for the username in the RGW user form triggers an... 23391/head
authorVolker Theile <vtheile@suse.com>
Thu, 2 Aug 2018 11:17:24 +0000 (13:17 +0200)
committerVolker Theile <vtheile@suse.com>
Mon, 6 Aug 2018 08:17:37 +0000 (10:17 +0200)
Fixes: https://tracker.ceph.com/issues/25161
Signed-off-by: Volker Theile <vtheile@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts

index a2609aa0a45d6fc687cf422c24ff8bab3565fe4e..e47a33571925c8442303c1fd4e1257231d92f70b 100644 (file)
@@ -1,10 +1,10 @@
 import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
 import { FormControl, ReactiveFormsModule } from '@angular/forms';
 import { RouterTestingModule } from '@angular/router/testing';
 
 import { BsModalService } from 'ngx-bootstrap/modal';
-import { of as observableOf } from 'rxjs';
+import { Observable, of as observableOf } from 'rxjs';
 
 import { configureTestBed } from '../../../../testing/unit-test-helper';
 import { RgwUserService } from '../../../shared/api/rgw-user.service';
@@ -123,41 +123,49 @@ describe('RgwUserFormComponent', () => {
     it('should validate user id (1/3)', () => {
       const validatorFn = component.userIdValidator();
       const ctrl = new FormControl('');
-      const validatorPromise = validatorFn(ctrl);
-      expect(validatorPromise instanceof Promise).toBeTruthy();
-      if (validatorPromise instanceof Promise) {
-        validatorPromise.then((resp) => {
+      const validator$ = validatorFn(ctrl);
+      expect(validator$ instanceof Observable).toBeTruthy();
+      if (validator$ instanceof Observable) {
+        validator$.subscribe((resp) => {
           expect(resp).toBe(null);
         });
       }
     });
 
-    it('should validate user id (2/3)', () => {
-      const validatorFn = component.userIdValidator();
-      const ctrl = new FormControl('ab');
-      ctrl.markAsDirty();
-      const validatorPromise = validatorFn(ctrl);
-      expect(validatorPromise instanceof Promise).toBeTruthy();
-      if (validatorPromise instanceof Promise) {
-        validatorPromise.then((resp) => {
-          expect(resp).toBe(null);
-        });
-      }
-    });
-
-    it('should validate user id (3/3)', () => {
-      queryResult = ['abc'];
-      const validatorFn = component.userIdValidator();
-      const ctrl = new FormControl('abc');
-      ctrl.markAsDirty();
-      const validatorPromise = validatorFn(ctrl);
-      expect(validatorPromise instanceof Promise).toBeTruthy();
-      if (validatorPromise instanceof Promise) {
-        validatorPromise.then((resp) => {
-          expect(resp instanceof Object).toBeTruthy();
-          expect(resp.userIdExists).toBeTruthy();
-        });
-      }
-    });
+    it(
+      'should validate user id (2/3)',
+      fakeAsync(() => {
+        const validatorFn = component.userIdValidator(0);
+        const ctrl = new FormControl('ab');
+        ctrl.markAsDirty();
+        const validator$ = validatorFn(ctrl);
+        expect(validator$ instanceof Observable).toBeTruthy();
+        if (validator$ instanceof Observable) {
+          validator$.subscribe((resp) => {
+            expect(resp).toBe(null);
+          });
+          tick();
+        }
+      })
+    );
+
+    it(
+      'should validate user id (3/3)',
+      fakeAsync(() => {
+        queryResult = ['abc'];
+        const validatorFn = component.userIdValidator(0);
+        const ctrl = new FormControl('abc');
+        ctrl.markAsDirty();
+        const validator$ = validatorFn(ctrl);
+        expect(validator$ instanceof Observable).toBeTruthy();
+        if (validator$ instanceof Observable) {
+          validator$.subscribe((resp) => {
+            expect(resp instanceof Object).toBeTruthy();
+            expect(resp.userIdExists).toBeTruthy();
+          });
+          tick();
+        }
+      })
+    );
   });
 });
index fb79e8bfe79644809839530bcdc39bece824bb63..f681542fee6ae729cb0aa8a10fd29ad3c968c054 100644 (file)
@@ -4,7 +4,13 @@ import { ActivatedRoute, Router } from '@angular/router';
 
 import * as _ from 'lodash';
 import { BsModalService } from 'ngx-bootstrap';
-import { forkJoin as observableForkJoin, Observable } from 'rxjs';
+import {
+  forkJoin as observableForkJoin,
+  Observable,
+  of as observableOf,
+  timer as observableTimer
+} from 'rxjs';
+import { map, switchMapTo, take } from 'rxjs/operators';
 
 import { RgwUserService } from '../../../shared/api/rgw-user.service';
 import { CdFormBuilder } from '../../../shared/forms/cd-form-builder';
@@ -274,25 +280,31 @@ export class RgwUserFormComponent implements OnInit {
 
   /**
    * Validate the username.
+   * @param {number|Date} dueTime The delay time to wait before the
+   *   API call is executed. This is useful to prevent API calls on
+   *   every keystroke. Defaults to 500.
    */
-  userIdValidator(): AsyncValidatorFn {
+  userIdValidator(dueTime = 500): AsyncValidatorFn {
     const rgwUserService = this.rgwUserService;
-    return (control: AbstractControl): Promise<ValidationErrors | null> => {
-      return new Promise((resolve) => {
-        // Exit immediately if user has not interacted with the control yet
-        // or the control value is empty.
-        if (control.pristine || control.value === '') {
-          resolve(null);
-          return;
-        }
-        rgwUserService.exists(control.value).subscribe((resp: boolean) => {
+    return (control: AbstractControl): Observable<ValidationErrors | null> => {
+      // Exit immediately if user has not interacted with the control yet
+      // or the control value is empty.
+      if (control.pristine || control.value === '') {
+        return observableOf(null);
+      }
+      // Forgot previous requests if a new one arrives within the specified
+      // delay time.
+      return observableTimer(dueTime).pipe(
+        switchMapTo(rgwUserService.exists(control.value)),
+        map((resp: boolean) => {
           if (!resp) {
-            resolve(null);
+            return null;
           } else {
-            resolve({ userIdExists: true });
+            return { userIdExists: true };
           }
-        });
-      });
+        }),
+        take(1)
+      );
     };
   }