X

Best Practices from Oracle Development's A‑Team

Integrating Oracle Mobile Cloud Service with Oracle IoT Cloud Service

Introduction

The Oracle Internet of Things Cloud Service (IoTCS) allows you to set up an integration with the Oracle Mobile Cloud Service (MCS) to process and analyze the data received from your mobile devices that are connected to IoTCS. This article explains how you can create a custom API in MCS that exposes this device data so you can easily build a mobile or web app on top of it.

Main Article

This article discusses two techniques to store the device data in MCS:

  • Using the MCS storage service
  • Using the MCS database service

While only the first technique is documented in the IoTCS Developers Guide, you will see that the second technique is actually easier to implement and also results in a better performance.

Using the MCS Storage Service

This technique is partially explained in chapter Integrating Oracle Mobile Cloud Service with Oracle IoT Cloud Service of the IoTCS developer's guide. Please read this chapter first if you are new to IotCS and MCS and want to learn how to set up an MCS integration in IotCS and a mobile backend and storage collection in MCS. The key point is that the URL property in the Connection tab of your MCS integration points to the MCS storage API POST endpoint:

IotStorageGOED

IotCS will call this endpoint to add a JSON file with the device message to the specified MCS storage collection (named "IoT" in the above example). If you go to the storage collection in the MCS web user interface, you can see how the collection is populated with these JSON files:

StorageContent

The content of each JSON file will look similar to this (actual payload data attributes will vary based on device and message type):

[   {     "id": "6748cf3a-a2cc-467e-a823-b1761bcb3b3f",     "clientId": "e6ce7146-e326-4628-bebd-fda7a69e07f4",     "source": "AAAAAAQ4EO8A-AM",     "destination": "",     "priority": "HIGHEST",     "reliability": "BEST_EFFORT",     "eventTime": 1468583693714,     "sender": "",     "type": "ALERT",     "properties": {},     "direction": "FROM_DEVICE",     "receivedTime": 1468583685315,     "sentTime": 1468583689020,     "payload": {       "format": "urn:com:oracle:iot:device:hvac:alert:unabletoconnect",       "description": "Unable to connect alert",       "severity": "SIGNIFICANT",       "data": {         "unable_to_connect": true       }     }   } ]

So far so good, we have all the messages stored in MCS. The next step is to create a custom API to expose these device messages and optionally apply some filtering, aggregations and/or transformations as needed by the client app that you want to build on top of this API.

If you are new to building a custom API with MCS, you might want to check out the article series Creating a Mobile-Optimized API Using MCS.

We create a simple iot custom API, with one GET endpoint /messages:

IOT ENDPOINT

After we downloaded the implementation scaffold we are ready to implement the endpoint. Now things become less trivial: we need to loop over all the files in our IoT storage collection, retrieve the content of each file, and merge that together in one JSON array of messages that we return as response. (In reality you might wat to apply some additional filters, aggregations and transformations, but that is beyond the scope of this article).

Let's start with checking out the section Calling MCS APIs from Custom Code in the MCS developer's guide. After some general info that applies to all MCS API's, there is a specific section on Accessing the Storage API from Custom Code. We can learn from this section that we first need to call storage.getAll to get the metadata of all objects (JSON files in our case) in the storage collection. We can then loop over the result array of this call and make a call to storage.getById to get the content of each file. Every REST call in MCS is made in an asynchronous way and to speed up performance, we should make all the storage.getById calls in parallel, and once the last call is finished, merge the results of each call.

A so-called promise provides access to the result of such an asynchronous request, and every JavaScript promise library includes functionality to make multiple asychronous requests in parallel and then do some processing when all requests are finished. MCS internally uses the bluebird promise library and we recommend to use the same library for your custom API implementations. To install this library, go to the root directory of your custom API implementation that was created when unzipping the scaffold zip file (this directory should have a file named package.json). In this directory, execute the following command:

npm install bluebird --save

This command creates a subdirectory called node_modules which in turn contains a directory named bluebird.We are now ready to code the implementation in our main JavaScript file. Here it is:

var Promise = require("bluebird"); module.exports = function (service) {     service.get('/mobile/custom/iot/messages', function (req, res) {         req.oracleMobile.storage.getAll("IoT").then(           function (result) {               var items = JSON.parse(result.result).items;               var promises = [];               items.forEach(function (item) {                   var promise = req.oracleMobile.storage.getById("IoT", item.id, {outType: 'json'});                   promises.push(promise);               });               return promises;           })           .then(function (promises) {               return Promise.all(promises);           })           .then(function (result) {               var itemsContent = [];               result.forEach(function (result) {                   itemsContent.push(result.result);               });               res.send(200, itemsContent);           })           .catch(function (err) {               res.send(500, err);           })     }); };

