Wednesday, December 12, 2012

ApplicationCraft - Easy Notifications with Xtify & Pusher






Background

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

Overview

This blog is about learning how to notify connected or unconnected native mobile app users and also desktop browser web apps.  ApplicationCraft makes it very easy to build both web apps and mobile native apps.  So we'll be looking out how all this notification can be done with a single code base and deployed as a web app on the mobile and desktop and as a native mobile app.

I'll be using Xtify to do notifications to a native app.  The notifications will be sent to the mobile device and made available through the devices notification manager if the app is not running.  If the app is running, the app receives the notification.

I'll be using Pusher to do real time notifications to connected browsers running either on the desktop or on the mobile.

Video

The following video gives a brief overview of how AC utilizes Xtify and Pusher to notify connected and unconnected users.



Live App

Because this app uses my personal keys from Xtify, the Xtify portion won't work as I've changed the values. I've also changed the Pusher keys/secret values but if you have your own values, you can enter them in the UI and validate the Pusher portion works.

The app is here: http://acft.ws/nnqe

Source Code

All the source code for the ApplicationCraft app and the Pusher javascript class is located here: https://github.com/bartonhammond/TaskList

Xtify

Note: I'm only showing how to set up the Android.

Xtify is provided by ApplicationCraft as a plugin for the AC Mobile Build.  You can read about setting Xtify up by starting here: http://www.applicationcraft.com/developers/documentation/ac-mobile-build-phonegap/ac-mobile-build/ac-build-plugins/xtify-push-notifications/

You first start here: http://developer.android.com/guide/google/gcm/gs.html and then take the link with "Google APIs Console Page".  On the following picture you'll create a API Project - be sure to name the Project ID with some meaning text.

On your API Access tab you will want to copy the API Key:


Now over to Xtify,  once you've registered you'll go to console and then App Manager -> App Key Manager which is at https://console.xtify.com/manage-app-keys.   Here you'll add a new Application and provide the Google API Key from the previous image above.


Then you have to go to the App Manager -> API Keys Manager and "Add New API Key" and be sure to add the Advanced Key Type:

Let's look at the code required for this

Now we're almost ready to send a message but first we need to set up a few things.  The app needs some configuration which is documented here: http://www.applicationcraft.com/developers/documentation/ac-mobile-build-phonegap/ac-mobile-build/ac-build-plugins/xtify-push-notifications/configuring-your-app/

Once you have the configuration in place and the code added to the "handler_application_onAppStarted()" function, we need to invoke the SSJS (Server Side JavaScript) to do the Push Notification.  Now typically the SSJS would be written to respond to a DB commit and notify everyone of the change.  Or in my case the SSJS function will be monitoring for Date/Time reminders by checking the database.  When a reminder is due, a notification will be sent out.

On the client side we have a simple function:
/**
 * Test my device using XID
 */
function handler_sendMessageBtn_onClick(mouseev){
    var xid = app.getData("xidTxt");
    debugger;
 app.callSSJ('pushMessage',function(foo) {
       console.log(foo);
 }, [xid]);
}
We're call the SSJS function "pushMessage" and provide the XID from the textField from line 5.

On the SSJS function we have:
function pushMessage(xid) {
    var url = "http://api.xtify.com/2.0/push";
    var apiKey = "fced37b9-0a50-40ba-a0d9-6fd4dc018dcd"; //https://console.xtify.com/api-keys-manager
    var appKey = "ac7d8afd-50d7-4af5-a312-ae3c883adfb2"; //https://console.xtify.com/manage-app-keys 
    var headers = {"Content-Type": "application/json"};
    
    
    var request = buildPushRequest(apiKey, appKey, xid);
    
    var response = ssj.httpRequest(url, "POST", ssj.OBJ2JSON(request),"string", headers);

    return response;
}

function buildPushRequest(apiKey, appKey, xid) {

    var request = {
        "apiKey": apiKey,
        "appKey": appKey,
        "xids": [ xid ],
        "content": {
            "subject": "Test Subject Barton",
            "message": "Test message Barton"
        }
    }
    
    return request;

}

The function "pushMessages" has the apiKey and appKey defined.  The apiKey is from //https://console.xtify.com/api-keys-manager while the appKey is from //https://console.xtify.com/manage-app-keys

