Posts tagged 'usergrid'


Monday Jan 26, 2015

Usergrid and Ember.js - part 2

In part one, I explained the basics of the example Usergrid-Ember "Checkin" app, how the index page is displayed and how login is implemented. In part two, I'll explain how Ember.js can be hooked into the Usergrid REST API to store and query JSON objects.

Ember logo

Ember.js includes a feature referred to as Ember-Data, which provides a persistence interface for storing and retrieving JavaScript objects that could be stored in memory, or stored on a server and accessed via REST API.

To use Ember-Data with your REST API you've got to define an Ember-Data model and add an Ember-Data REST adapter. If your REST API differs from what Ember-Data expects then you will probably have to extend the built-in REST adapter to handle your URL pattens, and extend the built-in REST serializer to handle your JSON format. By extending Ember-Data in this way, you can use it to store and query data from Usergrid without using the Usergrid JavaScript SDK at all. Below I'll explain what I had to do to make the Checkin app's Activities collection available via Ember-Data.

Define Ember-Data models

Ember-Data expects each of your REST API collections to have a defined data model, one that extends the DS.Model class. Here's what I added for the Activities collection:

From app.js (link)

App.Activity = DS.Model.extend({
  uuid: DS.attr('string'),
  type: DS.attr('string'),
  content: DS.attr('string'),
  location: DS.attr('string'),
  created: DS.attr('date'),
  modified: DS.attr('date'),
  actor: DS.attr('string'),
  verb: DS.attr('string'),
  published: DS.attr('date'),
  metadata: DS.attr('string')
});

Create a custom RESTAdapter

The Ember-Data REST adapter expects a REST API to follow some common conventions for URL patterns and for JSON data formats. For example, if your REST API provides a collection of cats then Ember-Data will expect your REST API to work like so:

What Ember-Data expects for a cats collection:

  • GET /cats - get collection of cats
  • POST /cats - create new cat.
  • GET /cats/{cat-id} - get cat specified by ID.
  • PUT /cats/{cat-id} - update cat specified by ID.
  • DELETE /cats/{cat-id} - delete cat specified by ID.

Usergrid follows the above conventions for collections, but there are some exceptions. For example, the Usergrid Activities collection. A GET on the /activities path will return the Activities of the users that you (i.e. the currently authenticated user) follow. You don't POST new activities there, instead you post to your own Activities collection at the path /users/{your-user-id}/activities. It works like this:

Usergrid's Activities collection:

  • GET /activities - get Activities of all users that you follow.
  • POST /user/{user-id}/activities - create new Activity for user specified by ID
  • GET /user/{user-id}/activities - get Activities for one specific user.

To adapt the Activities collection to Ember-Data, I decided to create a new model called NewActivity. A NewActivity represents the data needed to create a new Activity, here's the model:

From app.js (Link)

// Must have a special model for new activity because new 
// Activities must be posted to the path /{org}/{app}/users/activities, 
// instead of the path /{org}/{app}/activities as Ember-Data expects.
App.NewActivity = DS.Model.extend({
  content: DS.attr('string'),
  location: DS.attr('string'),
  actor: DS.attr('string'),
  verb: DS.attr('string')
});

Then, in Checkin's custom REST adapter, I added logic to the pathForType() function to ensure that NewActivities are posted to the correct path. Here's the adapter:

From app.js (Link)

App.ApplicationAdapter = DS.RESTAdapter.extend({

  host: Usergrid.getAppUrl(),

  headers: function() { 
    if ( localStorage.getItem("access_token") ) {
      return { "Authorization": "Bearer " 
          + localStorage.getItem("access_token") }; 
    } 
    return {};
  }.property().volatile(), // ensure value not cached

  pathForType: function(type) {
    var ret = Ember.String.camelize(type);
    ret = Ember.String.pluralize(ret);

    if ( ret == "newActivities" ) {
      // Must have a special logic here for new activity 
      // because new Activities must be posted to the 
      // path /{org}/{app}/users/activities, instead of the 
      // path /{org}/{app}/activities as Ember-Data expects.
      ret = "/users/" + Usergrid.user.username + "/activities";
    }
    return ret;
  }

});

You can see a couple of other interesting things in the example above. First, there's the host field which specifies the base-URL of the REST API for the Checkin app. Next, there's the headers() function, which ensures that every request carries the access_token that was acquired during login.

Create a custom RESTSerializer

Ember-Data also has expectations about the JSON format returned by a REST API. Unfortunately, what Ember-Data expects and what Usergrid provides are quite different. The two examples below illustrate the differences:

