Working Example of oAuth and XERO

Hi,

does anyone have a few snippets of code for authenticating with XERO accounts with or without the oAuth plugin.

Just doing some XMAS research whilst I have some downtime. I see some people have tried this, but I think the code snippets available appear to be after the initial requesting of authentication.

It would be a great shortcut if anyone has a couple of routines to do the authentication and then a basic call to XERO.

Thanks in advance

David

Hi David,

I use the oAuth plugin Overview - OAuth Plugin - ServoyForge (but happy to be shown another way :-)

I’ve built methods for GET, POST and PUT, but you could collapse these into a generic method.
I’ll post some examples of how I call these shortly.

function OAuthXeroGET(vLive, vEndpoint, vAttributeName, vAccept) {
	try 
	{
		if (!vAccept) // default to json for backwards compatability.
			vAccept = "json"
				
		vEndpoint = globals.UrlEncode(vEndpoint);
		plugins.oauth // initialise
		var oauth_api = OAuthAPI.XERO 
		var client_id = vLive?gvClientId:gvClientIdDemo;
		var secret = vLive?gvSecret:gvSecretDemo	
		var resource_url = gvXeroURL + vEndpoint;
		var method = Methods.GET;
		var request = plugins.oauth.createOAuthRequest(oauth_api, client_id, secret, method);
		if (!request)
			throw "No request object returned"
		request.setResourceUrl(resource_url);
		request.setHeader("Accept","application/" + vAccept);
		request.execute();
		var response = request.getResponse();
		if (!response)
			return null

		if (vAccept == "xml" ||vAccept == "pdf")
		{
			var vContent = request.getResponse().getContent();
			return vContent
		}
		else if (vAccept == "json")
		{
			var vAttribute = response.getAttributeValue(vAttributeName)
			var json = eval('(' + vAttribute + ')')
			return json
		}
		else
			throw "OAuthXeroGET Unsupported content type " + vAccept
	}
	catch(e) 
	{
		var vMsg = "OAuthXeroGET Error - " + e + "\nvEndpoint = " + vEndpoint + "\nvAttributeName = " + vAttributeName
		//...	
	} 
}
function OAuthXeroPOST(vLive, vEndpoint, vAttributeName, vXML, vAccept) {
	try 
	{
		if (!vAccept) // XML in POST body, but want response as json for easy parsing.
			vAccept = "json";
		plugins.oauth // initialise
		var oauth_api = OAuthAPI.XERO 
		var client_id = vLive?gvClientId:gvClientIdDemo;
		var secret = vLive?gvSecret:gvSecretDemo	
		var resource_url = gvXeroURL + vEndpoint;
		var method = Methods.POST;
		var request = plugins.oauth.createOAuthRequest(oauth_api, client_id, secret, method);
		request.setPostData("xml",vXML);
		request.setResourceUrl(resource_url);
		request.setHeader("Accept","application/" + vAccept);
		request.execute();
		var response = request.getResponse();
		if (!response)
			return null
		if (vAccept == "xml")
		{
			var vContent = request.getResponse().getContent();
			return vContent
		}
		else if (vAccept == "json")
		{
			var vAttribute = response.getAttributeValue(vAttributeName)
			var json = eval('(' + vAttribute + ')')
			return json
		}
		else
			throw "OAuthXeroPOST Unsupported content type " + vAccept
	}
	catch(e) 
	{
		var vMsg = "OAuthXeroPOST Error - " + e + "\nvEndpoint = " + vEndpoint + "\nvAttributeName = " + vAttributeName + "\nvXML = " + vXML
		//...	
	} 
}
function OAuthXeroPUT(vLive, vEndpoint, vAttributeName, vXML, vAccept) {
	try 
	{
		if (!vAccept)
			vAccept = "json"
		plugins.oauth // initialise
		var oauth_api = OAuthAPI.XERO 
		var client_id = vLive?gvClientId:gvClientIdDemo;
		var secret = vLive?gvSecret:gvSecretDemo	
		var resource_url = gvXeroURL + vEndpoint;
		var method = Methods.PUT;
		var request = plugins.oauth.createOAuthRequest(oauth_api, client_id, secret, method);
		request.setPostData("xml",vXML);
		request.setResourceUrl(resource_url);
		request.setHeader("Accept","application/" + vAccept);		
		request.execute();
		var response = request.getResponse();
		if (!response)
			return null
		if (vAccept == "xml")
		{
			var vContent = request.getResponse().getContent();
			return vContent
		}
		else if (vAccept == "json")
		{
			var vAttribute = response.getAttributeValue(vAttributeName)
			var json = eval('(' + vAttribute + ')')
			return json
		}
		else
			throw "OAuthXeroPUT Unsupported content type " + vAccept
	}
	catch (e) 
	{
		var vMsg = "OAuthXeroPUT Error - " + e + "\nvEndpoint = " + vEndpoint + "\nvAttributeName = " + vAttributeName + "\nvXML = " + vXML
		//...	
	} 
}

Great stuff Antonio. Thank you very much. Just one question. Does the OAuth plugin works in SmartClient?
Thanks again

Yes, I just tried the plugin in SC in Dev environment, it works fine.
Here’s an example of how I use the above code.

