Live, fluid RESTing with the Tradeshift API and Groovy (part 1)

The advantage of having a REST API is that many actions can be readily performed with just a web-browser or a command-line HTTP tool like wget or curl. The authentication with OAuth complicates this somewhat, but that hurdle can be overcome. In this blog post, I'll give some tips on getting acquainted with the REST language that the Tradeshift API speaks.
This is part 1 of a 2-part series. Also check out part 2 on working with documents.

Prerequisites

Groovy is a dynamic scripting language for the Java platform. It comes with a neat interpreter shell, that will evaluate and run commands real-time. Because of the vast amount of libraries available to Groovy, we'll be using the Groovy shell with HTTPBuilder for our examples.Make sure that Groovy is installed and in your PATH.

In order to get logging of all your HTTP requests, some files must be available in the directory you run groovysh from. Please do the following:
  • Save a file org.apache.commons.logging.LogFactory to your working directory with this contents
  • Save a file log4j.xml to your working directory with this contents
  • Save log4j.jar to ${GROOVY_HOME}/lib
We'll configure the Groovy shell to execute a couple of commands on start-up, so we don't have to type them ourselves every time. Create a file tradeshift_setup.txt with the following code in it (put your consumer key and consumer secret at the appropriate places):
groovy.grape.Grape.grab(group:'org.codehaus.groovy.modules.http-builder', module:'http-builder', version:'0.5.1' )
groovy.grape.Grape.grab(group:'oauth.signpost', module:'signpost-core', version:'1.2.1.1')
groovy.grape.Grape.grab(group:'oauth.signpost', module:'signpost-commonshttp4', version:'1.2.1.1')
groovy.grape.Grape.grab(group:'oauth.signpost', module:'oauth-signpost', version:'1.2.1.1')
import groovyx.net.http.*
ts = new RESTClient( 'https://api-sandbox.tradeshift.com/tradeshift/rest/external/' )
ts.contentType = ContentType.JSON
ts.auth.oauth 'your-consumerkey-here', 'your-secret-here', '', ''
Now, type groovysh to fire up the Groovy shell. You'll be greeted with the prompt, after which we'll instruct groovy to load and execute our file. Type only the text in bold. Do give it a moment, as there'll be a set of JAR files downloaded.
groovy:000> load tradeshift_setup.txt
===> null
===> null
===> null
===> null
===> [import groovyx.net.http.*]
===> groovyx.net.http.RESTClient@4d2bbe6b
===> application/json
We now have a variable ts of type HTTPBuilder, which is set up to:
  • Automatically use JSON for all requests
  • Automatically sign requests with our OAuth credentials
  • Automatically prefix all requests with our Sandbox API base URL

Getting the list of company accounts

Let's start by asking Tradeshift which accounts our consumer key has access to. We send a GET request to the consumer/accounts path on our API.
groovy:000> ts.get(path: 'consumer/accounts').data
May 27, 2011 3:43:44 PM groovyx.net.http.ParserRegistry getCharset
WARNING: Could not find charset in response
===> {"CompanyAccounts":["e854db42-eef6-4388-b050-fbddc41eea18","87a7ab22-c03f-4781-9299-b73a60dad18a"]}
(please ignore the warning, it's a known issue that will be fixed in a newer HTTPBuilder version; the rest of this blog post has the warning removed for legibility).
Check out the response: a nice JSON structure with all the company account IDs that your consumer key has access to (2 in case of the example).

Let's put the first company account ID into a variable cid, so we can use it further.
groovy:000>cid = ts.get(path: 'consumer/accounts').data.CompanyAccounts[0]
===> e854db42-eef6-4388-b050-fbddc41eea18

Getting account information

From now on, we'll be creating requests for a particular company account. Because of that, we need to set a X-Tradeshift-TenantId header with every request. This can be done globally with a simple statement.
groovy:000>ts.headers = ['X-Tradeshift-TenantId': cid]
===> {X-Tradeshift-TenantId=e854db42-eef6-4388-b050-fbddc41eea18}
Now let's find out which account we're actually dealing with. There's an API call for that.
groovy:000>ts.get(path: 'account/info').data
===> {"CompanyName":"Jans Test Company",
"Country":"DK",
"CompanyAccountId":"e854db42-eef6-4388-b050-fbddc41eea18",
"Description":"This is a test company.",
"Identifiers":[{"scheme":"DK:CVR","value":"12345655"}],
"AddressLines":[{"scheme":"zip","value":"2330"},
{"scheme":"street","value":"Norregade"},
{"scheme":"buildingnumber","value":"36B"},
{"scheme":"city","value":"Copenhagen"}],
"AcceptingDocumentProfiles":[]}

Creating a connection

Let's try creating a connection to another Tradeshift account. We'll search for a Tradeshift company by name. I'll use one of my test account's name as an example.
groovy:000>ts.get(path: 'network/suggestnew', query:[q:'Jans Screencast Company']).data
===> {"CompanyAccount":[
{"CompanyName":"Jans Screencast Company",
"Country":"DK",
"CompanyAccountId":"62582acd-c4ef-4c82-af8e-971e9a1e785a",
"Identifiers":[],"AddressLines":[],"AcceptingDocumentProfiles":[]}]}
That looks just fine! Let's save this company's companyAccountId in a variable cid2.
groovy:000>cid2 = ts.get(path: 'network/suggestnew', query:[q:'Jans Screencast Company']).data.CompanyAccountId[0].CompanyAccountId
===> 62582acd-c4ef-4c82-af8e-971e9a1e785a
Just to be sure, let's ask the system if we're already connected to the companyAccountId saved in cid2.
groovy:000>ts.get(path: 'network/connections', query: [companyAccountId: cid2]).data
===> {"numPages":null,"pageId":null,"itemsPerPage":null,"itemCount":0,"Connection":[]}
The result shows an empty connection list, i.e. no existing connection is linking to cid2.

We're now ready to execute a PUT request to actually create the connection. When creating connections over the API, we generate a random UUID to save the new connection under.
groovy:000> conn = ts.put(path: "network/connections/${UUID.randomUUID()}", body: [ConnectionType:'TradeshiftConnection', CompanyAccountId: cid2])
===> groovyx.net.http.HttpResponseDecorator@591ce4fe
groovy:000> conn.statusLine
===> HTTP/1.1 201 Created
groovy:000> conn.headers.Location
===> http://api-sandbox.tradeshift.com/tradeshift/rest/external/network/connections/341d3031-409a-48e5-97c5-fb2bbf1e38a6
groovy:000> ts.get(path: "network/connections/341d3031-409a-48e5-97c5-fb2bbf1e38a6").data
===> {"ConnectionType":"TradeshiftConnection",
"ConnectionId":"341d3031-409a-48e5-97c5-fb2bbf1e38a6",
"Properties":{},
"CompanyAccountId":"62582acd-c4ef-4c82-af8e-971e9a1e785a",
"State":"REQUESTED"}
Note how the PUT request yields in a 201 response, having its Location HTTP response header set to the URL of the new connection. Afterwards, you can GET the new connection to check the result.

This call has exactly the same request as the "Add this connection to my network" link on the web: the receiving company will get an e-mail with your connection request.

More reading

Continue with part 2 to read about how to work with documents!