/**
* Licensed Materials - Property of IBM* and/or HCL**
* IBM UrbanCode Deploy
* (c) Copyright IBM Corporation 2010, 2017. All Rights Reserved.
* (c) Copyright HCL Technologies Ltd. 2018. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*
* * Trademark of International Business Machines
* ** Trademark of HCL Technologies Limited
*/

/*
 * JIRA REST API Documentation: https://docs.atlassian.com/jira/REST/latest/
 */

package com.urbancode.air.plugin.jira.addcomments

import com.urbancode.air.XTrustProvider
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.util.XmlSlurper
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.client.methods.HttpPut
import org.apache.http.client.utils.URIBuilder
import org.apache.http.client.HttpResponseException
import org.apache.http.entity.StringEntity
import org.apache.http.HttpEntity
import org.apache.http.HttpResponse
import org.apache.http.NameValuePair
import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.message.BasicNameValuePair
import org.apache.http.util.EntityUtils

/**
 * Helper class for the JIRA Plugin. Makes many basic REST calls that assist in creating and
 * modifying JIRA issues. In addition, several functions are made to help format the correct
 * URI for the REST calls.
 */
public class JIRAHelper {
    /**
    * Empty constructor
    */
    public JIRAHelper() {
    }

    /**
    * Returns the DefaultHttpClient object that will connect and authenticate with the JIRA Server
    * Unless switching accounts, this object can be used for all REST calls. In addition, it is
    * set with Preemptive Authentication to send the authenticating message early to reduce overhead.
    *
    * @param username String representation of the username used to login into JIRA
    * @param password String representation of the password used to login into JIRA
    * @param proxyHost String representation of the proxy host
    * @param proxyHost String representation of the proxy port number
    * @return         Object contains the authentication information to connect with JIRA
    * @see            DefaultHttpClient
    */
    public DefaultHttpClient createClient(String username, String password, String proxyHost, String proxyPort, boolean allowInsecure) {
        DefaultHttpClient client = new DefaultHttpClient()
        HttpClientBuilder clientBuilder = new HttpClientBuilder()
        clientBuilder.setUsername(username)
        clientBuilder.setPassword(password)
        clientBuilder.setPreemptiveAuthentication(true)
        if (proxyHost && proxyPort) {
            clientBuilder.setProxyHost(proxyHost)
            clientBuilder.setProxyPort(Integer.valueOf(proxyPort))
        }
        if(allowInsecure) {
            println ("[Info] Allowing insecure connections...")
            XTrustProvider.install()
            clientBuilder.setTrustAllCerts(true)
        }

        client = clientBuilder.buildClient()
        return client
    }

    /**
     * Constructs and returns the formatted address for the specified REST call. JIRA REST calls
     * constructed in this function will have the following structure:
     * <serverUrl>/rest/api/latest/<resource>/<target>/<command>
     *
     * @param serverUrl The full URL of the JIRA server
     * @param resource  The data entity that will be defined by the target
     * @param target    The key, id, or type of the resource
     * @param command   The transition that will take place on the target
     * @return          The fully constructed address for the REST call
     */
    public String createAPIAddress(String serverUrl, String resource, String target, String command) {
        return createAPIAddress(serverUrl, resource, target + "/" + command)
    }

    /**
     * Constructs and returns the formatted address for the specified REST call. JIRA REST calls
     * constructed in this function will have the following structure:
     * <serverUrl>/rest/api/latest/<resource>/<target>
     *
     * @param serverUrl The full URL of the JIRA server
     * @param resource  The data entity that will be defined by the target
     * @param target    The key, id, or type of the resource
     * @return          The fully constructed address for the REST call
     */
    public String createAPIAddress(String serverUrl, String resource, String target) {
        return createAPIAddress(serverUrl, resource + "/" + target)
    }

