This is an automated email from the ASF dual-hosted git repository.

hshpak pushed a commit to branch fix/DATALAB-2863/group-name-validation
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 1a6f310ba6e733286c08b82c7071b23b3d0440d1
Author: Hennadii_Shpak <[email protected]>
AuthorDate: Tue Aug 23 12:50:52 2022 +0300

    added directive for validating
---
 .../app/administration/administration.module.ts    |  5 +-
 .../webapp/src/app/administration/roles/index.ts   | 14 +++---
 .../app/administration/roles/roles.component.html  |  2 +
 .../app/administration/roles/roles.component.ts    | 30 +++++++-----
 .../webapp/src/app/core/directives/index.ts        | 19 ++++++--
 .../directives/is-group-name-unique.directive.ts   | 54 ++++++++++++++++++++++
 .../models/role.model.ts}                          | 35 ++++++++------
 .../app/core/services/rolesManagement.service.ts   | 11 +----
 8 files changed, 126 insertions(+), 44 deletions(-)

diff --git 
a/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
 
b/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
index dfbbbf7fd..73bed3c3a 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
@@ -25,10 +25,11 @@ import { ProjectModule } from './project';
 import { RolesModule } from './roles';
 import {ConfigurationModule} from './configuration';
 import {OdahuModule} from './odahu';
+import { DirectivesModule } from '../core/directives';
 
 @NgModule({
-  imports: [CommonModule, ManagenementModule, ProjectModule, RolesModule, 
ConfigurationModule, OdahuModule],
+  imports: [ CommonModule, ManagenementModule, ProjectModule, RolesModule, 
ConfigurationModule, OdahuModule ],
   declarations: [],
-  exports: [ManagenementModule, ProjectModule, RolesModule, 
ConfigurationModule, OdahuModule]
+  exports: [ ManagenementModule, ProjectModule, RolesModule, 
ConfigurationModule, OdahuModule ]
 })
 export class AdministrationModule { }
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/administration/roles/index.ts
 
b/services/self-service/src/main/resources/webapp/src/app/administration/roles/index.ts
index 28c16e255..a3e3984a7 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/administration/roles/index.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/administration/roles/index.ts
@@ -26,15 +26,17 @@ import { FormControlsModule } from 
'../../shared/form-controls';
 import { RolesComponent, ConfirmDeleteUserAccountDialogComponent } from 
'./roles.component';
 import { GroupNameValidationDirective } from 
'./group-name-validarion.directive';
 import {InformMessageModule} from '../../shared/inform-message';