Ember-Data vs. Usergrid JSON formats

Ember-Data expects collections like this:

{
   cats: [{
       "id": "6b2360d0",
       "name": "enzo",
       "color": "orange"
   },{
       "id": "a01dfaa0",
       "name": "bertha",
       "color": "tabby"
   }]
}






Usergrid returns collections like this:

{
   action: "get",
   path: "/cats",
   count: 2,
   entities: [{
       "uuid": "6b2360d0",
       "type": "cat",
       "name": "enzo",
       "color": "orange"
   },{
       "uuid": "a01dfaa1",
       "type": "cat",
       "name": "bertha",
       "color": "tabby"
   }]
}

Ember-Data expects individual objects like this:

 
{
   cat: {
       "id": "a01dfaa0",
       "name": "bertha",
       "color": "tabby"
   }
}

Usergrid returns individual objects like this:

 
{
   "id": "a01dfaa0",
   "type": "cat",
   "name": "bertha",
   "color": "tabby"
}

You can see two differences above. Ember-Data expects JSON objects to be returned with a "type key" which you can see above: the "cats" field in the collection and the "cat" field in the individual object. Also, Ember-Data expects an object's ID field to be named "id" but Usergrid returns it as "uuid."

The deal with these differences, the Checkin app extends Ember-Data's DS.RESTSerializer. Here's the code:

From app.js (Link)

App.ApplicationSerializer = DS.RESTSerializer.extend({

  // Extract Ember-Data array from Usergrid response
  extractArray: function(store, type, payload) {

    // Difference: Usergrid does not return wrapper object with 
    // type-key. So here we grab the Usergrid Entities and stick 
    // them under a type-key
    var typeKey = payload.path.substring(1);
    payload[ typeKey ] = payload.entities;

    // Difference: Usergrid returns ID in 'uuid' field, Ember-Data 
    // expects 'id'. So here we add an 'id' field for each Entity, 
    // with its 'uuid' value.
    for ( var i in payload.entities ) {
      if ( payload.entities[i] && payload.entities[i].uuid ) {
        payload.entities[i].id = payload.entities[i].uuid;
      }
    }
    return this._super(store, type, payload);
  },

  // Serialize Ember-Data object to Usergrid compatible JSON format
  serializeIntoHash: function( hash, type, record, options ) {

    // Usergrid does not expect a type-key
    record.eachAttribute(function( name, meta ) {
      hash[name] = record.get(name);
    });

    return hash;
  }
});

In the code above you can see how the extractArray() method moves the "entities" collection returned by Usergrid into a type-key field as expected by Ember-Data and how it copies the "uuid" field to add the "id" field that Ember-Data expects.

We also need to transform the data that Ember-Data sends to Usergrid. You can see this above in the serializeInHash() function, which ensures that when data is POSTed or PUT to Usergrid, the type key is removed because that's what Usergrid expects.

Implementing Add-Checkin

To implement Add-Checkin, I added an HTML template called "add-checkin" to Checkin's index.html file. The template displays an Add-Checkin form with two fields: one for content and one for the location. Here's what it looks like in all its modal glory:


screenshot of add-checkin page