If you are new to the concept of promises this code might look a bit intimidating but we will explain line-by line what is going on:

  • at line 1 we make the bluebird promise library available for use in our JavaScript file
  • at line 5 we make the REST call to get all metadata of all files in the collection
  • at lines 7-12 we process the response from the storage.getAll call and create an array of promises where we use the id of the file included in the metadata to construct the proper storage.getById promise (every MCS REST call returns a promise)
  • at line 13, we return the array of promises, so it is passed in as argument into the next then statement at line 15.
  • at line 16, we execute all storage.getById calls in parallel using the Promise.all command.
  • at lines 19-22 we loop over the result array produced by the Promise.all command. Each result includes the complete REST response, not just the actual file content. To get the fie content, we need to get the value of the result property of the REST response, this is why we push result.result on our array that we use to send as response at line 23.
  • at lines 28-30, we catch any unexpected error and set the error message as response

That's it, if we now call this endpoint using the MCS tester page it will return an array of all device messages together.

Using the MCS Database Service

The MCS integration functionality in IoTCS is not really aware that it is calling the MCS storage API through the URL property. All it knows is that it needs to call this REST endpoint with the POST method, include the mobile backend id request header parameter, and send the content of the device message in the request body. In other words, we can also provide a custom API endpoint (or even a non-MCS endpoint that would simply ignore the mobile backend id request header param) that supports the POST method and then write our own logic to store the message content in a database table using the MCS database API.

First, we need to create the database table that will hold all the device messages. We can do this by navigating to the database management API pages in the MCS web interface. The database management API is a bit hard to find. It is included at the bottom of the API page where you have a "film strip" of Platform APIs. Many other pages have the same platform APIs film strip, but these other pages do not include the database management API. So, from the MCS dashboard page, click on APIs, scroll to the bottom, then browse to the right in the film strip and the database management icon should appear:

DBMgtAccess

We then click on the POST Create a Table link, which brings us to a page where we enter "id" in the Oracle-Mobile-Extra-Fields and the following JSON payload in the body field to create the table:

{   "name" : "IOT_Messages",   "columns": [     {       "name": "content", "type": "string"     }   ] }

Then we enter authentication details, choose a mobile backend and click the Test Endpoint button which will create a very simple table with just one column that will hold the message content. In our custom API we now create the same /messages endpoint as we did when using the storage service, but this time we add both a GET method and a POST method:

service.post('/mobile/custom/iot/messages', function (req, res) {     var messages = req.body;     var rows = [];     messages.forEach(function (message) {         rows.push({content: JSON.stringify(message)});     })     req.oracleMobile.database.insert('IOT_Messages', rows).then(       function (result) {           res.send(result.statusCode, result.result);       },       function (error) {           res.send(500, error.error);       }); }); service.get('/mobile/custom/iot/messages', function (req, res) {     req.oracleMobile.database.getAll("IOT_Messages")       .then(function (result) {           var rows = JSON.parse(result.result).items;           var messages = [];           rows.forEach(function (row) {               messages.push(JSON.parse(row.content));           })           res.send(result.statusCode, messages);       })       .catch(function (error) {           res.send(500, error.error);       }); });

 

In the POST method we loop over the array sent as request body (which typically only contains one message), then stringify the JSON message, and create a row JSON object with the content attribute value set to the stringified device message. Then we call the database.insert method to insert the rows array.

The GET method has become much simpler compared to the implementation using the storage service, we now need just one REST call to retrieve all the rows from the database table. We loop over all rows, and create an array of the values of the content column. We convert the stringified message back to JSON to prevent a response payload that includes escape characters for all quotes.

The last step is to configure IoTCS to use our custom API rather then the storage API. This is as simple as changing the URL field in the Connection tab:

IotCustomGOED

Conclusion

Both the MCS storage API and MCS database API can be used to store device messages. When using the storage API, you do not have to write custom code to store the messages, but the custom code to retrieve the messages is much more complex, and involves a separate REST call for each message. A-Team recommends to use the database API because the code is simpler and requires only one REST call to retrieve all messages sent by IoTCS.

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha