Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

...

...

The purpose of the plugin is to allow to set up a specific configuration for a Wedia portal application, by overriding a basic configuration provided by the product.

The base of the configuration is a JSON file. Any element of this JSON can be overriden in a folder, through a tree of files and folders that correspond to the tree of the base JSON. Multiple folders can be added to redefine the configuration redefined by the previous folders.

Mappings

A configuration is thus defined by a series of files that will redefine the basic configuration successively, in order to obtain a final configuration, the fusion of the whole.

This series of folder constitutes a path designated by a unique identifier. A client application will retrieve its final configuration via this ID. Mappings define the correspondence between IDs and paths. The configuration of mappings is done in the plugin settings.

Adaptation to the context

The final configuration depends on an invocation context that constitutes different variables, mainly from the surfer, in order to have different configurations depending on the connected user.

There are therefore folders in the path associated with a mapping whose name depends on the invocation context that allow specific configurations.

Post processors

A postprocessor is a component that determines a folder name according to elements of the invocation context.

...

  • a list of possible values, at least one, but preferably several (for a single value, you might as well use a static file).

  • a function to convert the invocation context into a value that must be one of the possible values

Plugin settings

Mappings

Mappings associate configuration identifiers with paths defining a configuration.

...

  • _portal

  • _portal@config

  • _portal@config@picker

The path _portal@$config@picker designates a configuration defined in 3 folders whose relative paths are successively :

...

Code Block
languagejson
{
    "mappings": {
        "portal": "_portal@club-wed@:surferRole@:userAgent",
        "picker": "_portal@club-wed@:picker"
    }
}

Postprocessors

To configure the post processors, use the config_settings plugin parameter. This is a JSON object in the postProcessors property that designs the post processors as an object, whose keys are the post processors names (variable names) and whose associated values define the corresponding post processors.

...

  • a class name from a set of standard post processors provided in the plugin

  • configuration set-up parameters (depending of the component)

Base locations

By default, folders and configuration files are stored in arbitrary folders in the :

...

  • safeProcessCaptureTimeout, time, optional (default 10000 ms)

Miscellaneous

default_holding_plugin, string, optional

...

this means that you can create a new configuration universe by duplicating the plugin and using another uri, for example, /api/whatyouwant/*

Time set-up

The values of type time can be

  • either a number (the unit depends on the parameter)

  • or a number (integer or decimal) followed by a unit, among

    • s (seconds)

    • ms (milliseconds)

    • m (minutes)

    • h (hours)

Contributing to configuration in your plugins

At startup, the config resolver plugin scans all activated tagged plugins to discover contributions to its configuration (this feature can be disabled by the discover_external_config_settings plugin parameter). The discover_external_config_settings_exclusions parameter allows to exclude plugins from this scan.

Discovery of bases (base locations)

For each plugin, the config folder of the plugin is first scanned to determine if there are any bases in its config-resolver/bases subfolder. Then, we scan the resource folder of the plugin (res).

If a base already exists, it is ignored (note that you cannot control the order of the plugins).

Adding mappings

For each plugin, if a config-resolver-mappings.json file is present in its configuration folder (config), it is loaded to add one or more mappings. The format is the same as the mappings section of the config_settings plugin parameter.

...

If a mapping already exists, it is ignored (note that you cannot control the order of the plugins).

Adding post processors

For each plugin, if a config-resolver-postprocessors.json file is present in its configuration folder (config), it is loaded to add one or more post processors. The format is the same as the postProcessorssection of the config_settings plugin parameter, but you can additionally indicate that postprocessor is defined in the plugin itself using object syntax without indicating a plugin property (if you indicate a null plugin property, the default contribution plugin will be used).

If a post processor already exists, it is ignored (note that you cannot control the order of the plugins).

Un exemple complet de contribution de mappings et postprocessors

...

Where to put the groovy script

In config-resolver-postprocessors.json, when the definition is a groovy file path, it is a relative path to the root of the plugin.

By example, if your script file myprocessor.groovy is in the folder res/postprocessors, the json will be like this:

Code Block
languagejson
{
    "mappingsmyprocessor": {
        "portalprocessor": "_portal@club-wed@:surferRole@:userAgentres/postprocessors/myprocessor.groovy",
        "picker": "_portal@club-wed@:picker"
    },
}
}

You can also put a slash at beginning like this:

Code Block
languagejson
{
   "postProcessorsmyprocessor": {
        "surferRoleprocessor": "fr.wedia.confres.core.model.processor.SurferRolePostProcessor/res/postprocessors/myprocessor.groovy",
      }
}

It’s also possible to put the script in a plugin jar, by using a JAR URI :

Code Block
{
   "userAgentmyprocessor": {

           "processor": "fr.wedia.confres.core.model.processor.UserAgentPostProcessorjar:file:/thejarfile.jar!/fr/wedia/mypackage/postprocessors/myprocessorinjar.groovy",
   }
}

The file thejarfile.jar must be in plugin lib directory. If you want to put this jar in another folder, prefix you jar path by the relative (to the plugin root) path:

Code Block
{
       "configmyprocessor": [
                {
       
            "patternprocessor": ".*Mobi.*jar:file:/other/path/thejarfile.jar!/fr/wedia/mypackage/postprocessors/myprocessorinjar.groovy",
       }
}

Deal with the host plugin

It is possible to exchange with the contribution plugin. However, it is not certain that it is started when the postprocessor is running, whatever the method, and even less so when the postprocessor is loaded, initialized and started. The plugin can even be stopped and restarted at any time.
This means:

  • always make sure the plugin is started when calling one of its classes, and provide a fallback (for getValue() and getAvailableValues()).

  • to retrieve parameters, you have to proceed in lazy, try to systematically read the parameters as long as it failed, then store them (cache) to use them.

  • before invoking a method of a plugin class, check the state of the plugin
    to have the plugin instance available, implement the injection method (setPlugin(IPlugin) or setPlugin(String))

  • any modification of the plugin requires a restart of WXM_CONFIG_RESOLVER after.

Here is an example of possible interactions with plugin (get parameters and invoke method):

Code Block
languagegroovy
package fr.wedia.confref.postprocessor.examples;

import java.util.stream.*;
import java.util.Collection;

import com.google.gson.*;
import com.noheto.plugins.IPlugin;

import fr.wedia.confres.core.model.processor.*;
import fr.wedia.confres.utils.Utils;
import fr.wedia.confres.utils.annot.PostProcessorConfigExclude; 

import fr.wedia.confres.core.model.processor.EngineToolkit.ParameterType; // need to import explicitly the enum (in groovy)

/**
 * A basic example of postprocessor implementation in groovy.
 *
 * <p>This is an example of a postprocessor with interaction with the plugin that declares the postprocessor.
 * The value is a configurable fixed value.</p>
 * <p>There are two types of interactions </p>
 * <ul>
 * <li>We read some configuration properties of the plugin (they are not used, it is just for the example)</li>
 * <li>We invoke a method of the plugin at each call (it doesn't matter what it does, it's just for the example)</li>
 * </ul>
 * 
 * <p></p>
 *
 * @author joel.drigo@wedia-group.com
 *
 */
class CustomProcessorExampleOfInteractionWithThePlugin extends AbstractPostProcessor {
	
	/**
	 * This value is always returned. It is configurable because there isn't any {@link @PostProcessorConfig} annotation.
	 */
	private String value = "fixed";
	
	/**
	 * This is the list of possible returned values. It is initialized at startup, since the returned value is configurable (variable {@code value})
	 * <p>This variable is excluded explicitly from configuration, because there isn't any {@link @PostProcessorConfig} annotation</p>
	 */
	@PostProcessorConfigExclude
	private Set<String> values;

	/**
	 * Stores the plugin that declares the postprocessor (initialized at loading)
	 */
	@PostProcessorConfigExclude
	private IPlugin plugin;
	
	/**
	 * True when the plugin parameters has been effectly loaded
	 */
	@PostProcessorConfigExclude
	private boolean configured;

	/**
	 * This is a string parameter from plugin parameters
	 */
	@PostProcessorConfigExclude
	private String paramExample1;

	/**
	 * This is a JsonObject parameter from plugin parameters
	 */
	@PostProcessorConfigExclude
	private JsonObject paramExample2;

	/**
	 * This method is used when loading the postprocessor to inject the plugin that declares this postprocessor.
	 *
	 * @param plugin the name of the plugin that declares this postprocessor.
	 */
	public void setPlugin(IPlugin plugin) {
		this.plugin = plugin;
		loadConfig(); // lazy loading of plugin parameters
	}
	
	/**
	 * Start the processor.
	 */
	public void start() {
		loadConfig(); // lazy loading of plugin parameters
		value = Utils.withDefault(value, ""); // if value is null or blank, replace with empty string
		values = Utils.unmodifiableSet(value);
	}

	/**
	 * This method reads the configuration from plugin (whose declare this postprocessor).
	 */
	private void loadConfig() {
		if ( !configured ) { // if configuration has not been yet loaded
			if ( plugin==null ) {
				PostProcessorLogger.warn("Plugin has not be set correctly");
				configured = true; // we force this state to avoid trying to load again the configuration
			}
			else if ( plugin.isStarted() ) {
				if ( PostProcessorLogger.debug() ) PostProcessorLogger.debug(CustomProcessorExampleOfInteractionWithThePlugin.class,"Load parameters from plugin " + plugin.getName() );
				try {
					// we read the desired plugin parameters
					Map<String,Object> parameters = new EngineToolkit.PluginParameters()
					 .registerParameter("param_example_1", ParameterType.STRING, "default") // reads parameter_example_1 of type string
					 .registerParameter("param_example_2", ParameterType.JSON, ()-> new JsonObject()) // reads parameter_example_2 of type json
					 .collect(plugin);
					 configured = true;
					 paramExample1 = (String)parameters.get("param_example_1");
					 JsonElement json = (JsonElement)parameters.get("param_example_2");
					 if ( json instanceof JsonObject ) {
						 paramExample2 = (JsonObject)json; 
					 }
					 else {
						 paramExample2 = new JsonObject();
					 }
					if ( PostProcessorLogger.debug() ) {
						PostProcessorLogger.debug(CustomProcessorExampleOfInteractionWithThePlugin.class,"Parameter param_example_1 = " + paramExample1 );
						PostProcessorLogger.debug(CustomProcessorExampleOfInteractionWithThePlugin.class,"Parameter param_example_2 = " + paramExample2 );
					}
				}
				catch(Throwable t) {
					// show stack trace only in debug level
					if ( PostProcessorLogger.debug() ) PostProcessorLogger.debug(CustomProcessorExampleOfInteractionWithThePlugin.class,"Error while loading parameters from plugin " + plugin.getName() +".", t );
				    else if ( PostProcessorLogger.warn() ) PostProcessorLogger.warn(CustomProcessorExampleOfInteractionWithThePlugin.class,"Error while loading parameters from plugin " + plugin.getName() +". Set log to debug to see stacktrace." );
				}
			}
		}
	}