    /**
     * Constructs and returns the formatted address for the specified REST call. JIRA REST calls
     * constructed in this function will have the following structure:
     * <serverUrl>/rest/api/latest/<resource>
     *
     * @param serverUrl The full URL of the JIRA server
     * @param resource  The data entity that will be defined by the target
     * @return          The fully constructed address for the REST call
     */
    public String createAPIAddress(String serverUrl, String resource) {
        String fullAddress = serverUrl + "/rest/api/latest/" + resource
        return checkHTTP(fullAddress)
    }

    /**
     * Confirm that the URL begins with either http or https. If it does not, then https:// will
     * be appended to the front of the string. This method is used in conjunction with the
     * createAPIAddress() functions to construct a proper REST call.
     *
     * @param url The string representation of a URL
     * @return    String that beings with http, otherwise https:// will be appended to the front
     */
    public String checkHTTP(String url) {
        if (!url.startsWith("http")) {
            url = "https://" + url
        }
        return url
    }

    /**
     * Calls a REST Get request and returns the HTTPResponse
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param fullUrl   Expects the full String URL for the given REST call
     * @param params    Default null. The name/value pairs to use in the request query
     * @return          The HTTPResponse for the GET REST Call
     */
    public HttpResponse doHttpGet(DefaultHttpClient client, String fullUrl, List<NameValuePair> params = null){
        URIBuilder uribuilder = new URIBuilder(fullUrl)
        if (params) {
            uribuilder.setParameters(params)
        }
        URI uri = uribuilder.build()

        HttpGet getRequest = new HttpGet(uri)
        getRequest.setHeader('Accept', 'application/json')

        // Execute the REST call
        HttpResponse response = client.execute(getRequest)
        return response
    }

     /**
      * Calls a REST POST request and returns the HTTPResponse
      *
      * @param client    The DefaultHttpClient that contains the necessary authentication information.
      * @param fullUrl   The complete URL address of the REST POST call.
      * @param postData  The data structure that contains the information for the post request.
      * @return          The HTTPResponse for the POST REST Call.
      */
      public HttpResponse doHttpPost(DefaultHttpClient client, String fullUrl, def postData){
          // Form that Issue JSON for the REST call
          StringEntity issueEntity = new StringEntity(postData.toString(), "UTF-8")
          issueEntity.setContentType("application/json")

          URI uri = new URIBuilder(fullUrl).build()

          // Create HttpPost Request and append Issue JSON
          HttpPost postRequest = new HttpPost(uri)
          postRequest.setHeader('Accept', 'application/json')
          postRequest.setEntity(issueEntity)

          // Execute the REST Call
          HttpResponse response = client.execute(postRequest)
          return response
      }

    /**
     * Calls a REST PUT request and returns the HTTPResponse
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param fullUrl   The complete URL address of the REST PUT call.
     * @param putData   The data structure that contains the information for the put request.
     * @return          The HTTPResponse for the PUT REST Call.
     */
    public HttpResponse doHttpPut(DefaultHttpClient client, String fullUrl, def putData){
        // Form that Issue JSON for the REST call
        StringEntity issueEntity = new StringEntity(putData.toString(), "UTF-8")
        issueEntity.setContentType("application/json")

        URI uri = new URIBuilder(fullUrl).build()

        // Create HttpPut Request and append Issue JSON
        HttpPut putRequest = new HttpPut(uri)
        putRequest.setHeader('Accept', 'application/json')
        putRequest.setEntity(issueEntity)

        // Execute the REST Call
        HttpResponse response = client.execute(putRequest)
        return response
    }

      /**
       * On success, REST JIRA responses often return information about the given resource. They are
       * returned as a JSON entity. This function will retrieve the response entity and convert
       * the JSON in a data structure of lists and maps.
       *
       * @param response  The HTTP response message the JIRA server responded with
       * @return          The returned enitity in an interpretable data structure of lists and maps
       */
      public def httpResponse2Map(HttpResponse response){
          // Parse the returned JSON
          HttpEntity returnEntity = response.getEntity()
          String json = EntityUtils.toString(returnEntity)
          JsonSlurper slurper = new JsonSlurper()
          def map = slurper.parseText(json)
          return map
      }

