You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by hs...@apache.org on 2022/09/09 13:02:47 UTC

[incubator-datalab] 01/01: DATALAB-2996 implemented sharing confirmation

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

hshpak pushed a commit to branch feat/DATALAB-2996/sharing-confirmation
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit d1ddd1b1e38aa03063c8ca825121edd4bdcce2b3
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Fri Sep 9 13:32:13 2022 +0300

    DATALAB-2996 implemented sharing confirmation
---
 .../services/applicationServiceFacade.service.ts   |  6 +++
 .../app/core/services/user-images-page.service.ts  | 16 +++++-
 .../src/app/core/services/userResource.service.ts  |  1 -
 .../image-action-dialog.component.html             |  7 ++-
 .../image-action-dialog.component.ts               |  3 +-
 .../share-dialog/share-dialog.component.html       | 17 +++++--
 .../share-dialog/share-dialog.component.ts         | 58 ++++++++++++++++++----
 .../terminate-dialog.component.html                |  2 +-
 .../src/app/resources/images/images.component.ts   | 22 +++++---
 .../src/app/resources/images/images.config.ts      |  2 +-
 .../src/app/resources/images/images.model.ts       |  5 +-
 .../src/app/resources/images/images.service.ts     | 12 +++--
 12 files changed, 118 insertions(+), 33 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
index c4715c6ae..69cd6e607 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
@@ -460,6 +460,12 @@ export class ApplicationServiceFacade {
       data, { responseType: 'text', observe: 'response' });
   }
 