The function "buildPushRequest" just builds an object and substitues the apiKey, appKey and the one XID that we're targeting.

On line 10 the SSJS httpRequest method is used to send the Push Notification.  Note that the request object on line 10 has to be converted into JSON with the "ssj.OBJ2JSON" function.

You're now ready to build the native Android app.

AC Mobile Build

After you configure your app, then you can proceed to the AC Mobile Build.  The build is very straight forward - just close your app and from the console select Mobile Build -> AC Mobile Build.  For Android, I used the default certificate.

Install the generated app on to your Android device.  You don't have to run it but if your do, go ahead and close it so this next part works right.

Sending A Push Notification with Xtify

You're now ready to send a Notification to your Android.  You'll have to run the native app once on the Android and click the button "Display Xid on Mobile" and copy that.  You'll want to close the Android app and bring up your desktop browser and past the XID into the second section (see the video above).

Click the "Send Mobile Notification" and watch your Andriod get notified.  You can then click on the notification and it will start up your native app.

Pusher

Let's look at the SSJS Pusher code

Now on to using Pusher to send real time notifications to connected browsers.  First let's look at the code that was used to make all this possible.

I wrote an Pusher javascript class that requires a class to do Crypto from Google call crypto-js.  Within AC on the server side, I created a new page and copied / pasted the code from Google.

You'll want to do the same thing with my Pusher class - just copy/paste into a Server page. Below is the implementation of the ApplicationCraft version of SSJS Pusher:
//This is based of the work of
//https://github.com/fabrik42/pusher
//
//
//For details see: http://pusher.com/docs/rest_api

/**
 * Constructor expects options as:
 * 
 * {
        appId:  YOUR_app_id,
        appKey: YOUR_auth_key,
        secret: YOUR_auth_secret
    }
    */
function Pusher(options) {
    this.options = options;
    this.url = 'http://api.pusherapp.com';
    this.headers = {"Content-Type": "application/json"};
}
/**
 * Return a channel
 */
Pusher.prototype.channel = function(chan){
    this.options.channel = chan; 
    return this;
};
/**
 * Trigger the event for this channel
 */
Pusher.prototype.trigger = function(event, data) {
    this.event = event;
    this.data = data;
    
    this.jsonData = ssj.OBJ2JSON({"name": event,"channels":[this.options.channel],
        "data":JSON.stringify(data)});
    
    this.body_md5 = ssj.md5(this.jsonData);
        
    var response = ssj.httpRequest(this.path(),
        "POST", 
        this.jsonData,
        "string", this.headers);
};
/**
 * Return the url path to POST to
 */
Pusher.prototype.path = function() {
    return this.url 
        + this.uri() + '?' 
        + this.queryString() 
        + '&auth_signature=' + this.signature();
};
/**
 * Return the uri
 */
Pusher.prototype.uri = function() {
    return '/apps/' + this.options.appId + '/events';
};
/**
 * Return the queryString with the body_md5 
 */
Pusher.prototype.queryString = function() {
    var timestamp = parseInt(new Date().getTime() / 1000);
    return [
      'auth_key=',        this.options.appKey,
      '&auth_timestamp=', timestamp,
      '&auth_version=',   '1.0',
      '&body_md5=',       this.body_md5,
    ].join('');
}
/**
 * Return the Google HmacSHA256 for the querystring
 */ 
Pusher.prototype.signature = function() {
    var signData = ['POST', this.uri(), this.queryString()].join('\n');
    var crypto = CryptoJS.HmacSHA256(signData, this.options.secret);
    return crypto.toString();
};

The class is documented and I won't go into a lot of detail here.  I based this class off a node.js version I found here: https://github.com/fabrik42/pusher

The class Pusher is defined on lines 16-20.  It assumes that the options object will be passed in which has your app_id, auth_key and secret.  These are the values that you entered via the UI.

Pusher.com documents how the Restful API for the server is done.  You can read about it here: http://pusher.com/docs/rest_api.  If you follow through the Pusher class along with the reference, you should be able to follow along.  There's some tricky javascript stuff but not too bad.

Note on line 77 the use of the Google CrytoJS.HmacSHA256 function.

Let's see how easy it is to use this Pusher class.
/**
 * Pusher notification message to channel/event
 */
