You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by dg...@apache.org on 2020/04/27 14:10:33 UTC

[incubator-dlab] branch bucket-browser-gcp updated: [Dlab 1551]: Fixed bug file download (#708)

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

dgnatyshyn pushed a commit to branch bucket-browser-gcp
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git


The following commit(s) were added to refs/heads/bucket-browser-gcp by this push:
     new 3b3a0fe  [Dlab 1551]: Fixed bug file download (#708)
3b3a0fe is described below

commit 3b3a0fea00f6f0e4d4f92cdd63913d1e02afbf1f
Author: Dmytro Gnatyshyn <42...@users.noreply.github.com>
AuthorDate: Mon Apr 27 17:10:24 2020 +0300

    [Dlab 1551]: Fixed bug file download (#708)
    
     [DLAB-1551]: Bucket browser on UI
---
 .../administration/project/project.component.ts    |   1 -
 .../core/interceptors/http.token.interceptor.ts    |  10 +-
 .../src/app/core/services/appRouting.service.ts    |   1 +
 .../services/applicationServiceFacade.service.ts   |  21 ++--
 .../app/core/services/bucket-browser.service.ts    |  95 +----------------
 .../webapp/src/app/core/util/fileUtils.ts          |  10 ++
 .../bucket-browser/bucket-browser.component.html   |  29 +++---
 .../bucket-browser/bucket-browser.component.scss   |  16 +++
 .../bucket-browser/bucket-browser.component.ts     | 107 +++++++------------
 .../bucket-browser/bucket-data.service.ts          | 108 ++++++++++++++++++++
 .../folder-tree/folder-tree.component.ts           | 113 ++++++++++++---------
 .../detail-dialog/detail-dialog.component.html     |   4 +-
 .../detail-dialog/detail-dialog.component.ts       |   4 +-
 .../webapp/src/app/resources/resources.module.ts   |   3 +
 14 files changed, 292 insertions(+), 230 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/project/project.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/project/project.component.ts
index 9833a40..ef71ca7 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/project/project.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/project/project.component.ts
@@ -96,7 +96,6 @@ export class ProjectComponent implements OnInit, OnDestroy {
     if (this.projectList.length)
       this.dialog.open(EditProjectComponent, { data: { action: 'create', item: null }, panelClass: 'modal-xl-s' })
         .afterClosed().subscribe(() => {
-          console.log('Create project');
           this.getEnvironmentHealthStatus();
         });
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/interceptors/http.token.interceptor.ts b/services/self-service/src/main/resources/webapp/src/app/core/interceptors/http.token.interceptor.ts
index 29aa010..9d72d0b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/interceptors/http.token.interceptor.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/interceptors/http.token.interceptor.ts
@@ -33,7 +33,15 @@ import { Observable } from 'rxjs';
     if (token)
       headersConfig['Authorization'] = `Bearer ${token}`;
 
-    if (!request.headers.has('Content-Type') && !request.headers.has('Upload') && request.url.indexOf('upload') === -1)
+    // if (request.url.indexOf('api/bucket') !== -1) {
+    //   headersConfig['Authorization'] = `Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfSEVvZmliX2djZXJpcllidE51dVBoSk81OEJNOFc5M1dHZW9VR3hTR2l3In0.eyJqdGkiOiIxY2E4OTQ1OS02MDU5LTQzOTctYTZhMy1kMzY5YTY0OTkyNzIiLCJleHAiOjE1ODc5OTUyNjgsIm5iZiI6MCwiaWF0IjoxNTg3OTk0OTY4LCJpc3MiOiJodHRwczovL2lkcC5kZW1vLmRsYWJhbmFseXRpY3MuY29tL2F1dGgvcmVhbG1zL2RsYWIiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiNWIwYWEwMmYtYTU3ZS00MGM0LTk4ODQtNDlmYmU5OGViMzU4IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoib2Z1a3MtMTMwNC11aSIsIm [...]
+    // }
+
+    if (!request.headers.has('Content-Type')
+      && !request.headers.has('Upload')
+      && request.url.indexOf('upload') === -1
+      && request.url.indexOf('download') === -1)
+
       headersConfig['Content-Type'] = 'application/json; charset=UTF-8';
 
     const header = request.clone({ setHeaders: headersConfig });
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
index 1a80759..0f05bcb 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/appRouting.service.ts
@@ -34,6 +34,7 @@ export class AppRoutingService {
   }
 
   redirectToHomePage(): void {
+    console.log('redirect');
     this.router.navigate(['/resources_list']);
   }
 
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 ebaec6d..792b748 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
@@ -22,7 +22,6 @@ import { Observable } from 'rxjs';
 import { HttpClient } from '@angular/common/http';
 
 import { Dictionary } from '../collections';
-
 import { environment } from '../../../environments/environment';
 import { HTTPMethod } from '../util';
 
@@ -254,10 +253,11 @@ export class ApplicationServiceFacade {
       null);
   }
 
-  public buildGetBucketData(): Observable<any> {
+
+  public buildGetBucketData(data): Observable<any> {
     return this.buildRequest(HTTPMethod.GET,
-      this.requestRegistry.Item(ApplicationServiceFacade.BUCKET) + '/ofuks-1304-prj1-local-bucket/endpoint/local',
-     null);
+      this.requestRegistry.Item(ApplicationServiceFacade.BUCKET),
+      data);
   }
 
   public buildUploadFileToBucket(data): Observable<any> {
@@ -266,10 +266,12 @@ export class ApplicationServiceFacade {
       data);
   }
 
-  public buildDownloadFileFromBucket(data): Observable<any> {
+  public buildDownloadFileFromBucket(data) {
     return this.buildRequest(HTTPMethod.GET,
       this.requestRegistry.Item(ApplicationServiceFacade.BUCKET),
-      data, { observe: 'response', responseType: 'text' } );
+      data, { dataType : 'binary',
+        processData : false,
+        responseType : 'arraybuffer' } );
   }
 
   public buildDeleteFileFromBucket(data): Observable<any> {
@@ -722,6 +724,9 @@ export class ApplicationServiceFacade {
   private buildRequest(method: HTTPMethod, url_path: string, body: any, opt?) {
     // added to simplify development process
     const url = environment.production ? url_path : API_URL + url_path;
+    // if (url_path.indexOf('/api/bucket') !== -1) {
+    //   url = 'https://35.233.183.55' + url_path;
+    // }
 
     if (method === HTTPMethod.POST) {
       return this.http.post(url, body, opt);
@@ -729,6 +734,8 @@ export class ApplicationServiceFacade {
       return this.http.delete(body ? url + JSON.parse(body) : url, opt);
     } else if (method === HTTPMethod.PUT) {
       return this.http.put(url, body, opt);
-    } else return this.http.get(body ? (url + body) : url, opt);
+    } else {
+      return this.http.get(body ? (url + body) : url, opt);
+    }
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
index ff27ef5..0cb8557 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/bucket-browser.service.ts
@@ -1,8 +1,11 @@
 import { Injectable } from '@angular/core';
-import {BehaviorSubject, Observable} from 'rxjs';
+
+import {Observable} from 'rxjs';
 import {catchError, map} from 'rxjs/operators';
 import {ErrorUtils} from '../util';
 import {ApplicationServiceFacade} from './applicationServiceFacade.service';
+import {insideWorkspace} from '@angular/cli/utilities/project';
+
 
 export class TodoItemNode {
   children: TodoItemNode[];
@@ -11,46 +14,12 @@ export class TodoItemNode {
   size: number;
 }
 
-/** Flat to-do item node with expandable and level information */
 export class TodoItemFlatNode {
   item: string;
   level: number;
   expandable: boolean;
 }
 
-/**
- * The Json object for to-do list data.
- */
-
-
-const array = [{'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '4.txt', 'size': '18 bytes', 'creationDate': '21-4-2020 11:36:36'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '5.txt', 'size': '18 bytes', 'creationDate': '21-4-2020 11:56:46'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'Untitled', 'size': '5 bytes', 'creationDate': '13-4-2020 03:39:11'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'adasdas', 'size': '1 KB', 'creationDate': '15-4-2020 02:17 [...]
-
-const processFiles = (files, target) => {
-  let pointer = target;
-  files.forEach((file, index) => {
-    if (!pointer[file]) {
-      pointer[file] = {};
-    }
-    pointer = pointer[file];
-  });
-
-};
-
-const processFolderArray = (acc, curr) => {
-  const files = curr.object.split('/').filter(x => x.length > 0);
-  processFiles(files, acc);
-  return acc;
-};
-
-const convertToFolderTree = (data) => data
-  .reduce(
-    processFolderArray,
-    {}
-  );
-
-const TREE_DATA = convertToFolderTree(array);
-
-
 @Injectable({
   providedIn: 'root'
 })
@@ -71,58 +40,6 @@ export class BucketBrowserService {
         catchError(ErrorUtils.handleServiceError));
   }
 
-  public initialize() {
-    let backetData = [];
-    this.getBacketData().subscribe(v => {
-      this.serverData = v;
-      backetData = convertToFolderTree(v);
-      const data = this.buildFileTree({'ofuks-1304-prj1-local-bucket': backetData}, 0);
-      this.dataChange.next(data);
-    });
-    // this.serverData = array;
-    // backetData = convertToFolderTree(this.serverData);
-    // const data = this.buildFileTree({'ofuks-1304-prj1-local-bucket': backetData}, 0);
-    // this.dataChange.next(data);
-  }
-
-  /**
-   * Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
-   * The return value is the list of `TodoItemNode`.
-   */
-  public buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
-    return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
-      const value = obj[key];
-      const node = new TodoItemNode();
-      node.item = key;
-      if (Object.keys(value).length) {
-        if (typeof value === 'object') {
-          node.children = this.buildFileTree(value, level + 1);
-        } else {
-          node.item = value;
-        }
-      } else {
-        node.size = this.serverData.filter(v => v.object.indexOf(node.item) !== -1)[0];
-      }
-      return accumulator.concat(node);
-    }, []);
-  }
-
-  public insertItem(parent: TodoItemNode, name, isFile) {
-    if (parent.children) {
-      if (isFile) {
-        parent.children.push(name as TodoItemNode);
-      } else {
-        parent.children.unshift({item: name, children: []} as TodoItemNode);
-        this.dataChange.next(this.data);
-      }
-    }
-  }
-
-  public updateItem(node: TodoItemNode, file) {
-    node.item = file;
-    this.dataChange.next(this.data);
-  }
-
   public downloadFile(data) {
     return this.applicationServiceFacade
       .buildDownloadFileFromBucket(data)
@@ -147,7 +64,5 @@ export class BucketBrowserService {
         map(response => response),
         catchError(ErrorUtils.handleServiceError));
   }
-  // initBucket(bucketType) {
-  //   bucketType !== 'project' ? TREE_DATA = local : TREE_DATA = projecta;
-  // }
+
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/util/fileUtils.ts b/services/self-service/src/main/resources/webapp/src/app/core/util/fileUtils.ts
index 98ec0f5..d1c2628 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/util/fileUtils.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/util/fileUtils.ts
@@ -37,6 +37,16 @@ export class FileUtils {
     window.URL.revokeObjectURL(url);
   }
 
+  public static downloadBigFiles(data, fileName) {
+    const a = document.createElement('a');
+    document.body.appendChild(a);
+    const blob = new Blob([data], { type: 'octet/stream' }),
+      url = window.URL.createObjectURL(blob);
+    a.href = url;
+    a.download = fileName;
+    a.click();
+    window.URL.revokeObjectURL(url);
+  }
 
   public static copyToClipboard(val: string) {
     const selBox = document.createElement('textarea');
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
index 8671562..3feda30 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
@@ -22,7 +22,8 @@
     <h4 class="modal-title">Bucket browser</h4>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
-  <div class="dialog-content tabs">
+  
+  <div class="dialog-content tabs" [hidden]="!path" >
     <div class="submit m-bott-10 m-top-10">
       <button mat-raised-button type="button" class="butt action"><input type="file" (change)="handleFileInput($event)">Add file</button>
       <button mat-raised-button type="button" class="butt action" (click)="folderTreeComponent.addNewItem(selectedFolder, '', false)">Create folder</button>
@@ -52,10 +53,10 @@
                 <span class="item-name">{{file.item}}</span>
               </div>
               <div class="size">{{file.size.size}}</div>
-              <div class="size">{{file.size.creationDate }}</div>
-<!--              <div class="progress-wrapper">-->
-<!--&lt;!&ndash;                <div class="progres" *ngIf="file.isSelected"><div class="bar"></div></div>&ndash;&gt;-->
-<!--              </div>-->
+              <div class="size" *ngIf="!file.isDownloading">{{file.size.lastModifiedDate }}</div>
+              <div class="progress-wrapper" *ngIf="file.isDownloading">
+                <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+              </div>
             </div>
 
           </li>
@@ -64,15 +65,9 @@
           <li *ngFor="let file of addedFiles" class="folder-item">
             <div class="folder-item-wrapper">
               <div class="name">{{file.item}}</div>
-              <div class="size">{{file.size}} MB</div>
+              <div class="size">{{file.size}}MB</div>
               <div class="progress-wrapper">
-<!--                <div class="progres">-->
-<!--                  <div class="bar" [ngClass]="{'full': isUploading}">-->
-<!--                    -->
-<!--                  </div>-->
-
-<!--                </div>-->
-              <mat-progress-bar mode="indeterminate" *ngIf="isUploading"></mat-progress-bar>
+                <mat-progress-bar mode="indeterminate" *ngIf="isUploading"></mat-progress-bar>
               </div>
               <div (click)="deleteAddedFile(file)"><i class="material-icons close">close</i></div>
             </div>
@@ -87,4 +82,12 @@
       <button *ngIf="this.addedFiles.length !== 0" type="button" class="butt butt-success" mat-raised-button (click)="uploadNewFile()">Upload</button>
     </div>
   </div>
+  
+  <div class="loading-block" *ngIf="!path">
+    <div class="uploading">
+      <p>Please wait until DLab loads bucket: <span class="strong">{{data.bucket}}</span>...</p>
+      <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+    </div>
+  </div>
+
 </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
index b9033da..bb16184 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
@@ -18,6 +18,22 @@
  */
 
 .bucket-browser {
+  .loading-block{
+    width: 80%;
+    margin: 20% auto 0 auto;
+    display: flex;
+    align-content: center;
+    justify-content: center;
+    height: 100%;
+    .uploading{
+      width: 100%;
+      text-align: center;
+      p{
+        margin-bottom: 20px;
+      }
+    }
+  }
+
   .path{
     margin: 0 4px 10px 4px;
     padding: 4px 4px 4px 20px;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
index fef7d0d..04b2d3f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
@@ -26,6 +26,12 @@ import { ManageUngitService } from '../../core/services';
 import {FolderTreeComponent} from './folder-tree/folder-tree.component';
 import {BucketBrowserService, TodoItemNode} from '../../core/services/bucket-browser.service';
 import {FileUtils} from '../../core/util';
+import {BucketDataService} from './bucket-data.service';
+import FileSaver from 'file-saver';
+import {HttpErrorResponse, HttpEventType} from '@angular/common/http';
+import {catchError, map} from 'rxjs/operators';
+import {of} from 'rxjs';
+
 
 @Component({
   selector: 'dlab-bucket-browser',
@@ -33,7 +39,6 @@ import {FileUtils} from '../../core/util';
   styleUrls: ['./bucket-browser.component.scss']
 })
 export class BucketBrowserComponent implements OnInit {
-  public filenames: Array<any> = [];
   public addedFiles = [];
   public folderItems = [];
   public path = '';
@@ -55,13 +60,14 @@ export class BucketBrowserComponent implements OnInit {
     private manageUngitService: ManageUngitService,
     private _fb: FormBuilder,
     private bucketBrowserService: BucketBrowserService,
+    private bucketDataService: BucketDataService,
     private formBuilder: FormBuilder
   ) {
 
   }
 
   ngOnInit() {
-    // this.bucketBrowserService.getBacketData();
+    this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
     this.endpoint = this.data.endpoint;
     this.uploadForm = this.formBuilder.group({
       file: ['']
@@ -74,54 +80,46 @@ export class BucketBrowserComponent implements OnInit {
   }
 
   public handleFileInput(event) {
-    //   for (let i = 0; i < files.length; i++) {
-    //     const file = files[i];
-    //     const path = file.webkitRelativePath.split('/');
-    //   }
-    // }
     if (event.target.files.length > 0) {
       const file = event.target.files[0];
       this.uploadForm.get('file').setValue(file);
-      this.filenames = Object['values'](event.target.files).map(v => ({item: v.name, 'size': (v.size / 1048576).toFixed(2)} as unknown as TodoItemNode));
-      this.addedFiles = [...this.addedFiles, ...this.filenames];
+      const newAddedFiles = Object['values'](event.target.files).map(v => (
+        {item: v['name'], 'size': (v['size'] / 1048576).toFixed(2)} as unknown as TodoItemNode  ));
+      this.addedFiles = [...newAddedFiles];
     }
-
   }
 
+
   public toggleSelectedFile(file) {
+   // remove if when will be possible download several files
+    if (!file.isSelected) {
+      this.folderItems.forEach(item => item.isSelected = false);
+    }
+
     file.isSelected = !file.isSelected;
     this.selected = this.folderItems.filter(item => item.isSelected);
   }
 
   filesPicked(files) {
-    // console.log(files);
 
     Array.prototype.forEach.call(files, file => {
       this.addedFiles.push(file.webkitRelativePath);
     });
-    // console.log(this.addedFiles);
   }
 
   public onFolderClick(event) {
     this.selectedFolder = event.flatNode;
     this.folderItems = event.element ? event.element.children : event.children;
-    // this.folderItems = this.folderItems.sort((a, b) => (a.children > b.children) ? 1 : -1)
-    // console.log(this.folderItems);
-    this.path = event.path;
-    this.pathInsideBucket = this.path.indexOf('/') !== -1 ?  this.path.slice(this.path.indexOf('/') + 1) + '/' : '';
-    this.bucketName = this.path.substring(0, this.path.indexOf('/')) || this.path;
-    this.folderItems.forEach(item => item.isSelected = false);
-  }
+    if (this.folderItems) {
+      const folders = this.folderItems.filter(v => v.children).sort((a, b) => a.item > b.item ? 1 : -1);
+      const files = this.folderItems.filter(v => !v.children).sort((a, b) => a.item > b.item ? 1 : -1);
+      this.folderItems = [...folders, ...files];
+      this.path = event.path;
+      this.pathInsideBucket = this.path.indexOf('/') !== -1 ?  this.path.slice(this.path.indexOf('/') + 1) + '/' : '';
+      this.bucketName = this.path.substring(0, this.path.indexOf('/')) || this.path;
+      this.folderItems.forEach(item => item.isSelected = false);
+    }
 
-  private upload(tree, path) {
-    tree.files.forEach(file => {
-      this.addedFiles.push(path + file.name);
-    });
-    tree.directories.forEach(directory => {
-      const newPath = path + directory.name + '/';
-      this.addedFiles.push(newPath);
-      this.upload(directory, newPath);
-    });
   }
 
   public deleteAddedFile(file) {
@@ -134,31 +132,12 @@ export class BucketBrowserComponent implements OnInit {
     const formData = new FormData();
     formData.append('file', this.uploadForm.get('file').value);
     formData.append('object', path);
-    formData.append('bucket', 'ofuks-1304-prj1-local-bucket');
-    formData.append('endpoint', this.endpoint);
-    // file.inProgress = true;
+    formData.append('bucket', this.bucketName);
+    formData.append('endpoint', this.endpoint);    
     this.isUploading = true;
     this.bucketBrowserService.uploadFile(formData)
-      // .pipe(
-      // map(event => {
-      //   switch (event.type) {
-      //     case HttpEventType.UploadProgress:
-      //       file.progress = Math.round(event.loaded * 100 / event.total);
-      //       break;
-      //     case HttpEventType.Response:
-      //       return event;
-      //   }
-      // }),
-      // catchError((error: HttpErrorResponse) => {
-      //   file.inProgress = false;
-      //   return of(`${file.name} upload failed.`);
-      // }))
       .subscribe((event: any) => {
-      //   if (typeof (event) === 'object') {
-      //     console.log(event.body);
-      //   }
-      // this.isUploading = false;
-        this.bucketBrowserService.initialize();
+        this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
         this.addedFiles = [];
         this.isUploading = false;
         this.toastr.success('File successfully uploaded!', 'Success!');
@@ -168,37 +147,29 @@ export class BucketBrowserComponent implements OnInit {
 
   public fileAction(action) {
     this.selected = this.folderItems.filter(item => item.isSelected);
+    const selected = this.folderItems.filter(item => item.isSelected)[0];
     const path = encodeURIComponent(`${this.pathInsideBucket}${this.selected[0].item}`);
     if (action === 'download') {
-      this.bucketBrowserService.downloadFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}/download`
-      ).subscribe(data =>  {
-        FileUtils.downloadFile(data);
-        // this.downLoadFile(response, 'aplication/octet-stream');
-          this.toastr.success('File downloading started!', 'Success!');
+      selected['isDownloading'] = true;
+      this.bucketBrowserService.downloadFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}/download`)
+        .subscribe(data =>  {
+        FileUtils.downloadBigFiles(data, selected.item);
+        selected['isDownloading'] = false;
+        this.folderItems.forEach(item => item.isSelected = false);
         }, error => this.toastr.error(error.message || 'File downloading error!', 'Oops!')
       );
     }
 
     if (action === 'delete') {
       this.bucketBrowserService.deleteFile(`/${this.bucketName}/object/${path}/endpoint/${this.endpoint}`).subscribe(() => {
-        this.bucketBrowserService.initialize();
+
+        this.bucketDataService.refreshBucketdata(this.data.bucket, this.data.endpoint);
           this.toastr.success('File successfully deleted!', 'Success!');
+        this.folderItems.forEach(item => item.isSelected = false);
         }, error => this.toastr.error(error.message || 'File deleting error!', 'Oops!')
       );
     }
-
-    this.folderItems.forEach(item => item.isSelected = false);
-    this.selected = this.folderItems.filter(item => item.isSelected);
   }
-
-  // downLoadFile(data: any, type: string) {
-  //   const blob = new Blob([data], { type: type});
-  //   const url = window.URL.createObjectURL(blob);
-  //   const pwa = window.open(url);
-  //   if (!pwa || pwa.closed || typeof pwa.closed === 'undefined') {
-  //     alert( 'Please disable your Pop-up blocker and try again.');
-  //   }
-  // }
 }
 
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
new file mode 100644
index 0000000..7a169e5
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
@@ -0,0 +1,108 @@
+/*
+ * 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 { Injectable } from '@angular/core';
+import { BehaviorSubject, of } from 'rxjs';
+import {BucketBrowserService, TodoItemNode} from '../../core/services/bucket-browser.service';
+
+const array = [{'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '4.txt', 'size': '18 bytes', 'creationDate': '21-4-2020 11:36:36'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': '5.txt', 'size': '18 bytes', 'creationDate': '21-4-2020 11:56:46'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'Untitled', 'size': '5 bytes', 'creationDate': '13-4-2020 03:39:11'}, {'bucket': 'ofuks-1304-prj1-local-bucket', 'object': 'adasdas', 'size': '1 KB', 'creationDate': '15-4-2020 02:17 [...]
+
+
+@Injectable()
+export class BucketDataService {
+  public _bucketData = new BehaviorSubject<any>(null);
+  private serverData: any = [];
+  get data(): TodoItemNode[] { return this._bucketData.value; }
+  constructor(
+    private bucketBrowserService: BucketBrowserService,
+  ) {
+  }
+
+  public refreshBucketdata(bucket, endpoint) {
+    let backetData = [];
+    this.bucketBrowserService.getBacketData(bucket, endpoint).subscribe(v => {
+      this.serverData = v;
+      backetData = this.convertToFolderTree(v);
+      const data = this.buildFileTree({[bucket]: backetData}, 0);
+      this._bucketData.next(data);
+    });
+
+    //   this.serverData = array;
+    //   backetData = this.convertToFolderTree(array);
+    //   const data = this.buildFileTree({[bucket]: backetData}, 0);
+    //   this._bucketData.next(data);
+    // }
+  }
+  public buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
+      return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
+        const value = obj[key];
+        const node = new TodoItemNode();
+        node.item = key;
+        if (Object.keys(value).length) {
+          if (typeof value === 'object') {
+            node.children = this.buildFileTree(value, level + 1);
+          } else {
+            node.item = value;
+          }
+        } else {
+          node.size = this.serverData.filter(v => v.object.indexOf(node.item) !== -1)[0];
+        }
+        return accumulator.concat(node);
+      }, []);
+    }
+
+  public insertItem(parent: TodoItemNode, name, isFile) {
+      if (parent.children) {
+        if (isFile) {
+          parent.children.push(name as TodoItemNode);
+        } else {
+          parent.children.unshift({item: name, children: []} as TodoItemNode);
+          this._bucketData.next(this.data);
+        }
+      }
+    }
+
+  public updateItem(node: TodoItemNode, file) {
+      node.item = file;
+      this._bucketData.next(this.data);
+    }
+
+    public processFiles = (files, target) => {
+      let pointer = target;
+      files.forEach((file, index) => {
+        if (!pointer[file]) {
+          pointer[file] = {};
+        }
+        pointer = pointer[file];
+      });
+    }
+
+    public processFolderArray = (acc, curr) => {
+      const files = curr.object.split('/').filter(x => x.length > 0);
+      this.processFiles(files, acc);
+      return acc;
+    }
+
+    public convertToFolderTree = (data) => data
+      .reduce(
+        this.processFolderArray,
+        {}
+      )
+
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
index 0b367c5..2e93cc1 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
@@ -1,8 +1,11 @@
-import {Component, OnInit, AfterViewInit, Output, EventEmitter} from '@angular/core';
+import {Component, OnInit, AfterViewInit, Output, EventEmitter, OnDestroy} from '@angular/core';
 import {SelectionModel} from '@angular/cdk/collections';
 import {FlatTreeControl} from '@angular/cdk/tree';
 import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
 import {BucketBrowserService, TodoItemFlatNode, TodoItemNode} from '../../../core/services/bucket-browser.service';
+import {BucketDataService} from '../bucket-data.service';
+import {Subscription} from 'rxjs';
+
 
 
 @Component({
@@ -10,39 +13,45 @@ import {BucketBrowserService, TodoItemFlatNode, TodoItemNode} from '../../../cor
   templateUrl: './folder-tree.component.html',
   styleUrls: ['./folder-tree.component.scss']
 })
-export class FolderTreeComponent implements OnInit {
-
-  @Output() showFolderContent: EventEmitter<any> = new EventEmitter();
 
-  path = [];
-  selectedFolder: TodoItemFlatNode;
-  flatNodeMap = new Map<TodoItemFlatNode, TodoItemNode>();
-  nestedNodeMap = new Map<TodoItemNode, TodoItemFlatNode>();
-  selectedParent: TodoItemFlatNode | null = null;
-  newItemName = '';
-  treeControl: FlatTreeControl<TodoItemFlatNode>;
-  treeFlattener: MatTreeFlattener<TodoItemNode, TodoItemFlatNode>;
-  dataSource: MatTreeFlatDataSource<TodoItemNode, TodoItemFlatNode>;
-
-  checklistSelection = new SelectionModel<TodoItemFlatNode>(true /* multiple */);
-
-  constructor(private bucketBrowserService: BucketBrowserService) {
-    this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel,
-      this.isExpandable, this.getChildren);
+export class FolderTreeComponent implements OnInit, OnDestroy {
 
+  @Output() showFolderContent: EventEmitter<any> = new EventEmitter();
+  private folderTreeSubs;
+  private path = [];
+  private selectedFolder: TodoItemFlatNode;
+  private flatNodeMap = new Map<TodoItemFlatNode, TodoItemNode>();
+  private nestedNodeMap = new Map<TodoItemNode, TodoItemFlatNode>();
+  private selectedParent: TodoItemFlatNode | null = null;
+  private newItemName = '';
+  private subscriptions: Subscription = new Subscription();
+  public treeControl: FlatTreeControl<TodoItemFlatNode>;
+  private treeFlattener: MatTreeFlattener<TodoItemNode, TodoItemFlatNode>;
+  public dataSource: MatTreeFlatDataSource<TodoItemNode, TodoItemFlatNode>;
+
+  private checklistSelection = new SelectionModel<TodoItemFlatNode>(true /* multiple */);
+
+  constructor(
+    private bucketBrowserService: BucketBrowserService,
+    private bucketDataService: BucketDataService,
+    ) {
+    this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel, this.isExpandable, this.getChildren);
     this.treeControl = new FlatTreeControl<TodoItemFlatNode>(this.getLevel, this.isExpandable);
     this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
 
-    bucketBrowserService.dataChange.subscribe(data => {
-      this.dataSource.data = data;
-      if (!this.selectedFolder) {
+    this.subscriptions.add(bucketDataService._bucketData.subscribe(data => {
+     if (data) {
+       this.dataSource.data = data;
         const subject = this.dataSource._flattenedData;
-        subject.subscribe((subjectData) => {
-          this.treeControl.expand(subjectData[0]);
-          this.showItem(subjectData[0]);
+       this.folderTreeSubs = subject.subscribe((subjectData) => {
+          if (this.selectedFolder) {
+            this.selectedFolder = subjectData.filter(v => v.item === this.selectedFolder.item && v.level === this.selectedFolder.level)[0];
+          }
+          this.expandAllParents(this.selectedFolder || subjectData[0]);
+          this.showItem(this.selectedFolder || subjectData[0]);
         });
       }
-    });
+    }));
   }
 
   getLevel = (node: TodoItemFlatNode) => node.level;
@@ -70,14 +79,14 @@ export class FolderTreeComponent implements OnInit {
 
 
   ngOnInit() {
-    // const subject = this.dataSource._flattenedData;
-    // subject.subscribe((data) => {
-    //   this.treeControl.expand(data[0]);
-    //   this.showItem(data[0]);
-    // });
   }
 
-  showItem(el) {
+  ngOnDestroy() {
+    this.folderTreeSubs.unsubscribe();
+    this.bucketDataService._bucketData.next([]);
+  }
+
+  private showItem(el) {
     if (el) {
       this.treeControl.expand(el);
       this.selectedFolder = el;
@@ -92,7 +101,7 @@ export class FolderTreeComponent implements OnInit {
     }
   }
 
-  getPath(el) {
+  private getPath(el) {
     if (el) {
       if (this.path.length === 0) {
         this.path.unshift(el.item);
@@ -105,7 +114,17 @@ export class FolderTreeComponent implements OnInit {
     }
   }
 
-  descendantsAllSelected(node: TodoItemFlatNode): boolean {
+  private expandAllParents(el) {
+    if (el) {
+      this.treeControl.expand(el);
+      if (this.getParentNode(el) !== null) {
+        this.expandAllParents(this.getParentNode(el));
+      }
+    }
+  }
+
+  private descendantsAllSelected(node: TodoItemFlatNode): boolean {
+
     const descendants = this.treeControl.getDescendants(node);
     const descAllSelected = descendants.every(child =>
       this.checklistSelection.isSelected(child)
@@ -113,13 +132,14 @@ export class FolderTreeComponent implements OnInit {
     return descAllSelected;
   }
 
-  descendantsPartiallySelected(node: TodoItemFlatNode): boolean {
+  private descendantsPartiallySelected(node: TodoItemFlatNode): boolean {
+
     const descendants = this.treeControl.getDescendants(node);
     const result = descendants.some(child => this.checklistSelection.isSelected(child));
     return result && !this.descendantsAllSelected(node);
   }
 
-  todoItemSelectionToggle(node: TodoItemFlatNode): void {
+  private todoItemSelectionToggle(node: TodoItemFlatNode): void {
     this.checklistSelection.toggle(node);
   const descendants = this.treeControl.getDescendants(node);
   this.checklistSelection.isSelected(node)
@@ -133,12 +153,12 @@ export class FolderTreeComponent implements OnInit {
   this.checkAllParentsSelection(node);
 }
 
-  todoLeafItemSelectionToggle(node: TodoItemFlatNode): void {
+  private todoLeafItemSelectionToggle(node: TodoItemFlatNode): void {
     this.checklistSelection.toggle(node);
     this.checkAllParentsSelection(node);
   }
 
-  checkAllParentsSelection(node: TodoItemFlatNode): void {
+  private checkAllParentsSelection(node: TodoItemFlatNode): void {
     let parent: TodoItemFlatNode | null = this.getParentNode(node);
     while (parent) {
       this.checkRootNodeSelection(parent);
@@ -146,7 +166,8 @@ export class FolderTreeComponent implements OnInit {
     }
   }
 
-  checkRootNodeSelection(node: TodoItemFlatNode): void {
+
+  private checkRootNodeSelection(node: TodoItemFlatNode): void {
     const nodeSelected = this.checklistSelection.isSelected(node);
     const descendants = this.treeControl.getDescendants(node);
     const descAllSelected = descendants.every(child =>
@@ -159,9 +180,8 @@ export class FolderTreeComponent implements OnInit {
     }
   }
 
-  getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null {
+  private getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null {
     const currentLevel = this.getLevel(node);
-
     if (currentLevel < 1) {
       return null;
     }
@@ -177,15 +197,16 @@ export class FolderTreeComponent implements OnInit {
     }
     return null;
   }
-
-  addNewItem(node: TodoItemFlatNode, file, isFile, path) {
+  
+  private addNewItem(node: TodoItemFlatNode, file, isFile, path) {
     const parentNode = this.flatNodeMap.get(node);
-    this.bucketBrowserService.insertItem(parentNode!, file, isFile);
+    this.bucketDataService.insertItem(parentNode!, file, isFile);
     this.treeControl.expand(node);
   }
 
-  saveNode(node: TodoItemFlatNode, itemValue: string) {
+  private saveNode(node: TodoItemFlatNode, itemValue: string) {
     const nestedNode = this.flatNodeMap.get(node);
-    this.bucketBrowserService.updateItem(nestedNode!, itemValue);
+    this.bucketDataService.updateItem(nestedNode!, itemValue);
+
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
index 817ca22..9e669b6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
@@ -60,14 +60,14 @@
                 class="strong">{{ notebook.password }}</span></p>
 
             <p class="m-top-30">{{ DICTIONARY[PROVIDER].personal_storage }}: &nbsp;</p>
-            <div class="links_block" (click)="bucketBrowser('project', notebook.endpoint)">
+            <div class="links_block" (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint)">
               <p *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'azure' && notebook.account_name">{{ DICTIONARY[PROVIDER].account }}
                 <span class="bucket-info">{{ notebook.account_name}}</span></p>
               <p *ngIf="notebook.bucket_name">{{ DICTIONARY[PROVIDER].container }} <span
                   class="bucket-info">{{ notebook.bucket_name }}</span></p>
             </div>
             <p>Shared endpoint bucket: &nbsp;</p>
-            <div class="links_block" (click)="bucketBrowser('endpoint', notebook.endpoint)">
+            <div class="links_block" (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint)">
               <p *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'azure' && notebook.shared_account_name">{{ DICTIONARY[PROVIDER].account }}
                 <span class="bucket-info">{{ notebook.shared_account_name}}</span></p>
               <p *ngIf="notebook.shared_bucket_name">{{ DICTIONARY[PROVIDER].container }}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
index ca264b9..ac5b6ba 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
@@ -123,8 +123,8 @@ export class DetailDialogComponent implements OnInit {
         : null;
   }
 
-  public bucketBrowser(type, endpoint): void {
-  this.dialog.open(BucketBrowserComponent, { data: {type: type, endpoint: endpoint}, panelClass: 'modal-fullscreen' })
+  public bucketBrowser(bucketName, endpoint): void {
+  this.dialog.open(BucketBrowserComponent, { data: {bucket: bucketName, endpoint: endpoint}, panelClass: 'modal-fullscreen' })
     .afterClosed().subscribe();
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index d2d5f02..c87d12c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -30,6 +30,8 @@ import { ConfirmDeleteAccountDialog } from './manage-ungit/manage-ungit.componen
 import {BucketBrowserComponent} from './bucket-browser/bucket-browser.component';
 import {FolderTreeComponent} from './bucket-browser/folder-tree/folder-tree.component';
 import {MatTreeModule} from '@angular/material/tree';
+import {BucketDataService} from './bucket-browser/bucket-data.service';
+
 
 @NgModule({
   imports: [
@@ -49,6 +51,7 @@ import {MatTreeModule} from '@angular/material/tree';
     FolderTreeComponent
   ],
   entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialog, BucketBrowserComponent, FolderTreeComponent],
+  providers: [BucketDataService],
   exports: [ResourcesComponent]
 })
 export class ResourcesModule { }


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