Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

I want to store Asset Object value of Field A into another Asset Field

Sriharsha
Contributor
September 3, 2025

Hi all,

I have two asset object fields, called Field A and Field B.

Let’s say I added the value “Jira” to Field A, and later changed it to “Confluence.” Now, I want to store the original value, "Jira," in Field B after it was changed to "Confluence." Could you please suggest a solution?


I am using Data Center v10.3.7, and we have ScriptRunner for Jira. I’m using the listener below, but I’m not getting the intended result.


import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import org.apache.log4j.Logger

def log = Logger.getLogger("com.example.plugins")

// IDs of the relevant Asset object fields
def sourceFieldId = "customfield_XXXXX"
def targetFieldId = "customfield_XXXXX"

def issue = event.issue
def cfManager = ComponentAccessor.customFieldManager
def sourceCF = cfManager.getCustomFieldObject(sourceFieldId)
def targetCF = cfManager.getCustomFieldObject(targetFieldId)

if (!sourceCF || !targetCF) {
log.warn("Source or target custom field not found.")
return
}

// Use field name for change detection
def sourceCFName = sourceCF.name
def changeItem = event.getChangeLog()?.getRelated("ChildChangeItem")?.find {
it.field == sourceCFName && it.oldstring != it.newstring
}
if (!changeItem) {
log.info("No change detected for Field A (${sourceCFName}). Skipping update.")
return
}

def mutableIssue = issue as com.atlassian.jira.issue.MutableIssue
def selected = mutableIssue.getCustomFieldValue(sourceCF)
if (!selected) {
log.info("No value selected in Field A.")
return
}

// Handle single-select field: get the object directly
def sourceObject = (selected instanceof Collection) ? selected[0] : selected
if (!sourceObject) {
log.warn("Source object is null after extraction.")
return
}

// Copy the Asset object to the target field
mutableIssue.setCustomFieldValue(targetCF, [sourceObject])

// Persist the issue update (user context is managed by ScriptRunner listener configuration)
ComponentAccessor.issueManager.updateIssue(
ComponentAccessor.jiraAuthenticationContext.loggedInUser,
mutableIssue,
EventDispatchOption.DO_NOT_DISPATCH,
false
)

if (sourceObject instanceof com.riadalabs.jira.plugins.insight.services.model.ObjectBean) {
log.info("Copied asset object '${sourceObject.name}' from Field A to Field B for issue ${issue.key}.")
} else {
log.warn("sourceObject is not an ObjectBean. Actual type: ${sourceObject?.getClass()?.name}")
}


 

2 answers

1 vote
Stefan Stadler
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 3, 2025

Hi @Sriharsha 

I have done multiple times a very similar stuff. Adding ObjectBeans to issues has always been working for me using the objectKey attribute of ObjectBean. This is also ensuring that there is no translation or such stuff, if you are working in a multi-language environment (not necessarily true for Asset objects, but still something to consider).

Therefore, I would change the setCustomFieldValue method accordingly:

mutableIssue.setCustomFieldValue(targetCF, [sourceObject.objectKey])

What you could also consider is to validate, if the sourceObject actually has the objectKey attribute and print it - at least for debugging purposes.

So my total change would look like this:

// Copy the Asset object to the target field
if(sourceObject?.objectKey){
   log.info("Setting source object with key ${sourceObject.objectKey}")
   mutableIssue.setCustomFieldValue(targetCF, [sourceObject.objectKey])
   
   // Persist the issue update (user context is managed by ScriptRunner listener configuration)
   ComponentAccessor.issueManager.updateIssue(
      ComponentAccessor.jiraAuthenticationContext.loggedInUser,
      mutableIssue,
      EventDispatchOption.DO_NOT_DISPATCH,
      false
   )

   log.info("Copied asset object '${sourceObject.name}' from Field A to Field B for issue ${issue.key}.")
}
else{
   log.warn("sourceObject appears to be not an ObjectBean. Actual type: ${sourceObject?.getClass()?.name}")
}

Hope this helps!

Stefan

Sriharsha
Contributor
September 4, 2025

Thank you so much, @Stefan Stadler ! Let me check and update you ASAP.

Sriharsha
Contributor
September 4, 2025

@Stefan Stadler  - I'm getting an issue. Can you please provide me the full script?

 

Best regards,

Sriharsha

Sriharsha
Contributor
September 4, 2025

Changed it like this - 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import org.apache.log4j.Logger

def myLog = Logger.getLogger("com.example.plugins")

// IDs of the relevant Asset object fields
def sourceFieldId = "customfield_XXXXX"
def targetFieldId = "customfield_XXXXX"

