Development guidelines

You are about to write a new S-360 plugin, that's great !

Here are a few rules to follow to help you pass the plugin validation and ease the deployment of your plugin.

Generic concept

A plugin, in S-360, is a specific piece of code allowing S-360 to communicate with an external system.

Writing a plugin in S-360 means implementing a specific plugin API class.

Let us suppose that you plan to create an url shortener plugin using Google (old fashioned) url shortener API.

In S-360, Url Shortener plugin must inherit UrlShortenerPlugin class (in repository STX-Plugin-API-UrlShortener)

Which depends itself of STX-Plugin-API

Plugins are coded and delivered as maven projects.

The examples illustrating the code below can be downloaded here.

Basic plugin class

Your plugin class must be located in a com.secutix.plugin.* directory and inherit the reference class. You also have to implement at least the method getInterfaceType and getInterfaceNameByLanguageCode

package com.secutix.plugin.urlshortener.google;
import java.util.Collections;
import java.util.Map;

public class GoogleUrlShortenerPlugin extends UrlShortenerPlugin {

    @Override
    public String getInterfaceType() {
        return "US_GOOGLE";
    }

    @Override
    public Map<String, String> getInterfaceNamesByLanguageCode() {
        return ImmutableMap.of("en", "Google url shortener plugin","fr", "Plugin url shortener Google");
    }

}

Implementing plugin behavior

In order to implement plugin behavior you either override your abstract plugin method, either, if you to be able to run batch recurring processes from S-360, implement batch methods (see below).

To implement an URL shortener plugin, for example, you have to override shorten Url.

package com.secutix.plugin.urlshortener.google;
import com.secutix.plugin.v1_0.dto.PluginCallResult;
import java.util.Collections;
import java.util.Map;

public class GoogleUrlShortenerPlugin extends UrlShortenerPlugin {

    @Override
    public String getInterfaceType() {
        return "US_GOOGLE";
    }

    @Override
    public Map<String, String> getInterfaceNamesByLanguageCode() {
        return ImmutableMap.of("en", "Google url shortener plugin","fr", "Plugin url shortener Google");
    }

    @Override
    public ShortUrlCallResult shortenUrl(String longUrl) {
        return PluginCallResult.addFailure(new ShortUrlCallResult(),"not.implemented","Feature not yet implemented");
    }

}

Accessing parameters

In the plugin all the parameters and context values are found through InterfaceParameterProvider helper.

This helper offers some convenience methods (like getUrl, getLogin, getPassword) allowing to retrieve setup value as put in S-360 screens.

More generic methods allow to access all kind of parameters.

    @Override
    public ShortUrlCallResult shortenUrl(String longUrl) {
        //Validating the parameters.
        String url = getInterfaceParametersProvider().getUrl();
        String apiKey = getInterfaceParametersProvider().getPassword();
        if(Strings.isNullOrEmpty(url)){
            return PluginCallResult.addFailure(new ShortUrlCallResult(),"invalid.parameter","No URL defined");
        }

        if(Strings.isNullOrEmpty(apiKey)){
            return PluginCallResult.addFailure(new ShortUrlCallResult(),"invalid.parameter","No API Key defined");
        }

        return PluginCallResult.addFailure(new ShortUrlCallResult(),"not.implemented","Feature not yet implemented");

    }

Logging

Logging must be also done through specific helpers.

Either getLogger for plain application logging.

Or OperatorInternationalizedLogger for logging intended at the operator. This logger can be accessed through helper method logForOperator

    @Override
    public ShortUrlCallResult shortenUrl(String longUrl) {
        //Validating the parameters.
        String url = getInterfaceParametersProvider().getUrl();
        String apiKey = getInterfaceParametersProvider().getPassword();
        if (Strings.isNullOrEmpty(url)) {
            logForOperator(LogLevel.ERROR, "[en] url not defined. Please define one",
            "[fr] url non définie. Veuillez en définir une.");
            return PluginCallResult.addFailure(new ShortUrlCallResult(), "invalid.parameter", "No URL defined");
        }

        if (Strings.isNullOrEmpty(apiKey)) {
            logForOperator(LogLevel.ERROR, "[en] ApiKey not defined. Please store your google ApiKey in password field.",
            "[fr] ApiKey non définie. Veuillez enregistrer votre clef d'Api Google dans le champ mot de passe.");
            return PluginCallResult.addFailure(new ShortUrlCallResult(), "invalid.parameter", "No API Key defined");
        }

        getLogger().debug("All parameters properly defined");
        return PluginCallResult.addFailure(new ShortUrlCallResult(), "not.implemented", "Feature not yet implemented");
    }

