How to sync epics, stories and subtasks using a visual mode connection

Originally asked by Francis Martens (Exalate) on 14 October 2021 (original question)


As in summary, the challenge is

  • Setup a Jira On Premise (A) to Jira On Premise (B) visual mode connection
  • ensure that stories are synced such that the epic relation is respected
    Ie. when Story-A1 is related to Epic-A1, then Story-B1 (the twin of Story-A1) is part of Epic-B1 (the twin epic of Epic-B1

How?


Answer by Tony on 15 March 2022

I am doing sever to cloud connection using visual mode and to achieve Epic- story- sub task relationship, I am using below code as described in this article

if (executionInstanceName == “IT”) {

if (IT.issue.type.name == “Epic Feature”) {
IT.issue.customFields.“Epic Name”?.value = Marketing.issue.customFields.“Epic Name”?.value
} else {

if (Marketing.issue.“parentId”) {
// this is a subtask - set the local parent it

Long remoteParentId = Marketing.issue.parentId as Long
Long localParentId = nodeHelper.getLocalIssueKeyFromRemoteId(remoteParentId, “issue”).id
IT.issue.parentId = localParentId

} else {
// set the epic link custom field, such that it points to the twin epic of the parent of the source

def remoteEpicIssueKey = Marketing.issue.customFields.“Epic Link”?.value?.urn
IT.issue.customFields.“Epic Link”.value = nodeHelper.getLocalIssueKeyFromRemoteUrn(remoteEpicIssueKey, “issue”)
}
}
}

I am getting connection configuration error. Error trace

  • Impact: ISSUE

  • Local entity: null

  • Remote entity: EXA-11

  • Connection: Marketing

  • Error type: Connection configuration

  • Error Creation Time: Mar 15, 2022 21:08:46

  • Error Detail Message: Check the documentation for more details.

  • Error Stack Trace: com.exalate.domain.exception.editor.ScriptEditorException: com.exalate.admin.editor.errors.mappings.script at com.exalate.replication.services.replication.mapping.MappingService$$anonfun$1.applyOrElse(MappingService.scala:294) at com.exalate.replication.services.replication.mapping.MappingService$$anonfun$1.applyOrElse(MappingService.scala:276) at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:38) at scala.util.Failure.recoverWith(Try.scala:236) at com.exalate.replication.services.replication.mapping.MappingService.executeInScriptRule(MappingService.scala:276) at com.exalate.replication.services.replication.mapping.MappingService.$anonfun$receive$8(MappingService.scala:181) at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:307) at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41) at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64) at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:56) at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:93) at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23) at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85) at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:93) at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:48) at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:48) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) Caused by: com.exalate.api.exception.script.ScriptException: startup failed: Script21.groovy: 7: expecting ‘)’, found ‘IT’ @ line 7, column 19. if (Marketing.issue.“parentId”) { ^ 1 error at com.exalate.error.services.ScriptExceptionCategoryService.categorizeProcessorAndIssueTrackerExceptionsIntoScriptExceptions(ScriptExceptionCategoryService.scala:42) at com.exalate.processor.ExalateProcessor.executeProcessor(ExalateProcessor.java:57) at com.exalate.replication.services.replication.mapping.MappingService.$anonfun$executeInScriptRule$1(MappingService.scala:272) at scala.util.Try$.apply(Try.scala:213) at com.exalate.replication.services.replication.mapping.MappingService.executeInScriptRule(MappingService.scala:269) … 16 more Caused by: javax.script.ScriptException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script21.groovy: 7: expecting ‘)’, found ‘IT’ @ line 7, column 19. if (Marketing.issue.“parentId”) { ^ 1 error at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:158) at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at com.exalate.processor.ExalateProcessor.execute(ExalateProcessor.java:98) at com.exalate.processor.ExalateProcessor.executeProcessor(ExalateProcessor.java:55) … 19 more Caused by: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script21.groovy: 7: expecting ‘)’, found ‘IT’ @ line 7, column 19. if (Marketing.issue.“parentId”) { ^ 1 error at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:311) at org.codehaus.groovy.control.ErrorCollector.addFatalError(ErrorCollector.java:151) at org.codehaus.groovy.control.ErrorCollector.addError(ErrorCollector.java:121) at org.codehaus.groovy.control.ErrorCollector.addError(ErrorCollector.java:133) at org.codehaus.groovy.control.SourceUnit.addError(SourceUnit.java:325) at org.codehaus.groovy.antlr.AntlrParserPlugin.transformCSTIntoAST(AntlrParserPlugin.java:224) at org.codehaus.groovy.antlr.AntlrParserPlugin.parseCST(AntlrParserPlugin.java:192) at org.codehaus.groovy.control.SourceUnit.parse(SourceUnit.java:226) at org.codehaus.groovy.control.CompilationUnit$1.call(CompilationUnit.java:201) at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:965) at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:642) at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:618) at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:595) at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:401) at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:89) at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:341) at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:338) at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:147) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:336) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:320) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:262) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.getScriptClass(GroovyScriptEngineImpl.java:331) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:153) … 22 more

  • **Incoming sync data:**Summary:

      TEST 5:06  
    

