Check if all actions have status 'Done'

Starting from v 1.19.0 there is a more convenient way to do this task using Table Grid Editor API. Check "Validate grid data on transition" article

Introduction
 

Assume you track actions with a grid, and you want to validate that all rows in a grid have status 'Done'.

 

 

Approach

We use the script runner to implement a script validator which will request the content of the grid using a REST call,
traverse the rows and fail the validator when a status is found different from 'Done'





Setup



  • Install the Script Runner add-on from the Atlassian marketplace

  • Create a workflow with a transition 'All done'

    • This transition should have a dialog box containing the grid

  • Add a validator function of type 'Script Validator' and choose 'Custom Script validator'

  • Point the script file parameter to the script location. 

    • you can use relative paths but this goes beyond the scope of this faq.

    • using a script file instead of an inline script allows you to easily change the content, without editing the workflow

 

Script

The complete script is avaliable as a snippet:

http://stash.idalko.com/snippets/6693b98916de434aa512c95827fb57df



The core functionality is contained in a loop which checks if all statuses have been set to 'Done'

/* ** Traversing all the row, checking if the column is set to 'Done' */ for (int i = 0; i < rows.length(); i++) { def jSONObject = rows.getJSONObject(i); def cells = jSONObject.getJSONArray("cell"); def cellValue = cells.get(COLUMN_INDEX); if (!EXPECTED_VALUE.equals(cellValue)) { invalidInputException = new InvalidInputException("Not all values have been set to '${EXPECTED_VALUE}'"); return; } }

 

To retrieve the content of the grid, we use a REST call

final String ROWS = "rows"; final String ENCODING = "UTF-8"; final String BASE_URL = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL); final String REST_PATH = "/rest/idalko-igrid/1.0/datagrid/data";   ...   def uri = new URIBuilder() .setPath(BASE_URL + REST_PATH) .setParameter("_mode", "edit") .setParameter("_issueId", issueId) .setParameter("_fieldId", customFieldId) .setParameter("_confSchemeId", confSchemeId) .setParameter("_dataId", tempId) .build();     /* ** retrieving the complete grid */ def httpGet = new HttpGet(uri); def response = httpclient.execute(httpGet); def contentInputStream = response.getEntity().getContent(); def responseContent = IOUtils.toString(contentInputStream, ENCODING); JSONObject jsonObject = new JSONObject(responseContent); JSONArray rows = jsonObject.getJSONArray(ROWS);

 

 

Note about the tempId

When a table grid is being edited, all the rows relating to the issue under edit will be stored in a temporary table.
This allows the table grid to cancel an update (in case a user would hit 'cancel'). 

Given that the edited values are only flushed to the permanent table after the completion of the transition (as a postfunction),
we need a way to access the content of the temporary table, if we want to check if all the statusses have been set to 'Done'
The tempId will allow us to get to this content.

The addon also uses a 'command pattern' to queue all the changes that need to be done to the permanent table. This is 
contained in the customFieldValue of the transition when the transition is saved

 

For instance, when you add a row, the customFieldValue will contain following entries

[["tempId","u1434790456934"],["add",{,"idue":"2015-06-27","iassignee_text":"angelina","iassignee_value":"angelina","istatus_text":"Open", "istatus_name":"Open"}]]

Using following code we will extract the tempId

 

Parameters in the script

Although this is mainly an example how to use REST to access the grid content, you can use following parameters
to configure the behaviour