def issue = event.issue
def cfManager = ComponentAccessor.customFieldManager
def sourceCF = cfManager.getCustomFieldObject(sourceFieldId)
def targetCF = cfManager.getCustomFieldObject(targetFieldId)

if (!sourceCF || !targetCF) {
    myLog.warn("Source or target custom field not found.")
    return
}

// Use field name for change detection
def sourceCFName = sourceCF.name
def changeItem = event.getChangeLog()?.getRelated("ChildChangeItem")?.find {
    it.field == sourceCFName && it.oldstring != it.newstring
}
if (!changeItem) {
    myLog.info("No change detected for Field A (${sourceCFName}). Skipping update.")
    return
}

def mutableIssue = issue as com.atlassian.jira.issue.MutableIssue
def selected = mutableIssue.getCustomFieldValue(sourceCF)
if (!selected) {
    myLog.info("No value selected in Field A.")
    return
}

// Check if selected is a collection (if multi-select) or a single object (single-select)
def sourceObject = (selected instanceof Collection) ? selected[0] : selected

// If sourceObject is an ObjectBean, it should have the objectKey
if (sourceObject instanceof com.riadalabs.jira.plugins.insight.services.model.ObjectBean) {
    if (sourceObject?.objectKey) {
        myLog.info("Setting source object with key ${sourceObject.objectKey}")
       
        // Load the ObjectBean using the objectKey
        def objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade)
        def objectBean = objectFacade.loadObjectBean(sourceObject.objectKey)

        // Set the objectBean to the target field
        mutableIssue.setCustomFieldValue(targetCF, [objectBean])

        // Persist the issue update (user context is managed by ScriptRunner listener configuration)
        ComponentAccessor.issueManager.updateIssue(
            ComponentAccessor.jiraAuthenticationContext.loggedInUser,
            mutableIssue,
            EventDispatchOption.DO_NOT_DISPATCH,
            false
        )

        myLog.info("Copied asset object '${objectBean.name}' from Field A to Field B for issue ${issue.key}.")
    } else {
        myLog.warn("sourceObject does not have an objectKey: ${sourceObject?.name}")
    }
} else {
    myLog.warn("Selected value is not an ObjectBean. Actual type: ${selected?.getClass()?.name}")
}
Stefan Stadler
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 4, 2025

Hi @Sriharsha 

getting the ObjectBean once more should not be necessary as the object from getCustomFieldValue is basically already a List of ObjectBean objects (which in your case only includes one member). Usually, it is the safest to assign the issue the objectKey of the desired object. That is why I am using the objectKey. The data needs to be passed in as an array independent from having an Asset custom field with single or multiple selection.

This is the complete code (untested as I do not have an Asset installation handy at the moment). I have also changed the ObjectBean.name into ObjectBean.label as name has been deprecated:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import org.apache.log4j.Logger

def myLog = Logger.getLogger("com.example.plugins")

// IDs of the relevant Asset object fields
def sourceFieldId = "customfield_XXXXX"
def targetFieldId = "customfield_XXXXX"

def issue = event.issue
def cfManager = ComponentAccessor.customFieldManager
def sourceCF = cfManager.getCustomFieldObject(sourceFieldId)
def targetCF = cfManager.getCustomFieldObject(targetFieldId)

if (!sourceCF || !targetCF) {
myLog.warn("Source or target custom field not found.")
return
}

// Use field name for change detection
def sourceCFName = sourceCF.name
def changeItem = event.getChangeLog()?.getRelated("ChildChangeItem")?.find {
it.field == sourceCFName && it.oldstring != it.newstring
}
if (!changeItem) {
myLog.info("No change detected for Field A (${sourceCFName}). Skipping update.")
return
}

def mutableIssue = issue as com.atlassian.jira.issue.MutableIssue
def selected = mutableIssue.getCustomFieldValue(sourceCF)
if (!selected) {
myLog.info("No value selected in Field A.")
return
}

// Check if selected is a collection (if multi-select) or a single object (single-select)
def sourceObject = (selected instanceof Collection) ? selected[0] : selected