      /**
       * Create an Issue with the given JSON mapping. Return a Boolean based on its successful
       * creation.
       *
       * @param client    The DefaultHttpClient that contains the necessary authentication information.
       * @param serverUrl The URL address of the JIRA server.
       * @param issueJSON The JSON data structure that contains the information for the Issue
       * @return          Boolean based on the creation success of the issue. Expect status = 201
       */
      public Boolean createIssue(DefaultHttpClient client, String serverUrl, JsonBuilder issueJSON){
          // Construct the HTTP addresses
          String fullAddress = createAPIAddress(serverUrl, "issue")
          HttpResponse response = doHttpPost(client, fullAddress, issueJSON)
          def statusLine = response.getStatusLine()
          def statusCode = statusLine.getStatusCode()

          // Confirm the REST call was a success, fail otherwise
          if (statusCode == 201) {
            def issueResponse = httpResponse2Map(response)
            println "Creation of new Issue ${issueResponse.key} was successful."
            return true
          }
          else if (statusCode == 400) {
            println "Create Issue Failure: Input is invalid"
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return false
          }
          else {
            println "Create Issue Failure: Unexpected Status Code"
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return false
          }
      }

    /**
     * Creates and calls the REST call to perform an edit on an issue. The PUT data
     * will be appended to the call and sent to the server. The response is then analyzed and
     * will respond accordingly.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID whose transition will be updated.
     * @param editJSON  The JSON data structure that contains the update information for the Issue
     * @return          Boolean based on the updates success of the issue. Expect status = 204
     */
    public Boolean editIssue(DefaultHttpClient client, String serverUrl, String issueId, JsonBuilder editJSON){
        // Create HttpPost request and append Issue JSON
        String fullAddress = createAPIAddress(serverUrl, "issue", issueId)
        HttpResponse response = doHttpPut(client, fullAddress, editJSON)
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        // Analyze returned JSON...
        if (statusCode == 204) {
            println "Successfully edited issue ${issueId}"
            return true
        }
        else if (statusCode == 400) {
            println "Skipping Issue ${issueId}: Issue update failed."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return false
        }
        else if (statusCode == 404) {
            println "Skipping Issue ${issueId}: Issue does not exist or you do not " +
            "have the correct permissions to view it."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return false
        }
        else {
            println "Skipping Issue ${issueId}: Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return false
        }
    }

     /**
      * Creates and calls the REST call to perform a transition on an issue. The POST data has the
      * will be appended to the call and sent to the server. The response is then analyzed and
      * will respond accordingly.
      *
      * @param client    The DefaultHttpClient that contains the necessary authentication information.
      * @param serverUrl The URL address of the JIRA server.
      * @param issue     The Issue key or ID whose transition will be updated.
      * @param updateJSON The JSON data structure that contains the update information for the Issue
      * @return          Boolean based on the updates success of the issue. Expect status = 204
      */
     public Boolean transitionIssue(DefaultHttpClient client, String serverUrl, String issueId, JsonBuilder updateJSON){
         // Create HttpPost request and append Issue JSON
         String fullAddress = createAPIAddress(serverUrl, "issue", issueId, "transitions")
         HttpResponse response = doHttpPost(client, fullAddress, updateJSON)
         def statusLine = response.getStatusLine()
         def statusCode = statusLine.getStatusCode()

         // Analyze returned JSON...
         if (statusCode == 204) {
             println "Successfully performed transition on issue ${issueId}"
             return true
         }
         else if (statusCode == 400) {
             println "Skipping Issue ${issueId}: No transition was specified."
             println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
             return false
         }
         else if (statusCode == 404) {
             println "Skipping Issue ${issueId}: Issue does not exist or you do not " +
                 "have the correct permissions to view it."
             println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
             return false
         }
         else {
             println "Skipping Issue ${issueId}: Unexpected Status Code."
             println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
             return false
         }
     }