	/**
	 * This method invoke a method of a class of the plugin (whose declare this postprocessor).
	 */
	private void invokePlugin(ProcessorContext context) { 
		if ( PostProcessorLogger.trace() ) {
			PostProcessorLogger.trace("Invoking " + plugin.getName() + " / fr.wedia.confref.postprocessor.examples.Example.helloPlugin()");
		}

		String pluginMessage = EngineToolkit.invokePlugin(plugin, t->{
			if ( t!=null ) { // if t is null plugin is not started
			    // show stack trace only in debug level
				if ( PostProcessorLogger.debug() ) PostProcessorLogger.debug(CustomProcessorExampleOfInteractionWithThePlugin.class,"Error while invoking plugin " + plugin.getName() +".", t );
				else if ( PostProcessorLogger.warn() ) PostProcessorLogger.warn(CustomProcessorExampleOfInteractionWithThePlugin.class,"Error while invoking plugin " + plugin.getName() +". Set log to debug to see stacktrace." );
			}
		}, "fr.wedia.confref.postprocessor.examples.Example","helloPlugin", context.getProcessorName() );
		if ( pluginMessage!=null ) {
			if ( PostProcessorLogger.info() ) {
				PostProcessorLogger.info("The plugin " + plugin.getName() + " says: \"" + pluginMessage + "\"");
			}
		}
		else {
			if ( PostProcessorLogger.debug() ) {
				PostProcessorLogger.debug("Invoking " + plugin.getName() + "... seems to be not started.");
			}
		}
	}

	/**
	 * {@inheritDoc}
	 * Returns the postprocessor value corresponding (always the value {@code value}.
	 */
	@Override
	String getValue(ProcessorContext context) {
		loadConfig(); // lazy loading of plugin parameters
		if ( PostProcessorLogger.trace() ) {
			PostProcessorLogger.trace("Invoking " + plugin.getName() + "...");
		}
		invokePlugin(context); // invoke plugin
		return value;
	}
	
	/**
	 * Returns all possible values.
	 *
	 * @return all possible values.
	 */
	@Override
	public Collection<String> getAvailableValues() {
		loadConfig(); // lazy loading of plugin parameters
		return values;
	}
	
	/**
	 * Returns a hash code value for this postprocessor.
	 */
	public int hashCode() {
		return Objects.hash(value);
	}
		
	/**
	 * Returns true if the specified object is equals to this processor, i.e. the classes of both are equals and indexes are equals.
	 *
	 * return true if the specified object is equalis to this processor.
	 */
	@Override
	public boolean equals(Object obj) {
	   return fr.wedia.confres.utils.Utils.equals(CustomProcessorExampleOfInteractionWithThePlugin.class, this, obj, (o1,o2)-> Objects.equals(o1.value,o2.value));
	}
	
}

A complete example of the contribution of mappings and postprocessors

The configuration of the WXM_CONFIG_RESOLVER plugin is

Code Block
languagejson
{
    "mappings": {
        "portal": "_portal@club-wed@:surferRole@:userAgent",
        "picker": "_portal@club-wed@:picker"
    },
    "postProcessors": {
        "surferRole": "fr.wedia.confres.core.model.processor.SurferRolePostProcessor",
        "userAgent": {
            "processor": "fr.wedia.confres.core.model.processor.UserAgentPostProcessor",
            "config": [
                {
                    "pattern": ".*Mobi.*",
                    "name": "mobile"
                }
            ]
        }
    }
}

Le The service GET /api/portalconfig/mappings (liste des list of mappings) retourne returns the following two mappings:

Code Block
languagejson
[
	"portal",
	"picker"
]

Le The service GET api/portalconfig/postprocessors/list?options=%7B%0A%09withMappings%3A%20true,%0A%09withValues%3A%20true%0A%7D (liste des list of postprocessors, avec leurs mappings associés et valeurs) retourne with their associated mappings and values) returns the following two postprocessors :

Code Block
languagejson
[
	{
		"name": "surferrole",
		"mappings": [
			"portal"
		],
		"values": [
			"",
			"role-1",
			"role-4",
			"role-27",
			"role-31",
			"role-32",
			"role-33"
		]
	},
	{
		"name": "useragent",
		"mappings": [
			"portal"
		],
		"values": [
			"",
			"mobile"
		]
	}
]

Je créé un plugin de contribution I create a contribution plugin WXM_CONFIG_RESOLVER_GPP tel que (ici j’ai mis les sources des as (here I put two of the sources of the postprocessors (groovy) dans un dossier in a res , mais ils peuvent être mis dans un autre dossier).folder, but they can be put in another folder). The third processor is in a JAR.

📁 WXM_CONFIG_RESOLVER_GPP

...

📄 config-resolver-postprocessors.json

📁 lig

📄 gpp.jar

📁 res

📁 gpp

📄 CustomProcessor.groovy

📄 CustomProcessorExample.groovy

📄 CustomProcessorExampleOfInteractionWithThePlugin.groovy

📄 CustomProcessorWithConfig.groovy

(le plugin d’exemple contient d’autres processors groovy non utilisés dans cet exemple)the plugin may contain other groovy processors not used in this example).

