JIRA Integration From .NET

A quick note on JIRA (if you don't know already) - It is used for bug tracking, issue tracking, and project management. The name "JIRA" is actually inherited from the Japanese word "Gojira" which means "Godzilla". The basic use of this tool is to track issues and bugs related to your software and Mobile apps.  Reference link.
 
It is assumed you already have a JIRA environment setup (on-premises or cloud). For this sake of this article, we will be integrating with the cloud version but the same code will work for on-premises JIRA. For authentication to JIRA, we have 2 options 1) Passing username and password of valid JIRA user 2 ) Passing username and valid API token of Valid Jira User.
 
The second option is recommended because very soon the JIRA team is going to stop support for plain password authentication, so here we will do using 2nd option.
 
To generate an API token, refer to this link. 
 

JIRA Setup 

 
Project - DEM
 
Create 2 custom fields,
  1. DROPDOWNCUSTOMFIELD : Type - Dropdown, Possible Value -  India, USA, Canada
  2. CUSTOMFIELDTEXT : Type - Text (free field)
 
Solution Setup 
  • Created an ASP.NET application.
  • Added button 'Create Issue' 
  • Added 2 File Upload Control, to save multiple attachments.
  • Added a simple label to show success/failure(hidden on page load)
UI would look something like this.
 
Let us start adding supporting methods.
 
Step 1
 
Create local variables to store username and API token and Jira URL(of our organization),
  1. private string UserName = "brucewayne@gmail.com";  
  2. private string Password = "gdHlWI0PKoNlCCLzNzM0B22A";  
  3. private string Url = "https://wayneenterprise.atlassian.net/";  
Password here is API token, generated using this link.
 
Step 2 
 
Encoded credentials.
  1. public static string GetEncodedCredentials(string UserName, string Password)  
  2. {  
  3.     string mergedCredentials = String.Format("{0}:{1}", UserName, Password);  
  4.     byte[] byteCredentials = Encoding.UTF8.GetBytes(mergedCredentials);  
  5.     return Convert.ToBase64String(byteCredentials);  
  6. }  
Step 3
 
Method to create Issue,
  1. public void AddJiraIssue() {  
  2.  try {  
  3.   string JsonString = @ "{"  
  4.   "fields"  
  5.   "                :  {    
  6.   ""  
  7.   project "": {  
  8.     ""  
  9.     key """"  
  10.     DEM ""  
  11.    },  
  12.    ""  
  13.   summary """"  
  14.   Demo Jira from C#.NET "",  
  15.    ""  
  16.   description """"  
  17.   Demo Jira from C#.NET "",  
  18.    ""  
  19.   issuetype "": {  
  20.     ""  
  21.     name """"  
  22.     Bug ""  
  23.    },  
  24.    ""  
  25.   customfield_10028 """"  
  26.   Value that we 're putting into a Free Text Field."",    
  27.   ""  
  28.   customfield_10027 "": {  
  29.    ""  
  30.    value """"  
  31.    India ""  
  32.   }  
  33.  }  
  34. }  
  35. ";    
  36.   
  37. string restUrl = String.Format("{0}rest/api/2/issue/", Url);  
  38. HttpWebResponse response = null;  
  39. HttpWebRequest request = WebRequest.Create(restUrl) as HttpWebRequest;  
  40. request.Method = "POST";  
  41. request.Accept = "application/json";  
  42. request.ContentType = "application/json";  
  43. request.Headers.Add("Authorization""Basic " + Utility.GetEncodedCredentials(UserName, Password));  
  44. byte[] data = Encoding.UTF8.GetBytes(JsonString);  
  45. using(var requestStream = request.GetRequestStream()) {  
  46.  requestStream.Write(data, 0, data.Length);  
  47.  requestStream.Close();  
  48. }  
  49. using(response = request.GetResponse() as HttpWebResponse) {  
  50.  if (response.StatusCode == HttpStatusCode.Created) {  
  51.   var reader = new StreamReader(response.GetResponseStream());  
  52.   string str = reader.ReadToEnd();  
  53.   Console.WriteLine("The server returned '{0}'\n{1}", response.StatusCode, str);  
  54.   var jss = new System.Web.Script.Serialization.JavaScriptSerializer();  
  55.   var sData = jss.Deserialize < Dictionary < string,  
  56.    string >> (str);  
  57.   string issueKey = sData["key"].ToString();  
  58.   showSuccess("Issue created sucessfully.");  
  59.   AddAttachments(issueKey);  
  60.  } else {  
  61.   showError("Error returned from Server:" + response.StatusCode + " Status Description : " + response.StatusDescription);  
  62.  }  
  63. }  
  64. request.Abort();  
  65. }  
  66. catch (Exception ex) {  
  67.  showError("Exception:" + ex.Message);  
  68. }   
  69. } 
Note
  • We are creating issueType as Bug, 
  • We need to pass custom fields with id, this id can be retrieved from URL. 
Go to Settings->Issues->CustomFields, we will see a list of all fields here. Edit the Target field and it will open below the page. Get Id from query string parameter.
 
 
For field type like dropdown, checkbox, radio button, we need to pass value in json object like below
  1. "customfield_10027":{    
  2.                      "value" : "India"    
  3.                      }    
