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()">×</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