Entity Key:

	EXA\-11(id: 466510\)  

Encoded payload:
eyJ2ZXJzaW9uIjp7Im1ham9yIjoxLCJtaW5vciI6MTQsInBhdGNoIjowfSwiaHViSXNzdWUiOnsiX19FWEFMQVRFX1NDT1BFX1VJRCI6ImE2NDVmMTI0LTI4NGEtNDE5Ny1hZDhlLTNkMzE3OTI5NjJmNCIsIl9fRVhBTEFURV9TQ09QRV9JTlNUQU5DRV9OQU1FIjoiUm95YWwgSVQiLCJfX0VYQUxBVEVfU0NPUEVfU0lERSI6IkxPQ0FMIiwiY29tcG9uZW50cyI6W10sImF0dGFjaG1lbnRzIjpbXSwidm90ZXJzIjpbXSwiY3VzdG9tRmllbGRzIjp7IlNMOiBEcml2ZSBMZXR0ZXIgKEQpIjp7ImlkIjoxNjUyNywibmFtZSI6IlNMOiBEcml2ZSBMZXR0ZXIgKEQpIiwidWlkIjoiMTY1MjciLCJkZXNjcmlwdGlvbiI6IkRyaXZlIFNpemUgKEdCcykiLCJ0eXBlIjoiT1BUSU9OIiwidmFsdWUiOnsiaWQiOiIxNzIwNCIsInNlcXVlbmNlIjowLCJ2YWx1ZSI6IlRlbXBsYXRlIFN0YW5kYXJkIiwiZGlzYWJsZWQiOmZhbHNlLCJjaGlsZE9wdGlvbnMiOltdfX0sIjEzODAxIjp7ImlkIjoxMzgwMSwibmFtZSI6IkF1dG9tYXRpb24gU3RhdHVzIiwidWlkIjoiMTM4MDEiLCJ0eXBlIjoiT1BUSU9OIiwidmFsdWUiOnsiaWQiOiIxNDMwMSIsInNlcXVlbmNlIjo0LCJ2YWx1ZSI6IlJlYWR5IHRvIEF1dG9tYXRlIiwiZGlzYWJsZWQiOmZhbHNlLCJjaGlsZE9wdGlvbnMiOltdfX0sIjEwMTE2Ijp7ImlkIjoxMDExNiwibmFtZSI6IlJhbmsiLCJ1aWQiOiIxMDExNiIsImRlc2NyaXB0aW9uIjoiR2xvYmFsIHJhbmsgZmllbGQgZm9yIEppcmEgU29mdHdhcmUgdXNlIG9ubHkuIiwidHlwZSI6IkxFWE9fUkFOSyIsInZhbHVlIjp7InZhbHVlIjoiMnxpMDU3aGc6In19LCIxNjUxNiI6eyJpZCI6MTY1MTYsIm5hbWUiOiJTTDogUkFNIChHQnMpIiwidWlkIjoiMTY1MTYiLCJkZXNjcmlwdGlvbiI6IlVubGVzcyBzcGVjaWZpZWQsIDQgR0JzIHdpbGwgYmUgc3RhbmRhcmQuIDE2IEdCcyBNYXggd2l0aG91dCBwcmlvciBhdXRob3JpemF0aW9uIiwidHlwZSI6Ik9QVElPTiIsInZhbHVlIjp7ImlkIjoiMTcyMDIiLCJzZXF1ZW5jZSI6MCwidmFsdWUiOiJUZW1wbGF0ZSBTdGFuZGFyZCIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX19LCIxNjUxNSI6eyJpZCI6MTY1MTUsIm5hbWUiOiJTTDogT3BlcmF0aW5nIFN5c3RlbSIsInVpZCI6IjE2NTE1IiwiZGVzY3JpcHRpb24iOiIiLCJ0eXBlIjoiQ0FTQ0FESU5HX1NFTEVDVCIsInZhbHVlIjp7InBhcmVudCI6eyJpZCI6IjE2OTY5Iiwic2VxdWVuY2UiOjAsInZhbHVlIjoiV2luZG93cyIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbeyJpZCI6IjE2OTcyIiwic2VxdWVuY2UiOjAsInZhbHVlIjoiMjAxNiIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX0seyJpZCI6IjE2OTczIiwic2VxdWVuY2UiOjEsInZhbHVlIjoiMjAxOSIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX0seyJpZCI6IjE2OTc0Iiwic2VxdWVuY2UiOjIsInZhbHVlIjoiT3RoZXIiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119XX0sImNoaWxkIjp7ImlkIjoiMTY5NzMiLCJzZXF1ZW5jZSI6MSwidmFsdWUiOiIyMDE5IiwiZGlzYWJsZWQiOmZhbHNlLCJjaGlsZE9wdGlvbnMiOltdfX19LCJTTDogRHJpdmUgTGV0dGVyIChDKSI6eyJpZCI6MTY1MjYsIm5hbWUiOiJTTDogRHJpdmUgTGV0dGVyIChDKSIsInVpZCI6IjE2NTI2IiwiZGVzY3JpcHRpb24iOiJEcml2ZSBTaXplIChHQnMpIiwidHlwZSI6Ik9QVElPTiIsInZhbHVlIjp7ImlkIjoiMTcyMDMiLCJzZXF1ZW5jZSI6MCwidmFsdWUiOiJUZW1wbGF0ZSBTdGFuZGFyZCIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX19LCJTTDogT3BlcmF0aW5nIFN5c3RlbSI6eyJpZCI6MTY1MTUsIm5hbWUiOiJTTDogT3BlcmF0aW5nIFN5c3RlbSIsInVpZCI6IjE2NTE1IiwiZGVzY3JpcHRpb24iOiIiLCJ0eXBlIjoiQ0FTQ0FESU5HX1NFTEVDVCIsInZhbHVlIjp7InBhcmVudCI6eyJpZCI6IjE2OTY5Iiwic2VxdWVuY2UiOjAsInZhbHVlIjoiV2luZG93cyIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbeyJpZCI6IjE2OTcyIiwic2VxdWVuY2UiOjAsInZhbHVlIjoiMjAxNiIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX0seyJpZCI6IjE2OTczIiwic2VxdWVuY2UiOjEsInZhbHVlIjoiMjAxOSIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX0seyJpZCI6IjE2OTc0Iiwic2VxdWVuY2UiOjIsInZhbHVlIjoiT3RoZXIiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119XX0sImNoaWxkIjp7ImlkIjoiMTY5NzMiLCJzZXF1ZW5jZSI6MSwidmFsdWUiOiIyMDE5IiwiZGlzYWJsZWQiOmZhbHNlLCJjaGlsZE9wdGlvbnMiOltdfX19LCJTTDogUkFNIChHQnMpIjp7ImlkIjoxNjUxNiwibmFtZSI6IlNMOiBSQU0gKEdCcykiLCJ1aWQiOiIxNjUxNiIsImRlc2NyaXB0aW9uIjoiVW5sZXNzIHNwZWNpZmllZCwgNCBHQnMgd2lsbCBiZSBzdGFuZGFyZC4gMTYgR0JzIE1heCB3aXRob3V0IHByaW9yIGF1dGhvcml6YXRpb24iLCJ0eXBlIjoiT1BUSU9OIiwidmFsdWUiOnsiaWQiOiIxNzIwMiIsInNlcXVlbmNlIjowLCJ2YWx1ZSI6IlRlbXBsYXRlIFN0YW5kYXJkIiwiZGlzYWJsZWQiOmZhbHNlLCJjaGlsZE9wdGlvbnMiOltdfX0sIlNoaXAgRGVwbG95bWVudCBUZXN0aW5nIEJ1Zz8iOnsiaWQiOjE0NTAyLCJuYW1lIjoiU2hpcCBEZXBsb3ltZW50IFRlc3RpbmcgQnVnPyIsInVpZCI6IjE0NTAyIiwidHlwZSI6Ik9QVElPTiIsInZhbHVlIjp7ImlkIjoiMTQ1MjMiLCJzZXF1ZW5jZSI6MSwidmFsdWUiOiJObyIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX19LCJSYW5rIjp7ImlkIjoxMDExNiwibmFtZSI6IlJhbmsiLCJ1aWQiOiIxMDExNiIsImRlc2NyaXB0aW9uIjoiR2xvYmFsIHJhbmsgZmllbGQgZm9yIEppcmEgU29mdHdhcmUgdXNlIG9ubHkuIiwidHlwZSI6IkxFWE9fUkFOSyIsInZhbHVlIjp7InZhbHVlIjoiMnxpMDU3aGc6In19LCJEYXRhIFJlcXVlc3QgRm9ybSI6eyJpZCI6MTM2MTQsIm5hbWUiOiJEYXRhIFJlcXVlc3QgRm9ybSIsInVpZCI6IjEzNjE0IiwidHlwZSI6IlVSTCIsInZhbHVlIjoiaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL29wZW4_aWQ9MUYxY2xOdDZUV2txZWpNOGh3N0Z2VzRRZFo0czdOX25MIn0sIlNMOiBEcml2ZSBMZXR0ZXIgKEUpIjp7ImlkIjoxNjUyOCwibmFtZSI6IlNMOiBEcml2ZSBMZXR0ZXIgKEUpIiwidWlkIjoiMTY1MjgiLCJkZXNjcmlwdGlvbiI6IiIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjE3MjA1Iiwic2VxdWVuY2UiOjAsInZhbHVlIjoiVGVtcGxhdGUgU3RhbmRhcmQiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119fSwiMTA0MDMiOnsiaWQiOjEwNDAzLCJuYW1lIjoiU3RlcHMgdG8gUmVwcm9kdWNlIiwidWlkIjoiMTA0MDMiLCJ0eXBlIjoiVEVYVCIsInZhbHVlIjoiMS5cclxuMi5cclxuQWN0dWFsIFJlc3VsdDogXHJcbkV4cGVjdGVkIFJlc3VsdDoifSwiQXV0b21hdGlvbiBTdGF0dXMiOnsiaWQiOjEzODAxLCJuYW1lIjoiQXV0b21hdGlvbiBTdGF0dXMiLCJ1aWQiOiIxMzgwMSIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjE0MzAxIiwic2VxdWVuY2UiOjQsInZhbHVlIjoiUmVhZHkgdG8gQXV0b21hdGUiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119fSwiU3RlcHMgdG8gUmVwcm9kdWNlIjp7ImlkIjoxMDQwMywibmFtZSI6IlN0ZXBzIHRvIFJlcHJvZHVjZSIsInVpZCI6IjEwNDAzIiwidHlwZSI6IlRFWFQiLCJ2YWx1ZSI6IjEuXHJcbjIuXHJcbkFjdHVhbCBSZXN1bHQ6IFxyXG5FeHBlY3RlZCBSZXN1bHQ6In0sIlN0cnVjdHVyZSBJbmRleCBNb25pdG9yIjp7ImlkIjoxNjkwMCwibmFtZSI6IlN0cnVjdHVyZSBJbmRleCBNb25pdG9yIiwidWlkIjoiMTY5MDAiLCJkZXNjcmlwdGlvbiI6IlN5c3RlbSBjdXN0b20gZmllbGQsIHJlcXVpcmVkIGZvciBjb3JyZWN0IGZ1bmN0aW9uaW5nIG9mIFN0cnVjdHVyZSBhZGQtb24uIERvZXMgbm90IHN0b3JlIGFueSBkYXRhLiIsInR5cGUiOiJVTkhBTkRMRUQiLCJ2YWx1ZSI6IiJ9LCJTaGlwIjp7ImlkIjoxMzYxMSwibmFtZSI6IlNoaXAiLCJ1aWQiOiIxMzYxMSIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjEzNTg3Iiwic2VxdWVuY2UiOjYsInZhbHVlIjoiQVpBTUFSQSBRVUVTVCIsImRpc2FibGVkIjpmYWxzZSwiY2hpbGRPcHRpb25zIjpbXX19LCIxMzYxNCI6eyJpZCI6MTM2MTQsIm5hbWUiOiJEYXRhIFJlcXVlc3QgRm9ybSIsInVpZCI6IjEzNjE0IiwidHlwZSI6IlVSTCIsInZhbHVlIjoiaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL29wZW4_aWQ9MUYxY2xOdDZUV2txZWpNOGh3N0Z2VzRRZFo0czdOX25MIn0sIjE2NTI4Ijp7ImlkIjoxNjUyOCwibmFtZSI6IlNMOiBEcml2ZSBMZXR0ZXIgKEUpIiwidWlkIjoiMTY1MjgiLCJkZXNjcmlwdGlvbiI6IiIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjE3MjA1Iiwic2VxdWVuY2UiOjAsInZhbHVlIjoiVGVtcGxhdGUgU3RhbmRhcmQiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119fSwiMTY1MjciOnsiaWQiOjE2NTI3LCJuYW1lIjoiU0w6IERyaXZlIExldHRlciAoRCkiLCJ1aWQiOiIxNjUyNyIsImRlc2NyaXB0aW9uIjoiRHJpdmUgU2l6ZSAoR0JzKSIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjE3MjA0Iiwic2VxdWVuY2UiOjAsInZhbHVlIjoiVGVtcGxhdGUgU3RhbmRhcmQiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119fSwiMTM2MTEiOnsiaWQiOjEzNjExLCJuYW1lIjoiU2hpcCIsInVpZCI6IjEzNjExIiwidHlwZSI6Ik9QVElPTiIsInZhbHVlIjp7ImlkIjoiMTM1ODciLCJzZXF1ZW5jZSI6NiwidmFsdWUiOiJBWkFNQVJBIFFVRVNUIiwiZGlzYWJsZWQiOmZhbHNlLCJjaGlsZE9wdGlvbnMiOltdfX0sIjE0NTAyIjp7ImlkIjoxNDUwMiwibmFtZSI6IlNoaXAgRGVwbG95bWVudCBUZXN0aW5nIEJ1Zz8iLCJ1aWQiOiIxNDUwMiIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjE0NTIzIiwic2VxdWVuY2UiOjEsInZhbHVlIjoiTm8iLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119fSwiMTY1MjYiOnsiaWQiOjE2NTI2LCJuYW1lIjoiU0w6IERyaXZlIExldHRlciAoQykiLCJ1aWQiOiIxNjUyNiIsImRlc2NyaXB0aW9uIjoiRHJpdmUgU2l6ZSAoR0JzKSIsInR5cGUiOiJPUFRJT04iLCJ2YWx1ZSI6eyJpZCI6IjE3MjAzIiwic2VxdWVuY2UiOjAsInZhbHVlIjoiVGVtcGxhdGUgU3RhbmRhcmQiLCJkaXNhYmxlZCI6ZmFsc2UsImNoaWxkT3B0aW9ucyI6W119fSwiMTY5MDAiOnsiaWQiOjE2OTAwLCJuYW1lIjoiU3RydWN0dXJlIEluZGV4IE1vbml0b3IiLCJ1aWQiOiIxNjkwMCIsImRlc2NyaXB0aW9uIjoiU3lzdGVtIGN1c3RvbSBmaWVsZCwgcmVxdWlyZWQgZm9yIGNvcnJlY3QgZnVuY3Rpb25pbmcgb2YgU3RydWN0dXJlIGFkZC1vbi4gRG9lcyBub3Qgc3RvcmUgYW55IGRhdGEuIiwidHlwZSI6IlVOSEFORExFRCIsInZhbHVlIjoiIn19LCJldmVudFRyaWdnZXJDb250ZXh0Ijp7InVzZXIiOiJBUmF2dWxhcGFsbHkifSwicHJvamVjdCI6eyJpZFN0ciI6IjE1MzAwIiwia2V5IjoiRVhBIiwibmFtZSI6IlRlc3QgRXhhbGF0ZSIsImxlYWQiOnsia2V5IjoiYXJhdnVsYXBhbGx5IiwiYWN0aXZlIjp0cnVlLCJlbWFpbCI6ImFyYXZ1bGFwYWxseUByY2NsLmNvbSIsImRpc3BsYXlOYW1lIjoiQXJhdmluZCBSYXZ1bGFwYWxseSIsInVzZXJuYW1lIjoiQVJhdnVsYXBhbGx5In0sInZlcnNpb25zIjpbXSwiY29tcG9uZW50cyI6W119LCJ3YXRjaGVycyI6W3sia2V5IjoiYXJhdnVsYXBhbGx5IiwiYWN0aXZlIjp0cnVlLCJlbWFpbCI6ImFyYXZ1bGFwYWxseUByY2NsLmNvbSIsImRpc3BsYXlOYW1lIjoiQXJhdmluZCBSYXZ1bGFwYWxseSIsInVzZXJuYW1lIjoiQVJhdnVsYXBhbGx5In1dLCJmaXhWZXJzaW9ucyI6W10sImlzc3VlTGlua3MiOltdLCJpZCI6IjQ2NjUxMCIsImtleSI6IkVYQS0xMSIsInN1bW1hcnkiOiJURVNUIDU6MDYiLCJjcmVhdG9yIjp7ImtleSI6ImFyYXZ1bGFwYWxseSIsImFjdGl2ZSI6dHJ1ZSwiZW1haWwiOiJhcmF2dWxhcGFsbHlAcmNjbC5jb20iLCJkaXNwbGF5TmFtZSI6IkFyYXZpbmQgUmF2dWxhcGFsbHkiLCJ1c2VybmFtZSI6IkFSYXZ1bGFwYWxseSJ9LCJjb21tZW50cyI6W10sImNyZWF0ZWQiOjE2NDczNzg1MTkxMDEsImludGVybmFsTWFwIjp7Il9fRVhBTEFURV9TQ09QRV9VSUQiOiJhNjQ1ZjEyNC0yODRhLTQxOTctYWQ4ZS0zZDMxNzkyOTYyZjQiLCJfX0VYQUxBVEVfU0NPUEVfSU5TVEFOQ0VfTkFNRSI6IlJveWFsIElUIiwiX19FWEFMQVRFX1NDT1BFX1NJREUiOiJMT0NBTCJ9LCJyZXBvcnRlciI6eyJrZXkiOiJhcmF2dWxhcGFsbHkiLCJhY3RpdmUiOnRydWUsImVtYWlsIjoiYXJhdnVsYXBhbGx5QHJjY2wuY29tIiwiZGlzcGxheU5hbWUiOiJBcmF2aW5kIFJhdnVsYXBhbGx5IiwidXNlcm5hbWUiOiJBUmF2dWxhcGFsbHkifSwicHJpb3JpdHkiOnsiaWQiOiIxMDAwMCIsIm5hbWUiOiJQMSIsImRlc2NyaXB0aW9uIjoiUzEg4oCTIEZ1bmN0aW9uYWxpdHkgc2ltcGx5IGRvZXMgbm90IHdvcmsgYXMgZGVzaWduZWQgYW5kIHRoZXJlIGlzIG5vIHdvcmsgYXJvdW5kIHRvIGNvbXBsZXRlIHVzZXIgYWN0aW9uL3Rhc2sifSwibGFiZWxzIjpbXSwiY3VzdG9tS2V5cyI6e30sIndvcmtMb2dzIjpbXSwiaXNzdWVUeXBlIjp7ImlkIjoiMTAxMDAiLCJuYW1lIjoiU3RvcnkiLCJkZXNjcmlwdGlvbiI6IkNyZWF0ZWQgYnkgSmlyYSBTb2Z0d2FyZSAtIGRvIG5vdCBlZGl0IG9yIGRlbGV0ZS4gSXNzdWUgdHlwZSBmb3IgYSB1c2VyIHN0b3J5LiJ9LCJiYXNpY0h1YkNoYW5nZUhpc3RvcnkiOltdLCJ2b3RlcyI6MCwiYWZmZWN0ZWRWZXJzaW9ucyI6W10sInVwZGF0ZWQiOjE2NDczNzg1MTkxMDEsImVudGl0eVByb3BlcnRpZXMiOnt9LCJzdGF0dXMiOnsiaWQiOiIxMDgwMCIsIm5hbWUiOiJUbyBEbyIsImRlc2NyaXB0aW9uIjoiU3RvcnkgaXMgaW4gdmVuZG9yIHF1ZXVlIHRvIGJlIHdvcmtlZC4iLCJjYXRlZ29yeSI6eyJpZCI6Miwia2V5IjoibmV3IiwibmFtZSI6Ik5ldyJ9fX0sImlzc3VlVXJsIjoiaHR0cHM6Ly9qaXJhZGV2LnJjY2wuY29tL2Jyb3dzZS9FWEEtMTEifQ


