diff --git a/frontend/app/src/app/modules/config/config.provider.spec.ts b/frontend/app/src/app/modules/config/config.provider.spec.ts index 79c6103a..5b09a3c5 100644 --- a/frontend/app/src/app/modules/config/config.provider.spec.ts +++ b/frontend/app/src/app/modules/config/config.provider.spec.ts @@ -67,7 +67,8 @@ describe('ConfigProvider', () => { it('should fetch app configuration', async () => { spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(sampleIndexResponse)); - const result = await configProvider.fetch(); + await configProvider.fetch(); + const result = configProvider.config; expect(result).toEqual(sampleIndexResponse); }); @@ -110,7 +111,7 @@ describe('ConfigProvider', () => { expect(storageProviderSpy.has).toHaveBeenCalled(); expect(storageProviderSpy.get).toHaveBeenCalledTimes(0); expect(configProvider.client.handshake).toHaveBeenCalled(); - expect(await configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name); + expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name); }); it('should throw error on failed initialisation', async () => { @@ -192,4 +193,31 @@ describe('ConfigProvider', () => { expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name); }); + + it('should fetch new config from remote on init', async () => { + storageProviderSpy.has.and.returnValue(Promise.resolve(true)); + storageProviderSpy.get.and.returnValue(Promise.resolve(sampleIndexResponse)); + spyOn(configProvider, 'fetch'); + await configProvider.init(); + + expect(configProvider.fetch).toHaveBeenCalled(); + }); + + it('should update the local config with the one from remote', async () => { + storageProviderSpy.has.and.returnValue(Promise.resolve(true)); + storageProviderSpy.get.and.returnValue(Promise.resolve(sampleIndexResponse)); + const newConfig = structuredClone(sampleIndexResponse); + newConfig.app.name = 'New app name'; + spyOn(configProvider.client, 'handshake').and.returnValue(Promise.resolve(newConfig)); + await configProvider.init(); + + // Validate that the initial configuration is loaded + expect(configProvider.getValue('name')).toEqual(sampleIndexResponse.app.name); + + // Fetch the new configuration from the remote + await configProvider.fetch(); + + // Validate that the new configuration is now set + expect(configProvider.getValue('name')).toEqual(newConfig.app.name); + }); }); diff --git a/frontend/app/src/app/modules/config/config.provider.ts b/frontend/app/src/app/modules/config/config.provider.ts index c26e779f..f64cea40 100644 --- a/frontend/app/src/app/modules/config/config.provider.ts +++ b/frontend/app/src/app/modules/config/config.provider.ts @@ -83,17 +83,20 @@ export class ConfigProvider { /** * Fetches configuration from backend */ - async fetch(): Promise { + async fetch(): Promise { try { const isOffline = await firstValueFrom(this.internetConnectionService.offline$); if (isOffline) { throw new Error('Device is offline.'); } else { - return await this.client.handshake(this.scVersion); + const fetchedConfig: SCIndexResponse = await this.client.handshake(this.scVersion); + await this.set(fetchedConfig); + this.logger.log(`Configuration updated from remote`); } } catch (error) { const error_ = error instanceof Error ? new ConfigFetchError(error.message) : new ConfigFetchError(); - throw error_; + this.logger.warn(`Failed to fetch remote configuration:`, error_); + throw error_; // Rethrow the error to handle it in init() } } @@ -121,40 +124,33 @@ export class ConfigProvider { /** * Initialises the ConfigProvider - * @throws ConfigInitError if no configuration could be loaded. - * @throws WrongConfigVersionInStorage if fetch failed and saved config has wrong SCVersion + * @throws ConfigInitError if no configuration could be loaded both locally and remote. */ async init(): Promise { - let loadError; - let fetchError; - // load saved configuration try { + // Attempt to load the configuration from local storage this.config = await this.loadLocal(); this.firstSession = false; this.logger.log(`initialised configuration from storage`); + + // Check if the stored configuration has the correct version if (this.config.backend.SCVersion.split('.')[0] !== this.scVersion.split('.')[0]) { - loadError = new WrongConfigVersionInStorage(this.scVersion, this.config.backend.SCVersion); + throw new WrongConfigVersionInStorage(this.scVersion, this.config.backend.SCVersion); } - } catch (error) { - loadError = error; - } - // fetch remote configuration from backend - try { - const fetchedConfig: SCIndexResponse = await this.fetch(); - await this.set(fetchedConfig); - this.logger.log(`initialised configuration from remote`); - } catch (error) { - fetchError = error; - } - // check for occurred errors and throw them - if (loadError !== undefined && fetchError !== undefined) { - throw new ConfigInitError(); - } - if (loadError !== undefined) { + + // Fetch the remote configuration in a non-blocking manner + void this.fetch(); + } catch (loadError) { this.logger.warn(loadError); - } - if (fetchError !== undefined) { - this.logger.warn(fetchError); + + try { + // If local loading fails, immediately try to fetch the configuration from remote + await this.fetch(); + } catch (fetchError) { + this.logger.warn(`Failed to fetch remote configuration:`, fetchError); + // If both local loading and remote fetching fail, throw ConfigInitError + throw new ConfigInitError(); + } } }