Both fields are simple strings (someday I'd like to extend Checkin to use location information from the browser). I won't go into detail here, but it took a bit of research to figure out how to make a Bootstrap modal dialog work with Ember.js. Below you can see the add-checkin controller, which provides a save() function to save a new checkin.

From app.js (Link)

App.AddCheckinModalController = Ember.ObjectController.extend({

  actions: {

    save: function( inputs ) {

      var content = inputs.content;
      var location = inputs.location;
      var target = this.get("target");

      var activity = this.store.createRecord( "NewActivity", {
        content: content,
        location: location,
        verb: "checkin",
        actor: {
          username: Usergrid.user.username
        }
      });

      activity.save().then(
        function( success ) { 
          alert("Saved"); 
        },
        function( error ) { 
          alert("Error " + error.responseJSON.error_description); 
        }
      ); 

    } 
  }
});

In the code above you can see how easy it is to access Usergrid data via Ember-Data now that we've got our custom REST adapter and serializer in place. We create a new Activity with a call to this.store.createRecord() and to save it all we need to do is activity.save().

Time to wrap up...

To sum things up, here are some closing thoughts and observations.

  • If you are considering JavaScript MVC frameworks, then Ember.js is definitely worthy of your consideration. The documentation makes it easy to learn and the community is friendly and helpful.
  • It would be great for Usergrid to provide an Ember.js SDK that makes it really easy to build apps with Ember.js and Usergrid.
  • Ember-Data is an integral part of Ember.js, something that you need to do pretty much anything, but it is treated as a separate package with separate documentation. That is somewhat confusing for a new user.
  • Ember-Data does not include built-in form validation so if your app includes a large number of non-trivial forms, then you may prefer AngularJS over Ember.js.
  • There is a form validation plugin for Ember.js, but it requires the experimental Ember-CLI utility. I tried to use it, but Ember-CLI was unpleasnt enough that I gave up.

I appreciate any feedback you might have about this article, the Usergrid-Ember project and Apache Usergrid. If you want to see how the whole Usergrid-Ember project fits together, find it on GitHub here: Usergrid-Ember. Next up, I'll write about my experiences using Apache Shiro to replace Spring Security in Apache Roller.

Monday Jan 05, 2015

2014 side projects

For various reasons, I've always got a couple of coding projects on the back burner, things that I hack around with on weekends and breaks. In 2014, I started four projects and learned about Ember.js, jQuery Mobile, Apache Shiro, Apache CXF and the Arquillian test framework.

I like to share my code, so I've put my code on GitHub and I'm going to write a brief post about each here on my blog. I'll provide links as I go and, of course, I welcome any criticisms and suggestions for improvement that you might have. First up: the Usergrid-Mobile project.

The Usergrid-Mobile project

ApacheCon EU logo
To be honest, Budapest was the goal of this project. In the Spring of 2014, I decided that the best chance of getting to ApacheCon EU in Budapest was to create a great "mobile development with Usergrid" talk, and to do that I needed a great example project. The resulting project shows how to create a dumbed-down Foursquare-style "checkin" app using HTML5, JavaScript, jQuery Mobile and Apache Cordova.

Luckily for me, my talk was accepted for ApacheCon EU and in November I traveled to Budapest (took some photos) and gave the talk there.

I also presented the talk at the All Things Open conference in Raleigh, NC and you can view a video of that talk, Mobile Development with Usergrid on YouTube.



You can find the code for usergrid-mobile on GitHub. I also created a Vagrant File to launch a local instance of Usergrid for demo purposes. It's called usergrid-vagrant.

That's all for now. Next up: Usergrid-Ember.

Sunday Mar 16, 2014

Talking Usergrid at ApacheCon 2014

ApacheCon 2014

I've been working at Apigee since September 2013 and one of the things I love most about my new job is the fact that I'm actively contributing to open source again.

I'm working on Apache Usergrid (incubating), an open source Backend-As-A-Service (BaaS) that's built on the Apache Cassandra database system. Apigee uses Usergrid as part of Apigee Edge (see the Build Apps part of the docs).

Apigee contributed code for Usergrid to the Apache Software Foundation back in October 2013 and Usergrid is now part of the Apache Incubator. The project is working towards graduating from the Incubator. That means learning the Apache way, following the processes to get a release out and most importantly, building a diverse community of contributors to build and maintain Usergrid.

One on the most important parts of building an open source community is making it easy for people to contribute and and that's why I submitted a talk to the ApacheCon US 2014 conference (April 7-9 in Denver, CO) titled How to Contribute to Usergrid.

The talk is intended to be a briefing for contributors, one that will lead you through building and running Usergrid locally, understanding the code-base and test infrastructure and how to get your code accepted into the Usergrid project.

Here's the outline I have so far:

How to Contribute to Apache Usergrid

  • Motivation
    • Why would anybody want to contribute to Usergrid?
  • First steps
    • The basics
    • Getting signed up
  • Contributing to the Stack
    • Understanding the architecture & code base
    • Building the code. Making and testing changes
    • Running Usergrid locally via launcher & via Tomcat
  • Contributing to the Portal
    • Understanding the architecture & code base
    • Building the code. Making and testing changes
    • Running the portal locally via node.js
  • Contributing to the SDKs
    • Understanding the architecture & code base
    • Building the code. Making and testing changes
  • Contributor workflow: how to get your code into Usergrid
    • For quickie drive-by code contributions
    • For more substantial code contributions
    • For documentation & website changes
  • Contributing Docs and Website changes
    • Website, wiki and GitHub pages
    • How to build the website and docs
  • Roadmap
    • First release
    • New Core Persistence system
    • The two-dot-o branch
    • Other ideas

I'm in the process of writing this talk now so suggestions and other feedback are most welcome.