function GetXeroInvoiceByID(vID) {
	// ID can be invoice number or UUID
	if (!vID) 
		return
	var vLive = true		
	var vEndpoint = 'Invoices/' + vID
	var vAttributeName = "Invoices" // the part of the XML we want to get back
	var vResponse = globals.OAuthXeroGET(vLive, vEndpoint, vAttributeName, "xml")
	return vResponse // xml invoice object
}

Thanks Antonio, we’ll give a go to your samples

For some years I’ve been using the oAuth plugin (date stamped 08 March 2015) with the examples in this thread to connect with Xero. It’s been working well with Java 6 and Servoy 5.2.16
I recently had to upgrade to Java 7 for TLS compatibility, and now I get the following error (full error log below).

Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

I tried the latest (Servoy 8) version with my Servoy 5.2.16 solution, but it doesn’t appear under plugins in the Eclipse solution explorer.

Grateful for any tips on getting an oAuth connection working again, either using the plugin or with some other method. I’ve not in a position to change the solution from Servoy 5.2.16 any time soon.

Tony

org.scribe.exceptions.OAuthConnectionException: There was a problem while creating a connection to the remote service.
at org.scribe.model.Request.send(Request.java:69)
at org.scribe.model.Request.send(Request.java:75)
at com.donay.oauth.responses.ResponseFactory.create(ResponseFactory.java:23)
at com.donay.oauth.requests.JSOAuthRequest2Legged.js_execute(JSOAuthRequest2Legged.java:82)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:179)
at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:353)
at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:3666)
at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:2680)
at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:166)
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:387)
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3135)
at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:165)
at com.servoy.j2db.scripting.ScriptEngine.executeFunction(ScriptEngine.java:476)
at com.servoy.j2db.FormController.executeFunction(FormController.java:4003)
at com.servoy.j2db.FormController.executeFunction(FormController.java:3891)
at com.servoy.j2db.FormController.executeFunction(FormController.java:3813)
at com.servoy.j2db.FormController$ScriptExecuter.executeFunction(FormController.java:3668)
at com.servoy.j2db.ui.BaseEventExecutor.fireEventCommand(BaseEventExecutor.java:272)
at com.servoy.j2db.ui.BaseEventExecutor.fireEventCommand(BaseEventExecutor.java:242)
at com.servoy.j2db.ui.BaseEventExecutor.fireChangeCommand(BaseEventExecutor.java:173)
at com.servoy.j2db.server.headlessclient.dataui.WebDataComboBox$3.execute(WebDataComboBox.java:446)
at com.servoy.j2db.server.headlessclient.ServoyForm.processDelayedActions(ServoyForm.java:84)
at com.servoy.j2db.server.headlessclient.dataui.WebEventExecutor.onEvent(WebEventExecutor.java:352)
at com.servoy.j2db.server.headlessclient.dataui.WebEventExecutor.onEvent(WebEventExecutor.java:341)
at com.servoy.j2db.server.headlessclient.dataui.ServoyFormComponentUpdatingBehavior.onUpdate(ServoyFormComponentUpdatingBehavior.java:65)
at org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior.onEvent(AjaxFormComponentUpdatingBehavior.java:158)
at org.apache.wicket.ajax.AjaxEventBehavior.respond(AjaxEventBehavior.java:177)
at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.onRequest(AbstractDefaultAjaxBehavior.java:312)
at org.apache.wicket.request.target.component.listener.BehaviorRequestTarget.processEvents(BehaviorRequestTarget.java:157)
at org.apache.wicket.request.AbstractRequestCycleProcessor.processEvents(AbstractRequestCycleProcessor.java:92)
at org.apache.wicket.RequestCycle.processEventsAndRespond(RequestCycle.java:1279)
at org.apache.wicket.RequestCycle.step(RequestCycle.java:1358)
at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1465)
at org.apache.wicket.RequestCycle.request(RequestCycle.java:545)
at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:486)
at com.servoy.j2db.server.servlets.Zl.doGet(Zl.java:7)
at org.apache.wicket.protocol.http.WicketServlet.doPost(WicketServlet.java:160)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:567)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at sun.security.ssl.Handshaker.activate(Handshaker.java:470)
at sun.security.ssl.SSLSocketImpl.kickstartHandshake(SSLSocketImpl.java:1438)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1308)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
at org.scribe.model.Response.(Response.java:28)
at org.scribe.model.Request.doSend(Request.java:115)
at org.scribe.model.Request.send(Request.java:65)
… 55 more

I also note a small change in syntax in the oAuth plugin examples on servoyforge -

Old code

		plugins.oauth // initialise
		var oauth_api = OAuthAPI.XERO

New code

	var oauth_api = plugins.oauth.OAuthAPI.XERO;

Is that correct?

That old code seems like it would never have worked. You can set a variable and do something similar, but this is essentially the same as the new code you have. The new code seems like it would be correct.

var oauth = plugins.oauth;
var oauth_api = oauth.OAuthAPI.XERO;

Same as:

var oauth_api = plugins.oauth.OAuthAPI.XERO; 

thanks Scott, the new syntax does look better, but the old code worked fine, and curiously didn’t work with out the initialize line.