You should refer to the below link to see different REST API examples provided at JIRA documentation.
 
 https://developer.atlassian.com/server/jira/platform/jira-rest-api-examples/
 
In response to this web request, we will get issue key(id) which will be used further to add attachments. 
 
Please note, we don't have any endpoint directly which creates tickets and adds attachments in a single request hence we have to make 2 separate calls. 
 
Step 5 
 
Add a custom model(class) to hold the file name and its content. 
  1. class JiraFile  
  2. {  
  3.     public string FileName { get; set; }  
  4.     public Stream Stream { get; set; }  
  5. }   
Step 6 - Add the Attachments method
 
In this method, we will use Stream of FileUpload control to get file contents in memory and then upload directly to JIRA. This will make sure we don't have to store file at Server as we are posting file to JIRA from server-side
  1. public void AddAttachments(string issueKey)  
  2. {  
  3.     string restUrl = String.Format("{0}rest/api/2/issue/{1}/attachments", Url, issueKey);  
  4.     List<JiraFile> lstStream = new List<JiraFile>();  
  5.     if (FileUpload1.HasFile)  
  6.     {   
  7.         lstStream.Add(new JiraFile() { FileName = FileUpload1.FileName, Stream = FileUpload1.PostedFile.InputStream });  
  8.     }  
  9.     if (FileUpload2.HasFile) {   
  10.         lstStream.Add(new JiraFile() { FileName = FileUpload2.FileName, Stream = FileUpload2.PostedFile.InputStream });  
  11.     }  
  12.     if (lstStream.Count > 0)  
  13.     {  
  14.         PostFile(restUrl, lstStream);  
  15.     }  
  16. }  
Step 7
 
Making post request to JIRA API to add attachments,
  1. private void PostFile(string restUrl, List<JiraFile> lstStream)  
  2. {  
  3.     HttpWebResponse response = null;  
  4.     HttpWebRequest request = null;  
  5.     String boundary = String.Format("----------{0:N}", Guid.NewGuid());  
  6.     var stream = new MemoryStream();  
  7.     var writer = new StreamWriter(stream);  
  8.     foreach (var file in lstStream)  
  9.     {  
  10.         var data = new byte[file.Stream.Length];  
  11.         file.Stream.Read(data, 0, data.Length);  
  12.         file.Stream.Close();  
  13.         writer.WriteLine("--{0}", boundary);  
  14.         writer.WriteLine("Content-Disposition: form-data; name=\"file\"; filename=\"{0}\"", file.FileName);  
  15.         writer.WriteLine("Content-Type: application/octet-stream");  
  16.         writer.WriteLine();  
  17.         writer.Flush();  
  18.         stream.Write(data, 0, data.Length);  
  19.         writer.WriteLine();  
  20.     }  
  21.          
  22.     writer.WriteLine("--" + boundary + "--");  
  23.     writer.Flush();  
  24.     stream.Seek(0, SeekOrigin.Begin);  
  25.     request = WebRequest.Create(restUrl) as HttpWebRequest;  
  26.     request.Method = "POST";  
  27.     request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);  
  28.     request.Accept = "application/json";  
  29.     request.Headers.Add("Authorization""Basic " + Utility.GetEncodedCredentials(UserName, Password));  
  30.     request.Headers.Add("X-Atlassian-Token""nocheck");  
  31.     request.ContentLength = stream.Length;  
  32.     using (Stream requestStream = request.GetRequestStream())  
  33.     {  
  34.         stream.WriteTo(requestStream);  
  35.         requestStream.Close();  
  36.     }  
  37.     using (response = request.GetResponse() as HttpWebResponse)  
  38.     {  
  39.         if (response.StatusCode != HttpStatusCode.OK)  
  40.         {  
  41.             var reader = new StreamReader(response.GetResponseStream());  
  42.             Console.WriteLine("The server returned '{0}'\n{1}", response.StatusCode, reader.ReadToEnd());  
  43.         }  
  44.     }  
  45.     request.Abort();  
  46.   
  47. }  
Step 8
 
Add methods to show success and failure.
  1. private void showError(string text)  
  2. {  
  3.     lblStatus.Text =text;  
  4.     lblStatus.ForeColor = System.Drawing.Color.Red;  
  5.     lblStatus.Visible = true;  
  6. }  
  7.   
  8. private void showSuccess(string text)  
  9. {  
  10.     lblStatus.Text = text;  
  11.     lblStatus.ForeColor = System.Drawing.Color.Green;  
  12.     lblStatus.Visible = true;  
  13.   
  14. }  
Now we have everything we need, so let us run (press F5).
 
Select files and click on 'Create Issue', 
 
Let us go to Jira and see a newly created issue. If you see all the fields which are marked with numbers are the fields which we populated from our code. 
 
 

Conclusion

 
Modern web development is now mostly based on REST API integration, it is a must-have skillset for developers to understand how to make REST calls. Products nowadays expose their API via REST for cross-platform integration. If we know the basics of how to call this REST API, we can integrate our code/custom application with any products which have exposed via web services or SDK. We should be referring to product developer documentation to understand what parameters are required to pass and what will be returned in the response. This article demonstrates one such use about integration JIRA software from our custom application.  For advanced integration of JIRA, refer to its documentation at this link
 
I hope you enjoyed reading...happy coding!!!