Hello,
I'm trying to move an Insight object from one object type to another. Of course, if you are using the interface, this is no problem. But we want to move an object in a post function. The background of this is that we have a JIRA process that handles Hardware exchanges. At a certain status the user is asked, if the "old" Hardware should be scraped or put back in the pool for further usage.
If the user chooses "Scrape" the Hardware will be moved to the object type "Deprecated Hardware". With this, we can still access the old hardware in the Insight Backend, but remove the object from being chosen by a user during a "New User" Workflow.
I'm using the code below, and it does actually move the object (I'm using JMWE Groovy Script), but unfortunately without any values, but I can't figure out, where the problem in my code is. Help is much appreciated.
Thanks a lot, Michael
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;
/* Get Insight Object Facade from plugin accessor */
Class objectFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade");
def objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectFacadeClass);
Class objectTypeAttFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade");
def objectTypeAttFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeAttFacadeClass);
def moveMap = new MoveObjectMapping();
def fromAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(826);
def toAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(832);
def index = 0;
toAtts?.each { item ->
MoveAttributeMapping temp = MoveAttributeMapping.create(item,fromAtts[index]);
moveMap.map(fromAtts[index].id,temp);
index++;
}
def mObject = new MoveObjectBean();
mObject.setFromObjectTypeId(826);
mObject.setObjectSchemaId(47);
mObject.setToObjectTypeId(832);
mObject.setMapping(moveMap);
mObject.setReferences(mObject.getReferences().valueOf("KEEP_REFERENCES_TO_OBJECT"));
def insightObjects = issue.get("Old asset");
if (insightObjects != null) {
insightObjects.each{insightObject ->
/* Move object/s */
try {
mObject.setIql("Key = " + insightObject.objectKey);
objectFacade.moveObjects(mObject);
} catch (Exception vie) {
log.warn("Could not update object attribute due to validation exception:" + vie.getMessage());
}
}
}
return true
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;
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);
/*
* 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 performs it.
*/
// definition
def moveObject = new MoveObjectBean()
moveObject.objectSchemaId = schemaId
moveObject.fromObjectTypeId = sourceObjectTypeId
moveObject.toObjectTypeId = destObjectTypeId
moveObject.mapping = moveMap
moveObject.references = moveObject.references.valueOf(ifInboundReferences)
// performing the actual move.
try {
if (object != null) {
moveObject.iql = "Key == ${objectKey}"
objectFacade.moveObjects(moveObject)
log.info("Object ${objectKey} moved from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}")
}
} catch (Exception vie) {
log.warn("Could not move object ${objectKey} due to Exception: ${vie.message}")
}
return true;
Hi Guys,
I tried this script with automation, but it do not work. Do you have any idea what may wrong with it?
BR,
Attila
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger log = LoggerFactory.getLogger("1. log");
/* Get Insight Object Facade from plugin accessor */
Class objectFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade");
def objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectFacadeClass);
Class objectTypeAttFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade");
def objectTypeAttFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeAttFacadeClass);
def moveMap = new MoveObjectMapping();
def fromAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(object.getObjectTypeId());
def toAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(144);
def index = 0;
toAtts?.each { item ->
MoveAttributeMapping temp = MoveAttributeMapping.create(item,fromAtts[index]);
moveMap.map(fromAtts[index].id,temp);
index++;
}
def mObject = new MoveObjectBean();
mObject.setFromObjectTypeId(object.getObjectTypeId());
mObject.setObjectSchemaId(1);
mObject.setToObjectTypeId(144);
mObject.setMapping(moveMap);
mObject.setReferences(mObject.getReferences().valueOf("KEEP_REFERENCES_TO_OBJECT"));
def insightObjects = object;
if (insightObjects != null)
{
/* Move object */
try
{
mObject.setIql("Key = " + insightObjects.objectKey);
objectFacade.moveObjects(mObject);
objectFacade.storeObjectBean(insightObjects);
} catch (Exception vie)
{
log.warn("Could not update object attribute due to validation exception:" + vie.getMessage());
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@attilasalanki1983old topic, but did you resolve your problem? Was your problem with missing attributes?
I am using quite same logic and object gets moved, but all the attributes are empty.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I thought I'd not do a new topic, but rather update this old one as the problem is still the same and there are some few topics here as well, but they all end up with the same result - no attributes are being updated.
As this does not let me to submit my code highlihted I've used Pastebin. See my reply with working example.
The idea is the same - when objectType "Domains" attribute "STATUS" value gets changed to "NOT USED" I would like to move this Insight object to a child objectType ("Domains not used"). Child objectType holds the same attributes and in the same order as parent. Schema is the same.
I am going to use automation which is simplifed like this: when object is updated and IQL (insert your necessary condition here) execute (this) groovy script.
I have done some extra checks to script, but this is not definitely necessary, because the idea is the same as previously posted here.
@Peter-Dave Sheehanmention here, as you have done the same in another topic: https://community.atlassian.com/t5/Jira-Software-questions/Moving-Insight-objects-via-Groovy-fails/qaq-p/1897498
When I'm doing mapping manually like in this quoted thread (see link before) I will get all attributes in the KEY value.
When I'm doing mapping automatically (with each), all attributes are "lost", but the object gets moved.
I have tried "sleep", to wait for object to move, it does not change the result.
I have tried to change the order (attribute map before and actually move later), also the result is the same.
I'm going to post some log.info output here:
2022-10-20 08:43:10,414 [http-nio-8080-exec-4] | fromAtts: [
ObjectTypeAttributeBean [id=789, name=Key, type=DEFAULT, defaultType=TEXT],
ObjectTypeAttributeBean [id=791, name=Created, type=DEFAULT, defaultType=DATE_TIME],
ObjectTypeAttributeBean [id=792, name=Updated, type=DEFAULT, defaultType=DATE_TIME],
ObjectTypeAttributeBean [id=841, name=Domeeninimi, type=DEFAULT, defaultType=TEXT],
ObjectTypeAttributeBean [id=842, name=Kontakt, type=REFERENCED_OBJECT, defaultType=NONE],
ObjectTypeAttributeBean [id=843, name=Kontakt_, type=DEFAULT, defaultType=TEXT],
ObjectTypeAttributeBean [id=844, name=Kus registris, type=DEFAULT, defaultType=SELECT],
ObjectTypeAttributeBean [id=845, name=Aegub, type=DEFAULT, defaultType=DATE],
ObjectTypeAttributeBean [id=846, name=Staatus, type=STATUS, defaultType=NONE],
ObjectTypeAttributeBean [id=847, name=Kommentaar, type=DEFAULT, defaultType=TEXT]
]
2022-10-20 08:43:10,414 [http-nio-8080-exec-4] | toAtts: [
ObjectTypeAttributeBean [id=1223, name=Key, type=DEFAULT, defaultType=TEXT],
ObjectTypeAttributeBean [id=1225, name=Created, type=DEFAULT, defaultType=DATE_TIME],
ObjectTypeAttributeBean [id=1226, name=Updated, type=DEFAULT, defaultType=DATE_TIME],
ObjectTypeAttributeBean [id=1227, name=Domeeninimi, type=DEFAULT, defaultType=TEXT],
ObjectTypeAttributeBean [id=1228, name=Kontakt, type=REFERENCED_OBJECT, defaultType=NONE],
ObjectTypeAttributeBean [id=1229, name=Kontakt_, type=DEFAULT, defaultType=TEXT],
ObjectTypeAttributeBean [id=1230, name=Kus registris, type=DEFAULT, defaultType=SELECT],
ObjectTypeAttributeBean [id=1231, name=Aegub, type=DEFAULT, defaultType=DATE],
ObjectTypeAttributeBean [id=1232, name=Staatus, type=STATUS, defaultType=NONE],
ObjectTypeAttributeBean [id=1233, name=Kommentaar, type=DEFAULT, defaultType=TEXT]
]
2022-10-20 08:43:10,425 [http-nio-8080-exec-4] | moveObject:
MoveObjectBean{
fromObjectTypeId=69,
toObjectTypeId=100,
mapping=MoveObjectMapping{attributeMappingMap={789=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@5ab84c21,
791=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@c695d39,
792=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@22c08628,
841=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@156ab381,
842=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@dab3c49,
843=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@4b6d3850,
844=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@6d3a16d3,
845=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@2d0c564e,
846=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@79917ed2,
847=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@5b9c6608}},
iql=Key = KAS-132347,
objectSchemaId=3,
references=KEEP_REFERENCES_TO_OBJECT}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
One question before I dig into it. .. when you say "child objecttype" are you leveraging the attribute inheritance (this means that the attribute id is the same for the parent and the child) or do the child and parent have the same attribute names but with different ids?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The first error I see is here:
MoveAttributeMapping.create(item, fromAtts[index]);
You have your 2 arguments reversed. Is't from then to.
But also, how can you be certain your 2 lists of attributes will be in the same sequence?
And are you 100% sure they are of the same type?
Maybe try something like this:
toAtts?.each { toAttribute ->
def fromAttribute = fromAtts.find{it.name == toAttribute.name && it.type == toAttribute.type}
if(!fromAttribute) {
log.warn "Nothing to map to $toAttribute.name"
return
}
MoveAttributeMapping attMap = MoveAttributeMapping.create(fromAttribute, toAttribute);
log.info "Adding mapping of $fromAttribute.name from $fromAttribute.id to $toAttribute.id"
moveMap.map(fromAttribute.id, attMap);
}
Also, don't create a mutable of your object. That's only for editing attributes.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you @Peter-Dave Sheehan, such a rookie mistake for mapping attributes.
For my case right now this object is "tree" child
- CMDB
-- Infra
--- Domain names
---- domains not used
And they always use the same attribute names and types in the same order. IDs of course are different.
Regarding to not/being mutable, if it's not mutable, I will get this error:
Could not update object attribute due to error: No signature of method: com.riadalabs.jira.plugins.insight.channel.external.api.facade.impl.ObjectFacadeImpl.storeObjectBean() is applicable for argument types: (com.riadalabs.jira.plugins.insight.services.model.ObjectBean) values: [domain.tld (KAS-132375)]
Working as expected, I am going to post a working example in some time.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
So my working example of moving an Insight object from one type to another.
I have used some extra checks (especially IDs), but they are not necessary when you "trust" your automation workflow. 😃
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;
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 objectTypeAttFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade");
def objectTypeAttFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeAttFacadeClass);
// Define some variables
// set schemaId and typeId for active and inactive domains (from Jira Insight)
int schemaId = 3;
int activeDomainTypeId = 69;
int inactiveDomainTypeId = 100;
// set statusActiveAttributeId which is the ID of "Staatus" in active Object Type (secure/ObjectSchema.jspa?id=3&typeId=YOUR_ID&view=list&mode=attribute)
// set statusActive and statusNotActive IDs based on Jira Insight ID (secure/InsightConfigure.jspa?id=3&tab=statuses)
int statusActiveAttributeId = 846;
int statusActive = 70;
int statusNotActive = 72;
// object needs to be mutable and initiator gives us the object (which triggers this script)
// current object ID and KEY from actual request
object = object.createMutable();
int objectId = object.getId();
String objectKey = object.getObjectKey();
def objectTypeCurrentId = object.getObjectTypeId();
if ( objectTypeCurrentId != activeDomainTypeId ){
log.info ("objectTypeCurrentId is: " + objectTypeCurrentId + " and we are looking for: " + activeDomainTypeId + ". So, it's a mismatch! Exiting.");
// This object is not in correct typeId, exiting
return false;
}
def objectAttributeValue = objectFacade.loadObjectAttributeBean(objectId, statusActiveAttributeId).getObjectAttributeValueBeans()[0].getValue();
if (objectAttributeValue == statusNotActive){
/**
* Define move map - for mapping attributes between two typeId (old type -> new type)
* and make map with current attributes
* and to map them with new attributes
*/
MoveObjectMapping moveMap = new MoveObjectMapping();
def fromAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(activeDomainTypeId);
def toAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(inactiveDomainTypeId);
def index = 0;
// do the actual mapping with both arrays
// NB! This assumes that both attributes are same type and same name and in exact same order.
toAtts?.each { item ->
MoveAttributeMapping attMap = MoveAttributeMapping.create(fromAtts[index], item);
moveMap.map(fromAtts[index].id, attMap);
index++;
}
/**
* Define moveObject and store necessary elements
* for new object
*/
MoveObjectBean moveObject = new MoveObjectBean();
moveObject.setObjectSchemaId(schemaId);
moveObject.setFromObjectTypeId(activeDomainTypeId);
moveObject.setToObjectTypeId(inactiveDomainTypeId);
moveObject.setMapping(moveMap);
moveObject.setReferences(moveObject.getReferences().valueOf("KEEP_REFERENCES_TO_OBJECT"));
// Actually moving the object
// and storing attributes to the new object
try{
if (object != null){
moveObject.setIql("Key = " + objectKey);
objectFacade.storeObjectBean(object);
objectFacade.moveObjects(moveObject);
log.info("domain-status-move-to_unused for object: " + objectKey );
}
} catch (Exception e){
log.warn("Could not update object attribute due to error: " + e.getMessage());
}
return true;
} // if (objectAttributeValue == statusNotActive)
else {
log.info ("objectAttributeValue is: " + objectAttributeValue + " and we are looking for: " + statusNotActive + ". So, it's a mismatch! Exiting.");
return false;
}
return false;
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You don't need to store the object (objectFacade.storeObjectBean(object)).
Remove that line and you won't need to make it mutable.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Peter-Dave SheehanI've replaced my attribute mapping with your suggestion. The problem is that now when I'm moving within two objectTypeId's, which are the same attributes (order, name and type), the object gets moved.
But when I'm moving within two quite different objectTypeId's, then return is "true", so no errors, but the object is not being moved actually.
The idea should be that when a mapping is found, then this attribute is moved, others should be discarded.
Also moveObject seems OK to me as well.
2022-10-31 16:50:36,875 [http-nio-8080-exec-6] |
moveObject: MoveObjectBean{fromObjectTypeId=14,
toObjectTypeId=5,
mapping=MoveObjectMapping{attributeMappingMap={880=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@3064a8e,
144=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@4e7951d1,
83=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@590b1081,
85=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@2b84a08a,
86=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@1a67dd4e,
1306=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@1e14f00a,
667=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@1f9a7451,
141=com.riadalabs.jira.plugins.insight.services.model.move.MoveAttributeMapping@119417d8}},
iql=Key = KAS-18177,
objectSchemaId=3,
references=KEEP_REFERENCES_TO_OBJECT}
Anything I'm missing with "discarding" attributes?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Sorry, I don't have a lot more to add. I've only ever really moved within 2 objects that largely share the same attributes.
I'm not sure how else to discard an attribute other than not including it in the mapping.
Based on the MoveAttributeMapping documentation I can see that there is a possibility of creating a mapping using From only, which sets the "ShouldMap" to false.
You could try to reverse the logic (process all the "fromAtts" and find a matching "toAtt" instead of processing all the "toAtts" and finding a matching "fromAtt") and add a no-map mapping.
fromAtts?.each { fromAttribute ->
def toAttribute = toAtts.find{it.name == fromAttribute.name && it.type == fromAttribute.type}
MoveAttributeMapping attMap
if(!toAttribute) {
log.warn "Nothing to map to $fromAttribute.name"
attMap = MoveAttributeMapping.create(fromAttribute)
} else {
attMap = MoveAttributeMapping.create(fromAttribute, toAttribute);
}
log.info "Adding mapping of $fromAttribute.name from $fromAttribute.id to ${toAttribute?.id}"
moveMap.map(fromAttribute.id, attMap);
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Actually I guess it's the GUI behaviour too, that you find "fromAttributes" and map them to "toAttributes".
And this worked like a charm! Thank you for your support and I believe whoever finds this topic, they will get it done as well. :)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Michael.
You have to map every attribute from the source to the destination - and the type must match between source and target attributes...
The best way to understand the Move Object is to follow the Bulk-Move UI - the same should be done in your script.
Missing attributes won't be moved if not specified.
Kind regards,
Yinon
Team Riada
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Yinon Negev ,
I faced pretty the same challenge @[deleted] was dealing with. Unfortunately, I'm not sure I did follow your hint exactly the way you were describing. Let me describe the issue, what is it current status and ask you for piece of code if possible :)
We are storing important kind of data in CRC object schema.
Because of the importance, we don't want anyone to operate manually.
Operations like new object creation or update are fully automated. Unfortunately, objects "removal" has to be done manually. We'd like to automate it.
There are 3 main object types:
Here is one of my try, mainly based on this post.
Object is moved but without any attribute. It's searchable by it's label, but none attribute displayed and none attribute is saved when object edited manually.
Wondering if anything should be done with Progress object returned by moveObjects method (https://insight-javadoc.riada.io/insight-javadoc-5.4.2/com/riadalabs/jira/plugins/insight/channel/external/api/facade/ObjectFacade.html#moveObjects-com.riadalabs.jira.plugins.insight.services.model.move.MoveObjectBean-) but was not able any clue for that :/
/*
skipping imports and issue load
*/
def object = (issue.getCustomFieldValue(cfObject)[0]).createMutable()
Class objectSchemaFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectSchemaFacade");
ObjectSchemaFacade objectSchemaFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectSchemaFacadeClass);
Class objectTypeFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeFacade");
ObjectTypeFacade objectTypeFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeFacadeClass);
ObjectSchemaBean crc = objectSchemaFacade.findObjectSchemaBeans().find
{ o -> o.objectSchemaKey == 'CRC'}
List<ObjectTypeBean> crcAllObjectTypes = objectTypeFacade.findObjectTypeBeansFlat(crc.id)
ObjectTypeBean srcObjectType = crcAllObjectTypes.find
{ o -> o.id == object.objectTypeId}
ObjectTypeBean tgtObjectType = crcAllObjectTypes.find
{ o -> o.name == ('Removed ' + srcObjectType.name)}
Class objectFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectFacade");
ObjectFacade objectFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectFacadeClass);
Class objectTypeAttFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade");
ObjectTypeAttributeFacade objectTypeAttFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeAttFacadeClass);
MoveObjectMapping moveMap = new MoveObjectMapping();
List<ObjectTypeAttributeBean> fromAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(srcObjectType.id);
List<ObjectTypeAttributeBean> toAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(tgtObjectType.id);
def index = 0;
toAtts?.each
{ item -> MoveAttributeMapping temp = MoveAttributeMapping.create(item,fromAtts[index]); moveMap.map(fromAtts[index].id,temp); index++; }
MoveObjectBean mObject = new MoveObjectBean();
mObject.setObjectSchemaId(crc.id);
mObject.setFromObjectTypeId(srcObjectType.id);
mObject.setToObjectTypeId(tgtObjectType.id);
mObject.setMapping(moveMap);
// mObject.setReferences(mObject.getReferences().valueOf("REMOVE_REFERENCES_TO_OBJECT"));
try
{ mObject.setIql("Key = " + object.objectKey); objectFacade.moveObjects(mObject); // objectFacade.storeObjectBean(object) }
catch (Exception vie)
{ log.warn("Could not update object attribute due to validation exception:" + vie.getMessage()); }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I am able to move the object ID when i use the script in the insight script console below provided in this thread.
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;
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 objectTypeAttFacadeClass = ComponentAccessor.getPluginAccessor().getClassLoader().findClass("com.riadalabs.jira.plugins.insight.channel.external.api.facade.ObjectTypeAttributeFacade");
def objectTypeAttFacade = ComponentAccessor.getOSGiComponentInstanceOfType(objectTypeAttFacadeClass);
// Define some variables
// set schemaId and typeId for active and inactive domains (from Jira Insight)
int schemaId = 3;
int activeDomainTypeId = 69;
int inactiveDomainTypeId = 100;
// set statusActiveAttributeId which is the ID of "Staatus" in active Object Type (secure/ObjectSchema.jspa?id=3&typeId=YOUR_ID&view=list&mode=attribute)
// set statusActive and statusNotActive IDs based on Jira Insight ID (secure/InsightConfigure.jspa?id=3&tab=statuses)
int statusActiveAttributeId = 846;
int statusActive = 70;
int statusNotActive = 72;
// object needs to be mutable and initiator gives us the object (which triggers this script)
// current object ID and KEY from actual request
object = object.createMutable();
int objectId = object.getId();
String objectKey = object.getObjectKey();
def objectTypeCurrentId = object.getObjectTypeId();
if ( objectTypeCurrentId != activeDomainTypeId ){
log.info ("objectTypeCurrentId is: " + objectTypeCurrentId + " and we are looking for: " + activeDomainTypeId + ". So, it's a mismatch! Exiting.");
// This object is not in correct typeId, exiting
return false;
}
def objectAttributeValue = objectFacade.loadObjectAttributeBean(objectId, statusActiveAttributeId).getObjectAttributeValueBeans()[0].getValue();
if (objectAttributeValue == statusNotActive){
/**
* Define move map - for mapping attributes between two typeId (old type -> new type)
* and make map with current attributes
* and to map them with new attributes
*/
MoveObjectMapping moveMap = new MoveObjectMapping();
def fromAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(activeDomainTypeId);
def toAtts = objectTypeAttFacade.findObjectTypeAttributeBeans(inactiveDomainTypeId);
def index = 0;
// do the actual mapping with both arrays
// NB! This assumes that both attributes are same type and same name and in exact same order.
toAtts?.each { item ->
MoveAttributeMapping attMap = MoveAttributeMapping.create(fromAtts[index], item);
moveMap.map(fromAtts[index].id, attMap);
index++;
}
/**
* Define moveObject and store necessary elements
* for new object
*/
MoveObjectBean moveObject = new MoveObjectBean();
moveObject.setObjectSchemaId(schemaId);
moveObject.setFromObjectTypeId(activeDomainTypeId);
moveObject.setToObjectTypeId(inactiveDomainTypeId);
moveObject.setMapping(moveMap);
moveObject.setReferences(moveObject.getReferences().valueOf("KEEP_REFERENCES_TO_OBJECT"));
// Actually moving the object
// and storing attributes to the new object
try{
if (object != null){
moveObject.setIql("Key = " + objectKey);
objectFacade.storeObjectBean(object);
objectFacade.moveObjects(moveObject);
log.info("domain-status-move-to_unused for object: " + objectKey );
}
} catch (Exception e){
log.warn("Could not update object attribute due to error: " + e.getMessage());
}
return true;
} // if (objectAttributeValue == statusNotActive)
else {
log.info ("objectAttributeValue is: " + objectAttributeValue + " and we are looking for: " + statusNotActive + ". So, it's a mismatch! Exiting.");
return false;
}
return false;
However, when i saved the script as a file and tried to initiate a post function - i m unable to get the result. Can anyone help me with this code? I m trying to load the object from customfield_ID and move that object from sourcetypeID to desinationtypeID to another where both are childobjecttypes of same parentobjecttype
Thanks
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi
Have you added this script to allowed list?
Administration -> Manage apps -> Insight Whitelist
JIRAURL/secure/admin/InsightPluginWhiteList.jspa
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Yes. I have whitelisted it.
In the Insight post function, there is an option to test with a issue key. The code executes but without a desired result.
In the Insight script console, I am able to get TRUE/ false result in the result tab and comment in the log tab
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Try my code (above) and adapt it. It works as post-function 100%! Nevertheless, I moved it with the same logic as a REST Endpoint with ScriptRunner and have it in the production environment.
The reason I moved to REST Endpoint was precisely that... as post-function, from time to time it stopped working. Something related to the browser or cache in the server... or who knows why.
Since I moved the script to REST ... NO failures with tons of objects.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You need to keep in mind that during the mapping process, you must adjust the object's attributes to match the structure of the destination object type; otherwise, it will fail.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
REST Endpoint to run with ScriptRunner: (I'll really appreciate it if you keep my credits on it!)
/*
* Script written by Carlos E. Tichy
* Last updated on: May 05, 2023
* Contact:
* - Email: carlos.tichy@gmail.com
* - Phone: +43 1 26030 6438
* Affiliation: Comprehensive Nuclear-Test-Ban Treaty Organization (CTBTO)
*/
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import com.riadalabs.jira.plugins.insight.services.model.ObjectBean
import customRiadaLibraries.insightmanager.InsightManagerForScriptrunner
import org.apache.log4j.Level
import groovy.json.JsonSlurper
import groovy.transform.BaseScript
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
@WithPlugin("com.riadalabs.jira.plugins.insight")
@BaseScript CustomEndpointDelegate delegate
def im = new InsightManagerForScriptrunner()
//im.readOnly = true //false is default
im.dispatchEvents(true)//After this, this specific instance of InsightManager (im) wont dispatch events.
// Set log level to INFO (default for REST endpoints is WARN)
log.setLevel(Level.WARN)
def allowedGroups = ["jira-administrators"]
moveObjectFromOneObjectTypeToAnother(httpMethod: "PUT", groups: allowedGroups) { MultivaluedMap queryParams, String body ->
def jsonSlurper = new JsonSlurper()
def request = jsonSlurper.parseText(body) as Map<String, List>
String insightObjectKey = request.insightObjectKey
// get schemaId and typeId of source and destination objectTypes (secure/InsightConfigure.jspa?objecttypeid="SOURCE_AND_DEST_OBJECT_TYPES")
int schemaId = Integer.parseInt(request.schemaId as String)
int sourceObjectTypeId = Integer.parseInt(request.sourceObjectTypeId as String);
int destObjectTypeId = Integer.parseInt(request.destObjectTypeId as String);
// 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 = Integer.parseInt(request.verificationAttributeId as String);
// set verificationAttributeValue Id based on Jira Insight ID (/secure/InsightConfigure.jspa?id="YOUR_SCHEMA_ID"&tab=statuses)
Object verificationAttributeValue
try {
verificationAttributeValue = Integer.parseInt(request.verificationAttributeValue as String);
// First try to get the integer value
} catch (NumberFormatException e) {
verificationAttributeValue = request.verificationAttributeValue as String
// If not, then the value is a simple string
}
// 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 = request.ifInboundReferences // ("ADVICE: Do the exercise on the GUI beforehand to see the real behavior.")
/*
* Gets the actual values of current object
*/
ObjectBean insightObjectBean = im.getObjectBean(insightObjectKey)
if (!insightObjectBean) {
return Response.status(Response.Status.BAD_REQUEST).entity("Insight object not found").type(MediaType.TEXT_PLAIN).build()
}
if(im.moveObject(insightObjectKey, schemaId, sourceObjectTypeId, destObjectTypeId, verificationAttributeId, verificationAttributeValue, ifInboundReferences)) {
return Response.ok("Object ${insightObjectKey} moved from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}.").type(MediaType.TEXT_PLAIN).build()
} else {
return Response.status(Response.Status.BAD_REQUEST).entity("Object ${insightObjectKey} not moved").type(MediaType.TEXT_PLAIN).build()
}
}
And here is my modification to the InsightManagerForScriptRunner:
Look at this URL for the InsightManager => https://github.com/Riada-AB/InsightManager
/*
* Script modification written by Carlos E. Tichy
* Last updated on: May 05, 2023
* Contact:
* - Email: carlos.tichy@gmail.com
* - Phone: +43 1 26030 6438
* Affiliation: Comprehensive Nuclear-Test-Ban Treaty Organization (CTBTO)
*/
/**
* Moves an object
* @param object Can be object ID, Object Key or ObjectBean
* @param schemaId What scheme to do the move on
* @param sourceObjectTypeId ID of the type of object to be moved from
* @param destObjectTypeId ID of the object type you want to move to
* @param verificationAttributeId ID of "Status" in source objectType (/secure/ObjectSchema.jspa?id="YOUR_SCHEMA_ID"&typeId="SOURCE_OBJECT_TYPE_ID"&view=list&mode=attribute)
* @param verificationAttributeValue Id based on Jira Insight ID (/secure/InsightConfigure.jspa?id="YOUR_SCHEMA_ID"&tab=statuses)
* @param ifInboundReferences action to take if the object to move has inboundReferences (options: "REMOVE_REFERENCES_TO_OBJECT" || "KEEP_REFERENCES_TO_OBJECT" || "DO_NOT_MOVE_OBJECT")
* @return boolean representing success or failure
*/
boolean moveObject(def object, int schemaId, int sourceObjectTypeId, int destObjectTypeId, int verificationAttributeId, Object verificationAttributeValue, String ifInboundReferences) {
log.debug("Moving object:" + object)
ObjectBean objectBean = getObjectBean(object)
int objectId = objectBean.getId();
String objectKey = objectBean.getObjectKey();
log.trace("\tObject id:" + objectBean.id)
if (readOnly) {
log.info("\tCurrently in readOnly mode, not moving object $objectBean")
return false
} else {
// get the current objectType to verify
def currentObjectTypeId = objectBean.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.info("Nothing to map to ${destAttribute.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.warn("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.warn("Object ${objectKey} moved from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}")
return true;
} else {
log.warn("Object ${objectKey} failed to move from objectType ${sourceObjectTypeId} to objectType ${destObjectTypeId}")
return false;
}
}
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Namaskaram
With regards to attributed mapping , in one of my use case, both the object types are child of the same parent as I has mentioned earlier, so it's ensured by default I think
Thanks a lot for REST code. I will keep the credits 🙂
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
If you encounter any difficulties, just let me know. I walked the same challenging path some time back and know how frustrating it can be. It works 100%.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This is the logic for the automation done by a functional user who has Jira Administrator rights.
In this example, decommissioned Hosts are moved to another branch called "Removed Hosts".
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.