UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

111 lines (110 loc) 4.6 kB
// SPDX-License-Identifier: Apache-2.0 import GirafeSingleton from '../../base/GirafeSingleton.js'; import GirafeConfig from './girafeconfig.js'; import { getPropertyByPath, mergeObjects } from '../utils/pathUtils.js'; class ConfigManager extends GirafeSingleton { config = null; // Backup of config before applying user overrides defaultConfig = null; loadingPromise = null; storagePathForOverrides = 'configOverrides'; abortController = new AbortController(); constructor(context) { super(context); globalThis.addEventListener('gg:redirect', () => { this.abortController.abort(); }); } get Config() { return this.config; } getConfigUrls() { /* <meta name="configs" content="main,mobile" /> <link rel="config-main-url" href="config.json" /> <link rel="config-mobile-url" href="config.mobile.json" /> */ const configUrls = []; const configNames = document .querySelector('meta[name=configs]') ?.getAttribute('content') ?.split(',') .map((name) => name.trim()); if (!configNames) { throw new Error("No configuration names found in 'configs' meta tag."); } for (const configName of configNames) { const configUrl = document.querySelector(`link[rel=config-${configName}-url]`)?.getAttribute('href'); if (!configUrl) { throw new Error(`Configuration URL for '${configName}' not found in 'config-${configName}-url' meta tag.`); } configUrls.push(configUrl); } return configUrls; } async loadConfig() { if (this.loadingPromise) { // There's already a promise for loading the configuration // => return it instead of starting another request return this.loadingPromise; } if (this.config) { // Config was already loaded. return Promise.resolve(this.config); } this.loadingPromise = this.doLoadConfig(); return this.loadingPromise; } async doLoadConfig() { // Load config const configUrls = this.getConfigUrls(); let jsonConfig = {}; for (const configUrl of configUrls) { try { const response = await fetch(configUrl, { signal: this.abortController.signal }); const newJsonConfig = await response.json(); jsonConfig = this.mergeConfigs(jsonConfig, newJsonConfig); } catch { // If the loading of config file was aborted due to automatic redirect to another interface // We do not display any error. if (!this.abortController.signal.aborted) { const errorMessage = `Error while reading the configuration file ${configUrl}. Please verify your configuration.`; globalThis.alert(errorMessage); } } } // Create a backup of the default config before applying overrides this.defaultConfig = new GirafeConfig(structuredClone(jsonConfig)); // Load config overrides and merge them with the default config const configOverrides = this.getConfigOverrides(this.defaultConfig.userdata.source); jsonConfig = this.mergeConfigs(jsonConfig, configOverrides); this.config = new GirafeConfig(jsonConfig); console.log('Application Configuration loaded.'); return this.config; } /** * Merges two configuration objects recursively. */ mergeConfigs(obj1, obj2) { return mergeObjects(obj1, obj2); } /** * Retrieves saved configuration overrides based on the user data source. * @param userDataSource The source of user data. */ getConfigOverrides(userDataSource) { // Set the user data source first before requesting config overrides from the userDataManager this.context.userDataManager.setSource(userDataSource); return this.context.userDataManager.getUserData(this.storagePathForOverrides) || {}; } /** * Retrieves the original (default) configuration value before applying overrides. * @param path The path to the configuration property. */ getDefaultConfigValue(path) { const { found, parentObject, lastKey } = getPropertyByPath(this.defaultConfig, path); return found && parentObject && lastKey ? parentObject[lastKey] : undefined; } } export default ConfigManager;