Monday, April 13, 2009

Using Rally Java Web Service API

 There are a number of developer tools that can be used from Rally ranging from integrating Rally to a different application(s) or just plainly extracting data from Rally. This link https://rally1-wiki.rallydev.com/display/Word/Developer+Tools provides necessary rally developer documentation.

Web Service API

Since Rally supports several implementation of their WS API this will mainly focus on SOAP in Java. You can find on this link https://rally1.rallydev.com/slm/doc/webservice/ the other implementations. The only thing I don't like about this API implementation is that you always have to pass the object reference through the wire to get the values of the object. The WS calls are so fine-grained that the number of objects queried is directly proportional to the number of round-trip calls. The sample usage of the SOAP in Java implementation is shown below in sequence.

Assuming our target of interest is to extract a Story from Rally. The Story in Rally is actually map to a SOAP object called HierarchicalRequirement. Always remember to use the read() method of RallyService? object to grab the physical object from Rally.

  1. Grab the WSDL from the current version of your Rally application which might have the form https://rallyx.rallydev.com/slm/webservice/x.xx/meta/34343483/rally.wsdl.
  2. Generate the Java code from the given wsdl file. Their will be 3 packages generated - com.rallydev.webservice.domain and com.rallydev.webservice.service.
    • com.rallydev.webservice.domain contains all SOAP objects that represent the data in Rally.
    • com.rallydev.webservice.service contains the web service interface.
  3.  Acquire connection from the web service endpoint and grab available Workspaces.
     URL url = new URL("https://rally1.rallydev.com/slm/webservice/1.10/RallyService");
    RallyService service = (new RallyServiceServiceLocator()).getRallyService(url);

    Stub stub = (Stub)service;
    stub.setUsername(rally_username);
    stub.setPassword(rally_password);
    stub.setMaintainSession(true);
    Subscription subscription = (Subscription)service.getCurrentSubscription();
    Workspace[] workspaces = subscription.getWorkspaces();
    if(workspaces==null || workspaces.length==0){
    errorBuf.append("The login credentials doesn't have any subscription or there are " +
    "no Workspaces configured from Rally.");
    writeToFile(serviceBean, errorBuf.toString());
    return null;
    }
  4. If the target workspace is "IT: the next generation" then loop through the workspaces that matches that workspace.
    Workspace workspace = null;
    for(int i=0; i<workspaces.length;i++){
    WSObject wsObject = (WSObject)service.read(workspaces[i]);
    workspace = (Workspace)wsObject;
    String workspaceName = workspace.getName();
    if(workspaceName.equalsIgnoreCase("IT: the next generation" )){
    break;
    }
    }
  5. Submit query and get results (DomainObject?[]). The serviceBean.getQuery() is a name/value pair which might be of the form Release.Name= "Test Release For TWiki" AND ScheduleState? = "Completed". Process each DomainObject?.
    QueryResult queryResult = service.query(workspace, "HierarchicalRequirement", serviceBean.getQuery(), "", false, 1, 100);
    if(queryResult.getErrors().length>0){
    for(int i=0; i<queryResult.getErrors().length;i++){
    errorBuf.append("ERROR: ");
    errorBuf.append(queryResult.getErrors()[i]);
    errorBuf.append("\n");
    }
    writeToFile(serviceBean, errorBuf.toString());
    return null;
    }
    DomainObject[] domainObjects = queryResult.getResults();
    if(domainObjects!=null && domainObjects.length>0){
    List releaseNotesBeanList = new ArrayList();
    TwikiBean twikiBean = new TwikiBean();
    Map packageStoryMap = new HashMap();
       for(int i=0;i<domainObjects.length;i++){
    HierarchicalRequirement story = (HierarchicalRequirement)service.read(domainObjects[i]);
    Release release = (Release)service.read(story.getRelease());
    DateFormat dateFormat = DateFormat.getInstance();
    String releaseDate = dateFormat.format(release.getReleaseDate().getTime());
          if(story.getAttachments()==null || story.getAttachments().length==0){
    errorBuf.append(NO_ATTACHMENT).append("\n");
    }
    if(story.get_package()==null || story.get_package().equals("")){
    errorBuf.append(NO_PACKAGE_NAME).append("\n");
    }
    if(release==null || release.getName()==null
    || release.getName().equals("")){
    errorBuf.append(NO_RELEASE_NAME).append("\n");
    }
          if(errorBuf.length()>0){
    errorBuf.insert(0, "ERROR: User Story ID: " + story.getFormattedID() + "\n");
    writeToFile(serviceBean, errorBuf.toString());
    continue;
    }
          ReleaseNotesBean releaseNotesBean = new ReleaseNotesBean();
    releaseNotesBean.setPackageName(story.get_package());
    releaseNotesBean.setUserStoryId(story.getFormattedID());
    releaseNotesBean.setUserStoryName(story.getName());
    releaseNotesBean.setReleaseDate(releaseDate);
    releaseNotesBean.setReleaseName(release.getName());
          Attachment[] attachments = story.getAttachments();
    Attachment attachment = attachments[0];
    attachment = (Attachment)service.read(attachment);
          AttachmentContent attachmentContent = (AttachmentContent)service.read(attachment.getContent());
    byte[] content = attachmentContent.getContent();
          String twikiTopic = new String(content);
    releaseNotesBean.setTwikiTopic(twikiTopic);
          releaseNotesBeanList.add(releaseNotesBean);
    prepareTopics(releaseNotesBean, packageStoryMap);
    }
       twikiBean.setPackageStoryMap(packageStoryMap);
    twikiBean.setReleaseNotesBeanList(releaseNotesBeanList);
    return twikiBean;
    }

8 comments:

  1. I'm trying to do this with Iteration Burndown's in rally and for some reason when I query the CumulativeFlowData it doesn't compile. any tips?

    ReplyDelete
  2. This is the perfect website for anybody who wants to find out about
    this topic. You understand a whole lot its almost tough to argue with you (not that I actually
    will need to…HaHa). You certainly put a fresh spin on a subject which
    has been discussed for decades. Excellent stuff, just great!


    Feel free to visit my web page :: Www.playgame3.com

    ReplyDelete
  3. Howdy! Do you use Twitter? I'd like to follow you if that would be ok. I'm definitely enjoying your blog and look forward to new updates.


    Here is my site :: www.christkorner.com

    ReplyDelete
  4. I am extremely impressed together with your
    writing skills and also with the layout in your weblog. Is this a paid
    theme or did you modify it your self? Either way stay up
    the excellent quality writing, it's uncommon to look a nice weblog like this one nowadays..

    My web site ... Author's
    external home page...

    ReplyDelete
  5. I constantly spent my half an hour to read this website's posts daily along with a mug of coffee.

    Here is my web-site; remove hair

    ReplyDelete
  6. Generally we don't find out article on blogs, but I wish to assert that this write-up very forced me to confirm out and choose therefore! Your authorship essence has been surprised me personally. Say thanks a ton, really sweet article.

    Here is my web-site ... Author's external home page.
    ..

    ReplyDelete
  7. Hello, Neat post. There is an issue together with your web site in web explorer, may test this?
    IE nonetheless is the market leader and a good element of
    folks will pass over your magnificent writing due to this problem.


    my website :: http://www.sbwire.com/press-releases/somanabolic-muscle-maximizer-review-important-information-factors-in-nutrition-to-build-lean-muscles-247681.htm

    ReplyDelete