The list below contains some use cases that can be realized using Smart Fields for Jira.
Make sure to come by once in a while since the number of use cases will grow over time.
For all examples marked with a , you do not even have to copy and paste the code. Simply search for the field in the expression editor.
Use cases and examples
Use case | Feature | Built-in | Parsing mode | Expression |
---|---|---|---|---|
All email addresses mentioned in the issue description | General | %{findPattern(%{issue.description},"(?<=mailto:)([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)")} | ||
Assignee groups | Jira expression | issue.assignee?.groups || "" | ||
Attachment authors | Jira expression | issue?.attachments?.reduce((result,att) => result.set(att.author.displayName,""), new Map()).entries().flatten().filter(entry => entry != "") | ||
Attachment filenames | General | %{issue.attachments} | ||
Attachment mimetypes | Jira expression | issue?.attachments?.reduce((result,att) => result.set(att.mimeType,""), new Map()).entries().flatten().filter(entry => entry != "") | ||
Average time since creation of sub-tasks | General | %{floor(avg(toNumberList(jiraExpression("issue.subtasks.map(i=>(Number(new Date())-Number(i.created))/1000/60/60/24)"))))} | ||
Comment authors | Jira expression | issue.comments.reduce((result, comment) => result.set(comment.author.displayName, ""), new Map()).entries().flatten().filter(entry => entry != "") | ||
Completion percentage by status | General | %{%{issue.status} = "Open" ? 0 : ( %{issue.status} = "Planning" ? 0.15 : ( %{issue.status} = "In Progress" ? 0.6 : ( %{issue.status} = "In Review" ? 0.6 : ( %{issue.status} = "Implementing" ? 0.85 : 1 ))))} Please, replace the status names as well as the completion percentages with the ones of your choice. Any status not included in the sequence will return 1. | ||
Components from all sub-tasks | General | %{distinct(fieldValue(%{issue.components}, subtasks()))} | ||
Creation date of the linked epic | Jira expression | issue?.epic ? issue?.epic.created.toCalendarDate().toString() : "" | ||
Difference between the current day and the earliest sub-task due date | General | %{max(fieldValue({issue.dueDate},subtasks())) != null ? ceil((min(filterByValue(fieldValue({issue.dueDate},subtasks()),!=, null )) - {system.currentDateTime}) / DAY) : null} | ||
Elapsed time between creation and resolution | General | %{{issue.resolutionDate} != null ? (({issue.resolutionDate} - {issue.created}) / DAY ) : null} The duration can be retuned in years, weeks, days, hours or minutes if the DAY keyword is replaced in the above expression with the corresponding time constant. | ||
Field value validation | General | %{matches(%{issue.cfnnnnn},"6|7|8|9") ? "Valid":"Invalid"} The values entered as arguments in the matches() parser function can be replaced with different values or with a regular expressions to validate differently the value of the custom field. Please, note that it is necessary to replace nnnnn with the ID of the custom field. | ||
First comment | Jira expression | issue?.comments[0]?.body?.plainText == null ? '' : issue?.comments[0]?.body?.plainText The text will be returned using wiki markup, e.g. bold text will be shown as *bold text*. | ||
First comment author | Jira expression | issue?.comments[0]?.author?.displayName | ||
Highest value out of several fields | General | %{max([{issue.cfAAAAA}, {issue.cfBBBBB}, {issue.cfCCCCC}, {issue.cfDDDDD}, {issue.cfEEEEE}])} Please, replace the field codes with the ones of your choice. | ||
Historical due dates | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'duedate' && item.from != null)). map(changelog => {duedateentry: changelog.items.filter(item => item.fieldId == 'duedate' )}). map(e => new CalendarDate(e.duedateentry[0].from).toString()).join(", ") | ||
ICE score | Jira expression | (issue?.customfield_IIIII || 0) * (issue?.customfield_CCCCC || 0) * (issue?.customfield_EEEEE || 0) Replace the IDs with the ones of your number custom fields Impact (IIIII), Confidence (CCCCC) and Ease (EEEEE). | ||
Issue ID | General | %{issue.id} | ||
Issue type description | Jira expression | issue?.issueType?.description | ||
Keys of linked issues in current project | General | %{filterByProject(linkedIssues(), %{issue.project.key})} | ||
Keys of non-estimated sub-tasks | Jira expression | issue.subtasks.filter(s => s?.originalEstimate == null && s?.customfield_nnnnn == null).map(i => i.key).join(", ") Please, note that it is necessary to replace nnnnn with the ID of the custom field displaying the original estimate or story points. | ||
Last assignee change author | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'assignee')).map(changelog => changelog.author.displayName)[0] || "" | ||
Last comment | General | %{issue.lastComment} | ||
Last comment author | Jira expression | issue?.comments[issue?.comments?.length-1]?.author?.displayName || "" | ||
Last status change author | Jira expression | issue.changelogs.filter(c=>c.items.some(i=>i.field=="status"))[0]?.author?.displayName || "" | ||
Last status change date | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'status')).map(changelog => changelog.created).concat(issue.created)[0].toString() | ||
Lowest date and time from linked issues | Jira expression | let minDate = issue?.links.map(link => link.linkedIssue.customfield_nnnnn).filter( d => d != null); minDate.length > 0 ? new Date(minDate.reduce((a, b) => a < b ? a : b )).toString() : "" Please, replace nnnnn with the ID of a Date Time Picker custom field. | ||
Lowest date from linked issues | Jira expression | let minDate = issue?.links.map(link => link.linkedIssue.customfield_nnnnn).filter( d => d != null); minDate.length > 0 ? new CalendarDate(minDate.reduce((a, b) => a < b ? a : b )).toString() : "" Please, replace nnnnn with the ID of a Date Picker custom field. | ||
Months elapsed | General | %{{issue.cfnnnnn} != null ? ((year({system.currentDateTime}, RUN_AS_LOCAL) - year({issue.cfnnnnn}, RUN_AS_LOCAL))*12 + month({system.currentDateTime}, RUN_AS_LOCAL) - (month({issue.cfnnnnn}, RUN_AS_LOCAL))) : null} Please, replace both nnnnn in the above expression with the ID of your Date Picker custom field. | ||
Number of affected versions | General | %{issue.versions.length} | ||
Number of assignee changes | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'assignee')).length | ||
Number of attachment authors | Jira expression | issue.attachments.map(attachment => attachment.author.accountId).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length | ||
Number of attachments | General | %{issue.attachments.length} | ||
Number of attachments in sub-tasks | Jira expression | issue.subtasks.map(subtask => subtask.attachments.length).reduce((a, b) => a + b, 0) | ||
Number of comment authors | Jira expression | issue.comments.map(comment => comment.author.displayName).reduce((a, b) => a.includes(b) ? a : a.concat(b), []).length | ||
Number of comments | Jira expression | issue?.comments?.length | ||
Number of components | Jira expression | issue.components.length | ||
Number of days in previous status | Jira expression | let statusChangelogs = issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'status')); statusChangelogs[0] ? (statusChangelogs[0].created.getTime() - (statusChangelogs[1] ? statusChangelogs[1].created : issue.created).getTime()) / (24*60*60*1000) : null | ||
Number of fix versions | General | %{issue.fixVersions.length} | ||
Number of incoming issue links | Jira expression | issue?.links?.filter(link => link.direction == "inward").length | ||
Number of issues under epic | Jira expression | issue.stories?.length | ||
Number of issues under epic including sub-tasks | General | %{%{issue.issueType} = "Epic" ? count(append(issuesUnderEpic(),subtasks(issuesUnderEpic()))) : null } | ||
Number of issues with the same fix versions | General | %{%{issue.fixVersions} != null ? count(issuesFromJQL("fixVersion in ('" + jiraExpression( "issue?.fixVersions?.map(v => v?.name).join(\"','\") ") + "') ")) : null} | ||
Number of labels | General | %{issue.labels.length} | ||
Number of linked issues | General | %{issue.links.length} | ||
Number of open child issues | General | %{count(issuesFromJQL("statusCategory in ('To Do','In Progress') and parent = " + %{issue.key}))} | ||
Number of outgoing issue links | Jira expression | issue?.links?.filter(link => link.direction == "outward").length | ||
Number of status changes | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'status')).length | ||
Number of sub-tasks | General | %{issue.subtasks.length} | ||
Number of sub-tasks based on status | General | %{count(filterByStatus(subtasks(), "Done, Completed"))} Please, note that by changing "Done, Completed" into other statuses and resolutions you can adapt this use case for your needs. | ||
Number of sub-tasks based on status and issue type | General | %{count(filterByIssueType(filterByStatus(subtasks(), "Done, Completed"), "Sub-task, Sub-story"))} Please, note that by changing "Done, Completed" into other statuses and "Sub-task, Sub-story" into other issue types, you can adapt this use case for your needs. | ||
Number of times issue was changed to the current status | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'status' && item.toString == issue?.status?.name)).length | ||
Number of times that a custom field has been changed | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'customfield_nnnnn')).length Please, replace nnnnn with the ID of your custom field in the above expression. | ||
Number of times that an issue was rejected | Jira expression | issue.changelogs.filter(changelog => changelog.items.some(item => item.fieldId == 'resolution' && item.toString == 'Rejected')).length In case you have other statuses and resolutions, feel free to change these arguments: " item.fieldId == 'resolution' && item.toString == 'Rejected'" | ||
Number of unresolved blocking issues | General | %{count(filterByResolution(linkedIssues("is blocked by"), ""))} | ||
Operations with field values from sub-tasks | Jira expression | issue.subtasks.length && issue.subtasks.map( s => (s.customfield_nnnnn || 0) * (s.customfield_ppppp || 0)).reduce((a, b) => a + b) Please, replace nnnnn and ppppp in the above expression with the IDs of the Number fields. | ||
Parent assignee | General | %{parent.assignee.displayName} | ||
Parent priority | General | %{parent.priority} | ||
Parent reporter | General | %{parent.reporter.displayName} | ||
Parent status | General | %{parent.status} | ||
Percentage of completed child issues | Jira expression | let children = (issue.isEpic ? issue.stories : issue.subtasks); children.length ? (children.filter(child => child.resolution).length / children.length * 100 + '').slice(0, 4) + '%' : '' | ||
Previous assignee | Jira expression | issue.changelogs.map(i=>i.items).flatten().filter(i=>i.field=="assignee").length>0 ? issue.changelogs.map(i=>i.items).flatten().filter(i=>i.field=="assignee").map(a=>a.fromString)[0] : "" | ||
Previous issue status | General | %{issue.status.previous} | ||
Previous reporter | Jira expression | let reporters= issue.changelogs.map(i=>i.items).flatten().filter(i=>i.field=="reporter"); reporters.length>0 ? reporters.map(a=>a.fromString)[0] == null ? "" :reporters.map(a=>a.fromString)[0] : "" | ||
Priority assessment based on field values | General | %{sum([%{issue.cfnnnnn} = "Option 1" ? 1 : 0, %{issue.cfppppp} = "Option 1" ? 1 : 0 ])} Please, replace nnnnn and ppppp with the IDs of the custom fields, e.g. Select List (single choice). | ||
Priority description | Jira expression | issue?.priority?.description | ||
Project category of issue | General | %{issue.project.category} | ||
Project description | General | %{issue.project.description} | ||
Project lead | General | %{issue.project.leadDisplayName} | ||
Remaining budget | General | %{{issue.cfnnnnn} = null ? null : {issue.cfnnnnn} - sum(append(fieldValue({issue.cfppppp}, subtasks()), [0]))} | ||
Status description | Jira expression | issue?.status?.description | ||
Sub-task assignees | General | %{distinct(fieldValue(%{issue.assignee.displayName}, subtasks()))} | ||
Sub-task labels | General | %{distinct(fieldValue(%{issue.labels}, subtasks()))} | ||
Sub-task reporters | General | %{distinct(fieldValue(%{issue.reporter.displayName}, subtasks()))} | ||
Sum of field values from issues in JQL query | General | %{sum(fieldValue({issue.cfnnnnn},issuesFromJQL("project = DEMO and 'fieldName' is not empty")))} Please, replace nnnnn in the above expression with the ID of the Number custom field and fieldName with its name. | ||
Sum of original estimates of issues under epic's parent | General | %{%{issue.issueType} = "Initiative" ? sum( append(fieldValue( {issue.originalEstimate} , union( issuesFromJQL("issuekey in portfolioChildIssuesOf("+ %{issue.key} +")"), issuesUnderEpic(issuesFromJQL("issuekey in portfolioChildIssuesOf("+ %{issue.key} +")")))), [0] )) / 60 : null} We consider the epic's parent to be a Initiatiave in this expression, but it could differ depending on the Advanced Roadmaps issue type hierarchy. To adapt to your issue type hierarchy change the following part of the expression: " %{issue.issueType} = "Your Custom Issue Type" " | ||
Sum of story points of completed child issues | General | %{sum(fieldValue({issue.cfnnnnn},issuesFromJQL("statusCategory = Done AND 'parent' = "+%{issue.key})))} Replace "nnnnn" with the ID of the story points field. Keep in mind, that this smart fields should be displayed on a parent issue. | ||
Sum of story points of completed sibling issues | General | %{sum(fieldValue({issue.cfnnnnn},issuesFromJQL("statusCategory = Done AND 'parent' = "+%{parent.key})))} Replace "nnnnn" with the ID of the story points field. Keep in mind, that this smart fields should be displayed on a child issue. | ||
Sum of time spent of blocking issues | General | %{sum(append(fieldValue({issue.timeSpent},linkedIssues("is blocked by")), [0]))/60} If the resulting number from the expression contains too many decimal numbers, feel free to select one of the available display formats or to configure it manually. | ||
Sum of time spent of epic and issues under it | General | %{%{issue.issueType} = "Epic" ? (sum(append(fieldValue({issue.timeSpent}, issuesUnderEpic()), [{issue.timeSpent}] )) / 60) : null} If the resulting number from the expression contains too many decimal numbers, feel free to select one of the available display formats or to configure it manually. | ||
Sum of time spent of issues under epic | General | %{%{issue.issueType} = "Epic" ? sum(append(fieldValue({issue.timeSpent}, issuesUnderEpic()), [0])) / 60 : null} If the resulting number from the expression contains too many decimal numbers, feel free to select one of the available display formats or to configure it manually. | ||
Sum of time spent of linked issues | General | %{sum(append(fieldValue({issue.timeSpent}, linkedIssues()), [0])) / 60} | ||
Sum of time spent of the current and linked issues | General | %{sum(append(fieldValue({issue.timeSpent}, union(linkedIssues(), issueKeysToIssueList(%{issue.key}) )), [0])) / 60} | ||
Time left until due date | General | %{{issue.dueDate} != null ? max({issue.dueDate} - {system.currentDateTime}, 0) / DAY : null} | ||
Timely resolution assessment | General | %{{issue.resolutionDate} != null AND {issue.dueDate} != null ? ({issue.resolutionDate} <= {issue.dueDate} ? "Yes" : "No") : (datePart({system.currentDateTime}, RUN_AS_LOCAL) > {issue.dueDate} ? "No" : "")} | ||
Time since creation | General | %{floor(({system.currentDateTime} - {issue.created})/DAY)} The duration can be retuned in years, weeks, days, hours or minutes if the DAY keyword is replaced in the above expression with the corresponding time constant. | ||
Time spent ratio | General | %{%{issue.originalEstimate} != null AND %{issue.timeSpent} != null ? {issue.timeSpent} / {issue.originalEstimate} * 100 : null} | ||
Total attachment size in MB | Jira expression | issue.attachments.reduce((result, att) => result + att.size, 0) / 1024 / 1024 | ||
Total original estimate of blocking issues | General | %{sum(fieldValue({issue.originalEstimate},linkedIssues("is blocked by")))/60} If the resulting number from the expression contains too many decimal numbers, feel free to select one of the available display formats or to configure it manually. | ||
Total remaining estimate of blocking issues | General | %{sum(append(fieldValue({issue.remainingEstimate},linkedIssues("is blocked by")),[0]))/60} If the resulting number from the expression contains too many decimal numbers, feel free to select one of the available display formats or to configure it manually. | ||
Total remaining estimate of issues under epic | General | %{%{issue.issueType} = "Epic" ? sum(append(fieldValue({issue.remainingEstimate}, issuesUnderEpic()), [0])) / 60 : null} | ||
Total remaining estimate of issues under epic and sub-tasks | General | %{%{issue.issueType} = "Epic" ? sum(append(fieldValue({issue.remainingEstimate}, union(issuesUnderEpic(), subtasks(issuesUnderEpic())) ), [0])) / 60 : null} | ||
Total story points of issues in an epic | General | %{sum(fieldValue({issue.cfNNNNN}, issuesUnderEpic()))} Replace the ID with the one of your 'Story Points' custom field. |