Hello! Alexey Matveev here.

Let’s talk about SIL®. SIL® or Simple Issue Language®, is a programming language for automating actions in Atlassian Jira and Confluence. You can find more information about SIL® here

I’m fluent in SIL®, and want to show you how to write clean SIL® code.

First, we’ll discuss my rules on writing clean SIL® code. Then I’ll walk you through an example of SIL® code, and refactor it to make it clean using the rules.


Rules

  1. Start structs names with an upper case letter.
  2. Add elements to an array with addElement routine.
  3. Use user-defined routines to make your code readable and avoid comments in your code.
  4. Move common functions to inclusions.
  5. Declare structs before any other variable declarations.
  6. Give structs and other variables meaningful names, so that you would not need to comment on your code.
  7. Name functions and variables according to the Google Java style guide.

Here is an example of SIL® code:

string USER = currentUser();
// Response
struct returnData {
    string status;
}
// Project
struct space {
    string key;
}
// Inner part with content
struct storage {
    string value;
    string representation;
}
// Part for storage
struct body {
    storage storage;
}
// Main entity for sending to Confluence
struct reqData {
    string type;
    string title;
    space space;
    body body;
}


reqData data;
data.type = "page";
data.title = "Page for issue " + key + "  " + summary + ".";
data.space.key = project;
data.body.storage.value = "<p> Author:"+userFullName(USER) + " description: "  + description + "</p>";
data.body.storage.representation = "storage";
// Create request
HttpRequest request;
HttpHeader header = httpCreateHeader("Content-Type", "application/json");
HttpHeader authHeader = httpBasicAuthHeader("admin", "admin");
request.headers += header;
request.headers += authHeader;
logPrint("WARN", request);
//POST
string JSONData = toJson(data);
logPrint("WARN", JSONData);
returnData result = httpPost("http://192.168.54.203:8090/rest/api/content/", request, JSONData);
string errMsg = httpGetErrorMessage();
logPrint("ERROR", "Last error message: " + errMsg);
logPrint("WARN", result);
string COMMENT = "Page created in Confluence " + updated + " by " + userFullName(USER) + " Status : " +result.status + "."; 
addComment(key, USER, COMMENT);

//Return Status
return result.status;

This code is difficult to understand, and comments don’t make your job any easier. Let’s try to modify this code to make it more readable using the rules.

Have a look at the structs:

struct body {
    storage storage;
}

You see storage storage looks a bit confusing here. It would be more meaningful if it were like this:

struct body {
    Storage storage;
}

Now it’s obvious that we need to define the storage variable with the Storage type. To do this, we need to start the names of all structs with the upper case:

// Response
struct ReturnData {
    string status;
}
// Project
struct Space {
    string key;
}
// Inner part with content
struct Storage {
    string value;
    string representation;
}
// Part for storage
struct Body {
    Storage storage;
}
// Main entity for sending to Confluence
struct ReqData {
    string type;
    string title;
    Space space;
    Body body;
}

And let’s name our structs so that we don’t need comments:

struct CreateConfluencePageResponse {
    string status;
}
struct Space {
    string key;
}
struct Storage {
    string value;
    string representation;
}
struct Body {
    Storage storage;
}
struct CreateConfluencePageRequest {
    string type;
    string title;
    Space space;
    Body body;
}

We’ve now completed Rules 1, 5, 6. As you can see, these rules make the names clear and concise to understand the structure.

Next, put the struct declaration at the start of the code. See the following code:

string USER = currentUser();
CreateConfluencePageRequest data;
data.type = "page";
data.title = "Page for issue " + key + "  " + summary + ".";
data.space.key = project;
data.body.storage.value = "<p> Author:"+userFullName(USER) + " description: "  + description + "</p>";
data.body.storage.representation = "storage";

 

// Create request
HttpRequest request;
HttpHeader header = httpCreateHeader("Content-Type", "application/json");
HttpHeader authHeader = httpBasicAuthHeader("admin", "admin");
request.headers += header;
request.headers += authHeader;
logPrint("WARN", request);
//POST
string JSONData = toJson(data);
logPrint("WARN", JSONData);
CreateConfluencePageResponse result = httpPost("http://192.168.54.203:8090/rest/api/content/", request, JSONData);
string errMsg = httpGetErrorMessage();
logPrint("ERROR", "Last error message: " + errMsg);
logPrint("WARN", result);
string COMMENT = "Page created in Confluence " + updated + " by " + userFullName(USER) + " Status : " +result.status + "."; 
addComment(key, USER, COMMENT);

//Return Status
return result.status;

At first glance, it’s difficult to quickly grasp the idea of the code. Let’s try to split the code into blocks:

getNewConfluencePageContent();
createHttpRequest();
createConfluencePage();
addCommentToJiraIssue();