    /**
     * Uses the REST Post call to add a new comment to the Issue. A status code of 201 is
     * expected when the add comment is successful. The call will fail if an unknown issue is
     * given. Any String postData input is valid.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID where the new comment will be added.
     * @param postData  The String data structure that contains the comment
     * @return          True or false based on the success of adding the comment
     */
     public Boolean addIssueComment(DefaultHttpClient client, String serverUrl, String issueId, String postData){
         // Construct the issue specific address and postRequest
         String fullAddress = createAPIAddress(serverUrl, "issue", issueId, "comment")
         HttpResponse response = doHttpPost(client, fullAddress, postData)
         def statusLine = response.getStatusLine()
         def statusCode = statusLine.getStatusCode()

         // Analyze returned JSON...
         if (statusCode == 201) {
            println "Adding Comment to issue ${issueId} was successful."
            return true
         }
         else if (statusCode == 404) {
            println "Skipping Issue '${issueId}': Invalid input. (ex. ID or Key of issue)"
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return false
         }
         else {
            println "Skipping Issue ${issueId}: Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            throw new IllegalStateException("Critical Failure: Unexpected Status Code")
         }
     }

    /**
     * Returns the metadata for creating an issue in a given project, including fields and field types.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param projectKey Results will be limited to the given project.
     * @return          Mapping of all allowed issue types and fields for the given project.
     */
    public def getCreateIssueMeta(DefaultHttpClient client, String serverUrl, String projectKey){
        // Construct the URI to call upon
        String fullAddress = createAPIAddress(serverUrl, "issue", "createmeta")

        List<NameValuePair> params = []
        params.add(new BasicNameValuePair('expand', 'projects.issuetypes.fields'))
        params.add(new BasicNameValuePair('projectKeys', projectKey))

        HttpResponse response = doHttpGet(client, fullAddress, params)
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        // Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved Create Issue metadata...")
            return httpResponse2Map(response)
        }
        else {
            println "Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }

    /**
     * Returns the metadata for creating an issue in a given project, including fields and field types.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param projectKey Results will be limited to the given project.
     * @return          It will return the project details.
     */
    public def getProjectDetails(DefaultHttpClient client, String serverUrl, String projectKey){        
        String fullAddress = serverUrl + "/rest/api/2/project"
        HttpResponse response = doHttpGet(client, fullAddress)

        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        //Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved Create Issue metadata...")
            return httpResponse2Map(response)
        }
        else {
            println "Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }

    /**
     * Returns the type of issue in a given project, including fields and field types.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param projectKey Results will be limited to the given project.
     * @return          It will return the issue types.
     */
    public def getIssueTypes(DefaultHttpClient client, String serverUrl, String projectKey){
        
        String fullAddress = serverUrl + "/rest/api/2/issue/createmeta/" + projectKey + "/issuetypes"
        HttpResponse response = doHttpGet(client, fullAddress)
    
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        //Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved Create Issue metadata...")
            return httpResponse2Map(response)
        }
        else {
            println "Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }

    /**
     * Returns the metadata for of an issue in a given project, including fields and field types.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param projectKey Results will be limited to the given project.
     * @return          It will return the issue details.
     */
    public def getIssueDetails(DefaultHttpClient client, String serverUrl, String projectKey, def issueId){
                
        String fullAddress = serverUrl + "/rest/api/2/issue/createmeta/" + projectKey + "/issuetypes/" + issueId
        HttpResponse response = doHttpGet(client, fullAddress)
    
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        //Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved Create Issue metadata...")
            return httpResponse2Map(response)
        }
        else {
            println "Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }


    /**
     * Returns the metadata for editing an issue.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issueId   The issue that would be edited.
     * @return          Mapping of all allowed fields for the given issue.
     */
    public def getEditIssueMeta(DefaultHttpClient client, String serverUrl, String issueId){
        // Construct the URI to call upon
        String fullAddress = createAPIAddress(serverUrl, "issue", issueId, 'editmeta')

        HttpResponse response = doHttpGet(client, fullAddress)
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        // Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved Edit Issue metadata...")
            return httpResponse2Map(response)
        }
        else {
            println "Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }

    /**
     * Create a REST call that retrieves all information about the given issue. The call returns
     * the issue information in JSON format that is then parsed into a data structure of lists and
     * maps.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID whose information will be returned.
     * @return          Mapping of the Issue's information
     */
    public def getIssue(DefaultHttpClient client, String serverUrl, String issueId) {
        // Construct the URI to call upon
        String fullAddress = createAPIAddress(serverUrl, "issue", issueId)
        HttpResponse response = doHttpGet(client, fullAddress)
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        // Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved issue ${issueId}...")
            return httpResponse2Map(response)
        }
        else if (statusCode == 404) {
            println "Skipping Issue ${issueId}: Issue does not exist or you do not " +
                "have the correct permissions to view it."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
        else {
            println "Skipping Issue ${issueId}: Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }

    /**
     * Return the list of possible transitions for the given issue
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID whose information will be returned.
     * @return          Mapping of the Issue's transitions
     */
    public def getIssueTransitions(DefaultHttpClient client, String serverUrl, String issueId){
        // Construct the URI to call upon
        String fullAddress = createAPIAddress(serverUrl, "issue", issueId, "transitions")

        List<NameValuePair> params = []
        params.add(new BasicNameValuePair('expand', 'transitions.fields'))

        HttpResponse response = doHttpGet(client, fullAddress, params)
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()

        // Analyze returned JSON...
        if (statusCode == 200) {
            println("Retrieved transition for issue ${issueId}...")
            return httpResponse2Map(response)
        }
        else if (statusCode == 404) {
            println "Skipping Issue ${issueId}: Issue does not exist or you do not " +
                "have the correct permissions to view it."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
        else {
            println "Skipping Issue ${issueId}: Unexpected Status Code."
            println "Error - StatusCode: ${statusCode} - Message: " + response.entity?.content?.getText("UTF-8")
            return null
        }
    }

	/**
	 * Check if issues found with specified issue type given issue
	 *
	 * @param client    The DefaultHttpClient that contains the necessary authentication information.
	 * @param serverUrl The URL address of the JIRA server.
	 * @param issueType     The Issue key or ID whose information will be returned.
	 * @param statusName    Name of the status
	 * @return         Boolean based on if issues found are greater than 0.
	 */
	public boolean areIssuesFound(DefaultHttpClient client, String serverUrl, String project, String issueType, String statusName){
		// Construct the URI to call upon
		
		String jql="";
		
		if (!project.isEmpty()) {
			
			String[] projects = project.split(",");
			
			for(int i=0;i<projects.length;i++){
				projects[i] = projects[i].trim();
			}
			
			project = String.join(",", projects);
			
			jql+="project IN(" + project + ")" + " AND ";
			
		}
		
		jql+= "issuetype="+issueType;
		
		if (!statusName.equals("All")) {
			jql+=" AND status="+ "\"" + statusName +  "\"" ;
		}
		
		String encodeJql=URLEncoder.encode( jql, "UTF-8" )
		
		//the max results will still be limited by a setting in JIRA.
		encodeJql+="&maxResults=-1"
		
		String resource = "search?jql="+encodeJql
		
		String fullAddress = createAPIAddress(serverUrl, resource)
		
		println("JQL Search url  : " + fullAddress)

		HttpResponse response = doHttpGet(client, fullAddress)
		def statusLine = response.getStatusLine()
		def statusCode = statusLine.getStatusCode()

		int noOfIssuesFound
		def searchResult
		
		// Analyze returned JSON...
		if (statusCode == 200) {
			
			searchResult = httpResponse2Map(response)
			noOfIssuesFound = searchResult.total
			
		}else if (statusCode == 401) {
			println "Invalid credentials!!"
			throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
		}else {
			throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
		}
		
		if (noOfIssuesFound > 0) {
			
			println("Retrieved issues : ${noOfIssuesFound}")
			println("")
			println("Listing issues below :")
			println("")
			
			searchResult.issues.each{ issue->
				println(issue.key)
			}
			println("")
			
			return true;
		}else {
			return false;
		}
	}
	
    /**
     * The status represents the stage at which the issue is located in its lifecycle such
     * as 'Open' or 'Resolved.' This function will retrieve the status of the given Issue.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID whose status is being retrieved.
     * @return          The status of the given Issue
     */
    public String getIssueStatus(DefaultHttpClient client, String serverUrl, String issueId) {
        // Retrieve the Issue mapping. getIssue() failure will return null
        def statusMap = getIssue(client, serverUrl, issueId)

        // Analyze the JSON status map. Return the status name
        if (statusMap) {
            return statusMap.fields.status.name
        }
        return null
    }

    /**
     * The status category represents the general stage at which the issue is located in its lifecycle.
     * Each status is grouped under a variety different status categories such as: 'To-Do,'
     * 'In Progress,' 'Done,' or 'Complete.' This function will retrieve the status cateogry of the
     * given Issue's status.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID whose status is being retrieved.
     * @return          The status category of the given Issue's status
     */
    public String getIssueStatusCategory(DefaultHttpClient client, String serverUrl, String issueId) {
        // Retrieve the Issue mapping. getIssue() failure will return null
        def statusMap = getIssue(client, serverUrl, issueId)

        // Analyze the JSON status map. Return the status category name
        if (statusMap) {
            return statusMap.fields.status.statusCategory.name
        }
        return null
    }

    /**
     * Checks the JIRA server to confirm issue does or does not exist. Returns true or false
     * based on the issues existance. Expects a status code of 200 returned from the REST call
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information
     * @param serverUrl The URL address of the JIRA server
     * @param issue     The Issue key or ID whose existance will be determined
     * @return          True indicates the Issue does exist. False indicates that the Issue does not.
     */
     public Boolean doesIssueExist(DefaultHttpClient client, String serverUrl, String issueId){
        if (getIssue(client, serverUrl, issueId)) {
            return true
        }
        return false
    }

    /**
     * For certain transtion states, a resolution state can be specified to better explain the state
     * of a given issue. This function will confirm that a given resolution does exist. By default,
     * the resolution state can only be used for 'Close Issue' and 'Resolve Issue' transitions
     *
     * @param client        The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl     The URL address of the JIRA server.
     * @param resolution    The way in which the issue can be closed. States specified in JIRA
     * @return              Boolean based on the resolution's existance in JIRA server
     */
    public Boolean doesResolutionExist(DefaultHttpClient client, String serverUrl, String resolutionName){

        // Construct the URI, run GET REST, and retrieve the entity mapping
        String fullAddress = createAPIAddress(serverUrl, "resolution")
        HttpResponse response = doHttpGet(client, fullAddress)
        def resolutionsMap = httpResponse2Map(response)

        // Analyze the JSON transition map. Determine if map contains the resolution
        def resolutionArray = []
        for (def foundResolution : resolutionsMap) {
            resolutionArray << foundResolution.name
            if (foundResolution.name == resolutionName){
                return true
            }
        }

        println "Could not find '${resolutionName}' resolution."
        println "Available resolutions: " + resolutionArray
        return false
    }


    /**
     * The resolution explains how the Issue has been closed. This function will determine if the
     * given resolution is valid. True or false will be returned based on valid resolution
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param serverUrl The URL address of the JIRA server.
     * @param issue     The Issue key or ID whose status being checked for the resolution
     * @param resolutionName     The resolution's name in question
     * @return          True if resolution is valid, else false
     */
    public Boolean isResolutionValid(DefaultHttpClient client, String serverUrl, String issueId, String resolutionName){
        if (resolutionName) {
            String issueStatusCategory = getIssueStatusCategory(client, serverUrl, issueId)
            if(issueStatusCategory == 'New' || issueStatusCategory == "In Progress") {
                if(!doesResolutionExist(client, serverUrl, resolutionName)) {
                    println "${resolutionName} does not exist. Default resolution of 'Done'" +
                        "will be assigned."
                }
                else {
                    return true
                }
            }
            else {
                println "${resolutionName} is not necessary for issue ${issueId}. " +
                    "Default resolution of 'Done' will be assigned."
            }
        }
        else {
            println "No resolution name was given. Default resolution of 'Done' will be assigned."
        }
        return false
    }
}
