Earlier today IBM announced a new partnership with Box. Box is a cloud storage provider much like Dropbox, OneDrive, and other services, but also provides some pretty cool workflow features as well. While it is still early, you'll soon see some interesting collaborations between IBM and Box. I decided to see how easy it would be to integrate Box into a hybrid mobile application using both Ionic and IBM MobileFirst. This is just a simple proof of concept, but it demonstrates how you can use all these different pieces together in one application.
Before diving into the code, let's look at a few screen shots of the application in action. On loading the application, you will see a button prompting you to login with Box.
Clicking this button will begin the authentication process. You need to have an account with Box.com of course.
After logging in, you have to allow the application access to your data:
After you've allowed the app to access your Box account, you can then begin working with your data. For my demo, I simply let the app upload images from the device to the Box account. (You could modify the code to allow new pictures to be taken with the camera too. Since I was testing with the simulator, I limited it to existing pictures.)
After selecting the image, I display a thumbnail and then upload it.
If you have your Box account open in a browser (they have a desktop client as well), you can see the image appear.
And that's it. The Box API allows for full access to Box content, more then just uploading. You can even use a special View API to display renditions of Box content. It is a pretty great API and I encourage you to read more about it on their developer site. So - let's talk about the code.
In order to handle the OAuth, I used a great library from Nic Raboy called ng-cordova-oauth. It provides OAuth support for a butt load of different services, with Box being one of them. How simple is it? Here is the code behind the button you saw in the screen shot above.
$scope.doAuth = function() {
Logger.log("Beginning to auth against Box");
$cordovaOauth.box(clientId, clientSecret,state).then(function(result) {
Logger.log("Successful log to Box");
token = result.access_token;
$scope.noAuth = false;
}, function(error) {
console.log('Error',error);
});
}
Yep, that's it. Then using the API itself is rather simple. I first wrote some code to just test hitting the API, in my case, requesting folders at the root of the account. Here is how I did it using Angular's $http service:
$scope.getFolders = function() {
console.log("attempting to get folders");
$http.defaults.headers.common.Authorization = 'Bearer '+token;
$http.get("https://api.box.com/2.0/folders/0").success(function(data, status, headers, config) {
console.log('succcess');
console.dir(data);
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
console.log('error');
console.dir(arguments);
});
}
The only real interesting part here is setting the OAuth token in the header. You can see that is one simple line before the get. Technically I only need to do this once and should set it after logging in - but as I said - I wrote this just as a test of the API. File uploads were a bit more complex. Instead of using $http, I used Cordova's FileTransfer plugin. This let me upload the image file selected by the user. Here's the entirety of the operation including the camera selection and upload.
$scope.doPicture = function() {
navigator.camera.getPicture(function(uri) {
$scope.selectedImage = uri;
$scope.status.message = "Uploading bits to Box...";
$scope.$apply();
Logger.log("Going to send a file to Box");
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
$scope.status.message = "Sent to box!";
Logger.log("Sent a file to box!");
$scope.$apply();
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
Logger.log("Failed to send to Box");
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = uri.substr(uri.lastIndexOf('/') + 1);
options.mimeType = "image/jpeg";
var headers={'Authorization':'Bearer '+token};
options.headers = headers;
var params = {};
params.attributes = '{"name":"'+options.fileName+'", "parent":{"id":"0"}}';
options.params = params;
var ft = new FileTransfer();
ft.upload(uri, encodeURI("https://upload.box.com/api/2.0/files/content"), win, fail, options);
}, function(err) {
console.log("Camera error", err);
}, {
quality:25,
destinationType:Camera.DestinationType.FILE_URI,
sourceType:Camera.PictureSourceType.PHOTOLIBRARY
});
}
And that's it. I then mixed in MobileFirst - specifically the logging service. I blogged about this a few months back (). It is a rather simple API I can make available via a service in my app:
}).factory('Logger', function() {
var logger = WL.Logger.create({autoSendLogs:true});
return {
log:function(s) {
logger.log('log', s);
console.log(s);
}
}
})
Then when I inject Logger
into my controllers, I can just do Logger.log("some message")
. There were a few examples of that above. Then when my application is out in the wild, I can look at my analytics in my MobileFirst server:
Want to see all of the code? You can see all the code here: https://github.com/cfjedimaster/Cordova-Examples/tree/master/boxdemo_mfp. Note that I ran into two small issues with Nic's OAuth plugin. The first is that after authenticating with Box, you will see a 404 error temporarily. Nic already has a fix for this in the dev branch of his library. It is harmless and can be ignored. The second issue was specifically involving his code running in MobileFirst. Plugins act a bit differently there and his code to check for the InAppBrowser didn't work. (To be clear, that one is absolutely not his fault.) The workaround was a quick mod to his code and is in the GitHub repo. You can see a video of the app in action below.