Skip to content

Tutorial Talend Component Kit 7: Implement "Advanced Settings" of a Jira input component

Simon Neidig edited this page Dec 11, 2019 · 2 revisions

Unfortunately the documentation for the implementation of the "Advanced Settings" of a component with the help of the "Talend Component Kit" is not available. Neither Talend itself nor 3rd parties provide an explanation on how to implement the "Advanced Settings".

We have found out and explain our solution step by step in the following tutorial! The aim of this part of the tutorial is to provide a setting option for experienced Jira API users to use our Jira input component to perform so-called "JQL" queries.

Fig. 1: Final selection list

After this tutorial you can:

  • implement the advanced settings of a "Talend Component Kit"-component.

1. Layout Definition in the Dataset

The core element of the "Advanced Settings" is a second, separate layout. This layout can be defined separately from the general layout, only it has to be declared correctly. To do this, you have to create a second layout in the dataset class (to be found under "src→main→java→de.odisys.talend.components→dataset→CustomDataset") below the existing GridLayout, with the attribute "names = GridLayout.FormType.ADVANCED" in the annotation. This indicates that it is the layout of the "Advanced Settings".

@DataSet("CustomDataset")
@GridLayout({
        @GridLayout.Row({"datastore"}),
        @GridLayout.Row({"projectId"}),
        @GridLayout.Row({"status"})
})
@GridLayout(names = GridLayout.FormType.ADVANCED, value = {
        @GridLayout.Row({"jql"})
})

Once the layout is defined, the attributes must be declared as usual and their getters and setters implemented. In our case we implement a "@TextArea" "jql", so that the following dataset class results:

@DataSet("CustomDataset")
@GridLayout({
        @GridLayout.Row({"datastore"}),
        @GridLayout.Row({"projectId"}),
        @GridLayout.Row({"status"})
})
@GridLayout(names = GridLayout.FormType.ADVANCED, value = {
        @GridLayout.Row({"jql"})
})
@Documentation("")
public class CustomDataset implements Serializable {
    @Option
    @Documentation("")
    private CustomDatastore datastore;

    @Option
    @TextArea
    @Documentation("")
    private String projectId;

    @Option
    @Proposable("valuesProvider")
    @Documentation("")
    private String status;

    @Option
    @TextArea
    @Documentation("")
    private String jql = null;

    public CustomDataset(CustomDatastore datastore, String projectId, String status, String jql) {
        this.datastore = datastore;
        this.projectId = projectId;
        this.status = status;
        this.jql = jql;
    }

    public CustomDataset() { }
    public CustomDatastore getDatastore() { return datastore; }
    public String getProjectId() { return projectId; }
    public String getStatus() { return status; }
    public String getJql() { return jql; }
    public void setJql(String jql) { this.jql = jql; }
    public void setStatus(String status) { this.status = status; }
    public void setDatastore(CustomDatastore datastore) { this.datastore = datastore; }
    public void setProjectId(String projectId) { this.projectId = projectId; }
}

Don't forget: At least the display name ("_displayName") must be declared in the properties file under "src→main→ressources→de.odisys.talend.components→dataset→Messages.properties" for the new attribute!

CustomDataset.datastore._displayName=Datastore
CustomDataset.projectId._displayName=Project Id
CustomDataset.status._displayName=Status
CustomDataset.jql._displayName=JQL

2. Layout-Definition in the Configuration

Once the new layout has been declared in the dataset, it must also be added to the parent class, the "Configuration". For this purpose, a new layout with the definition "names = GridLayout.FormType.ADVANCED" must be created equivalent to the declaration in the dataset and the dataset hast to be inserted as a line.

@GridLayout({
        @GridLayout.Row({"dataset"})
})
@GridLayout(names = GridLayout.FormType.ADVANCED, value = {
        @GridLayout.Row({"dataset"})
})

The remaining "Configuration" class remains unchanged:

@GridLayout({
        @GridLayout.Row({"dataset"})
})
@GridLayout(names = GridLayout.FormType.ADVANCED, value = {
        @GridLayout.Row({"dataset"})
})
@Documentation("")
public class OdiSysInputMapperConfiguration implements Serializable {
    @Option
    @Documentation("")
    private CustomDataset dataset;