Comments:

Daniel Carvajal commented on 16 March 2022

Hi Tony

If that is the totality of the code snippet you’ve added, I believe you are missing a curly bracket } at the very end of the script, the one that’s opened at:

if (executionInstanceName == "IT") {

Let us know if making that correction clears the problem.

Cheers,

Daniel

Tony commented on 16 March 2022

Hi Daniel, I don’t think so curly bracket is causing the issue, I verified it and still the error reads the same.

Tony commented on 16 March 2022

I have connection set up between cloud and sever, hope thats not an issue?

Daniel Carvajal commented on 17 March 2022

Hi Aravind

The problem seems to be the definition of the project at the added script since the name contains a space you need to wrap it into [“name”] like so:

if (on [“Royal IT”].issue.”parentId") { 
...

The script where the problem is generated is the following:

if (executionInstanceName == "IT") {     
    if (IT.issue.type.name == "Epic") {         
        IT.issue.customFields."Epic Name"?.value = Royal IT.issue.customFields."Epic Name"?.value     
    } else { 
                 if (Royal IT.issue."parentId")  {         
    // this is a subtask - set the local parent it 
...

Please adjust and let us know how it goes.

Cheers,

Daniel

Tony commented on 17 March 2022

I was able to sync Epic now after making above changes but not story under the Epic, it again throwing configuration error, Epic I synced was EXA-16

trace:

com.exalate.domain.exception.editor.ScriptEditorException: com.exalate.admin.editor.errors.mappings.script at com.exalate.replication.services.replication.mapping.MappingService$$anonfun$1.applyOrElse(MappingService.scala:294) at com.exalate.replication.services.replication.mapping.MappingService$$anonfun$1.applyOrElse(MappingService.scala:276) at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:38) at scala.util.Failure.recoverWith(Try.scala:236) at com.exalate.replication.services.replication.mapping.MappingService.executeInScriptRule(MappingService.scala:276) at com.exalate.replication.services.replication.mapping.MappingService.$anonfun$receive$8(MappingService.scala:181) at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:307) at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41) at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64) at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:56) at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:93) at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23) at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85) at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:93) at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:48) at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:48) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) Caused by: com.exalate.api.exception.script.ScriptException: No signature of method: services.jcloud.hubobjects.NodeHelper.getLocalIssueKeyFromRemoteUrn() is applicable for argument types: (String, String) values: [EXA-16, issue] Possible solutions: getLocalIssueKeyFromRemoteId(java.lang.String, java.lang.String), getLocalIssueFromRemoteUrn(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long, java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long) at com.exalate.error.services.ScriptExceptionCategoryService.categorizeProcessorAndIssueTrackerExceptionsIntoScriptExceptions(ScriptExceptionCategoryService.scala:40) at com.exalate.processor.ExalateProcessor.executeProcessor(ExalateProcessor.java:57) at com.exalate.replication.services.replication.mapping.MappingService.$anonfun$executeInScriptRule$1(MappingService.scala:272) at scala.util.Try$.apply(Try.scala:213) at com.exalate.replication.services.replication.mapping.MappingService.executeInScriptRule(MappingService.scala:269) … 16 more Caused by: javax.script.ScriptException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: services.jcloud.hubobjects.NodeHelper.getLocalIssueKeyFromRemoteUrn() is applicable for argument types: (String, String) values: [EXA-16, issue] Possible solutions: getLocalIssueKeyFromRemoteId(java.lang.String, java.lang.String), getLocalIssueFromRemoteUrn(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long, java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:158) at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at com.exalate.processor.ExalateProcessor.execute(ExalateProcessor.java:98) at com.exalate.processor.ExalateProcessor.executeProcessor(ExalateProcessor.java:55) … 19 more Caused by: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: services.jcloud.hubobjects.NodeHelper.getLocalIssueKeyFromRemoteUrn() is applicable for argument types: (String, String) values: [EXA-16, issue] Possible solutions: getLocalIssueKeyFromRemoteId(java.lang.String, java.lang.String), getLocalIssueFromRemoteUrn(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long, java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155) … 22 more Caused by: groovy.lang.MissingMethodException: No signature of method: services.jcloud.hubobjects.NodeHelper.getLocalIssueKeyFromRemoteUrn() is applicable for argument types: (String, String) values: [EXA-16, issue] Possible solutions: getLocalIssueKeyFromRemoteId(java.lang.String, java.lang.String), getLocalIssueFromRemoteUrn(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long, java.lang.String), getLocalIssueKeyFromRemoteId(java.lang.Long) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:70) at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:135) at Script49.run(Script49.groovy:18) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317) … 23 more

