diff --git a/src/app/apiserver.ts b/src/app/apiserver.ts index 6dec7014b94810e0192b1e54c0a3b36eca3d7555..e2a72ce6a1e6587132e65ee67d63864f4af0b4cd 100644 --- a/src/app/apiserver.ts +++ b/src/app/apiserver.ts @@ -1,5 +1,6 @@ export class APIServer { name: string; tes: string; - tws: string + tws: string; + ping: number; } diff --git a/src/app/backend-selection.service.ts b/src/app/backend-selection.service.ts index 7c3fb7bf8c8b6354beaea3ecf455ad21532df1e9..4a5889100327b4e58153bc1259835ea74d55f922 100644 --- a/src/app/backend-selection.service.ts +++ b/src/app/backend-selection.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { APIServer } from './apiserver'; -import {BehaviorSubject, pipe, Observable, combineLatest, of} from 'rxjs'; -import {map, tap, switchMap, timeout, catchError } from 'rxjs/operators'; +import {BehaviorSubject, pipe, Observable, combineLatest, of, timer} from 'rxjs'; +import {map, tap, switchMap, timeout, catchError, repeat } from 'rxjs/operators'; import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -18,22 +18,80 @@ export class BackendSelectionService { this.apiservers = new BehaviorSubject([]); this.apiserver = new BehaviorSubject(null); this.localapi = new BehaviorSubject(false); - this.initApiServer(); - this.getAPIServers(); + this.apiservers.subscribe((v) => this.updateApiServer(v)); + //this.apiservers.subscribe((v) => this.pingApiServers(v)); + //this.pingApiServers(); + this.initApiServers(); this.apiserver.subscribe((v) => console.log('apiserver change',v)); this.apiservers.subscribe((v) => console.log('apiservers change',v)); + timer(10000).pipe(repeat()).subscribe(() => this.pingApiServers(10000)) + //this.apiservers.subscribe((v) => removeFailedServer(v): } + private serverAvailable(s: APIServer) { + if (s.tes.indexOf('localhost') == -1) { + return true; + } else { + } + } + private updateApiServer(apiservers: APIServer[]) { + var nextserver: APIServer; + //TODO + // If the current API server is unavailable in the list of apiservers, move to a new one + // also test the ping response for the APIservers to select the best one + + if (this.apiserver.value === null || this.apiserver.value === undefined || !(this.serverAvailable(this.apiserver.value))) { + var lastserver: APIServer; + try { + + lastserver = <APIServer>JSON.parse(localStorage.getItem('apiserver')); + } catch { + lastserver = null; + } + if (lastserver !== null) { + var s: APIServer; + for ( s of apiservers) { + if (lastserver.tes.indexOf(s.tes) != -1 && this.serverAvailable(s)) { + nextserver = s; + } + } + } + if (nextserver === undefined) { + nextserver = apiservers[0] + } + this.apiserver.next(nextserver); + } + } + + + + private pingApiServers(timeouts: number) { + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: false}; + var servers: APIServer[]; + servers = this.apiservers.value; + for (let server of servers) { + combineLatest([of([performance.now()]), + this.http.get<string>(server.tes+'/sshagent',options).pipe( + timeout(timeouts))]) + .subscribe((ok) => {server.ping = (performance.now() - ok[0])}, + (err) => {server.ping = undefined}); + } + } + +/* this.testApiServers(this.apiservers.value).pipe( + map(function (v) {return v.server}) + ) + .subscribe((v) => this.apiservers.next(v))}*/ private testApiServers(servers: APIServer[]): Observable<any> { var obs: Observable<any>[] = []; let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: false}; - console.log('entering testAPIServers'); for (let server of servers) { //obs.push( this.http.get(s.tes+'/sshagent',options).pipe(map((r) => {return {'server':s,'check':r}}))); obs.push( this.http.get<string>(server.tes+'/sshagent',options).pipe( - timeout(2000), + timeout(10000), catchError(e=> {console.error('testing api server',server); console.error(e); return of(null)}), map((v) => {return {'server':server,'query':v}}), )); @@ -46,17 +104,18 @@ export class BackendSelectionService { } private initApiServer() { - try { - this.apiserver.next(<APIServer>JSON.parse(localStorage.getItem('apiserver'))); - } catch { - this.apiserver.next(null); - } + var lastserver: APIServer; + try { + + lastserver = <APIServer>JSON.parse(localStorage.getItem('apiserver')); + } catch { + lastserver = null; + } if (this.apiserver.value === null) { this.apiservers.subscribe((v) => this.defaultApiServer(v)) } if (this.apiserver.value !== undefined && this.apiserver.value !== null && this.apiserver.value.tes.indexOf('localhost') != -1) { this.localapi.next(true); - console.log('localapi true', this.apiserver.value.tes); } else { this.localapi.next(false); } @@ -70,18 +129,16 @@ export class BackendSelectionService { localStorage.setItem('apiserver', JSON.stringify(s)); } - setApiServer(server: APIServer) { - this.apiserver.next(server); - if (this.apiserver.value.tes.indexOf('localhost') != -1) { - this.localapi.next(true); - console.log('localapi true', this.apiserver.value.tes); - } else { - this.localapi.next(false); - console.log('localapi false', this.apiserver.value.tes); - } - - this.saveLastApiServer(this.apiserver.value) - } + private setApiServer(server: APIServer) { + this.apiserver.next(server); + if (this.apiserver.value.tes.indexOf('localhost') != -1) { + this.localapi.next(true); + } else { + this.localapi.next(false); + } + this.saveLastApiServer(this.apiserver.value) + } + /* storeLocalAPIServers(apiservers) { localStorage.setItem('localAPIServers',JSON.stringify(apiservers)); @@ -93,12 +150,18 @@ export class BackendSelectionService { this.getAPIServers(); }*/ - getAPIServers() { + private initApiServers() { + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: false}; + this.http.get<APIServer[]>('./assets/config/apiservers.json',options).subscribe(resp => this.updateAPIServers(resp)); + } + + + private getAPIServers() { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: false}; this.http.get<APIServer[]>('./assets/config/apiservers.json',options).pipe( switchMap((v) => this.testApiServers(v)), - tap((v) => console.log('testapiservers observable fired',v)), map((list, idx) => (<any[]>list) .filter(function (v) { return v.query != null}) .map(function (v) {return v.server})) // Note the two maps are not the same one is an rxjs observable map one is a list map diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index 94bfadb0b2b6bcd051f94b70903cb10192363c96..18a50fabf71397ed8e11d8de4e07b3478aaac471 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -28,8 +28,8 @@ <mat-form-field style="width: 100%"> <mat-label>API Server</mat-label> <mat-select [ngModel]="selectedApiServer" (selectionChange)="backendSelectionService.setApiServer($event.value)"> - <mat-option *ngFor="let apis of backendSelectionService.apiservers|async" [value]="apis"> - {{ apis.name }} + <mat-option *ngFor="let apis of backendSelectionService.apiservers|async" [value]="apis" [disabled] ="apis.ping === undefined"> + {{ apis.name }} <span *ngIf="apis.ping !== undefined">(ping: {{ apis.ping }} ms)</span> </mat-option> </mat-select> </mat-form-field>