Sync transitions with mandatory field inputs

Originally asked by Eugeniu Aftenii on 19 April 2023 (original question)


Hi all,

We have recently started using Exalate and encountered an issue that we can’t find a workaround for. The issue we are having is related to status change sync. Basically when we change a status, for example from Open to Resolved with the transition name being “Discard”, Exalate throws the following error message: “Could not perform a transition `{“transition”:{“id”:“431”}}` for issue with id `191,204`: Field resolution: The selected resolution cannot be chosen during this action.”

I know that this is happening due to mandatory field data that has to be filled out before succeeding the the transition. In our case we have a validator that prevents the user from performing the transition successfully if a value for fields Comment, Resolution is not provided.

I wanted to check if anyone else here had a similar issue and what was the work around? I’ve tried to add a ScriptRunner validator to handle this but haven’t had any luck so far.

Thanks


Answer by Mathieu Lepoutre on 20 April 2023

Hi Eugeniu Aftenii

A potential workaround is to modify the validator on the target system to allow for the transition even if the Comment and Resolution fields are not filled out.

This would allow the transition to be performed successfully, and the required fields can be filled in afterwards.

Because we want to automate as much as possible with Exalate, let’s include those fields in the incoming sync.

We can include the necessary fields when syncing status changes.

Can you share your incoming sync at this moment please?


Comments:

Eugeniu Aftenii commented on 20 April 2023

Hi Mathieu,

Thanks for looking into it. Yes indeed we are trying to add some sort of validator to handle this. But so far we did not succeed.

See below the incoming sync:

if(firstSync && replica.project.key == “PB”){
issue.projectKey = “PA”
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Business Epic”
}
if(firstSync && replica.project.key == “PA”){
issue.projectKey = “PB”
issue.typeName = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: “Administration”
}

issue.summary = replica.summary
issue.description = replica.description
issue.comments = commentHelper.mergeComments(issue, replica)
issue.attachments = attachmentHelper.mergeAttachments(issue, replica)
issue.labels = replica.labels
issue.assignee = replica.assignee
issue.resolution = replica.resolution

issue.customFields.“Technical Team”.value = replica.customFields.“Technical Team”.value
issue.customFields.“Documentation Link”.value = replica.customFields.“Documentation Link”.value

def remoteStatusName = replica.status.name
issue.setStatus(remoteStatusName)

if(issue.typeName == “Bug”) {
issue.“Account” = nodeHelper.getOption(issue, “Account”, “Administration”).getId()
issue.“Partner”=“iSeatz”
issue.“Billable Work”=“Non-billable”
}

if(issue.typeName == “Story”) {
issue.“Account” = nodeHelper.getOption(issue, “Account”, “Administration”).getId()
issue.“Partner”=“iSeatz”
issue.“Billable Work”=“Non-billable”
}

if(issue.typeName == “Epic”) {
issue.“Account” = nodeHelper.getOption(issue, “Account”, “Administration”).getId()
issue.“Partner”=“iSeatz”
issue.“Billable Work”=“Non-billable”
}

Epic.receive()

Eugeniu Aftenii commented on 21 April 2023

Hi Mathieu,

To add to this topic - I believe the main issue here is when we try to sync the “Resolution” field.

Even after adding the below snippet:

*******

if (replica.resolution == null) {
   // if the remote issue is not resolved
   issue.resolution = null
}
 
if (replica.resolution != null) {
   // the remote issue is resolved, but the local isn't - look up the correct local resolution object.


   def resolutionMap = [
        "Done" : "Done",
        "Won't Do" : "Won't Do",
        "Known Error" :  "Won't Fix"
   ]
   // use 'done' as resolution if the remote resolution is not found
   def targetResolutionName = resolutionMap[replica.resolution.name] ?: "Done"
   
   // nodeHelper.getResolution looks up the local resolution object based on the provided name
   issue.resolution = nodeHelper.getResolution(targetResolutionName)
}  
  
*******************

I’m still not able to achieve the desired result. In fact I’m getting a new error: “Jira responded with: Field Field ‘resolution’ cannot be set. It is not on the appropriate screen, or unknown.: resolution.”

Mathieu Lepoutre commented on 24 April 2023

Hi Eugeniu Aftenii

Is this the only transition that Exalate has having trouble with? We can add something like this for this specific use-case.

if (replica.status.name == "Resolved" && previous?.status?.name == "Open") {
 issue.comments = commentHelper.addComment("Status changed", issue.comments)
 issue.resolution = nodeHelper.getResolution("Done")
}

The other way to solve this is by removing a user from a validator through editing the validators settings. If you remove the Exalate Proxy user for this particular use-case.

Thanks,

Mathieu

Eugeniu Aftenii commented on 24 April 2023

Hi Mathieu,

Appreciate your assistance here.

  1. To quickly answer, when I used the snippet you provided, the resolution gets changed to wherever we specify in the if statement but the Status stays the same. Not sure why status isn’t being synchronized?

  2. Regarding “Exalate Proxy” user → I tried the following ScriptRunner approach:
    (user.displayName == “Exalate”) || (issue.resolution != null && issue.comments.filter(comment => comment.id == null).length == 1)

     However, this didn't help either.. So I'm still blocked as of now..
    
Mathieu Lepoutre commented on 24 April 2023

Hi

Apologies, we need to add the status in the if-statement aswell.

if (replica.status.name == "Resolved" && previous?.status?.name == "Open") {
  issue.setStatus("Resolved")
 issue.comments = commentHelper.addComment("Status changed", issue.comments)
 issue.resolution = nodeHelper.getResolution("Done")
}

Let me know how this goes.

Another route:

if (replica.status.name == "Resolved") {

   workflowHelper.transition(issue, "Discard")
issue.comments = commentHelper.addComment("Status changed", issue.comments)
 issue.resolution = nodeHelper.getResolution("Done")
} 

Thanks,

Mathieu

Eugeniu Aftenii commented on 24 April 2023

Hi Mathieu,

Unfortunately the outcome is the same - it changes only the resolution but not the status. I tried both snippets and the result was the same.

Error: Could not perform a transition `{“transition”:{“id”:“431”}}` for issue with id `191,482`: Please fill in the values for Resolution and comment.

Mathieu Lepoutre commented on 25 April 2023

Hi

Please consult this documentation about the possibility to store an issue, it allows performing multiple consecutive operations within one synchronization.

https://docs.exalate.com/docs/how-to-use-a-storeissue-function

Eugeniu Aftenii commented on 25 April 2023

Hi Mathieu,

Thanks for sharing the documentation. It seems that store(issue) won’t do it in my case either. I tried but I’m still blocked. I believe we are seeing this issue since we have the validator setup that is preventing the user from moving an issue to “Resolved” status if the comment and resolution fields aren’t filled in. Please let me know if perhaps you have anything else you could suggest. As of now this blocker is preventing us from moving forward with our project.

Thanks,

E

Mathieu Lepoutre commented on 27 April 2023

Hi Eugeniu

I have fully reproduced the problem, it turns out that the pop-up screen for adding a comment is what is causing a problem. The resolution gets set. Would it be possible to turn off this validator for the Exalate user through A4J for now while I do research on how to fix this through the REST API? Thank you

Eugeniu Aftenii commented on 01 May 2023

Hi Mathieu,

So we tried several ScriptRunner snippets to try to handle this but still no luck. Let me know if you find a way to achieve this.

Best,

Eugeniu