Accessing the outside world

All connections to systems outside S-360 must be done using provided S-360 helpers: httpClientHelper for http calls, JaxWsClientHelper for SOAP JaxWs etc.

String urlWithApiKey = url + "?key=" + apiKey;
//urlWithApiKey = url ;
Map<String, String> values = ImmutableMap.of("longUrl", longUrl);
try {
    StxPluginHttpResponse res = getHttpClientHelper().doPost(urlWithApiKey, values, ImmutableMap.of("Content-Type","application/json"));
    if (res.getStatusCode() != StxPluginHttpResponse.HTTP_200) {
        logForOperator(LogLevel.ERROR, "[en] Failed shortening url :" + longUrl + " " + res, "[fr] Erreur en raccourcissant l'url :" + longUrl + " " + res);
        return PluginCallResult.addFailure(new ShortUrlCallResult(), "http.error", "" + res);
    }
    Map<String, String> responseValues = jsonMapper.readValue(res.getResponseData(), Map.class);
    if (!responseValues.containsKey(ID)) {
        logForOperator(LogLevel.ERROR, "[en] Failed shortening url :" + longUrl + ". Response does not contain any id. " + res.getResponseData(), "[fr] Erreur en raccourcissant l'url :" + longUrl + ". La réponse ne contient pas la clef id. " + res.getResponseData());
        return PluginCallResult.addFailure(new ShortUrlCallResult(), "invalid.response", "" + res.getResponseData());
    }
    String shortenedUrl = responseValues.get(ID);
    getLogger().info("" + longUrl + " -> " + shortenedUrl);
    ShortUrlCallResult finalResult = new ShortUrlCallResult();
    finalResult.setShortUrl(shortenedUrl);
    return PluginCallResult.addSucess(finalResult,"");
} catch (Exception e) {
    logForOperator(LogLevel.ERROR, "[en] Technical error when calling google APIs:" + e.getMessage(),
    "[fr] Erreur technicque en appelant l'API google:" + e.getMessage());
    getLogger().error("Error when calling google APIs.", e);
    return PluginCallResult.addFailure(new ShortUrlCallResult(), "technical error", "" + e.getMessage());
}

Importing external libraries in plugin pom

The STX-Plugin-API is referencing last version of Guava, Jackson and some apache utilities libraries.

We advise you to strongly limit the usage of external libraries. You may add a dependency in the pom of your project, but S-360 team will have to approve it.

FAQ

How is it possible to access data/persist data from inside a plugin ?

It is not possible to access directly any dao layer. All integrations must be done through the helpers injected inside the plugin.

Writing tests

A plugin must be delivered with a set of automated JUnit tests.

JUnit simple tests

Those ones do not connect to the outside world. All used connectors (like HttpClientHelper) must be mocked.

Their name must contain the word Test

In the example code, see TestGoogleUrlShortenerPlugin

One test must check that the validate() method of the plugin is not throwing any exception

JUnit validation tests

Those ones do can connect to the outside world.

Their name must not contain the word Test

In the example code, see ValidateGoogleUrlShortenerPlugin

Defining parameters

If you need specific, named parameters to be displayed and filled in S-360 screens, please override the method

listParameterDefinitions

The parameters defined this way can be accessed through the InterfaceParametersProvider

[]: https://bitbucket.svc.elca.ch/projects/SECUTIX-PLUGINS/repos/STX-Plugin-API-UrlShortener