Moving Insight objects via Groovy fails

Christof Hurst December 29, 2021

Hi,
as we have to move objects in various steps we want to automate this with groovy. I've seen similar requests in the forum but no valid answers.

The move with the following script works but the details / attributes are lost.
The objectTypes have the same parent and the source has one additional attribute that will be discarded.

Anybody with an idea?


import com.riadalabs.jira.plugins.insight.services.model.move.MoveObjectMapping
import com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping
import com.riadalabs.jira.plugins.insight.services.model.move.MoveObjectBean
import helper.InsightManager

def im = new InsightManager()

def asset = im.getObjectBean("PROD-208936")

def moveMap = new MoveObjectMapping().create()
def fromAtts = im.objectTypeAttributeFacade.findObjectTypeAttributeBeans(asset.objectTypeId)
def toAtts = im.objectTypeAttributeFacade.findObjectTypeAttributeBeans(20)

moveMap.map(59,MoveAttributeMapping.create(fromAtts.find{id=59},toAtts.find{id=59})) // Key
moveMap.map(259,MoveAttributeMapping.create(fromAtts.find{id=259},toAtts.find{id=259})) // Artikelnummer
moveMap.map(60,MoveAttributeMapping.create(fromAtts.find{id=60},toAtts.find{id=60})) // Artikelname
moveMap.map(61,MoveAttributeMapping.create(fromAtts.find{id=61},toAtts.find{id=61})) // Created
moveMap.map(4522,MoveAttributeMapping.create(fromAtts.find{id=4522},toAtts.find{id=4522})) // Hersteller
moveMap.map(62,MoveAttributeMapping.create(fromAtts.find{id=62},toAtts.find{id=62})) // Updated
moveMap.map(4839,MoveAttributeMapping.create(fromAtts.find{id=4839},toAtts.find{id=4839})) // Gewicht
moveMap.map(403,MoveAttributeMapping.create(fromAtts.find{id=403})) // Kategorie

def mObject = new MoveObjectBean()

mObject.setFromObjectTypeId(asset.objectTypeId)
mObject.setObjectSchemaId(3)
mObject.setToObjectTypeId(20)
mObject.setMapping(moveMap)
mObject.setReferences(mObject.references.valueOf("KEEP_REFERENCES_TO_OBJECT"))
mObject.setIql("Key = " + asset.objectKey)

im.objectFacade.moveObjects(mObject)
im.objectFacade.storeObjectBean(asset.createMutable())

4 answers

1 accepted

1 vote
Answer accepted
Christof Hurst December 31, 2021

Sorry, the stupiest mistake. In the find closure there were assigments (id=59) instead of comparison (it.id==59).

Carlos Tichy May 3, 2023

@Christof Hurst , Hi! For God's sake, please tell me what library InsightManager you imported into your script. Is it the same InsightManagerForScriptrunner.groovy the wrapper from Riada?

Where can be found a release of IM that can be run by Insight own groovy capabilities?

Thank's for your help! I'm also struggling to move objects from one objectType to another objectType.

 

Best regards.

Christof Hurst May 3, 2023

Hi,

I use an adapted script of the one you mentioned. I made it more fail safe.

If you use the wrapper of Riada, my above mentioned script will work if you use id.equals(<attr_id>)

BR Christof

Carlos Tichy May 4, 2023

Hi Christof,

Thanks for your reply. I appreciate your help with the InsightManager explanation. Would it be possible for you to share the adaptation with me or at least let me know how you adapted it and where you located it so that I can access it from the Groovy console?

Thanks in advance for your assistance.

Best regards,
Carlos Tichy

Christof Hurst May 4, 2023

Just add the class in the scriptrunner editor. In my case in path /helper/

I renamed the class and the file to "Insightmanager" (InsightManager.groovy)

Carlos Tichy May 4, 2023

/helper ? ... like $JiraHomePath/helper ?  (/var/atlassian/application-data/JIRA/helper/InsightManager.groovy)

Christof Hurst May 4, 2023

Go to Scriptrunner Editor, make a folder "helper" on root level, create file InsightManager.groovy in that folder und put the code in it. 

Then you can access via "import helper/InsightManager" and you can create an instance via "def im = new InsightManager()"

Like Carlos Tichy likes this
Carlos Tichy May 4, 2023

Or  C:\Program Files\Atlassian\Application Data\JIRA\helper\InsightManager.groovy  ?

Christof Hurst May 4, 2023

I don't know. Why not using Scriptrunner Editor? Best and easiest way.

Like Carlos Tichy likes this
vahem2lu May 4, 2023

I have added this script to filesystem and then go to JIRA_URL/secure/admin/InsightPluginWhiteList.jspa and add this as allowed script.

Then in automation I've added "action" to run this script.

As I have all Groovy scripts in git, Jenkins deployed etc.

