HP ALM – Give it a REST

I’m not a fan of HP ALM. They’ve managed to make it seem hard. However, there’s no doubt that they have a decent SaaS platform and a lot of reporting against their items. Today I needed to build something that could read items from their REST API and could then update them. There’s a distinct lack of information online about how to do this using the technology stack I’d chosen – C#. There’s also a lack of information about what to do if you have problems connecting to the HP ALM REST API from C#, especially you get the dreaded “Unsupported Media Type” error.

Preparing for this
I needed two things to get started:

Getting started was easy
It’s not hard to get a get working:

        /// <summary>
        /// Construct with the url (just the https://something.saas.hp.com bit)
        /// </summary>
        /// <param name="url">Base url for ALM</param>
        /// <param name="username">username for login</param>
        /// <param name="password">password</param>
        public ALMClient(string url, string username, string password, string domain, string project) 
        {
            client = new RestClient(url+"/qcbin/");
            client.Authenticator = new HttpBasicAuthenticator(username, password);
            client.CookieContainer = new System.Net.CookieContainer();
            Domain = domain;
            Project = project;
        }

The above, which is a snipped from my ALMClient class, shows how you can easily set the right authenticator in RestSharp for the username and password, and how you can have RestSharp remember authentication cookies via the cookie container.

I also had my class remember the project and domain for ALM, as they’re used in most of the REST calls.

The actual fetch of all defects was straightforward:

        public List<ALMItem> GetDefects()
        {
            // now try to read a defect
            var getDefects = new RestRequest("rest/domains/{domain}/projects/{project}/defects");
            getDefects.AddParameter("domain", Domain, ParameterType.UrlSegment);
            getDefects.AddParameter("project", Project, ParameterType.UrlSegment);

            // client is the RestClient that this ALMClient class has as a property
            IRestResponse response = client.Execute(getDefects);

            ///// snipped code for parsing response.Content, which has the XML back from ALM
        }

RestSharp has its own way of helping you parse the XML if you want to. In my example I didn’t use that. The fact that you can express the URL the same was as the documentation does, is a nice aspect of using RestSharp.

Then it all went wrong
Owing to a lack of the right documentation for both ALM and RestSharp, the next bit hurt. I wanted to update a field within an ALM defect. I could work out how to lock the defect for editing (not that I needed to) but the update was failing with an Unsupported Media Type error. It’s not hard to get it right. Here’s the snippet that made it work. It isn’t hard to get it right, but it’s very very very easy to get caught up in a method for getting it wrong.

        private void Update(string id, ALMItem changes)
        {
            // simple to plug all the parameters into the request URL
            var req = new RestRequest("/rest/domains/{domain}/projects/{project}/{Entity Type}/{Entity ID}");
            req.AddParameter("domain", Domain, ParameterType.UrlSegment);
            req.AddParameter("project", Project, ParameterType.UrlSegment);
            req.AddParameter("Entity Type", "defects", ParameterType.UrlSegment);
            req.AddParameter("Entity ID", id, ParameterType.UrlSegment);

            // the update is a put method
            req.Method = Method.PUT;

            // These two headers are VITAL VITAL and VITAL again to the avoidance of a 415 error
            // in this code, the first is probably redundant, but if you're not using RestSharp, this pattern should
            // be used for whatever technology you are using
            req.AddHeader("Content-Type", "application/xml");
            req.AddHeader("Accept", "application/xml");

            // The ConvertToFieldXML in this case returns the entity model showing which fields are to change
            // the secret to making RestSharp work is to put the content type as the name of the requestbody
            // parameter - in fact RestSharp replaces the content type with the name of this parameter, so get it wrong
            // and it's hello 415 error
            req.AddParameter("application/xml", ConvertToFieldXml(changes.Fields), ParameterType.RequestBody);

            var response = client.Execute(req);

            // snip - check the response is ok etc.
        }

Once it worked, I got my heart rate back down to normal.

Hope this helps anyone Googling for how to solve this annoying problem. If anyone wants me to release an ALM client open source, feel free to comment on this post.

Edit: by popular demand, this code is now available on GitHub – Open Source HP ALM client in C#

Advertisements

12 comments

  1. Hi Ashley,
    Thanks for this helpful information.
    I am about to start a project with the same agenda, and you made it sound much easier than I thought.

  2. Actually using this approach I am not able to authenticate. I am not connecting to ALM SaaS solution, its just a simple hosted solution. It failed with the following error:
    “Authentication failed. Browser based integrations – to login append ‘?login-form-required=y’ to the URL you tried to access.”

    So I tried creating a new Request and pointed it to “”/authentication-point/alm-authenticate” but that request.Execute call fails with the following error:
    “An Authentication object was not found in the SecurityContext”

    Here is the modified code:
    client = new RestClient(url + “/qcbin/”);
    client.Authenticator = new HttpBasicAuthenticator(username, password);
    client.CookieContainer = new System.Net.CookieContainer();
    Domain = domain;
    Project = project;
    var authRequest = new RestRequest(“authentication-point/alm-authenticate”);
    authRequest.AddHeader(“Authentication”, “Basic ” + Convert.ToBase64String(Encoding.Default.GetBytes(username + “:” + password)));
    IRestResponse response = client.Execute(authRequest);

    Notice that I have added the Authentication header. Any idea, what I am doing wrong?

  3. Hi,
    Have you tried to deploy this code as a Share Point Add-In.
    I am trying to build a report on my sharepoint site that will execute the REST API and get the details of defects, but i have not been able to successfully authenticate myself against the QC site.
    Any pointers?
    Thanks,
    Vimal

    • Hi Vimal

      Using both ALM AND SharePoint – you must really dislike yourself 😉

      Have a look at the latest version of the code on GitHub – I believe it DOES work with the QC site’s authentication. I didn’t write the most recent version – it was a pull request from another contributor.

      My recommendation is to try to get this working a bit at a time from a simple console app. If it’s not working, then check out which REST calls we’re making and use your favourite REST console plugin for your favourite browser to simulate the calls. The error message back from the server may give you more clues.

      If QC has moved on further and needs more pernickety API calling, then feel free to work it out and send me a pull request in GitHub with your findings.

      I’m not working in this area any more, but this page gets a lot of hits, so I’d be happy to share anyone’s findings on here to help people get this nuisance of a system to work for them.

      Best of luck!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s