Writing a Task with a User Interface

Available:

Bamboo 3.1 and later

The Bamboo "Hello World" plugin is the example Bamboo plugin that ships with the Atlassian Plugin SDK default project. In this tutorial we break down the plugin and explain how to build a User Interface and add validation.

Create a new plugin

  1. First download the copy of the latest Plugin SDK and install it on your machine.
  2. Run atlas-create-bamboo-plugin from the command line.
  3. When asked for the groupId, artifactId and package just enter "helloworld" and leave the other options as the defaults.

What is the Hello World plugin?

The "Hello World" plugin is the example plugin from the SDK that demonstrates how to build a Task plugin with a User Interface, validation and internationalisation. The plugin is very simple. It allows a user in Bamboo to configure the Task so that it prints a user configured value to the log and returns a successful TaskResult.

Try it out

To try the default Hello World plugin out run the atlas-run command from the root of the project you generated when you started the tutorial. Once Bamboo is up, create a new Plan with a "None" repository type and configure the Job to contain the "Hello World" Task.

Try removing the default value in the "Say" field and saving. You should see a validation error like the one below:

Thats the validation we have defined in the Configurator. We will show you how to add your own validation in a moment. Change the "Say" field value to be "Hello, World!" then save the task then run your plan. Once the plan has completed running you should see the plugin executing and printing "Hello, World!" in the default Jobs logs

Breakdown of the plugin

We will now break down the plugin into its components and explain how its put together.

TaskType

We covered what a TaskType is and how it works in the Introduction to Tasks tutorial. This Task takes the a value out of the configuration map using the key "say" and adds a new entry with its value to the build log and returns a successful TaskResult.

From ExampleTask.java

public class ExampleTask implements TaskType
{
    @NotNull
    @java.lang.Override
    public TaskResult execute(@NotNull final TaskContext taskContext) throws TaskException
    {
        final BuildLogger buildLogger = taskContext.getBuildLogger();

        final String say = taskContext.getConfigurationMap().get("say");

        buildLogger.addBuildLogEntry(say);

        return TaskResultBuilder.create(taskContext).success().build();
    }
}

Templates

Lets have a look at the Hello World plugins templates. Bamboo uses the WebWork web framework and the Freemarker templating language to render its user interface. There are numerous tags available from WebWork that can be used from within freemarker to map values in the Configurator to elements in the user interface (we will talk more about the Configurator in a moment).

From editExampleTask.ftl

[@ww.textfield labelKey="helloworld.say" name="say" required='true'/]

This template provides the configuration form for the Task. We use a textField here to provide input. The labelKey value helloworld.say maps to a property defined in the english.properties file that provides the translation and the name specifies what the value of the textField should be in the Configurator. In this case we always want the user to specify a value for the field so we mark it with the required attribute with the value set to true.

TaskConfigurator

So whats a TaskConfigurator? A TaskConfigurator is a class that controls what objects and values are available when rendering the User Interface, how input is persisted and validated.

In our example we use the AbstractTaskConfigurator class instead of the TaskConfigurator interface as its possible you may not need to implement all of its interface members.

Saving configuration inputs

To save your configuration you need to override or implement the generateTaskConfigMap method on your TaskConfigurator.

From ExampleTaskConfigurator.java

public Map<String, String> generateTaskConfigMap(@NotNull final ActionParametersMap params, @Nullable final TaskDefinition previousTaskDefinition)
{
    final Map<String, String> config = super.generateTaskConfigMap(params, previousTaskDefinition);

    config.put("say", params.getString("say"));

    return config;
}

When the user saves the Task the generateTaskConfigMap method is called and a ActionParametersMap and TaskDefinition is provided.

The ActionParametersMap contains all the form parameter keys and values from the input fields of the editExampleTask.ftl. The TaskDefinition is the saved state of the Task within Bamboo. It contains its user description and its map of configuration key/values. One thing to note is that if the Task is being created the TaskDefinition instance here is null but if it is being edited it contains the previous state of the Task before the user changes it.

Remember the field named "say" in the editExampleTask.ftl? Well the value set by the user for this field is available in the ActionParametersMap. In order to persist the value in a new TaskDefinition we must return a new map that contains this value. For simplicity sake we use the same key for this value as was used as the name of the field.

Validating configuration inputs

From ExampleTaskConfigurator.java

public void validate(@NotNull final ActionParametersMap params, @NotNull final ErrorCollection errorCollection)
{
    super.validate(params, errorCollection);

    final String sayValue = params.getString("say");
    if (StringUtils.isEmpty(sayValue))
    {
        errorCollection.addError("say", textProvider.getText("helloworld.say.error"));
    }
}

Before the generateTaskConfigMap is called the ActionParametersMap is validated to ensure it contains the correct values needed to create the new TaskDefinition in generateTaskConfigMap. Validation is simple. If the field does not pass your Tasks validation rules you simply call addError on the ErrorCollection with the name of the field that did not pass validation and a message about why it failed.

Populate for Create and Edit

In this method you can set the default configuration of your Task when it is being created by populating the given context map with the key of the field and the value you want it to contain.

From ExampleTaskConfigurator.java

@Override
public void populateContextForCreate(@NotNull final Map<String, Object> context)
{
    super.populateContextForCreate(context);

    context.put("say", "Hello, World!");
}

Once your Task has been created the populateContextForEdit can be used to show the configured values of the Task. Simply populate the given context map with the values stored in the map on the TaskDefinition (These are the values you set to a new map from the ActionParametersMap in the generateTaskConfigMap method). Like populateContextForCreate, the keys of the context map should match the field names in your templates.

From ExampleTaskConfigurator.java

@Override
public void populateContextForEdit(@NotNull final Map<String, Object> context, @NotNull final TaskDefinition taskDefinition)
{
    super.populateContextForEdit(context, taskDefinition);

    context.put("say", taskDefinition.getConfiguration().get("say"));
}

Registering a Configurator and associated Templates with the Plugin system

From atlassian-plugin.xml:

<taskType name="helloworld" class="helloworld.ExampleTask" key="test">
  <description>A simple Hello World Task</description>
  <!-- Categories available in 3.1: "builder", "test" and "deployment"
  <category name=""/>
  -->
  <configuration class="helloworld.ExampleTaskConfigurator"/>
  <resource type="freemarker" name="edit" location="editExampleTask.ftl"/>
</taskType>

Internationalisation and TextProvider

In our example we use the injected TextProvider in ExampleTaskConfiguratior.java to load the helloworld.say.error string from the english.properties internationalisation file when validation fails.

To register the english.properties with the plugin system add a line to the atlassian-plugin.xml like so:

From atlassian-plugin.xml:

<resource type="i18n" name="helloworld language" location="english"/>
Was this page helpful?

Have a question about this article?

See questions about this article

Powered by Confluence and Scroll Viewport