function pushNotification(app_id, auth_key, auth_secret, channel, event, message) {
    //var app_id = '33490';
    //var auth_key = '282e38c72e6f4fd8ff8b';
    //var auth_secret = '3c78d3ce5911feead471';
    
    debugger;
    var pusher = new Pusher({
        appId:  app_id,
        appKey: auth_key,
        secret: auth_secret
    });
    
    var channel = pusher.channel(channel);


    channel.trigger(event, message);

}
This SSJS function has all the required fields passed to it from the client side code.  On line 10, a new instance of Pusher is created and a object with the appId, appKey, and secret is passed as the options.

On line 16, the channel is defined and a instance is returned.

On line 19, the event for this channel is triggered - it's fired or sent in other words.  It fires with the message.

The SSJS Pusher class makes all this very easy.

Now let's look at the Pusher Client

Before you use Pusher on the client you have to include the file "http://js.pusher.com/1.12/pusher.min.js".  You do this in the Application Properties -> Preload Files.

The following code demonstrates how to subscribe the client to the channel and event:
/**
 * Subscribe to Pusher event
 */
function handler_subscribeBtn_onClick(mouseev){
    //key is from http://app.pusherapp.com/apps/xxxx/api_access
    var pusher = new Pusher(app.getData('appKeyTxt'));
    var channel = pusher.subscribe(app.getData('channelTxt'));
    channel.bind(app.getData('eventTxt'), function(data) {
       alert('just got a mesesage: ' + JSON.stringify(data));
    }); 
}

On line 6 a new Pusher is constructed with the appKey value from the textbox.

On line 7, the pusher object subscribes to the channel which is provided by the textbox.

On lines 8-10, the channel is bound to the event and the callback function is used everytime a message is received.  In this case, on line 9, an alert displays the stringified data.

Summary

Using both Xtify and Pusher with ApplicationCraft is rather easy.  Admittedly it was somewhat of a challenge to figure it out and write the Pusher object only because there wasn't a server side library available for AC.  But for those that are wanting to use Pusher, it should be rather straight forward now.  I think the Xtify is more difficult to setup but if you follow the docs it will make sense.  Hopefully this blog helps with some of that setup.

I hope you enjoyed this blog.






Friday, December 7, 2012

Easy Testing with Application Craft and QUnit

&

Background

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

Overview

I am in the process of writing a TaskList using Application Craft.  This blog is about learning how to do unit and intergration test with ApplicationCraft.  Unfortunatly ApplicationCraft doesn't provide a testing framework.  So I did some searching around for a javascript testing framework and found QUnit.  This testing framework was originally written by John Resig of JQuery frame.  I figured since AC is written using JQuery Mobile that this would be a reasonable testing framework for my purposes.

Video

The following video gives a brief overview of how AC was utilized to drive the tests and how to incorporate QUnit.  

Live app

The live app can be run from here

Source code

As usual, you can download all the code as a ApplicationCraft project from my github account for TaskList.

Tutorial

So to start off we will look at the Step 3 - Unit Test project and how it is configured.  If you watch the video it will help alot.

Setting up the Step 3-  Unit Test App

  • On the testing app page, set the property "Preload Files" to use http://code.jquery.com/qunit/qunit-git.js and http://code.jquery.com/qunit/qunit-git.css
  • On the testing app page drop a HTML component and add the following which is used by QUnit to display the test results.  
    <h1 id="qunit-header">QUnit Test Suite</h1>
    <h2 id="qunit-banner"></h2>
    <div id="qunit-testrunner-toolbar"></div>
    <h2 id="qunit-userAgent"></h2>
    <div id="qunit-testresult"></div>
    <ol id="qunit-tests"></ol>
    <div id="qunit-fixture">
          <div id="container"></div>
    </div>
    
    
    

  • Be sure to enable the HTML component for Resizing.
  • Add an Embed App widget (See Toolbox -> Advanced).  Once you drop that widget set the property Embed App to the "Step 2 - Persistence" app.
  • Select the AppEmbed component and in the Events tab, select the On Started so that a function is created.

Our first test

A little prep work in the SUT (System Under Test - in this case Step 2 - Persistence app).


