Questions for Confluence license has expired.

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

How to sync reporter from GitHub to Zendesk

 
1
0
-1

I am trying to set the reporter in ZD as the username from GH. Here is what I'm trying:


//set reporter as github username
issue.reporter = nodeHelper.getUserByUsername(replica.reporter?.username)


The reporter is not being updated in Zendesk and continues to submit as the same user (me) every time. 

    CommentAdd your comment...

    1 answer

    1.  
      1
      0
      -1

      Hi Sydney,

      This actually works. If the GitHub username for example is: sydneyh, If the user with the exact name exists in Zendesk as: sydneyh. You will get the reporter/requester changed. Now if they are not the exact same value, you will need to do a mapping left side GitHub - right side Zendesk:

      final def userMapping = [
            "sydneyh" : "Sydney Hollingsworth",
      ]
       issue.reporter = nodeHelper.getUserByUsername(userMapping[replica.reporter?.username])

      Or even do:

      final def userMapping = [
            "sydneyh" : "syneyh@company.com",
      ]
       issue.reporter = nodeHelper.getUserByEmail(userMapping[replica.reporter?.username])

      Kind regards,

      Ariel

      1. Sydney Hollingsworth

        Hi Ariel, 


        For our case, the users from GitHub will not exists in Zendesk and I would like them to be created when the ticket is created in Zendesk. Would this be possible? 


        Sydney

      2. Ariel Aguilar

        Hi Sydney,

        It is possible but it will require the use of advanced rest API configuration, so you can try:

        if (replica.typeName == "Issue") {
          issue.labels += nodeHelper.getLabel("github")
            new ZdClient(httpClient)
                        .http(
                        "POST",
                        "/api/v2/users/create_or_update.json",
                        [:],
                        groovy.json.JsonOutput.toJson(["user": ["name": replica.reporter.displayName, "email": replica.reporter.username + "@company.com"]]),
                        [
                            "Content-Type": ["application/json"], 
                            "Authorization":["Basic ZGV2emVuZGVzazNAaWRhbGtvLmNvbTpvaHJvQ3pYTzAz"]
                        ]
                        ) { response ->
                if (response.code >= 300 && response.code != 404) {
                    throw new com.exalate.api.exception.IssueTrackerException(
                        "Failed to perform the request POST/api.v2/users.json (status ${response.code}), and body was: \n\"${response.body}\"\nPlease contact Exalate Support: ".toString())
                }
                if (response.code == 404) {
                    return null
                }
                response.body as String
            }
            issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.username + "@company.com")
        }
        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.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) {
                this.httpClient = httpClient
            }
        
            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 (body != null) {
                        //def writable = play.api.libs.ws.WSBodyWritables$.MODULE$.writeableOf_String()
                        def json = play.api.libs.json.Json.parse(body)
                        def w = play.api.libs.ws.JsonBodyWritables$.MODULE$.writeableOf_JsValue()
                        request = request.withBody(json, w)
                    }
                    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)
                    }
                    //def authorizationHeader = await(httpClient.zendeskClient.getAuthHeaderFromGs())
                    //request = request.addHttpHeaders(scala.collection.JavaConversions.asScalaBuffer([pair("Authorization", authorizationHeader) as scala.Tuple2<String, String>]))
                    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
                }
            }
        }

        The most important part will be found here:


        if (replica.typeName == "Issue") {
          issue.labels += nodeHelper.getLabel("github")
            new ZdClient(httpClient)
                        .http(
                        "POST",
                        "/api/v2/users/create_or_update.json",
                        [:],
                        groovy.json.JsonOutput.toJson(["user": ["name": replica.reporter.displayName, "email": replica.reporter.username + "@company.com"]]),
                        [
                            "Content-Type": ["application/json"], 
                            "Authorization":["Basic ZGV2emVuZGVzazNAaWRhbGtvLmNvbTpvaHJvQ3pYTzAz"]
                        ]
                        ) { response ->
                if (response.code >= 300 && response.code != 404) {
                    throw new com.exalate.api.exception.IssueTrackerException(
                        "Failed to perform the request POST/api.v2/users.json (status ${response.code}), and body was: \n\"${response.body}\"\nPlease contact Exalate Support: ".toString())
                }
                if (response.code == 404) {
                    return null
                }
                response.body as String
            }
            issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.username + "@company.com")
        }

        Where it says Authorization, basically you will need to go into Zendesk, API and use API token to make this work. Basically, we are getting the username from GitHub and then tries to create a user in Zendesk if not existent. Once done after creating, I have also included what to do with the created user. It will assign the correct reporter as per this line:

        issue.reporter = nodeHelper.getUserByEmail(replica.reporter?.username + "@company.com")
        Final thoughts, you can leave the "class InjectorGetter" on last lines as best practice.
        Kind regards,

        Ariel

      CommentAdd your comment...