# Jira Expressions Library: Examples & Use Cases

In this library you’ll find ready-to-use **Jira expressions** for common workflow **conditions** and **validators**. Each example below illustrates a practical **use case**, such as making a field required or restricting transitions based on roles. These **Jira expressions** can be plugged into your [Workflow Validator](https://docs.forgappify.com/workflow-building-blocks-for-jira/conditions-validators/jira-expression) to enforce custom rules.

## Field Conditions

{% hint style="info" %}
IDs used in following examples vary between Jira instances. You need to change the ID of the custom fields to a valid one. In the editor, after `issue.`, start typing the name of the field, and you will get suggestions with the correct value.
{% endhint %}

### Is required

**Condition example**: Require an assignee.

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.assignee != null
```

{% endcode %}

Issue must have work logged:&#x20;

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.timeSpent != null
```

{% endcode %}

### Compare Two Field Values

The issue assignee must be different from the issue reporter:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.reporter != issue.assignee
```

{% endcode %}

### Parent value in condition

Assuming that you have a separate workflow for sub-tasks and want to allow a transition in sub-tasks only when the field in its parent has the value "SAP":

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.parent.customfield_12345 == "SAP"
```

{% endcode %}

It depends what kind of field you are referring. If it is of the Select type, then you need to use .value suffix:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.parent.customfield_12345.value == "SAP"
```

{% endcode %}

It is up to you to find out the id and type of your custom field. In Jira expression field, after *issue.parent.* start typing the name of your field, you will get suggestion, which you can click and id of the field will be entered for you. You will also get a syntax error in case a type of the field is incorrect, which will help you make it right.

Finally, if you don't have a separate workflow, then you can make the condition to be checked only for subtasks, i.e. if parent != null. e.g.:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.parent != null 
    ? issue.parent.customfield_12345?.value == "SAP"
    : true
```

{% endcode %}

### Field mandatory based on

#### Resolution

Make the "Root Cause" field required if the resolution is set to "Fixed":

<pre class="language-javascript" data-title="Jira expression" data-overflow="wrap" data-line-numbers><code class="lang-javascript">issue.resolution != null &#x26;&#x26; issue.resolution.name == 'Fixed' 
  ? issue.<a data-footnote-ref href="#user-content-fn-1">customfield_10126</a> != null
  : true
</code></pre>

Make the "Fix version" field mandatory only when the resolution is set to "Done":

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.resolution != null && issue.resolution.name == 'Done' 
  ? issue.fixVersions.length > 0
  : true
```

{% endcode %}

#### Labels

Make a field required if a "LabelA" label is set:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.labels.includes('LabelA') ? issue.customfield_12345 != null : true
```

{% endcode %}

### Value starts with

Summary starts with a string:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.summary.indexOf('Test Case') == 0
```

{% endcode %}

Summary starts with a string and is minimum 15 characters long:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.summary.indexOf('Test Case') == 0 && issue.summary.length >= 15
```

{% endcode %}

### Match regex

A custom field with only serial numbers, can be empty. The serial number format should consist of two letters followed by a sequence of five numbers (e.g., AB12345).

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.customfield_12345 == null || issue.customfield_12345.match('^AB[0-9]{5}$')
```

{% endcode %}

### Issue in active sprint

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.sprint?.state == 'active'
```

{% endcode %}

### Original estimate field condition

The Original estimate field is stored as a number of seconds, e.g.: 4h = 4x60x60 = 14400.&#x20;

Original estimate must be less than 4h:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.originalEstimate < 14400
```

{% endcode %}

## Rich Text

The content of a multi-line text field in Jira can be represented either as a string or an ADF (Atlassian Document Format) object, depending on the field's renderer or view context. To handle the field content consistently, regardless of its type, you can declare a helper function as follows:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
let plainTextValue = value => typeof value == 'Map' ? new RichText(value).plainText : value ; 
```

{% endcode %}

### Is not empty

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
let plainTextValue = value => typeof value == 'Map' ? new RichText(value).plainText : value ; 

issue.customfield_12345 != null && plainTextValue(issue.customfield_12345) != ''
```

{% endcode %}

### Description

The description field is an exception, as it is always represented as an ADF (Atlassian Document Format) object. To check if it is not empty, use the `plainText` property:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.description.plainText != '' 
```

{% endcode %}

## Time Spent

### Ensure Time has been logged

The following expression requires that the issue has some time logged before the transition can be completed:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.timeSpent != null
```

{% endcode %}

In order to check if at least 1 hour has been logged in the issue, compare the value against the number of seconds stored in `timeSpent:`

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.timeSpent != null && issue.timeSpent >= 3600
```

{% endcode %}

### Log time during the transition

if you want to ensure that time is logged during the transition (if you have a dedicated screen with the **Log Work** field), you can check against `originalIssue`—which is a snapshot of the issue before the transition screen was opened:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
originalIssue.timeSpent == null 
    ? issue.timeSpent != null 
    : issue.timeSpent > originalIssue.timeSpent
```

{% endcode %}

## Original Estimate

The originalEstimate field holds the original time estimate in **seconds**.

For simple checks, such as whether the field is set or above a threshold, consider using the [Ultimate Validator](https://docs.forgappify.com/workflow-building-blocks-for-jira/conditions-validators/ultimate)—no expression needed.

For advanced scenarios, like detecting changes during a transition, use the [jira-expression](https://docs.forgappify.com/workflow-building-blocks-for-jira/conditions-validators/jira-expression "mention").

### Check if Original Estimate was changed during transition

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.originalEstimate != null 
    && issue.originalEstimate != originalIssue.originalEstimate
```

{% endcode %}

This expression ensures the field is set and was modified during the current transition.

### Check if Original Estimate is greater than 1 hour

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.originalEstimate > 3600
```

{% endcode %}

## Fix Versions

Block a transition if the Fix Versions contains "-UPDATE""

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
!issue.fixVersions.some(version => version.name.includes('-UPDATE'))
```

{% endcode %}

## Cascading Select

Allow a transition if the parent option is equal to A:

```javascript
issue.customfield_12345 != null && issue.customfield_12345.value == 'ParentA'
```

In order to check the child option, you can use the following:

```javascript
issue.customfield_12345?.child?.value == 'ChildA'
```

Allow transition if any of the parent's options is selected:

```javascript
['ParentA', 'ParentB'].includes(issue.customfield_12345?.value)
```

## Comments

### Require a comment on a transition screen

If a user selects "No" in a Radio Button field, then require a comment on the transition screen:

```javascript
issue.customfield_12345?.value == "No" 
  ? issue.comments.length > originalIssue.comments.length
  : true
```

## Attachments

### Force attaching a file on a transition screen&#x20;

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.attachments.length > originalIssue.attachments.length 
```

{% endcode %}

### Validate filename&#x20;

Use "issue.attachments\[issue.attachments.length-1]" to access the last added file

<pre class="language-javascript" data-title="Jira expression" data-overflow="wrap" data-line-numbers><code class="lang-javascript"><strong>issue.attachments[issue.attachments.length-1].filename.includes('NDA')
</strong></code></pre>

A filename property is a string. Check other string methods: <https://developer.atlassian.com/cloud/jira/software/jira-expressions-type-reference/#string>

### Validate extension

Don't allow .bmp, .exe, .bat, jam.dev file extensions:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.attachments.every(item => !item.filename.match('(bmp|exe|bat|jam.dev)$'))
```

{% endcode %}

## Pull request validation

Information about commits and pull requests is stored in the **Development** field. The value of the field is a string with format as follows:

```
"{pullrequest={dataType=pullrequest, state=OPEN, stateCount=1},
 json={\"cachedValue\":{\"errors\":[],\"summary\":{\"pullrequest\":{\"overall\":{\"count\":1,\"lastUpdated\":\"2024-05-28T10:24:34.912+0200\",\"stateCount\":1,\"state\":\"OPEN\",\"dataType\":\"pullrequest\",\"open\":true},\"byInstanceType\":{\"bitbucket\":{\"count\":1,\"name\":\"Bitbucket Cloud\"}}}}},\"isStale\":false}}"
```

Use the **includes** method or any other available function that operates on strings to validate the state of the field.

<pre class="language-javascript" data-title="Jira expression" data-overflow="wrap" data-line-numbers><code class="lang-javascript"><strong>issue.customfield_10000.includes('state=OPEN') ? true : false
</strong></code></pre>

When a pull request is open, the value should contain `state=OPEN`; when it is merged, the value should contain `state=MERGED`.

## Dates comparison

Due date is approaching in 2 days from now:

<pre class="language-javascript" data-title="Jira expression" data-overflow="wrap" data-line-numbers><code class="lang-javascript"><strong>issue.dueDate != null 
</strong>	&#x26;&#x26; issue.dueDate &#x3C; new Date().toCalendarDate().plusDays(2)
</code></pre>

## User conditions

### Only the issue creator can close the issue.

In the **Close** transition add Jira Expression Condition or Validator (WBB):

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
user.accountId == issue.creator.accountId
```

{% endcode %}

### Prevent the reporter from transitioning the issue (approval transition)

In the **Approval** transition add Jira Expression Condition or Validator (WBB):

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
user.accountId != issue.reporter.accountId
```

{% endcode %}

## Linked issues

### Prevent closing an issue if it has an “is blocked by“ issue linked

<pre class="language-javascript" data-title="Jira expression" data-overflow="wrap" data-line-numbers><code class="lang-javascript">issue.links
  .filter(link => link.type.id == <a data-footnote-ref href="#user-content-fn-2">10000</a> &#x26;&#x26; link.direction == 'inward')
  .map(link => link.linkedIssue)
  .reduce(
      (pass, issue) => pass &#x26;&#x26; ['done'].includes(issue.status.category.key),
      true
  )
</code></pre>

Link type ID may vary between Jira instances. Use the [Jira expression preview](https://docs.forgappify.com/workflow-building-blocks-for-jira/testing-and-debugging/testing-conditions-and-validators) from the Linked Issues Condition to learn how to construct a similar expression.&#x20;

### Prevent closing an epic until all its issues are done

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
(issue.isEpic ? issue.stories : []).reduce(
  (pass, issue) => pass && ['done'].includes(issue.status.category.key),
  true
)
```

{% endcode %}

## Request type

Use following expression to allow only the Service Desk Team to submit tickets without a request type:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
let hasRole = user.getProjectRoles(issue.project).reduce(
  (isAllowed, role) => isAllowed || ["Service Desk Team"].includes(role.name),
  false
);
!hasRole ? issue.customfield_10010 != null : true
```

{% endcode %}

In the editor, after typing `issue.`, start typing 'Request Type,' and you will receive a valid custom field ID suggestion.

## Restricting issue creation

1. Edit the workflow of issue type that you want to restrict&#x20;
2. In diagram mode, select the **Create** transition arrow and click **Validators** in the properties panel.
3. Select the **Validators** tab and click on **Add validator** button
4. Add **Jira Expression Validator** (WBB)

### Restricting the creation of certain issue types based on the user's project role

{% hint style="info" %}
Requires a separate workflow for certain issue types
{% endhint %}

For example, if you want only users with the 'Developers' project role to be able to create Story issues, add Jira Expression Validator to the Create transition with following settings:

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
user.getProjectRoles(issue.project).reduce(
  (isAllowed, role) => isAllowed || ["Developers"].includes(role.name),
  false
)
```

{% endcode %}

{% code title="Validation message" overflow="wrap" lineNumbers="true" %}

```javascript
"You must have the Developer project role to create Story issues, but got: " 
  + user.getProjectRoles(issue.project).map(projectRole => projectRole.name)
```

{% endcode %}

#### Version without separate workflows

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
issue.issueType.name == "Story" 
  ? user.getProjectRoles(issue.project).reduce(
      (isAllowed, role) => isAllowed || ["Developers"].includes(role.name),
      false
    )
  : true
```

{% endcode %}

### Field is required during a workflow transition for certain issue types

For example, to make the Story Points field required during creation only for the Story issue type, use the following expression. If you press Ctrl + Space after typing `issue.`, start typing "story" to get suggestions about the Story Points field key.&#x20;

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
["Story"].includes(issue.issueType.name) 
  ? issue.customfield_10036 != null
  : true
```

{% endcode %}

Note that thanks to our validator, there's no need to create separate workflows for each issue type.

## Jira Service Management

### Validate fields specific to a request type&#x20;

You can find the request type ID by looking at the URL of the request type form settings page.

Go to: Project settings -> Request types -> Click on a chosen request type; the URL contains the ID, e.g.:

<https://example.atlassian.net/jira/servicedesk/projects/KEY/settings/request-types/request-type/**34**/request-form>

{% code title="Jira expression" overflow="wrap" lineNumbers="true" %}

```javascript
customerRequest.requestType.id == 34 ? issue.dueDate != null : true
```

{% endcode %}

[^1]: Root Cause field id

[^2]: "is blocked by" link type id
