Create asset with stock conditionally
This page is about Assets & Inventory Plugin for Jira DC. Using Cloud? Click here.
This document explains how to create a Jira post function using a Groovy script to conditionally create a new asset and set its stock level based on user input.
In this example, we will define a post function to create an asset and set it to the Assets asset custom field.
Functionality
The post function performs the following operations:
Checks if the asset custom field is empty. If it is not empty, does nothing.
Checks if
In Stock
is not Yes.Creates asset with:
Asset Name
field value will be the name of the asset.Order Amount
will be Number of items in stock asset value if the asset has this attribute.
Sets new asset to Assets custom field.
Script configuration
Fields of screen and asset
Assets - Asset custom fieldÂ
maps to assetCustomFieldId in Groovy script
Other fields
Asset Type - ListBox -Â Values are
Asset Type Name - ID
.ÂMaps to assetTypeCustomFieldId in Groovy script
Examples:
Laptop - 3
Keyboard - 2
Computer - 27
Asset Name - Text Field: Name of the asset. If asset names are set to unique and the asset name is not unique for assets, it will not be created.Â
Maps to assetNameCustomFieldId in Groovy script.
Order Amount - Number Field - number of items to be purchased
Maps to orderAmountCustomFieldId in Groovy script.
In Stock - Radio (Yes / No) - This must be
No
for this operation as we will create a new asset.Maps to inStockCustomFieldId in Groovy script.
Asset attribute
Number of items in stock - Number - This will hold inventory of assets.
Add the post function
In your Jira project settings, navigate to Workflows.
Select the desired workflow and choose the transition where you want the script to run (for example,
Create
).Click on Post Functions.
Add a new post function named [AIP] - Asset generic groovy script.
Position the function after Issue Updated but before Update change history for an issue and store the issue in the database, GenerateChangeHistoryFunction, and Re-index post functions.
Post function parameters
Asset custom fields (with values) to inject as script parameter
:Â Select the target asset custom field.
Groovy script:Â
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import groovy.json.JsonSlurper
import org.apache.commons.lang3.StringUtils
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils
import org.apache.log4j.Level
import org.apache.log4j.Logger
// To test with script runner
//import com.atlassian.jira.issue.Issue
//def issueManager = ComponentAccessor.getIssueManager()
//def Issue issue = issueManager.getIssueObject("KTP-25")
Logger logger = Logger.getLogger("inventoryplugin.groovy.script")
logger.setLevel(Level.DEBUG)
/*** variables to configure **************************/
def assetCustomFieldId = 'customfield_10227';
def assetTypeCustomFieldId = 'customfield_10314';
def inStockCustomFieldId = 'customfield_10317';
def orderAmountCustomFieldId = 'customfield_10316';
def assetNameCustomFieldId = 'customfield_10318';
def inStockNoValue = 'No'
def AUTH_HASH = 'Basic YWRtaW46YWRtaW4='; // auth hash for authorization. this sample is for user=admin, and password=admin
def stockAttributeId = 67; // asset "Number of items in stock" attribute id
/*****************************************************/
boolean isNotInStockCase(issueObj, inStockCustomFieldId, inStockNoValue) {
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject(inStockCustomFieldId)
def option = (Option) issueObj.getCustomFieldValue(cField);
return option != null && option.getValue() != null && option.getValue().equals(inStockNoValue)
}
def updateAssetCustomFieldOfTheIssue(issueObj, assetCustomFieldId, issueKey, assetCustomFieldValue) {
// get issue
def issueToUpdate = ComponentAccessor.getIssueManager().getIssueObject(issueKey);
// get asset custom field
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
def assetCf = customFieldManager.getCustomFieldObject(assetCustomFieldId) // asset cf id
// execute update
assetCf.updateValue(null, issueToUpdate, new ModifiedValue(issueToUpdate.getCustomFieldValue(assetCf), assetCustomFieldValue), new DefaultIssueChangeHolder())
}
int getAssetTypeId(issueObj, assetTypeCustomFieldId) {
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject(assetTypeCustomFieldId)
def assetTypeValue = issueObj.getCustomFieldValue(cField).getValue() as String
return StringUtils.trim(StringUtils.substring(assetTypeValue, StringUtils.lastIndexOf(assetTypeValue, "- ") + 2)) as Integer;
}
/**
* Checks if Asset custom field value is empty
*/
boolean isCustomFieldEmpty(issueObj, assetCustomFieldId) {
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject(assetCustomFieldId)
return StringUtils.isBlank(issueObj.getCustomFieldValue(cField));
}
/**
* converts stream to json
*/
def streamToJson(stream) {
BufferedReader br = new BufferedReader(new InputStreamReader(stream));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
br.close();
def jsonSlurper = new JsonSlurper()
return jsonSlurper.parseText(sb.toString())
}
/**
* Calls GET service
*/
def callGetRestServiceAndReturnJson(serviceUrl, authHash) {
Logger logger = Logger.getLogger("inventoryplugin.groovy.script")
try {
// get asset ID(s)
def baseurl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
CloseableHttpClient httpclient = HttpClients.createDefault();
def httpGet = new HttpGet(baseurl + serviceUrl);
httpGet.setHeader("Accept", "application/json");
httpGet.setHeader("Content-type", "application/json");
httpGet.setHeader("Authorization", authHash);
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
if (response.getStatusLine().getStatusCode() == 200) {
return streamToJson(response.getEntity().getContent());
} else {
return null;
}
} finally {
EntityUtils.consume(response.getEntity());
httpclient.close();
}
} catch (Exception e) {
logger.error("Error while calling service: " + e.getMessage() + serviceUrl);
return null;
}
}
/**
* Calls POST service
*/
def callPostRestServiceAndReturnJson(serviceUrl, queryToPostJson, authHash) {
Logger logger = Logger.getLogger("inventoryplugin.groovy.script")
try {
// get asset ID(s)
def baseurl = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
CloseableHttpClient httpclient = HttpClients.createDefault();
def httpPost = new HttpPost(baseurl + serviceUrl);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
httpPost.setHeader("Authorization", authHash);
def entity = new StringEntity(queryToPostJson);
httpPost.setEntity(entity);
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
if (response.getStatusLine().getStatusCode() == 200) {
return streamToJson(response.getEntity().getContent())
} else {
return null;
}
} finally {
EntityUtils.consume(response.getEntity());
httpclient.close();
}
} catch (Exception e) {
logger.error("Error while calling service:" + serviceUrl, e);
return null;
}
}
String getAssetName(issueObj, assetNameCustomFieldId) {
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject(assetNameCustomFieldId)
return issueObj.getCustomFieldValue(cField);
}
int getOrderAmount(issueObj, orderAmountCustomFieldId) {
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject(orderAmountCustomFieldId)
def orderAmount = issueObj.getCustomFieldValue(cField);
return (orderAmount == null) ? 0 : orderAmount as Integer;
}
def createAsset(issueObj, assetTypeCustomFieldId, assetNameCustomFieldId, stockAttributeId, orderAmountCustomFieldId, authHash) {
Logger logger = Logger.getLogger("inventoryplugin.groovy.script")
int assetTypeId = getAssetTypeId(issueObj, assetTypeCustomFieldId);
if (assetTypeId > 0) {
def addStockAttr = false;
def assetTypeJson = callGetRestServiceAndReturnJson("/rest/jip-api/1.0/form/" + assetTypeId + ".json", authHash);
logger.debug('assetTypeJson')
logger.debug(assetTypeJson)
if (assetTypeJson == null || assetTypeJson.attributes == null) {
return null;
}
// loop JsonSlurper
assetTypeJson.attributes.each {
if (it.attribute.id == stockAttributeId) {
addStockAttr = true;
}
}
// get asset ID(s)
def assetName = getAssetName(issueObj, assetNameCustomFieldId)
// this is the search query parameters. When you search assets on Asset Navigator,
// same parameters appear on Developer tools-> Network Tab. You can make different searches and get the parameters from there when you need.
def queryToPostJson = "{\n" +
" \"formId\": " + assetTypeId + ",\n" +
" \"name\": \"" + assetName + "\",\n";
// if asset type has stock attribute set it
if (addStockAttr) {
queryToPostJson += " \"attributes\": [ {\"attributeId\": " + stockAttributeId +
", \"attributeValue\": \"" + getOrderAmount(issueObj, orderAmountCustomFieldId) + "\"}] ";
} else {
queryToPostJson += " \"attributes\": [ ] ";
}
queryToPostJson += " }"
logger.debug(queryToPostJson);
def assetJson = callPostRestServiceAndReturnJson("/rest/jip-api/1.0/inventory.json", queryToPostJson, authHash)
if (assetJson != null) {
return assetJson.id;
} else {
return null;
}
}
}
if (isCustomFieldEmpty(issue, assetCustomFieldId)) {
if (isNotInStockCase(issue, inStockCustomFieldId, inStockNoValue)) {
Integer assetId = createAsset(issue, assetTypeCustomFieldId, assetNameCustomFieldId, stockAttributeId, orderAmountCustomFieldId, AUTH_HASH);
if (assetId != null && assetId > 0) {
updateAssetCustomFieldOfTheIssue(issue, assetCustomFieldId, issue.getKey(), "," + assetId + ",")
return true;
}
}
}
return false;
Variables to set
Inspect the Issue Edit screen, locate the IDs of the fields, and then update the script.
def assetCustomFieldId = 'customfield_10227';
def assetTypeCustomFieldId = 'customfield_10314';
def inStockCustomFieldId = 'customfield_10317';
def orderAmountCustomFieldId = 'customfield_10316';
def assetNameCustomFieldId = 'customfield_10318';
def inStockNoValue = 'No'
def AUTH_HASH = 'Basic YWRtaW46YWRtaW4='; // auth hash for authorization. this sample is for user=admin, and password=admin
def stockAttributeId = 67; // asset "Number of items in stock" attribute id
Example: Post function location
Test
After the Done
transition, a new asset will be created with inventory and assigned to the issue.
Before | After |
---|---|
Â