I created a Notesin9.com video for the purpose of demonstrating how quickly and easily you can repeat one of the IBM example applications for yourself. In just over 10 minutes you can deploy a new Bluemix hosted Text To Speech example using the Watson Text To Speech service which is reeeeeally cool 🙂
In this article I will demonstrate how to integrate the Bluemix Watson Translation service into a functioning XPages application.
Bluemix Watson Translation Service
Following on from one from my previous posts on the subject I have been looking for a good workable example of using a Bluemix service within an XPages application. As I said before this is as much an exercise in me learning more about node.js and Bluemix as anything else – but I also love being able to share.
Based on the previous post about how to get the Watson Q and A service up and running it took me 12 minutes to get a working example of the Watson Translation service up and running on a Bluemix site. Just for disclaimers I have no idea how good the Watson service is and I am not advocating it – this is purely an exercise in being able to use a Bluemix service, more so that what it does.
So I followed the steps in my previous blog but applied them to the example post on the Watson Translation service. This was pretty simple and once again the only thing to change was the manifest.yml file to name the service and application correctly.
When you type into the TEXTAREA box (1) and then hit “Translate”(2) the result is displayed in the Output (3)
The page does a full refresh and the answer is displayed. The node.js code running behind the application is fairly self explanatory :
(looking at the code snippet below) we can see the values POST’d from the application are received on the node.js server in the app.POST
The posted field values are turned into a separate request to the Watson translation service
The Watson request (watson_req) is sent as a serversiude request to the Watson service
The results from that are sent back to the response object “watson_res”
The node.js response (res) back to the user is then set back to the browser using the jade template (index)
return res.render(‘index’,request_data);
// Handle the form POST containing the text and sid, reply with the language
app.post('/', function(req, res){
var request_data = {
'txt': req.body.text,
'sid': req.body.sid,
'rt':'text' // return type e.g. json, text or xml
};
var parts = url.parse(service_url);
// create the request options to POST our question to Watson
var options = { host: parts.hostname,
port: parts.port,
path: parts.pathname,
method: 'POST',
headers: {
'Content-Type' :'application/x-www-form-urlencoded', // only content type supported
'X-synctimeout' : '30',
'Authorization' : auth }
};
// Create a request to POST to Watson
var watson_req = https.request(options, function(result) {
result.setEncoding('utf-8');
var responseString = '';
result.on("data", function(chunk) {
responseString += chunk;
});
result.on('end', function() { //this is triggered when the response from Watson is completed
// add the response to the request so we can show the text and the response in the template
request_data.translation = responseString;
console.log('request',request_data);
return res.render('index',request_data); //<------response sent back to the web page
})
});
watson_req.on('error', function(e) {
return res.render('index', {'error': e.message})
});
// create the request to Watson
watson_req.write(querystring.stringify(request_data));
watson_req.end();
});
You can see that in action through firebug – here is the POST parameters
and here is the response – the new HTML containing the answer
Modifying the service
Here is the thought process which occurred to me:
Well that is cool but if I want to integrate this into an XPage then I will need to return the result as JSON and not a whole HTML page
But I do not want to break the example so how can I do that ?
I can use a new route – so if I POST at a different URL, the node.js server can be made to be smart enough to do something different
Ah…….and then I will have CORS issues because my application will be running at xomino.com and the bluemix app is not
Replicating the POST as code
So the first thing I did was try and replicate the code in jQuery (as I know best) so that I could mimic a POST event in ajax without using a form. In the following example you can see I added jQuery to my page (jQuerify plugin for firebug). I then simulated an AJAX POST to the translation service (emulating the form post).
The response is the HTML of the new page, so I know this is at least the right AJAX code.
But when I am on the copper.xomino.com site and I try and repeat the same thing, as expected CORS issues.
Ryan Baxter (who frankly has been a huge support and help in learning all this) said to me
The way you add a package to your Bluemix application is by updating the package.json file. This file contains all the npm modules that will be needed and what is even cooler is that these will be npm installed within your node server automagically on deployment
So then I need to add the cors enabled page to my application. But I did not want to change the core example on the site. So what I did was create a new “route” within the node.js application and told it to do something different….
Originally we had this within the example
// Handle the form POST containing the text and sid, reply with the language
//the route in this case is "/" - so basically the root of the application
app.post('/', function(req, res){
//stuff
}
and to create a new route we just add a different app.post
// Handle the form POST containing the text and sid, reply with the language
var cors = require('cors'); //add the cors module code to the application
app.use(cors());
var corsOptions = {
origin: 'http://copper.xomino.com' //allow copper to be a site which can work with the watson site
};
//note the new cors(corsOptions) parameter in the app.post function
app.post('/xpages', cors(corsOptions), function(req, res){ //when a user posts to site.com/xpages do this code instead of the default '/'
//stuff
}
What I then changed was the response code – this was the original code responding with text and rendering using the jade index.jade template
//......
result.on('end', function() {
// add the response to the request so we can show the text and the response in the template
request_data.translation = responseString;
console.log('request',request_data);
return res.render('index',request_data); //render the response using the index,jade template and passing the request_data
})
Instead of res.render I used res.json to send the request_data object back to the browser directly.
//......
result.on('end', function() {
// add the response to the request so we can show the text and the response in the template
request_data.translation = responseString;
console.log('request',request_data);
return res.json(request_data);
})
So in this way I left the default page alone (“/” server root) – the example still works – but I added a new route (“/xpages”) so that my XPage application could POST at it.
I committed the code and pushed back up to Jazz Hub, restarting the service as we went…….and low and behold success…..
As you can see from the above image, I am at the copper.xomino.com website. Using the slightly modified code to now post at
I get a response from the Bluemix website with the JSON from the Translation service. You can see from the headers the CORS header is added for copper.xomino.com
Integrating the Watson Transation service into an XPage application
Once I have the working ajax code it is very simple to add it to an XPage and just pick up the field values on the fly….. I cheated to some extent and copied and pasted the HTML from the original example into my XPage….hey why not this is an example after all…
I added a couple of classes to the example so that I could easily pick out the field values…
And then the jQuery code to add to the application. In this code I
Select the .translate class (the translate button)
In the click event I get the data from the form
the .sid select box for the language translation
The text value from the textbox (.theOriginal) field
I then submit to the the translation service as before
In the .done() of the AJAX request, the incoming msg.translation is then added to the .theTranslation field
The demo
As you can see from the video – I am able to:
type in any value
send it to the bluemix service
receive the answer and display it on the screen
save the original and translated values as notes documents within my XPages application
Firebug shows the fact that the POSTS are going to http://xominowatsontranslate.mybluemix.net/xpages
Conclusion
I think this is a great example of using a “service” on another website to be able to enhance the functionality in your own XPages application. If IBM are successful in their investigation to put Domino inside of Bluemix, this capability will all happen behind the scenes and could be easily written as one true application……bring it on… 🙂
PS
This exercise was particularly gratifying because it feels like a culmination of all the work I have done in XPages over the last 3 years.
Without the jQuery and Firebug work I did at the start of XPages development, prototyping this would have been much more tedious.
Learning XPages has forced me to have a better understanding of AJAX and jQuery
If it were not for Angular and the “Write once run anywhere” I did for MWLUG, I would not have learned about CORS.
If it was not for the Angular work I would not understand “routes” within an application
…..and now I am learning about node.js and Bluemix PaaS