But overall it's the same goal as using Script Editor like mentioned.

1 vote
Carlos Tichy May 5, 2023

Hello everyone on the forum, first of all, I want to thank you for the support and collaboration in this community; as a token of appreciation, I want to contribute my grain of sand by sharing with you this script (which was achieved thanks to the contributions from this and other threads). This script moves any object from one object type to another, mapping the attributes according to the structure of the destination object type.

 

import com.atlassian.jira.component.ComponentAccessor;
import com.riadalabs.jira.plugins.insight.services.model.move.MoveObjectMapping;
import com.riadalabs.jira.plugins.insight.services.model.move.MoveObjectBean;
import com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping;
import com.riadalabs.jira.plugins.insight.services.progress.model.Progress

Class objectFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade");
def objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectFacadeClass);
Class objectTypeFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeFacade");
def objectTypeFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeFacadeClass);
Class objectTypeAttributeFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade");
def objectTypeAttributeFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeAttributeFacadeClass);
Class moveProgressClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.services.progress.model.Progress");
def moveProgress = ComponentAccessor.getOSGiComponentInstanceOfType(moveProgressClass);

/*
* Define some variables
*/

// set schemaId and typeId of source and destination objectTypes (secure/InsightConfigure.jspa?objecttypeid="SOURCE_AND_DEST_OBJECT_TYPES")
int schemaId = 22; // <- Replace here with your own value
int sourceObjectTypeId = 12463; // <- Replace here with your own value
int destObjectTypeId = 12562; // <- Replace here with your own value

// set verificationAttributeId which is the ID of "Status" in source objectType (/secure/ObjectSchema.jspa?id="YOUR_SCHEMA_ID"&typeId="SOURCE_OBJECT_TYPE_ID"&view=list&mode=attribute)
int verificationAttributeId = 250332; // <- Replace here with your own value

// set verificationAttributeValue Id based on Jira Insight ID (/secure/InsightConfigure.jspa?id="YOUR_SCHEMA_ID"&tab=statuses)
int verificationAttributeValue = 234; // <- Replace here with your own value

// actual object ID and KEY from current request
int objectId = object.getId();
String objectKey = object.getObjectKey();

// action to take if the object to move has inboundReferences (options: "REMOVE_REFERENCES_TO_OBJECT" || "KEEP_REFERENCES_TO_OBJECT" || "DO_NOT_MOVE_OBJECT")
String ifInboundReferences = "REMOVE_REFERENCES_TO_OBJECT" // <- Replace here with your own value ("ADVICE: Do the exercise on the GUI beforehand to see the real behavior.")

/*
* Gets the actual values of current object
*/

// get the current objectType to verify
def currentObjectTypeId = object.getObjectTypeId();
if (currentObjectTypeId != sourceObjectTypeId) {
log.info("currentObjectTypeId is ${currentObjectTypeId} and it was expected ${sourceObjectTypeId}. Mistmatch of source objectTypes; no action taken on object ${objectKey}");
return false;
}

// get the current verification value of the "Status" objectAttribute to verify
def currentVerificationAttributeValue = objectFacade.loadObjectAttributeBean(objectId, verificationAttributeId).getObjectAttributeValueBeans()[0].getValue();
if (currentVerificationAttributeValue != verificationAttributeValue) {
log.info("currentVerificationAttributeValue is ${currentVerificationAttributeValue} and it was expected ${verificationAttributeValue}. Mistmatch of value in the Status objectAttribute; no action taken on object ${objectKey}");
return false;
}

/*
* Define move Map - for mapping attributes between two objectTypes (source objectType -> destination objectType)
*/
MoveObjectMapping moveMap = new MoveObjectMapping();
def sourceAttributes = objectTypeAttributeFacade.findObjectTypeAttributeBeans(sourceObjectTypeId);
def destAttributes = objectTypeAttributeFacade.findObjectTypeAttributeBeans(destObjectTypeId);

// Map attributes based on destination map structure to properly adjust source object attributes with their corresponding destination attributes.
sourceAttributes?.each {
sourceAttribute->
def destAttribute = destAttributes.find {
it.name == sourceAttribute.name && it.type == sourceAttribute.type
}
def attributesMap
if (!destAttribute) {
log.warn("Nothing to map to ${sourceAttribute.name}")
attributesMap = MoveAttributeMapping.create(sourceAttribute)
} else {
attributesMap = MoveAttributeMapping.create(sourceAttribute, destAttribute)
}
log.info("Adding mapping of ${sourceAttribute.name} from ${sourceAttribute.id} to ${destAttribute?.id}")
moveMap.map(sourceAttribute.id, attributesMap)
}

