Using VisualScript's JavaScript Report Builder

Introduction

VisualScript lets you create, store, and edit scripts directly from the gadget or macro you're using to display the output. This tutorial will cover the following topics to help you become familiar with VisualScript:

  1. Add the VisualScript JavaScript Report Builder gadget to your page
  2. Familiarize yourself with the editor
  3. Add a new script to the editor
  4. Monitor the script's execution using a web browser debugger
  5. Modify the sample script to produce the desired results
  6. Add a user interface
  7. Script management

Add the VisualScript Editor Gadget

The first step is to add the VisualScript gadget to your dashboard.

To begin, go to the dashboard you wish to use. If you don't already have a dashboard set up, you can create one using the "Manage Dashboards" option in Jira's "Dashboard" menu item:

Jira dashboard dropdown

On the dashboard, use the "add a new gadget" link to open the gadget browser.

Add new gadget

The "Add a gadget" dialog contains a list of gadgets that have already been loaded, but you'll almost certainly need to click the "Load All Gadgets" link in the "More Gadgets Available" box.

Gadget dialog

After you click the "Load all Gadgets" link, a scrolling list of gadgets will appear. You can either scroll down to the list of VisualScript gadgets, or you can type "VisualScript" into the search/filter box in the top left area of the list. You'll want to choose "VisualScript Editor" to add the gadget that will give you a custom scripting environment where you can write your own code.

Gadgets list

The VisualScript JavaScript Report Builder gadget is now set up on your dashboard and ready for you to begin configuring it. Click the "Edit" button to open the builder.

Edit script

An Overview of the JavaScript Report Builder

To create a report, click the "Edit" button on your VisualScript gadget and launch the Report Builder.

Gadget ready to configure

The interface has four main sections:

Code Editor

The Code Editor is a feature-rich JavaScript editor in which you will create and modify your script's code. It provides many convenient and familiar features of lightweight modern code editors such as syntax highlighting (primarily via color), bracket-matching, auto-complete of brackets, parenthesis and quotation marks. The Code Editor also alerts you to syntactical errors.

Script Information

The Script Information section contains:

  • Script Name: Name of the script as it will appear in the Load Script drop-down.
  • Script Description: Description of the script.
UI Builder

Right under script information section, you have a chance to define parameters you may need to collect from the user. This will help you create a UI where you can ask for issue ID, epic name, or other information needed to build your visual report.

Scripts List

At the bottom section of the editor is a drop-down list of scripts you can use a starting point under "New Script". This list contains the built in scripts you can use as a template. There's a second drop-down list under "Load Script" for all the scripts you may create and save. It is populated as you create scripts using the interface. Selecting a script will load its code in the builder so you can make modifications. When you choose a "New Script" you will first have to name your script before you can save it or run it.

If you choose a script from the list of previously saved scripts under "Load Script", you can either edit the existing script and save your changes or create a duplicate of the script to use as the starting point for a new script.

Add a New Script to the Editor

To begin, add a new script to the editor. This can easily be done by selecting one of the built-in templates under the "New Script" button. This will load an example script into the Code Editor that you can modify. You can start with the "Basic Script".

Add script

You will have to name your script before you can "Save and Run" and see the output. In this, I'm going to name the script "My Basic Script". Hitting "Save and Run" will render a "Hello World" visual.

First script

If you need to edit your script, you can access it using the "Edit" button.

The saved script is also available from other JavaScript Report Builder instances using the "Load Script" drop-down. From here you can modify existing scripts, delete unused ones, or create a duplicate as a new starting point.

Load saved script

Modify the Sample Script

Now that you have a sample script set up and working, you can begin to modify it to achieve the results that you're looking for. Most scripts will follow the pattern:

  1. Issue REST API query to obtain data from Jira or some other source
  2. Process the returned data into a model
  3. Use the VisualScript SDK to transform data into a visual
  4. Tell VisualScript to render the visual

Let's walk through each of these steps as we modify the sample script to produce our desired outcome: a list of Jira issues containing a word or phrase in its summary.

Query for Data

You can use standard JavaScript libraries and techniques to issue REST API calls, but you might find it easier and more convenient to use VisualScript functions to get the data from Jira. For our purposes, we want to issue a query for all issues that have the word "market" in the summary. The JQL (Jira Query Language) for that filter is "summary ~ market". So the first part of our call is:

VSCallFunctionForJiraQuery(localhost, "summary ~ market", <more to come>);

The VisualScript function "VSCallFunctionForJiraQuery" accepts as its first parameter the URL of the Jira instance we need information from. Almost always we will want to use the "localhost" that was passed as the first parameter to the script we're writing. The 2nd parameter is our JQL query, which in our case just filters for the issues that contain the word "market" in its summary.

