///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Net;
using System.Xml;
using CookComputing.XmlRpc;
namespace JetBrains.ExceptionReport
{
///
/// Class for submitting exceptions into JIRA.
///
public class JiraExceptionSubmitter : IExceptionSubmitter
{
public event SubmitProgressEventHandler SubmitProgress;
private readonly string myJiraProjectKey;
private readonly string myProduct;
private bool myNeedProcessEvents = true;
private string myDefaultUserName;
private string myDefaultPassword;
private string myComponent = "Submitted Exceptions";
private string myStatus = null;
public string Status
{
get { return myStatus; }
set { myStatus = value; }
}
public string Component
{
get { return myComponent; }
set { myComponent = value; }
}
public JiraExceptionSubmitter( string jiraProjectKey, string product )
{
myJiraProjectKey = jiraProjectKey;
myProduct = product;
}
public bool NeedProcessEvents
{
set { myNeedProcessEvents = value; }
}
public void SetDefaultLogin( string userName, string password )
{
myDefaultUserName = userName;
myDefaultPassword = password;
}
protected void OnSubmitProgress( string message )
{
if (SubmitProgress != null)
SubmitProgress( this, new SubmitProgressEventArgs( message ) );
}
public SubmissionResult SubmitException( Exception e, string description, string userName, string password, int buildNumber, IWebProxy proxy )
{
return SubmitException( e.ToString(), e.Message, e.StackTrace, description,
userName, password, buildNumber, null, proxy );
}
public SubmissionResult SubmitException( string excString, string excMessage, string excStackTrace, string description, string itnUserName, string itnPassword, int buildNumber, string osVersion, IWebProxy proxy )
{
string md5Hash = ITNExceptionSubmitter.GetExceptionHash( excString );
ErrorReportProxy reportProxy = new ErrorReportProxy( proxy, myNeedProcessEvents );
try
{
OnSubmitProgress( "Checking..." );
int itnThread = -1;
ExceptionStruct es = new ExceptionStruct();
bool checkFailed = reportProxy.CheckException( md5Hash, ref es );
if (!checkFailed)
itnThread = es.exceptionItnThread;
OnSubmitProgress( "Posting..." );
bool isComment = true;
string errDescription = (description.Length == 0)
? excString
: description + "\n" + excString;
IJiraService service = (IJiraService) XmlRpcProxyGen.Create( typeof (IJiraService) );
string token;
if (myDefaultUserName != null && myDefaultPassword != null)
{
try
{
token = service.login( itnUserName, itnPassword );
}
catch (XmlRpcFaultException)
{
token = service.login( myDefaultUserName, myDefaultPassword );
itnUserName = myDefaultUserName;
itnPassword = myDefaultPassword;
}
}
else
token = service.login( itnUserName, itnPassword );
XmlRpcStruct[] statuses = service.getStatuses( token );
XmlRpcStruct[] resolutions = service.getResolutions( token );
XmlRpcStruct issue = new XmlRpcStruct();
if (itnThread == -1)
{
isComment = false;
XmlRpcStruct[] components = service.getComponents( token, myJiraProjectKey );
XmlRpcStruct[] issueTypes = service.getIssueTypes( token );
XmlRpcStruct[] priorities = service.getPriorities( token );
issue[ "project" ] = myJiraProjectKey;
if (myComponent != null)
{
foreach( XmlRpcStruct component in components )
{
if ((string) component[ "name" ] == myComponent)
issue[ "components" ] = new XmlRpcStruct[] {component};
}
}
MapNameToId( issue, issueTypes, "type", "Exception" );
MapNameToId( issue, priorities, "priority", "Major" );
issue[ "summary" ] = ITNExceptionSubmitter.GetExceptionTitle( excMessage, excStackTrace );
issue[ "description" ] = errDescription;
issue = service.createIssue( token, issue );
string issueKey = (string) issue[ "key" ];
itnThread = Int32.Parse( issueKey.Substring( issueKey.IndexOf( "-" ) + 1 ) );
if (myStatus != null)
{
foreach( XmlRpcStruct status in statuses )
{
if ((string) status[ "name" ] == myStatus)
{
service.setIssueStatus( itnUserName, itnPassword, issueKey, (string) status[ "id" ], "" );
break;
}
}
service.setIssueStatus( itnUserName, itnPassword, issueKey, myStatus, "" );
}
}
else
{
string issueKey = myJiraProjectKey + "-" + itnThread;
service.addIssueComment( itnUserName, itnPassword, issueKey,
errDescription );
issue = service.getIssue( token, issueKey );
}
XmlDocument requestDescription = new XmlDocument();
requestDescription.LoadXml( "" );
requestDescription.DocumentElement.SetAttribute( "url",
"http://www.jetbrains.net/jira/browse/" + issue[ "key" ] );
requestDescription.DocumentElement.SetAttribute( "state",
MapIdToName( issue, statuses, "status" ) );
requestDescription.DocumentElement.SetAttribute( "resolution",
MapIdToName( issue, resolutions, "resolution" ) );
XmlElement fixVersionNodes = requestDescription.CreateElement( "fixVersions" );
requestDescription.DocumentElement.AppendChild( fixVersionNodes );
object[] fixVersions = (object[]) issue ["fixVersions"];
foreach( object fixVersionObj in fixVersions )
{
XmlRpcStruct fixVersion = (XmlRpcStruct) fixVersionObj;
XmlElement fixVersionNode = requestDescription.CreateElement( "version" );
fixVersionNode.SetAttribute( "name", (string) fixVersion ["name"] );
fixVersionNodes.AppendChild( fixVersionNode );
}
if (!checkFailed)
{
if (!isComment)
{
es = new ExceptionStruct();
es.exceptionHash = md5Hash;
es.exceptionMessage = excString;
es.exceptionDate = DateTime.Now;
es.exceptionStack = excStackTrace == null ? String.Empty : excStackTrace;
es.exceptionItnThread = itnThread;
es.exceptionBuildNumber = buildNumber.ToString();
es.exceptionProductCode = myProduct;
es.exceptionScrambled = false;
}
OnSubmitProgress( "Submitting..." );
reportProxy.SubmitException( itnUserName, es, excString, isComment );
}
return new SubmissionResult( itnThread, isComment, requestDescription );
}
finally
{
OnSubmitProgress( "" );
}
}
private static void MapNameToId( XmlRpcStruct issue, XmlRpcStruct[] items, string idField, string name )
{
foreach( XmlRpcStruct item in items )
{
if ((string) item[ "name" ] == name )
{
issue[ idField ] = item[ "id" ];
break;
}
}
}
private string MapIdToName( XmlRpcStruct issue, XmlRpcStruct[] items, string idField )
{
string id = (string) issue [idField];
foreach( XmlRpcStruct item in items )
{
if ((string) item[ "id" ] == id )
{
return (string) item ["name"];
}
}
return "?";
}
}
[XmlRpcUrl( "http://www.jetbrains.net/jira/rpc/xmlrpc" )]
public interface IJiraService
{
[XmlRpcMethod( "jira1.login" )]
string login( string userName, string password );
[XmlRpcMethod( "jira1.getIssueTypes" )]
XmlRpcStruct[] getIssueTypes( string token );
[XmlRpcMethod( "jira1.getVersions" )]
XmlRpcStruct[] getVersions( string token, string projectKey );
[XmlRpcMethod( "jira1.getComponents" )]
XmlRpcStruct[] getComponents( string token, string projectKey );
[XmlRpcMethod( "jira1.getStatuses" )]
XmlRpcStruct[] getStatuses( string token );
[XmlRpcMethod( "jira1.getPriorities" )]
XmlRpcStruct[] getPriorities( string token );
[XmlRpcMethod( "jira1.getResolutions" )]
XmlRpcStruct[] getResolutions( string token );
[XmlRpcMethod( "jira1.createIssue" )]
XmlRpcStruct createIssue( string token, XmlRpcStruct rIssueStruct );
[XmlRpcMethod( "jira1.getIssue" )]
XmlRpcStruct getIssue( string token, string issueKey );
[XmlRpcMethod( "omeajira1.setIssueStatus" )]
void setIssueStatus( string userName, string password, string issueKey, string status, string resolution );
[XmlRpcMethod( "omeajira1.addIssueComment" )]
void addIssueComment( string userName, string password, string issueKey, string comment );
}
}