Custom ScriptRunner job to fetch issues and then assets from asset custom field

In this example, we'll configure a ScriptRunner custom job. It will executes a JQL periodically. Takes the selected assets of the asset custom field and extracts the Install date system field. You can get other type if fields, please see Get Assets of custom field and extract field values with Script Runner.

And then, you can continue to your business logic, for example creating an issue or sending an e-mail.

Please see Script Runner documentation to configure a custom job. https://scriptrunner.adaptavist.com/6.12.0/jira/custom-script-jobs.html

How it works:

  • Searches for issues with the JQL
  • Extracts the date custom field  with otherCustomFieldId variable. And formats it with ISO date format yyyy-MM-dd
  • Find the asset custom field value with assetCustomFieldId variable. (there can be more than one asset custom field but it takes only one of them)
  • Extracts assets and pulls asset objects
  • For each asset, asset's installDate (by assetSystemFieldName) and it is compared to date CF value. Asset attributes or other system field types can be extracted as well, please see https://confluence.snapbytes.com/x/-5PNAg for examples.
  • If the values are equal it sends an email to reporter.

Following part is the variables. If you can narrow search results of the JQL, performance will be better.

// FIXME: PLEASE EDIT HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@Field assetCustomFieldId = 10208
@Field jqlToExecute = "project = ISD"
@Field otherCustomFieldId = 11401
@Field assetSystemFieldName = "asset.installDate"
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



Code (Inline Script)

/*-------------------------------------*/

import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.mail.Email
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.mail.server.SMTPMailServer
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import groovy.transform.Field
import inventoryplugin.dto.portable.FormWithRelationsDto
import inventoryplugin.dto.portable.InventoryDto
import inventoryplugin.service.controller.InventoryController
import inventoryplugin.service.rest.dto.SystemFieldDto
import org.apache.commons.lang3.StringUtils
import org.apache.log4j.Level
import org.apache.log4j.Logger

import java.sql.Timestamp
import java.time.format.DateTimeFormatter

@WithPlugin("plugin.jip")
@PluginModule InventoryController inventoryController;

// FIXME: PLEASE EDIT HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@Field assetCustomFieldId = 10208
@Field jqlToExecute = "project = ISD"
@Field otherCustomFieldId = 11401
@Field assetSystemFieldName = "asset.installDate"
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

@Field Logger logger = Logger.getLogger("snapbytes.inventoryplugin.groovy.script")
logger.setLevel(Level.DEBUG)

@Field user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
@Field customFieldManager = ComponentAccessor.getCustomFieldManager();
@Field issueManager = ComponentAccessor.getIssueManager();
@Field jiraBaseUrl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
@Field SMTPMailServer mailServer = ComponentAccessor.getMailServerManager().getDefaultSMTPMailServer()

Integer stringToInteger(String s) {
try {
return Integer.parseInt(s);
} catch (def e) {
logger.debug("Can not convert asset id to INTEGER" + e.getMessage())
return null;
}
}

List<FormWithRelationsDto> getCustomFieldAssets(inventoryController, theIssue, customFieldId) {
List<FormWithRelationsDto> assetObjects = new ArrayList<>();

def cField = customFieldManager.getCustomFieldObject(customFieldId)
// for assets custom field, asset cf value will be delimited with commas with additional leading and trailing commas, i.e: ,1,2,3,
def cfValue = theIssue.getCustomFieldValue(cField)
def assetIds = StringUtils.split(cfValue, ",");
for (String assetIdAsStr : assetIds) {
if (StringUtils.isNotBlank(assetIdAsStr)) {
Integer assetId = stringToInteger(assetIdAsStr);
if (assetId != null && assetId > 0) {
FormWithRelationsDto formWithRelationsDto =
inventoryController.getFormWithRelationsAndOneInventoryDto(user, assetId);
assetObjects.add(formWithRelationsDto);
}
}
}
return assetObjects;
}

static List<InventoryDto> getAssetsOfObject(FormWithRelationsDto oneFormWithRelationsDto) {
if (oneFormWithRelationsDto != null) {
List<InventoryDto> inventoryDtos = oneFormWithRelationsDto.getInventories();
return inventoryDtos;
}
return null
}

