Build a Scalable System for SMS Text Messaging with Infobip
Infobip is an enterprise mobile messaging platform that offers a number of services for device messaging like SMS, email, mobile push notifications, and 2FA. With the release of our new Infobip BLOCK for SMS, this post will focus on sending SMS messages to devices in real time.
In this tutorial, we’ll walk through a simple example of how to track and display SMS alert events in a real-time app.
Infobip API for SMS
As anyone running a large-scale application will attest, sending technical and business related messages quickly is vital to ensuring continuity of service, business health, and recovering from system issues quickly.
Large-scale cross-platform device SMS messaging is a notoriously difficult area, and it requires substantial effort and engineering resources to maintain high availability and high efficiency in a robust delivery system. Not to mention all of the work involved in managing fault analysis and end user support!
Infobip provides a solution to these issues with a friendly UI and easy-to-use API that “just works.” Through the API and user interface, it is possible to send new SMS messages, track delivery, do batch and delayed delivery, and more. To top it all off, Infobip offers a wide range of transactional messaging services for end-user devices.
Obtaining Your PubNub Developer Keys
You’ll first need your PubNub publish/subscribe keys. If you haven’t already, sign up for PubNub. Once you’ve done so, go to the PubNub Admin Portal to get your unique keys.
The publish and subscribe keys look like UUIDs and start with “pub-c-” and “sub-c-” prefixes respectively. Keep those handy – you’ll need to plug them in when initializing the PubNub object in your HTML5 app below.
Getting Started with Infobip
The next thing you’ll need to get started with Infobip services is an Infobip trial account to take advantage of the SMS APIs.
Setting Up the Function
The Infobip BLOCK runs on Functions, which makes it easy to create code to run in the network. Here’s how to make it happen:
First, go to the application instance on the PubNub Admin Dashboard.Create a new Function.
Paste in the Function code from the next section.
Start the Function, and test it using the “publish message” button and payload on the left-hand side of the screen (including the Auth Key from the steps above).
That’s all it takes to create your serverless code running in the cloud!
Diving into the Code – the Function
You’ll want to grab the 56 lines of Function JavaScript and save them to a file called pubnub_infobip_function.js
. It’s available as a Gist on GitHub for your convenience.
First up, we set up our constants. The first one, INFOBIPAPIKEY, should be your API key from the steps above. The second and third ones won’t need to change – the API URL, and valid message codes.
const INFOBIP_API_KEY = 'YOUR_API_KEY'; const INFOBIP_API_URL = 'https://api.infobip.com/sms/1/text/single'; const SUCCESSFUL_SMS_STATUS_GROUPS = [1, 3];
Then, we declare our dependency on xhr (for HTTP requests), and pubnub (in case we want to send messages to alternate channels). We’ll use these to create a function to handle incoming messages from PubNub.
const pubNub = require('pubnub'); const xhr = require('xhr');
Next, we declare a function for creating an Infobip API request, consisting of doing a POST of JSON to the API with an auth header.
function composeOptions(apiKey, body) { return { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `App ${apiKey}`, 'User-Agent': 'APIv5_PUBNUB' }, body: JSON.stringify(body) }; }
Next up is a utility function for telling whether the API request was successful.
function isSuccessfullySent(responseBody) { return SUCCESSFUL_SMS_STATUS_GROUPS.indexOf(responseBody.messages[0].status.groupId) > -1; }
Here, we declare a utility function for handling the API response and decorating the message with results if successful.
function handleApiResponse(pubNubRequest, apiResponse) { const httpStatus = apiResponse.status; const responseBody = JSON.parse(apiResponse.body); if (httpStatus === 200 && isSuccessfullySent(responseBody)) { pubNubRequest.message.sent = responseBody; pubNubRequest.message.sent.timestamp = new Date().toISOString(); return pubNubRequest.ok(); } else { return pubNubRequest.abort('Message sending failed.'); } }
As well as a utility function for handling the API response when it’s not successful.
function handleSendingError(pubNubRequest, error) { const message = error.message; const fileName = error.fileName; const lineNumber = error.lineNumber; return pubNubRequest.abort('Failed to make API request to Infobip.'); }
Finally, we create our default message handler that receives the incoming message, extracts parameters and calls Infobip.
export default (pubNubRequest) => { const to = pubNubRequest.message.to; const text = pubNubRequest.message.text; const from = pubNubRequest.message.from; if (!to) return pubNubRequest.ok(); const options = composeOptions(INFOBIP_API_KEY, {from, to, text}); return xhr.fetch(INFOBIP_API_URL, options) .then(handleApiResponse.bind(this, pubNubRequest)) .catch(handleSendingError.bind(this, pubNubRequest)); };
All in all, it doesn’t take a lot of code to add SMS messaging to our application. We like that!
OK, let’s move on to the UI!
Diving into the Code – the User Interface
You’ll want to grab these 96 lines of HTML & JavaScript and save them to a file called pubnub_infobip_ui.html
.
The first thing you should do after saving the code is to replace two values in the JavaScript:
- YOUR_PUB_KEY: with the PubNub publish key mentioned above.
- YOUR_SUB_KEY: with the PubNub subscribe key mentioned above.
If you don’t, the UI will not be able to communicate with anything and probably clutter your console log with entirely too many errors.
For your convenience, this code is also available as a Gist on GitHub, and a Codepen as well.
Dependencies
First up, we have the JavaScript code & CSS dependencies of our application.
<!DOCTYPE html> <html> <head> <title>Angular 2</title> <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.7.2/dist/zone.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.9/Reflect.js"></script> <script src="https://unpkg.com/rxjs@5.0.1/bundles/Rx.js"></script> <script src="https://unpkg.com/@angular/core/bundles/core.umd.js"></script> <script src="https://unpkg.com/@angular/common/bundles/common.umd.js"></script> <script src="https://unpkg.com/@angular/compiler/bundles/compiler.umd.js"></script> <script src="https://unpkg.com/@angular/platform-browser/bundles/platform-browser.umd.js"></script> <script src="https://unpkg.com/@angular/forms/bundles/forms.umd.js"></script> <script src="https://unpkg.com/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script> <script src="https://unpkg.com/pubnub@4.3.3/dist/web/pubnub.js"></script> <script src="https://unpkg.com/pubnub-angular2@1.0.0-beta.8/dist/pubnub-angular2.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" /> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" /> </head>
For folks who have done front-end implementation with Angular2 before, these should be the usual suspects:
- CoreJS ES6 Shim, Zone.JS, Metadata Reflection, and RxJS : Dependencies of Angular2.
- Angular2 : core, common, compiler, platform-browser, forms, and dynamic platform browser modules.
- PubNub JavaScript client: to connect to our data stream integration channel.
- PubNub Angular2 JavaScript client: provides PubNub services in Angular2 quite nicely indeed.
In addition, we bring in the CSS features:
- Bootstrap: in this app, we use it just for vanilla UI presentation.
Overall, we were pretty pleased that we could build a nifty UI with so few dependencies. And with that… on to the UI!
The User Interface
Here’s what we intend the UI to look like:
The UI is pretty straightforward – everything is inside a main-component
tag that is managed by a single component that we’ll set up in the Angular2 code.
<body> <main-component> Loading... </main-component>
Let’s skip forward and show that Angular2 component template. The h3
heading should be pretty self-explanatory. We provide a simple button to perform the publish()
action to send a PANIC event to be processed by the Function.
<div class="container"> <pre> NOTE: make sure to update the PubNub keys below with your keys, and ensure that the SMS Transmission FUNCTION is configured properly! </pre> <h3>MyApp Instant SMS Notifications</h3> <input type="button" (click)="publish()" value="PANIC!" /> <br /><br /> <ul> <li *ngFor="let item of messages.slice().reverse()"> Event Description: <span class="label label-danger">{{item.message.text}}</span> <br /> SMS Message ID: <span class="label label-default">{{item.message.sent.messages[0].messageId}}</span> <br /> Timestamp: <span class="label label-default">{{item.message.sent.timestamp}}</span> </li> </ul> </div>
The component UI consists of a simple list of events (in our case, the alerts data). We iterate over the messages in the component scope using a trusty ngFor
. Each message includes the data from the Infobip API.
And that’s it – a functioning real-time UI in just a handful of code (thanks, Angular2)!
The Angular2 Code
Right on! Now we’re ready to dive into the Angular2 code. It’s not a ton of JavaScript, so this should hopefully be pretty straightforward.
The first lines we encounter set up our application (with a necessary dependency on the PubNub AngularJS service) and a single component (which we dub main-component
).
<script> var app = window.app = {}; app.main_component = ng.core.Component({ selector: 'main-component', template: `...see previous...`
The component has a constructor that takes care of initializing the PubNub service and configuring the channel name and initial values. NOTE: make sure the channel matches the channel specified by your BLOCK configuration and the BLOCK itself!
}).Class({ constructor: [PubNubAngular, function(pubnubService){ var self = this; self.pubnubService = pubnubService; self.channelName = 'infobip_sms'; self.messages = [];
Early on, we initialize the pubnubService
with our credentials.
pubnubService.init({ publishKey: 'YOUR_PUB_KEY', subscribeKey: 'YOUR_SUB_KEY', ssl:true });
We subscribe to the relevant channel, create a dynamic attribute for the messages collection, and configure a blank event handler since the messages are presented unchanged from the incoming channel.
pubnubService.subscribe({channels: [self.channelName], triggerEvents: true}); self.messages = pubnubService.getMessage(this.channelName,function(msg){ // no handler necessary, dynamic collection of msg objects }); }],
We also create a publish()
event handler that performs the action of publishing the new message to the PubNub channel. In this case, we hard-code the phone number in the UI. In your application, you might choose to code this in the FUNCTION to keep it private from curious users.
publish: function(){ this.pubnubService.publish({ channel: this.channelName, message: {to:"15554443333",from:"InfoSMS",text:"PANIC sent by user on channel " + this.channelName + "!"} }); } });
Now that we have a new component, we can create a main module for the Angular2 app that uses it. This is pretty standard boilerplate that configures dependencies on the Browser and Forms modules and the PubNubAngular service.
app.main_module = ng.core.NgModule({ imports: [ng.platformBrowser.BrowserModule, ng.forms.FormsModule], declarations: [app.main_component], providers: [PubNubAngular], bootstrap: [app.main_component] }).Class({ constructor: function(){} });
Finally, we bind the application bootstrap initialization to the browser DOM content loaded event.
document.addEventListener('DOMContentLoaded', function(){ ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(app.main_module); });
We mustn’t forget close out the HTML tags accordingly.
}); </script> </body> </html>
All in all, we found it pretty easy to get started with messaging features using the API, and we look forward to using more of the deeper integration features!
Conclusion
Thank you so much for joining us in the SMS alerting article of our Functions and web services series! Hopefully it’s been a useful experience learning about SMS-aware technologies. In future articles, we’ll dive deeper into additional web service APIs and use cases for other nifty services in real time web applications.