JD2022-TU1/main/tools/legacy/CrashDumpSender/BugTrackSystem/Implementations/JiraImplementation.cs

493 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CrashDumpSender.JiraSoap;
using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
namespace CrashDumpSender.BugTrackSystem.Implementations
{
class JiraImplementation : IBugTracksystem
{
JiraSoapServiceService jiraProxy = null;
string reporterUsername = "crashdumpsender";
string reporterPassword = "justdance5";
string jiraToken;
string jiraProject;
SortedList<string, RemotePriority> priorities;
SortedList<string, RemoteComponent> components;
SortedList<string, RemoteCustomFieldValue> milestones;
SortedList<string, RemoteIssueType> types;
string fixCL;
public JiraImplementation()
{
jiraProxy = new JiraSoapServiceService();
jiraToken = jiraProxy.login(reporterUsername, reporterPassword);
jiraProject = Properties.Settings.Default.JiraProject;
priorities = new SortedList<string, RemotePriority>();
components = new SortedList<string, RemoteComponent>();
milestones = new SortedList<string, RemoteCustomFieldValue>();
types = new SortedList<string, RemoteIssueType>();
fixCL = null;
}
private void Relogin()
{
jiraToken = jiraProxy.login(reporterUsername, reporterPassword);
}
/// <summary>
/// Look for CustomField "nb of occurences - editor" in jira
/// </summary>
/// <returns>Custom field id</returns>
private string getNbOccurencesEditor()
{
RemoteField[] RemoteObject = jiraProxy.getCustomFields(jiraToken);
foreach (RemoteField value in RemoteObject)
{
if (value.name.Equals("nb of occurences - tools", StringComparison.OrdinalIgnoreCase))
{
return value.id;
}
}
return null;
}
/// <summary>
/// Gets id of "urgent" priority in Jira
/// </summary>
/// <returns>priority id</returns>
private string getCrashPriority()
{
RemotePriority[] RemoteObject = jiraProxy.getPriorities(jiraToken);
foreach (RemotePriority value in RemoteObject)
{
if (value.name.ToLower().Equals(CrashDumpSender.Properties.Settings.Default.Severity))
{
return value.id;
}
}
return null;
}
/// <summary>
/// Look for "Crash" issue type in jira
/// </summary>
/// <returns>Issue type id</returns>
private string getCrashType()
{
RemoteIssueType[] RemoteObject = jiraProxy.getIssueTypes(jiraToken);
foreach (RemoteIssueType value in RemoteObject)
{
if (value.name.ToLower().Trim().Equals(("crash")))
{
return value.id;
}
}
return null;
}
private string getCustomFieldId(string fieldName)
{
RemoteField[] RemoteObject = jiraProxy.getCustomFields(jiraToken);
foreach (RemoteField value in RemoteObject)
{
if (value.name.Trim().Equals(fieldName, StringComparison.OrdinalIgnoreCase))
{
return value.id;
}
}
return null;
}
private RemoteCustomFieldValue CreateCustomFieldValue(string name, string value)
{
string fieldId = getCustomFieldId(name);
if (string.IsNullOrEmpty(fieldId)) throw new System.Exception("Unknown JIRA custom field " + name);
RemoteCustomFieldValue customFieldValue = new RemoteCustomFieldValue();
customFieldValue.customfieldId = fieldId;
customFieldValue.values = new string[] { value };
return customFieldValue;
}
#region IBugTracksystem Members
// Unused
public string AddIssue(BugInfo bug)
{
return "";
}
/// <summary>
/// Adds a crash on Jira
/// </summary>
/// <param name="bug">Bug inforamtions passed by the user</param>
/// <returns>id of the issue on Jira</returns>
public string AddCrash(BugInfo bug)
{
Relogin();
RemoteIssue ri; // Remote Issue to update or Create
// First we build a JQL search string to find duplicates
// summary of crashes are a CRC of the callstack that's why we look for an issue
// with the same summary
string ExistingIssue = string.Format("project = {0} AND MD5 ~ {1}",
jiraProject,
bug.MD5);
// Do the search on Jira
RemoteIssue[] ExistingIssues = jiraProxy.getIssuesFromJqlSearch(jiraToken, ExistingIssue, 10);
// look for the CustomField ID of "Nb of Occurences - PC"
string NbOccEditorId = getNbOccurencesEditor();
if (ExistingIssues.Length > 0)
{
// Crash with same Stack exists
// => Update existing issue
ri = ExistingIssues[0];
// Create a field of ID "Nb of Occurences - PC" to update or create
RemoteFieldValue nbOccPc = new RemoteFieldValue();
nbOccPc.id = NbOccEditorId;
// Use a linq search to find if the issue already has a "Nb of Occurences - PC" Custom field
var NbOccPCField = from x
in ri.customFieldValues
where x.customfieldId.Equals(NbOccEditorId)
select x;
// Check if we can update issue with new value
bool updateIssue = false;
if (NbOccPCField.Count() == 0)
{
// field wasn't present before, now we now that the crash occured at least 2 times
nbOccPc.values = new string[] { "2" };
updateIssue = true;
}
else
{
int nbOccurences;
if (int.TryParse(NbOccPCField.Single().values[0], out nbOccurences))
{
nbOccurences++;
nbOccPc.values = new string[] { nbOccurences.ToString() };
updateIssue = true;
}
}
RemoteStatus[] statuses = jiraProxy.getStatuses(jiraToken);
var ClosedStatus = from x in statuses where x.name.ToLower().Trim().Equals("closed") select x;
var ResolvedStatus = from x in statuses where x.name.ToLower().Trim().Equals("resolved") select x;
if (ri.status.Equals(ClosedStatus.Single().id) || ri.status.Equals(ResolvedStatus.Single().id))
{
if (!string.IsNullOrEmpty(bug.changelist))
{
int CurCL = Convert.ToInt32(bug.changelist);
string clId = getCustomFieldId("changelist");
string FixedByCodeVersion = getFixedByCodeCustomFieldId();
bool bMustReopen = false;
if (!string.IsNullOrEmpty(clId))
{
foreach (RemoteCustomFieldValue rcfv in ri.customFieldValues)
{
if (rcfv.customfieldId.Equals(clId))
{
int bugCL = Convert.ToInt32(rcfv.values[0]);
if (CurCL > bugCL)
{
// We found a crash occuring on a new changelist than the original crash.
// So flag the issue as *potentially* must reopen, but continue cycling through custom fields
// as we may not know yet the changelist it was fixed on ("Fixed by code" customfield)
bMustReopen = true;
}
}
if (rcfv.customfieldId.Equals(FixedByCodeVersion))
{
// We found a "Fixed by code custom field
if (string.IsNullOrEmpty(rcfv.values[0]))
{
// if we found an empty value, then continue
// The decision of reopening the bug is now the based on the "changelist" custom field
continue;
}
// Check the changelist the crash is supposed to be fixed
int fixedByCode = Convert.ToInt32(rcfv.values[0]);
if (CurCL > fixedByCode)
{
// if the engine cl detected is greater than the fixed by code value then reopen the issue
bMustReopen = true;
break;
}
else
{
// if the engine cl detected is lower than the fixed by code value then DON'T reopen the issue !!
bMustReopen = false;
fixCL = fixedByCode.ToString();
break;
}
}
}
if (bMustReopen)
{
RemoteNamedObject[] ro = jiraProxy.getAvailableActions(jiraToken, ri.key);
var ApproveBug = from s in ro
where s.name.ToLower().Trim().Equals("reopen")
select s;
ri = jiraProxy.progressWorkflowAction(jiraToken, ri.key, ApproveBug.Single().id, null);
}
}
}
}
if (updateIssue)
{
RemoteFieldValue[] newValues = new RemoteFieldValue[] { nbOccPc };
ri = jiraProxy.updateIssue(jiraToken, ri.key, newValues);
// add comments
RemoteComment AdditionalComment = new RemoteComment();
int indexCamInfo = bug.comments.IndexOf("Camera Info");
string Comments;
if (indexCamInfo != -1)
Comments = bug.comments.Substring(indexCamInfo);
else
Comments = bug.comments;
Comments = Comments.Replace("-----------------------------\r\n", "");
AdditionalComment.body = string.Concat("*Reporter:* ", bug.reporter, "\n\n",
"*Engine:* ", bug.changelist, "\n\n",
"*Call stack & Warnings:* ", "\n",
Comments , "\n\n",
"*Steps to reproduce:* ", "\n",
bug.stepsToReproduce);
jiraProxy.addComment(jiraToken, ri.key, AdditionalComment);
if (bug.attachements != null && bug.attachements.Length > 0)
{
foreach (string str in bug.attachements)
{
string[] fileNames = new string[] { Path.GetFileName(str) };
string[] picData = new string[] { Convert.ToBase64String(File.ReadAllBytes(str)) };
jiraProxy.addBase64EncodedAttachmentsToIssue(jiraToken, ri.key, fileNames, picData);
}
}
return ri.key;
}
// ELSE we're not able to update an existing issue with same stack
// in order not to lose the crash info, we allow the creation of a new crash in Jira
}
// Create new Issue
ri = new RemoteIssue();
ri.summary = bug.summary;
// Recover crash type from jira
ri.type = getCrashType();
// Assign issue to our project
ri.project = jiraProject;
int iSeparateBar = bug.callstack.IndexOf("-----------------------------");
if (iSeparateBar != -1)
{
bug.description += "\n" + bug.callstack.Substring(iSeparateBar);
bug.callstack = bug.callstack.Substring(0, iSeparateBar);
}
// Adds description
ri.description = string.Format("*Reporter:* {0}\n\n{1}",
Environment.UserName,
bug.description);
// Check if an assignee was given
if (!string.IsNullOrEmpty(bug.assignee))
ri.assignee = bug.assignee;
// try to add the "Engine" component to the crash
RemoteComponent rcmpnt = GetComponent(bug.component);
if (rcmpnt != null)
{
ri.components = new RemoteComponent[] { rcmpnt };
}
// It doesn't exist (hidden?) in Ubisoft JIRA databases
// ri.priority = getCrashPriority();
// Prepare a list of custom fields to add to the issue
List<RemoteCustomFieldValue> crashCustomFields = new List<RemoteCustomFieldValue>();
crashCustomFields.Add(CreateCustomFieldValue("callstack", bug.callstack));
crashCustomFields.Add(CreateCustomFieldValue("MD5", bug.MD5));
crashCustomFields.Add(CreateCustomFieldValue("severity", bug.severity));
crashCustomFields.Add(CreateCustomFieldValue("probability", bug.probability));
crashCustomFields.Add(CreateCustomFieldValue("technical reproducibility", bug.technicalReproducibility));
crashCustomFields.Add(CreateCustomFieldValue("milestone", bug.milestone));
crashCustomFields.Add(CreateCustomFieldValue("platform/module", bug.platformModule));
crashCustomFields.Add(CreateCustomFieldValue("steps to reproduce", bug.stepsToReproduce));
crashCustomFields.Add(CreateCustomFieldValue("nb of occurences - tools", "1"));
crashCustomFields.Add(CreateCustomFieldValue("map", "None"));
crashCustomFields.Add(CreateCustomFieldValue("teams", "None"));
crashCustomFields.Add(CreateCustomFieldValue("Mode Specific", "NONE"));
// if we managed to custom fields, assign them to the issue
if (crashCustomFields.Count > 0)
{
ri.customFieldValues = crashCustomFields.ToArray();
}
// Create the new issue in Jira !
RemoteIssue NewlyCreatedIssue = jiraProxy.createIssue(jiraToken, ri);
if (bug.attachements != null && bug.attachements.Length > 0)
{
foreach (string str in bug.attachements)
{
string[] fileNames = new string[] { Path.GetFileName(str) };
string[] picData = new string[] { Convert.ToBase64String(File.ReadAllBytes(str)) };
jiraProxy.addBase64EncodedAttachmentsToIssue(jiraToken, NewlyCreatedIssue.key, fileNames, picData);
}
}
return NewlyCreatedIssue.key;
}
private string getFixedByCodeCustomFieldId()
{
RemoteField[] RemoteObject = jiraProxy.getCustomFields(jiraToken);
foreach (RemoteField value in RemoteObject)
{
if (value.name.ToLower().Trim().Equals(("fixed by code version")))
{
return value.id;
}
}
return null;
}
public List<string> GetAllMilestones()
{
List<string> miles = new List<string>();
//miles.Add("Go to prod");
RemoteVersion[] versions = jiraProxy.getVersions(jiraToken, jiraProject);
var VerNames = from x in versions orderby x.releaseDate descending select x;
foreach (RemoteVersion rv in VerNames.ToList())
{
miles.Add(rv.name);
}
return miles;
}
public List<string> getAllTypes()
{
RemoteIssueType[] RemoteObject = jiraProxy.getIssueTypes(jiraToken);
List<string> Names = new List<string>();
string strTemp;
foreach (RemoteIssueType value in RemoteObject)
{
strTemp = value.name;
Names.Add(strTemp);
types.Add(strTemp, value);
}
return Names;
}
private RemoteVersion getVersion(string versionlabel)
{
RemoteVersion[] versions = jiraProxy.getVersions(jiraToken, jiraProject);
var FoundVersion = from x in versions where x.name.Equals(versionlabel) select x;
if (FoundVersion.Count() == 1)
return FoundVersion.Single();
return null;
}
public List<string> getAllUsers()
{
StreamReader SR;
string S;
List<string> users = new List<string>();
if (File.Exists("jiraUsers.txt"))
{
SR = File.OpenText("jiraUsers.txt");
S = SR.ReadToEnd();
string[] lines = Regex.Split(S, "\r\n");
foreach (string user in lines)
users.Add(user);
SR.Close();
}
return users;
}
public RemoteComponent GetComponent(string ComponentName)
{
RemoteComponent[] RemoteObject = jiraProxy.getComponents(jiraToken, jiraProject);
string temp = ComponentName.ToLower().Trim();
foreach (RemoteComponent value in RemoteObject)
{
if (temp.Equals(value.name.ToLower()))
{
return value;
}
}
return null;
}
public List<string> GetAllComponents()
{
RemoteComponent[] RemoteObject = jiraProxy.getComponents(jiraToken, jiraProject);
List<string> Names = new List<string>();
string strTemp;
foreach (RemoteComponent value in RemoteObject)
{
strTemp = value.name;
Names.Add(strTemp);
components.Add(strTemp, value);
}
return Names;
}
public List<string> GetAllPriorities()
{
RemotePriority[] rp = jiraProxy.getPriorities(jiraToken);
List<string> Names = new List<string>();
string strTemp;
foreach (RemotePriority p in rp)
{
strTemp = p.name;
Names.Add(strTemp);
priorities.Add(strTemp, p);
}
return Names;
}
public string GetName()
{
return "Jira";
}
public string GetLinkToIssue(string IssueNumber)
{
return jiraProxy.Url.Replace("/rpc/soap/jirasoapservice-v2", "/browse/") + IssueNumber;
}
public string IssueIsFixed()
{
return fixCL;
}
#endregion
}
}