Getting Bugs From Bugzilla Using XML-RPC

bugzill-xml-rpc-vs

I recently had to develop an interface to get bug information out of an older Bugzilla installation (version 4.x). The older installation didn’t support REST and only supported the older XML-RPC interface.

Using some basic code from xml-rpc.net I was able to get a simple interface up and running in short order…

If you have a project, you can install xml-rpc.net using NuGet.

PM> Install-Package xmlrpcnet
bugzill-xml-rpc-vs-references

Once the XMLRPCNET packages are installed, you should be able to see the References added to your project.

You can get more information on the XML-RPC spec for Bugzilla, here.

For the actual Bug.get call, the documentation is, here.

The way the xmlrpcnet packages work is by setting up and interface that then is turned into a proxy for the XML-RPC service.

Here is the basic and super simple interface for getting a bug out of Bugzilla using the “Bug.get” interface.

[XmlRpcUrl("https://bugzilla/xmlrpc.cgi")]
public interface IBugzillaInterface : IXmlRpcProxy
{
	[XmlRpcMethod("Bug.get", StructParams = true)]
	XmlRpcStruct Get(string Bugzilla_login, string Bugzilla_password, int[] ids);
}

In this code, we are defining the interface and providing the URL to our Bugzilla installation (XmlRpcUrl) and then defining the “get” method. To the get method we are passing in a login, password and the IDs of the bugs we wish to be returned. In the later specs, you can pass a Bugzilla_login and Bugzilla_password variable to any interface and it will login as that user. This keeps us from having to mess with the Login interfaces, and keeps it simple for this implementation.

Once we have the Interface defined, it’s easy to use it…

IBugzillaInterface bugzilla = XmlRpcProxyGen.Create<IBugzillaInterface>();
bugzilla.NonStandard = XmlRpcNonStandard.AllowStringFaultCode;

XmlRpcStruct returnValue = bugzilla.Get(
	"User Name",
	"Password",
	new int[] { iBugNumber });

If everything was done correctly, this will populate the “returnValue” with the information for the bugs you provided in the format of an XmlRpcStruct (an XmlRpcStruct is basically an array of key/value pairs like a Dictionary).

The XmlRpcStruct return value will generally have two keys, “bugs” and “faults”. Each bug will generally contain the following fields (in no particular order). For more details on these, reference the Bugzilla documentation, here.

  • [0]: “creator”
  • [1]: “platform”
  • [2]: “update_token”
  • [3]: “cc”
  • [4]: “flags”
  • [5]: “is_cc_accessible”
  • [6]: “blocks”
  • [7]: “classification”
  • [8]: “see_also”
  • [9]: “component”
  • [10]: “creation_time”
  • [11]: “is_confirmed”
  • [12]: “assigned_to”
  • [13]: “product”
  • [14]: “id”
  • [15]: “status”
  • [16]: “version”
  • [17]: “groups”
  • [18]: “summary”
  • [19]: “is_open”
  • [20]: “op_sys”
  • [21]: “whiteboard”
  • [22]: “keywords”
  • [23]: “url”
  • [24]: “last_change_time”
  • [25]: “qa_contact”
  • [26]: “target_milestone”
  • [27]: “cf_cloud”
  • [28]: “is_creator_accessible”
  • [29]: “severity”
  • [30]: “resolution”
  • [31]: “priority”
  • [32]: “depends_on”

These values are accessed like how you would access a Dictionary<string, string> value in C#.

XmlRpcStruct[] bugs = (XmlRpcStruct[])returnValue["bugs"];
XmlRpcStruct bug = (XmlRpcStruct)bugs[0];
string summary = bug["summary"].ToString();

Putting it all together into something…

public class BugzillaXmlRpcLoader
{
	[XmlRpcUrl("https://bugzilla/xmlrpc.cgi")]
	public interface IBugZillaInterface : IXmlRpcProxy
	{
		[XmlRpcMethod("Bug.get", StructParams = true)]
		XmlRpcStruct Get(string Bugzilla_login, string Bugzilla_password, int[] ids);
	}

	public static Bug GetBug(int bugNumber)
	{
		int iBugNumber = 0;

		if(!int.TryParse(bugNumber, out iBugNumber)) {
			throw new Exception("Unable to parse bug number. Bug numbers for Bugzilla must be integer values.");
		}

		if(iBugNumber < 1) {
			throw new Exception("The bug number must be greater than 0.");
		}

		try {
			// Create the Bugzilla proxy
			IBugzillaInterface bugzilla = XmlRpcProxyGen.Create<IBugzillaInterface>();
			bugzilla.NonStandard = XmlRpcNonStandard.AllowStringFaultCode;

#if DEBUG
			// Log the request/response
			RequestResponseLogger logger = new RequestResponseLogger();
			logger.Directory = "C:\\bugzilla\\";
			bugzilla.AttachLogger(logger);
#endif
			
			XmlRpcStruct returnValue = bugzilla.Get(
				"user name",
				"password",
				new int[] { iBugNumber });

			if (returnValue != null) {
				if (returnValue["bugs"] != null) {

					XmlRpcStruct[] bugs = (XmlRpcStruct[])returnValue["bugs"];

					// process the bug here
					if (bugs != null && bugs.Length > 0) {
						XmlRpcStruct bug = bugs[0];
						
						Bug b = new Bug();

						b.BugNumber = bug["id"].ToString();
						b.Description = bug["summary"].ToString();

						return b;
					}
				}
				else {
					// handle faults here
				}
			}
		}
		catch (Exception ex) {
			throw ex;
		}

		return null;
	}
}

This is an example of some of the logic in an application, not all of the components are there, but it should give you a good starting point for anything you may be doing.

Also, note the DEBUG section,

#if DEBUG
	RequestResponseLogger logger = new RequestResponseLogger();
	logger.Directory = "C:\\bugzilla\\";
	bugzilla.AttachLogger(logger);
#endif

This will create files containing the XML Request and XML Response. These are very helpful for debugging. If you don’t need the files, just comment out or remove the code.

bugzill-xml-rpc-xml-files

Hope it helps! Have any questions? Feel free to ask them in the comments below.

Leave a Reply

Your email address will not be published. Required fields are marked *