From d391189a833d71f40508d542d989228eaf72909e Mon Sep 17 00:00:00 2001 From: Nathan Franklin Date: Mon, 2 Oct 2023 15:49:32 -0500 Subject: [PATCH 1/4] Task/wg 145 fix spinner error on project listing page (#148) * Fix spinner/error-handling for projects listing * Fix error-handling for DS proejcts listing * Fix linting --- .../main-welcome/main-welcome.component.html | 14 +++++-- .../main-welcome/main-welcome.component.ts | 33 +++++++++-------- .../src/app/services/agave-systems.service.ts | 34 ++++++++++++++++- angular/src/app/services/projects.service.ts | 37 +++++++++++++++---- 4 files changed, 89 insertions(+), 29 deletions(-) diff --git a/angular/src/app/components/main-welcome/main-welcome.component.html b/angular/src/app/components/main-welcome/main-welcome.component.html index dc4ffed3..2a9b2b8c 100644 --- a/angular/src/app/components/main-welcome/main-welcome.component.html +++ b/angular/src/app/components/main-welcome/main-welcome.component.html @@ -24,7 +24,7 @@ -
+
Loading Projects @@ -34,15 +34,21 @@
-
+
Failed to Connect to Server!
+
+ Unable to get maps: {{ projectsData.failedMessage }} +
+
+ Unable to get projects: {{ dsProjectsData.failedMessage }} +
-
-
+
+
{ - this.notConnected = notConnected; - }); - - combineLatest([this.projectsService.projects, this.agaveSystemsService.projects]).subscribe(([projects, dsProjects]) => { - this.projects = this.agaveSystemsService.getProjectMetadata(projects, dsProjects); - this.spinner = false; - }); + combineLatest([this.projectsService.projectsData, this.agaveSystemsService.projectsData]).subscribe( + ([projectsData, dsProjectsData]) => { + this.projectsData = projectsData; + this.dsProjectsData = dsProjectsData; + if (!this.projectsData.loading && !this.dsProjectsData.loading) { + if (this.projectsData.failedMessage && !this.dsProjectsData.failedMessage) { + // add extra info (i.e. DS project id/description) from related DS projects + this.projectsData.projects = this.agaveSystemsService.getProjectMetadata(projectsData.projects, dsProjectsData.projects); + } + this.loading = false; + } + } + ); } routeToProject(projectUUID: string) { diff --git a/angular/src/app/services/agave-systems.service.ts b/angular/src/app/services/agave-systems.service.ts index 8e6e7dba..e948dd43 100644 --- a/angular/src/app/services/agave-systems.service.ts +++ b/angular/src/app/services/agave-systems.service.ts @@ -2,25 +2,47 @@ import { Injectable } from '@angular/core'; import { System, SystemSummary } from 'ng-tapis'; import { ApiService } from 'ng-tapis'; import { NotificationsService } from './notifications.service'; -import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, Observable, ReplaySubject, combineLatest } from 'rxjs'; +import { map } from 'rxjs/operators'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { EnvService } from '../services/env.service'; import { DesignSafeProjectCollection, Project, AgaveFileOperations } from '../models/models'; +export interface AgaveProjectsData { + projects: SystemSummary[]; + failedMessage: string | null; + loading: boolean; +} + @Injectable({ providedIn: 'root', }) export class AgaveSystemsService { private _systems: ReplaySubject = new ReplaySubject(1); public readonly systems: Observable = this._systems.asObservable(); + private _projects: ReplaySubject = new ReplaySubject(1); public readonly projects: Observable = this._projects.asObservable(); + private _loadingProjects: BehaviorSubject = new BehaviorSubject(true); + public loadingProjects: Observable = this._loadingProjects.asObservable(); + private _loadingProjectsFailedMessage: BehaviorSubject = new BehaviorSubject(''); + public loadingProjectsFailedMessage: Observable = this._loadingProjectsFailedMessage.asObservable(); + public readonly projectsData: Observable; + constructor( private tapis: ApiService, private notificationsService: NotificationsService, private envService: EnvService, private http: HttpClient - ) {} + ) { + this.projectsData = combineLatest([this.projects, this.loadingProjectsFailedMessage, this.loadingProjects]).pipe( + map(([projects, failedMessage, loading]) => ({ + projects, + failedMessage, + loading, + })) + ); + } list() { this.tapis.systemsList({ type: 'STORAGE' }).subscribe( @@ -31,6 +53,11 @@ export class AgaveSystemsService { this._systems.next(null); } ); + + this._projects.next(null); + this._loadingProjects.next(true); + this._loadingProjectsFailedMessage.next(null); + this.http.get(this.envService.designSafeUrl + `/projects/v2/`).subscribe( (resp) => { const projectSystems = resp.projects.map((project) => { @@ -41,9 +68,12 @@ export class AgaveSystemsService { }; }); this._projects.next(projectSystems); + this._loadingProjects.next(false); }, (error) => { this._projects.next(null); + this._loadingProjectsFailedMessage.next(error.message || 'An error occured.'); + this._loadingProjects.next(false); } ); } diff --git a/angular/src/app/services/projects.service.ts b/angular/src/app/services/projects.service.ts index bd4efff1..9b078238 100644 --- a/angular/src/app/services/projects.service.ts +++ b/angular/src/app/services/projects.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; -import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, Observable, ReplaySubject, combineLatest } from 'rxjs'; import { DesignSafeProjectCollection, Project, ProjectRequest, AgaveFileOperations } from '../models/models'; import { RapidProjectRequest } from '../models/rapid-project-request'; import { IpanelsDisplay, defaultPanelsDisplay } from '../models/ui'; @@ -14,6 +14,12 @@ import { MAIN, LOGIN } from '../constants/routes'; import { AgaveSystemsService } from '../services/agave-systems.service'; import { Router } from '@angular/router'; +export interface ProjectsData { + projects: Project[]; + failedMessage: string | null; + loading: boolean; +} + @Injectable({ providedIn: 'root', }) @@ -29,8 +35,13 @@ export class ProjectsService { private _currentProjectUser: BehaviorSubject = new BehaviorSubject(null); public readonly currentProjectUser: Observable = this._currentProjectUser.asObservable(); - private _loadingProjectsFailed: BehaviorSubject = new BehaviorSubject(false); - public loadingProjectsFailed: Observable = this._loadingProjectsFailed.asObservable(); + private _loadingProjects: BehaviorSubject = new BehaviorSubject(true); + public loadingProjects: Observable = this._loadingProjects.asObservable(); + + private _loadingProjectsFailedMessage: BehaviorSubject = new BehaviorSubject(''); + public loadingProjectsFailedMessage: Observable = this._loadingProjectsFailedMessage.asObservable(); + + public readonly projectsData: Observable; private _loadingActiveProject: BehaviorSubject = new BehaviorSubject(true); public loadingActiveProject: Observable = this._loadingActiveProject.asObservable(); @@ -52,7 +63,15 @@ export class ProjectsService { private router: Router, private authService: AuthService, private envService: EnvService - ) {} + ) { + this.projectsData = combineLatest([this.projects, this.loadingProjectsFailedMessage, this.loadingProjects]).pipe( + map(([projects, failedMessage, loading]) => ({ + projects, + failedMessage, + loading, + })) + ); + } public getLastProjectKeyword() { return `${this.envService.env}LastProject`; @@ -72,15 +91,17 @@ export class ProjectsService { } getProjects(): void { - this._loadingProjectsFailed.next(false); + this._loadingProjects.next(true); + this._loadingProjectsFailedMessage.next(null); this.http.get(this.envService.apiUrl + `/projects/`).subscribe( (resp) => { this.updateProjectsList(resp); - this._loadingProjectsFailed.next(false); + this._loadingProjects.next(false); + this._loadingProjectsFailedMessage.next(null); }, (error) => { - this._loadingProjectsFailed.next(true); - this.notificationsService.showErrorToast('Failed to retrieve project data! Geoapi might be down.'); + this._loadingProjects.next(false); + this._loadingProjectsFailedMessage.next(error.message || 'An error occured.'); } ); } From 5d75a2ff4fb3ca8df0100c81c0de84bc915d1135 Mon Sep 17 00:00:00 2001 From: Nathan Franklin Date: Tue, 3 Oct 2023 11:20:56 -0500 Subject: [PATCH 2/4] Fix condition for adding info from related DS projects (#151) --- .../src/app/components/main-welcome/main-welcome.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/angular/src/app/components/main-welcome/main-welcome.component.ts b/angular/src/app/components/main-welcome/main-welcome.component.ts index bd0e54b4..9bb32cc6 100644 --- a/angular/src/app/components/main-welcome/main-welcome.component.ts +++ b/angular/src/app/components/main-welcome/main-welcome.component.ts @@ -51,7 +51,7 @@ export class MainWelcomeComponent implements OnInit { this.projectsData = projectsData; this.dsProjectsData = dsProjectsData; if (!this.projectsData.loading && !this.dsProjectsData.loading) { - if (this.projectsData.failedMessage && !this.dsProjectsData.failedMessage) { + if (!this.projectsData.failedMessage && !this.dsProjectsData.failedMessage) { // add extra info (i.e. DS project id/description) from related DS projects this.projectsData.projects = this.agaveSystemsService.getProjectMetadata(projectsData.projects, dsProjectsData.projects); } From f3bc5afb3e6b2c41f62346896d4cfb114f8f85a6 Mon Sep 17 00:00:00 2001 From: Nathan Franklin Date: Tue, 3 Oct 2023 11:58:29 -0500 Subject: [PATCH 3/4] Fix projects listing from DS (#152) --- .../app/components/file-browser/file-browser.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/angular/src/app/components/file-browser/file-browser.component.ts b/angular/src/app/components/file-browser/file-browser.component.ts index f9dad628..ee5564ee 100644 --- a/angular/src/app/components/file-browser/file-browser.component.ts +++ b/angular/src/app/components/file-browser/file-browser.component.ts @@ -57,9 +57,8 @@ export class FileBrowserComponent implements OnInit { // TODO: change those hard coded systemIds to environment vars or some sort of config // wait on the currentUser, systems and projects to resolve - combineLatest([this.authService.currentUser, this.agaveSystemsService.systems, this.agaveSystemsService.projects]) - .pipe(take(1)) - .subscribe(([user, systems, projects]) => { + combineLatest([this.authService.currentUser, this.agaveSystemsService.systems, this.agaveSystemsService.projects]).subscribe( + ([user, systems, projects]) => { this.myDataSystem = systems.find((sys) => sys.id === 'designsafe.storage.default'); this.communityDataSystem = systems.find((sys) => sys.id === 'designsafe.storage.community'); this.publishedDataSystem = systems.find((sys) => sys.id === 'designsafe.storage.published'); @@ -73,7 +72,8 @@ export class FileBrowserComponent implements OnInit { path: this.currentUser.username, }; this.browse(init); - }); + } + ); } selectSystem(system: SystemSummary): void { From 337eaf68fa56ce487d826550720109a2bf6073f5 Mon Sep 17 00:00:00 2001 From: Nathan Franklin Date: Tue, 3 Oct 2023 13:04:02 -0500 Subject: [PATCH 4/4] Fix check of ds projects (#153) --- angular/src/app/services/agave-systems.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/angular/src/app/services/agave-systems.service.ts b/angular/src/app/services/agave-systems.service.ts index e948dd43..fc74e076 100644 --- a/angular/src/app/services/agave-systems.service.ts +++ b/angular/src/app/services/agave-systems.service.ts @@ -79,7 +79,7 @@ export class AgaveSystemsService { } getProjectMetadata(projects: Project[], dsProjects: SystemSummary[]): Project[] { - if (dsProjects.length > 0) { + if (dsProjects && dsProjects.length > 0) { return projects.map((p) => { const dsProject = dsProjects.find((dp) => dp.id === p.system_id); p.title = dsProject ? dsProject.description : null;