This is an automated email from the ASF dual-hosted git repository. dgnatyshyn pushed a commit to branch DLAB-1960 in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit d32fc3cd60238748fd539daf2994d177b9f30203 Author: Dmytro_Gnatyshyn <[email protected]> AuthorDate: Fri Jul 24 15:39:48 2020 +0300 [DLAB-1960]: Fixed library issues --- .../install-libraries.component.html | 59 +++++---- .../install-libraries.component.scss | 10 +- .../install-libraries.component.ts | 137 ++++++++++++--------- .../install-libraries/install-libraries.model.ts | 2 +- .../resources/webapp/src/assets/styles/_theme.scss | 8 ++ .../src/main/resources/webapp/src/styles.scss | 3 +- 6 files changed, 130 insertions(+), 89 deletions(-) diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html index c05b6f9..54f1d86 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html @@ -28,13 +28,14 @@ <p class=" message">Cannot install libraries: Exploratory <strong>{{ notebook?.name }}</strong> is not running</p> </div> - <div class="loading-block" *ngIf="!libs_uploaded && uploading && data.status === 'running'"> + <div *ngIf="notebook?.status === 'running'" class="top-wrapper"> + <div class="loading-block" *ngIf="!libs_uploaded && uploading && data.status === 'running'"> <div class="uploading"> <p>Please wait until DLab loads full list of available libraries for you...</p> <img src="assets/img/gif-spinner.gif" alt="loading"> </div> </div> - <div *ngIf="notebook?.status === 'running' && !uploading" class="lib-view-wrap"> + <div *ngIf="notebook?.status === 'running' && !uploading" class="lib-view-wrap"> <div class="search-box"> <div class="search-form"> <div> @@ -48,7 +49,7 @@ <label class="label">Select group</label> <div class="control"> <dropdown-list #groupSelect (selectedItem)="onUpdate($event)" (emitClick)="emitClick()"></dropdown-list> - <span class="error-message" *ngIf="!group && libSearch.value">Group field is required. Please choose appropriate group.</span> + <span class="error-message" *ngIf="!group && libSearch?.value">Group field is required. Please choose appropriate group.</span> </div> </div> </div> @@ -59,7 +60,6 @@ <span class="other-message" *ngIf="group === 'others'">Other group can include libs from Python 2 and Python 3 groups</span> <input type="text" [placeholder]="'Enter library name'" - [(ngModel)]="lib.name" [disabled]="!group" [matAutocomplete]="auto" [formControl]="libSearch" @@ -68,15 +68,13 @@ > </div> <mat-autocomplete #auto="matAutocomplete" class="suggestions" > - <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index" *ngIf="!selectedLib"> - <mat-option > + <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index"> + <mat-option> <div class="option" (click)="selectLibrary(item);$event.stopPropagation()" [ngClass]="{'not-allow': item.isInSelectedList}"> <a *ngIf="!item.isInSelectedList"> - <span [innerHTML]="item.name | highlight:query"></span> + <span [innerHTML]="item.name | highlight:lib.name.toLowerCase()"></span> </a> - <span *ngIf="item.isInSelectedList">{{ item.name }} -<!-- <span *ngIf="item.version && item.version !== 'N/A' && item.isInstalled && !item.isInSelectedList">{{ item.version }}</span>--> - </span> + <span *ngIf="item.isInSelectedList">{{ item.name }}</span> <strong *ngIf="item.isInSelectedList">selected <i class="material-icons">done</i> </strong> @@ -113,10 +111,10 @@ <div class="control-group constol-select java-library-search"> <label class="label">Library</label> <div class="control control-relative"> - <span class="other-message" *ngIf="lib.name.length && !this.selectedLib">groupId:artifactId:versionId</span> + <span class="other-message" *ngIf="lib.name?.length && !this.selectedLib">groupId:artifactId:versionId</span> <input type="text" [placeholder]="'Enter library name in <groupId>:<artifactId>:<versionId> format'" - [(ngModel)]="lib.name" + [disabled]="!group" [matAutocomplete]="auto" [formControl]="libSearch" @@ -132,11 +130,11 @@ </span> </div> <mat-autocomplete #auto="matAutocomplete" class="suggestions"> - <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index" *ngIf="!selectedLib"> + <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index"> <mat-option > <div class="option" (click)="selectLibrary(item);$event.stopPropagation()" [ngClass]="{'not-allow': item.isInSelectedList}"> <a *ngIf="!item.isInSelectedList"> - <span [innerHTML]="item.name + ':' + item.version | highlight:query"> + <span [innerHTML]="item.name + ':' + item.version | highlight:item.name.toLowerCase()"> <span>{{ item.version }}</span> </span> </a> @@ -231,6 +229,10 @@ </div> </div> </div> + </div> + + + <div class="libs-info"> <mat-list> <mat-list-item class="list-header"> @@ -344,8 +346,8 @@ <div class="wrap-col"> <div class="lib-destination-col">{{ item.resource }}</div> <div class="lib-resource-type-col">{{ item.resourceType }}</div> - <div class="lib-status-col" ngClass="{{ item.status.toLowerCase() || 'failed' }}">{{ item.status.replace('_', ' ') }} - <div class="warn-action" *ngIf="(item.status === 'failed' || item.status.toLowerCase() === 'invalid_version') && notebook?.status === 'running'"> + <div class="lib-status-col" ngClass="{{ item.status.toLowerCase() || 'installation_error' }}">{{ item.status.replace('_', ' ') }} + <div class="warn-action" *ngIf="(item.status === 'installation_error' || item.status.toLowerCase() === 'invalid_version') && notebook?.status === 'running'"> <div *ngIf="!item.available_versions"> <span *ngIf="!installingInProgress" (click)="reinstallLibrary(item, lib)" matTooltip="Retry installation" matTooltipPosition="above"> <i class="material-icons">replay</i> @@ -355,22 +357,23 @@ <i class="material-icons">replay</i> </span> </div> - <div *ngIf="item.status === 'failed' && item.error" class="lib-error" (click)="showErrorMessage(item)"> - <i class="material-icons">error_outline</i> + <div *ngIf="item.status === 'installation_error' && item.error" class="lib-error" (click)="showErrorMessage(item)" matTooltip="Show error message" matTooltipPosition="above"> + <i class="material-icons terminated">error_outline</i> </div> <div class="lib-error" - *ngIf="item.status.toLowerCase() === 'invalid_version' && item.available_versions?.length" class="lib-error" + *ngIf="item.status.toLowerCase() === 'invalid_version' && item.available_versions?.length" (click)="openLibInfo(item, 'available');$event.stopPropagation()" matTooltip="Show available version" matTooltipPosition="above" > - <i class="material-icons terminated">error_outline</i> + <i class="material-icons">error_outline</i> </div> - </div> - <div class="warn-action" matTooltip="Show installed dependency" matTooltipPosition="above" *ngIf="item.add_pkgs?.length"> + <div matTooltip="Show installed dependency" matTooltipPosition="above" *ngIf="item.add_pkgs?.length"> <span class="info-icon" (click)="openLibInfo(item, 'added');$event.stopPropagation() "> <i class="material-icons">info</i> </span> + </div> </div> + </div> </div> @@ -411,8 +414,16 @@ </mat-list> </div> <div class="m-top-15 actions" *ngIf="!uploading && notebook?.status === 'running'"> - <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Close</button> - <button mat-raised-button type="submit" class="butt butt-success action" (click)="model.confirmAction()" [disabled]="!model.selectedLibs.length || installingInProgress || !destination">Install</button> + <button mat-raised-button type="button" class="butt action close-btn" (click)="dialogRef.close()">Close</button> + <span matTooltip="Please wait until lib installation completes" [matTooltipDisabled]="!installingInProgress" matTooltipPosition="above"> + <button mat-raised-button type="submit" + class="butt butt-success action install-btn" + (click)="model.confirmAction()" + [disabled]="!model.selectedLibs.length || installingInProgress || !destination" + > + Install + </button> + </span> </div> </div> </div> diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss index 86cd434..48ed412 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss @@ -65,6 +65,10 @@ height: 30%; } +.top-wrapper{ + height: 285px; +} + .install-libraries { height: 100%; padding-bottom: 130px; @@ -78,7 +82,6 @@ } .info { - height: 40%; display: flex; flex-direction: row; align-items: center; @@ -99,7 +102,6 @@ .lib-view-wrap { display: flex; flex-direction: column; - height: 40%; } .error { @@ -342,7 +344,6 @@ mat-chip.mat-chip:not(.mat-basic-chip) { display: flex; flex: 1 1 auto; min-height: 0px; - height: 50%; .mat-list { width: 100%; @@ -555,6 +556,9 @@ mat-chip.mat-chip:not(.mat-basic-chip) { .btn{ line-height: 26px; + &.install-btn{ + margin-left: 0; + } } .control-relative{ diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts index 169156d..ae38165 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts @@ -22,12 +22,13 @@ import {Component, OnInit, ViewChild, ViewEncapsulation, ChangeDetectorRef, Inje import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { FormControl } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; -import { debounceTime } from 'rxjs/operators'; +import {debounceTime, takeUntil} from 'rxjs/operators'; import { InstallLibrariesModel } from './install-libraries.model'; import { LibrariesInstallationService } from '../../../core/services'; import { SortUtils, HTTP_STATUS_CODES } from '../../../core/util'; import {FilterLibsModel} from './filter-libs.model'; +import {Subject} from 'rxjs'; interface Library { name: string; @@ -42,16 +43,13 @@ interface Library { encapsulation: ViewEncapsulation.None }) export class InstallLibrariesComponent implements OnInit, OnDestroy { - + private unsubscribe$ = new Subject(); public model: InstallLibrariesModel; public notebook: any; public filteredList: any = []; public groupsList: Array<string>; public notebookLibs: Array<any> = []; - public notebookFailedLibs: Array<any> = []; public loadLibsTimer: any; - - public query: string = ''; public group: string; public destination: any; public uploading: boolean = false; @@ -81,10 +79,8 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { @ViewChild('groupSelect', { static: false }) group_select; @ViewChild('resourceSelect', { static: false }) resource_select; @ViewChild('trigger', { static: false }) matAutoComplete; - public isLibInfoOpened = { }; private isLibExist: boolean; public lib: Library = {name: '', version: ''}; - public javaLib: {group, artifact, version} = {group: '', artifact: '', version: ''}; private selectedLib: any = null; constructor( @@ -103,9 +99,11 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { this.libSearch.disable(); this.libSearch.valueChanges .pipe( - debounceTime(1000)) - .subscribe(newValue => { - this.query = newValue; + debounceTime(1000), + takeUntil(this.unsubscribe$) + ) + .subscribe(value => { + this.lib.name = value; this.isDuplicated(this.lib); this.filterList(); }); @@ -114,12 +112,17 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { ngOnDestroy() { window.clearTimeout(this.loadLibsTimer); window.clearTimeout(this.clear); + this.unsubscribe$.next(); + this.unsubscribe$.complete(); } uploadLibGroups(): void { this.libs_uploaded = false; this.uploading = true; this.librariesInstallationService.getGroupsList(this.notebook.project, this.notebook.name, this.model.computational_name) + .pipe( + takeUntil(this.unsubscribe$) + ) .subscribe( response => { this.libsUploadingStatus(response); @@ -150,7 +153,7 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { public filterList(): void { this.validity_format = ''; - (this.query && this.query.length >= 2 && this.group && !this.selectedLib) ? this.getFilteredList() : this.filteredList = null; + (this.lib.name && this.lib.name.length >= 2 && this.group ) ? this.getFilteredList() : this.filteredList = null; } public filterGroups(groupsList) { @@ -166,6 +169,7 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { if ($event.model.type === 'group_lib') { this.libSearch.enable(); this.group = $event.model.value; + this.lib = {name: '', version: ''}; } else if ($event.model.type === 'destination') { this.resetDialog(); this.destination = $event.model.value; @@ -186,25 +190,27 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { public isDuplicated(item) { if (this.filteredList) { if (this.group !== 'java') { - this.selectedLib = this.filteredList.find(lib => lib.name === item.name); + this.selectedLib = this.filteredList.find(lib => lib.name.toLowerCase() === item.name.toLowerCase()); } else { this.selectedLib = this.filteredList.find(lib => { - return lib.name === item.name.substring(0, item.name.lastIndexOf(':')); + return lib.name.toLowerCase() === item.name.substring(0, item.name.lastIndexOf(':')).toLowerCase(); }); } } else { this.selectedLib = null; } - - if (this.selectedLib) { - this.filteredList = null; - } } public addLibrary(item): void { if (this.selectedLib && !this.selectedLib.isInSelectedList) { - this.model.selectedLibs.push({ group: this.group, name: item.name, version: item.version || 'N/A' }); - this.query = ''; + if (this.group !== 'java') { + this.model.selectedLibs.push({ group: this.group, name: item.name.toLowerCase(), version: item.version.toLowerCase() || 'N/A' }); + } else { + this.model.selectedLibs.push({ + group: this.group, name: item.name.substring(0, item.name.lastIndexOf(':')).toLowerCase(), + version: item.name.substring(item.name.lastIndexOf(':') + 1).toLowerCase() || 'N/A' + }); + } this.libSearch.setValue(''); this.lib = {name: '', version: ''}; this.filteredList = null; @@ -240,7 +246,7 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { this.resetDialog(); } }, - error => this.toastr.error(error.message || 'Library installation failed!', 'Oops!'), + error => this.toastr.error(error.message || 'Library installation error!', 'Oops!'), () => { this.getInstalledLibrariesList(true); this.changeDetector.detectChanges(); @@ -274,6 +280,9 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { private getInstalledLibrariesList(init?: boolean) { this.model.getInstalledLibrariesList(this.notebook) + .pipe( + takeUntil(this.unsubscribe$) + ) .subscribe((data: any) => { if ( !this.filtredNotebookLibs.length || data.length !== this.notebookLibs.length) { this.filtredNotebookLibs = [...data]; @@ -305,6 +314,9 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { private getInstalledLibsByResource() { this.librariesInstallationService.getInstalledLibsByResource(this.notebook.project, this.notebook.name, this.model.computational_name) + .pipe( + takeUntil(this.unsubscribe$) + ) .subscribe((data: any) => this.destination.libs = data); } @@ -322,52 +334,57 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { private getFilteredList(): void { this.validity_format = ''; - - if (this.group === 'java') { - this.model.getDependencies(this.query) - .subscribe( - libs => { - this.filteredList = [libs]; - this.filteredList.forEach(lib => { - lib.isInSelectedList = this.model.selectedLibs.some(el => lib.name === el.name.substring(0, el.name.lastIndexOf(':'))); - lib.isInstalled = this.notebookLibs.some(libr => { - // && lib.version === item.name.substring(item.name.lastIndexOf(':') + 1 - return lib.name === libr.name.substring(0, libr.name.lastIndexOf(':')) && - this.group === libr.group && - libr.status.some(res => res.resource === this.destination.name); + if (this.lib.name.length > 1) { + if (this.group === 'java') { + this.model.getDependencies(this.lib.name) + .pipe( + takeUntil(this.unsubscribe$) + ) + .subscribe( + libs => { + this.filteredList = [libs]; + this.filteredList.forEach(lib => { + lib.isInSelectedList = this.model.selectedLibs.some(el => lib.name.toLowerCase() === el.name.substring(0, el.name.lastIndexOf(':')).toLowerCase()); + lib.isInstalled = this.notebookLibs.some(libr => { + return lib.name.toLowerCase() === libr.name.substring(0, libr.name.lastIndexOf(':')).toLowerCase() && + this.group === libr.group && + libr.status.some(res => res.resource === this.destination.name); + } + ); + }); + this.isDuplicated(this.lib); + }, + error => { + if (error.status === HTTP_STATUS_CODES.NOT_FOUND + || error.status === HTTP_STATUS_CODES.BAD_REQUEST + || error.status === HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR) { + this.validity_format = error.message; + this.filteredList = null; } - ); }); - this.isDuplicated(this.lib); - }, - error => { - if (error.status === HTTP_STATUS_CODES.NOT_FOUND - || error.status === HTTP_STATUS_CODES.BAD_REQUEST - || error.status === HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR) { - this.validity_format = error.message; - this.filteredList = null; - } - }); - } else { - this.getMatchedLibs(); + } else { + this.getMatchedLibs(); + } } } private getMatchedLibs() { - if (this.query.length > 1) { - this.model.getLibrariesList(this.group, this.query) - .subscribe((libs: Library[]) => { - this.isLibExist = libs.some(v => v.name === this.query); - this.filteredList = libs; - this.filteredList.forEach(lib => { - lib.isInSelectedList = this.model.selectedLibs.some(el => el.name === lib.name); - lib.isInstalled = this.notebookLibs.some(libr => lib.name === libr.name && - this.group === libr.group && - libr.status.some(res => res.resource === this.destination.name)); - }); - this.isDuplicated(this.lib); + this.model.getLibrariesList(this.group, this.lib.name.toLowerCase()) + .pipe( + takeUntil(this.unsubscribe$) + ) + .subscribe((libs: Library[]) => { + this.isLibExist = libs.some(v => v.name.toLowerCase() === this.lib.name.toLowerCase()); + this.filteredList = libs; + this.filteredList.forEach(lib => { + lib.isInSelectedList = this.model.selectedLibs.some(el => el.name.toLowerCase() === lib.name.toLowerCase()); + lib.isInstalled = this.notebookLibs.some(libr => lib.name === libr.name && + this.group === libr.group && + libr.status.some(res => res.resource === this.destination.name)); }); - } + this.isDuplicated(this.lib); + }); + } private selectorsReset(): void { @@ -378,7 +395,7 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy { private resetDialog(): void { this.group = ''; - this.query = ''; + this.lib.name = ''; this.libSearch.setValue(''); this.isInstalled = false; this.isInSelectedList = false; diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts index 65896d3..b201904 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts @@ -60,7 +60,7 @@ export class InstallLibrariesModel { project_name: this.notebook.project, exploratory_name: this.notebook.name, group: group, - start_with: query.slice(0, query.indexOf(':')) + start_with: query }; if (this.computational_name) lib_query.computational_name = this.computational_name; diff --git a/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss b/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss index 2500caf..92a7349 100644 --- a/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss +++ b/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss @@ -49,6 +49,14 @@ &.action { width: 140px; + + &.install-btn{ + margin-left: 0; + } + + &.close-btn{ + margin-right: 25px; + } } &.butt-success { diff --git a/services/self-service/src/main/resources/webapp/src/styles.scss b/services/self-service/src/main/resources/webapp/src/styles.scss index 459f495..006bd60 100644 --- a/services/self-service/src/main/resources/webapp/src/styles.scss +++ b/services/self-service/src/main/resources/webapp/src/styles.scss @@ -141,7 +141,8 @@ mat-chip.mat-chip strong { .failed, .deleting, .deleted, -.invalid_version{ +.invalid_version, +.installation_error{ color: #f1696e; } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