+  public buildGetImageShareInfo(data: string): Observable<any> {
+    return this.buildRequest(HTTPMethod.GET,
+      this.requestRegistry.Item(ApplicationServiceFacade.IMAGE) + data,
+      null);
+  }
+
   public buildGetExploratorySchedule(data): Observable<any> {
     return this.buildRequest(HTTPMethod.GET,
       this.requestRegistry.Item(ApplicationServiceFacade.SCHEDULER),
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
index 49d6ace10..5811202da 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
@@ -19,7 +19,7 @@
 
 import {  Injectable } from '@angular/core';
 import {  Observable } from 'rxjs';
-import { catchError, map } from 'rxjs/operators';
+import { catchError } from 'rxjs/operators';
 import {  ErrorUtils } from '../util';
 
 import { ApplicationServiceFacade } from './applicationServiceFacade.service';
@@ -29,6 +29,7 @@ import {
   ProjectImagesInfo,
   ImageParams
 } from '../../resources/images';
+import { UserData } from '../../resources/exploratory/image-action-dialog/image-action.model';
 
 @Injectable()
 export class UserImagesPageService {
@@ -64,6 +65,17 @@ export class UserImagesPageService {
     return this.applicationServiceFacade
       .buildDeleteImage(JSON.stringify(url))
       .pipe(
-        catchError(ErrorUtils.handleServiceError));
+        catchError(ErrorUtils.handleServiceError)
+      );
+  }
+
+  getImageShareInfo(image: ImageParams): Observable<UserData[]> {
+    const { imageName, projectName, endpoint} = image;
+    const url = `/sharing_info/${imageName}/${projectName}/${endpoint}/`;
+    return this.applicationServiceFacade
+      .buildGetImageShareInfo(url)
+      .pipe(
+        catchError(ErrorUtils.handleServiceError)
+      );
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts
index 6f5fe08ff..406270b90 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/userResource.service.ts
@@ -184,7 +184,6 @@ export class UserResourceService {
   }
 
   public createAMI(data): Observable<any> {
-    const body = JSON.stringify(data);
     return this.applicationServiceFacade
       .buildCreateAMI(data)
       .pipe(
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html
index 297a95bb7..238361994 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html
@@ -19,7 +19,10 @@
 
 <div id="dialog-box">
   <header class="dialog-header">
-    <h4 class="modal-title">{{data.title}}<span *ngIf="data.actionType === actionType.share">: {{data.imageName}}</span></h4>
+    <h4 class="modal-title">{{data.title}}<span
+      *ngIf="data.actionType === actionType.share">: {{data.image.name}}
+      </span>
+    </h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
   <section class="content">
@@ -33,7 +36,7 @@
         [disabled]="isApplyBtnDisabled"
         type="button"
         class="butt butt-success mat-raised-button"
-        (click)="dialogRef.close(true)">
+        (click)="dialogRef.close(responseObj)">
         {{confirmBtnName}}
       </button>
     </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts
index 2b7e46a1a..79f7049b3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts
@@ -20,7 +20,7 @@
 import { Component, Inject, Input, OnInit } from '@angular/core';
 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
 import { ImageActions, ImageActionModalData } from '../../images';
-import { DialogWindowTabConfig } from './image-action.model';
+import { DialogWindowTabConfig, UserData } from './image-action.model';
 
 @Component({
   selector: 'datalab-image-action-dialog',
@@ -30,6 +30,7 @@ import { DialogWindowTabConfig } from './image-action.model';
 export class ImageActionDialogComponent implements OnInit {
   @Input() activeTabConfig: DialogWindowTabConfig;
   @Input() isApplyBtnDisabled: Boolean;
+  @Input() responseObj: UserData[] = [];
 
   readonly actionType: typeof ImageActions = ImageActions;
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html
index 8b7f10eb1..3b527305c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html
@@ -17,7 +17,14 @@
   ~ under the License.
   -->
 
-<datalab-image-action-dialog [activeTabConfig]="activeTabConfig" [isApplyBtnDisabled]="isApplyBtnDisabled">
+<datalab-image-action-dialog
+  [activeTabConfig]="activeTabConfig"
+  [responseObj]="responseObj"
+  [isApplyBtnDisabled]="isApplyBtnDisabled"
+>
+
+  <ng-container *ngIf="($getUserListData | async)"></ng-container>
+
   <div datalab-share-dialog class="wrapper">
     <ul class="title__list">
       <li
@@ -91,8 +98,8 @@
       </div>
       <div class="user-list__wrapper scrolling">
         <ul class="user-list">
-          <li *ngFor="let entity of temporaryUserList" class="user-list__item">
-            <datalab-share-user-data (removeUserData)="onRemoveUserData($event, tabsName.shareImage)" [userData]="entity"></datalab-share-user-data>
+          <li *ngFor="let entity of temporaryUserDataList" class="user-list__item">
+            <datalab-share-user-data (removeUserData)="onRemoveUserData($event)" [userData]="entity"></datalab-share-user-data>
           </li>
         </ul>
       </div>
@@ -101,8 +108,8 @@
     <div *ngIf="activeTabConfig.shareWith" class="share-with__wrapper">
       <div class="user-list__wrapper scrolling">
         <ul class="user-list">
-          <li *ngFor="let entity of userList" class="user-list__item">
-            <datalab-share-user-data (removeUserData)="onRemoveUserData($event, tabsName.shareWith)" [userData]="entity"></datalab-share-user-data>
+          <li *ngFor="let entity of userDataList" class="user-list__item">
+            <datalab-share-user-data (removeUserData)="onRemoveUserData($event)" [userData]="entity"></datalab-share-user-data>
           </li>
         </ul>
       </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts
index 50a194a8c..05fb44872 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts
@@ -17,10 +17,15 @@
  * under the License.
  */
 
-import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Inject, OnInit, ViewChild } from '@angular/core';
 import { SharePlaceholder, TabName, UserDataTypeConfig } from '../image-action.config';
 import { DialogWindowTabConfig, UserData, UserDataType } from '../image-action.model';
 import { NgModel } from '@angular/forms';
+import { ImagesService } from '../../../images/images.service';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { ImageActionModalData } from '../../../images';
+import { tap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
 
 @Component({
   selector: 'datalab-share-dialog',
@@ -28,15 +33,15 @@ import { NgModel } from '@angular/forms';
   styleUrls: ['./share-dialog.component.scss'],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class ShareDialogComponent {
+export class ShareDialogComponent implements OnInit {
   @ViewChild('searchUser') searchUser: NgModel;
 
   readonly placeholder: typeof SharePlaceholder = SharePlaceholder;
   readonly tabsName: typeof TabName = TabName;
   readonly userDataTypeConfig: typeof UserDataTypeConfig = UserDataTypeConfig;
 
-  userList: UserData[] = [];
-  temporaryUserList: UserData[] = [];
+  userDataList: UserData[] = [];
+  temporaryUserDataList: UserData[] = [];
   userNameOrGroup: UserDataType;
   activeTabConfig: DialogWindowTabConfig = {
     shareImage: true,
@@ -44,6 +49,18 @@ export class ShareDialogComponent {
   };
   searchInput = '';
 
+  $getUserListData: Observable<UserData[]>;
+
+  constructor(
+    private imagesService: ImagesService,
+    @Inject(MAT_DIALOG_DATA) public data: ImageActionModalData,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.getSharingUserList();
+  }
+
   onAddUser(): void {
     if (!this.searchInput) {
       return;
@@ -52,21 +69,44 @@ export class ShareDialogComponent {
       value: this.searchInput.trim(),
       type: this.userNameOrGroup
     };
-    this.temporaryUserList = [...this.temporaryUserList, newUserEntity];
+    this.temporaryUserDataList = [...this.temporaryUserDataList, newUserEntity]
+      .sort((a, b) => a.value < b.value ? 1 : -1)
+      .sort((a, b) => a.type > b.type ? 1 : -1);
     this.searchInput = '';
   }
 
+  aaa() {
+    console.log(this.searchUser);
+
+  }
+
   onTabTitle(tabName: keyof DialogWindowTabConfig): void {
     Object.keys(this.activeTabConfig).forEach(item => this.activeTabConfig[item] = false);
     this.activeTabConfig = {...this.activeTabConfig, [tabName]: true};
   }
 
-  onRemoveUserData(userName: string, tabName: TabName): void {
-    this.temporaryUserList = this.temporaryUserList.filter(({value}) => value !== userName);
+  onRemoveUserData(userName: string): void {
+    this.temporaryUserDataList = this.temporaryUserDataList.filter(({value}) => value !== userName);
+  }
+
+  private getSharingUserList(): void {
+    const { name, project, endpoint} = this.data.image;
+    const imageParams = {
+      imageName: name,
+      projectName: project,
+      endpoint
+    };
+    this.$getUserListData = this.imagesService.getImageShareInfo(imageParams).pipe(
+      tap(userListData => this.userDataList = userListData)
+    );
+  }
+
+  get responseObj(): UserData[] {
+    return [...this.temporaryUserDataList, ...this.userDataList];
   }
 
   get isApplyBtnDisabled(): boolean {
-    return this.searchInput.length > 25 || !Boolean(this.temporaryUserList.length);
+    return this.searchInput.length > 25 || !Boolean(this.temporaryUserDataList.length);
   }
 
   get isAddUserDataBtnDisabled(): boolean {
@@ -80,7 +120,7 @@ export class ShareDialogComponent {
   get isUserInputEmpty(): boolean {
     return this.searchUser?.control.touched
       && !Boolean(this.searchInput.length)
-      && !Boolean(this.temporaryUserList.length);
+      && !Boolean(this.temporaryUserDataList.length);
   }
 
   get isLongInputMessage() {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html
index 532f4e2d0..e7328d1f5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html
@@ -25,7 +25,7 @@
         <div class="status">Further status</div>
       </div>
       <div class="scrolling-content scrolling terminate-image-list__wrapper">
-        <div class="image-name ellipsis">{{data.imageName}}</div>
+        <div class="image-name ellipsis">{{data.image.name}}</div>
         <div class="status terminated">Terminated</div>
       </div>
       <p *ngIf="data.isShared" class="shared-warning">!The image is shared with other users.</p>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
index dca4feafb..b4ec1677a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
@@ -19,8 +19,8 @@
 
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { MatDialog } from '@angular/material/dialog';
-import { EMPTY, Observable } from 'rxjs';
-import { map, switchMap, tap } from 'rxjs/operators';
+import { EMPTY, Observable, of } from 'rxjs';
+import { catchError, map, switchMap, tap } from 'rxjs/operators';
 
 import { ToastrService } from 'ngx-toastr';
 
@@ -31,7 +31,7 @@ import {
   ImageActionType,
   ImageFilterFormDropdownData,
   ImageFilterFormValue,
-  ImageModel,
+  ImageModel, ImageParams,
   ProjectModel
 } from './images.model';
 import {
@@ -49,12 +49,10 @@ import {
   ImageActions,
   Toaster_Message,
 } from './images.config';
-import { ImageActionDialogComponent } from '../exploratory/image-action-dialog/image-action-dialog.component';
 import { ImagesService } from './images.service';
 import { ProgressBarService } from '../../core/services/progress-bar.service';
 import { ImageDetailDialogComponent } from '../exploratory/image-detail-dialog/image-detail-dialog.component';
 import { ActivatedRoute } from '@angular/router';
-import { TerminateDialogComponent } from '../exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component';
 
 @Component({
   selector: 'datalab-images',
@@ -162,7 +160,7 @@ export class ImagesComponent implements OnInit, OnDestroy {
   }
 
   onActionClick(image: ImageModel, actionType: ImageActionType): void {
-    const imageInfo = this.imagesService.createImageRequestInfo(image);
+    let imageInfo: ImageParams;
     const data = this.imagesService.createActionDialogConfig(image, actionType);
     const requestCallback = this.imagesService.getRequestByAction(actionType).bind(this.imagesService);
     const component = this.imagesService.getComponentByAction(actionType);
@@ -172,13 +170,17 @@ export class ImagesComponent implements OnInit, OnDestroy {
       panelClass: 'modal-sm'
     }).afterClosed()
       .pipe(
+        tap(userDataList => {
+          imageInfo = this.imagesService.createImageRequestInfo(image, userDataList);
+        }),
         switchMap((confirm) => {
           if (confirm) {
             return requestCallback(imageInfo, actionType);
           }
           return EMPTY;
         }),
-        tap(() => this.callActionHelpers(actionType, this.callToasterShareSuccess))
+        tap(() => this.callActionHelpers(actionType, this.callToasterShareSuccess)),
+        catchError(() => of(this.callToasterShareError(actionType)))
     )
       .subscribe();
   }
@@ -239,6 +241,12 @@ export class ImagesComponent implements OnInit, OnDestroy {
     }
   }
 
+  private callToasterShareError(actionType: ImageActionType): void {
+    if (actionType === ImageActions.share) {
+      this.toastr.error('Something went wrong. Please try again.', 'Oops!');
+    }
+  }
+
   private checkAuthorize(): void {
     this.applicationSecurityService.isLoggedIn().subscribe(() => {
       this.getEnvironmentHealthStatus();
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
index ef7cb6289..2bf2e0f39 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
@@ -50,7 +50,7 @@ export enum Localstorage_Key {
 }
 
 export enum Toaster_Message {
-  successShare = 'The image has been shared with all current Regular Users on the project!',
+  successShare = 'The image has been successfully shared.',
   successTerminate = 'The image has been terminated'
 }
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
index 09b32cce8..63f95908c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
@@ -17,6 +17,8 @@
  * under the License.
  */
 
+import { UserData } from '../exploratory/image-action-dialog/image-action.model';
+
 export interface ProjectImagesInfo {
   filterData: ImageFilterFormDropdownData;
   imageFilter: ImageFilterFormValue;
@@ -58,12 +60,13 @@ export interface ImageParams {
   imageName: string;
   projectName: string;
   endpoint: string;
+  sharedWith?: UserData[];
 }
 
 export interface ImageActionModalData {
   actionType: ImageActionType;
   title: string;
-  imageName: string;
+  image: ImageModel;
   isShared?: boolean;
 }
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
index a3dfee632..469086713 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
@@ -19,6 +19,7 @@ import { ChangedColumnStartValue, FilterFormInitialValue, ModalTitle, SharedStat
 import { ShareDialogComponent } from '../exploratory/image-action-dialog/share-dialog/share-dialog.component';
 import { TerminateDialogComponent } from '../exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component';
 import { ComponentType } from 'ngx-toastr';
+import { UserData } from '../exploratory/image-action-dialog/image-action.model';
 
 @Injectable({
   providedIn: 'root'
@@ -81,6 +82,10 @@ export class ImagesService {
       );
   }
 
+  getImageShareInfo(imageInfo: ImageParams): Observable<UserData[]> {
+    return this.userImagesPageService.getImageShareInfo(imageInfo);
+  }
+
   getActiveProject(projectName: string): void {
     const projectList = this.$$cashedProjectList.getValue();
     if (!projectName) {
@@ -176,12 +181,13 @@ export class ImagesService {
     this.$$isImageListFiltered.next(isImageListFiltered);
   }
 
-  createImageRequestInfo(image: ImageModel): ImageParams {
+  createImageRequestInfo(image: ImageModel, userDataList: UserData[]): ImageParams {
     const { name, project, endpoint } = image;
     return {
       imageName: name,
       projectName: project,
-      endpoint: endpoint
+      endpoint: endpoint,
+      sharedWith: userDataList
     };
   }
 
@@ -193,7 +199,7 @@ export class ImagesService {
     return {
       title: modalTitle[actionType],
       actionType,
-      imageName: image.name,
+      image,
       isShared: this.isImageShared(image)
     };
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org