    public CustomDataset getDataset() {
        return dataset;
    }
    public void setDataset(CustomDataset dataset) { this.dataset = dataset; }
}

3. Processing of user input in the Source

Now the "Advanced Settings" are implemented, but the processing of the user inputs is still missing. Here we want, if the user specifies a JQL-Querry, to execute it, if he does not specify one, to process the data in the general layout. Thus we extend our "@Producer" in the Source class under "src→main→java→de.odisys.talend.components→source→OdiSysInputSource" by an if condition.

Either the input for JQL is "null", i.e. the user has not made an entry, then we assemble the query as usual and filter the results afterwards (therefore the Boolean "filter" is set to "true"). Alternatively the user makes a specification for JQL, then we append this query to the base URL of Jira, execute the query and do not filter at the end ("filter" is set to "false").

@Producer
public Record next() {
    if (!executed) {
        String resString = "";
        boolean filter;
        String requestUrl = configuration.getDataset().getDatastore().getUrl() + "/rest/api/2/search?jql=";
        if (configuration.getDataset().getJql() == null) {
            requestUrl += "project=" + configuration.getDataset().getProjectId();
            filter = true;
        } else {
            requestUrl += configuration.getDataset().getJql();
            filter = false;
        }
        try {
            HttpResponse<String> res = Unirest.get(requestUrl)
                    .basicAuth(configuration.getDataset().getDatastore().getUsername(),
                            configuration.getDataset().getDatastore().getPassword())
                    .asString();
            resString = res.getBody();
        } catch (UnirestException e) {
            e.printStackTrace();
        }
        executed = true;

        resString = filterStatus(resString, filter);

        return builderFactory.newRecordBuilder().withString("data", resString).build();
    }
    return null;
}

We extend the method "filterStatus()" defined in the previous tutorial by a Boolean value "filter", with which we can determine whether the filter operation is executed or skipped. It should be skipped if the user enters a JQL-Querry.

public String filterStatus(String resString, boolean filter) {
    if (!filter || configuration.getDataset().getStatus().equals("All")){
        return JsonPath.read(resString, "$.issues[*]").toString();
    } else {
        return JsonPath.read(resString, "$.issues[?(@.fields.status.statusCategory.name =~ /.*" + configuration.getDataset().getStatus() + "/i)]").toString();
    }
}

After the changes the following source class results:

@Documentation("")
public class OdiSysInputSource implements Serializable {
    private final OdiSysInputMapperConfiguration configuration;
    private final OdiSysService service;
    private final RecordBuilderFactory builderFactory;
    private boolean executed = false;

    public OdiSysInputSource(@Option("configuration") final OdiSysInputMapperConfiguration configuration,
                             final OdiSysService service,
                             final RecordBuilderFactory builderFactory) {
        this.configuration = configuration;
        this.service = service;
        this.builderFactory = builderFactory;
    }

    @Producer
    public Record next() {
        if (!executed) {
            String resString = "";
            boolean filter;
            String requestUrl = configuration.getDataset().getDatastore().getUrl() + "/rest/api/2/search?jql=";
            if (configuration.getDataset().getJql() == null) {
                requestUrl += "project=" + configuration.getDataset().getProjectId();
                filter = true;
            } else {
                requestUrl += configuration.getDataset().getJql();
                filter = false;
            }
            try {
                HttpResponse<String> res = Unirest.get(requestUrl)
                        .basicAuth(configuration.getDataset().getDatastore().getUsername(),
                                configuration.getDataset().getDatastore().getPassword())
                        .asString();
                resString = res.getBody();
            } catch (UnirestException e) {
                e.printStackTrace();
            }
            executed = true;

            resString = filterStatus(resString, filter);

            return builderFactory.newRecordBuilder().withString("data", resString).build();
        }
        return null;
    }

    public String filterStatus(String resString, boolean filter) {
        if (!filter || configuration.getDataset().getStatus().equals("All")){
            return JsonPath.read(resString, "$.issues[*]").toString();
        } else {
            return JsonPath.read(resString, "$.issues[?(@.fields.status.statusCategory.name =~ /.*" + configuration.getDataset().getStatus() + "/i)]").toString();
        }
    }
}