Here is the zip of the plugin (including source of class fr.wedia.confref.postprocessor.examples.Example.

View file
nameWXM_CONFIG_RESOLVER_GPP.zip

Le fichier The config-resolver-mappings.json définit des file defines additional mappings supplémentaires :

Code Block
languagejson
{
        "mapping_GPP_1": "_empty",
        "mapping_GPP_2": "_empty@:gppProcessor",
        "mapping_GPP_3": "_empty@:gppProcessor@:gppProcessorWithConfig",
        "mapping_GPP_4": "_empty@:gppProcessor@:gppProcessorPlugin"
}

Le fichier The config-resolver-postprocessors.json définit des file declares additional postprocessors supplémentaires :

Code Block
languagejson
{
	"gppProcessor": {
		"processor": "/res/gppjar:file:/gpp.jar!/fr/wedia/confref/postprocessor/examples/CustomProcessor.groovy"
	},
	"gppProcessorWithConfig": {
		"processor": "/res/gpp/CustomProcessorWithConfig.groovy",
		"config": {
					"values": ["x","y" ]
				 }
	},
	"gppProcessorPlugin": {
		"processor": "/res/gpp/CustomProcessorExampleOfInteractionWithThePlugin.groovy"
	}
}

On utilise ici la syntaxe “objet” sans propriété “plugin”, ce qui détermine que le fichier groovy sera cherché dans ce This defines the following postprocessors:

  • gppProcessor source script is located in jar gpp.jar (located in the directory /lib of the plugin). The source is also in folder /res/gpp if you want to look at the source. It is used by mappings mapping_GPP_2, mapping_GPP_3 and mapping_GPP_4.

  • gppProcessorWithConfig source script is located in folder /res/gpp. It is used by mapping mapping_GPP_3.

  • gppProcessorPlugin source script is located in folder /res/gpp. It is used by mapping mapping_GPP_4.

We use here the "object" syntax without "plugin" property, which determines that the groovy file will be searched in this plugin (WXM_CONFIG_RESOLVER_GPP), avec un path relatif à sa racine.On installe et active ce plugin et on redémarre with a relative path to its root.

We install and activate this plugin and restart WXM_CONFIG_RESOLVER.

Le service GET /api/portalconfig/mappings (liste des mappings) retourne :

Code Block
languagejson
[
	"portal",
	"picker",
	"mapping_GPP_1",
	"mapping_GPP_2",
	"mapping_GPP_3",
	"mapping_GPP_4"
]

Le service GET api/portalconfig/postprocessors/list?options=%7B%0A%09withMappings%3A%20true,%0A%09withValues%3A%20true%0A%7D (liste des postprocessors, avec leurs mappings associés et valeurs) retourne :

Code Block
languagejson
[
	{
		"name": "surferrole",
		"mappings": [
			"portal"
		],
		"values": [
			"",
			"role-1",
			"role-4",
			"role-27",
			"role-31",
			"role-32",
			"role-33"
		]
	},
	{
		"name": "useragent",
		"mappings": [
			"portal"
		],
		"values": [
			"",
			"mobile"
		]
	},
	{
		"name": "gppprocessorplugin",
		"mappings": [
			"mapping_GPP_4"
		],
		"values": [
			"fixed"
		]
	},
	{
		"name": "gppprocessor",
		"mappings": [
			"mapping_GPP_2",
			"mapping_GPP_3",
			"mapping_GPP_4"
		],
		"values": [
			"V1",
			"V2",
			"V3",
			"V4",
			"V5"
		]
	},
	{
		"name": "gppprocessorwithconfig",
		"mappings": [
			"mapping_GPP_3"
		],
		"values": [
			"x",
			"y"
		]
	}
]

Standard postprocessors

Surfer role

Class name: fr.wedia.confres.core.model.processor.SurferRolePostProcessor

This post processor allows to have a specific configuration for each role. The folder name for a role is formed by concatenating a prefix and the role identifier.

For example: role-1.

Set-up

The prefix can be configured using the property prefix.

...

The value for not connected mode is ““. It could be changed with the property notConnectedValue.

Abstraction

You can implement your own surfer-dependent post processor by implementing the fr.wedia.confres.core.model.processor.AbstractSurferPostProcessor abstraction and its String getValue(CTSurfer) method.

User agent

Class name: fr.wedia.confres.core.model.processor.UserAgentPostProcessor

...

If several patterns are indicated, they are tested in the order of the array. The first one that matches is used.

Set-up

By default, only one User-Agent header is tested, the first one found. You can test all the values of this header using the anyMatch configuration property by setting it to true.

Code Block
languagejson
"userAgent": {
           "processor": "fr.wedia.confres.core.model.processor.UserAgentPostProcessor",
           "anyMatch": true,
           "config": [{
                       "pattern": ".*Mobi.*",
                       "name": "mobile"
           }]
}

Abstraction

You can implement your own header-dependent post processor by implementing the fr.wedia.confres.core.model.processor.AbstractHeaderPostProcessor abstraction and its String getValue(String,Collection<fr.wedia.confres.api.util.Headers>) method. See others methods getValueFoNoHeaders and getValueForNull to return a value respectively for no headers or null headers. See the javadoc for more information getValueFoNoHeaders and getValueForNull to return a value respectively for no headers or null headers. See the javadoc for more information

Parameter processor

Abstraction

fr.wedia.confres.core.model.processor.AbstractParameterPostProcessor is a base for processors whose value depends on parameters.

Customizings postprocessors

You can create your own postprocessors. Just define the implementation class in a groovy extension file. The class must extend one of the abstractions provided as standard, or possibly a standard postprocessor.

...

Code Block
languagejson
"surferRole": {
            "processor": "customprocessors/mysurferrolepostprocessors.groovy",
            "prefix": "altrole-"
}

Location

The location of the file can be:

  • the SAN

  • a plugin
    To specify a plugin, add the plugin property.

    Code Block
    languagejson
    "surferRole": {
                "processor": "customprocessors/mysurferrolepostprocessors.groovy",
                "plugin": name of the plugin
    }
    • If the value of property plugin is a string, it’s the name of the plugin

    • In all other cases,, the plugin is a default plugin, configured in the default_contribution_plugin parameter of the wxm_config_resolver plugin

Implementation

To implement a postprocessor in groovy, extend the class fr.wedia.confres.core.model.processor.AbstractPostProcessor. You can also base your class on one of the pre-implemented postprocessor classes (see Standard postprocessors).

...

  1. The getAvailableValues() method must return all possible values returned by the other method.

  2. The method getValue(ProcessorContext) will return the desired value in the invocation context. It is important to consider the context rather than the query, because in some cases, for the purposes of the configuration editing tool, and for testing purposes, the context information may override query values. It is the ProcessorContext class that allows access to the invocation context:

    1. ProcessorContext.getSurfer(): returns the surfer for which we want to execute the postprocessor

    2. ProcessorContext.getSurferProperty(String name): returns the value of a property of the surfer (or null if it does not exist).

    3. ProcessorContext.getHeaders(String name): returns the values of the specified header

    4. ProcessorContext.getParameters(): returns a wrapper to retrieve query parameters

    5. ProcessorContext.getMappingId(): returns the ID of the mapping we are trying to solve

    6. ProcessorContext.getPathInfo(): allows to have a contextual description of invocation to log in particular

    7. As a last resort, you can retrieve the request, with the method ProcessorContext.getRequest()parameters

    8. ProcessorContext.getMappingId(): returns the ID of the mapping we are trying to solve

    9. ProcessorContext.getPathInfo(): allows to have a contextual description of invocation to log in particular

    10. ProcessorContext.getProcessorName(): get the name of the post processor

    11. As a last resort, you can retrieve the request, with the method ProcessorContext.getRequest().

Note

Avoid using the query to retrieve context information (like the surfer for example). Always prefer to use the context when possible. Simulation queries can pass context information (like the surfer, or a header) as parameters, and this information is put in ProcessorContext, the query cannot be modified. Moreover, during unit tests your postprocessor could be called without request with a mocked server. In this case, the request instance will be null. If the context does not give you access to the information you want, you can use the request, but in this case your postprocessor cannot be used in simulation for this information nor in testing.

Info

There is no guarantee that any of the returned values are not null: if any of the information provided by the ProcessorContext is used, all cases must be tested (including, for example, testing whether getRequest() is null, as the postprocessor could be invoked as part of a simulation outside of HTTP calls).

...

  • as it is a folder name, there must not be any forbidden characters in the file names (slash (/), backslash(\), column(:), pipe (|), chevrons (< and >), question mark (?), double-quotes (")...)

  • do not use arobase (@), this character being used as a name separator in configuration folder names, to avoid possible conflicts. Avoid also other separators, like dot (.), whitespaces…

  • generally avoid characters other than letters (without accent), numbers, underscore and dash (avoid dash if possible, this character can cause problems within URLs

  • the initial dollar character ($) is always considered as a cutting order

  • values should be considered case insensitive, but use lower case (as folders can be deployed by nar)

  • the blank values are always ignored (skip the processor value in the final path). For example, if a path is defined by x@:processor@y, and the corresponding postprocessor returns a blank string, the path will be x@y.

Since 2023.2

A processor can return a value

  • composed of several paths (thus containing @, for example x@y)

  • referring to another processor (for example the returned value is x@:otherproc@y
    beware however of an infinite recursion, the path will be x@y.there is no test. In general, limit the depth of recursion to limit computing time.

  • in addition we can escape a character with the backslash

Set-up

You can configure a custom postprocessor via its declaration in the plugin configuration.

Basic set-up

By default, the properties indicated in the postprocessor configuration, except the reserved properties processor, plugin, config and configDeserializer are all injected in the class variables by compliant setter.

...

The two variables connectedValue and notConnectedValue will have the values "on" and "off" respectively.

Control of configuration variables

You can designate which variables will be configurable by using the @PostProcessorConfig annotation. In this case, only the variables that have this annotation will be configurable.

...

It’s not possible to mix the setter and the annotation ways to configure the postprocessor.

Advanced set-up

You can also deport the configuration to a separate object. If you configure your postprocessor by specifying properties in a config section, the properties will also be injected into the class variables.

...

  1. the type of argument can be com.google.gson.JsonElement.

  2. the type of argument can be fr.wedia.confres.core.model.processor.PostProcessorConfig. It is an encapsulation of com.google.gson.JsonElement that allows to retrieve information more easily.

  3. at any inner class with a no-argument constructor. It will be instancied the properties will be injected as described above
    Example:

    Code Block
    languagegroovy
    package fr.wedia.confref.custom.processor;
    
    import fr.wedia.confres.core.model.processor.*; 
    
    import com.google.gson.*;
    
    import fr.wedia.confres.plugin.PluginLogger;
    
    class CustomProcessorWithConfig extends AbstractPostProcessor {
    
         private static final List<String> VALUES = Arrays.asList("V1","V2","V3","V4","V5");
    
         /**
           * this is a debug postprocessor which use a non contextual property (index attribute value)
           */
         @fr.wedia.confres.utils.annot.PostProcessorConfig()
         private int index=0;
     
         private Config config;
    
         private List<String> values = VALUES;
         
         public void start() {
         	  PluginLogger.info(CustomProcessorWithConfig.class,"starting component with config " + config);
         	  if ( config!=null ) {
    	     	  if ( config.getValues()!=null ) {
    	     	       PluginLogger.info(CustomProcessorWithConfig.class,"starting component with config " + config.getValues());
    	     	       values = config.getValues();
    	     	  }
    	     }
         }
    
         @Override
         String getValue(ProcessorContext context) {
             return values.get(index);
         }
         
         @Override
         Collection<String> getAvailableValues() {
             return values;
         }
         
         @Override
         String toString() {
             return this.getClass().getName()+" Groovy:CustomProcessor#"+index+"#"+values;
         }
         
         @Override
         public boolean equals(Object obj) {
            return fr.wedia.confres.utils.Utils.equals(CustomProcessorWithConfig.class, this, obj, (o1,o2)-> o1.index==o2.index);
         }
         
         public void setConfig(Config config) {
         	this.config=config;
    	 }
    	 
    	 public static class Config {
    	 
    	       private List<String> values;
    
    	       public void setValues(List<String> values) {
    	            this.values=values;
    	       }
    	 
    	       public List<String> getValues() {
    	            return values;
    	       }
    	 
    	 }
    	
    }

    And the configuration:

    Code Block
    languagejson
    "connectionState": {
          "processor": "customprocessors/mypostprocessor.groovy",
          "plugin": name of the plugin,
          "index": 2,
          "config": {
              "values": ["a","b","c"]
          }
    }

Startup

You can execute code right after loading the postprocessor by adding the start() method.

...

Code Block
languagegroovy
package fr.wedia.confref.custom.processor;

import fr.wedia.confres.core.model.processor.*; 

import com.google.gson.*;

import fr.wedia.confres.plugin.PluginLogger;

class CustomProcessorJsonElement extends AbstractPostProcessor {

    private static final List<String> VALUES = Arrays.asList("V1","V2","V3","V4","V5");  

    private JsonElement config;
    private List<String> values = VALUES;   
     
    public void start() {
          PluginLogger.info(CustomProcessorJsonElement.class,"starting component with config " + config);
          if ( config!=null && config.isJsonArray() ) {
     	       JsonArray jsonArray = config.getAsJsonArray();
     	       List<String> list = new ArrayList<>();
     	       for(int i=0; i<jsonArray.size(); i++) {
     	           list.add(jsonArray.get(i).getAsString());
     	       }
     	       values = list;
     	  }
     }

     @Override
     String getValue(ProcessorContext context) {
         return ...
     }
     
     @Override
     Collection<String> getAvailableValues() {
         return values;
     }
     
     public void setConfig(JsonElement config) {
     	this.config=config;
	 }
	 
}

Plugin

If the postprocessor is contributed by a plugin, you can retrieve this plugin by adding a method setPlugin with an argument of type String or com.noheto.plugins.IPlugin. However, be aware that postprocessors are loaded at the start of the .plugins.IPlugin. However, be aware that postprocessors are loaded at the start of the wxm_config_resolver plugin and the contributing plugins are not necessarily started at that time.

Class fr.wedia.confres.core.model.processor.PostProcessorConfig

This class makes it easier to recover a JSON postprocessor configuration.

  1. the method isList() tests if the configuration is a list (a JSON array): if true, you can get it with asList() method, as a fr.wedia.confres.core.model.processor.PostProcessorConfigList.

  2. the method isMap() tests if the configuration is a map (a JSON object): if true, you can get it with asMap() method, as a fr.wedia.confres.core.model.processor.PostProcessorConfigMap.

See the JavaDoc of these classes for more information.

Operation

Postprocessors are loaded when the wxm_config_resolver plugin and the contributing plugins are not necessarily started is started. If startup is not possible at that time .

Class fr.wedia.confres.core.model.processor.PostProcessorConfig

This class makes it easier to recover a JSON postprocessor configuration.

...

(error during instantiation, file not found, etc.), the component will not be available until the plugin is restarted.

In any case a postprocessor loaded from a plugin will be invoked (whether the plugin is activated, started or not). Be careful, however, if you invoke plugin classes in the resolution (with a plugin.invoke() ): remember that a plugin cannot be invoked when it is not fully started (and therefore necessarily activated).

In case of an error during the resolution, the postprocessor is ignored and therefore its value is removed from the resolved path.

Logging

Logging can be done using

  • The fr.wedia.confres.core.model.processor

...

  • .PostProcessorLogger class for a log specific to the execution of custom postprocessors

  • The fr.wedia.confres.plugin.PluginLogger class to log in the WXM_CONFIG_RESOLVER main log

Example:

Code Block
package fr.wedia.confref.custom.processor;

import fr.wedia.confres.core.model.processor.

...

See the JavaDoc of these classes for more information.

Operation

Postprocessors are loaded when the wxm_config_resolver plugin is started. If startup is not possible at that time (error during instantiation, file not found, etc.), the component will not be available until the plugin is restarted.

In any case a postprocessor loaded from a plugin will be invoked (whether the plugin is activated, started or not). Be careful, however, if you invoke plugin classes in the resolution (with a plugin.invoke() ): remember that a plugin cannot be invoked when it is not fully started (and therefore necessarily activated).

...

PostProcessorLogger;

class CustomProcessorLoggingExample extends AbstractPostProcessor {

    private JsonElement config;
     
    public void start() {
          if ( PostProcessorLogger.info() ) {
              PostProcessorLogger.info(CustomProcessorLoggingExample.class,"starting component with config " + config);          
          }
          ...
     }

     @Override
     String getValue(ProcessorContext context) {
         if ( PostProcessorLogger.trace() ) {
             PostProcessorLogger.trace(context, CustomProcessorLoggingExample.class,"getting value from post processor for surfer " + context.getSurfer());
         }
         return ...
     }
     
}

Services

general information

JSON request parameters

JSON parameters of options (so except data files, but also verbosity, specific parameters like processorValues for example, etc) can use a lenient syntax.

Errors

The error system is basic and only based on the standard HTTP code. It is possible to get an explicit message in debug mode.

Instance reference parameter

When referring to an instance of an object, in particular a user (to set up a surfer for example), we use a parameter of type "instance reference". The valke:

  • either a uuid

  • either a uid (object type followed by the object instance id, separated by an underscore, a slash or a dash)

  • or an id (the type of the object will automatically be the main type by default, for example user, for a surfer)

  • or, for a surfer/user, the value “notconnected” (case insensitive) to set a not connected surfer

Specific parameters syntax

pathMatcher

The pathMatchers allow to define file filters. The syntax used is based on the Java interface java.nio.file.PathMatcher:

...

Beware, the handling of the glob syntax may be system dependent, especially regarding case.

get configuration

Gets a final configuration for a mapping ID.

...

The difference between the two endpoints is that the first one takes no parameters other than the mapping id (and debug). It is intended to retrieve the desired configuration for the requesting surfer (or no surfer). The second one is more for the configuration UX to get different views of the configuration depending on the parameters. The first one is not secured while the second one is.

parameters

  • mappingID: string (mandatory), the mapping for which we want the configuration

  • wip: boolean (optional, false by default), activate the “wip” mode (if false, wip/inherit files are ignored, if true, wip/inherit files are taken into account

  • pathFilter, string/nodePath (optional, none by default), a pathfilter to select nodes (See Filtering)

  • pathFilterMode, string/enum (optional, FILTER by default), the filter mode (See Filtering)

  • nodesWithPath, boolean (optional, false by default), (See Filtering)

  • surfer, instance reference (optional, none by default), a substitute user/surfer to get the configuration that this surfer would have if he called the service. (See Instance reference parameter)

  • processorValues, JSON/map (optional, none by default), a processor/value association map to obtain the corresponding configuration
    For example,

    Code Block
    languagejson
    {
       "surferRole": "role-31"
    }
  • options, JSON (optional)

    • xjson: boolean (options, false by default), true to get the configuration as an xjson

explain configuration

Shows a detailed view of the components of a configuration and how they were obtained.

GET /api/portalconfig/config/explain (operation ID: configExplain)

parameters

  • mappingID: string (mandatory), the mapping for which we want the configuration

  • wip: boolean (optional, false by default), activate the “wip” mode (if false, wip/inherit files are ignored, if true, wip/inherit files are taken into account

  • pathFilter, string/nodePath (optional, none by default), a pathfilter to select nodes (See Filtering)

  • pathFilterMode, string/enum (optional, FILTER by default), the filter mode (See Filtering)

  • nodesWithPath, boolean (optional, false by default), (See Filtering)

  • surfer, instance reference (optional, none by default), a substitute user/surfer to get the configuration that this surfer would have if he called the service. (See Instance reference parameter)

  • processorValues, json/map (optional, none by default), a processor/value association map to obtain the corresponding configuration

  • options JSON (optional)

    • xjson: boolean (options, false by default), true to get the configuration as an xjson

  • verbosity JSON, int or string/enum (optional), determines what information will or will not be exposed
    We can configure the verbosity

    • either by a numerical verbosity level (from 0 to 15)

    • or by one of the following words:

      • max

      • min

      • default

    • or by a json which lists the different properties and their values (none is mandatory, all have a default value)

    • or by a combination of both: in the json, the verbosity property is the level, the other properties allowing to select values different from those of the level.
      For example:

      Code Block
      languagejson
      {
         verbosity: default,
         includesVerbosity: true
      }
    • if not specified, the verbosity is

      Code Block
      languagejson
      {
          "includesVerbosity": false,
      	"includesLocation": true,
      	"includesLayer": true,
      	"includesOverrides": true,
      	"locationVerbose": true,
      	"locationRecursive": true,
      	"pathVerbosity": true, 
        	"decomposePaths": false,
          "decomposePathsVerbose": true,
        	"includeNodePath": true,
      	"includeJsonPath": true,
      	"includeXJsonPath": true,
      	"nameVerbosity": true,
      	"includesIgnored": true,
      	"groupsIgnoredInRoot": true,
      	"ignoredLocationVerbose": true,
      	"includesErrors": true,
      	"groupsErrorsInRoot": true,
      	"errorVerbose": true,
      	"exceptionVerbose": true,
      	"exceptionDepth": -1,
      	"stackTraceVerbosity": 2
      }
    • here is the default json for the verbosity (value default or 3)

      Code Block
      languagejson
      { 
        "includesVerbosity": false,
        "includesLocation": true,
        "includesLayer": true,
        "includesOverrides": true,
        "locationVerbose": false,
        "locationRecursive": false,
        "pathVerbosity": true,
        "decomposePaths": false,
        "decomposePathsVerbose": false,
        "includeNodePath": false,
        "includeJsonPath": false,
        "includeXJsonPath": false,
        "nameVerbosity": false,
        "simpleName": true,
        "includesIgnored": true,
        "groupsIgnoredInRoot": false,
        "ignoredLocationVerbose": false,
        "includesErrors": true,
        "groupsErrorsInRoot": false,
        "errorVerbose": false,
        "exceptionVerbose": false,
        "exceptionDepth": 0,
        "stackTraceVerbosity": 0
      }

      The meaning of the properties

      • includesVerbosity, boolean (optional, default is false): If true, include the verbosity configuration in the response

      • includesLocation, boolean (optional, default is true): if true, locations are included in the response

      • includesLayer, boolean (optional, default is true): if true, layer (location root folder) are included in the response

      • includesOverrides, boolean (optional, default is true): if true, overridden elements are included in the response

      • locationVerbose, boolean (optional, default is true): if true, the locations are exposed with a maximum of information

      • locationRecursive, boolean (optional, default is true): if true, the locations are exposed with their parent, recursively

      • pathVerbosity, boolean (optional, default is true): if true, the paths of the locations are exported with a maximum of verbosity

      • decomposePaths, boolean (optional, default is false): if true,the path decomposition of the locations is exported (how these paths were built, in particular which postprocessors were used and their value)

      • decomposePathsVerbose, boolean (optional, default is true): if true, the path decomposition of the locations is in maximum verbosity

      • includeNodePath, boolean (optional, default is true): if true, the nodepaths of each element are exported

      • includeJsonPath, boolean (optional, default is true): if true, the JSONpaths of each element are exported

      • includeXJsonPath: boolean (optional, default is true): if true, the XJSONpaths of each element are exported

      • nameVerbosity, boolean (optional, default is false): if true, the names are exported in maximum verbosity (in particular, we get the details of the type of name: if it is a wip, an inherit, an array, etc)

      • simpleName, boolean (optional, default is true): if true, the name doesn’t include decorations( wip, inherit, array…).

      • includesIgnored, boolean (optional, default is true): if true, files found but ignored are exported, with the reason for ignoring them

      • groupsIgnoredInRoot, boolean (optional, default is true): if true, the ignored files are grouped at the root, if false, they are exported at the level where they were detected

      • ignoredLocationVerbose, boolean (optional, default is true): if true, ignored files are exported with maximum verbosity

      • includesErrors, boolean (optional, default is true): if true, the files or folders whose reading triggered an error are exported

      • groupsErrorsInRoot, boolean (optional, default is true): if true, the files in error are grouped at the root, if false, they are exported at the level where they were detected

      • errorVerbose, boolean (optional, default is true): if true, files with errors are exported with maximum verbosity

      • exceptionVerbose, boolean (optional, default is true): if true, the exceptions encountered are exported with a maximum of verbosity

      • exceptionDepth, int (optional, default is 0): defines the depth of the causes of the exceptions (0 corresponds to no cause)

      • stackTraceVerbosity, int (optional, default is 0): defines the verbosity level of the stack traces

configuration merge and explain tests

It is possible to test the config merge and config explain services without having any configuration, mapping, processors, etc.
The config/test service allows you to test the merging of several Jsons (or XJsons) by obtaining either a merge response or an explain response.

...

GET /api/portalconfig/config/test also works, with the parameter explain (operation ID: configTest)

parameters

The parameters are a combination of the parameters found in the services config/merge, config/explain and xjson/to and xjson/from.

  • json, string or file, (at least one is mandatory), that contains JSon (or XJSon), multiple values allowed (JSon lenient parsing is not supported)

  • xjson, boolean, optional (default is false). If true, if the jsons passed in parameter must be considered as xjson, false, otherwise (not to be confused with the xjson option which controls how the response json is formed)

  • lenient, boolean, optional (default is false). If true, the xjsons passed in parameter must be processed as lenient

  • explain, boolean optional (default is false). If true, the result is an explain, if false, the result is a merge. (works only with end point /test)

  • locations, optional
    This parameter allows to give location names to each JSon. Indeed, as there is no corresponding physical location (no files or folders), a virtual location is generated with just an incremental index (from 1 to n, and Root for root). This parameter allows to name the layers.

    • either by a JSon array (example: locations=[_base,_base@test])

    • or by strings (example ...&locations=_base&locations=_base%40test&...)

  • pathFilter (see get configuration and explain configuration)

  • pathFilterMode (see get configuration and explain configuration)

  • nodesWithPaths (see get configuration and explain configuration)

  • options (see get configuration and explain configuration)

  • verbosity, only if explain (see get configuration and explain configuration)

processors list

Gets processors list (variable names)

GET /api/portalconfig/postprocessors/list (operation ID: postProcessorsList)

parameters

  • mappingID: string (optional), designates a particular mapping (filter)

  • postProcessor: multiple strings or comma-separated (optional), designates a particular post processor, or a list of post processors (filter)
    (you can have several times the parameter, or list several postProcessor in the same parameter, separated by commas)

  • options: a JSON object (optional), defines options

    • withMappings: (default false) allows to have the list of mappings that use the postprocessor

    • withValues: (default false) allows to have the list of mappings that use the postprocessor

processors value

Gets post processor value for the current context

GET /api/portalconfig/postprocessors/value (operation ID: postProcessorsValue)

parameters

  • mappingID: string (mandatory) the mapping ID

  • postProcessor multiple strings or comma-separated (optional), designates a particular post processor, or a list of post processors (filter)
    (you can have several times the parameter, or list several postProcessor in the same parameter, separated by commas)

  • surfer:string, as UUID, UID, ID… (optional), a particular user for whom we want the resolution

  • options: a JSON object (optional), defines options

    • withMappings (false)

    • withValues (false)

all paths

Gets all possible paths for a mapping to resolve a configuration.

GET /api/portalconfig/path/all (operation ID: pathAll)

parameters

  • mappingID string (mandatory), the mapping ID

  • options a JSON object (optional), defines options

    • explain boolean (optional)

      • true: gives the details of each part of the paths, with the source of the postprocessors and values that determined them

      • false (or absent): no details (default behavior)

    • withFileInfo (optional) boolean

    • sizeOfDirectory boolean (optional, default is true) if true, the size of a folder is calculated by adding the size of all the files in it. If false, it is the size of the file-folder.Files and directories

  • filter JSON object (optional)
    This filter allows you to select files according to criteria. It is defined as a JSON with the following properties:

    • withBase boolean (optional)

      • if true, base config is included in response

      • if false, base config is not included in response

      • if absent or null, has no effect

    • exists boolean (optional)

      • if true, configuration files that do not exist aren’t included in the response

      • if false, configuration files that exist aren’t included in the response

      • if absent or null, has no effect

    • postProcessors (optional) string, or array of strings (A list of post processor names)
      Only the paths that have been determined from the listed processors are included in the response.

    • postProcessorValues (optional) JSON object/map
      Allows to get only the paths that contain certain values of post processors.

      • keys are post processors names

      • values (string, or array) are the list of values from the post processors for which a path will be included in the response

resolve paths

Gets all possible full paths for a mapping. The difference with “all paths” is that we only have the final folders (those that correspond to the evaluation of postprocessors in the context).

GET /api/portalconfig/path/resolve (operation ID: pathResolve)

parameters

  • mappingID, string (mandatory): the mapping ID

  • options, JSON (optional)

    • explain boolean (optional, default is false) if true, expose details on each paths

    • withFileInfo boolean (optional, default is false) if true, informations on files are included (size and length)

    • sizeOfDirectory boolean (optional, default is true) if true, the size of a folder is calculated by adding the size of all the files in it. If false, it is the size of the file-folder.Files and directories

  • filter, JSON (optional)
    This filter allows you to select files according to criteria. It is defined as a JSON with the following properties:

    • withBase, boolean (optional, default is false)

      • if true, base config is included in response

      • if false, base config is not included in response

      • if absent or null, has no effect

    • exists boolean (optional, default is false)

      • if true, configuration files that do not exist aren’t included in the response

      • if false, configuration files that exist aren’t included in the response

      • if absent or null, has no effect

    • postProcessors (optional) string, or array of strings (A list of post processor names)
      Only the paths that have been determined from the listed processors are included in the response.

    • postProcessorValues (optional), JSON object
      Allows to get only the paths that contain certain values of post processors.

      • keys are post processors names

      • values (string, or array) are the list of values from the post processors for which a path will be included in the response

mappings

Gets list of existing mappings

...

A configuration is finally accessed by an identifier. For each identifier, we define a configuration path, consisting of the paths of the different folders or configuration files, separated with the character arobase (@) that will successively overload the database. The combination of the two is called mapping.

parameters

  • options, JSON (optional)

    • withStatus, boolean(optional, default is false)
      Expose status of mapping:

      • OK: the mapping is existing and is valid

      • NO_FILE: the mapping exists but no base file has been found

      • INVALID: the configuration of the base path is invalid (probably the folder is outside the specific base location)

      • VARIABLE: the path associated to mapping is variable. This service could not handle variable bases.

    • withFileInfo, boolean (optional, default is false) if true, informations on base files are included (size and length)

    • sizeOfDirectory, boolean (optional, default is true) if true, the size of a folder is calculated by adding the size of all the files in it. If false, it is the size of the file-folder.Files and directories

layers

Gets a list of configuration layers (configuration layer root directories)

GET /api/portalconfig/layers (operation ID: layersList)

parameters

  • pathMatcher (optional)
    A filter to select layers. See pathMatcher.

  • options, JSON (optional)

    • withRights, boolean, (optional, default is true)
      If true, we expose the writing rights in this layer

    • onlyIfExists, boolean, (optional, default is false)
      If true, we expose that the layers that physically exist

    • withMappings, boolean, (optional, default is false)
      If true, we expose the list of mappings that include the layer stored in this layer

    • withInfo, boolean (optional, default is false)
      If true, we expose some information about the layer:

      • exists, a boolean: if true, the layer physically exists

      • var, a boolean: if true, the layer name has been calculated by postprocessors

      • restarting, a boolean: if true, the layer name is relative (it has been cut by a $ during its resolution)

    • withFileinfo, boolean (optional, default is false)
      If true, we expose the last modification date and the size of the layer (if it exists)

    • sizeOfDirectory, boolean (optional, default is false)
      If true, the size of the folder is the total size of all the files it contains (otherwise, this information is what the java.nio.file.Files.size() method returns)

    • outOfMappings, boolean (option, default is false)
      if true, also retrieves the layers that are not used by a mapping (so all folders created in the configuration storage folder)

Panel
panelIconIdatlassian-light_bulb_on
panelIcon:light_bulb_on:
panelIconText:light_bulb_on:
bgColor#C0B6F2

This service can be used to know if a layer is used in a mapping. For example to know if _portal@role-4 is used in a mapping:

  • pathMatcher=_portal@role-4

  • options

    • withMappings=true

    • outOfMappings=true

diff

Compares two JSON. The result is a list of differences.

GET/POST /api/portalconfig/diff (operation ID: jsonDiff)

Parameters

  • leftJson, JSON descriptor (mandatory), a JSON to compare to rightJson

  • rightJson, JSON descriptor (mandatory), a JSON to compare to leftJson

  • processorValues, JSON/map (optional), a default set of processor values

  • options, JSON (optional),

JSON descriptor

Describes the reference JSON to compare the other to (left), or the JSON to compare to the reference (right).

...

  1. json

    1. In this case, the json field (mandatory) contains the JSON value to compare.
      Example:

      Code Block
      languagejson
      {
         "type":"json",
         "json": {
              "a": 1,
      		"b": true,
      		"c": "abc"
          }
      }

    2. xjson (optional, default is false) if true, the JSON is a XJSON, if false the JSON will be converted to XJSON to compute differences.

  2. form

    1. In this case, the field field (mandatory) contains the name of the form field that contains the JSON (file or string)
      Example:

      Code Block
      languagejson
      {
         type: form,
         field: json1,
         xjson: true
      }
    2. xjson (optional, default is false) if true, the JSON is a XJSON, if false the JSON will be converted to XJSON to compute differences.

  3. mapping
    References a mapping.

    1. mappingID, string (mandatory), the mapping ID

    2. wip, boolean (optional, default is false): true activates the wip mode

    3. processorValues, JSON/map (optional, none by default) a set of processor values

    4. surfer, instance reference (optional, none by default), a surfer

  4. config
    References a configuration layer

    1. In this case, the path field (mandatory) is the path of the configuration layer
      Example

      Code Block
      languagejson
      {
         type: config,
         path : '_portal@club-wed@role-4'
      }
  5. base
    References a configuration base

    1. In this case, the mappingID field is the mapping for which we want the base
      Example

      Code Block
      {
         type: base,
         mappingID: portal
      }

dir

To get a filesystem view of a configuration folder.

GET /api/portalconfig/fs/dir (operation ID: dir)

Parameters

  • path string (mandatory): the path of the desired layer
    for exemple, _portal@club-wed@role-4

  • subpath, string (optional): a path relative to the layer path
    for example, boardsview

  • verbosity, JSON (optional):

    • includesFileInfos, boolean (optional, default is true): exports dates and file sizes

    • includesFileCounters, boolean (optional, default is true): exports the number of files and folders

  • options, JSON (optional):

    • depth: int (optional, default is -1=infinite depth)

    • pathMatcher: (optional) defines a filter on paths and file names. The filter is applied to the relative path and name: if it matches one of the two, then the file or folder is exported, and those that do not match one of the two are not. See pathMatcher.

merged dir

To get a filesystem oriented view of a set of configuration folders corresponding to a mapping resolution, in a merged way.

GET /api/portalconfig/fs/dir (operation ID: dirMerged)

Parameters

  • path: string, multiple (mandatory, if mappingID is not present): a list of paths (base or configuration layers). If mappingID is also present, it’s used as an inclusive filter. If mappingID is not present, it's the list of paths to get.

  • mappingID: string (mandatory, if path is not present), the mapping for which we want the configuration

  • wip: boolean (optional, false by default), activate the “wip” mode (if false, wip/inherit files are ignored, if true, wip/inherit files are taken into account

  • surfer, instance reference (optional, none by default), a substitute user/surfer to get the configuration that this surfer would have if he called the service. (See Instance reference parameter)

  • processorValues, JSON/map (optional, none by default), a processor/value association map to obtain the corresponding configuration
    For example,

    Code Block
    languagejson
    {
       "surferRole": "role-31"
    }
  • maxLayers, int (optional, default is -1=infinite): only if mappingID present and resolve true, limits the numbers of paths

  • resolve, boolean (optional, default is true): only if mappingID present, if true, resolve the configuration layers against the context (so get only the involved paths), if false, get all possible paths

  • verbosity, JSON (optional):

    • includesErrors, boolean (optional, default is true): export the files or directories that caused read errors

    • includesIgnored, boolean (optional, default is true): export the files or directories ignored during the resolution of a mapping

    • includesNotFound, boolean (optional, default is true): export the configuration layer folders that do not exist

    • includesFileInfos, boolean (optional, default is true): exports dates and file sizes

    • includesFileContent, boolean (optional, default is true): exports the content of files

  • options, JSON (optional):

    • depth: int (optional, default is -1=infinite depth)

    • pathMatcher: (optional) defines a filter on paths and file names. The filter is applied to the relative path and name: if it matches one of the two, then the file or folder is exported, and those that do not match one of the two are not. See pathMatcher.

Lock services

These services allow you to obtain a lock that allows you to call the configuration creation or modification services (and configuration files). Once the lock is obtained, other users cannot obtain one until it is released or expired.

Comming soon: locks per layer, by the layer parameter

lock

POST /fs/lock(operation ID: lockPut)

Put an exclusive access lock (if not already owned).

Response

Http status

200

The lock has been obtained or extended

403

Unable to obtain a lock (already owned, or error)

503

Locks are unavailable

500

Other errors

unlock

DELETE /fs/lock(operation ID: lockDelete)

Delete the owned lock.

Response

Http status

200

Lock deleted

202

No lock

403

Lock not owned

500

Other errors

get lock

GET /fs/lock(operation ID: lockGet)

Test if lock is owned (and extend it)

Http status

200

Lock is owned and extended

202

Lock not owned (free)

403

Lock is owned by some body else (other surfer or other session)

503

Locks are unavailable

500

Errors

Auto locking

Any service that creates, modifies or deletes configuration files automatically creates a lock (enableable/disableable by plugin configuration).

Lock live time

Lock live time is configurable in plugin configuration (by default 60s).

Enable/disable

This feature is disableable by plugin configuration.

Command submission⚠️

Services that may fail due to lack of lock availability can be run in batch with guaranteed execution. When the execution fails because of the lock system, the execution is retried later.
At invocation, a set of commands is specified, each command corresponding to a service invocation. The service returns a command identifier and the current status of each command. It is possible to query this status via the command set identifier at any time until complete. Completed command sets are kept for a limited time. If the status of an unknown order set is requested (unknown id, or retention time exceeded), a done status will be obtained, without details per command

...

The parameter value that describes the command set is a JSON (lenient syntax).

Description of a set of commands

A set of commands is described by a JSON array of commands. A single command can be described directly (out of an array).
Any value other than an array or object (command description) will result in an empty command set.

Description of a command

A command is described by a JSON object with the following properties:

...

The parameter names are the same as for the corresponding services.

Examples

Code Block
languagejson
{
   name: 'FS/LAYER/WRITE',
   parameters: {
      layer: '_portal@test@role-4'
   }
}
Code Block
[
   {
      id: 'writeMainLayers',
      name: 'FS/LAYER/WRITE',
      parameters: {
          layer: ['_portal@test@role-4','portal@test@role-14']
      }
   },
   {
      id: 'role_4_displayNaturalSearch'
      name: 'FS/FILE/WRITE',
      parameters: {
        layer: '_portal@test@role-4',
        path: [home,displayNaturalSearch],
        content: true,
        options: {
            mkDirs: true
        }
      }   
   },
   {
      id: 'role_14_displayNaturalSearch'
      name: 'FS/FILE/WRITE',
      parameters: [
         {
            name: layer,
            values: '_portal@test@role-14'
         },
         {
            name: path,
            values: [[home,displayNaturalSearch]]
         },
         {
            name: content,
            values: false
         },
         {
            name: options,
            values: {
                mkDirs: true
            }
         }
      ]
   },
   {
      id: 'role_16_displayNaturalSearch'
      name: 'FS/FILE/WRITE',
      parameters: [
         {
            name: layer,
            value: '_portal@test@role-16'
         },
         {
            name: path,
            value: [home,displayNaturalSearch]
         },
         {
            name: content,
            values: false
         },
         {
            name: options,
            values: {
                mkLayers: true,
                mkDirs: true,
                clear: true
            }
         }
      ]
   }
]

Response

Timeout

Service to submit a command set

POST /fs/command(operation ID: commandSubmit)

parameters

commands, mandatory, description of commands (see above)

...

You can specify unit (as ms, s, m(for minute)). For examples: 45s, 45000 ms, 0.75m...

options

none

Service to get state of a command set

GET /fs/command(operation ID: commandStatus)

parameters

id, mandatory, command id

timeout, optional (default 0), maximum waiting time in ms for service response (ignored if commandset is finished, or not found)

options

none

Runtime system configuration

The job manager is configured by the command_submit_manager_config parameter in the WXM_CONFIG_RESOLVER plugin configuration which is a JSON object with the following properties:

  • threadCount: number (int), optional (default is 3, minimum is 1),
    the number of allocated threads

  • keepOldJobTime: number (long), optional (default is 30000, minimum is 0),
    the maximum retention time of a set of commands (after execution), in ms

  • maxOldJobCount: number (int), optional (default is 1000, minimul is 0),
    the maximum number of command sets retained after they have been completed

Modify or create configuration properties

These are services to modify the configuration without manipulating files (without using basic file operations).

Operations

General

With a few exceptions, these services operate on the same principle.
For write services, a configuration can be specified either as an XJSON (lenient) or as a list of paths (XJSONPATH), always in a specified configuration layer (layer). If a path is specified together with an xjson, the path must be unique and constitute the complete path to the property being written. If the path is omitted, the root of the configuration layer is addressed. You can write either configuration, wip files or delete files, but not all three at the same time in one call: this is handled by two boolean parameters named delete and wip.
In the XJSON, as in the paths, we do not put a file extension. The XJSON is called lenient because it accepts JSON arrays: in this case, the XJSON object is evaluated automatically (indexes are assigned automatically), and the property name does not have to end with empty brackets ([]). In writing, only what is described in the XJSON is written, without modifying the other properties (but the properties referenced by the XJSON are well replaced: all siblings files for the property are deleted). The option merge allows you to specify that you want to delete the properties that are not described in the json.

Patch configuration properties with a XJSON

To patch a configuration layer with an XJSON, use

  • service: POST fs/config(operation ID: configWrite)

  • parameters

    • content (mandatory): the XJSON

    • layer (mandatory): the configuration layer name

    • path (optional): an xjsonpath target subpath

Patch configuration delete files by paths

To patch a configuration layer with delete files, use

  • service: DELETE fs/config (operation ID: configDelete)

  • parameters

    • content: none

    • layer (mandatory): the configuration layer name

    • path (at least one): one or several xjsonpath

Replace configuration properties with a XJSON

To patch a configuration layer with an XJSON, use

  • service: POST fs/config(operation ID: configWrite)

  • parameters

    • content (mandatory): the XJSON

    • layer (mandatory): the configuration layer name

    • path (optional): an xjsonpath target subpath

    • options

      • merge: false

Delete configuration (write delete files and/or remove files) with a XJSON

To patch a configuration layer with an XJSON, to delete configuration properties of the above layers or remove configuration properties from this layer, use

  • service: DELETE fs/config (operation ID: configDelete)

  • parameters

    • content (mandatory): the XJSON
      In that mode, the XJSON is not lenient, because arrays are used to specify the mode. In this array, specify the desired options among

      • DELETE (or D, or DEL, insentive case)
        to write a file with delete suffix

      • REMOVE (or R, or REM, insentive case)
        to remove the property

        for example:

        Code Block
        {
           a: [delete],
           b: [remove],
           c: [delete, remove]
        }

        • the file a.delete will be created

        • all the files and directories for the property b will be removed

        • all the files and directories for the property c will be removed, then the file c.delete will be created

    • layer (mandatory): the configuration layer name

    • path (optional): an xjsonpath target subpath

Service to write configuration

POST fs/config (operation ID: configWrite)

...

This service allows to modify (patch or replace) the configuration in a layer, or in a subfolder of a layer

Parameters

  • layer, (see Basic file operatirons/Parameters for details), mandatory
    denotes the configuration layer

  • path, (see Basic file operatirons/Parameters for details), optional, can be single or multiple, depending on the presence of content
    specifies the relative xjsonpath (to the configuration layer) where to write

  • content, string or file(multipart/formdata), optional
    The XJSON (lenient) to write. If this parameter is not specified, multiple paths can be specified. If this parameter is specified, only one path or none can be specified (in this case, we write directly to the layer)

  • wip, boolean, optional(default is false)⛔
    True to write to wip data

  • delete, boolean, optional(default is false)⛔
    True to write delete overrides. In this mode, the primitive values in the xjson (if present) are ignored.

  • options, JSON with the following properties

    • merge, boolean, optional (true by default)
      True to keep the other properties already existing in the namespace we are writing. If false, All other properties in the written namespace will be removed

      For example, the name space is currently

      Code Block
      /a
          /b
              /c
                c1.json = "c1"
                c2.json = "c2"
          /d
              d1.json = "d1"

      We write this XJSON:

      Code Block
      languagejson
      {
        "a": 
          "b": {
            "c": {
                "c2": "c2>modified",
                "c3": "new"
            }
          }
        }
      }

      With merge=true, the result is

      Code Block
      /a
          /b
              /c
                c1.json = "c1"
                c2.json = "c2>modified"
                c3.json = "new"
          /d
              d1.json = d1

      With merge=false, the result is

      Code Block
      /a
          /b
              /c
                c2.json = "c2>modified"
                c3.json = "new"    
    • mkLayers, boolean, optional(false by default)
      True to create layer if it doesn’t exists

    • keepFormat, boolean, optional(false by default)
      By default, only JSon is written to files (or empty content for .delete files). If keepFormat is true, and the JSon value is string, and the target file is already existing, the original format (from the suffix) is kept.
      Example
      Here the current file system:

      Code Block
      languagejson
      /a
          /b
              /c
                c1.json = "c1"
                c2.txt= c2
          /d
              d1.json = "d1"

      XJSON to write:

      Code Block
      languagejson
      {
        "a": 
          "b": {
            "c": {
                "c1": "c1>modified",
                "c2": "c2>modified"
            }
          }
        }
      }

      Result with keepFormat=false

      Code Block
      languagejson
      /a
          /b
              /c
                c1.json = "c1>modified"
                c2.json = "c2>modified"
          /d
              d1.json = "d1"

      Result with keepFormat=true

      Code Block
      /a
          /b
              /c
                c1.json = "c1>modified"
                c2.txt= c2>modified
          /d
              d1.json = "d1"
    • keepDelete, boolean (optional, true by default)
      when a property is written, the files concerning this same property are deleted (the writing constitutes a replacement). This option indicates how the file with the delete extension is to be considered during this replacement: by default, it is not deleted (keepDelete=true). You can force the deletion by setting keepDelete to false.

    • keepProperty, boolean (optional, true by default)
      when you write .delete files in path mode, the files concerning the same property aren’t deleted. You can force deletion by setting keepProperty to false.

Delete (erase or write delete files)

DELETE fs/config (operation ID: configDelete)

Command name: fs/config/delete

  • layer, (see Basic file operatirons/Parameters for details), mandatory
    denotes the configuration layer

  • path, (see Basic file operatirons/Parameters for details), optional, can be single or multiple, depending on the presence of content
    specifies the relative xjsonpath (to the configuration layer) where to write

  • content, string or file(multipart/formdata), optional
    The XJSON to write. If this parameter is not specified, multiple paths can be specified. If this parameter is specified, only one path or none can be specified (in this case, we write directly to the layer).
    This XJSON is not lenient. Arrays are used to specify the operation (see details in the paragraph Delete configuration (write delete files and/or remove files) with a XJSON):

    • DELETE

    • REMOVE

  • wip, boolean, optional(default is false)⛔
    True to write to wip data

  • options, JSON with the following properties

    • deleteNotEmpty, boolean (optional, true by default)
      if false, REMOVE option is not applyed on not empty directories

    • mkLayers, boolean (optional, false by default)

Push

Commit

Rollback

Basic file operations

These are file manipulation operations, with minimal controls. An operation (a service call) is performed exclusively. Other concurrent operations are put on hold until the current operation is completed, or possibly rejected after a timeout.

Parameters

  • layer, a string
    denotes the configuration layer (e.g. _portal@club-wed@role-4)

  • path
    specifies the relative path (to the configuration layer) of the file. The suffix can be omitted: in this case, the best matching file will be used.

    • string
      There is no automatic escape or conversion: the value will be used as is. Slashes and backslashes can be used interchangeably and their successions are ignored. You can escape slashes o backslashes with a backslash before.
      The path can't be an empty string or only made of spaces.
      For examples:

      • home/display-natural-search.json

      • home/display-natural-search

    • JSON array
      Each value of the array is escaped (the method that is used is Utils.camel2Kebab() (see Test.jsp), according to the rules indicated here). Then the path is reconstructed by joining these values, separating them with a slash. The JSON lenient syntax is supported. Values can be a string or an array of one string (in that case, the string is not escaped).
      For examples:

      • [home,[display-natural-search]]

      • [home,displayNaturalSearch]

    • JSON object

      • property path (mandatory)

        • string

        • JSON array

      • property suffix, string, the suffix of the file (optional, if ommited, the best matching file will be used)

      • property escape, boolean (optional, default is true)
        if true, parts of file path will be escaped or converted.

...

  • pathMatcher
    A path matcher, that uses the same syntax as pathMatcher (see pathMatcher), with an additional property in its object form:

    • onlyName, boolean (optional, false by default)
      If true, the filter is only applied to the file name.

  • content
    The file content

    • a file (multipart/form-data)

    • a string

  • contentType
    Indicates under which type (the value can be a MIME type or a file suffixe) the content provided by the content parameter should be considered. If
    If there is no contentType parameter, it will be evaluated:

    • either by analysis of the file suffixe when content is a file

    • or by a short analysis of the text if content is a string

      • if text is empty, it’s an empty file

      • if text is not empty,

        • we try to parse it as JSON (not lenient). If it doesn't parse, we go to the next step. If it doesn't make an error, we consider the content as JSON

        • Otherwise, it is plain text.

    • ignored files are supported only in the path: the file in multipart/form-data must have the true extension

  • options
    A JSON object (lenient). All properties are optional with specific default values depending on the type of operation.

Best matching file

When reading (or for deletions), when it is not explicit, we determine the most suitable file extension by analyzing the existing files on the disk.
When writing, the contentType is used.

Read file(tick)

GET/HEAD /api/portalconfig/fs/file (operation ID: fileGet)

This service allows you to read a file (its content). This service does not allow to retrieve the content of a folder (use the dir service for that).

Parameters

  • layer, mandatory, single value

  • path, mandatory, single value

Options

  • lookForBestMatching, boolean (optional, true by default)
    If false, disable looking for best matching file (suffixe becomes mandatory)

Response

The content of the file is returned in the body of the response, with the status 200. If the file is empty, a status 204 (No Content) is returned, with no body. The Content-Type header gives the type of content. The Content-Length header gives its size.

Write file(warning)

POST/PUT /api/portalconfig/fs/file

...

  • If a non-empty content is present, the file is a text file of one of the following types:

    • plain text
      The suffix must be txt (or text) (or specify the type text/plain)

    • HTML
      The suffix must be htm (or html) (or specify the type text/html)

    • JSON
      The suffix must be json (or specify the type application/json)

    • ignored file are supported

  • if empty content is present, the file will be an empty file of one of the following types:

    • delete
      The suffix must be delete

    • ignored files are supported

  • if no content is present, the file will be a directory

Parameters

  • layer, mandatory, single value

  • path, mandatory, single value

  • content, the file content if any

  • contentType, the content type

Options

  • replace, boolean (optional, false by default)
    If false, if the file already exists, the operation returns an error. If true, the existing file (or directory) is replaced.

  • mkDirs, boolean (optional, false by default)
    If false, if the directory doesn’t exists, the operation returns an error. If true, the directories are created.

  • mkLayers, boolean (optional, false by default)
    If false, if the layer doesn’t exists, the operation returns an error. If true, the layer directory is created.

  • clear, boolean (optional, false by default)
    Other files with the same base name in the same directory will be deleted automatically.

Delete file⛔

DELETE /api/portalconfig/fs/file (operation ID: fileDelete)

...

Command name: fs/file/delete

Parameters

  • layer, mandatory, single value

  • path, multiple values

  • pathMatcher, mutiple values

  • options

    • recursive

    • onlyDir

    • onlyFile

    • onlyContent

At least one path or one pathMatcher must be specified.

Create layers(tick)

POST /api/portalconfig/fs/layer

...

This service allows you to create one or several layer (root configuration directories)

Parameters

  • layer, mandatory, multiple values

Response

The response, when no error is returned, is a JSON array of objects with the following properties:

...

If at least one layer has been created, the code 201 is returned. If all creations have failed, the code 400 is returned. Otherwise code 200 is returned.

Example
Code Block
languagejson
[
	{
		"layer": "_portal@testcreate",
		"state": "ALREADY_EXISTS"
	},
	{
		"layer": "_portal@testcreate1",
		"state": "ALREADY_EXISTS"
	},
	{
		"layer": "_portal@testcreate2",
		"state": "CREATED"
	}
]

Errors

Some errors are not raised in error if there are several layer names. In this case, see the state property in the response.

HTTP status

When

State when several layers

400 - Bad request

no layer name (missing mandatory parameter)

400 - Bad request

a layer name is a base

BASE

400 - Bad request

a layer name contains forbidden char (slash, backslash, dollars, or file forbidden chars)

INVALID

500 - Internal Server error

An error occurred while creating the layer

ERROR

Example

Code Block
curl --request POST \
  --url 'http://<host>/api/portalconfig/fs/layer?layer=_portal%40testcreate&layer=_portal%40testcreate1&layer=_portal%40testcreate2'
Code Block
curl --request POST \
  --url http://<host>/api/portalconfig/fs/layer \ 
  --data layer=_portal@testcreate \
  --data layer=_portal@testcreate1 \
  --data layer=_portal@testcreate2

Delete layers(tick)

DELETE /api/portalconfig/fs/layer (operation ID: layerDelete)

...

This service allows you to create one or several configuration layers (root folders).

Parameters

  • layer, mandatory, multiple values

  • options:

    • recursive: boolean, default is false
      if false, deleting a non-empty layer will cause an error. if true, a non-empty layer will be deleted with all its contents.

Response

The response, when no error is returned, is a JSON array of objects with the following properties:

  • layer: the name of the layer

  • state: the state among:

    • INVALID: the layer name contains invalid characters

    • BASE: the layer name is identified as the base name (not deletable)

    • VARIABLE: the layer name is identified as being dependent on variables (postprocessors)

    • NOT_FOUND: a layer of this name hasn’t been found

    • DELETED: the layer has been deleted

    • NOT_EMPTY: the layer can’t be deleted because it’s not empty

    • ERROR: There was an error when deleting the layer

Rename layer

TO DOCUMENT

Convert JSON to XJSON

GET/POST /api/portalconfig/xjson/to (operation ID: xjsonTo)

Parameters

  • json: JSON or JSON file (multipart/form-data)

Convert XJSON to JSON

GET/POST /api/portalconfig/xjson/from (operation ID: xjsonFrom)

Parameters

  • json: JSON or JSON file (multipart/form-data)

  • lenient: boolean (optional, false by default). If true, non xjson structures (like arrays) do not cause errors.

Cache generation

POST /api/portalconfig/cache/flush (operation ID: cacheFlush)

Regenerate cache from current configuration directories state

Cache status

GET /api/portalconfig/cache (operation ID: cacheStatus)

Get cache status

Info

GET /api/portalconfig/info (operation ID: info)

Get some information about the plugin.

Response

  • version, version of the plugin

  • env, type of environment, among

    • DEV

    • PROD

    • PREPROD

  • security, true, if security is enabled

  • status, plugin status

    • label, status of the plugin, among

      • OK

      • Startup error

      • Startup

      • Shutdown

      • Shutting down

    • code, status code color, among

      • GREEN

      • YELLOW

      • RED

Filtering

Path

JSON filtering is done by path. A path is defined as a sequence of :

...

  • The value 3 has the absolute path /a[0]/d.

Multipath

You can combine several paths by one or, by separating them with a pipe (|).

  • /a/b|/a/c will select the elements corresponding to the paths /a/b and /a/c;

  • /a/b||0] will select the elements corresponding to the paths /a/b and [0].

Escaping

To escape a reserved character, use a backslash (\).

...

  • slash (/)

  • opening bracket ([)

  • closing bracket (])

  • pipe (or) (|)

  • antislash (escape character) (\)

Syntax tolerance

It is preferable to respect the syntax scrupulously, but some syntaxes are tolerated and will be converted automatically.

  1. slash before an array index is useless and will be ignored. /a/[0] will be replace by /a[0]

  2. slash is mandatory between an array index and before a fieldname, but could be ommited. /[0]a will be replaced by /[0]/a.

  3. brackets must be escaped if then don’t define an array index (so if characters between brackets aren’t only digits), but you can avoir to escape them (the opening, the closing or the both). /a[b]c will replaced by /a\[b\]c. /a[b\]c will be replaced by /a\[b\]c. /a\[b]c will be replaced by /a\[b\]c.

  4. array indexes are defined in square brackets with numbers and only numbers. Any other character will cause them to be considered part of a field name. [0] is an array index and [ 0 ] is a field name. If it is necessary to reference a field name that contains a number part in square brackets, it will be necessary to escape the square brackets so that it is not considered as an array index.

Filtering a JSon

You can retrieve a part of a JSON by a path.

...

  • with the path /a/d/h you can retrieve the following part:

    Code Block
    languagejson
    {
        "i": 5,
        "j": 6
    }
  • with the path /k, you will retrieve the following part:

    Code Block
    languagejson
    [7,8,9]

Filtering mode

There are two filtering modes:

...

Code Block
languagejson
{
   "node": the node,
   "path": the path of the node,
   "pathArray": the path of the node decomposed in an array,
   "JSONPath": the JSONPath of the node,
   "JSONPathArray": the JSONPath of the node decomposed in an array,
   "XJSonPath": the XJsonPath of the node,
   "XJSonPathArray": the xjson path of the node decomposed in an array
}

Configuration files and folders

The configuration consists of configuration files and folders.

Configuration layers

This is a folder that contains folders and configuration files. It is a configuration layer that will override a base or other configuration layers if eventually. It can be a file or a folder.

...

If it is a folder, we will load its components as indicated below, in the "folders and files" section.

Base configuration

It is a folder or a file that is provided by the product, or projects. The bases are not modifiable by the API. They can be provided in the WXM_CONFIG_RESOLVER plugin or other plugins provided by the product. Projects can provide their own bases per plugin (see WXM_CONFIG_RESOLVER plugin configuration).

Principle

A final configuration, a JSON file, is the fusion of several configuration folders. The base is used first, then different layers come to redefine or overload (add, delete, modify properties).

Folders and files

A configuration layer, if it is a folder, contains folders and files, which will be

  • either considered as elements of the final configuration JSON

  • or ignored

  • or in error if they are not readable or incorrect

Ignored and unsupported files

The following files are ignored:

...

Ignored files are ignored at the base, when retrieving the contents of a folder. They are never involved in the interpretation of the final configuration. It is as if they do not exist. However, we can have a list of them, through the explain function, in the ignored section (except wip files excluded in non-wip mode).

Reasons

In explain mode, if the includesIgnored option (in ExplainVerbosity) is true, we get the list of ignored files (in the ignored section). For each ignored file, we have the reason. Here is the list of possible reasons:

  • NOT_FOUND
    occurs when you try to load a configuration directory (or file) that is not found (for example, you try to load the portal mapping defined by _portal@config@custom, and the _portal@config folder does not exist)

  • NOT_JSON_OBJECT

    • occurs when you try to load a json base (a file with the name _json) in a folder that defines an object and the loaded JSON is not an object

    • occurs also when you try to load a configuration from a file configuration layer and the JSON is not an object (a configuration layer must always be an object, as the root configuration is an object).

  • NOT_JSON_ARRAY
    occurs when you try to load a json base (a file with the name _json) in a folder that defines an array and the loaded JSON is not an array

  • FILE_WITHOUT_SUFFIX
    files that define properties of type primitive or null (so except array and object) must have a suffix to determine how to read the content: json, text (or txt) or html (or htm).

  • DIR_WITH_SUFFIX
    folders with suffix are ignored.

  • FILE_WITH_UNKNOWN_SUFFIX
    only the following suffixes are supported, others are ignored:

    • json, text, txt, html, htm

    • delete

    • ignore

  • JSON_BASE_AS_DIR
    a folder with name _json is always ignored

  • JSON_BASE_WIP_OR_INHERIT
    json base (file with name _json) can’t be a wip or an inherit type.

  • HIDDEN_FILE
    all files and folders whose name starts with a dot are ignored

  • NOT_INDEX
    a file or folder that defines a property in the context of an array must be a number, otherwise it will be ignored.

  • IGNORE_SUFFIX
    you can add the ignore suffix to any file or folder so that it is ignored

  • NOT_SUPPORTED
    any file with an extension other than those supported (like jpeg for example, or rtf, or doc, zip, etc) are ignored

  • INHERITED
    override cancelled by an inherit file

  • INHERIT_WITH_SUFFIX
    an inherit file or folder must not have an extension. Preferably it should be an empty file, with a simple name, just preceded by the caret character (^).

  • INHERIT_ARRAY
    an inherit file or folder must not end with []. Preferably it should be an empty file, with a simple name, just preceded by the caret character (^).

  • DUPLICATE_NAME
    When there are multiple files that could be interpreted as defining the same property (for example, an a.json file and a folder with name a[]), they are all ignored.

  • DUPLICATE_INDEX
    When there are several files that point to the same array index (e.g., 1.json, 01.json, 0001.json, etc.), they are all ignored)

  • INHERIT_WITHOUT_NAME
    an inherit file will be ignored if there is no corresponding property in the same folder

  • WIP_AND_INHERIT_TOGETHER
    you can only have either a wip file or an inherit file. If you have both at the same time, then they are both ignored.

  • DUPLICATE_WIP
    There should be only one wip for a property. If there are several, one is processed, the others are ignored.

  • DUPLICATE_INHERIT
    There should be only one inherit for a property. If there are several, one is processed, the others are ignored.

  • ERROR
    When a file cannot be read due to an error, the file is ignored. In this case, in addition to the reason, the error will be given (see ExplainVerbosity options for details).

  • UNKNOWN
    If the reason for ignoring a file cannot be determined.

  • WIP_FILE

  • INHERIT_FILE

JSON bases

These are files with the name _json, without suffix. The _json file is a file that contains JSON (not XJSON). It defines a JSON base for the object or array defined by the folder in which it is located.

...

A file with the name _json and a suffix, like json for example, is not a json base, it’s a property file with the property name _json.

Override a property

To redefine a property (an object field, an array value), simply put a file or a folder in the folder of the object or array that contains this property.

...

Code Block
languagejson
{
  "a": {
     "b": 42
     "c": true
     "d": "abc"
  },
  "e": 3
}

Override an array

To be able to replace and especially insert values in an array, an object structure is used to store an array. The name of the folder that corresponds to the array property must end with []. Inside, the file names correspond to indexes. The indexes of the values of the initial array are redefined in order to allow the insertion of values: we start at index 1000 and repeat from 1000 to 1000, thus leaving 1000 possibilities of insertions between each one.

...

Code Block
languagejson
{
  "a": [42,1,true,3,"abc"]
}

Value file

3 types of content are supported:

...

Storage encoding is always UTF-8.

Names

In the folders, the file names are encoded:

  • they must always contain only lower case letters

  • to support capital letters, the kebab-case format is used: the capital letters are lower case preceded by a dash

  • the first character is special

    • if it is a tilde (~), then the file is considered as wip

    • if it is a circumflex (^), then the file is considered as wip inherit

  • if the last two characters are the sequence [], then the file is an array property

  • the last dot (.) is always considered as a suffix separator

  • if one of the above reserved characters (tilde, circumflex, dot, opening or closing bracket) is part of the name of a property and is in a position where it will be interpreted as indicated above, it will be escaped. Characters that cannot be used in a file name are also escaped.

    • To escape an opening bracket, we double it
      For example, the field with name abc[12] will be stored in a file with name abc[[12].json.

    • To escape another character, put the name corresponding to the character in brackets, or the corresponding hexadecimal sequence.

      • SLASH for the character /

      • BACKSLASH for the character \

      • GT for the character >

      • LT for the character <

      • QUOTE for the character '

      • PIPE for the character |

      • QUESTION for the character ?

      • STAR for the character *

      • COLUMN for the character :

      • TILDE for the character ~

      • DASH for the character -

      • DOT for the character .

      • CARET for the character ^

    • Any character can be escaped by a hexadecimal sequence, on 4 hexadecimal digits. For example, the non-breaking space will be escaped like this: [000A].

    • Any escape sequences that cannot be unescaped are retained as is.

    • Some examples:

      • abc-def 🡆 abc[DASH]def

      • abc[0]def 🡆 abc[[0]def

      • abc🡆def 🡆 abc[f09f][a186]def

  • .




Delete a property

Ignore a property (comment)

XJSON

NodePath and JSONPath

JSONpath

JSONpath est une syntaxe standard de requêtes dans du JSON similaire à du XPath pour le XML. L’API supporte le uniquement le standard Jayway.

NodePath

NodePath est une syntaxe simplifiée de chemin d'élément JSON qui est utilisée pour faire du filtrage.

Name

Array index

Absolute path and relative path

Or

Escape

Security

The security of the services is based on three actions of the ConfigResolver domain.

read

To test the reading rights

parameter

surfer, the surfer

write

To test the writing rights

parameter

surfer, the surfer

writeLayer

To test the writing right in a layer (or of a layer), i.e. a configuration container.

...

layer, the name of the layer

delivery

The plugin comes with the domain and the three actions. For the read action, a passing rule is set. For the write and writeLayer actions, a blocking rule is set.

Services accessibles only with administrator (tomcat) account

Service inventory

Get the list of services and associated security actions by calling the GET /api/portalconfig/services service (only in tomcat administrator or developer role)

Tools

this path allows the access to the services accessible in user account, by extension of the path (we concatenate the URI of the user end point to the uri /api/portalconfig/tools). Not all services are accessible. Security is not applied in this type of invocation.

Services available:

  • POST /xjson/to (convert a JSON to XJSON)

  • GET /cache (get cache status)

  • POST /cache (flush cache)

Configuration file manipulation services

It is a set of end points intended to write or delete configuration files. It is not a service to be used in the configuration editing application, but a build support.
These endpoints do not honor security. There is no management of access locks (in case of concurrent access, risk of conflicts, errors, etc). There is no control over the content of the files. Writing a file or folder replaces the previous one without warning.

Parameters

The parameters for writing, deleting and reading are almost the same.

target

This parameter allows you to choose the type of location from

  • project (default value)

  • customer

path

This optional parameter allows to specify a common root path for all processed files. If this parameter is not specified, the root of the location corresponding to the target parameter is used directly.

files and content

For GET and DELETE, the file parameter (several possible values) is used to indicate the file or files to be processed. They can be either regular files or directories.

...

  • If the name is file, the content is designated. The value of the parameter is the final content of the file.

  • If the name is filename, we designate the name (or path).

  • The file and the filename are associated by the same index value. So file_myfile=42 and filename_myfile=/myfolder/myfile.json designates a json file containing 42 of relative path /myfolder/myfile.json.

  • A filename parameter without an associated file parameter will create an empty file (but you can also indicate an empty file parameter value)

  • If a file parameter does not have an associated filename, we will use the index to determine the file name if possible. Thus file_test.json=42, without filename_test.json allows to create the file test.json containing the value 42.

  • To create folders, use the dir parameter.

escape

By default, the paths (in the parameters path, file, filename, etc) are relative physical file paths. Paths can be specified as property names of type xjsonpath, using escape=true. In this case, slashes, backslashes and dots must be escaped (by preceding them with a backslash) so as not to be considered as name separators or extensions.

dir

Used with a POST to designate directory paths to be created.

depth

Used with a GET to get the contents of directories: represents the recursion depth.

Write files

POST /api/portalconfig/tools/file

Delete files

DELETE /api/portalconfig/tools/file

List files (test existence and list content)

GET /api/portalconfig/tools/file

Raw configuration file manipulation services

Paramètres en commun

  • storage (non usité pour résolution)
    le nom d’un storage (project ou customer, project par défaut)

  • path (seulement en export)
    un chemin (filtre), valeurs multiples

  • pathMatcher (seulement en export)
    un filtre (exemple: pathMatcher=backup_portal@club-wed**/*.json)

  • depth (seulement en export)
    la profondeur (le nombre de niveaux) d’exploration du filesystem (defaut: -1 soit infinie)

  • escape
    indique le type d'échappement à appliquer aux noms/chemin
    deux valeurs possibles

    • NONE par défaut, les noms sont des noms de fichiers, les chemins sont des chemins de fichiers (/ ou \ comme séparateurs,

    • XJSONPATH: les noms sont des noms de propriétés XJSON, les chemins sont des chemins de propriétés (XJSONPATH), avec slashes ou backslashes en séparateurs de noms, échappement des slashs, backslashs ou dot en précédent d’un backslash

  • flat: (seulement en export) boolean (défaut false) si true : on exporte des chemins (tous les dossiers sont des objets vides), si false: on exporte une arbo d’objets JSON.

  • body, ou propriété json : (en import ou pour resolve), la structure de fichiers (à écrire ou à convertir en configuration), qui correspond à celle qu’on obtient par export

Export storage file set

POST /api/portal/tools/file/export

...

On peut choisir d’obtenir une vision arborescente (dans ce cas les noms de propriétés sont des noms de fichiers (pas de chemins), ou une vision à plat (dans ce cas les noms de propriétés sont des chemins (des noms séparés par des slashes).

Import storage file set

POST /api/portal/tools/file/import

Ce service permet de créer une arborescence de fichiers dans un storage à partir d’un JSON qui correspond au format d’export, en mode arborescent ou à plat, ou les deux combinés. Seules les valeurs de type string sont considérées pour les fichiers et les valeurs de type objet constituent des dossiers.

Resolve configuration from file set

POST /api/portal/tools/file/resolve

Ce service prend en paramètre le même type de JSON que le service d’import. Il permet d’obtenir la configuration qu’on obtiendrait avec si ces fichiers étaient écrit dans un storage (sans fusion avec aucune base ou autre couche de configuration).

Links

The “Urls” page of the plugin gives access to the following links:

  • See the main log of the plugin

  • See the log concerning the access to the cache

  • See the security log

  • Debug Toolkit

  • Cache debugging

  • API Documentation (ReDoc)

  • Services
    The list of services (the available information is obtained from the service inventory).

JSP

Include configuration in your own JSP

You can include the configuration corresponding to a mapping using the URL /_plugins/wxm_config_resolver/page/config.jspz?mappingID=<mappingID>

Debug Toolkit

You can test the different conversion and detection methods via the JSP for testing these methods, at URL /_plugins/wxm_config_resolver/page/toolkit.jspz (login by backoffice user).

...

  1. A page to test the escapement and the different conversions between file names and property names.

  2. A page to test the detection of the type of a text (if the service considers it to be JSON, HTML or text).

  3. A page allowing to see the files and folders contained in the different configuration layers, and how these files are considered by the plugin

Escapement tests

This page shows the result of applying different functions to a string. Depending on the case this string can be

...

  • escaping functions
    These are basic character escaping functions for JSon properties when used as filenames in configuration layers (escapement to convert a property name into a json to be used as a file name). The escapeNotSuffix version isolates the suffix (the file extension) and does not escape it (in particular to keep the separating dot).

  • string to name (and name to string) conversion functions.
    For each type of conversion of a String into a Name, we have three conversion functions that allow us to obtain:

    • a String representation

    • a name in a XJson

    • a file name

      In addition, we can see the property name in the final JSon as well as the associated states (wip, inherit and array).

  • case conversion functions

    • kebab to camel calse

    • camel to kebab case

Content type detection

This page allows to see how a text content will be considered by the plugin when it comes to store it in a file (in JSon or plain text)

Folders content

This page allows you to consult all the files currently stored and to have information on these files.

Note

It is a version 0 which does not manage progressive loading : all the tree structure is loaded at once

Cache debugging

This page displays the currently cached files. It allows you to

...