This first test will demonstrate how to access a client side function in the embedded app.  In the Step 2 - Persistence app (the app being tested) there is a silly function I've added call "sayHi".  Now just how does the parent app (Step 3 - Unit Testing) get access to that embedded app's function?  Well, the trick is to set a global object with that function as a member.  The following code demonstrates how an Embedded App function is made available for testing.
//Silly function for demonstration purpose
function sayHi(foo) {
    return "Hi " + foo;
}

//Set global object
var gObj = {};

//Set the functions in the global object
gObj.sayHi = sayHi;

gObj.fillZingerChart = fillZingerChart;

//Make the object available 
app.setGlobalData('gObj',gObj);

The function "sayHi" is defined on lines 2-4.  It merely returns the parameter prepended with "Hi ".

On line 7, there is a object defined.  On lines 10 & 12, that object is updated to have two functions: 1) sayHi and 2) fillZingerChart.  These are the two client side functions that I want to test so I define them to this object.

On line 15 that object gObj is set into the global data with the key 'gObj'.  We'll see later how the unit testing code can now access that gObj and invoke these client side functions.

The actual client test code

The following code is entered into the embedded on start function as shown below

function handler_appEmbed_onStarted(){
    /**
     * Simple client side test
     */
    test('call client side function',1, function() {
        var gObj = app.childApp('appEmbed').getGlobalData('gObj');
        var rtn = gObj.sayHi('Barton');
        equal(rtn,'Hi Barton','sayHi should say Hi Barton');    
    });
So line 5 starts the test case.  The first parameter to "test" is a description of what the test will do.  It will show up later in the test results that QUnit provides.  The second parameter, 1 in this case, says how many assertions will be done.  Then a callback function is provided that will be executed when the test is run.

On line 6, we reference the embedded app and get the Global Data value for the key 'gObj' which we set earlier in the client/embedded app.

On line 7 we invoke the "sayHi" function and pass a parameter and save the return value.

On line 8 we assert that the return value is correct.  The first parm is the actual value, the second is the expected and the third value is a description of what the test is asserting.

There you have it - our first unit test run from within Application Craft to test Application Craft.  The code is external to the code we're testing and all the features of being a project within Application Craft are ours for free: source control, debugging, the IDE, the editor, etc.

Let's test the SSJS code

In this next bit of testing we are going to invoke some SSJS code.  Now this gets a little more complex because you have to let the test run asyncronously.  So I want to call three methods: 1) clear the log 2) write to the log 3) return the logs.  I want to confirm that if I send only one log, that I get only one log.  

Note that this test also is running in the embedded on Started method so that all the "plumbing" is in place.
/**
     * Simple server side only test 
     */
    test('test log get cleared and only 1 log is added',3,function() {
        stop();
        //Clear the log 
        app.childApp('appEmbed').callSSJ('clearLog', function(error,data){
            //Set one log object
            app.childApp('appEmbed').callSSJ('setLog', function(error,data) {
                //Get all the logs
                app.childApp('appEmbed').callSSJ('getLog',function(error,data) {
                    start();
                    equal(data.length, 1, 'only one log should be returned');
                    equal(data[0].id, 1, 'id should be 1');
                    ok(data[0].time > 1, 'time should have a value');
                },[true]);
            },[{id:1},true]);   
        },[]);
    });

The test is defined starting on line 4 and it expects to have 3 assertions which are on lines 13-15. In the callback of the test, on line 5, the tests are stopped because we're doing async calls.  The first call to the server is on line 7 which clears the log.  In the callback of that function, on line 9 a call is made to "setLog".  On the callback of that function call on line 11, a call is made to get the log.  Within the callback function we start the QUnit testing again with a "start" on line 12.  Lines 13-15 assert that the logs returned are of length 1 and the data contains the "id" and "time" attributes.

Combining SSJS and Client side testing