Daniel Carvajal commented on 23 March 2022

Hi Tony

After some testing we found the following, try this script(adapt for instance execution as in the previous examples):

if (executionInstanceName == "B") {
    
    if (B.issue.type.name == "Epic") {
        B.issue.customFields."Epic Name".value = A.issue.customFields."Epic Name"?.value
    } else {
        
        if (A.issue."parentId"){
            
            Long remoteParentId = A.issue.parentId as Long
            Long localParentId = nodeHelper.getLocalIssueKeyFromRemoteId(remoteParentId, "issue").id
            B.issue.parentId = localParentId
        } else {
            def remoteEpicIssueKey = A.issue.customFields."Epic Link"?.value?.id
            B.issue.customFields."Epic Link".value = nodeHelper.getLocalIssueKeyFromRemoteId(remoteEpicIssueKey.toString(), "issue")?.urn

        }
        
    }
}


Seems like the issue was the “getLocalIssueKeyFromRemoteUrn” which should’ve been “getLocalIssueKeyFromRemoteId”.

Let me know if this works on your end.

Cheers,

Daniel

Answer by Francis Martens (Exalate) on 14 October 2021

There are different levels to answer this question

  • First level is that the combination Epic, Story, Subtask is synced correctly
  • Second level is that the whole Epic, with all its stories, and subtasks are synced in one go
  • Third level is to have this functionality works bidirectionally
  • Fourth level is to have it on board level, including sprints and all that jazz

This answer will focus on the first level:

  • Sync an epic correctly (epic name is mandatory)
  • Whenever a story is synced, ensure that the epic link is set correctly
  • Whenever a subtask is synced, ensure that the parent is set correctly

All of this needs to work on a visual mode connection

Assume now for a moment that you have setup a visual connection ‘a’ to ‘b’ (a and b are the short names which you define when setting up the conenction)

Apart from configuring standard fields, you will have to define a script rule to ensure that the issue relations (Story to Epic, Subtask to parent) are being respected.

As there is currently no graphical rule allowing to configure this behaviour. you will have to revert to the scripted rule

And in the script enter the code as detailed here

Some explanation

  • Line 1
    Apply the logic on side ‘b’
  • Line 3 and line 4
    if the incoming sync is an epic, ensure that the epic name is set correctly to the epic name on the source
  • Line 7
    Check if the incoming sync is about a subtask. It will be the case because the parentId is set.
    (psst - this is specific for jira-jira. ado-Jira is a bit different)
  • Line 10-12
    In case that the incoming sync is about a subtask, calculate the twin parent, and set it accordingly
  • Line 18-20
    In the case of a story, set the epic link

There is a flaw in the example above, but we leave it as an exercise to the reader to locate the flaw in the logic.
Hint - what happens with issues not part of an epic.

