@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
111 lines (110 loc) • 4.6 kB
JavaScript
// 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;