Questions for Confluence license has expired.

Please purchase a new license to continue using Questions for Confluence.

Sync Jira assignees to ZD dropdown for user selection.

 
1
0
-1

We need a user-defined drop-down field in ZD to be populated with data from all possible assignees in Jira i.e. if a new user in Jira Cloud is created and assigned an issue, this should create a new value in the ZD drop-down and populate the field with this value.


    CommentAdd your comment...

    2 answers

    1.  
      2
      1
      0

      Hi George Smith,


      The way I have designed this use case is that with every sync transaction, Jira sends out a list of all users. Then on the ZD side, this payload is received and populated into a ZD drop down field. The scripts to be used are the following:



      Jira Outgoing
      def a = new JiraClient(httpClient).http("GET", "/rest/api/3/user/assignable/multiProjectSearch",["projectKeys":["CM"]], null, [:])
      def jsonSlurper = new groovy.json.JsonSlurper()
      def parseText = jsonSlurper.parseText(a)
      
      def asg_list = new String[parseText.size()]
      def asg_email = new String[parseText.size()]
      for(int i=0; i<parseText.size(); i++){
          if (parseText[i].displayName != null){
              asg_list[i] = parseText[i].displayName
              asg_email[i] = parseText[i].emailAddress
          }
      }  
      replica.assignee_list = asg_list
      replica.assignee_email = asg_email



      Zendesk Incoming script
      class InjectorGetter {
      
      
          static Object getInjector() {
              try {
                  return play.api.Play$.MODULE$.current().injector()
              } catch (e) {
                  def context = com.exalate.replication.services.processor.CreateReplicaProcessor$.MODULE$.threadLocalContext.get()
                  if (!context) {
                      context = com.exalate.replication.services.processor.ChangeIssueProcessor$.MODULE$.threadLocalContext.get()
                  }
                  if (!context) {
                      context = com.exalate.replication.services.processor.CreateIssueProcessor$.MODULE$.threadLocalContext.get()
                  }
                  if (!context) {
                      throw new com.exalate.api.exception.IssueTrackerException(""" No context for executing external script CreateIssue.groovy. Please contact Exalate Support.""".toString())
                  }
                  context.injector
              }
          }
      }
      class ZdClient {
          // SCALA HELPERS
          private static <T> T await(scala.concurrent.Future<T> f) {
              scala.concurrent.Await$.MODULE$.result(f, scala.concurrent.duration.Duration$.MODULE$.Inf())
          }
      
          private static <T> T orNull(scala.Option<T> opt) { opt.isDefined() ? opt.get() : null }
      
          private static <T> scala.Option<T> none() { scala.Option$.MODULE$.<T> empty() }
      
          @SuppressWarnings("GroovyUnusedDeclaration")
          private static <T> scala.Option<T> none(Class<T> evidence) { scala.Option$.MODULE$.<T> empty() }
      
          private static <L, R> scala.Tuple2<L, R> pair(L l, R r) { scala.Tuple2$.MODULE$.<L, R> apply(l, r) }
      
          // SERVICES AND EXALATE API
          private static play.api.inject.Injector getInjector() {
              InjectorGetter.getInjector()
          }
      
          private static def getGeneralSettings() {
              def gsp = InjectorGetter.getInjector().instanceOf(com.exalate.api.persistence.issuetracker.IGeneralSettingsRepository.class)
              //def gsp = InjectorGetter.getInjector().instanceOf(com.exalate.api.persistence.issuetracker.IGeneralSettingsPersistence.class)
              def gsOpt = await(gsp.get())
              def gs = orNull(gsOpt)
              gs
          }
      
          private static String getIssueTrackerUrl() {
              final def gs = getGeneralSettings()
      
              def removeTailingSlash = { String str -> str.trim().replace("/+\$", "") }
              final def issueTrackerUrl = removeTailingSlash(gs.issueTrackerUrl)
              issueTrackerUrl
          }
      
          private httpClient
      
          def parseQueryString = { String string ->
              string.split('&').collectEntries { param ->
                  param.split('=', 2).collect { URLDecoder.decode(it, 'UTF-8') }
              }
          }
      
          //Usage examples: https://gist.github.com/treyturner/4c0f609677cbab7cef9f
          def parseUri
          {
              parseUri = { String uri ->
                  def parsedUri
                  try {
                      parsedUri = new URI(uri)
                      if (parsedUri.scheme == 'mailto') {
                          def schemeSpecificPartList = parsedUri.schemeSpecificPart.split('\\?', 2)
                          def tempMailMap = parseQueryString(schemeSpecificPartList[1])
                          parsedUri.metaClass.mailMap = [
                                  recipient: schemeSpecificPartList[0],
                                  cc       : tempMailMap.find { it.key.toLowerCase() == 'cc' }.value,
                                  bcc      : tempMailMap.find { it.key.toLowerCase() == 'bcc' }.value,
                                  subject  : tempMailMap.find { it.key.toLowerCase() == 'subject' }.value,
                                  body     : tempMailMap.find { it.key.toLowerCase() == 'body' }.value
                          ]
                      }
                      if (parsedUri.fragment?.contains('?')) { // handle both fragment and query string
                          parsedUri.metaClass.rawQuery = parsedUri.rawFragment.split('\\?')[1]
                          parsedUri.metaClass.query = parsedUri.fragment.split('\\?')[1]
                          parsedUri.metaClass.rawFragment = parsedUri.rawFragment.split('\\?')[0]
                          parsedUri.metaClass.fragment = parsedUri.fragment.split('\\?')[0]
                      }
                      if (parsedUri.rawQuery) {
                          parsedUri.metaClass.queryMap = parseQueryString(parsedUri.rawQuery)
                      } else {
                          parsedUri.metaClass.queryMap = null
                      }
      
                      if (parsedUri.queryMap) {
                          parsedUri.queryMap.keySet().each { key ->
                              def value = parsedUri.queryMap[key]
                              if (value.startsWith('http') || value.startsWith('/')) {
                                  parsedUri.queryMap[key] = parseUri(value)
                              }
                          }
                      }
                  } catch (e) {
                      throw new com.exalate.api.exception.IssueTrackerException("Parsing of URI failed: $uri $e ", e)
                  }
                  parsedUri
              }
          }
      
          ZdClient(httpClient, debug) {
              this.httpClient = httpClient
              this.debug = debug
          }
          def debug
      
          String http(String method, String path, java.util.Map<String, List<String>> queryParams, String body, java.util.Map<String, List<String>> headers) {
              http(method, path, queryParams, body, headers) { Response response ->
                  if (response.code >= 300) {
                      throw new com.exalate.api.exception.IssueTrackerException(
                              """Failed to perform the request $method $path (status ${response.code}), 
      and body was: ```$body``` 
      Please contact Exalate Support: """.toString() + response.body
                      )
                  }
                  response.body as String
              }
          }
      
          public <R> R http(String method, String path, java.util.Map<String, List<String>> queryParams, String body, java.util.Map<String, List<String>> headers, Closure<R> transformResponseFn) {
              def gs = getGeneralSettings()
              def unsanitizedUrl = issueTrackerUrl + path
              def parsedUri = parseUri(unsanitizedUrl)
      
              def embeddedQueryParams = parsedUri.queryMap
      
              def allQueryParams = embeddedQueryParams instanceof java.util.Map ?
                      ({
                          def m = [:] as java.util.Map<String, List<String>>;
                          m.putAll(embeddedQueryParams as java.util.Map<String, List<String>>)
                          m.putAll(queryParams)
                      })()
                      : (queryParams ?: [:] as java.util.Map<String, List<String>>)
      
              def urlWithoutQueryParams = { String url ->
                  URI uri = new URI(url)
                  new URI(uri.getScheme(),
                          uri.getUserInfo(), uri.getHost(), uri.getPort(),
                          uri.getPath(),
                          null, // Ignore the query part of the input url
                          uri.getFragment()).toString()
              }
              def sanitizedUrl = urlWithoutQueryParams(unsanitizedUrl)
      
              def response
              try {
                  def request = httpClient
                          .zendeskClient
                          .ws
                          .url(sanitizedUrl)
                          .withMethod(method)
      
                  if (!allQueryParams.isEmpty()) {
                      def scalaQueryParams = scala.collection.JavaConversions.asScalaBuffer(
                              queryParams
                                      .entrySet()
                                      .inject([] as List<scala.Tuple2<String, String>>) { List<scala.Tuple2<String, String>> result, kv ->
                                          kv.value.each { v -> result.add(pair(kv.key, v) as scala.Tuple2<String, String>) }
                                          result
                                      }
                      )
                      request = request.withQueryString(scalaQueryParams)
                  }
                  if (headers != null && !headers.isEmpty()) {
                      def scalaHeaders = scala.collection.JavaConversions.asScalaBuffer(
                              headers
                                      .entrySet()
                                      .inject([] as List<scala.Tuple2<String, String>>) { List<scala.Tuple2<String, String>> result, kv ->
                                          kv.value.each { v -> result.add(pair(kv.key, v) as scala.Tuple2<String, String>) }
                                          result
                                      }
                      )
                      request = request.withHeaders(scalaHeaders)
                  }
                  if (body != null) {
                      def writable = play.api.libs.ws.WSBodyWritables$.MODULE$.writeableOf_String()
                      request = request.withBody(body, writable)
                  }
                  
                  def authorizationHeader = await(httpClient.zendeskClient.getAuthHeaderFromGs())
                  request = request.addHttpHeaders(scala.collection.JavaConversions.asScalaBuffer([pair("Authorization", authorizationHeader) as scala.Tuple2<String, String>]))
                  //debug.error("${request.method()} ${request.url()} ${request.headers()}")
                  response = await(request.execute())
              } catch (Exception e) {
                  throw new com.exalate.api.exception.IssueTrackerException(
                          """Unable to perform the request $method $path with body:```$body```, 
      please contact Exalate Support: """.toString() + e.message,
                          e
                  )
              }
              java.util.Map<String, List<String>> javaMap = [:]
              for (scala.Tuple2<String, scala.collection.Seq<String>> headerTuple : scala.collection.JavaConverters.bufferAsJavaListConverter(response.allHeaders().toBuffer()).asJava()) {
                  def javaList = []
                  javaList.addAll(scala.collection.JavaConverters.bufferAsJavaListConverter(headerTuple._2().toBuffer()).asJava())
                  javaMap[headerTuple._1()] = javaList
              }
              def javaResponse = new Response(response.body(), new Integer(response.status()), javaMap)
              return transformResponseFn(javaResponse)
          }
      
          public static class Response {
              final String body
              final Integer code
              final java.util.Map<String, List<String>> headers
      
              Response(String body, Integer code, java.util.Map<String, List<String>> headers) {
                  this.body = body
                  this.code = code
                  this.headers = headers
              }
          }
      }
      
      
      issue.labels  = replica.labels
      issue.summary      = replica.summary
      issue.description  = replica.description ?: "No description"
      issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)
      issue.comments     += replica.addedComments
      
      
      def mapAssignees = [[:]]
      for (int i=0; i<replica.assignee_list.size(); i++){
          if(replica.assignee_email[i] != ""){
              Map temp = [:]
              temp.put("name" , "${replica.assignee_list[i]}")
              temp.put("value" , "${replica.assignee_email[i]?.replaceAll('@', '__')}")
              mapAssignees[i] = temp
            }
            else 
            {
              Map temp = [:]
              temp.put("name" , "${replica.assignee_list[i]}")
              temp.put("value" , "test${i}__email.com")
              mapAssignees[i] = temp
            }
            
      }
      
      Map<String,List<String>> queryParams = ["ticket_field": ["custom_field_options": mapAssignees]]
      def queryParamsJson = groovy.json.JsonOutput.toJson(queryParams)
      
      def res = new ZdClient(httpClient, debug)
          .http(
                  "PUT",
                  "/api/v2/ticket_fields/7309935056017",
                  [:],
                  "${queryParamsJson}",
                  ["Accept": ["application/json"], "Content-type" : ["application/json"]]
          ) 
          { 
              response ->
              if (response.code >= 400) {
                  throw new com.exalate.api.exception.IssueTrackerException("Failed to get orgnaizations with name ")
              } else response.body as String
          }
      
      if (replica.assignee?.email)
      issue.customFields."Jira Assignee".value.value = replica.assignee.email.replaceAll('@','__')


      Here is a short video demonstrating the solution in action:


      Thanks

      Majid


        CommentAdd your comment...
      1.  
        1
        0
        -1

        Hi Majid,
        I am getting an error and the list of email addresses is empty...remote replica is not showing the adresses.

        How to proceed at that point?

        1. Syed Majid Hassan

          What is the error please?

          And which side is it on?


        2. Kazim Yildirim

          Failed to get organizations name...on ZD side

        3. Syed Majid Hassan

          Not really, as it does not really give me much. Can you please book a slot here and we will give it another shot.

          Thanks

          Majid


        CommentAdd your comment...