The next step is to handle the results of the query. The results are returned asynchronously to the function passed as a 3rd argument to VSCallFunctionForJiraQuery. So to handle the response, we create a function named handleQueryResponse and pass it to VSCallFunctionForJiraQuery:

VSCallFunctionForJiraQuery(localhost, "summary ~ market", handleQueryResponse);
 
function handleQueryResponse(issues) {
 
}

Process the Returned Data into a Model

At this point, we have received the results from our query and need to process it into a model of the data we want to visually represent. This processing can range from trivial (which will be our case) to complex designs requiring multiple data structures and multiple asynchronous API calls. To produce our model, we really only need to loop through the list of returned issues, adding the issue's key, summary description and issue type to the model array.

VSCallFunctionForJiraQuery(localhost, "summary ~ market", handleQueryResponse);
 
function handleQueryResponse(issues) {
    var model = [];
     
    // Build the model from the issues
    for (var i = 0; i < issues.length; i++) {
        model.push({key: issues[i].key, summary: issues[i].fields.summary, issueType: issues[i].fields.issuetype.name});
    }
}

Use the VisualScript SDK to Transform Data

Now that we have our model built, we're ready to use that model to produce a visual. After setting up a VisualScript document, we get the main shape, add a shape connector to it, then loop through our model, adding shapes for each entry in our model.

VSCallFunctionForJiraQuery(localhost, "summary ~ market", handleQueryResponse);
 
function handleQueryResponse(issues) {
    var model = [];
 
    // Build the model from the issues
    for (var i = 0; i < issues.length; i++) {
        model.push({key: issues[i].key, summary: issues[i].fields.summary, issueType: issues[i].fields.issuetype.name});
    }
 
    // Build the markup from the model
    var visualScript = buildVisualScript(model);
}
 
function buildVisualScript(model) {
    // Set up the VisualScript document
    var vs = new VS.Document();
     
    // Get the main shape and add a shape connector to it.
    var mainShape = vs.GetTheShape();
    var connector = mainShape.AddShapeConnector();
     
    // Add a shape to the connector for each entry in the model
    for (var i = 0; i < model.length; i++) {
        connector.AddShape()
                    .SetLabel(model[i].key + "\n" + model[i].summary);
    }
     
    return vs;
}

Render the Visual

Finally, we use the callback that was passed to our script to display a visual.

VSCallFunctionForJiraQuery(localhost, "summary ~ market", handleQueryResponse);
 
function handleQueryResponse(issues) {
    var model = [];
 
    // Build the model from the issues
    for (var i = 0; i < issues.length; i++) {
        model.push({key: issues[i].key, summary: issues[i].fields.summary, issueType: issues[i].fields.issuetype.name});
    }
 
    // Build the VisualScript from the model
    var visualScript = buildVisualScript(model);
 
    // Render the VisualScript
    callback(visualScript.toJSON());
}
 
function buildVisualScript(model) {
    // Set up the VisualScript document
    var vs = new VS.Document();
     
    // Get the main shape and add a shape connector to it.
    var mainShape = vs.GetTheShape();
    var connector = mainShape.AddShapeConnector();
     
    // Add a shape to the connector for each entry in the model
    for (var i = 0; i < model.length; i++) {
        connector.AddShape()
                    .SetLabel(model[i].key + "\n" + model[i].summary);
    }
     
    return vs;
}

The entire script in the Code Editor should look like this:

function VisualScript(localhost, params, callback) {
 
    // Issue the query in the params. Calls the handler with the issues list
    VSCallFunctionForJiraQuery(localhost, "summary ~ market ORDER BY created ASC", handleQueryResponse);
 
    function handleQueryResponse(issues) {
        var model = [];
 
       // Build the model from the issues
        for (var i = 0; i < issues.length; i++) {
            model.push({key: issues[i].key, summary: issues[i].fields.summary, issueType: issues[i].fields.issuetype.name});
        }
 
        // Build the VisualScript from the model
        var visualScript = buildVisualScript(model);
 
        // Render the VisualScript
        callback(visualScript.toJSON());
    }
 
    function buildVisualScript(model) {
        // Set up the VisualScript document
        var vs = new VS.Document();
         
        // Get the main shape and add a shape connector to it.
        var mainShape = vs.GetTheShape();
        var connector = mainShape.AddShapeConnector();
 
        // Add a shape to the connector for each entry in the model
        for (var i = 0; i < model.length; i++) {
            connector.AddShape()
                            .SetLabel(model[i].key + "\n" + model[i].summary);
        }
 
        return vs;
    }
}

Presuming you have some sample data in your Jira installation that includes issues with the word "market" in the summary, your results when you run this script should look something like this:

Script result

From here, you're free to tinker with the appearance. The default view is "org chart", but you can very easily display this as a mind map by adding the following statement after the new VS.Document() statement:

vs.SetTemplate(VS.Templates.Mindmap);

While we're tinkering, let's give the root shape a label, make the issues rounded in appearance, and have a specific color for the issue type.

function buildVisualScript(model) {
       // Set up the VisualScript document
       var vs = new VS.Document();
        
       vs.SetTemplate(VS.Templates.Mindmap);
        
       // Get the main shape and add a shape connector to it.
       var mainShape = vs.GetTheShape()
                       .SetLabel("Issues containing: market");
        
       var connector = mainShape.AddShapeConnector();
 
       // Add a shape to the connector for each entry in the model
       for (var i = 0; i < model.length; i++) {
           // Assign a color based on the issue type
           var color;
           switch(model[i].issueType) {
               case "Story": color = "#91AECF";
                           break;
               case "Sub-task": color = "FADE9E";
                           break;
               default: color = "#ACE2FC";
               }         
            
           connector.AddShape()
                       .SetLabel(model[i].key + "\n" + model[i].summary)
                       .SetShapeType(VS.ShapeTypes.RoundedRectangle)
                       .SetFillColor(color);
       }
 
       return vs;
   }

These small modifications change the appearance of the output visual:

New script output

Add a User Interface

The example we developed in the preceding section is useful, but would be much more useful if we could provide a parameter to it that would let us filter for other words than just "marketing". To accomplish that, we will add a user interface and pass it to our script as a parameter.

The user interface is set up by adding parameters under the Script Information section. If the parameter is not changeable by the user, then just type marketing into the "Parameter Template" field. If, however, we want the user to be able to change that value to something else, we enter a parameter name surrounded by 2 pairs of curly braces (as shown below):

As soon as you enter the last "ending" curly brace, a form will appear that allows you to customize the user interface with respect to this parameter. You can specify the display name, the default value, and a description that appears next to the user input field to help the user.

Add parameter

Now if we click the "Save and Run" button, we will see that we've created some UI for the user.

User interface

If we set a breakpoint inside the code right at the top, before the query, we will see that whatever the user types into this field is passed in via the params parameter (in the very top line of the script). We can modify the script to use that param:

function VisualScript(localhost, params, callback) {
 
    // Issue the query in the params. Calls the handler with the issues list
    VSCallFunctionForJiraQuery(localhost, "summary ~ " + params + " ORDER BY created ASC", handleQueryResponse);
A User Interface with Multiple Parameters

Frequently, you will need a user interface that collects multiple parameters from the user and uses them in the script. To accomplish this, you will need to use the Parameter Template to specify a 2nd parameter. You can do this by entering a separator character (a comma, for example), and then a 2nd identifier surrounded by double-curly-braces.

Stringified parameter template

When we run this script now, if we enter "product" for the keyword and "viewpoint" for the product, the params string passed to the script will be product,viewpoint. Inside the script, it will need to break the params string into its two pieces (at the comma) and assign variables to each piece.

function VisualScript(localhost, params, callback) {
 
    debugger;
     
    var paramParts = params.split(',');
     
    var keyword = paramParts[0];
    var project = paramParts[1];
     
    // Issue the query in the params. Calls the handler with the issues list
    VSCallFunctionForJiraQuery(localhost, "summary ~ " + keyword + " and project = "+ project +" ORDER BY created ASC", handleQueryResponse);

Hopefully you can now see that the parameter template is in fact a template. The params will be passed exactly as that string is typed in to the Parameter Template field, with the variables enclosed in double-curly braces substituted for what the user typed in to the user interface form.

A useful but slightly more complicated variation of this use of the template is to create a JSON string in the Parameter Template that is then parsed into a JSON object when the script runs. To do this, change the Parameter Template to look like a stringified JavaScript object.

Stringified parameter template

Now when the script runs, it is passed a string that can be parsed into a JavaScript object.

New UI
function VisualScript(localhost, params, callback) {
 
   var parmsObj = JSON.parse(params);
     
    // Issue the query in the params. Calls the handler with the issues list
    VSCallFunctionForJiraQuery(localhost, "summary ~ " + parmsObj.keyword + " and project = "+ parmsObj.project +" ORDER BY created ASC", handleQueryResponse);

Now we can parse the params string into an object, and use paramsObj.keyword and paramsObj.project in our query. Note that JSON.parse() is very particular about quotes being placed around both the keyword and the value.

Script Management

Scripts you add to VisualScript are shared by all users who have access to Jira. By default, all Jira Administrators can add, edit and delete all scripts (not just the ones they themselves create). All non-administrators can execute all scripts, but may not create, modify or delete them. Each user's own account is used to access the underlying Jira objects queried in the scripts, so using VisualScript scripts does not give a user greater or lesser access to Jira information than they would have had otherwise.