Merge from 'brandonscript/usergrid-nodejs'
diff --git a/lib/usergrid.js b/lib/usergrid.js
new file mode 100755
index 0000000..b29dd3f
--- /dev/null
+++ b/lib/usergrid.js
@@ -0,0 +1,2584 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+var inflection = require('inflection');
+var request = require('request');
+var Usergrid = {};
+Usergrid.USERGRID_SDK_VERSION = '0.10.07';
+
+//authentication type constants
+var AUTH_CLIENT_ID = 'CLIENT_ID';
+var AUTH_APP_USER = 'APP_USER';
+var AUTH_NONE = 'NONE';
+
+  Usergrid.Client = function(options) {
+    //usergrid enpoint
+    this.URI = options.URI || 'https://api.usergrid.com';
+
+    //Find your Orgname and Appname in the Admin portal (http://apigee.com/usergrid)
+    if (options.orgName) {
+      this.set('orgName', options.orgName);
+    }
+    if (options.appName) {
+      this.set('appName', options.appName);
+    }
+
+    //authentication data
+    this.authType = options.authType || AUTH_NONE;
+    this.clientId = options.clientId;
+    this.clientSecret = options.clientSecret;
+    this.token = options.token || null;
+
+    //other options
+    this.buildCurl = options.buildCurl || false;
+    this.logging = options.logging || false;
+
+    //timeout and callbacks
+    this._callTimeout =  options.callTimeout || 30000; //default to 30 seconds
+    this._callTimeoutCallback =  options.callTimeoutCallback || null;
+    this.logoutCallback =  options.logoutCallback || null;
+  };
+
+  /*
+  *  Main function for making requests to the API.  Can be called directly.
+  *
+  *  options object:
+  *  `method` - http method (GET, POST, PUT, or DELETE), defaults to GET
+  *  `qs` - object containing querystring values to be appended to the uri
+  *  `body` - object containing entity body for POST and PUT requests
+  *  `endpoint` - API endpoint, for example 'users/fred'
+  *  `mQuery` - boolean, set to true if running management query, defaults to false
+  *
+  *  @method request
+  *  @public
+  *  @params {object} options
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.request = function (options, callback) {
+    var self = this;
+    var method = options.method || 'GET';
+    var endpoint = options.endpoint;
+    var body = options.body || {};
+    var qs = options.qs || {};
+    var mQuery = options.mQuery || false; //is this a query to the management endpoint?
+    var orgName = this.get('orgName');
+    var appName = this.get('appName');
+    if(!mQuery && !orgName && !appName){
+      if (typeof(this.logoutCallback) === 'function') {
+        return this.logoutCallback(true, 'no_org_or_app_name_specified');
+      }
+    }
+    var uri;
+    if (mQuery) {
+      uri = this.URI + '/' + endpoint;
+    } else {
+      uri = this.URI + '/' + orgName + '/' + appName + '/' + endpoint;
+    }
+
+    if (this.authType === AUTH_CLIENT_ID) {
+      qs['client_id'] = this.clientId;
+      qs['client_secret'] = this.clientSecret;
+    } else if (this.authType === AUTH_APP_USER && self.getToken()) {
+      qs['access_token'] = self.getToken();
+    }
+
+    if (this.logging) {
+      console.log('calling: ' + method + ' ' + uri);
+    }
+    this._start = new Date().getTime();
+    var callOptions = {
+      method: method,
+      uri: uri,
+      json: body,
+      qs: qs
+    };
+    request(callOptions, function (err, r, data) {
+
+      r.body = r.body || {};
+      data = data || {};
+
+      if (self.buildCurl) {
+        options.uri = r.request.uri.href;
+        self.buildCurlCall(options);
+      }
+      self._end = new Date().getTime();
+      if(r.statusCode === 200) {
+        if (self.logging) {
+          console.log('success (time: ' + self.calcTimeDiff() + '): ' + method + ' ' + uri);
+        }
+        callback(err, data);
+      } else {
+        err = true;
+        data.statusCode = r.statusCode;
+        if ((r.error === 'auth_expired_session_token') ||
+          (r.error === 'auth_missing_credentials')   ||
+          (r.error == 'auth_unverified_oath')       ||
+          (r.error === 'expired_token')   ||
+          (r.error === 'unauthorized')   ||
+          (r.error === 'auth_invalid')) {
+          //this error type means the user is not authorized. If a logout function is defined, call it
+          var error = r.body.error;
+          var errorDesc = r.body.error_description;
+          if (self.logging) {
+            console.log('Error (' + r.statusCode + ')(' + error + '): ' + errorDesc);
+          }
+          //if the user has specified a logout callback:
+          if (typeof(self.logoutCallback) === 'function') {
+            self.logoutCallback(err, data);
+          } else  if (typeof(callback) === 'function') {
+            callback(err, data);
+          }
+        } else {
+          var error = r.body.error;
+          var errorDesc = r.body.error_description;
+          if (self.logging) {
+            console.log('Error (' + r.statusCode + ')(' + error + '): ' + errorDesc);
+          }
+          if (typeof(callback) === 'function') {
+            callback(err, data);
+          }
+        }
+      }
+    });
+  };
+  /*
+   *  function for building asset urls
+   *
+   *  @method buildAssetURL
+   *  @public
+   *  @params {string} uuid
+   *  @return {string} assetURL
+   */
+   Usergrid.Client.prototype.buildAssetURL = function(uuid) {
+     var self = this;
+     var qs = {};
+     var assetURL = this.URI + '/' + this.orgName + '/' + this.appName + '/assets/' + uuid + '/data';
+
+     if (self.getToken()) {
+       qs['access_token'] = self.getToken();
+     }
+
+     //append params to the path
+     var encoded_params = encodeParams(qs);
+     if (encoded_params) {
+       assetURL += "?" + encoded_params;
+     }
+
+     return assetURL;
+   }
+
+  /*
+   *  Main function for creating new groups. Call this directly.
+   *
+   *  @method createGroup
+   *  @public
+   *  @params {string} path
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createGroup = function(options, callback) {
+    var getOnExist = options.getOnExist || false;
+    delete options.getOnExist;
+
+    var options = {
+      path: options.path,
+      client: this,
+      data:options
+    }
+
+    var group = new Usergrid.Group(options);
+    group.fetch(function(err, data){
+      var okToSave = (err && 'service_resource_not_found' === data.error || 'no_name_specified' === data.error || 'null_pointer' === data.error) || (!err && getOnExist);
+      if (okToSave) {
+        group.save(function(err, data){
+          if (typeof(callback) === 'function') {
+            callback(err, group);
+          }
+        });
+      } else {
+        if(typeof(callback) === 'function') {
+          callback(err, group);
+        }
+      }
+    });
+  }
+
+  /*
+  *  Main function for creating new entities - should be called directly.
+  *
+  *  options object: options {data:{'type':'collection_type', 'key':'value'}, uuid:uuid}}
+  *
+  *  @method createEntity
+  *  @public
+  *  @params {object} options
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.createEntity = function (options, callback) {
+    // todo: replace the check for new / save on not found code with simple save
+    // when users PUT on no user fix is in place.
+    /*
+    var options = {
+      client:this,
+      data:options
+    }
+    var entity = new Usergrid.Entity(options);
+    entity.save(function(err, data) {
+      if (typeof(callback) === 'function') {
+        callback(err, entity);
+      }
+    });
+    */
+    var getOnExist = options.getOnExist || false; //if true, will return entity if one already exists
+    delete options.getOnExist;
+
+    var options = {
+      client:this,
+      data:options
+    }
+    var entity = new Usergrid.Entity(options);
+    entity.fetch(function(err, data) {
+      //if the fetch doesn't find what we are looking for, or there is no error, do a save
+      var okToSave = (err && 'service_resource_not_found' === data.error || 'no_name_specified' === data.error || 'null_pointer' === data.error) || (!err && getOnExist);
+      if(okToSave) {
+        entity.set(options.data); //add the data again just in case
+        entity.save(function(err, data) {
+          if (typeof(callback) === 'function') {
+            callback(err, entity, data);
+          }
+        });
+      } else {
+        if (typeof(callback) === 'function') {
+          callback(err, entity, data);
+        }
+      }
+    });
+
+  }
+
+  /*
+   *  Main function for getting existing entities - should be called directly.
+   *
+   *  You must supply a uuid or (username or name). Username only applies to users.
+   *  Name applies to all custom entities
+   *
+   *  options object: options {data:{'type':'collection_type', 'name':'value', 'username':'value'}, uuid:uuid}}
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.getEntity = function (options, callback) {
+    var options = {
+      client:this,
+      data:options
+    }
+    var entity = new Usergrid.Entity(options);
+    entity.fetch(function(err, data) {
+      if (typeof(callback) === 'function') {
+        callback(err, entity, data);
+      }
+    });
+  }
+
+  /*
+   *  Main function for restoring an entity from serialized data.
+   *
+   *  serializedObject should have come from entityObject.serialize();
+   *
+   *  @method restoreEntity
+   *  @public
+   *  @param {string} serializedObject
+   *  @return {object} Entity Object
+   */
+  Usergrid.Client.prototype.restoreEntity = function (serializedObject) {
+    var data = JSON.parse(serializedObject);
+    var options = {
+      client:this,
+      data:data
+    }
+    var entity = new Usergrid.Entity(options);
+    return entity;
+  }
+
+  /*
+  *  Main function for creating new collections - should be called directly.
+  *
+  *  options object: options {client:client, type: type, qs:qs}
+  *
+  *  @method createCollection
+  *  @public
+  *  @params {object} options
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.createCollection = function (options, callback) {
+    options.client = this;
+    var collection = new Usergrid.Collection(options, function(err, data) {
+      if (typeof(callback) === 'function') {
+        callback(err, collection, data);
+      }
+    });
+  }
+
+  /*
+   *  Main function for restoring a collection from serialized data.
+   *
+   *  serializedObject should have come from collectionObject.serialize();
+   *
+   *  @method restoreCollection
+   *  @public
+   *  @param {string} serializedObject
+   *  @return {object} Collection Object
+   */
+  Usergrid.Client.prototype.restoreCollection = function (serializedObject) {
+    var data = JSON.parse(serializedObject);
+    data.client = this;
+    var collection = new Usergrid.Collection(data);
+    return collection;
+  }
+
+  /*
+   *  Main function for retrieving a user's activity feed.
+   *
+   *  @method getFeedForUser
+   *  @public
+   *  @params {string} username
+   *  @param {function} callback
+   *  @return {callback} callback(err, data, activities)
+   */
+  Usergrid.Client.prototype.getFeedForUser = function(username, callback) {
+    var options = {
+      method: "GET",
+      endpoint: "users/"+username+"/feed"
+    }
+
+    this.request(options, function(err, data){
+      if(typeof(callback) === "function") {
+        if(err) {
+          callback(err);
+        } else {
+          callback(err, data, data.entities);
+        }
+      }
+    });
+  }
+
+  /*
+  *  Function for creating new activities for the current user - should be called directly.
+  *
+  *  //user can be any of the following: "me", a uuid, a username
+  *  Note: the "me" alias will reference the currently logged in user (e.g. 'users/me/activties')
+  *
+  *  //build a json object that looks like this:
+  *  var options =
+  *  {
+  *    "actor" : {
+  *      "displayName" :"myusername",
+  *      "uuid" : "myuserid",
+  *      "username" : "myusername",
+  *      "email" : "myemail",
+  *      "picture": "http://path/to/picture",
+  *      "image" : {
+  *          "duration" : 0,
+  *          "height" : 80,
+  *          "url" : "http://www.gravatar.com/avatar/",
+  *          "width" : 80
+  *      },
+  *    },
+  *    "verb" : "post",
+  *    "content" : "My cool message",
+  *    "lat" : 48.856614,
+  *    "lon" : 2.352222
+  *  }
+  *
+  *  @method createEntity
+  *  @public
+  *  @params {string} user // "me", a uuid, or a username
+  *  @params {object} options
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.createUserActivity = function (user, options, callback) {
+    options.type = 'users/'+user+'/activities';
+    var options = {
+      client:this,
+      data:options
+    }
+    var entity = new Usergrid.Entity(options);
+    entity.save(function(err, data) {
+      if (typeof(callback) === 'function') {
+        callback(err, entity);
+      }
+    });
+  }
+
+  /*
+   *  Function for creating user activities with an associated user entity.
+   *
+   *  user object:
+   *  The user object passed into this function is an instance of Usergrid.Entity.
+   *
+   *  @method createUserActivityWithEntity
+   *  @public
+   *  @params {object} user
+   *  @params {string} content
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createUserActivityWithEntity = function(user, content, callback) {
+    var username = user.get("username");
+    var options = {
+      actor: {
+        "displayName":username,
+        "uuid":user.get("uuid"),
+        "username":username,
+        "email":user.get("email"),
+        "picture":user.get("picture"),
+        "image": {
+          "duration":0,
+          "height":80,
+          "url":user.get("picture"),
+          "width":80
+         },
+      },
+      "verb":"post",
+      "content":content };
+
+    this.createUserActivity(username, options, callback);
+
+  }
+
+  /*
+  *  A private method to get call timing of last call
+  */
+  Usergrid.Client.prototype.calcTimeDiff = function () {
+   var time = this._end - this._start;
+   return (time/1000).toFixed(2);
+  }
+
+  /*
+   *  A public method to store the OAuth token for later use - uses localstorage if available
+   *
+   *  @method setToken
+   *  @public
+   *  @params {string} token
+   *  @return none
+   */
+  Usergrid.Client.prototype.setToken = function (token) {
+    this.set('token', token);
+  }
+
+  /*
+   *  A public method to get the OAuth token
+   *
+   *  @method getToken
+   *  @public
+   *  @return {string} token
+   */
+  Usergrid.Client.prototype.getToken = function () {
+    return this.get('token');
+  }
+
+  Usergrid.Client.prototype.setObject = function(key, value) {
+    if (value) {
+      value = JSON.stringify(value);
+    }
+    this.set(key, value);
+  }
+
+  Usergrid.Client.prototype.set = function (key, value) {
+    var keyStore =  'apigee_' + key;
+    this[key] = value;
+    if(typeof(Storage)!=="undefined"){
+      if (value) {
+        localStorage.setItem(keyStore, value);
+      } else {
+        localStorage.removeItem(keyStore);
+      }
+    }
+  }
+
+  Usergrid.Client.prototype.getObject = function(key) {
+    return JSON.parse(this.get(key));
+  }
+
+  Usergrid.Client.prototype.get = function (key) {
+    var keyStore = 'apigee_' + key;
+    if (this[key]) {
+      return this[key];
+    } else if(typeof(Storage)!=="undefined") {
+      return localStorage.getItem(keyStore);
+    }
+    return null;
+  }
+
+  /*
+   * A public facing helper method for signing up users
+   *
+   * @method signup
+   * @public
+   * @params {string} username
+   * @params {string} password
+   * @params {string} email
+   * @params {string} name
+   * @param {function} callback
+   * @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.signup = function(username, password, email, name, callback) {
+    var self = this;
+    var options = {
+      type:"users",
+      username:username,
+      password:password,
+      email:email,
+      name:name
+    };
+
+    this.createEntity(options, callback);
+  }
+
+  /*
+  *
+  *  A public method to log in an app user - stores the token for later use
+  *
+  *  @method login
+  *  @public
+  *  @params {string} username
+  *  @params {string} password
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.login = function (username, password, callback) {
+    var self = this;
+    var options = {
+      method:'POST',
+      endpoint:'token',
+      body:{
+        username: username,
+        password: password,
+        grant_type: 'password'
+      }
+    };
+    this.request(options, function(err, data) {
+      var user = {};
+      if (err && self.logging) {
+        console.log('error trying to log user in');
+      } else {
+        var options = {
+          client:self,
+          data:data.user
+        }
+        user = new Usergrid.Entity(options);
+        self.setToken(data.access_token);
+        }
+      if (typeof(callback) === 'function') {
+        callback(err, data, user);
+      }
+    });
+  }
+
+
+  Usergrid.Client.prototype.reAuthenticateLite = function (callback) {
+    var self = this;
+    var options = {
+      method:'GET',
+      endpoint:'management/me',
+      mQuery:true
+    };
+    this.request(options, function(err, response) {
+      if (err && self.logging) {
+        console.log('error trying to re-authenticate user');
+      } else {
+
+        //save the re-authed token and current email/username
+        self.setToken(response.access_token);
+
+      }
+      if (typeof(callback) === 'function') {
+        callback(err);
+      }
+    });
+  }
+
+
+  Usergrid.Client.prototype.reAuthenticate = function (email, callback) {
+    var self = this;
+    var options = {
+      method:'GET',
+      endpoint:'management/users/'+email,
+      mQuery:true
+    };
+    this.request(options, function(err, response) {
+      var organizations = {};
+      var applications = {};
+      var user = {};
+      if (err && self.logging) {
+        console.log('error trying to full authenticate user');
+      } else {
+        var data = response.data;
+        self.setToken(data.token);
+        self.set('email', data.email);
+
+        //delete next block and corresponding function when iframes are refactored
+        localStorage.setItem('accessToken', data.token);
+        localStorage.setItem('userUUID', data.uuid);
+        localStorage.setItem('userEmail', data.email);
+        //end delete block
+
+
+        var userData = {
+          "username" : data.username,
+          "email" : data.email,
+          "name" : data.name,
+          "uuid" : data.uuid
+        }
+        var options = {
+          client:self,
+          data:userData
+        }
+        user = new Usergrid.Entity(options);
+
+        organizations = data.organizations;
+        var org = '';
+        try {
+          //if we have an org stored, then use that one. Otherwise, use the first one.
+          var existingOrg = self.get('orgName');
+          org = (organizations[existingOrg])?organizations[existingOrg]:organizations[Object.keys(organizations)[0]];
+          self.set('orgName', org.name);
+        } catch(e) {
+          err = true;
+          if (self.logging) { console.log('error selecting org'); }
+        } //should always be an org
+
+        applications = self.parseApplicationsArray(org);
+        self.selectFirstApp(applications);
+
+        self.setObject('organizations', organizations);
+        self.setObject('applications', applications);
+
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data, user, organizations, applications);
+      }
+    });
+  }
+
+  /*
+  *  A public method to log in an app user with facebook - stores the token for later use
+  *
+  *  @method loginFacebook
+  *  @public
+  *  @params {string} username
+  *  @params {string} password
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.loginFacebook = function (facebookToken, callback) {
+    var self = this;
+    var options = {
+      method:'GET',
+      endpoint:'auth/facebook',
+      qs:{
+        fb_access_token: facebookToken
+      }
+    };
+    this.request(options, function(err, data) {
+      var user = {};
+      if (err && self.logging) {
+        console.log('error trying to log user in');
+      } else {
+        var options = {
+          client: self,
+          data: data.user
+        }
+        user = new Usergrid.Entity(options);
+        self.setToken(data.access_token);
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data, user);
+      }
+    });
+  }
+
+  /*
+  *  A public method to get the currently logged in user entity
+  *
+  *  @method getLoggedInUser
+  *  @public
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Client.prototype.getLoggedInUser = function (callback) {
+    if (!this.getToken()) {
+      callback(true, null, null);
+    } else {
+      var self = this;
+      var options = {
+        method:'GET',
+        endpoint:'users/me'
+      };
+      this.request(options, function(err, data) {
+        if (err) {
+          if (self.logging) {
+            console.log('error trying to log user in');
+          }
+          if (typeof(callback) === 'function') {
+            callback(err, data, null);
+          }
+        } else {
+          var options = {
+            client:self,
+            data:data.entities[0]
+          }
+          var user = new Usergrid.Entity(options);
+          if (typeof(callback) === 'function') {
+            callback(err, data, user);
+          }
+        }
+      });
+    }
+  }
+
+  /*
+  *  A public method to test if a user is logged in - does not guarantee that the token is still valid,
+  *  but rather that one exists
+  *
+  *  @method isLoggedIn
+  *  @public
+  *  @return {boolean} Returns true the user is logged in (has token and uuid), false if not
+  */
+  Usergrid.Client.prototype.isLoggedIn = function () {
+    if (this.getToken()) {
+      return true;
+    }
+    return false;
+  }
+
+  /*
+  *  A public method to log out an app user - clears all user fields from client
+  *
+  *  @method logout
+  *  @public
+  *  @return none
+  */
+  Usergrid.Client.prototype.logout = function () {
+    this.setToken(null);
+  }
+
+  /*
+  *  A private method to build the curl call to display on the command line
+  *
+  *  @method buildCurlCall
+  *  @private
+  *  @param {object} options
+  *  @return {string} curl
+  */
+  Usergrid.Client.prototype.buildCurlCall = function (options) {
+    var curl = 'curl';
+    var method = (options.method || 'GET').toUpperCase();
+    var body = options.body || {};
+    var uri = options.uri;
+
+    //curl - add the method to the command (no need to add anything for GET)
+    if (method === 'POST') {curl += ' -X POST'; }
+    else if (method === 'PUT') { curl += ' -X PUT'; }
+    else if (method === 'DELETE') { curl += ' -X DELETE'; }
+    else { curl += ' -X GET'; }
+
+    //curl - append the path
+    curl += ' ' + uri;
+
+    //curl - add the body
+    body = JSON.stringify(body)//only in node module
+    if (body !== '"{}"' && method !== 'GET' && method !== 'DELETE') {
+      //curl - add in the json obj
+      curl += " -d '" + body + "'";
+    }
+
+    //log the curl command to the console
+    console.log(curl);
+
+    return curl;
+  }
+
+  Usergrid.Client.prototype.getDisplayImage = function (email, picture, size) {
+    try {
+      if (picture) {
+        return picture;
+      }
+      var size = size || 50;
+      if (email.length) {
+        return 'https://secure.gravatar.com/avatar/' + MD5(email) + '?s=' + size + encodeURI("&d=https://apigee.com/usergrid/images/user_profile.png");
+      } else {
+        return 'https://apigee.com/usergrid/images/user_profile.png';
+      }
+    } catch(e) {
+      return 'https://apigee.com/usergrid/images/user_profile.png';
+    }
+  }
+
+  /*
+  *  A class to Model a Usergrid Entity.
+  *  Set the type and uuid of entity in the 'data' json object
+  *
+  *  @constructor
+  *  @param {object} options {client:client, data:{'type':'collection_type', uuid:'uuid', 'key':'value'}}
+  */
+  Usergrid.Entity = function(options) {
+    if (options) {
+      this._data = options.data || {};
+      this._client = options.client || {};
+    }
+  };
+
+  /*
+   *  returns a serialized version of the entity object
+   *
+   *  Note: use the client.restoreEntity() function to restore
+   *
+   *  @method serialize
+   *  @return {string} data
+   */
+  Usergrid.Entity.prototype.serialize = function () {
+    return JSON.stringify(this._data);
+  }
+
+  /*
+  *  gets a specific field or the entire data object. If null or no argument
+  *  passed, will return all data, else, will return a specific field
+  *
+  *  @method get
+  *  @param {string} field
+  *  @return {string} || {object} data
+  */
+  Usergrid.Entity.prototype.get = function (field) {
+    if (field) {
+      return this._data[field];
+    } else {
+      return this._data;
+    }
+  }
+
+  /*
+  *  adds a specific key value pair or object to the Entity's data
+  *  is additive - will not overwrite existing values unless they
+  *  are explicitly specified
+  *
+  *  @method set
+  *  @param {string} key || {object}
+  *  @param {string} value
+  *  @return none
+  */
+  Usergrid.Entity.prototype.set = function (key, value) {
+    if (typeof key === 'object') {
+      for(var field in key) {
+        this._data[field] = key[field];
+      }
+    } else if (typeof key === 'string') {
+      if (value === null) {
+        delete this._data[key];
+      } else {
+        this._data[key] = value;
+      }
+    } else {
+      this._data = {};
+    }
+  }
+
+  /*
+  *  Saves the entity back to the database
+  *
+  *  @method save
+  *  @public
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Entity.prototype.save = function (callback) {
+    var type = this.get('type');
+    var method = 'POST';
+    if (isUUID(this.get('uuid'))) {
+      method = 'PUT';
+      type += '/' + this.get('uuid');
+    }
+
+    //update the entity
+    var self = this;
+    var data = {};
+    var entityData = this.get();
+    var password = this.get('password');
+    var oldpassword = this.get('oldpassword');
+    var newpassword = this.get('newpassword');
+    //remove system specific properties
+    for (var item in entityData) {
+      if (item === 'metadata' || item === 'created' || item === 'modified' ||
+          item === 'oldpassword' || item === 'newpassword' || //old and new pw not added to data
+      item === 'type' || item === 'activated' || item === 'uuid') {
+      continue;
+    }
+      data[item] = entityData[item];
+    }
+    var options =  {
+      method:method,
+      endpoint:type,
+      body:data
+    };
+    //save the entity first
+    this._client.request(options, function (err, retdata) {
+      //clear out pw info if present
+      self.set('password', null);
+      self.set('oldpassword', null);
+      self.set('newpassword', null);
+      if (err && self._client.logging) {
+        console.log('could not save entity');
+        if (typeof(callback) === 'function') {
+          return callback(err, retdata, self);
+        }
+      } else {
+        if (retdata.entities) {
+          if (retdata.entities.length) {
+            var entity = retdata.entities[0];
+            self.set(entity);
+            var path = retdata.path;
+            //for connections, API returns type
+            while (path.substring(0, 1) === "/") {
+              path = path.substring(1);
+            }
+            self.set('type', path);
+          }
+        }
+        //if this is a user, update the password if it has been specified;
+        var needPasswordChange = ((self.get('type') === 'user' || self.get('type') === 'users') && oldpassword && newpassword);
+        if (needPasswordChange) {
+          //Note: we have a ticket in to change PUT calls to /users to accept the password change
+          //      once that is done, we will remove this call and merge it all into one
+          var pwdata = {};
+          pwdata.oldpassword = oldpassword;
+          pwdata.newpassword = newpassword;
+          var options = {
+            method:'PUT',
+            endpoint:type+'/password',
+            body:pwdata
+          }
+          self._client.request(options, function (err, data) {
+            if (err && self._client.logging) {
+              console.log('could not update user');
+            }
+            //remove old and new password fields so they don't end up as part of the entity object
+            self.set('oldpassword', null);
+            self.set('newpassword', null);
+            if (typeof(callback) === 'function') {
+              callback(err, data, self);
+            }
+          });
+        } else if (typeof(callback) === 'function') {
+          callback(err, retdata, self);
+        }
+      }
+    });
+  }
+
+  /*
+  *  refreshes the entity by making a GET call back to the database
+  *
+  *  @method fetch
+  *  @public
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Entity.prototype.fetch = function (callback) {
+    var type = this.get('type');
+    var self = this;
+
+  //Check for an entity type, then if a uuid is available, use that, otherwise, use the name
+  try {
+    if (type === undefined) {
+      throw 'cannot fetch entity, no entity type specified'
+    } else if (this.get('uuid')) {
+      type += '/' + this.get('uuid');
+    } else if (type === 'users' && this.get('username')) {
+      type += '/' + this.get('username');
+    } else if (this.get('name')) {
+      type += '/' + encodeURIComponent(this.get('name'));
+    } else if (typeof(callback) === 'function') {
+      throw 'no_name_specified';
+    }
+  } catch (e) {
+    if (self._client.logging) {
+      console.log(e);
+    }
+    return callback(true, {
+      error: e
+    }, self);
+  }
+    var options = {
+      method:'GET',
+      endpoint:type
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('could not get entity');
+      } else {
+        if (data.user) {
+          self.set(data.user);
+          self._json = JSON.stringify(data.user, null, 2);
+        } else if (data.entities) {
+          if (data.entities.length) {
+            var entity = data.entities[0];
+            self.set(entity);
+          }
+        }
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data, self);
+      }
+    });
+  }
+
+  /*
+  *  deletes the entity from the database - will only delete
+  *  if the object has a valid uuid
+  *
+  *  @method destroy
+  *  @public
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  *
+  */
+  Usergrid.Entity.prototype.destroy = function (callback) {
+    var self = this;
+    var type = this.get('type');
+    var id = this.getEntityId(this);
+    if (!id) {
+      if (typeof(callback) === 'function') {
+        var error = 'Error trying to delete object - no uuid or name specified.';
+        if (self._client.logging) {
+          console.log(error);
+        }
+        return callback(true, error);
+      }
+    }
+    type += '/' + this.get('uuid');
+    var options = {
+      method:'DELETE',
+      endpoint:type
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('entity could not be deleted');
+      } else {
+        self.set(null);
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data);
+      }
+    });
+  }
+
+  /*
+  *  connects one entity to another
+  *
+  *  @method connect
+  *  @public
+  *  @param {string} connection
+  *  @param {object} entity
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  *
+  */
+  Usergrid.Entity.prototype.connect = function (connection, entity, callback) {
+
+    var self = this;
+
+    //connectee info
+    var connecteeType = entity.get('type');
+    var connectee = this.getEntityId(entity);
+    if (!connectee) {
+      if (typeof(callback) === 'function') {
+        var error = 'Error trying to connect object - no uuid specified.';
+        if (self._client.logging) {
+          console.log(error);
+        }
+        callback(true, error);
+      }
+      return;
+    }
+
+    //connector info
+    var connectorType = this.get('type');
+    var connector = this.getEntityId(this);
+    if (!connector) {
+      if (typeof(callback) === 'function') {
+        var error = 'Error in connect - no uuid specified.';
+        if (self._client.logging) {
+          console.log(error);
+        }
+        callback(true, error);
+      }
+      return;
+    }
+
+    var endpoint = connectorType + '/' + connector + '/' + connection + '/' + connecteeType + '/' + connectee;
+    var options = {
+      method:'POST',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('entity could not be connected');
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data);
+      }
+    });
+  }
+
+  /*
+  *  returns a unique identifier for an entity
+  *
+  *  @method connect
+  *  @public
+  *  @param {object} entity
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  *
+  */
+  Usergrid.Entity.prototype.getEntityId = function (entity) {
+    return entity.get('uuid') || entity.get('username') || entity.get('name') || false;
+  }
+
+  /*
+  *  gets an entities connections
+  *
+  *  @method getConnections
+  *  @public
+  *  @param {string} connection
+  *  @param {opts} options (actually, just options.qs for now)
+  *  @param {function} callback
+  *  @return {callback} callback(err, data, connections)
+  *
+  */
+  Usergrid.Entity.prototype.getConnections = function (connection, opts, callback) {
+
+    if (typeof(opts) == "function") { callback = opts; opts = undefined; }
+
+    var self = this;
+
+    //connector info
+    var connectorType = inflection.pluralize(this.get('type'));
+    var connector = this.getEntityId(this);
+    if (!connector) {
+      if (typeof(callback) === 'function') {
+        var error = 'Error in getConnections - no uuid specified.';
+        if (self._client.logging) {
+          console.log(error);
+        }
+        callback(true, error);
+      }
+      return;
+    }
+
+    var endpoint = connectorType + '/' + connector + '/' + connection + '/';
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    if (opts && opts.qs) { options.qs = opts.qs; }
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('entity connections could not be retrieved');
+      }
+
+      self[connection] = {};
+
+      var length = data.entities.length;
+      for (var i=0;i<length;i++)
+      {
+        if (data.entities[i].type === 'user'){
+          self[connection][data.entities[i].username] = data.entities[i];
+        } else {
+          self[connection][data.entities[i].name] = data.entities[i];
+        }
+      }
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+  Usergrid.Entity.prototype.getGroups = function (callback) {
+
+    var self = this;
+
+    var endpoint = 'users' + '/' + this.get('uuid') + '/groups' ;
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('entity could not be connected');
+      }
+
+      self['groups'] = data.entities;
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+  Usergrid.Entity.prototype.getActivities = function (callback) {
+
+    var self = this;
+
+    var endpoint = this.get('type') + '/' + this.get('uuid') + '/activities' ;
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('entity could not be connected');
+      }
+
+      for(entity in data.entities) {
+        data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+      }
+
+      self['activities'] = data.entities;
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+  Usergrid.Entity.prototype.getFollowing = function (callback) {
+
+    var self = this;
+
+    var endpoint = 'users' + '/' + this.get('uuid') + '/following' ;
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('could not get user following');
+      }
+
+      for(entity in data.entities) {
+        data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+        var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+        data.entities[entity]._portal_image_icon =  image;
+      }
+
+      self['following'] = data.entities;
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+
+  Usergrid.Entity.prototype.getFollowers = function (callback) {
+
+    var self = this;
+
+    var endpoint = 'users' + '/' + this.get('uuid') + '/followers' ;
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('could not get user followers');
+      }
+
+      for(entity in data.entities) {
+        data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+        var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+        data.entities[entity]._portal_image_icon =  image;
+      }
+
+      self['followers'] = data.entities;
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+  Usergrid.Entity.prototype.getRoles = function (callback) {
+
+    var self = this;
+
+    var endpoint = this.get('type') + '/' + this.get('uuid') + '/roles' ;
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('could not get user roles');
+      }
+
+      self['roles'] = data.entities;
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+  Usergrid.Client.prototype.createRole = function(roleName, permissions, callback) {
+
+      var self = this;
+      var options = {
+          type: 'role',
+          name: roleName
+      };
+
+      this.createEntity(options, function(err, entity, response) {
+          if (err) {
+              callback (err, response, self);
+          } else {
+              entity.assignPermissions(permissions, function (err, data) {
+                  if (err) {
+                      callback (err, response, self);
+                  } else {
+                      callback (err, data, data.data);
+                  }
+              })
+          }
+      });
+
+  };
+  Usergrid.Entity.prototype.assignRole = function(roleName, callback) {
+
+      var self = this;
+      var type = self.get('type');
+      var collection = type + 's';
+      var entityID;
+
+      if (type == 'user' && this.get('username') != null) {
+          entityID = self.get('username');
+      } else if (type == 'group' && this.get('name') != null) {
+          entityID = self.get('name');
+      } else if (this.get('uuid') != null) {
+          entityID = self.get('uuid');
+      }
+
+      if (type != 'users' && type != 'groups') {
+          callback ('entity must be a group or user', null, this);
+      }
+
+      var endpoint = 'roles/' + roleName + '/' + collection + '/' + entityID;
+      var options = {
+          method: 'POST',
+          endpoint: endpoint
+      };
+
+      this._client.request(options, function(err, response) {
+          if (err) {
+              console.log('Could not assign role.');
+          }
+          callback (err, response, self);
+      });
+
+  };
+
+  Usergrid.Entity.prototype.removeRole = function(roleName, callback) {
+
+      var self = this;
+      var type = self.get('type');
+      var collection = type + 's';
+      var entityID;
+
+      if (type == 'user' && this.get('username') != null) {
+          entityID = this.get('username');
+      } else if (type == 'group' && this.get('name') != null) {
+          entityID = this.get('name');
+      } else if (this.get('uuid') != null) {
+          entityID = this.get('uuid');
+      }
+
+      if (type != 'users' && type != 'groups') {
+          callback ('entity must be a group or user', null, this);
+      }
+
+      var endpoint = 'roles/' + roleName + '/' + collection + '/' + entityID;
+      var options = {
+          method: 'DELETE',
+          endpoint: endpoint
+      };
+
+      this._client.request(options, function(err, response) {
+          if (err) {
+              console.log('Could not assign role.');
+          }
+          callback (err, response, self);
+      });
+
+  };
+
+  Usergrid.Entity.prototype.assignPermissions = function(permissions, callback) {
+      var self = this;
+      var entityID;
+      var type = this.get("type");
+
+      if (type != 'user' && type != 'users' && type != 'group' && type != 'groups') {
+          callback( 'entity must be a group or user', null, this);
+      }
+
+      if (type == 'user' && this.get("username") != null) {
+          entityID = this.get("username");
+      } else if (type == 'group' && this.get("name") != null) {
+          entityID = this.get("name");
+      } else if (this.get("uuid") != null) {
+          entityID = this.get("uuid");
+      }
+
+      var endpoint = type + "/" + entityID + "/permissions";
+      var options = {
+          method: "POST",
+          endpoint: endpoint,
+          body: {
+              'permission': permissions
+          }
+      };
+      this._client.request(options, function(err, data) {
+          if (err && self._client.logging) {
+              console.log("could not assign permissions");
+          }
+          callback (err, data, data.data);
+      });
+  };
+
+  Usergrid.Entity.prototype.removePermissions = function(permissions, callback) {
+      var self = this;
+      var entityID;
+      var type = this.get("type");
+
+      if (type != 'user' && type != 'users' && type != 'group' && type != 'groups') {
+          callback ('entity must be a group or user', null, this);
+      }
+
+      if (type == 'user' && this.get("username") != null) {
+          entityID = this.get("username");
+      } else if (type == 'group' && this.get("name") != null) {
+          entityID = this.get("name");
+      } else if (this.get("uuid") != null) {
+          entityID = this.get("uuid");
+      }
+
+      var endpoint = type + "/" + entityID + "/permissions";
+      var options = {
+          method: "DELETE",
+          endpoint: endpoint,
+          qs: {
+              'permission': permissions
+          }
+      };
+      this._client.request(options, function(err, data) {
+          if (err && self._client.logging) {
+              console.log("could not remove permissions");
+          }
+          callback (err, data, data.params.permission);
+      });
+  };
+  Usergrid.Entity.prototype.getPermissions = function (callback) {
+
+    var self = this;
+
+    var endpoint = this.get('type') + '/' + this.get('uuid') + '/permissions' ;
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('could not get user permissions');
+      }
+
+      var permissions = [];
+      if (data.data) {
+        var perms = data.data;
+        var count = 0;
+
+        for (var i in perms) {
+          count++;
+          var perm = perms[i];
+          var parts = perm.split(':');
+          var ops_part = "";
+          var path_part = parts[0];
+
+          if (parts.length > 1) {
+            ops_part = parts[0];
+            path_part = parts[1];
+          }
+
+          ops_part.replace("*", "get,post,put,delete")
+          var ops = ops_part.split(',');
+          var ops_object = {}
+          ops_object['get'] = 'no';
+          ops_object['post'] = 'no';
+          ops_object['put'] = 'no';
+          ops_object['delete'] = 'no';
+          for (var j in ops) {
+            ops_object[ops[j]] = 'yes';
+          }
+
+          permissions.push( {operations : ops_object, path : path_part, perm : perm});
+        }
+      }
+
+      self['permissions'] = permissions;
+
+      if (typeof(callback) === 'function') {
+        callback(err, data, data.entities);
+      }
+    });
+
+  }
+
+  /*
+  *  disconnects one entity from another
+  *
+  *  @method disconnect
+  *  @public
+  *  @param {string} connection
+  *  @param {object} entity
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  *
+  */
+  Usergrid.Entity.prototype.disconnect = function (connection, entity, callback) {
+
+    var self = this;
+
+    //connectee info
+    var connecteeType = entity.get('type');
+    var connectee = this.getEntityId(entity);
+    if (!connectee) {
+      if (typeof(callback) === 'function') {
+        var error = 'Error trying to delete object - no uuid specified.';
+        if (self._client.logging) {
+          console.log(error);
+        }
+        callback(true, error);
+      }
+      return;
+    }
+
+    //connector info
+    var connectorType = this.get('type');
+    var connector = this.getEntityId(this);
+    if (!connector) {
+      if (typeof(callback) === 'function') {
+        var error = 'Error in connect - no uuid specified.';
+        if (self._client.logging) {
+          console.log(error);
+        }
+        callback(true, error);
+      }
+      return;
+    }
+
+    var endpoint = connectorType + '/' + connector + '/' + connection + '/' + connecteeType + '/' + connectee;
+    var options = {
+      method:'DELETE',
+      endpoint:endpoint
+    };
+    this._client.request(options, function (err, data) {
+      if (err && self._client.logging) {
+        console.log('entity could not be disconnected');
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data);
+      }
+    });
+  }
+
+/*
+ *  calls delete on the database w/ the passed query
+ *
+ *  @method delete
+ *  @param {opts} options containing query (include options.qs)
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Client.prototype.delete = function(opts, callback) {
+  if (typeof(opts) == "function") { callback = opts; opts = undefined; }
+
+  if (!opts.qs.q) { opts.qs.q = '*'; }
+
+  var options = {
+    method: 'DELETE',
+    endpoint: opts.type,
+    qs: opts.qs
+  };
+  var self = this;
+  this.request(options, function (err, data) {
+    if (err && self.logging) {
+      console.log('entities could not be deleted');
+    }
+    if (typeof(callback) === 'function') {
+      callback(err, data);
+    }
+  });
+};
+
+  /*
+  *  The Collection class models Usergrid Collections.  It essentially
+  *  acts as a container for holding Entity objects, while providing
+  *  additional funcitonality such as paging, and saving
+  *
+  *  @constructor
+  *  @param {string} options - configuration object
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Collection = function(options, callback) {
+
+    if (options) {
+      this._client = options.client;
+      this._type = options.type;
+      this.qs = options.qs || {};
+
+      //iteration
+      this._list = options.list || [];
+      this._iterator = options.iterator || -1; //first thing we do is increment, so set to -1
+
+      //paging
+      this._previous = options.previous || [];
+      this._next = options.next || null;
+      this._cursor = options.cursor || null;
+
+      //restore entities if available
+      if (options.list) {
+        var count = options.list.length;
+        for(var i=0;i<count;i++){
+          //make new entity with
+          var entity = this._client.restoreEntity(options.list[i]);
+          this._list[i] = entity;
+        }
+      }
+    }
+    if (callback) {
+      //populate the collection
+      this.fetch(callback);
+    }
+
+  }
+
+
+  /*
+   *  gets the data from the collection object for serialization
+   *
+   *  @method serialize
+   *  @return {object} data
+   */
+  Usergrid.Collection.prototype.serialize = function () {
+
+    //pull out the state from this object and return it
+    var data = {}
+    data.type = this._type;
+    data.qs = this.qs;
+    data.iterator = this._iterator;
+    data.previous = this._previous;
+    data.next = this._next;
+    data.cursor = this._cursor;
+
+    this.resetEntityPointer();
+    var i=0;
+    data.list = [];
+    while(this.hasNextEntity()) {
+      var entity = this.getNextEntity();
+      data.list[i] = entity.serialize();
+      i++;
+    }
+
+    data = JSON.stringify(data);
+    return data;
+  }
+
+  Usergrid.Collection.prototype.addCollection = function (collectionName, options, callback) {
+    self = this;
+    options.client = this._client;
+    var collection = new Usergrid.Collection(options, function(err, data) {
+      if (typeof(callback) === 'function') {
+
+        collection.resetEntityPointer();
+        while(collection.hasNextEntity()) {
+          var user = collection.getNextEntity();
+          var email = user.get('email');
+          var image = self._client.getDisplayImage(user.get('email'), user.get('picture'));
+          user._portal_image_icon = image;
+        }
+
+        self[collectionName] = collection;
+        callback(err, collection);
+      }
+    });
+  }
+
+  /*
+  *  Populates the collection from the server
+  *
+  *  @method fetch
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Collection.prototype.fetch = function (callback) {
+    var self = this;
+    var qs = this.qs;
+
+    //add in the cursor if one is available
+    if (this._cursor) {
+      qs.cursor = this._cursor;
+    } else {
+      delete qs.cursor;
+    }
+    var options = {
+      method:'GET',
+      endpoint:this._type,
+      qs:this.qs
+    };
+    this._client.request(options, function (err, data) {
+      if(err && self._client.logging) {
+       console.log('error getting collection');
+      } else {
+        //save the cursor if there is one
+        var cursor = data.cursor || null;
+        self.saveCursor(cursor);
+        if (data.entities) {
+          self.resetEntityPointer();
+          var count = data.entities.length;
+          //save entities locally
+          self._list = []; //clear the local list first
+          for (var i=0;i<count;i++) {
+            var uuid = data.entities[i].uuid;
+            if (uuid) {
+              var entityData = data.entities[i] || {};
+              self._baseType = data.entities[i].type; //store the base type in the collection
+              entityData.type = self._type;//make sure entities are same type (have same path) as parent collection.
+              var entityOptions = {
+                type:self._type,
+                client:self._client,
+                uuid:uuid,
+                data:entityData
+              };
+
+              var ent = new Usergrid.Entity(entityOptions);
+              ent._json = JSON.stringify(entityData, null, 2);
+              var ct = self._list.length;
+              self._list[ct] = ent;
+            }
+          }
+        }
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, data);
+      }
+    });
+  }
+
+  /*
+  *  Adds a new Entity to the collection (saves, then adds to the local object)
+  *
+  *  @method addNewEntity
+  *  @param {object} entity
+  *  @param {function} callback
+  *  @return {callback} callback(err, data, entity)
+  */
+  Usergrid.Collection.prototype.addEntity = function (options, callback) {
+    var self = this;
+    options.type = this._type;
+
+    //create the new entity
+    this._client.createEntity(options, function (err, entity) {
+      if (!err) {
+        //then add the entity to the list
+        var count = self._list.length;
+        self._list[count] = entity;
+      }
+      if (typeof(callback) === 'function') {
+        callback(err, entity);
+      }
+    });
+  }
+
+  Usergrid.Collection.prototype.addExistingEntity = function (entity) {
+    //entity should already exist in the db, so just add it to the list
+    var count = this._list.length;
+    this._list[count] = entity;
+  }
+
+  /*
+  *  Removes the Entity from the collection, then destroys the object on the server
+  *
+  *  @method destroyEntity
+  *  @param {object} entity
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Collection.prototype.destroyEntity = function (entity, callback) {
+    var self = this;
+    entity.destroy(function(err, data) {
+      if (err) {
+        if (self._client.logging) {
+          console.log('could not destroy entity');
+        }
+        if (typeof(callback) === 'function') {
+          callback(err, data);
+        }
+      } else {
+        //destroy was good, so repopulate the collection
+        self.fetch(callback);
+      }
+    });
+    //remove entity from the local store
+    this.removeEntity(entity);
+  }
+
+
+  Usergrid.Collection.prototype.removeEntity = function (entity) {
+    var uuid = entity.get('uuid');
+    for (key in this._list) {
+      var listItem = this._list[key];
+      if (listItem.get('uuid') === uuid) {
+        return this._list.splice(key, 1);
+      }
+    }
+    return false;
+  }
+
+  /*
+  *  Looks up an Entity by UUID
+  *
+  *  @method getEntityByUUID
+  *  @param {string} UUID
+  *  @param {function} callback
+  *  @return {callback} callback(err, data, entity)
+  */
+  Usergrid.Collection.prototype.getEntityByUUID = function (uuid, callback) {
+
+    for (key in this._list) {
+      var listItem = this._list[key];
+      if (listItem.get('uuid') === uuid) {
+        return listItem;
+      }
+    }
+
+    //get the entity from the database
+    var options = {
+      data: {
+        type: this._type,
+        uuid:uuid
+      },
+      client: this._client
+    }
+    var entity = new Usergrid.Entity(options);
+    entity.fetch(callback);
+  }
+
+  /*
+  *  Returns the first Entity of the Entity list - does not affect the iterator
+  *
+  *  @method getFirstEntity
+  *  @return {object} returns an entity object
+  */
+  Usergrid.Collection.prototype.getFirstEntity = function () {
+    var count = this._list.length;
+    if (count > 0) {
+      return this._list[0];
+    }
+    return null;
+  }
+
+  /*
+  *  Returns the last Entity of the Entity list - does not affect the iterator
+  *
+  *  @method getLastEntity
+  *  @return {object} returns an entity object
+  */
+  Usergrid.Collection.prototype.getLastEntity = function () {
+    var count = this._list.length;
+    if (count > 0) {
+      return this._list[count-1];
+    }
+    return null;
+  }
+
+  /*
+  *  Entity iteration -Checks to see if there is a "next" entity
+  *  in the list.  The first time this method is called on an entity
+  *  list, or after the resetEntityPointer method is called, it will
+  *  return true referencing the first entity in the list
+  *
+  *  @method hasNextEntity
+  *  @return {boolean} true if there is a next entity, false if not
+  */
+  Usergrid.Collection.prototype.hasNextEntity = function () {
+    var next = this._iterator + 1;
+    var hasNextElement = (next >=0 && next < this._list.length);
+    if(hasNextElement) {
+      return true;
+    }
+    return false;
+  }
+
+  /*
+  *  Entity iteration - Gets the "next" entity in the list.  The first
+  *  time this method is called on an entity list, or after the method
+  *  resetEntityPointer is called, it will return the,
+  *  first entity in the list
+  *
+  *  @method hasNextEntity
+  *  @return {object} entity
+  */
+  Usergrid.Collection.prototype.getNextEntity = function () {
+    this._iterator++;
+    var hasNextElement = (this._iterator >= 0 && this._iterator <= this._list.length);
+    if(hasNextElement) {
+      return this._list[this._iterator];
+    }
+    return false;
+  }
+
+  /*
+  *  Entity iteration - Checks to see if there is a "previous"
+  *  entity in the list.
+  *
+  *  @method hasPrevEntity
+  *  @return {boolean} true if there is a previous entity, false if not
+  */
+  Usergrid.Collection.prototype.hasPrevEntity = function () {
+    var previous = this._iterator - 1;
+    var hasPreviousElement = (previous >=0 && previous < this._list.length);
+    if(hasPreviousElement) {
+      return true;
+    }
+    return false;
+  }
+
+  /*
+  *  Entity iteration - Gets the "previous" entity in the list.
+  *
+  *  @method getPrevEntity
+  *  @return {object} entity
+  */
+  Usergrid.Collection.prototype.getPrevEntity = function () {
+     this._iterator--;
+     var hasPreviousElement = (this._iterator >= 0 && this._iterator <= this._list.length);
+     if(hasPreviousElement) {
+      return this.list[this._iterator];
+     }
+     return false;
+  }
+
+  /*
+  *  Entity iteration - Resets the iterator back to the beginning
+  *  of the list
+  *
+  *  @method resetEntityPointer
+  *  @return none
+  */
+  Usergrid.Collection.prototype.resetEntityPointer = function () {
+     this._iterator  = -1;
+  }
+
+  /*
+  * Method to save off the cursor just returned by the last API call
+  *
+  * @public
+  * @method saveCursor
+  * @return none
+  */
+  Usergrid.Collection.prototype.saveCursor = function(cursor) {
+    //if current cursor is different, grab it for next cursor
+    if (this._next !== cursor) {
+      this._next = cursor;
+    }
+  }
+
+  /*
+  * Resets the paging pointer (back to original page)
+  *
+  * @public
+  * @method resetPaging
+  * @return none
+  */
+  Usergrid.Collection.prototype.resetPaging = function() {
+    this._previous = [];
+    this._next = null;
+    this._cursor = null;
+  }
+
+  /*
+  *  Paging -  checks to see if there is a next page od data
+  *
+  *  @method hasNextPage
+  *  @return {boolean} returns true if there is a next page of data, false otherwise
+  */
+  Usergrid.Collection.prototype.hasNextPage = function () {
+    return !!this._next;
+  }
+
+  /*
+  *  Paging - advances the cursor and gets the next
+  *  page of data from the API.  Stores returned entities
+  *  in the Entity list.
+  *
+  *  @method getNextPage
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Collection.prototype.getNextPage = function (callback) {
+    if (this.hasNextPage()) {
+      //set the cursor to the next page of data
+      this._previous.push(this._cursor);
+      this._cursor = this._next;
+      //empty the list
+      this._list = [];
+      this.fetch(callback);
+    }
+  }
+
+  /*
+  *  Paging -  checks to see if there is a previous page od data
+  *
+  *  @method hasPreviousPage
+  *  @return {boolean} returns true if there is a previous page of data, false otherwise
+  */
+  Usergrid.Collection.prototype.hasPreviousPage = function () {
+    return (this._previous.length > 0);
+  }
+
+  /*
+  *  Paging - reverts the cursor and gets the previous
+  *  page of data from the API.  Stores returned entities
+  *  in the Entity list.
+  *
+  *  @method getPreviousPage
+  *  @param {function} callback
+  *  @return {callback} callback(err, data)
+  */
+  Usergrid.Collection.prototype.getPreviousPage = function (callback) {
+    if (this.hasPreviousPage()) {
+      this._next=null; //clear out next so the comparison will find the next item
+      this._cursor = this._previous.pop();
+      //empty the list
+      this._list = [];
+      this.fetch(callback);
+    }
+  }
+
+
+  /*
+   *  A class to model a Usergrid group.
+   *  Set the path in the options object.
+   *
+   *  @constructor
+   *  @param {object} options {client:client, data: {'key': 'value'}, path:'path'}
+   */
+  Usergrid.Group = function(options, callback) {
+    this._path = options.path;
+    this._list = [];
+    this._client = options.client;
+    this._data = options.data || {};
+    this._data.type = "groups";
+  }
+
+  /*
+   *  Inherit from Usergrid.Entity.
+   *  Note: This only accounts for data on the group object itself.
+   *  You need to use add and remove to manipulate group membership.
+   */
+  Usergrid.Group.prototype = new Usergrid.Entity();
+
+  /*
+  *  Fetches current group data, and members.
+  *
+  *  @method fetch
+  *  @public
+  *  @param {function} callback
+  *  @returns {function} callback(err, data)
+  */
+  Usergrid.Group.prototype.fetch = function(callback) {
+    var self = this;
+    var groupEndpoint = 'groups/'+this._path;
+    var memberEndpoint = 'groups/'+this._path+'/users';
+
+    var groupOptions = {
+      method:'GET',
+      endpoint:groupEndpoint
+    }
+
+    var memberOptions = {
+      method:'GET',
+      endpoint:memberEndpoint
+    }
+
+    this._client.request(groupOptions, function(err, data){
+      if(err) {
+        if(self._client.logging) {
+          console.log('error getting group');
+        }
+        if(typeof(callback) === 'function') {
+          callback(err, data);
+        }
+      } else {
+        if(data.entities) {
+          var groupData = data.entities[0];
+          self._data = groupData || {};
+          self._client.request(memberOptions, function(err, data) {
+            if(err && self._client.logging) {
+              console.log('error getting group users');
+            } else {
+              if(data.entities) {
+                var count = data.entities.length;
+                self._list = [];
+                for (var i = 0; i < count; i++) {
+                  var uuid = data.entities[i].uuid;
+                  if(uuid) {
+                    var entityData = data.entities[i] || {};
+                    var entityOptions = {
+                      type: entityData.type,
+                      client: self._client,
+                      uuid:uuid,
+                      data:entityData
+                    };
+                    var entity = new Usergrid.Entity(entityOptions);
+                    self._list.push(entity);
+                  }
+
+                }
+              }
+            }
+            if(typeof(callback) === 'function') {
+              callback(err, data, self._list);
+            }
+          });
+        }
+      }
+    });
+  }
+
+  /*
+   *  Retrieves the members of a group.
+   *
+   *  @method members
+   *  @public
+   *  @param {function} callback
+   *  @return {function} callback(err, data);
+   */
+  Usergrid.Group.prototype.members = function(callback) {
+    if(typeof(callback) === 'function') {
+      callback(null, this._list);
+    }
+  }
+
+  /*
+   *  Adds a user to the group, and refreshes the group object.
+   *
+   *  Options object: {user: user_entity}
+   *
+   *  @method add
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {function} callback(err, data)
+   */
+  Usergrid.Group.prototype.add = function(options, callback) {
+    var self = this;
+    var options = {
+      method:"POST",
+      endpoint:"groups/"+this._path+"/users/"+options.user.get('username')
+    }
+
+    this._client.request(options, function(error, data){
+      if(error) {
+        if(typeof(callback) === 'function') {
+          callback(error, data, data.entities);
+        }
+      } else {
+        self.fetch(callback);
+      }
+    });
+  }
+
+  /*
+   *  Removes a user from a group, and refreshes the group object.
+   *
+   *  Options object: {user: user_entity}
+   *
+   *  @method remove
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {function} callback(err, data)
+   */
+  Usergrid.Group.prototype.remove = function(options, callback) {
+    var self = this;
+
+    var options = {
+      method:"DELETE",
+      endpoint:"groups/"+this._path+"/users/"+options.user.username
+    }
+
+    this._client.request(options, function(error, data){
+      if(error) {
+        if(typeof(callback) === 'function') {
+          callback(error, data);
+        }
+      } else {
+        self.fetch(callback);
+      }
+    });
+  }
+
+  /*
+  * Gets feed for a group.
+  *
+  * @public
+  * @method feed
+  * @param {function} callback
+  * @returns {callback} callback(err, data, activities)
+  */
+  Usergrid.Group.prototype.feed = function(callback) {
+    var self = this;
+
+    var endpoint = "groups/"+this._path+"/feed";
+
+    var options = {
+      method:"GET",
+      endpoint:endpoint
+    }
+
+    this._client.request(options, function(err, data){
+      if (err && self.logging) {
+        console.log('error trying to log user in');
+      }
+      if(typeof(callback) === 'function') {
+          callback(err, data, data.entities);
+      }
+    });
+  }
+
+  /*
+  * Creates activity and posts to group feed.
+  *
+  * options object: {user: user_entity, content: "activity content"}
+  *
+  * @public
+  * @method createGroupActivity
+  * @params {object} options
+  * @param {function} callback
+  * @returns {callback} callback(err, entity)
+  */
+  Usergrid.Group.prototype.createGroupActivity = function(options, callback){
+    var user = options.user;
+    var options = {
+      actor: {
+        "displayName":user.get("username"),
+        "uuid":user.get("uuid"),
+        "username":user.get("username"),
+        "email":user.get("email"),
+        "picture":user.get("picture"),
+        "image": {
+          "duration":0,
+          "height":80,
+          "url":user.get("picture"),
+          "width":80
+         },
+      },
+      "verb":"post",
+      "content":options.content };
+
+      options.type = 'groups/'+this._path+'/activities';
+      var options = {
+        client:this._client,
+        data:options
+      }
+
+      var entity = new Usergrid.Entity(options);
+      entity.save(function(err, data) {
+        if (typeof(callback) === 'function') {
+          callback(err, entity);
+        }
+      });
+  }
+
+  /*
+  * Tests if the string is a uuid
+  *
+  * @public
+  * @method isUUID
+  * @param {string} uuid The string to test
+  * @returns {Boolean} true if string is uuid
+  */
+  function isUUID (uuid) {
+    var uuidValueRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
+    if (!uuid) return false;
+    return uuidValueRegex.test(uuid);
+  }
+
+  /*
+  *  method to encode the query string parameters
+  *
+  *  @method encodeParams
+  *  @public
+  *  @params {object} params - an object of name value pairs that will be urlencoded
+  *  @return {string} Returns the encoded string
+  */
+  function encodeParams (params) {
+    tail = [];
+    var item = [];
+    if (params instanceof Array) {
+      for (i in params) {
+        item = params[i];
+        if ((item instanceof Array) && (item.length > 1)) {
+          tail.push(item[0] + "=" + encodeURIComponent(item[1]));
+        }
+      }
+    } else {
+      for (var key in params) {
+        if (params.hasOwnProperty(key)) {
+          var value = params[key];
+          if (value instanceof Array) {
+            for (i in value) {
+              item = value[i];
+              tail.push(key + "=" + encodeURIComponent(item));
+            }
+          } else {
+            tail.push(key + "=" + encodeURIComponent(value));
+          }
+        }
+      }
+    }
+    return tail.join("&");
+  }
+/*
+ *  A class to model a Usergrid event.
+ *
+ *  @constructor
+ *  @param {object} options {timestamp:0, category:'value', counters:{name : value}}
+ *  @returns {callback} callback(err, event)
+ */
+Usergrid.Counter = function(options, callback) {
+  var self=this;
+  this._client = options.client;
+  this._data = options.data || {};
+  this._data.category = options.category||"UNKNOWN";
+  this._data.timestamp = options.timestamp||0;
+  this._data.type = "events";
+  this._data.counters=options.counters||{};
+  if(typeof(callback) === 'function') {
+    callback.call(self, false, self);
+  }
+  //this.save(callback);
+};
+var COUNTER_RESOLUTIONS=[
+  'all', 'minute', 'five_minutes', 'half_hour',
+  'hour', 'six_day', 'day', 'week', 'month'
+];
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Counter.prototype = new Usergrid.Entity();
+
+/*
+ * overrides Entity.prototype.fetch. Returns all data for counters
+ * associated with the object as specified in the constructor
+ *
+ * @public
+ * @method increment
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.fetch=function(callback){
+  this.getData({}, callback);
+}
+/*
+ * increments the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method increment
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.increment=function(options, callback){
+  var self=this,
+    name=options.name,
+    value=options.value;
+  if(!name){
+    if(typeof(callback) === 'function') {
+      return callback.call(self, true, "'value' for increment, decrement must be a number");
+    }
+  }else if(isNaN(value)){
+    if(typeof(callback) === 'function') {
+      return callback.call(self, true, "'value' for increment, decrement must be a number");
+    }
+  }else{
+    self._data.counters[name]=(parseInt(value))||1;
+    return self.save(callback);
+  }
+};
+/*
+ * decrements the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method decrement
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+
+Usergrid.Counter.prototype.decrement=function(options, callback){
+  var self=this,
+    name=options.name,
+    value=options.value;
+  self.increment({name:name, value:-((parseInt(value))||1)}, callback);
+};
+/*
+ * resets the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method reset
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+
+Usergrid.Counter.prototype.reset=function(options, callback){
+  var self=this,
+    name=options.name;
+  self.increment({name:name, value:0}, callback);
+};
+
+/*
+ * gets data for one or more counters over a given
+ * time period at a specified resolution
+ *
+ * options object: {
+ *                   counters: ['counter1', 'counter2', ...],
+ *                   start: epoch timestamp or ISO date string,
+ *                   end: epoch timestamp or ISO date string,
+ *                   resolution: one of ('all', 'minute', 'five_minutes', 'half_hour', 'hour', 'six_day', 'day', 'week', or 'month')
+ *                   }
+ *
+ * @public
+ * @method getData
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.getData=function(options, callback){
+  var start_time,
+      end_time,
+      start=options.start||0,
+      end=options.end||Date.now(),
+      resolution=(options.resolution||'all').toLowerCase(),
+      counters=options.counters||Object.keys(this._data.counters),
+      res=(resolution||'all').toLowerCase();
+  if(COUNTER_RESOLUTIONS.indexOf(res)===-1){
+    res='all';
+  }
+  if(start){
+    switch(typeof start){
+      case "undefined":
+        start_time=0;
+        break;
+      case "number":
+        start_time=start;
+        break;
+      case "string":
+        start_time=(isNaN(start))?Date.parse(start):parseInt(start);
+        break;
+      default:
+        start_time=Date.parse(start.toString());
+    }
+  }
+  if(end){
+    switch(typeof end){
+      case "undefined":
+        end_time=Date.now();
+        break;
+      case "number":
+        end_time=end;
+        break;
+      case "string":
+        end_time=(isNaN(end))?Date.parse(end):parseInt(end);
+        break;
+      default:
+        end_time=Date.parse(end.toString());
+    }
+  }
+  var self=this;
+  //https://api.usergrid.com/yourorgname/sandbox/counters?counter=test_counter
+  var params=Object.keys(counters).map(function(counter){
+      return ["counter", encodeURIComponent(counters[counter])].join('=');
+    });
+  params.push('resolution='+res)
+  params.push('start_time='+String(start_time))
+  params.push('end_time='+String(end_time))
+
+  var endpoint="counters?"+params.join('&');
+  this._client.request({endpoint:endpoint}, function(err, data){
+    if(data.counters && data.counters.length){
+      data.counters.forEach(function(counter){
+        self._data.counters[counter.name]=counter.value||counter.values;
+      })
+    }
+    if(typeof(callback) === 'function') {
+      callback.call(self, err, data);
+    }
+  })
+};
+
+exports.client = Usergrid.Client;
+exports.entity = Usergrid.Entity;
+exports.collection = Usergrid.Collection;
+exports.group = Usergrid.Group;
+exports.counter = Usergrid.Counter;
+exports.AUTH_CLIENT_ID = AUTH_CLIENT_ID;
+exports.AUTH_APP_USER = AUTH_APP_USER;
+exports.AUTH_NONE = AUTH_NONE;