How to change the configuration programmatically
map.apps allows a running app to inspect and change the configuration of its components programmatically through the so-called Configuration Admin Service.
This service provides read and write access to the component configurations of the app.json
in a way that allows to even persist modified configurations if desired.
Persisting modified configurations requires an app.json
to be stored in a database or other kind of system that allows to update the app.json.
For example, you cannot persistently modify the Demo app of map.apps for Developers in this way.
But persisting changes to a component configuration is not the only use case where the Configuration Admin Service comes in handy.
You can also just pass on a modified version of a component’s configuration to the App Runtime without actually changing the app.json
.
For each of the use cases, you find a how-to guide on this page.
But first, let’s explore a basic example to get an impression of what it takes to make use of the Configuration Admin Service.
Changing a component configuration
Here, we will demonstrate how to edit and save the configuration of a simple component. Changing the configuration of a factory component, is demonstrated here.
Step 1: Inject the Configuration Admin Service
First, you need to get instance of the Configuration Admin Service.
In this example it is injected into the MyComponentUpdater
component of some bundle as configAdminService
.
{
"components" [
{
"name": "MyComponentUpdater",
"references": [
{
"name": "configAdminService",
"providing": "ct.framework.api.ConfigurationAdmin"
}
]
}
]
}
Step 2: Get the component configuration
Inside the MyComponentUpdater.js
, in some method, we access the injected configAdminService
instance to look up the configuration of the Console
component provided by the console
bundle.
//...
const configAdminService = this.configAdminService;
const config = configAdminService.getConfiguration("console-Console", "console"); (1)
const configProperties = config.get("properties"); (2)
//...
1 | "console-Console" identifies the "Console" component of the "console" bundle. Identifiers have to obey the component identifier format. Additionally we need to pass the bundle name, "console" in this case. |
2 | This stores the component properties as they are defined in the app.json in a local variable. |
Step 3: Update the component configuration
The configuration properties for the component can now be inspected, modified, added, or deleted as a plain JavaScript object like this:
//...
configProperties["_shortCutKey"] = "44"; // Set "PrtScr" key as shortcut key
config.update(configProperties);
//...
update(configProperties)
eventually applies the new component configuration.
This will not save the configuration to the app.json
file, yet.
But the component properties get updated directly.
Changing a factory component configuration
The next examples demonstrate a special method to use in conjunction with factory components.
let configAdminService = this.configAdminService;
// sample configuration properties
let configProperties = { "property" : "test" };
// read configuration entry
let config = configAdminService.createFactoryConfiguration("agssearch-AGSStore-Factory", "agssearch");
config.update(configProperties);
configAdminService.save();
If you know an exact identifier of a factory component instance, like agssearch-AGSStore-0
, you can use the same code as shown in Changing a component configuration.
Otherwise you can use the method demonstrated in the following example, iterating over the returned component configurations:
let configAdminService = this.configAdminService;
// read all factory configuration instance entries
let configs = configAdminService.getFactoryConfigurations("agssearch-AGSStore-Factory", "agssearch");
for (var i=0;i<configs.length;++i){
var config = configs[i];
...
// for example delete all factory component instances
config.remove();
}
configAdminService.save();
Rewrite an app configuration on startup
map.apps allows bundle developers to provide a service that can rewrite the app configuration before it is interpreted by the runtime using configuration interceptors. As an example, a configuration interceptor can be used to migrate a deprecated bundle configuration to the current configuration approach.
To provide a configuration interceptor, a component has to implement the interface system.config.ConfigurationInterceptor
like this:
manifest.json
file{
"startLevel" : 1, (1)
"components": [
{
"name" : "MyConfigInterceptor",
"provides" : ["system.config.ConfigurationInterceptor"]
},
... // all other components, some of which require migration
]
}
You need to make sure, this bundle is started early enough to intercept the configuration of the targeted component.
If the target component is defined within the same bundle, just define the configuration interceptor before all other component, so it is loaded earlier than these.
If the target component is defined in another bundle, make sure the startLevel
of the bundle containing the interceptor is lower than the targeted one’s.
A configuration interceptor must implement the method "intercept":
intercept(configStore){
// read old config
let oldConfig = configStore.getConfig("oldbundle-ComponentName");
// delete old config
configStore.removeConfig("oldbundle-ComponentName");
// check if a configuration exists
if (configStore.getConfig("newbundle-ComponentName")){
// do nothing if configuration is present
return;
}
// write new configuration
configStore.setConfig("newbundle-ComponentName",{
bundleIdentifier: "newbundle",
properties: { ... }
});
}
Making non-persistent changes
In addition to directly changing configuration using the ways explained earlier, there is a way to change configuration properties before they are passed to a managed service and without persisting the changes.
This way is called configuration plugin.
A configuration plugin is a service registered by the interface ct.framework.api.ConfigurationPlugin
.
It has to implement the method modifyConfiguration()
.
modifyConfiguration(targetInfo, configProperties){
// targetInfo can be used to get information about the target component, which properties are given in "configProperties".
let managedServiceProperties = targetInfo.managedServiceProperties;
// the persistent identifier
let targetPid = targetInfo.pid;
// the factory persistent identifier
let targetPid = targetInfo.factoryPid;
// to change the or extent the configuration, simple change the configProperties
configProperties.changedProperty = "test";
// optional, allowed to return fully different properties
return configProperties;
}
A component configuration of a configuration plugin might look like this:
manifest.json
file{
"name" : "MyConfigPlugin",
"provides" : ["ct.framework.api.ConfigurationPlugin"],
"properties" : {
"CM-Target-Pids" : ["themes-ThemeModel"], (1)
"CM-Ranking" : 0 (2)
}]
}
1 | Array of component identifiers, for which this plugin should be called. If the property is not specified or an empty array, the plugin is called for each target service. |
2 | If more than one plugin is configured, you can control the order of their execution by this ranking. Lower rankings are called before higher rankings. A plugin with higher ranking can overwrite the changes of plugin with a lower ranking. Default value is 0 . |
The configuration plugin mechanism is the recommended way to implement Parametrizables , because you can change the configuration of a component based on query parameters before the component is started.
Components Identifiers Format
When using the Configuration Admin Service, components are identified by different kind of identifies, depending on the type of the component. Throughout this guide, we will use the term factoryPid for components that are factories, and pid for all other components, including those created by factories.
Component type | Format | Example |
---|---|---|
Component |
|
|
Factory component |
|
|
Factory component instances |
|
|