Next up - if time allows - we will focus on level 2 of the epic sync case


Comments:

Francis Martens (Exalate) commented on 15 October 2021

There is some doubt that this configuration works.
Therefore a video

Note the examples are examples and not full implementations

Wesley Adams commented on 29 October 2021

Hi Francis Martens (Exalate), thanks for the code you provided here. I am getting an error when applying it that states, “MissingPropertyException: No such property: urn for class: com.google.gson.JsonPrimitiveCheck”

I have included my code below just to make sure my syntax or where I have each instance is not causing an issue. Per the comments at the top of the visual mode scripting box I have used the on[“”“Instance Name Here”“”] format in my code to reference the remote and local sides of my connection. Could you let me know how to resolve this error?

if (executionInstanceName == "Remote Instance Name") {

    if (on["""Remote Instance Name"""].issue.type.name == "Epic") {
        on["""Remote Instance Name"""].issue.customFields."Epic Name"?.value = on["""Local Instance Name"""].issue.customFields."Epic Name"?.value
    } else {
        
        if (on["""Local Instance Name"""].issue."parentId")  {
            // this is a subtask - set the local parent it

            Long remoteParentId = on["""Local Instance Name"""].issue.parentId as Long
            Long localParentId = nodeHelper.getLocalIssueKeyFromRemoteId(remoteParentId, "issue").id
            on["""Remote Instance Name"""].issue.parentId = localParentId

        } else {
            // set the epic link custom field, such that it points to the twin epic of the parent of the source
            
            def remoteEpicIssueKey = on["""Local Instance Name"""].issue.customFields."Epic Link"?.value?.urn
            on["""Remote Instance Name"""].issue.customFields."Epic Link".value = nodeHelper.getLocalIssueKeyFromRemoteUrn(remoteEpicIssueKey, "issue")
        }
    }
}
Francis Martens (Exalate) commented on 29 October 2021