This next bit of code calls the SSJS function to "startTesting" which returns the logs from the test run.

    /**
     * Combination SSJS & Client side test
     */
    test('fill Zinger chart data',7, function() {
        stop();
        //Run 1 series of CRUD tests
        app.childApp('appEmbed').callSSJ('startTesting', function(error,data) {
            start();
            equal(data.length,10,'there should be 10 logs');
            
            //Start client side test
            var gObj = app.childApp('appEmbed').getGlobalData('gObj');
            var jsonData = gObj.fillZingerChart('test',data);
            
            //Validate the title
            var title = jsonData.graphset[0].title.text;
            var tokens = title.split(' ');
            equal(tokens[0],'test','title should be test');
            
            //Validate there are only 4 series (CRUD) and one value per series
            equal(jsonData.graphset[0].series.length, 4, 'there should be 4 series');
            for (var row = 0; row < 4; row++) {
                equal(jsonData.graphset[0].series[row].values.length, 1, 'there should only be one series in '
                + jsonData.graphset[0].series[row].text);
            }
        },[1,app.childApp('appEmbed').getValue('databaseRadio').value,false,false]);

          
    });
 
On line 4, we provide a description for the test and state that there will be 7 assertions.  In the callback we immediately stop all testing on line 5.  On line 7 we call the SSJS function "startTesting" and provide the required parameters on line 26.  Note on line 26 how the embedded apps component is referenced to obtain the DB connection string.

In the callback of the "startTesting" SSJS call, on line 8 we start our testing again and validate that there should be 10 log statements.

On line 12 we call get the global object again so that we can reference the client side function "fillZingerChart" which is called on line 13. Lines 16 - 26 then just validate that the structure built by "fillZingerChart" is basically correct.

Interacting with the UI components

This last test demonstrates how to interact with the visual components of the embedded app.


/**
     * Interact with the UI and run 20 tests confirming under 2 seconds 
     */
    test("run 20 crud tests under 2 seconds", 3, function () {
        stop();
        
        //Navigate to the 'crud' page
        app.childApp('appEmbed').w('actionBtn').base().trigger('vclick');
   
        setTimeout(function() {
            //Confirm crud page
            equal(app.childApp('appEmbed').currentPage().name(),'crud');
            
            //Set UI component to 20 CRUD tests
            app.childApp('appEmbed').setValue('numberOfTestsSlider',20);
            
            setTimeout(function() {  
                //Confirm UI set correctly
                equal(app.childApp('appEmbed').getValue('numberOfTestsSlider'),20, 'slider was set'); 
                
                //Start the test
                app.childApp('appEmbed').w('startTestBtn').base().trigger('vclick'); 
                
                setTimeout(function() {
                    //Get the data from the Zingchart
                    var json = zingchart.exec(app.childApp('appEmbed').w('ZingchartAC').getZingChartId(), "getdata");
                    var title = json.graphset[0].title.text;
                    var tokens = title.split(' ');
                    //Confirm the time is less then 2 seconds
                    ok(tokens[2] < 2000, 'the query is less then 2 seconds');
                    start();
                },4000);
                
            }, 2000);
            
        }, 2000); 
    });
Now the problem we're faced with again is the asyncronous of the calls.  Everytime we interact with a visual component we have to give up control and let the embedded app respond to the interaction.

So in the code above, on line 4 we setup the test and state that there will be 3 assertions.  In the callback we immediately stop the tests so that we can interact with the visual UI components.

On line 8 we trigger a "click" event on the embedded apps actionbtn.

On line 10, we set a standard javascript timeout function for 2000 milliseconds.  When that period of time expires, the function is invoked on line 12.  Here we assert that our new current page in the embedded app is  named 'crud'.

Now on line 15 we set the Slider component to have the value of 20 representing the number of tests to run.

Again we set a timeout to allow the component to respond.  The timeout is also 2 seconds.  In the callback on line 19 we validate that the slider component was indeed set to 20.

Now on line 22 we trigger the "Start Test" button and immediately on line 24 do another timeout.  At the completion of these 4 seconds (line 32),  we can reference the ZingChart that was created.  The ZingChart provides us the JSON data that is then validated on line 30.

Now this is an integration test - I'm using a real database and actually performing the 20 tests.

Summary

As you can see doing unit and integration tests with Application Craft is not so bad.  Other then the asynchronous call the effort is fairly straight forward.

What I really like about this approach is that I continue to do all my work using Application Craft itself.  I didn't have to start writing code using a 3rd party product and then have to worry about where to store that code in GitHub or whatever.  I didn't have to learn yet another tool to do debugging and editing.  With this approach we get to leverage all the power of Application Craft!

Please feel free to leave comments.