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:46 UTC

[incubator-datalab] branch feat/DATALAB-2996/sharing-confirmation created (now d1ddd1b1e)

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

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


      at d1ddd1b1e DATALAB-2996 implemented sharing confirmation

This branch includes the following new commits:

     new d1ddd1b1e DATALAB-2996 implemented sharing confirmation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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


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

Posted by hs...@apache.org.
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