Hello all,
This proposal concerns the choice of a Config API.
The design page: https://design.xwiki.org/xwiki/bin/view/Proposal/ConfigAPI
We have three options:
- Directly use one of the available configuration API.
- Define our own configuration API:
- And delegate the implementation to an available configuration API
- And define our own implementation.
My research of configuration libraries shows a variety of options (e.g., @configu/browser, dotenv, or config).
But, they are all designed in a way that make them usable only usable in a node environment (i.e., outside a browser).
Therefore, I don’t think this option can be considered. Consequently, option 2.1 is also not possible for the same reasons.
Knowing that, I suggest going with our own configuration API.
You can find below some more design requirements and constraints for the design of this API, as well as a snippet of the API interface.
Requirements:
- Able to run in the browser
- Compatible with typescript
- Being able to load a value from a hierarchical set of sources (e.g., a properties file, an XObject…)
- Being able to re-load values at runtime (e.g., an XObject property is updated)
- Preferable: efficient caching mechanism
Use-cases:
A lot of those use-cases does not surface at the API level, but I’m mentioning to give a better view of what I’m aiming for.
- values can change at runtime
- ability to define hierarchies of configurations
- each config component is in charge of defining if it must be loaded first (i.e., at startup or lazily), if it can change over-time, and a when the value can be invalidated
- no need for a notion of right, if a value is returned client-side and shouldn’t be visible to the current user, it’s a security issue on the server
- the API is read-only, the API user has now way to change the configuration value through this API, and must instead use APIs to edit the underlying source to change a config value
- as values can be fetched on-demand from the server (e.g., from a rest endpoint), any configuration result must be wrapped in a Promise, and should be expected to fail for external reasons (e.g., punctual networks issue)
API Example
/**
* Defines a common set of methods to access for configuration values.
*
* @since 0.6
*/
interface ConfigurationSource {
/**
* Get a configuration value for the given key. Fallbacks to the default value if no value is found.
*
* @param key the configuration key to access
* @param defaultValue a default value if no value is found for the request key
* @return a promise with the configuration value
* @throws ConfigurationException in case of issue when accessing the configuration
*/
getProperty<T>(key: string, defaultValue?: T): Promise<T | undefined>;
/**
* Returns `true` if the configuration source has the given key, `false` otherwise.
*
* @param key the configuration key to check the presence of
* @return a promise with `true` if the configuration source has the given key, `false` otherwise`
*/
hasProperty(key: string): Promise<boolean>
}
/**
* Exception thrown in case of configuration issue.
*
* @since 0.6
*/
class ConfigurationException extends Error {
}
Note: the design page has a more extensive snippet including implementation and tests.
WDYT?