These are the major blocks in our SIL® code. Let’s create all these functions:

function getNewConfluencePageContent() {
    CreateConfluencePageRequest data;
    data.type = "page";
    data.title = "Page for issue " + key + "  " + summary + ".";
    data.space.key = project;
    data.body.storage.value = "<p> Author:"+userFullName(currentUser()) + " description: "  + description + "</p>";
    data.body.storage.representation = "storage";
    return toJson(data);
}

function createHttpRequest() {
    HttpRequest request;
    HttpHeader header = httpCreateHeader("Content-Type", "application/json");
    HttpHeader authHeader = httpBasicAuthHeader("admin", "admin");
    request.headers = addElement(request.headers, header);
    request.headers += addElement(request.headers, authHeader);
    logPrint("WARN", request);
    return request;
}

function createConfluencePage(string pageJson) {
    HttpRequest request = createHttpRequest();
    CreateConfluencePageResponse result = httpPost("http://192.168.54.203:8090/rest/api/content/", request, pageJson);
    logPrint("ERROR", "Last error message: " + httpGetErrorMessage());
    logPrint("WARN", result);
    return result;
}

function addCommentToJiraIssue(string resultStatus) {
    string COMMENT = "Page created in Confluence " + updated + " by " + userFullName(currentUser()) + " Status : " +resultStatus + "."; 
    addComment(key, currentUser(), COMMENT);
}



string pageJson = getNewConfluencePageContent();
CreateConfluencePageResponse result = createConfluencePage(pageJson);
addCommentToJiraIssue(result.status);
return result.status;

So now we have a couple of functions (Rule 3 completed), and a set of events, making it much easier to understand:

string pageJson = getNewConfluencePageContent();
CreateConfluencePageResponse result = createConfluencePage(pageJson);
addCommentToJiraIssue(result.status);
return result.status;

We now have the contents of the new page. If we want to narrow down to the exact contents, we can look at the getNewConfluencePageContent function and find out what the contents are. We can create a Confluence page from a JSON, add comments to the Jira issues, and return the status of the response from Confluence.

Let’s create a Confluence page in more than one SIL® script. Move out the Confluence create page function into a reusable module.

Create a file called confluence_helper.incl with contents like this:

struct CreateConfluencePageResponse {
    string status;
}
struct Space {
    string key;
}
struct Storage {
    string value;
    string representation;
}
struct Body {
    Storage storage;
}
struct CreateConfluencePageRequest {
    string type;
    string title;
    Space space;
    Body body;
}

function createHttpRequest() {
    HttpRequest request;
    HttpHeader header = httpCreateHeader("Content-Type", "application/json");
    HttpHeader authHeader = httpBasicAuthHeader("admin", "admin");
    request.headers = addElement(request.headers, header);
    request.headers += addElement(request.headers, authHeader);
    logPrint("WARN", request);
    return request;
}

function createConfluencePage(string confluenceUrl, string pageJson) {
    HttpRequest request = createHttpRequest();
    CreateConfluencePageResponse result = httpPost(confluenceUrl, request, pageJson);
    logPrint("ERROR", "Last error message: " + httpGetErrorMessage());
    logPrint("WARN", result);
    return result;
}

We’ve finished the structs and functions, which are needed to create a Confluence page, and made the createConfluencePage function more generic by adding a new parameter (confluenceUrl) to it. Rule 4 completed!

Now our SIL® code looks like this:

include "confluence_helper.incl";

function getNewConfluencePageContent() {
    CreateConfluencePageRequest data;
    data.type = "page";
    data.title = "Page for issue " + key + "  " + summary + ".";
    data.space.key = project;
    data.body.storage.value = "<p> Author:"+userFullName(currentUser()) + " description: "  + description + "</p>";
    data.body.storage.representation = "storage";
    return toJson(data);
}

function addCommentToJiraIssue(string resultStatus) {
    string comment = "Page created in Confluence " + updated + " by " + userFullName(currentUser()) + " Status : " +resultStatus + "."; 
    addComment(key, currentUser(), comment);
}


const string CONFLUENCE_URL = "http://192.168.54.203:8090/rest/api/content/";

string pageJson = getNewConfluencePageContent();
CreateConfluencePageResponse result = createConfluencePage(CONFLUENCE_URL, pageJson);
addCommentToJiraIssue(result.status);
return result.status;

Save the Confluence URL in a constant variable and modified variable names according to the Google Java notation style. Rule 7 completed.

Keep practicing these rules, and you’ll master SIL® coding in no time.

 

 

 

ALEXEY MATVEEV
|  Software Developer for Cprime Apps

 

 

 

 

 



Tagged with: , , ,

Categorized in: , , , ,