+import { DirectivesModule } from '../../core/directives';
 
 @NgModule({
     imports: [
-        CommonModule,
-        FormsModule,
-        ReactiveFormsModule,
-        MaterialModule,
-        FormControlsModule,
-        InformMessageModule
+      CommonModule,
+      FormsModule,
+      ReactiveFormsModule,
+      MaterialModule,
+      FormControlsModule,
+      InformMessageModule,
+      DirectivesModule
     ],
   declarations: [RolesComponent, ConfirmDeleteUserAccountDialogComponent, 
GroupNameValidationDirective],
   entryComponents: [ConfirmDeleteUserAccountDialogComponent],
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.html
 
b/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.html
index 451085bb0..c7dd44dbb 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.html
+++ 
b/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.html
@@ -42,10 +42,12 @@
         <div class="inner-step mat-reset">
           <input
             [validator]="groupValidation()"
+            isGroupNameUnique
             type="text"
             placeholder="Enter group name"
             [(ngModel)]="setupGroup"
             #setupGroupName="ngModel"
+            #groupName
           />
           <div class="error" *ngIf="setupGroupName.errors?.patterns && 
setupGroupName.dirty">
             Group name can only contain letters, numbers, hyphens and '_'.
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.ts
 
b/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.ts
index 9739eaf86..573e8266d 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/administration/roles/roles.component.ts
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-import { Component, OnInit, Output, EventEmitter, Inject } from 
'@angular/core';
-import { ValidatorFn, FormControl } from '@angular/forms';
+import { Component, OnInit, Output, EventEmitter, Inject, ViewChild, 
AfterViewInit, ElementRef } from '@angular/core';
+import { ValidatorFn, FormControl, NgModel } from '@angular/forms';
 import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from 
'@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
 
@@ -27,16 +27,21 @@ import { CheckUtils, SortUtils } from '../../core/util';
 import { DICTIONARY } from '../../../dictionary/global.dictionary';
 import { ProgressBarService } from '../../core/services/progress-bar.service';
 import {ConfirmationDialogComponent, ConfirmationDialogType} from 
'../../shared';
+import { debounceTime, map, switchMap, take, tap } from 'rxjs/operators';
+import { fromEvent } from 'rxjs';
+import { GroupModel, Role } from '../../core/models/role.model';
 
 @Component({
   selector: 'datalab-roles',
   templateUrl: './roles.component.html',
   styleUrls: ['../../resources/resources-grid/resources-grid.component.scss', 
'./roles.component.scss']
 })
-export class RolesComponent implements OnInit {
+export class RolesComponent implements OnInit, AfterViewInit {
   readonly DICTIONARY = DICTIONARY;
 
   @Output() manageRolesGroupAction: EventEmitter<{}> = new EventEmitter();
+  @ViewChild('groupName') qwe: ElementRef;
+  @ViewChild('setupGroupName') setupGroupName: NgModel;
 
   private startedGroups: Array<any>;
 
@@ -53,6 +58,7 @@ export class RolesComponent implements OnInit {
   public groupnamePattern = new RegExp(/^[a-zA-Z0-9_\-]+$/);
   public noPermissionMessage: string = 'You have not permissions for groups 
which are not assigned to your projects.';
   public maxGroupLength: number = 25;
+  public isGroupNameDuplicated: boolean = false;
 
   stepperView: boolean = false;
   displayedColumns: string[] = ['name', 'roles', 'users', 'actions'];
@@ -71,6 +77,14 @@ export class RolesComponent implements OnInit {
     this.getEnvironmentHealthStatus();
   }
 
+  ngAfterViewInit() {
+    if (this.qwe) {
+      fromEvent(this.qwe.nativeElement, 'keyup').pipe(
+        debounceTime(500)
+      ).subscribe(({target}) => console.log(target.value));
+    }
+  }
+
   openManageRolesDialog() {
     this.progressBarService.startProgressBar();
     this.rolesService.getGroupsData().subscribe(groups => {
@@ -240,11 +254,11 @@ export class RolesComponent implements OnInit {
   }
 
   public extractIds(sourceList, target) {
-    const map = new Map();
+    const mapObj = new Map();
     const mapped = sourceList.reduce((acc, item) => {
       target.includes(item.description) && acc.set(item._id, item.description);
       return acc;
-    }, map);
+    }, mapObj);
 
     return this.mapToObj(mapped);
   }
@@ -264,16 +278,10 @@ export class RolesComponent implements OnInit {
   }
 
   public groupValidation(): ValidatorFn {
-    const duplicateList: any = this.groupsData.map(item => 
item.group.toLowerCase());
     return <ValidatorFn>((control: FormControl) => {
       if (control.value && control.value.length > this.maxGroupLength) {
         return { long: true };
       }
-
-      if (control.value && 
duplicateList.includes(CheckUtils.delimitersFiltering(control.value.toLowerCase())))
 {
-        return { duplicate: true };
-      }
-
       if (control.value && !this.groupnamePattern.test(control.value))
         return { patterns: true };
 
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/directives/index.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/directives/index.ts
index 40b54483b..57f767a92 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/directives/index.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/directives/index.ts
@@ -24,11 +24,24 @@ import { ClickOutsideDirective } from 
'./click-outside.directive';
 import { ScrollDirective } from './scrollTo.directive';
 import { IsEndpointsActiveDirective } from './is-endpoint-active.directive';
 import { ClickedOutsideMatSelectDirective } from 
'./click-outside-with-material-select.directive';
+import { IsGroupNameUniqueDirective } from './is-group-name-unique.directive';
 
 @NgModule({
-  imports: [CommonModule],
-  declarations: [ClickOutsideDirective, ScrollDirective, 
IsEndpointsActiveDirective, ClickedOutsideMatSelectDirective],
-  exports: [ClickOutsideDirective, ScrollDirective, 
IsEndpointsActiveDirective, ClickedOutsideMatSelectDirective]
+  imports: [ CommonModule ],
+  declarations: [
+    ClickOutsideDirective,
+    ScrollDirective,
+    IsEndpointsActiveDirective,
+    ClickedOutsideMatSelectDirective,
+    IsGroupNameUniqueDirective
+  ],
+  exports: [
+    ClickOutsideDirective,
+    ScrollDirective,
+    IsEndpointsActiveDirective,
+    ClickedOutsideMatSelectDirective,
+    IsGroupNameUniqueDirective
+  ]
 })
 
 export class DirectivesModule { }
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/directives/is-group-name-unique.directive.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/directives/is-group-name-unique.directive.ts
new file mode 100644
index 000000000..84f15aca0
--- /dev/null
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/directives/is-group-name-unique.directive.ts
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Directive } from '@angular/core';
+import { AbstractControl, NG_ASYNC_VALIDATORS, ValidationErrors, Validator } 
from '@angular/forms';
+import { Observable, of } from 'rxjs';
+import { catchError, map, switchMap, take } from 'rxjs/operators';
+import { RolesGroupsService } from '../services';
+import { GroupModel } from '../models/role.model';
+import 'rxjs-compat/add/observable/timer';
+
+
+@Directive({
+  selector: '[isGroupNameUnique]',
+  providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: 
IsGroupNameUniqueDirective, multi: true }]
+})
+export class IsGroupNameUniqueDirective implements Validator {
+  constructor( private rolesService: RolesGroupsService ) {
+  }
+  validate(control: AbstractControl): Observable<ValidationErrors | null> {
+    return Observable.timer(300).pipe(
+      switchMap( () => {
+        return this.rolesService.getGroupsData().pipe(
+          map(res => this.isGroupExist(res, control.value)),
+          map(isGroupExist => (isGroupExist ? { duplicate: true } : null)),
+          catchError(() => of(null)),
+          take(1)
+        );
+      })
+    );
+  }
+
+  private isGroupExist(groupList: GroupModel[], comparedValue: string): 
boolean {
+    return groupList.some(({group}) => {
+      return  group.toLowerCase() === comparedValue;
+    });
+  }
+}
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/models/role.model.ts
similarity index 57%
copy from 
services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
copy to 
services/self-service/src/main/resources/webapp/src/app/core/models/role.model.ts
index dfbbbf7fd..b2249ae5e 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/administration/administration.module.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/models/role.model.ts
@@ -17,18 +17,27 @@
  * under the License.
  */
 
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
 
-import { ManagenementModule } from './management';
-import { ProjectModule } from './project';
-import { RolesModule } from './roles';
-import {ConfigurationModule} from './configuration';
-import {OdahuModule} from './odahu';
+export interface GroupModel {
+  group: string;
+  roles: Role[];
+  users: string[];
+}
 
-@NgModule({
-  imports: [CommonModule, ManagenementModule, ProjectModule, RolesModule, 
ConfigurationModule, OdahuModule],
-  declarations: [],
-  exports: [ManagenementModule, ProjectModule, RolesModule, 
ConfigurationModule, OdahuModule]
-})
-export class AdministrationModule { }
+export interface Role {
+  id: string;
+  description: string;
+  type: EntityType;
+  cloud: CloudType;
+  pages: string[];
+  computationals: string[];
+  exploratories: string[];
+  exploratory_shapes: string[];
+  groups: string[];
+  images: string[];
+}
+
+
+export type EntityType = 'NOTEBOOK' | 'COMPUTATIONAL' | 'IMAGE' | 
'NOTEBOOK_SHAPE' | 'COMPUTATIONAL_SHAPE' | 'BILLING' | 'BUCKET_BROWSER' | 
'ADMINISTRATION';
+
+export type CloudType = 'AWS' | 'AZURE' | 'GSP' | 'GENERAL';
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/services/rolesManagement.service.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/services/rolesManagement.service.ts
index 17766914c..b82e9cf27 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/services/rolesManagement.service.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/services/rolesManagement.service.ts
@@ -23,16 +23,16 @@ import { catchError, map } from 'rxjs/operators';
 
 import { ApplicationServiceFacade } from './applicationServiceFacade.service';
 import { ErrorUtils } from '../util';
+import { GroupModel } from '../models/role.model';
 
 @Injectable()
 export class RolesGroupsService {
   constructor(private applicationServiceFacade: ApplicationServiceFacade) { }
 
-  public getGroupsData(): Observable<{}> {
+  public getGroupsData(): Observable<GroupModel[]> {
     return this.applicationServiceFacade
       .buildGetGroupsData()
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -40,7 +40,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildGetRolesData()
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -48,7 +47,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildSetupNewGroup(data)
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -56,7 +54,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildUpdateGroupData(data)
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -64,7 +61,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildSetupRolesForGroup(data)
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -72,7 +68,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildSetupUsersForGroup(data)
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -82,7 +77,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildRemoveUsersForGroup(JSON.stringify(url))
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 
@@ -92,7 +86,6 @@ export class RolesGroupsService {
     return this.applicationServiceFacade
       .buildRemoveGroupById(JSON.stringify(url))
       .pipe(
-        map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to