Versions Compared

Key

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

Overview

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.

...

  • _portal

  • _portal@config

  • _portal@config@picker

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

...

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

La configuration du plugin WXM_CONFIG_RESOLVER est

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 service GET /api/portalconfig/mappings (liste des mappings) retourne :

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

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"
		]
	}
]

Je créé un plugin de contribution WXM_CONFIG_RESOLVER_GPP tel que

📁 WXM_CONFIG_RESOLVER_GPP

📁 config

📄 config.xml

📄 config-resolver-mappings.json

📄 config-resolver-postprocessors.json

📁 res

📁 gpp

📄 CustomProcessor.groovy

📄 CustomProcessorWithConfig.groovy

(le plugin d’exemple contient d’autres processors groovy non utilisés dans cet exemple)

View file
nameWXM_CONFIG_RESOLVER_GPP.zip

Le fichier config-resolver-mappings.json définit des mappings supplémentaires :

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

Le fichier config-resolver-postprocessors.json définit des postprocessors supplémentaires :

Code Block
languagejson
{
	"gppProcessor": {
		"processor": "/res/gpp/CustomProcessor.groovy"
	},
	"gppProcessorWithConfig": {
		"processor": "/res/gpp/CustomProcessorWithConfig.groovy",
		"config": {
					"values": ["x","y" ]
		}
	}
}

On utilise ici la syntaxe “objet” sans propriété “plugin”, ce qui détermine que le fichier groovy sera cherché dans ce plugin (WXM_CONFIG_RESOLVER_GPP), avec un path relatif à sa racine.

On installe et active ce plugin et on redémarre 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"
]

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": "gppprocessor",
		"mappings": [
			"mapping_GPP_2",
			"mapping_GPP_3"
		],
		"values": [
			"V1",
			"V2",
			"V3",
			"V4",
			"V5"
		]
	},
	{
		"name": "gppprocessorwithconfig",
		"mappings": [
			"mapping_GPP_3"
		],
		"values": [
			"x",
			"y"
		]
	}
]

Standard postprocessors

Surfer role

...

You must implement two methods:

  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()

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).

...

You can also take control of the configuration decoding by adding to your class a setter called setConfig (void return type and only one argument).

  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.

...

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.

...

It is a JSON object (It can be a string of characters. In this case, it will automatically be the mapping type and the string is the mapping ID). The type field indicates the type of descriptor. Here are the different values for type:

  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.

...

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.

...

This page provides access to three testing tools (do not use the associated services as API):

  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

...

** Procédure d’installation version test

  1. j’ai créé un plugin wxm_config_test_portal

  2. dans le dossier config, j’ai créé un config_resolver/bases

  3. j’ai copié _portal.json dedans

dans wxm_config_resolver, dans config_settings, j’ai ajouté :

...