using spring RestTemplate to add issues to github

I’m using github as a project management tool for a (non-work) project.  That’s been working well for being able to track things and have it close to the project artifacts.  Milestones have dates (to remember what was committed to) and issues correspond to tasks or follow up items within the milestone.  I reached a point where I needed to add the same three tasks to over 15 milestones.  (And possibly more in the future.)

It probably would have taken an hour to do this manually.  It’s a boring task so I imagine I’d have gotten distracted.  The boredom of even contemplating the task plus the possibility that I might have to do again make me realize I should spend a little longer and write code.  Which meant the prep time was a little over an hour but the time to run the program was less than a minute.

I worked in iterations because I wanted to learn early on if this was feasible (or a time sink.)

Iteration 1 – setup a project and call a github web service

I used Maven because I knew I’d need a couple libraries and dependencies.  In Eclipse, I created a new Maven project with

  • group id = jb
  • artifact = github-issue-creator

I then added one dependency.  I knew I’d need Spring Web because I wanted to use RestTemplate.  At this point, my pom.xml looks like

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jb</groupId>
  <artifactId>github-issue-creator</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.0.2.RELEASE</version>
    </dependency>
  </dependencies>
</project>

It was then time to actually call RestTemplate.  I quickly reviewed the docs to confirm my memory of the flow.  Then I wrote the simplest program I could that would call a Github web service.  I had learned from the GitHub API docs of the Octocat Hello World test repository.   I confirmed the issues query web service command in the GitHub docs and went to https://api.github.com/repos/octocat/Hello-World/issues in a browser.  Perfect – I get JSON back.

I then wrote the RestTemplate code using this URL.

package jb;

import org.springframework.web.client.*;

/**
 * View a public repo's issue via a web service on github to make sure can
 * connect. This particular repo is referenced int he documentation.
 *
 * @author jeanne
 *
 */
public class Trial1 {

	public static void main(String[] args) {
		String url = "https://api.github.com/repos/octocat/Hello-World/issues";

		RestTemplate template = new RestTemplate();

		// ignore result because just testing connectivity
		template.getForObject(url, Object[].class);
		System.out.println("success");
	}

}

You’ll notice I’m telling Spring to return an array of Objects.  I don’t need a more specific class since I’m ignoring the result.  It’s just a proof of concept – my real code will do a POST and own’t care about getting the data back.  I knew an array was needed because the JSON in the browser returned a list.  If you do need to deal with the returned values, look at this Jackson example with Spring and RestTemplate.

Iteration 2 – use my credentials to access a github web service

So far so good.  I have two more steps left to accomplish my goal.  I can either test passing credentials or try a POST.  I decided to test passing credentials first so I would be doing harmless GET operations during the testing.

I’ve only used basic authentication with RestTemplate once so I had to look up what to do.  This blog post was helpful showing how to use the Apache Commons Codec library to deal with credentials.

First, I had to add this dependency to my POM.

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.9</version>
</dependency>

This code obtains all the issues for myRepoName.  And no, that isn’t my GitHub password.  It isn’t my repository name for that matter.

package jb;

import org.apache.commons.codec.binary.*;
import org.springframework.http.*;
import org.springframework.web.client.*;

/**
 * Call a web service that uses authenticated user to test passing credentials
 *
 * @author jeanne
 *
 */
public class Trial2 {

	public static void main(String[] args) {
		String url = "https://api.github.com/repos/boyarsky/myRepoName/issues";

		String plainCreds = "boyarsky:notMyGitHubPassword";
		byte[] plainCredsBytes = plainCreds.getBytes();
		byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
		String base64Creds = new String(base64CredsBytes);

		HttpHeaders headers = new HttpHeaders();
		headers.add("Authorization", "Basic " + base64Creds);

		// ignore result because just testing connectivity
		HttpEntity<String> request = new HttpEntity<String>(headers);
		RestTemplate template = new RestTemplate();
		template.exchange(url, HttpMethod.GET, request, Object[].class);
		System.out.println("success");
	}

}

As you can see, I encode the bytes of my username/password combination. I then set that as the basic authentication header. I had to switch to “exchange” instead of “getForObject” as noted in that blog post for this to work.

Iteration 3 – actually creating an issue

There are only four differences between this and iteration 2 from a web services perspective.

  1. From the github create issue docs, I learned the POST URL is/repos/:owner/:repo/issues.
    private static final String URL = “https://api.github.com/repos/boyarsky/myRepoName/issues”;
  2. Use HttpMethod.POST instead of HttpMethod.GET.  I actually wasted some time with this step.  I forgot to change it so the program was running without error and returning  issues rather than setting them.
  3. Pass in the JSON for the values you want to set in the issue.
  4. Now I tell Spring to excpect an Object instead of an array because the JSON returned is no longer an array.