static SystemFieldDto getSystemFieldDto(InventoryDto inventoryDto, String fieldName) {
List<SystemFieldDto> systemFields = inventoryDto.getSystemFields();
if (systemFields != null && systemFields.size() > 0) {
for (SystemFieldDto systemFieldDto : systemFields) {
if (StringUtils.equalsIgnoreCase(fieldName, systemFieldDto.getFieldName())) {
return systemFieldDto
}
}
}
return null
}

static String getSystemFieldRawValue(InventoryDto inventoryDto, attributeName) {
SystemFieldDto systemFieldDto = getSystemFieldDto(inventoryDto, attributeName);
return systemFieldDto != null ? systemFieldDto.getValue() : null
}

String getDateCustomFieldValue(currentIssue) {
def dateFormatter = DateTimeFormatter.ofPattern('yyyy-MM-dd')
def dateCustomField = customFieldManager.getCustomFieldObject(otherCustomFieldId)
if (dateCustomField != null) {
Timestamp dateCfValue = currentIssue.getCustomFieldValue(dateCustomField)
if (dateCfValue != null) {
return dateFormatter.format(dateCfValue.toLocalDateTime());
}
} else {
logger.error("Other custom field is not found. Please check 'otherCustomFieldId' variable value!")
}
return null
}

def sendEmail(String emailAddr, String subject, String body) {
if (mailServer) {
Email email = new Email(emailAddr)
email.setSubject(subject)
email.setBody(body)
email.setMimeType("text/html")
mailServer.send(email)
logger.debug(" Mail sent " + email)
} else {
logger.warn(" Mail Server is not configured!")
}
}

def getAssetUrl(assetId) {
return jiraBaseUrl + "secure/DashboardAIPAction!default.jspa#/browse/${assetId}";
}

def getIssueUrl(issueKey) {
return jiraBaseUrl + "/browse/${issueKey}";
}

def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchService = ComponentAccessor.getComponent(SearchService)

// edit this query to suit
def query = jqlQueryParser.parseQuery(jqlToExecute)
def search = searchService.search(user, query, PagerFilter.getUnlimitedFilter())

logger.debug("Total issues: ${search.total}")


search.results.each { documentIssue ->
// if you need a mutable issue you can do:
def issue = issueManager.getIssueObject(documentIssue.id)

// get all selected assets of custom field 10602 ()
def otherFieldValue = getDateCustomFieldValue(issue);
if (otherFieldValue != null) {
List<FormWithRelationsDto> assetsWithFields = getCustomFieldAssets(inventoryController, issue, assetCustomFieldId)
if (assetsWithFields.size > 0) {
logger.debug("--- Issue ${issue.key} ${issue.summary}... Date Field value: ${otherFieldValue}")
for (FormWithRelationsDto formWithRelationsDto : assetsWithFields) {
List<InventoryDto> inventoryDtos = getAssetsOfObject(formWithRelationsDto);
if (inventoryDtos != null && inventoryDtos.size() > 0) {
// in this case, the array will have one object at most
for (InventoryDto inventoryDto : inventoryDtos) {
if (inventoryDto != null) {
logger.debug(" Asset id: ${inventoryDto.id}, name: ${inventoryDto.name} ")
// install date system field
def assetInstallDate = getSystemFieldRawValue(inventoryDto, assetSystemFieldName);
logger.debug(" Asset Install Date (system field): " + assetInstallDate);
if (StringUtils.equals(otherFieldValue, assetInstallDate)) {
logger.debug(" ++++++++ sending e-mail to ${issue.reporter.emailAddress}");
def issueUrl = getIssueUrl(issue.key)
def assetUrl = getAssetUrl(inventoryDto.id)
def emailBody = "<div>Asset <a href=\"${assetUrl}\">${inventoryDto.name} #${inventoryDto.id}</a> needs to be calibrated.</div>" +
"<div>Reference issue is <a href=\"${issueUrl}\"> ${issue.key} - ${issue.summary} </a></div>" +
"<div>Thank you.</div>"
sendEmail(issue.reporter.emailAddress, "Oscilloscope calibration", emailBody)
}
}
}
}
}
} else {
logger.debug("--- Issue ${issue.key} ${issue.summary} : No assets");
}

} else {
logger.debug("--- Issue ${issue.key} ${issue.summary} : Other field is empty")
}
}