/**
* Defines the moveObject and moveObjectProgress to performs the move.
*/
def moveObject = new MoveObjectBean()
def Progress moveObjectProgress
moveObject.objectSchemaId = schemaId
moveObject.fromObjectTypeId = sourceObjectTypeId
moveObject.toObjectTypeId = destObjectTypeId
moveObject.mapping = moveMap
moveObject.references = moveObject.references.valueOf(ifInboundReferences)


// performing the actual move.
try {
moveObject.iql = "Key == ${objectKey}"
if (moveObject != null) {
moveObjectProgress = objectFacade.moveObjects(moveObject)
}
} catch (Exception vie) {
log.warn("Could not move object ${objectKey} due to Exception: ${vie.message}")
}
int curWait = 0
int maxWaitMillis = 60000
log.info("Waiting for move to finish: ${curWait} (${moveObjectProgress.status})}")
while (!moveObjectProgress.isFinished() && curWait < maxWaitMillis) {
Thread.sleep(100)
curWait = curWait + 100
log.warn("Waiting for move to finish: ${curWait} (${moveObjectProgress.status})")
}
if (curWait >= maxWaitMillis){
log.warn("Unknown final status moving object ${objectKey} from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}; maxWaitMillis reached while waiting for move to finish.")
return false;
}
if (!moveObjectProgress.isError()) {
log.info("Object ${objectKey} moved from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}")
return true;
} else {
log.error("Object ${objectKey} failed to move from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}")
return false;
}
0 votes
vahem2lu October 18, 2022

@Christof Hurstdid you find solution for this? In my case move is working, but also attributes are gone after moving an object form one typeId to another (in the same schema).

Christof Hurst October 18, 2022

Solution is in the accepted answer. If attributes are missing I would presume that they are not in the list to be copied or not with the correct data or data format.

vahem2lu October 18, 2022

I am using .each function and not mapping them manually with IDs, but I get the same result with your example and mapping them manually.

toAtts?.each { item ->
MoveAttributeMapping temp = MoveAttributeMapping.create(item, fromAtts[index]);
moveMap.map(fromAtts[index].id, temp);
index++;
}

 

After I've done the mapping my MoveAttributeMap object shows like this:

moveObject: MoveObjectBean{fromObjectTypeId=69,
toObjectTypeId=100,
mapping=MoveObjectMapping{attributeMappingMap={789=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@2a38a0a1,
791=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@39fd8d10,
792=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@221dac8a,
841=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@1061a041,
842=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@7f333744,
843=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@2a73a0bf,
844=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@65c53087,
845=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@78bbf7d6,
846=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@1ed46692,
847=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@29955baf}},
iql=Key = KAS-132313,
objectSchemaId=3,
references=KEEP_REFERENCES_TO_OBJECT}

 Which looks like the mapping is done correctly, no?

Christof Hurst October 18, 2022

The index variable is undefined. Can't work.

vahem2lu October 18, 2022

def index = 0; is set before this each loop.

Christof Hurst October 18, 2022

Sorry I don't know. I know that my method is working. I would never trust this automatic way. I always define the set of attributes explicitely by myself.

0 votes
Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
December 29, 2021

I have implemented a similar move (between two child object types with the same parent) and didn't need to use the "storeObjectBean" method.

So I wonder if that's what tripping your script. 

The moveObjects method kicks off an asynchronous process so in your case, perhaps the storeObjectBean method is attempting to save the old version of the object while the move is still being processed.

I had a similar problem when I was trying to populate a new attribute (date/time of the move on the target object after the move) before the move was complete.

I solved it by waiting for the move to complete before resuming any other part of my script.

Something like this:

def moveProgress = objectFacade.moveObjects(mObject)
Integer curWait = 0
Integer maxWaitMillis = 10000

log.debug "Waiting for move to finish: $curWait ($moveProgress.status)"
while (moveProgress.isFinished() == false && curWait < maxWaitMillis) {
Thread.sleep(100)
curWait = curWait + 100
log.debug "Waiting for move to finish: $curWait ($moveProgress.status)"
}
if (moveProgress.isFinished()) {
log.info "Move complete"
} else if (moveProgress.isError()) {
log.error "error during move"
return
} else {
log.warn "maxWaitMillis reach while waiting for move to finish."
return
}
//resume post move actions here, probably safects to re-laod your object bean after the move rather than keep the old asset object
Christof Hurst December 31, 2021

Thanks for this answer but it didn't solve the problem.

The move now takes some millis to finish but the result is the same. All details are gone. Even the label. Line with storeObject was deleted. A clean reindex also didn't helped.

Actually the script is complete as shown above. There are no further actions. As the script is pretty simple and straight forward I have no glue.

vahem2lu October 18, 2022

@Christof Hurstdid you find solution for this?

My attributes are gone too after a successful object movement from one typeId to another.

Christof Hurst October 18, 2022

Then I think finding the correct value in the old object is not working. For me it was = instead of ==

Suggest an answer

Log in or Sign up to answer