// If sourceObject is an ObjectBean, it should have the objectKey
if (sourceObject instanceof com.riadalabs.jira.plugins.insight.services.model.ObjectBean) {
if (sourceObject?.objectKey) {
myLog.info("Setting source object with key ${sourceObject.objectKey}")

// Set the objectBean to the target field
mutableIssue.setCustomFieldValue(targetCF, [sourceObject.objectKey])

// Persist the issue update (user context is managed by ScriptRunner listener configuration)
ComponentAccessor.issueManager.updateIssue(
ComponentAccessor.jiraAuthenticationContext.loggedInUser,
mutableIssue,
EventDispatchOption.DO_NOT_DISPATCH,
false
)

myLog.info("Copied asset object '${sourceObject.label}' from Field A to Field B for issue ${issue.key}.")
} else {
myLog.warn("sourceObject does not have an objectKey: ${sourceObject?.label}")
}
} else {
myLog.warn("Selected value is not an ObjectBean. Actual type: ${selected?.getClass()?.name}")
}

 

Sriharsha
Contributor
September 4, 2025

Hi @Stefan Stadler , the field is bringing the main label attribute data which is a default text attribute.

Sriharsha
Contributor
September 4, 2025

Hi @Stefan Stadler ,  I have made some changes and the below script is working as expected --

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import org.apache.log4j.Logger
import com.riadalabs.jira.plugins.insight.services.model.ObjectBean
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade
import com.riadalabs.jira.plugins.insight.channel.external.api.facade.IQLFacade

def myLog = Logger.getLogger("com.example.plugins")

// IDs of the relevant Asset object fields
def sourceFieldId = "customfield_XXXXX" // Source field
def targetFieldId = "customfield_XXXXX" // Target field

def issue = event.issue
def cfManager = ComponentAccessor.customFieldManager
def sourceCF = cfManager.getCustomFieldObject(sourceFieldId)
def targetCF = cfManager.getCustomFieldObject(targetFieldId)

if (!sourceCF || !targetCF) {
    myLog.warn("Source or target custom field not found.")
    return
}

// Detect change in source field
def sourceCFName = sourceCF.name
def changeItem = event.getChangeLog()?.getRelated("ChildChangeItem")?.find {
    it.field == sourceCFName && it.oldstring != it.newstring
}

if (!changeItem) {
    myLog.info("No change detected for Field A (${sourceCFName}). Skipping update.")
    return
}

// Get the old value from change history
def oldString = changeItem.oldstring ?: changeItem.from
if (!oldString) {
    myLog.warn("Previous value is null or not available.")
    return
}

myLog.info("Raw previous value from history: ${oldString}")

// Try to extract object key from format like "181964 [ IM-101980 ]"
def matcher = oldString =~ /(\d+)\s*\[\s*(WI-\d+)\s*\]/
def assetKey = null
def previousObject = null

def objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(ObjectFacade)

try {
    if (matcher) {
        assetKey = matcher[0][2]
        myLog.info("Extracted Asset Key: ${assetKey}")
        previousObject = objectFacade.loadObjectBean(assetKey)
    } else {
        def objectId = oldString as Integer
        myLog.info("Fallback: treating '${oldString}' as object ID.")
        previousObject = objectFacade.loadObjectBean(objectId)
    }
} catch (Exception e) {
    myLog.warn("Failed to load object by key or ID: ${e.message}")
}

// Fallback: Search by  XXXXX attribute if object not found
if (!previousObject && oldString) {
    try {
        def iqlFacade = ComponentAccessor.getOSGiComponentInstanceOfType(IQLFacade)
        def objectSchemaId = XX
        def attributeName = "XXXXXXX"
        def iqlQuery = "\"${attributeName}\" = \"${oldString}\""

        def results = iqlFacade.findObjects(objectSchemaId, iqlQuery)
        if (results && !results.isEmpty()) {
            previousObject = results.first()
            myLog.info("Found object via IQL search: ${previousObject.label}")
        } else {
            myLog.warn("No object found via IQL for Purchase Order = '${oldString}'")
        }
    } catch (Exception e) {
        myLog.error("IQL search failed: ${e.message}")
    }
}

if (!previousObject) {
    myLog.warn("No matching object found for value: ${oldString}")
    return
}

myLog.info("Found previous object: ${previousObject.label}")

// Set the previous object to Field B
def mutableIssue = issue as com.atlassian.jira.issue.MutableIssue
mutableIssue.setCustomFieldValue(targetCF, [previousObject])

ComponentAccessor.issueManager.updateIssue(
    ComponentAccessor.jiraAuthenticationContext.loggedInUser,
    mutableIssue,
    EventDispatchOption.DO_NOT_DISPATCH,
    false
)

myLog.info("Set previous object '${previousObject.label}' into Field B for issue ${issue.key}.")
0 votes
Dakwak Kyeng Monday
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
September 3, 2025

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
SERVER
VERSION
10.3.7
PRODUCT PLAN
STANDARD
TAGS
AUG Leaders

Atlassian Community Events