11import { HttpClient } from '@angular/common/http' ;
22import { Injectable } from '@angular/core' ;
3- import { iif , merge , Observable , of , Subject , timer } from 'rxjs' ;
4- import { catchError , map , shareReplay , switchMap } from 'rxjs/operators' ;
3+ import { BehaviorSubject , iif , merge , Observable , of , Subject , timer } from 'rxjs' ;
4+ import { catchError , delay , map , retryWhen , shareReplay , switchMap , tap } from 'rxjs/operators' ;
5+ import { webSocket } from 'rxjs/webSocket' ;
56
67import { Auth } from '..' ;
78import { environment } from '../../../../environments/environment' ;
@@ -31,15 +32,19 @@ const DEFAULT_ADMIN_SETTINGS: AdminSettings = {
3132 enableOIDCKubeconfig : false ,
3233} ;
3334
34- @Injectable ( )
35+ @Injectable ( {
36+ providedIn : 'root' ,
37+ } )
3538export class SettingsService {
36- private readonly restRoot : string = environment . restRoot ;
39+ private readonly restRoot = environment . restRoot ;
40+ private readonly wsProtocol = window . location . protocol . replace ( 'http' , 'ws' ) ;
41+ private readonly wsRoot = `${ this . wsProtocol } //${ window . location . host } /${ this . restRoot } /ws` ;
3742 private _userSettings$ : Observable < UserSettings > ;
38- private _userSettingsRefresh$ : Subject < any > = new Subject ( ) ;
39- private _adminSettings$ : Observable < AdminSettings > ;
40- private _adminSettingsRefresh $ : Subject < any > = new Subject ( ) ;
43+ private _userSettingsRefresh$ = new Subject ( ) ;
44+ private readonly _adminSettings$ = new BehaviorSubject ( DEFAULT_ADMIN_SETTINGS ) ;
45+ private _adminSettingsWatch $ : Observable < AdminSettings > ;
4146 private _admins$ : Observable < AdminEntity [ ] > ;
42- private _adminsRefresh$ : Subject < any > = new Subject ( ) ;
47+ private _adminsRefresh$ = new Subject ( ) ;
4348 private _refreshTimer$ = timer ( 0 , this . _appConfigService . getRefreshTimeBase ( ) * 5 ) ;
4449
4550 constructor (
@@ -86,28 +91,29 @@ export class SettingsService {
8691 }
8792
8893 get adminSettings ( ) : Observable < AdminSettings > {
89- if ( ! this . _adminSettings$ ) {
90- this . _adminSettings$ =
91- merge ( this . _refreshTimer$ , this . _adminSettingsRefresh$ )
92- . pipe ( switchMap (
93- ( ) =>
94- iif ( ( ) => this . _auth . authenticated ( ) , this . _getAdminSettings ( true ) , of ( DEFAULT_ADMIN_SETTINGS ) ) ) )
95- . pipe ( map ( settings => this . _defaultAdminSettings ( settings ) ) )
96- . pipe ( shareReplay ( { refCount : true , bufferSize : 1 } ) ) ;
94+ // Subscribe to websocket and proxy all the settings updates coming from the API to the subject that is
95+ // exposed in this method. Thanks to that it is possible to have default value and retry mechanism that
96+ // will run in the background if connection will fail. Subscription to the API should happen only once.
97+ // Behavior subject is used internally to always emit last value when subscription happens.
98+ if ( ! this . _adminSettingsWatch$ ) {
99+ const webSocket$ =
100+ webSocket < AdminSettings > ( `${ this . wsRoot } /admin/settings` )
101+ . asObservable ( )
102+ . pipe ( retryWhen (
103+ // Display error in the console for debugging purposes, otherwise it would be ignored.
104+ // tslint:disable-next-line:no-console
105+ errors => errors . pipe ( tap ( console . debug ) , delay ( this . _appConfigService . getRefreshTimeBase ( ) * 3 ) ) ) ) ;
106+ this . _adminSettingsWatch$ = iif ( ( ) => this . _auth . authenticated ( ) , webSocket$ , of ( DEFAULT_ADMIN_SETTINGS ) ) ;
107+ this . _adminSettingsWatch$ . subscribe ( settings => this . _adminSettings$ . next ( this . _defaultAdminSettings ( settings ) ) ) ;
97108 }
109+
98110 return this . _adminSettings$ ;
99111 }
100112
101113 get defaultAdminSettings ( ) : AdminSettings {
102114 return DEFAULT_ADMIN_SETTINGS ;
103115 }
104116
105- private _getAdminSettings ( defaultOnError = false ) : Observable < AdminSettings > {
106- const url = `${ this . restRoot } /admin/settings` ;
107- const observable = this . _httpClient . get < AdminSettings > ( url ) ;
108- return defaultOnError ? observable . pipe ( catchError ( ( ) => of ( DEFAULT_ADMIN_SETTINGS ) ) ) : observable ;
109- }
110-
111117 private _defaultAdminSettings ( settings : AdminSettings ) : AdminSettings {
112118 if ( ! settings ) {
113119 return DEFAULT_ADMIN_SETTINGS ;
@@ -120,10 +126,6 @@ export class SettingsService {
120126 return settings ;
121127 }
122128
123- refreshAdminSettings ( ) : void {
124- this . _adminSettingsRefresh$ . next ( ) ;
125- }
126-
127129 patchAdminSettings ( patch : any ) : Observable < AdminSettings > {
128130 const url = `${ this . restRoot } /admin/settings` ;
129131 return this . _httpClient . patch < AdminSettings > ( url , patch ) ;
0 commit comments