String json = "{\"title\": \"" + title + "\", \"assignee\": \""
				+ assignee + "\", \"milestone\": " + milestone + "}";

		// ignore result because just testing connectivity
		HttpEntity<String> request = new HttpEntity<String>(json, headers);
		RestTemplate template = new RestTemplate();
		template.exchange(URL, HttpMethod.POST, request, Object.class);

I tested this once and it worked. I then added the logic to get the proper values (it is formulaic) and let it fly.

Getting the milestone id was easy – it is in the URL: https://github.com/boyarsky/myRepoName/issues?milestone=4&state=open

Conclusion
While it would have been faster to add all the issues by hand, this was certainly more interesting and more fun. And the next time I need to add an issue to each milestone, it will be trivial. Thanks github for the wonderful documentation!

 

edit: if you need to do this for two factor authentication, see my updated post

windows 8.1 on virtualbox – a more permanent solution

In January, I installed the Windows 8.1 trial on VirtualBox.  The trial is good for 3 months.  I need a more permanent solution so I bought Windows 8.  I chose to buy a physical DVD so I have it in case I need to re-install it.  This turned out to be an “adventure” as Staples lost it the first time and there was some trouble picking it up.

This post assumes a tiny bit of experience with Windows 8.  If you don’t have any, see the trial post first.

Creating the VM from DVD – failed 64 bit attempt

The Windows 32 DVD didn’t stay in drive. 64 bit did though so I tried installing that.  In VirtualBox, you click the CD/DVD icon  and set storage to “host drive.”   After the Windows splash screen, I got “your PC needs to restart.”  So far so good.  Then I got

Error Code: 0x000000C4

Control command delete didn’t allow me to kill the virtual machine. I didn’t even have a cursor/pointer on my main Mac.  I had to press the power button on my Mac to get out of the virtual machine and close it.

I saw there were CPU requirements for 64 bit Windows and decided to try 32 bit again.  I’m doing anything powerful in Windows so I don’t need much memory.  The 32 bit DVD worked fine the second time.  A later found out there is an instruction you can give VirtualBox to load 64 bit Windows.

Creating the VM from DVD – successful 32 bit attempt

This went smoothly.  The gist of what went on:

  • Install now
  • Enter key from product key card
  • Choose custom: install windows only (advanced) – because not upgrading
  • Install Windows: “drive 0 unallocated space”
  • Started installing – showed % then blue windows splash for a few minutes then rebooted
  • Personalize screen – customize
  • Turn off shared info settings – don’t send data to MS
  • Prompted to sign into MS account.  I entered the wrong info (on purpose to see what would happen.)  I got prompted to create a local account.  Perfect.  Went with that.
  • Saw getting critical updates screen for a while since the DVD is older – the screen changes color as it downloads
  • Reboot again
  • Saw installing your apps – again the screen changes color as it goes

What I setup

  • view > switch to scale mode so don’t have to scroll up, down, left and right in Windows
  • Boot to desktop instead of Windows 8 screen.  This also allows shutting down by right clicking the windows icon at the bottom left.
  • I used solution 3 in this post to get rid of the black help box
  • in “tiles” went to command and pinned it to start menu and task bar so the DOS prompt is always handy.  I use it a lot in Windows to avoid having to use Windows while still being able to test Windows behavior in Java
  • Launched the control panel (which took a long time to load first time) so I could edit the PATH environment variable to add the path of javac

What I installed

  • JDK
  • The git app (because it is faster than setting up git manually) and login as me.  This gets the git shell quickly

Accessing the DVD drive

I wanted to run a DVD to see what it looked like and if there was anything beyond the PDF and Shockwave files.  (There wasn’t.)  To get VirtualBox to show me the d drive, I went to devices > host drive and chose my DVD drive.  Then the d drive turns into the real DVD drive instead of VirtualBox extensions.

As I was shutting down Virtual Box, I tried to eject the DVD from the Mac.  My Mac didn’t recognize the DVD.  I wound up having to wait until the Windows VM shut down (which took a while because Windows was busy patching.)  Then the Mac saw the DVD as if I had just stuck it in.

A nice aside

I learned you can drag a swf (Shockwave) file to the browser URL bar to open it.

Communicating with the VM

I’m still using my github system to communicate so repeating that from my trial post.

You can copy/paste from VirtualBox.  I decided to use github instead though as I already have the code I want to try on Windows in a private github repository.  Github has a Windows client.  I’ve never used it so decided to download to see what it is like.  It is only 41.2MB, but downloaded really slowly.   The app provided a UI to clone a repository in github which it automatically checks out into Documents/github/repo-name.  The UI is nice, but I wound up using the git bash shell provided with the application.  Which meant it really only saved me some setup.

Other options would have been Dropbox or just use the copy/paste functionality from VirtualBox.  (I really didn’t want to do that for whole files.)