Can you inspect the value of

on["""Local Instance Name"""].issue.customFields."Epic Link"?.value

by using something like

debug.error("epic link contains ${on["""Local Instance Name"""].issue.customFields."Epic Link"?.value?.properties}")

PS. if your shortnames are names without spaces, you can use the more convenient approach to name the objects

target.issue.customFields."Epic Link"?.value

It makes the code more readable.

Wesley Adams commented on 30 October 2021

Thanks Francis Martens (Exalate) , could you clarify where in the script I provided the suggested code *debug.error(“epic link contains ${on[”““Local Instance Name””“].issue.customFields.“Epic Link”?.value?.properties}”)*would be best entered? I am getting syntax issues when inserting it so it is difficult to tell where it would be best to place it.

Also, I definitely agree with the shortname suggestions and was thinking the same thing earlier. I am not seeing a place where I can rename the shortnames after establishing the connection. Do you know if there is a way I can rename each side so I do not have to establish a whole new connection in order to change the name of each side?

Wesley Adams commented on 30 October 2021

Francis Martens (Exalate) Does this script require any specific setup before it will be able to run? For instance, do I need to do anything involving uploading external scripts to a library related to our Jira Data Center instance?

Wesley Adams commented on 30 October 2021

I tried adding the debug example you provided after the last line of code but the error message did not change.