@webda/core
Version:
Expose API with Lambda
178 lines • 6.49 kB
JavaScript
import jsonpath from "jsonpath";
import { WebdaError } from "../index.js";
import { Service, ServiceParameters } from "./service.js";
export class ConfigurationServiceParameters extends ServiceParameters {
constructor(params) {
super(params);
this.checkInterval ?? (this.checkInterval = 3600);
}
}
/**
* Handle sessionSecret ( rolling between two secrets ) expire every hour
* Handle longTermSecret ( rolling between two longer secret ) expire every month
*
* Load configuration from another service
*
* If the result contains `webda.services` in his object then webda configuration will
* be dynamically reloaded
*
* @category CoreServices
* @WebdaModda
*/
export default class ConfigurationService extends Service {
constructor() {
super(...arguments);
/**
* Watchs for configuration update
*/
this.watchs = [];
}
/**
* Load parameters
*
* @param params
* @ignore
*/
loadParameters(params) {
return new ConfigurationServiceParameters(params);
}
/**
* @inheritdoc
*/
async init() {
// Check interval by default every hour
if (!this.parameters.source) {
throw new WebdaError.CodeError("CONFIGURATION_SOURCE_MISSING", "Need a source for ConfigurationService");
}
let source = this.parameters.source.split(":");
this.sourceService = this.getService(source[0]);
if (!this.sourceService) {
throw new WebdaError.CodeError("CONFIGURATION_SOURCE_INVALID", 'Need a valid service for source ("sourceService:sourceId")');
}
this.sourceId = source[1];
if (!this.sourceId) {
throw new WebdaError.CodeError("CONFIGURATION_SOURCE_INVALID", 'Need a valid source ("sourceService:sourceId")');
}
if (!this.sourceService.getConfiguration) {
throw new WebdaError.CodeError("CONFIGURATION_SOURCE_INVALID", `Service '${source[0]}' is not implementing ConfigurationProvider interface`);
}
this.serializedConfiguration = JSON.stringify(this.parameters.default);
await this.checkUpdate();
if (!this.sourceService.canTriggerConfiguration(this.sourceId, this.checkUpdate.bind(this, true))) {
this.interval = setInterval(this.checkUpdate.bind(this), 1000);
}
// Add webda info
this.watch("$.services", (updates) => this._webda.reinit(updates));
return this;
}
/**
* Watch a specific configuration modification
*
* @param jsonPath JSON Path to the object to watch
* @param callback Method to call with the updated version
* @param defaultValue Default value of the jsonPath if it does not exist
*/
watch(jsonPath, callback, defaultValue = undefined) {
this.watchs.push({ path: jsonPath, callback, defaultValue });
}
/**
* Clear the check interval if exist
*/
async stop() {
if (this.interval !== undefined) {
// @ts-ignore
clearInterval(this.interval);
}
return super.stop();
}
/**
*
* @returns current configuration
*/
getConfiguration() {
return this.configuration || {};
}
/**
* We cannot reinit the configurationService by itself
*
* @inheritdoc
*/
async reinit(_config) {
// Need to prevent any reinit
return this;
}
/**
* Load the configuration by calling the source service with the source id
* @returns
*/
async loadConfiguration() {
return this.sourceService.getConfiguration(this.sourceId);
}
async initConfiguration() {
throw new Error("ConfigurationService with dependencies cannot be used");
}
/**
* Checking for configuration updates
*
* If configuration is updated, it will trigger all the watchs accordingly
*
* @returns
*/
async checkUpdate(dynamic = false) {
// If the ConfigurationProvider cannot trigger we check at interval
if (!dynamic && this.interval && this.nextCheck > Date.now()) {
return;
}
this.log("DEBUG", "Refreshing configuration");
const newConfig = (await this.loadConfiguration()) || this.parameters.default;
this.emit("Configuration.Loaded", newConfig);
const serializedConfig = JSON.stringify(newConfig);
if (serializedConfig !== this.serializedConfiguration) {
this.emit("Configuration.Applying", newConfig);
this.log("DEBUG", "Apply new configuration");
this.serializedConfiguration = serializedConfig;
this.configuration = newConfig;
// Add the webda parameters logical
if (this.configuration && this.configuration.services) {
// Merge parameters with each service - cannot add new services for security
for (let i in this.configuration.services) {
if (this.getWebda().getService(i)) {
this.configuration.services[i] = this.getWebda().getServiceParams(i, this.configuration);
}
}
}
let promises = [];
this.watchs.forEach(w => {
this.log("TRACE", "Apply new configuration value", jsonpath.query(newConfig, w.path).pop() || w.defaultValue);
let p = w.callback(jsonpath.query(newConfig, w.path).pop() || w.defaultValue);
if (p) {
promises.push(p);
}
});
await Promise.all(promises);
this.emit("Configuration.Applied", newConfig);
}
// If the ConfigurationProvider cannot trigger we check at interval
if (this.interval) {
this.updateNextCheck();
this.log("DEBUG", "Next configuration refresh in", this.parameters.checkInterval, "s");
}
}
/**
* Update the next check time
*/
updateNextCheck() {
this.nextCheck = Date.now() + this.parameters.checkInterval * 1000;
}
/**
* Read the file and store it
*/
async loadAndStoreConfiguration() {
let res = await this.loadConfiguration();
this.emit("Configuration.Loaded", res);
this.serializedConfiguration = JSON.stringify(res);
return res;
}
}
export { ConfigurationService };
//# sourceMappingURL=configuration.js.map