// return something to see logs in script-runner screen
return 1

/*-------------------------------------*/


Email body preview:


Log output (truncated):

...
2021-02-10 12:49:21,618 DEBUG [groovy.script]: Total issues: 62
2021-02-10 12:49:21,620 DEBUG [groovy.script]: --- Issue ISD-62 ababab : Other field is empty
2021-02-10 12:49:21,621 DEBUG [groovy.script]: --- Issue ISD-61 new req : Other field is empty
2021-02-10 12:49:21,621 DEBUG [groovy.script]: --- Issue ISD-60 Test asset custom field : Other field is empty
2021-02-10 12:49:21,622 DEBUG [groovy.script]: --- Issue ISD-59 asd : Other field is empty
2021-02-10 12:49:21,623 DEBUG [groovy.script]: --- Issue ISD-58 Approve test with multiple approvals : Other field is empty
2021-02-10 12:49:21,636 DEBUG [groovy.script]: --- Issue ISD-40 Wireless router not working : Other field is empty
2021-02-10 12:49:21,636 DEBUG [groovy.script]: --- Issue ISD-39 VPN access request : Other field is empty
2021-02-10 12:49:21,769 DEBUG [groovy.script]: --- Issue ISD-38 Cannot turn on my laptop... Date Field value: 2021-02-28
2021-02-10 12:49:21,770 DEBUG [groovy.script]: Asset id: 186, name: Laptop-00015 
2021-02-10 12:49:21,772 DEBUG [groovy.script]: Asset Install Date (system field): 2021-02-28
2021-02-10 12:49:21,772 DEBUG [groovy.script]: ++++++++ sending e-mail to agrant-sd-demo@example.com
2021-02-10 12:49:23,525 DEBUG [groovy.script]: Mail sent To='agrant-sd-demo@example.com' Subject='Oscilloscope calibration' From='null' FromName='null' Cc='null' Bcc='null' ReplyTo='null' InReplyTo='null' MimeType='text/html' Encoding='UTF-8' Multipart='null' MessageId='<627884426.10.1612961362928@localhost>' ExcludeSubjectPrefix=false'
2021-02-10 12:49:23,690 DEBUG [groovy.script]: --- Issue ISD-37 Wireless dongle... Date Field value: 2021-02-28
2021-02-10 12:49:23,690 DEBUG [groovy.script]: Asset id: 186, name: Laptop-00015 
2021-02-10 12:49:23,690 DEBUG [groovy.script]: Asset Install Date (system field): 2021-02-28
2021-02-10 12:49:23,690 DEBUG [groovy.script]: ++++++++ sending e-mail to agrant-sd-demo@example.com
2021-02-10 12:49:25,279 DEBUG [groovy.script]: Mail sent To='agrant-sd-demo@example.com' Subject='Oscilloscope calibration' From='null' FromName='null' Cc='null' Bcc='null' ReplyTo='null' InReplyTo='null' MimeType='text/html' Encoding='UTF-8' Multipart='null' MessageId='<1631968154.11.1612961364805@localhost>' ExcludeSubjectPrefix=false'
2021-02-10 12:49:27,211 DEBUG [groovy.script]: --- Issue ISD-35 Guest wifi for Jane from ABC Corp : Other field is empty
2021-02-10 12:49:27,212 DEBUG [groovy.script]: --- Issue ISD-34 Visitor wifi access : Other field is empty
2021-02-10 12:49:27,213 DEBUG [groovy.script]: --- Issue ISD-33 Need 3 laptops for demo : Other field is empty
2021-02-10 12:49:27,237 DEBUG [groovy.script]: --- Issue ISD-3 Intranet server offline : Other field is empty
2021-02-10 12:49:27,238 DEBUG [groovy.script]: --- Issue ISD-2 Migrate intranet server : Other field is empty
2021-02-10 12:49:27,239 DEBUG [groovy.script]: --- Issue ISD-1 What am I looking at? : Other field is empty
...