Comparing version 0.9.2 to 0.10.0
2866
lib/usergrid.js
/** | ||
* This module is a collection of classes designed to make working with | ||
* the Appigee App Services API as easy as possible. | ||
* Learn more at http://apigee.com/docs/usergrid | ||
* | ||
* Copyright 2012 Apigee Corporation | ||
* | ||
* Licensed 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. | ||
*/ | ||
* This module is a collection of classes designed to make working with | ||
* the Appigee App Services API as easy as possible. | ||
* Learn more at http://apigee.com/docs/usergrid | ||
* | ||
* Copyright 2012 Apigee Corporation | ||
* | ||
* Licensed 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. | ||
* | ||
* @author rod simpson (rod@apigee.com) | ||
*/ | ||
/** | ||
* Query is a class for holding all query information and paging state | ||
* | ||
* @class Query | ||
* @author Rod Simpson (rod@apigee.com) | ||
*/ | ||
var request = require('request'); | ||
/** | ||
* @constructor | ||
* @param {string} method | ||
* @param {string} path | ||
* @param {object} jsonObj | ||
* @param {object} paramsObj | ||
* @param {function} successCallback | ||
* @param {function} failureCallback | ||
*/ | ||
Query = function(method, resource, jsonObj, paramsObj, successCallback, failureCallback) { | ||
//query vars | ||
this._method = method; | ||
this._resource = resource; | ||
this._jsonObj = jsonObj; | ||
this._paramsObj = paramsObj; | ||
this._successCallback = successCallback; | ||
this._failureCallback = failureCallback; | ||
//authentication type constants | ||
var AUTH_CLIENT_ID = 'CLIENT_ID'; | ||
var AUTH_APP_USER = 'APP_USER'; | ||
var AUTH_NONE = 'NONE'; | ||
//curl command - will be populated by runQuery function | ||
this._curl = ''; | ||
this._token = false; | ||
Client = function(options) { | ||
//usergrid enpoint | ||
this.URI = 'https://api.usergrid.com'; | ||
//paging vars | ||
this._cursor = null; | ||
this._next = null; | ||
this._previous = []; | ||
this._start = 0; | ||
this._end = 0; | ||
}; | ||
//Find your Orgname and Appname in the Admin portal (http://apigee.com/usergrid) | ||
this.orgName = options.orgName; | ||
this.appName = options.appName; | ||
Query.prototype = { | ||
setQueryStartTime: function() { | ||
this._start = new Date().getTime(); | ||
}, | ||
//authentication data | ||
this.authType = options.authType || AUTH_NONE; | ||
this.clientId = options.clientId; | ||
this.clientSecret = options.clientSecret; | ||
this.token = options.token || null; | ||
this.user = null; | ||
setQueryEndTime: function() { | ||
this._end = new Date().getTime(); | ||
}, | ||
//other options | ||
this.buildCurl = options.buildCurl || false; | ||
this.logging = options.logging || false; | ||
getQueryTotalTime: function() { | ||
var seconds = 0; | ||
var time = this._end - this._start; | ||
try { | ||
seconds = ((time/10) / 60).toFixed(2); | ||
} catch(e){ return 0; } | ||
return this.getMethod() + " " + this.getResource() + " - " + seconds + " seconds"; | ||
}, | ||
/** | ||
* A method to set all settable parameters of the Query at one time | ||
* | ||
* @public | ||
* @method validateUsername | ||
* @param {string} method | ||
* @param {string} path | ||
* @param {object} jsonObj | ||
* @param {object} paramsObj | ||
* @param {function} successCallback | ||
* @param {function} failureCallback | ||
* @return none | ||
*/ | ||
setAllQueryParams: function(method, resource, jsonObj, paramsObj, successCallback, failureCallback) { | ||
this._method = method; | ||
this._resource = resource; | ||
this._jsonObj = jsonObj; | ||
this._paramsObj = paramsObj; | ||
this._successCallback = successCallback; | ||
this._failureCallback = failureCallback; | ||
}, | ||
//timeout and callbacks | ||
this._callTimeout = options.callTimeout || 30000; //default to 30 seconds | ||
this._callTimeoutCallback = options.callTimeoutCallback || null; | ||
this.loggingoutCallback = options.logoutCallback || null; | ||
/** | ||
* A method to reset all the parameters in one call | ||
* | ||
* @public | ||
* @return none | ||
*/ | ||
clearAll: function() { | ||
this._method = null; | ||
this._resource = null; | ||
this._jsonObj = {}; | ||
this._paramsObj = {}; | ||
this._successCallback = null; | ||
this._failureCallback = null; | ||
}, | ||
/** | ||
* Returns the method | ||
* | ||
* @public | ||
* @method getMethod | ||
* @return {string} Returns method | ||
*/ | ||
getMethod: function() { | ||
return this._method; | ||
}, | ||
}; | ||
/** | ||
* sets the method (POST, PUT, DELETE, GET) | ||
* | ||
* @public | ||
* @method setMethod | ||
* @return none | ||
*/ | ||
setMethod: function(method) { | ||
this._method = method; | ||
}, | ||
/** | ||
* 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) | ||
*/ | ||
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? | ||
if (mQuery) { | ||
var uri = this.URI + '/' + endpoint; | ||
} else { | ||
var uri = this.URI + '/' + this.orgName + '/' + this.appName + '/' + endpoint; | ||
} | ||
/** | ||
* Returns the resource | ||
* | ||
* @public | ||
* @method getResource | ||
* @return {string} the resource | ||
*/ | ||
getResource: function() { | ||
return this._resource; | ||
}, | ||
if (this.authType === AUTH_CLIENT_ID) { | ||
qs['client_id'] = this.clientId; | ||
qs['client_secret'] = this.clientSecret; | ||
} else if (this.authType === AUTH_APP_USER) { | ||
qs['access_token'] = this.token; | ||
} | ||
/** | ||
* sets the resource | ||
* | ||
* @public | ||
* @method setResource | ||
* @return none | ||
*/ | ||
setResource: function(resource) { | ||
this._resource = resource; | ||
}, | ||
/** | ||
* Returns the json Object | ||
* | ||
* @public | ||
* @method getJsonObj | ||
* @return {object} Returns the json Object | ||
*/ | ||
getJsonObj: function() { | ||
return this._jsonObj; | ||
}, | ||
/** | ||
* sets the json object | ||
* | ||
* @public | ||
* @method setJsonObj | ||
* @return none | ||
*/ | ||
setJsonObj: function(jsonObj) { | ||
this._jsonObj = jsonObj; | ||
}, | ||
/** | ||
* Returns the Query Parameters object | ||
* | ||
* @public | ||
* @method getQueryParams | ||
* @return {object} Returns Query Parameters object | ||
*/ | ||
getQueryParams: function() { | ||
return this._paramsObj; | ||
}, | ||
/** | ||
* sets the query parameter object | ||
* | ||
* @public | ||
* @method setQueryParams | ||
* @return none | ||
*/ | ||
setQueryParams: function(paramsObj) { | ||
this._paramsObj = paramsObj; | ||
}, | ||
/** | ||
* Returns the success callback function | ||
* | ||
* @public | ||
* @method getSuccessCallback | ||
* @return {function} Returns the successCallback | ||
*/ | ||
getSuccessCallback: function() { | ||
return this._successCallback; | ||
}, | ||
/** | ||
* sets the success callback function | ||
* | ||
* @public | ||
* @method setSuccessCallback | ||
* @return none | ||
*/ | ||
setSuccessCallback: function(successCallback) { | ||
this._successCallback = successCallback; | ||
}, | ||
/** | ||
* Calls the success callback function | ||
* | ||
* @public | ||
* @method callSuccessCallback | ||
* @return {boolean} Returns true or false based on if there was a callback to call | ||
*/ | ||
callSuccessCallback: function(response) { | ||
if (this._successCallback && typeof(this._successCallback ) === "function") { | ||
this._successCallback(response); | ||
return true; | ||
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) { | ||
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 { | ||
return false; | ||
err = true; | ||
if ((r.error === 'auth_expired_session_token') || | ||
(r.error === 'unauthorized') || | ||
(r.error === 'auth_missing_credentials') || | ||
(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.loggingoutCallback) === 'function') { | ||
self.loggingoutCallback(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); | ||
} | ||
} | ||
} | ||
}, | ||
}); | ||
} | ||
/** | ||
* Returns the failure callback function | ||
* | ||
* @public | ||
* @method getFailureCallback | ||
* @return {function} Returns the failureCallback | ||
*/ | ||
getFailureCallback: function() { | ||
return this._failureCallback; | ||
}, | ||
Client.prototype.createEntity = function (data, callback) { | ||
var options = { | ||
client:this, | ||
data:data | ||
} | ||
var entity = new Entity(options); | ||
entity.save(function(err, data) { | ||
if (typeof(callback) === 'function') { | ||
callback(err, entity); | ||
} | ||
}); | ||
} | ||
/** | ||
* sets the failure callback function | ||
* | ||
* @public | ||
* @method setFailureCallback | ||
* @return none | ||
*/ | ||
setFailureCallback: function(failureCallback) { | ||
this._failureCallback = failureCallback; | ||
}, | ||
/** | ||
* Calls the failure callback function | ||
* | ||
* @public | ||
* @method callFailureCallback | ||
* @return {boolean} Returns true or false based on if there was a callback to call | ||
*/ | ||
callFailureCallback: function(response) { | ||
if (this._failureCallback && typeof(this._failureCallback) === "function") { | ||
this._failureCallback(response); | ||
return true; | ||
} else { | ||
return false; | ||
Client.prototype.createCollection = function (options, callback) { | ||
options.client = this; | ||
var collection = new Collection(options, function(err, data) { | ||
if (typeof(callback) === 'function') { | ||
callback(err, collection); | ||
} | ||
}, | ||
}); | ||
/** | ||
* Returns the curl call | ||
* | ||
* @public | ||
* @method getCurl | ||
* @return {function} Returns the curl call | ||
*/ | ||
getCurl: function() { | ||
return this._curl; | ||
}, | ||
} | ||
/** | ||
* sets the curl call | ||
* | ||
* @public | ||
* @method setCurl | ||
* @return none | ||
*/ | ||
setCurl: function(curl) { | ||
this._curl = curl; | ||
}, | ||
/** | ||
* Returns the Token | ||
* | ||
* @public | ||
* @method getToken | ||
* @return {function} Returns the Token | ||
*/ | ||
getToken: function() { | ||
return this._token; | ||
}, | ||
/** | ||
* A private method to get call timing of last call | ||
*/ | ||
Client.prototype.calcTimeDiff = function () { | ||
var seconds = 0; | ||
var time = this._end - this._start; | ||
try { | ||
seconds = ((time/10) / 60).toFixed(2); | ||
} catch(e) { return 0; } | ||
return seconds; | ||
} | ||
/** | ||
* Method to set | ||
* | ||
* @public | ||
* @method setToken | ||
* @return none | ||
*/ | ||
setToken: function(token) { | ||
this._token = token; | ||
}, | ||
/** | ||
* Resets the paging pointer (back to original page) | ||
* | ||
* @public | ||
* @method resetPaging | ||
* @return none | ||
*/ | ||
resetPaging: function() { | ||
this._previous = []; | ||
this._next = null; | ||
this._cursor = null; | ||
}, | ||
/** | ||
* Method to determine if there is a previous page of data | ||
* | ||
* @public | ||
* @method hasPrevious | ||
* @return {boolean} true or false based on if there is a previous page | ||
*/ | ||
hasPrevious: function() { | ||
return (this._previous.length > 0); | ||
}, | ||
/** | ||
* Method to set the paging object to get the previous page of data | ||
* | ||
* @public | ||
* @method getPrevious | ||
* @return none | ||
*/ | ||
getPrevious: function() { | ||
this._next=null; //clear out next so the comparison will find the next item | ||
this._cursor = this._previous.pop(); | ||
}, | ||
/** | ||
* Method to determine if there is a next page of data | ||
* | ||
* @public | ||
* @method hasNext | ||
* @return {boolean} true or false based on if there is a next page | ||
*/ | ||
hasNext: function(){ | ||
return (this._next); | ||
}, | ||
/** | ||
* Method to set the paging object to get the next page of data | ||
* | ||
* @public | ||
* @method getNext | ||
* @return none | ||
*/ | ||
getNext: function() { | ||
this._previous.push(this._cursor); | ||
this._cursor = this._next; | ||
}, | ||
/** | ||
* Method to save off the cursor just returned by the last API call | ||
* | ||
* @public | ||
* @method saveCursor | ||
* @return none | ||
*/ | ||
saveCursor: function(cursor) { | ||
//if current cursor is different, grab it for next cursor | ||
if (this._next !== cursor) { | ||
this._next = cursor; | ||
/* | ||
* 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) | ||
*/ | ||
Client.prototype.login = function (username, password, callback) { | ||
var self = this; | ||
var options = { | ||
method:'GET', | ||
endpoint:'token', | ||
qs:{ | ||
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 { | ||
user = new Entity('users'); | ||
user.set(data.user); | ||
self.user = user; | ||
self.token = data.access_token; | ||
} | ||
if (typeof(callback) === 'function') { | ||
callback(err, data, user); | ||
} | ||
}); | ||
} | ||
/** | ||
* Method to determine if there is a next page of data | ||
* | ||
* @public | ||
* @method getCursor | ||
* @return {string} the current cursor | ||
*/ | ||
getCursor: function() { | ||
return this._cursor; | ||
/** | ||
* 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, and that there is a valid UUID | ||
* | ||
* @method isLoggedIn | ||
* @public | ||
* @return {boolean} Returns true the user is logged in (has token and uuid), false if not | ||
*/ | ||
Client.prototype.isLoggedIn = function () { | ||
var user = this.user; | ||
var haveUser = (user && this.token); | ||
if (!haveUser) { | ||
return false; | ||
} | ||
}; | ||
if (!isUUID(user.get('uuid'))) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
/* | ||
* A private method to build the curl call to display on the command line | ||
* | ||
* @method buildCurlCall | ||
* @private | ||
* @param {object} options | ||
* @return {string} curl | ||
*/ | ||
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'; } | ||
/** | ||
* A class to Model a Usergrid Entity. | ||
* | ||
* @class Entity | ||
* @author Rod Simpson (rod@apigee.com) | ||
*/ | ||
//curl - append the path | ||
curl += ' ' + uri; | ||
/** | ||
* Constructor for initializing an entity | ||
* | ||
* @constructor | ||
* @param {string} collectionType - the type of collection to model | ||
* @param {uuid} uuid - (optional), the UUID of the collection if it is known | ||
*/ | ||
Entity = function(collectionType, uuid) { | ||
this._collectionType = collectionType; | ||
this._data = {}; | ||
if (uuid) { | ||
this._data['uuid'] = uuid; | ||
//curl - add the body | ||
body = JSON.stringify(body) | ||
if (body !== '{}') { | ||
//curl - add in the json obj | ||
curl += " -d '" + body + "'"; | ||
} | ||
}; | ||
//inherit prototype from Query | ||
Entity.prototype = new Query(); | ||
//log the curl command to the console | ||
console.log(curl); | ||
/** | ||
* gets the current Entity type | ||
* | ||
* @method getCollectionType | ||
* @return {string} collection type | ||
*/ | ||
Entity.prototype.getCollectionType = function (){ | ||
return this._collectionType; | ||
return curl; | ||
} | ||
/** | ||
* sets the current Entity type | ||
* | ||
* @method setCollectionType | ||
* @param {string} collectionType | ||
* @return none | ||
*/ | ||
Entity.prototype.setCollectionType = function (collectionType){ | ||
this._collectionType = collectionType; | ||
* A public method to log out an app user - clears all user fields from client | ||
* | ||
* @method logout | ||
* @public | ||
* @return none | ||
*/ | ||
Client.prototype.logout = function () { | ||
this.user = null; | ||
this.token = null; | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
Entity.prototype.get = function (field){ | ||
* A class to Model a Usergrid Entity. | ||
* Set the type of entity in the 'data' json object | ||
* | ||
* @constructor | ||
* @param {object} options {client:client, data:{'type':'collection_type', 'key':'value'}, uuid:uuid}} | ||
*/ | ||
Entity = function(options) { | ||
this._client = options.client; | ||
this._data = options.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 | ||
*/ | ||
Entity.prototype.get = function (field) { | ||
if (field) { | ||
@@ -465,22 +313,24 @@ return this._data[field]; | ||
} | ||
}, | ||
} | ||
/** | ||
* adds a specific field or object to the Entity's data | ||
* | ||
* @method set | ||
* @param {string} item || {object} | ||
* @param {string} value | ||
* @return none | ||
*/ | ||
Entity.prototype.set = function (item, value){ | ||
if (typeof item === 'object') { | ||
for(var field in item) { | ||
this._data[field] = item[field]; | ||
* 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 | ||
*/ | ||
Entity.prototype.set = function (key, value) { | ||
if (typeof key === 'object') { | ||
for(var field in key) { | ||
this._data[field] = key[field]; | ||
} | ||
} else if (typeof item === 'string') { | ||
} else if (typeof key === 'string') { | ||
if (value === null) { | ||
delete this._data[item]; | ||
delete this._data[key]; | ||
} else { | ||
this._data[item] = value; | ||
this._data[key] = value; | ||
} | ||
@@ -493,52 +343,23 @@ } else { | ||
/** | ||
* Saves the entity back to the database | ||
* | ||
* @method save | ||
* @public | ||
* @param {function} successCallback | ||
* @param {function} errorCallback | ||
* @return none | ||
*/ | ||
Entity.prototype.save = function (successCallback, errorCallback){ | ||
var path = this.getCollectionType(); | ||
* Saves the entity back to the database | ||
* | ||
* @method save | ||
* @public | ||
* @param {function} callback | ||
* @return {callback} callback(err, data) | ||
*/ | ||
Entity.prototype.save = function (callback) { | ||
//TODO: API will be changed soon to accomodate PUTs via name which create new entities | ||
// This function should be changed to PUT only at that time, and updated to use | ||
// either uuid or name | ||
var type = this.get('type'); | ||
var method = 'POST'; | ||
if (this.get('uuid')) { | ||
if (isUUID(this.get('uuid'))) { | ||
method = 'PUT'; | ||
if (validation.isUUID(this.get('uuid'))) { | ||
path += "/" + this.get('uuid'); | ||
} | ||
type += '/' + this.get('uuid'); | ||
} | ||
//if this is a user, update the password if it has been specified | ||
var data = {}; | ||
if (path === 'users') { | ||
data = this.get(); | ||
var pwdata = {}; | ||
//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 | ||
if (data.oldpassword && data.newpassword) { | ||
pwdata.oldpassword = data.oldpassword; | ||
pwdata.newpassword = data.newpassword; | ||
this.runAppQuery(new Query('PUT', 'users/'+uuid+'/password', pwdata, null, | ||
function (response) { | ||
//not calling any callbacks - this section will be merged as soon as API supports | ||
// updating passwords in general PUT call | ||
}, | ||
function (response) { | ||
} | ||
)); | ||
} | ||
//remove old and new password fields so they don't end up as part of the entity object | ||
delete data.oldpassword; | ||
delete data.newpassword; | ||
} | ||
//update the entity | ||
var self = this; | ||
data = {}; | ||
var data = {}; | ||
var entityData = this.get(); | ||
@@ -551,57 +372,90 @@ //remove system specific properties | ||
} | ||
this.setAllQueryParams(method, path, data, null, | ||
function(response) { | ||
try { | ||
var entity = response.entities[0]; | ||
var options = { | ||
method:method, | ||
endpoint:type, | ||
body:data | ||
}; | ||
//save the entity first | ||
this._client.request(options, function (err, retdata) { | ||
if (err && self._client.logging) { | ||
console.log('could not save entity'); | ||
if (typeof(callback) === 'function') { | ||
return callback(err, retdata, self); | ||
} | ||
} else { | ||
if (retdata.entities.length) { | ||
var entity = retdata.entities[0]; | ||
self.set(entity); | ||
if (typeof(successCallback) == "function"){ | ||
successCallback(response); | ||
} | ||
} catch (e) { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
} | ||
} | ||
}, | ||
function(response) { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
//if this is a user, update the password if it has been specified; | ||
var needPasswordChange = (type === 'users' && entityData.oldpassword && entityData.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 = entityData.oldpassword; | ||
pwdata.newpassword = entityData.newpassword; | ||
this._client.request( | ||
{ | ||
method:'PUT', | ||
endpoint:type, | ||
body:pwdata | ||
}, | ||
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); | ||
} | ||
} | ||
); | ||
ApiClient.runAppQuery(this); | ||
}); | ||
} | ||
/** | ||
* refreshes the entity by making a GET call back to the database | ||
* | ||
* @method fetch | ||
* @public | ||
* @param {function} successCallback | ||
* @param {function} errorCallback | ||
* @return none | ||
*/ | ||
Entity.prototype.fetch = function (successCallback, errorCallback){ | ||
var path = this.getCollectionType(); | ||
* refreshes the entity by making a GET call back to the database | ||
* | ||
* @method fetch | ||
* @public | ||
* @param {function} callback | ||
* @return {callback} callback(err, data) | ||
*/ | ||
Entity.prototype.fetch = function (callback) { | ||
var type = this.get('type'); | ||
var self = this; | ||
//if a uuid is available, use that, otherwise, use the name | ||
if (this.get('uuid')) { | ||
path += "/" + this.get('uuid'); | ||
type += '/' + this.get('uuid'); | ||
} else { | ||
if (path == "users") { | ||
if (this.get("username")) { | ||
path += "/" + this.get("username"); | ||
if (type === 'users') { | ||
if (this.get('username')) { | ||
type += '/' + this.get('username'); | ||
} else { | ||
console.log('no username specified'); | ||
if (typeof(errorCallback) == "function"){ | ||
console.log('no username specified'); | ||
if (typeof(callback) === 'function') { | ||
var error = 'cannot fetch entity, no username specified'; | ||
if (self._client.logging) { | ||
console.log(error); | ||
} | ||
return callback(true, error, self) | ||
} | ||
} | ||
} else { | ||
if (this.get()) { | ||
path += "/" + this.get(); | ||
if (this.get('name')) { | ||
type += '/' + this.get('name'); | ||
} else { | ||
console.log('no entity identifier specified'); | ||
if (typeof(errorCallback) == "function"){ | ||
console.log('no entity identifier specified'); | ||
if (typeof(callback) === 'function') { | ||
var error = 'cannot fetch entity, no name specified'; | ||
if (self._client.logging) { | ||
console.log(error); | ||
} | ||
return callback(true, error, self) | ||
} | ||
@@ -611,66 +465,61 @@ } | ||
} | ||
var self = this; | ||
this.setAllQueryParams('GET', path, null, null, | ||
function(response) { | ||
try { | ||
if (response.user) { | ||
self.set(response.user); | ||
} | ||
var entity = response.entities[0]; | ||
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); | ||
} else if (data.entities.length) { | ||
var entity = data.entities[0]; | ||
self.set(entity); | ||
if (typeof(successCallback) == "function"){ | ||
successCallback(response); | ||
} | ||
} catch (e) { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
} | ||
} | ||
}, | ||
function(response) { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
} | ||
} | ||
); | ||
ApiClient.runAppQuery(this); | ||
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} successCallback | ||
* @param {function} errorCallback | ||
* @return none | ||
* | ||
*/ | ||
Entity.prototype.destroy = function (successCallback, errorCallback){ | ||
var path = this.getCollectionType(); | ||
if (this.get('uuid')) { | ||
path += "/" + this.get('uuid'); | ||
* 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) | ||
* | ||
*/ | ||
Entity.prototype.destroy = function (callback) { | ||
var type = this.get('type'); | ||
if (isUUID(this.get('uuid'))) { | ||
type += '/' + this.get('uuid'); | ||
} else { | ||
console.log('Error trying to delete object - no uuid specified.'); | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback('Error trying to delete object - no uuid specified.'); | ||
if (typeof(callback) === 'function') { | ||
var error = 'Error trying to delete object - no uuid specified.'; | ||
if (self._client.logging) { | ||
console.log(error); | ||
} | ||
callback(true, error); | ||
} | ||
} | ||
var self = this; | ||
this.setAllQueryParams('DELETE', path, null, null, | ||
function(response) { | ||
//clear out this object | ||
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(successCallback) == "function"){ | ||
successCallback(response); | ||
} | ||
}, | ||
function(response) { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
} | ||
} | ||
); | ||
ApiClient.runAppQuery(this); | ||
if (typeof(callback) === 'function') { | ||
callback(err, data); | ||
} | ||
}); | ||
} | ||
@@ -681,161 +530,210 @@ | ||
/** | ||
* 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 | ||
* | ||
* @class Collection | ||
* @author Rod Simpson (rod@apigee.com) | ||
*/ | ||
/** | ||
* Collection is a container class for holding entities | ||
* | ||
* @constructor | ||
* @param {string} collectionPath - the type of collection to model | ||
* @param {uuid} uuid - (optional), the UUID of the collection if it is known | ||
*/ | ||
Collection = function(path, uuid) { | ||
this._path = path; | ||
this._uuid = uuid; | ||
* 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) | ||
*/ | ||
Collection = function(options, callback) { | ||
this._client = options.client; | ||
this._type = options.type; | ||
this._uuid = options.uuid; | ||
this.qs = options.qs || {}; | ||
//iteration | ||
this._list = []; | ||
this._Query = new Query(); | ||
this._iterator = -1; //first thing we do is increment, so set to -1 | ||
}; | ||
Collection.prototype = new Query(); | ||
//paging | ||
this._previous = []; | ||
this._next = null; | ||
this._cursor = null | ||
/** | ||
* gets the current Collection path | ||
* | ||
* @method getPath | ||
* @return {string} path | ||
*/ | ||
Collection.prototype.getPath = function (){ | ||
return this._path; | ||
} | ||
/** | ||
* sets the Collection path | ||
* | ||
* @method setPath | ||
* @param {string} path | ||
* @return none | ||
*/ | ||
Collection.prototype.setPath = function (path){ | ||
this._path = path; | ||
var self = this; | ||
//get list of collections, see if this one exists | ||
var callOptions = { | ||
method:'GET', | ||
endpoint:'' | ||
}; | ||
this._client.request(callOptions, function (err, data) { | ||
if (err && self._client.logging) { | ||
console.log('error getting collections - check options passed to client'); | ||
if (typeof(callback) === 'function') { | ||
return callback(err, data); | ||
} | ||
} else { | ||
//store collection list | ||
var collections = data.entities[0].metadata.collections; | ||
if ( collections.hasOwnProperty(self._type) ) { | ||
//collection exists, so just fetch | ||
self.fetch(function(err) { | ||
if (typeof(callback) === 'function') { | ||
return callback(err, data); | ||
} | ||
}); | ||
} else { | ||
//collection doesn't exist, post first | ||
self._client.request( | ||
{ | ||
method:'POST', | ||
endpoint:self._type, | ||
json:{}, | ||
qs:self.qs | ||
}, | ||
function (err, data) { | ||
if (err && self._client.logging) { | ||
console.log('error: collection not created'); | ||
} | ||
if (typeof(callback) === 'function') { | ||
callback(err, data); | ||
} | ||
} | ||
); | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* gets the current Collection UUID | ||
* | ||
* @method getUUID | ||
* @return {string} the uuid | ||
*/ | ||
Collection.prototype.getUUID = function (){ | ||
return this._uuid; | ||
} | ||
* Populates the collection from the server | ||
* | ||
* @method fetch | ||
* @param {function} callback | ||
* @return {callback} callback(err, data) | ||
*/ | ||
Collection.prototype.fetch = function (callback) { | ||
var self = this; | ||
var qs = this.qs; | ||
/** | ||
* sets the current Collection UUID | ||
* @method setUUID | ||
* @param {string} uuid | ||
* @return none | ||
*/ | ||
Collection.prototype.setUUID = function (uuid){ | ||
this._uuid = uuid; | ||
//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 | ||
for (var i=0;i<count;i++) { | ||
var uuid = data.entities[i].uuid; | ||
if (uuid) { | ||
var entityData = data.entities[i] || {}; | ||
var entityOptions = { | ||
type:self._type, | ||
client:self._client, | ||
uuid:uuid, | ||
data:entityData | ||
}; | ||
var ent = new Entity(entityOptions); | ||
var ct = self._list.length; | ||
self._list[ct] = ent; | ||
} | ||
} | ||
} | ||
} | ||
if (typeof(callback) === 'function') { | ||
callback(err, data); | ||
} | ||
}); | ||
} | ||
/** | ||
* Adds an Entity to the collection (adds to the local object) | ||
* | ||
* @method addEntity | ||
* @param {object} entity | ||
* @param {function} successCallback | ||
* @param {function} errorCallback | ||
* @return none | ||
*/ | ||
Collection.prototype.addEntity = function (entity){ | ||
//then add it to the list | ||
var count = this._list.length; | ||
this._list[count] = entity; | ||
} | ||
* 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) | ||
*/ | ||
Collection.prototype.addEntity = function (options, callback) { | ||
var self = this; | ||
options.type = this._type; | ||
/** | ||
* Adds a new Entity to the collection (saves, then adds to the local object) | ||
* | ||
* @method addNewEntity | ||
* @param {object} entity | ||
* @return none | ||
*/ | ||
Collection.prototype.addNewEntity = function (entity,successCallback, errorCallback) { | ||
//add the entity to the list | ||
this.addEntity(entity); | ||
//then save the entity | ||
entity.save(successCallback, errorCallback); | ||
//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); | ||
} | ||
}); | ||
} | ||
/** | ||
* Removes the Entity from the collection, then destroys the object on the server | ||
* | ||
* @method destroyEntity | ||
* @param {object} entity | ||
* @return none | ||
*/ | ||
Collection.prototype.destroyEntity = function (entity) { | ||
//first get the entities uuid | ||
var uuid = entity.get('uuid'); | ||
//if the entity has a uuid, delete it | ||
if (validation.isUUID(uuid)) { | ||
//then remove it from the list | ||
var count = this._list.length; | ||
var i=0; | ||
var reorder = false; | ||
for (i=0; i<count; i++) { | ||
if(reorder) { | ||
this._list[i-1] = this._list[i]; | ||
this._list[i] = null; | ||
* 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) | ||
*/ | ||
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 (this._list[i].get('uuid') == uuid) { | ||
this._list[i] = null; | ||
reorder=true; | ||
if (typeof(callback) === 'function') { | ||
callback(err, data); | ||
} | ||
} else { | ||
//destroy was good, so repopulate the collection | ||
self.fetch(callback); | ||
} | ||
} | ||
//first destroy the entity on the server | ||
entity.destroy(); | ||
}); | ||
} | ||
/** | ||
* Looks up an Entity by a specific field - will return the first Entity that | ||
* has a matching field | ||
* | ||
* @method getEntityByField | ||
* @param {string} field | ||
* @param {string} value | ||
* @return {object} returns an entity object, or null if it is not found | ||
*/ | ||
Collection.prototype.getEntityByField = function (field, value){ | ||
var count = this._list.length; | ||
var i=0; | ||
for (i=0; i<count; i++) { | ||
if (this._list[i].getField(field) == value) { | ||
return this._list[i]; | ||
} | ||
* Looks up an Entity by UUID | ||
* | ||
* @method getEntityByUUID | ||
* @param {string} UUID | ||
* @param {function} callback | ||
* @return {callback} callback(err, data, entity) | ||
*/ | ||
Collection.prototype.getEntityByUUID = function (uuid, callback) { | ||
//get the entity from the database | ||
var options = { | ||
data: { | ||
type: this._type, | ||
uuid:uuid | ||
}, | ||
client: this._client | ||
} | ||
return null; | ||
var entity = new Entity(options); | ||
entity.fetch(callback); | ||
} | ||
/** | ||
* Looks up an Entity by UUID | ||
* | ||
* @method getEntityByUUID | ||
* @param {string} UUID | ||
* @return {object} returns an entity object, or null if it is not found | ||
*/ | ||
Collection.prototype.getEntityByUUID = function (UUID){ | ||
* Returns the first Entity of the Entity list - does not affect the iterator | ||
* | ||
* @method getFirstEntity | ||
* @return {object} returns an entity object | ||
*/ | ||
Collection.prototype.getFirstEntity = function () { | ||
var count = this._list.length; | ||
var i=0; | ||
for (i=0; i<count; i++) { | ||
if (this._list[i].get('uuid') == UUID) { | ||
return this._list[i]; | ||
} | ||
if (count > 0) { | ||
return this._list[0]; | ||
} | ||
@@ -846,100 +744,90 @@ return null; | ||
/** | ||
* Returns the first Entity of the Entity list - does not affect the iterator | ||
* | ||
* @method getFirstEntity | ||
* @return {object} returns an entity object | ||
*/ | ||
Collection.prototype.getFirstEntity = function (){ | ||
* Returns the last Entity of the Entity list - does not affect the iterator | ||
* | ||
* @method getLastEntity | ||
* @return {object} returns an entity object | ||
*/ | ||
Collection.prototype.getLastEntity = function () { | ||
var count = this._list.length; | ||
if (count > 0) { | ||
return this._list[0]; | ||
} | ||
return null; | ||
if (count > 0) { | ||
return this._list[count-1]; | ||
} | ||
return null; | ||
} | ||
/** | ||
* Returns the last Entity of the Entity list - does not affect the iterator | ||
* | ||
* @method getLastEntity | ||
* @return {object} returns an entity object | ||
*/ | ||
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 | ||
*/ | ||
Collection.prototype.hasNextEntity = function (){ | ||
* 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 | ||
*/ | ||
Collection.prototype.hasNextEntity = function () { | ||
var next = this._iterator + 1; | ||
if(next >=0 && next < this._list.length) { | ||
return true; | ||
} | ||
return false; | ||
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 | ||
*/ | ||
Collection.prototype.getNextEntity = function (){ | ||
* 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 | ||
*/ | ||
Collection.prototype.getNextEntity = function () { | ||
this._iterator++; | ||
if(this._iterator >= 0 && this._iterator <= this._list.length) { | ||
return this._list[this._iterator]; | ||
} | ||
return false; | ||
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 hasPreviousEntity | ||
* @return {boolean} true if there is a previous entity, false if not | ||
*/ | ||
Collection.prototype.hasPreviousEntity = function (){ | ||
* 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 | ||
*/ | ||
Collection.prototype.hasPrevEntity = function () { | ||
var previous = this._iterator - 1; | ||
if(previous >=0 && previous < this._list.length) { | ||
return true; | ||
} | ||
return false; | ||
var hasPreviousElement = (previous >=0 && previous < this._list.length); | ||
if(hasPreviousElement) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Entity iteration - Gets the "previous" entity in the list. | ||
* | ||
* @method getPreviousEntity | ||
* @return {object} entity | ||
*/ | ||
Collection.prototype.getPreviousEntity = function (){ | ||
* Entity iteration - Gets the "previous" entity in the list. | ||
* | ||
* @method getPrevEntity | ||
* @return {object} entity | ||
*/ | ||
Collection.prototype.getPrevEntity = function () { | ||
this._iterator--; | ||
if(this._iterator >= 0 && this._iterator <= this._list.length) { | ||
return this.list[this._iterator]; | ||
} | ||
return false; | ||
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 | ||
*/ | ||
Collection.prototype.resetEntityPointer = function (){ | ||
* Entity iteration - Resets the iterator back to the beginning | ||
* of the list | ||
* | ||
* @method resetEntityPointer | ||
* @return none | ||
*/ | ||
Collection.prototype.resetEntityPointer = function () { | ||
this._iterator = -1; | ||
@@ -949,1356 +837,106 @@ } | ||
/** | ||
* gets and array of all entities currently in the colleciton object | ||
* | ||
* @method getEntityList | ||
* @return {array} returns an array of entity objects | ||
*/ | ||
Collection.prototype.getEntityList = function (){ | ||
return this._list; | ||
* Method to save off the cursor just returned by the last API call | ||
* | ||
* @public | ||
* @method saveCursor | ||
* @return none | ||
*/ | ||
Collection.prototype.saveCursor = function(cursor) { | ||
//if current cursor is different, grab it for next cursor | ||
if (this._next !== cursor) { | ||
this._next = cursor; | ||
} | ||
} | ||
/** | ||
* sets the entity list | ||
* | ||
* @method setEntityList | ||
* @param {array} list - an array of Entity objects | ||
* @return none | ||
*/ | ||
Collection.prototype.setEntityList = function (list){ | ||
this._list = list; | ||
* Resets the paging pointer (back to original page) | ||
* | ||
* @public | ||
* @method resetPaging | ||
* @return none | ||
*/ | ||
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 | ||
*/ | ||
Collection.prototype.hasNextPage = function (){ | ||
return this.hasNext(); | ||
* 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 | ||
*/ | ||
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 | ||
* @return none | ||
*/ | ||
Collection.prototype.getNextPage = function (){ | ||
if (this.hasNext()) { | ||
//set the cursor to the next page of data | ||
this.getNext(); | ||
//empty the list | ||
this.setEntityList([]); | ||
ApiClient.runAppQuery(this); | ||
} | ||
* 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) | ||
*/ | ||
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 | ||
*/ | ||
Collection.prototype.hasPreviousPage = function (){ | ||
return this.hasPrevious(); | ||
* 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 | ||
*/ | ||
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 | ||
* @return none | ||
*/ | ||
Collection.prototype.getPreviousPage = function (){ | ||
if (this.hasPrevious()) { | ||
this.getPrevious(); | ||
//empty the list | ||
this.setEntityList([]); | ||
ApiClient.runAppQuery(this); | ||
} | ||
* 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) | ||
*/ | ||
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); | ||
} | ||
} | ||
/** | ||
* clears the query parameters object | ||
* | ||
* @method clearQuery | ||
* @return none | ||
*/ | ||
Collection.prototype.clearQuery = function (){ | ||
this.clearAll(); | ||
* 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); | ||
} | ||
/** | ||
* A method to get all items in the collection, as dictated by the | ||
* cursor and the query. By default, the API returns 10 items in | ||
* a given call. This can be overriden so that more or fewer items | ||
* are returned. The entities returned are all stored in the colleciton | ||
* object's entity list, and can be retrieved by calling getEntityList() | ||
* | ||
* @method get | ||
* @param {function} successCallback | ||
* @param {function} errorCallback | ||
* @return none | ||
*/ | ||
Collection.prototype.fetch = function (successCallback, errorCallback){ | ||
var self = this; | ||
var queryParams = this.getQueryParams(); | ||
//empty the list | ||
this.setEntityList([]); | ||
this.setAllQueryParams('GET', this.getPath(), null, queryParams, | ||
function(response) { | ||
if (response.entities) { | ||
this.resetEntityPointer(); | ||
var count = response.entities.length; | ||
for (var i=0;i<count;i++) { | ||
var uuid = response.entities[i].uuid; | ||
if (uuid) { | ||
var entity = new Entity(self.getPath(), uuid); | ||
//store the data in the entity | ||
var data = response.entities[i] || {}; | ||
delete data.uuid; //remove uuid from the object | ||
entity.set(data); | ||
//store the new entity in this collection | ||
self.addEntity(entity); | ||
} | ||
} | ||
if (typeof(successCallback) == "function"){ | ||
successCallback(response); | ||
} | ||
} else { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
} | ||
} | ||
}, | ||
function(response) { | ||
if (typeof(errorCallback) == "function"){ | ||
errorCallback(response); | ||
} | ||
} | ||
); | ||
ApiClient.runAppQuery(this); | ||
} | ||
/** | ||
* A method to save all items currently stored in the collection object | ||
* caveat with this method: we can't update anything except the items | ||
* currently stored in the collection. | ||
* | ||
* @method save | ||
* @param {function} successCallback | ||
* @param {function} errorCallback | ||
* @return none | ||
*/ | ||
Collection.prototype.save = function (successCallback, errorCallback){ | ||
//loop across all entities and save each one | ||
var entities = this.getEntityList(); | ||
var count = entities.length; | ||
var jsonObj = []; | ||
for (var i=0;i<count;i++) { | ||
entity = entities[i]; | ||
data = entity.get(); | ||
if (entity.get('uuid')) { | ||
data.uuid = entity.get('uuid'); | ||
jsonObj.push(data); | ||
} | ||
entity.save(); | ||
} | ||
this.setAllQueryParams('PUT', this.getPath(), jsonObj, null,successCallback, errorCallback); | ||
ApiClient.runAppQuery(this); | ||
} | ||
/* | ||
* ApiClient | ||
* | ||
* A Singleton that is the main client for making calls to the API. Maintains | ||
* state between calls for the following items: | ||
* | ||
* Token | ||
* User (username, email, name, uuid) | ||
* | ||
* Main methods for making calls to the API are: | ||
* | ||
* runAppQuery (Query) | ||
* runManagementQuery(Query) | ||
* | ||
* Create a new Query object and then pass it to either of these | ||
* two methods for making calls directly to the API. | ||
* | ||
* A method for logging in an app user (to get a OAuth token) also exists: | ||
* | ||
* logInAppUser (username, password, successCallback, failureCallback) | ||
* | ||
* @class ApiClient | ||
* @author Rod Simpson (rod@apigee.com) | ||
* | ||
*/ | ||
var M = 'ManagementQuery'; | ||
var A = 'ApplicationQuery' | ||
var USER = 'USER_AUTH'; | ||
var CSID = 'CLIENT_SECRET'; | ||
ApiClient = (function () { | ||
//API endpoint | ||
var _apiUrl = "api.usergrid.com"; | ||
var _orgName = null; | ||
var _appName = null; | ||
var _authType = USER; | ||
var _token = null; | ||
var _clientId = null; | ||
var _clientSecret = null; | ||
var _callTimeout = 30000; | ||
var _queryType = null; | ||
var _loggedInUser = null; | ||
var _logoutCallback = null; | ||
var _callTimeoutCallback = null; | ||
/* | ||
* A method to set up the ApiClient with orgname and appname | ||
* | ||
* @method init | ||
* @public | ||
* @param {string} orgName | ||
* @param {string} appName | ||
* @return none | ||
* | ||
*/ | ||
function init(orgName, appName){ | ||
this.setOrganizationName(orgName); | ||
this.setApplicationName(appName); | ||
} | ||
/* | ||
* Public method to set the credentials for the client id and secret | ||
* | ||
* @method setClientSecretCombo | ||
* @public | ||
* @param {string} clientId | ||
* @param {string} clientSecret | ||
* @return none | ||
*/ | ||
function setClientSecretCombo(clientId, clientSecret){ | ||
_clientId = clientId; | ||
_clientSecret = clientSecret; | ||
} | ||
/* | ||
* Public method to enable client authentication | ||
* | ||
* @method enableClientSecretAuth | ||
* @public | ||
* @return none | ||
*/ | ||
function enableClientSecretAuth() { | ||
_authType = CSID; | ||
} | ||
/* | ||
* Public method to enable user authentication | ||
* | ||
* @method enableUserAuth | ||
* @public | ||
* @return none | ||
*/ | ||
function enableUserAuth(){ | ||
_authType = USER; | ||
} | ||
/* | ||
* Public method to disable authentication | ||
* | ||
* @method enableNoAuth | ||
* @public | ||
* @return none | ||
*/ | ||
function enableNoAuth(){ | ||
_authType = null; | ||
} | ||
/* | ||
* Public method to run calls against the app endpoint | ||
* | ||
* @method runAppQuery | ||
* @public | ||
* @params {object} Query - {method, path, jsonObj, params, successCallback, failureCallback} | ||
* @return none | ||
*/ | ||
function runAppQuery (Query) { | ||
var endpoint = "/" + this.getOrganizationName() + "/" + this.getApplicationName() + "/"; | ||
setQueryType(A); | ||
run(Query, endpoint); | ||
} | ||
/* | ||
* Public method to run calls against the management endpoint | ||
* | ||
* @method runManagementQuery | ||
* @public | ||
* @params {object} Query - {method, path, jsonObj, params, successCallback, failureCallback} | ||
* @return none | ||
*/ | ||
function runManagementQuery (Query) { | ||
var endpoint = "/management/"; | ||
setQueryType(M); | ||
run(Query, endpoint) | ||
} | ||
/* | ||
* A public method to get the organization name to be used by the client | ||
* | ||
* @method getOrganizationName | ||
* @public | ||
* @return {string} the organization name | ||
*/ | ||
function getOrganizationName() { | ||
return _orgName; | ||
} | ||
/* | ||
* A public method to set the organization name to be used by the client | ||
* | ||
* @method setOrganizationName | ||
* @param orgName - the organization name | ||
* @return none | ||
*/ | ||
function setOrganizationName(organizationName) { | ||
_orgName = organizationName; | ||
} | ||
/* | ||
* A public method to get the application name to be used by the client | ||
* | ||
* @method getApplicationName | ||
* @public | ||
* @return {string} the application name | ||
*/ | ||
function getApplicationName() { | ||
return _appName; | ||
} | ||
/* | ||
* A public method to set the application name to be used by the client | ||
* | ||
* @method setApplicationName | ||
* @public | ||
* @param appName - the application name | ||
* @return none | ||
*/ | ||
function setApplicationName(applicationName) { | ||
_appName = applicationName; | ||
} | ||
/* | ||
* A public method to get current OAuth token | ||
* | ||
* @method getToken | ||
* @public | ||
* @return {string} the current token | ||
*/ | ||
function getToken() { | ||
return session.getItem('token'); | ||
} | ||
/* | ||
* A public method to set the current Oauth token | ||
* | ||
* @method setToken | ||
* @public | ||
* @param token - the bearer token | ||
* @return none | ||
*/ | ||
function setToken(token) { | ||
session.setItem('token', token); | ||
} | ||
/* | ||
* A public method to return the API URL | ||
* | ||
* @method getApiUrl | ||
* @public | ||
* @return {string} the API url | ||
*/ | ||
function getApiUrl() { | ||
return _apiUrl; | ||
} | ||
/* | ||
* A public method to overide the API url | ||
* | ||
* @method setApiUrl | ||
* @public | ||
* @return none | ||
*/ | ||
function setApiUrl(apiUrl) { | ||
_apiUrl = apiUrl; | ||
} | ||
/* | ||
* A public method to return the call timeout amount | ||
* | ||
* @method getCallTimeout | ||
* @public | ||
* @return {string} the timeout value (an integer) 30000 = 30 seconds | ||
*/ | ||
function getCallTimeout() { | ||
return _callTimeout; | ||
} | ||
/* | ||
* A public method to override the call timeout amount | ||
* | ||
* @method setCallTimeout | ||
* @public | ||
* @return none | ||
*/ | ||
function setCallTimeout(callTimeout) { | ||
_callTimeout = callTimeout; | ||
} | ||
/* | ||
* Returns the call timeout callback function | ||
* | ||
* @public | ||
* @method setCallTimeoutCallback | ||
* @return none | ||
*/ | ||
function setCallTimeoutCallback(callback) { | ||
_callTimeoutCallback = callback; | ||
} | ||
/* | ||
* Returns the call timeout callback function | ||
* | ||
* @public | ||
* @method getCallTimeoutCallback | ||
* @return {function} Returns the callTimeoutCallback | ||
*/ | ||
function getCallTimeoutCallback() { | ||
return _callTimeoutCallback; | ||
} | ||
/* | ||
* Calls the call timeout callback function | ||
* | ||
* @public | ||
* @method callTimeoutCallback | ||
* @return {boolean} Returns true or false based on if there was a callback to call | ||
*/ | ||
function callTimeoutCallback(response) { | ||
if (_callTimeoutCallback && typeof(_callTimeoutCallback) === "function") { | ||
_callTimeoutCallback(response); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
/* | ||
* A public method to get the api url of the reset pasword endpoint | ||
* | ||
* @method getResetPasswordUrl | ||
* @public | ||
* @return {string} the api rul of the reset password endpoint | ||
*/ | ||
function getResetPasswordUrl() { | ||
return getApiUrl() + "/management/users/resetpw" | ||
} | ||
/* | ||
* A public method to get an Entity object for the current logged in user | ||
* | ||
* @method getLoggedInUser | ||
* @public | ||
* @return {object} user - Entity object of type user | ||
*/ | ||
function getLoggedInUser() { | ||
var data = JSON.parse(session.getItem('user')); | ||
var user = new Entity('user'); | ||
user.set(data); | ||
return user; | ||
} | ||
/* | ||
* A public method to set an Entity object for the current logged in user | ||
* | ||
* @method setLoggedInUser | ||
* @public | ||
* @param {object} user - Entity object of type user | ||
* @return none | ||
*/ | ||
function setLoggedInUser(user) { | ||
var data = null; | ||
if (user) { data = user.get(); } | ||
session.setItem('user', JSON.stringify(data)); | ||
} | ||
/* | ||
* A public method to log in an app user - stores the token for later use | ||
* | ||
* @method logInAppUser | ||
* @public | ||
* @params {string} username | ||
* @params {string} password | ||
* @params {function} successCallback | ||
* @params {function} failureCallback | ||
* @return {response} callback functions return API response object | ||
*/ | ||
function logInAppUser (username, password, successCallback, failureCallback) { | ||
var self = this; | ||
var data = {"username": username, "password": password, "grant_type": "password"}; | ||
this.runAppQuery(new Query('GET', 'token', null, data, | ||
function (response) { | ||
var user = new Entity('users'); | ||
user.set('username', response.user.username); | ||
user.set('name', response.user.name); | ||
user.set('email', response.user.email); | ||
user.set('uuid', response.user.uuid); | ||
self.setLoggedInUser(user); | ||
self.setToken(response.access_token); | ||
if (successCallback && typeof(successCallback) == "function") { | ||
successCallback(response); | ||
} | ||
}, | ||
function (response) { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(response); | ||
} | ||
} | ||
)); | ||
} | ||
/* | ||
* TODO: NOT IMPLEMENTED YET - A method to renew an app user's token | ||
* Note: waiting for API implementation | ||
* @method renewAppUserToken | ||
* @public | ||
* @return none | ||
*/ | ||
function renewAppUserToken() { | ||
} | ||
/** | ||
* A public method to log out an app user - clears all user fields from client | ||
* | ||
* @method logoutAppUser | ||
* @public | ||
* @return none | ||
*/ | ||
function logoutAppUser() { | ||
this.setLoggedInUser(null); | ||
this.setToken(null); | ||
} | ||
/** | ||
* 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, and that there is a valid UUID | ||
* | ||
* @method isLoggedInAppUser | ||
* @public | ||
* @params {object} Query - {method, path, jsonObj, params, successCallback, failureCallback} | ||
* @return {boolean} Returns true the user is logged in (has token and uuid), false if not | ||
*/ | ||
function isLoggedInAppUser() { | ||
var user = this.getLoggedInUser(); | ||
return (this.getToken() && validation.isUUID(user.get('uuid'))); | ||
} | ||
/* | ||
* A public method to get the logout callback, which is called | ||
* | ||
* when the token is found to be invalid | ||
* @method getLogoutCallback | ||
* @public | ||
* @return {string} the api rul of the reset password endpoint | ||
*/ | ||
function getLogoutCallback() { | ||
return _logoutCallback; | ||
} | ||
/* | ||
* A public method to set the logout callback, which is called | ||
* | ||
* when the token is found to be invalid | ||
* @method setLogoutCallback | ||
* @public | ||
* @param {function} logoutCallback | ||
* @return none | ||
*/ | ||
function setLogoutCallback(logoutCallback) { | ||
_logoutCallback = logoutCallback; | ||
} | ||
/* | ||
* A public method to call the logout callback, which is called | ||
* | ||
* when the token is found to be invalid | ||
* @method callLogoutCallback | ||
* @public | ||
* @return none | ||
*/ | ||
function callLogoutCallback() { | ||
if (_logoutCallback && typeof(_logoutCallback ) == "function") { | ||
_logoutCallback(); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
/** | ||
* Private helper 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 private method to get the type of the current api call - (Management or Application) | ||
* | ||
* @method getQueryType | ||
* @private | ||
* @return {string} the call type | ||
*/ | ||
function getQueryType() { | ||
return _queryType; | ||
} | ||
/* | ||
* A private method to set the type of the current api call - (Management or Application) | ||
* | ||
* @method setQueryType | ||
* @private | ||
* @param {string} call type | ||
* @return none | ||
*/ | ||
function setQueryType(type) { | ||
_queryType = type; | ||
} | ||
/* | ||
* A private method to build the curl call to display on the command line | ||
* | ||
* @method buildCurlCall | ||
* @private | ||
* @param {object} Query | ||
* @param {string} endpoint | ||
* @return none | ||
*/ | ||
function buildCurlCall(Query, endpoint) { | ||
var curl = 'curl'; | ||
try { | ||
//peel the data out of the query object | ||
var method = Query.getMethod().toUpperCase(); | ||
var path = Query.getResource(); | ||
var jsonObj = Query.getJsonObj() || {}; | ||
var params = Query.getQueryParams() || {}; | ||
//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 bearer token if this is not the sandbox app | ||
var application_name = ApiClient.getApplicationName(); | ||
if (application_name) { | ||
application_name = application_name.toUpperCase(); | ||
} | ||
if ( _authType == USER && ((application_name != 'SANDBOX' && ApiClient.getToken()) || (getQueryType() == M && ApiClient.getToken())) ) { | ||
curl += ' -i -H "Authorization: Bearer ' + ApiClient.getToken() + '"'; | ||
Query.setToken(true); | ||
} | ||
path = ApiClient.getApiUrl() + '/' + endpoint + '/' + path; | ||
//make sure path never has more than one / together | ||
if (path) { | ||
//regex to strip multiple slashes | ||
while(path.indexOf('//') != -1){ | ||
path = path.replace('//', '/'); | ||
} | ||
} | ||
//curl - append the path | ||
curl += ' "http://' + path; | ||
//curl - append params to the path for curl prior to adding the timestamp | ||
var curl_encoded_params = encodeParams(params); | ||
if (curl_encoded_params) { | ||
curl += "?" + curl_encoded_params; | ||
} | ||
curl += '"'; | ||
jsonObj = JSON.stringify(jsonObj) | ||
if (jsonObj && jsonObj != '{}') { | ||
//curl - add in the json obj | ||
curl += " -d '" + jsonObj + "'"; | ||
} | ||
} catch(e) { | ||
console.log('Unable to build curl call:' + e); | ||
} | ||
//log the curl command to the console | ||
console.log(curl); | ||
//store the curl command back in the object | ||
Query.setCurl(curl); | ||
return curl; | ||
} | ||
/** | ||
* A private method to validate, prepare,, and make the calls to the API | ||
* Use runAppQuery or runManagementQuery to make your calls! | ||
* | ||
* @method run | ||
* @private | ||
* @params {object} Query - {method, path, jsonObj, params, successCallback, failureCallback} | ||
* @params {string} endpoint - used to differentiate between management and app queries | ||
* @return {response} callback functions return API response object | ||
*/ | ||
function run (Query, endpoint) { | ||
//validate parameters | ||
try { | ||
//for timing, call start | ||
Query.setQueryStartTime(); | ||
//peel the data out of the query object | ||
var method = Query.getMethod().toUpperCase(); | ||
var path = Query.getResource(); | ||
var jsonObj = Query.getJsonObj() || {}; | ||
//add the client secret and id to the params if available | ||
var params = Query.getQueryParams() || {}; | ||
if (_authType == CSID && (_clientId != null && _clientSecret != null)) { | ||
params['client_id'] = _clientId; | ||
params['client_secret'] = _clientSecret; | ||
} | ||
//add in the cursor if one is available | ||
if (Query.getCursor()) { | ||
params.cursor = Query.getCursor(); | ||
} else { | ||
delete params.cursor; | ||
} | ||
Query.setQueryParams(params); | ||
//method - should be GET, POST, PUT, or DELETE only | ||
if (method != 'GET' && method != 'POST' && method != 'PUT' && method != 'DELETE') { | ||
throw(new Error('Invalid method - should be GET, POST, PUT, or DELETE.')); | ||
} | ||
//params - make sure we have a valid json object | ||
_params = JSON.stringify(params); | ||
if (!JSON.parse(_params)) { | ||
throw(new Error('Params object is not valid.')); | ||
} | ||
//add the endpoint to the path | ||
path = '/' + endpoint + '/' + path; | ||
//make sure path never has more than one / together | ||
if (path) { | ||
//regex to strip multiple slashes | ||
while(path.indexOf('//') != -1){ | ||
path = path.replace('//', '/'); | ||
} | ||
} | ||
//add in a timestamp for gets and deletes - to avoid caching by the browser | ||
if ((method == "GET") || (method == "DELETE")) { | ||
params['_'] = new Date().getTime(); | ||
} | ||
//append params to the path | ||
var encoded_params = encodeParams(params); | ||
if (encoded_params) { | ||
path += "?" + encoded_params; | ||
} | ||
//jsonObj - make sure we have a valid json object | ||
jsonObj = JSON.stringify(jsonObj) | ||
if (!JSON.parse(jsonObj)) { | ||
throw(new Error('JSON object is not valid.')); | ||
} | ||
//but clear it out if it is empty | ||
if (jsonObj == '{}') { | ||
jsonObj = null; | ||
} | ||
} catch (e) { | ||
//parameter was invalid | ||
console.log('error occured running query -' + e.message); | ||
return false; | ||
} | ||
//build the curl call | ||
buildCurlCall(Query, endpoint); | ||
//make the call to the API | ||
var http = require('http'); | ||
//params for the call | ||
var options = { | ||
host: ApiClient.getApiUrl(), | ||
path: path, | ||
method: method | ||
}; | ||
var headers = {}; | ||
//add a header with the oauth token if needed | ||
if (_authType == USER && ApiClient.getToken()) { | ||
headers['Authorization'] = "Bearer " + ApiClient.getToken(); | ||
} | ||
//add a header for content type if we have a request body | ||
if (jsonObj) { | ||
headers['Content-Type'] = "application/json"; | ||
} | ||
//add the headers | ||
options['headers'] = headers; | ||
//make the call | ||
var response = ''; | ||
var req = http.request(options, function(res) { | ||
res.setEncoding('utf8'); | ||
//capture the data as it comes back | ||
res.on('data', function (chunk) { | ||
response += chunk; | ||
}); | ||
res.on('end', function () { | ||
//for timing, call end | ||
Query.setQueryEndTime(); | ||
//for timing, log the total call time | ||
console.log(Query.getQueryTotalTime()); | ||
//convert the response to an object if possible | ||
try {response = JSON.parse(response);} catch (e) {} | ||
if (res.statusCode != 200) { | ||
if ( (response.error == "auth_expired_session_token") || | ||
(response.error == "unauthorized") || | ||
(response.error == "auth_missing_credentials") || | ||
(response.error == "auth_invalid")) { | ||
//this error type means the user is not authorized. If a logout function is defined, call it | ||
console.log(response.error); | ||
callLogoutCallback(); | ||
}else { | ||
Query.callFailureCallback(response); | ||
} | ||
} else { | ||
//query completed succesfully, so store cursor | ||
var cursor = response.cursor || null; | ||
Query.saveCursor(cursor); | ||
console.log(res.statusCode); | ||
Query.callSuccessCallback(response); | ||
} | ||
}); | ||
}); | ||
//add the request body if there is one | ||
if (method == 'POST' || method == 'PUT') { | ||
if (jsonObj) { | ||
req.write(jsonObj); | ||
} | ||
} | ||
//deal with any errors (usually network errors) | ||
req.on('error', function(e) { | ||
//for timing, call end | ||
Query.setQueryEndTime(); | ||
//for timing, log the total call time | ||
console.log(Query.getQueryTotalTime()); | ||
//clear the timeout | ||
var response = ''; | ||
console.log('API call error: ' + e.message); | ||
if (e.code == 'ENOTFOUND') { | ||
response = 'Could not connect to the API'; | ||
} else { | ||
response = e.message; | ||
} | ||
Query.callFailureCallback(response); | ||
}); | ||
//set up the timeout, just in case the meeting runs long | ||
req.setTimeout( ApiClient.getCallTimeout(), | ||
function() { | ||
console.log('API call timed out'); | ||
if (ApiClient.getCallTimeoutCallback() === 'function') { | ||
ApiClient.callTimeoutCallback('API CALL TIMEOUT'); | ||
} else { | ||
Query.callFailureCallback('API CALL TIMEOUT'); | ||
} | ||
} | ||
); | ||
//signal the end of the request | ||
req.end(); | ||
} | ||
return { | ||
init:init, | ||
setClientSecretCombo:setClientSecretCombo, | ||
enableClientSecretAuth:enableClientSecretAuth, | ||
enableUserAuth:enableUserAuth, | ||
runAppQuery:runAppQuery, | ||
runManagementQuery:runManagementQuery, | ||
getOrganizationName:getOrganizationName, | ||
setOrganizationName:setOrganizationName, | ||
getApplicationName:getApplicationName, | ||
setApplicationName:setApplicationName, | ||
getToken:getToken, | ||
setToken:setToken, | ||
getCallTimeout:getCallTimeout, | ||
setCallTimeout:setCallTimeout, | ||
getCallTimeoutCallback:getCallTimeoutCallback, | ||
setCallTimeoutCallback:setCallTimeoutCallback, | ||
callTimeoutCallback:callTimeoutCallback, | ||
getApiUrl:getApiUrl, | ||
setApiUrl:setApiUrl, | ||
getResetPasswordUrl:getResetPasswordUrl, | ||
getLoggedInUser:getLoggedInUser, | ||
setLoggedInUser:setLoggedInUser, | ||
logInAppUser:logInAppUser, | ||
renewAppUserToken:renewAppUserToken, | ||
logoutAppUser:logoutAppUser, | ||
isLoggedInAppUser:isLoggedInAppUser, | ||
getLogoutCallback:getLogoutCallback, | ||
setLogoutCallback:setLogoutCallback, | ||
callLogoutCallback:callLogoutCallback | ||
} | ||
})(); | ||
/** | ||
* validation is a Singleton that provides methods for validating common field types | ||
* | ||
* @class validation | ||
* @author Rod Simpson (rod@apigee.com) | ||
**/ | ||
validation = (function () { | ||
var usernameRegex = new RegExp("^([0-9a-zA-Z\.\-])+$"); | ||
var nameRegex = new RegExp("^([0-9a-zA-Z@#$%^&!?;:.,'\"~*-=+_\[\\](){}/\\ |])+$"); | ||
var emailRegex = new RegExp("^(([0-9a-zA-Z]+[_\+.-]?)+@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$"); | ||
var passwordRegex = new RegExp("^([0-9a-zA-Z@#$%^&!?<>;:.,'\"~*-=+_\[\\](){}/\\ |])+$"); | ||
var pathRegex = new RegExp("^([0-9a-z./-])+$"); | ||
var titleRegex = new RegExp("^([0-9a-zA-Z.!-?/])+$"); | ||
/** | ||
* Tests the string against the allowed chars regex | ||
* | ||
* @public | ||
* @method validateUsername | ||
* @param {string} username - The string to test | ||
* @param {function} failureCallback - (optional), the function to call on a failure | ||
* @return {boolean} Returns true if string passes regex, false if not | ||
*/ | ||
function validateUsername(username, failureCallback) { | ||
if (usernameRegex.test(username) && checkLength(username, 4, 80)) { | ||
return true; | ||
} else { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(this.getUsernameAllowedChars()); | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* Returns the regex of allowed chars | ||
* | ||
* @public | ||
* @method getUsernameAllowedChars | ||
* @return {string} Returns a string with the allowed chars | ||
*/ | ||
function getUsernameAllowedChars(){ | ||
return 'Length: min 4, max 80. Allowed: A-Z, a-z, 0-9, dot, and dash'; | ||
} | ||
/** | ||
* Tests the string against the allowed chars regex | ||
* | ||
* @public | ||
* @method validateName | ||
* @param {string} name - The string to test | ||
* @param {function} failureCallback - (optional), the function to call on a failure | ||
* @return {boolean} Returns true if string passes regex, false if not | ||
*/ | ||
function validateName(name, failureCallback) { | ||
if (nameRegex.test(name) && checkLength(name, 4, 16)) { | ||
return true; | ||
} else { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(this.getNameAllowedChars()); | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* Returns the regex of allowed chars | ||
* | ||
* @public | ||
* @method getNameAllowedChars | ||
* @return {string} Returns a string with the allowed chars | ||
*/ | ||
function getNameAllowedChars(){ | ||
return 'Length: min 4, max 80. Allowed: A-Z, a-z, 0-9, ~ @ # % ^ & * ( ) - _ = + [ ] { } \\ | ; : \' " , . / ? !'; | ||
} | ||
/** | ||
* Tests the string against the allowed chars regex | ||
* | ||
* @public | ||
* @method validatePassword | ||
* @param {string} password - The string to test | ||
* @param {function} failureCallback - (optional), the function to call on a failure | ||
* @return {boolean} Returns true if string passes regex, false if not | ||
*/ | ||
function validatePassword(password, failureCallback) { | ||
if (passwordRegex.test(password) && checkLength(password, 5, 16)) { | ||
return true; | ||
} else { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(this.getPasswordAllowedChars()); | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* Returns the regex of allowed chars | ||
* | ||
* @public | ||
* @method getPasswordAllowedChars | ||
* @return {string} Returns a string with the allowed chars | ||
*/ | ||
function getPasswordAllowedChars(){ | ||
return 'Length: min 5, max 16. Allowed: A-Z, a-z, 0-9, ~ @ # % ^ & * ( ) - _ = + [ ] { } \\ | ; : \' " , . < > / ? !'; | ||
} | ||
/** | ||
* Tests the string against the allowed chars regex | ||
* | ||
* @public | ||
* @method validateEmail | ||
* @param {string} email - The string to test | ||
* @param {function} failureCallback - (optional), the function to call on a failure | ||
* @return {boolean} Returns true if string passes regex, false if not | ||
*/ | ||
function validateEmail(email, failureCallback) { | ||
if (emailRegex.test(email) && checkLength(email, 4, 80)) { | ||
return true; | ||
} else { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(this.getEmailAllowedChars()); | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* Returns the regex of allowed chars | ||
* | ||
* @public | ||
* @method getEmailAllowedChars | ||
* @return {string} Returns a string with the allowed chars | ||
*/ | ||
function getEmailAllowedChars(){ | ||
return 'Email must be in standard form: e.g. example@com'; | ||
} | ||
/** | ||
* Tests the string against the allowed chars regex | ||
* | ||
* @public | ||
* @method validatePath | ||
* @param {string} path - The string to test | ||
* @param {function} failureCallback - (optional), the function to call on a failure | ||
* @return {boolean} Returns true if string passes regex, false if not | ||
*/ | ||
function validatePath(path, failureCallback) { | ||
if (pathRegex.test(path) && checkLength(path, 4, 80)) { | ||
return true; | ||
} else { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(this.getPathAllowedChars()); | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* Returns the regex of allowed chars | ||
* | ||
* @public | ||
* @method getPathAllowedChars | ||
* @return {string} Returns a string with the allowed chars | ||
*/ | ||
function getPathAllowedChars(){ | ||
return 'Length: min 4, max 80. Allowed: /, a-z, 0-9, dot, and dash'; | ||
} | ||
/** | ||
* Tests the string against the allowed chars regex | ||
* | ||
* @public | ||
* @method validateTitle | ||
* @param {string} title - The string to test | ||
* @param {function} failureCallback - (optional), the function to call on a failure | ||
* @return {boolean} Returns true if string passes regex, false if not | ||
*/ | ||
function validateTitle(title, failureCallback) { | ||
if (titleRegex.test(title) && checkLength(title, 4, 80)) { | ||
return true; | ||
} else { | ||
if (failureCallback && typeof(failureCallback) == "function") { | ||
failureCallback(this.getTitleAllowedChars()); | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* Returns the regex of allowed chars | ||
* | ||
* @public | ||
* @method getTitleAllowedChars | ||
* @return {string} Returns a string with the allowed chars | ||
*/ | ||
function getTitleAllowedChars(){ | ||
return 'Length: min 4, max 80. Allowed: space, A-Z, a-z, 0-9, dot, dash, /, !, and ?'; | ||
} | ||
/** | ||
* Tests if the string is the correct length | ||
* | ||
* @public | ||
* @method checkLength | ||
* @param {string} string - The string to test | ||
* @param {integer} min - the lower bound | ||
* @param {integer} max - the upper bound | ||
* @return {boolean} Returns true if string is correct length, false if not | ||
*/ | ||
function checkLength(string, min, max) { | ||
if (string.length > max || string.length < min) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** | ||
* 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); | ||
} | ||
return { | ||
validateUsername:validateUsername, | ||
getUsernameAllowedChars:getUsernameAllowedChars, | ||
validateName:validateName, | ||
getNameAllowedChars:getNameAllowedChars, | ||
validatePassword:validatePassword, | ||
getPasswordAllowedChars:getPasswordAllowedChars, | ||
validateEmail:validateEmail, | ||
getEmailAllowedChars:getEmailAllowedChars, | ||
validatePath:validatePath, | ||
getPathAllowedChars:getPathAllowedChars, | ||
validateTitle:validateTitle, | ||
getTitleAllowedChars:getTitleAllowedChars, | ||
isUUID:isUUID | ||
} | ||
})(); | ||
var sessionEntity = new Entity('session'); | ||
session = { | ||
/** | ||
* A public function to return the session id (pulls from the cookie) | ||
* | ||
* @method get_session_id | ||
* @public | ||
* @params {object} request | ||
* @return {string} sessionId | ||
*/ | ||
get_session_id: function(request){ | ||
var cookies = {}; | ||
request.headers.cookie && request.headers.cookie.split(';').forEach(function( cookie ) { | ||
var parts = cookie.split('='); | ||
cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim(); | ||
}); | ||
//get our coookie | ||
sessionId = cookies['session']; | ||
return sessionId; | ||
}, | ||
/** | ||
* A public function to start the session. Tries to get the session data from the database. | ||
* If the session object can be retrieved, use it. If not, then try to create a new one | ||
* | ||
* @method start_session | ||
* @public | ||
* @params {object} request | ||
* @params {object} response | ||
* @params {function} successCallback | ||
* @params {function} failureCallback | ||
* @return none | ||
*/ | ||
start_session: function(request, response, successCallback, failureCallback) { | ||
var sessionId = session.get_session_id(request) | ||
if (sessionId) { | ||
sessionEntity.set('uuid', sessionId); | ||
sessionEntity.fetch( | ||
function(output) { | ||
//got the session so store the value for the cookie | ||
sessionId = sessionEntity.get('uuid'); | ||
console.log("Starting session with id:" + sessionId ); | ||
response.setHeader('Set-Cookie', "session="+sessionId); | ||
successCallback(output); | ||
}, | ||
function (output) { | ||
console.log("No sesssion found, so making new one"); | ||
//didn't get one back, so try to make a new one | ||
sessionEntity.set('uuid',null);//wipe out the previous uuid | ||
session.save_session(response, successCallback, failureCallback, true); | ||
} | ||
); | ||
} else { | ||
//make a new session | ||
session.save_session(response, successCallback, failureCallback, true); | ||
} | ||
}, | ||
/** | ||
* A public function to save the session. | ||
* | ||
* @method save_session | ||
* @public | ||
* @params {object} request | ||
* @params {object} response | ||
* @params {function} successCallback | ||
* @params {function} failureCallback | ||
* @params {bool} startSession | ||
* @return none | ||
*/ | ||
save_session: function (response, successCallback, failureCallback, startSession) { | ||
sessionEntity.save( | ||
function(output) { | ||
//got the session so store the value for the cookie | ||
sessionId = sessionEntity.get('uuid'); | ||
console.log("Saved session with id:" + sessionId ); | ||
if (startSession) { | ||
response.setHeader('Set-Cookie', "session="+sessionId); | ||
} | ||
successCallback(output); | ||
}, | ||
function (output) { | ||
console.log("Failed to start session. Did you remember to set your client secret and id in server.js?" ); | ||
//failureCallback(output); | ||
} | ||
); | ||
}, | ||
/** | ||
* A public function to get a single item from the session. Pulls from local storage, not the database. | ||
* | ||
* @method getItem | ||
* @public | ||
* @params {string} key | ||
* @return the data for the specified key key | ||
*/ | ||
getItem: function(key) { | ||
return sessionEntity.get(key); | ||
}, | ||
/** | ||
* A public function to set a single item in the session. Writes to local storage, not the database. | ||
* Must call save_session to save the session to the database | ||
* | ||
* @method setItem | ||
* @public | ||
* @params {string} key | ||
* @params {data} value | ||
* @return none | ||
*/ | ||
setItem: function(key, value) { | ||
console.log('Adding to sesssion...'+key); | ||
sessionEntity.set(key, value); | ||
}, | ||
removeItem: function(key) { | ||
sessionEntity.set(key, null); | ||
}, | ||
kill_session: function() { | ||
sessionEntity.destroy( | ||
function(output) { | ||
successCallback(); | ||
}, | ||
function (output) { | ||
failureCallback(); | ||
} | ||
); | ||
}, | ||
garbage_collection: function(successCallback, failureCallback){ | ||
//our goal is to delete any sessions that are older than 24 hours | ||
//how we calculate our value: | ||
var minutes=1000*60; | ||
var hours=minutes*60; | ||
var one_day=hours*24; | ||
timestamp = (new Date()).getTime()- one_day; //24 hours ago | ||
//we want to delete any sessions older than one day: | ||
params = {"filter":"modified lt "+timestamp}; | ||
ApiClient.runAppQuery(new Query('DELETE', 'sessions', null, params, | ||
successCallback, | ||
failureCallback | ||
)); | ||
} | ||
}; | ||
exports.Query = Query; | ||
exports.Entity = Entity; | ||
exports.Collection = Collection; | ||
exports.ApiClient = ApiClient; | ||
exports.session = session; | ||
exports.client = Client; | ||
exports.entity = Entity; | ||
exports.collection = Collection; | ||
exports.AUTH_CLIENT_ID = AUTH_CLIENT_ID; | ||
exports.AUTH_APP_USER = AUTH_APP_USER; | ||
exports.AUTH_NONE = AUTH_NONE; |
{ | ||
"name": "usergrid", | ||
"version": "0.9.2", | ||
"version": "0.10.0", | ||
"description": "A Node.js module for making API calls to App Services (Usergrid) from within Node.js", | ||
"main": "./lib/usergrid.js", | ||
"dependencies": { | ||
"request": "2.12.x" | ||
}, | ||
"devDependencies": { | ||
"mocha": "1.7.x", | ||
"should": "1.2.1" | ||
}, | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"start": "node test/server.js" | ||
"test": "mocha", | ||
"start": "node example/server.js" | ||
}, | ||
@@ -14,2 +21,5 @@ "repository": { | ||
}, | ||
"bugs": { | ||
"url": "http://github.com/apigee/usergrid-node-module.git/issues" | ||
}, | ||
"keywords": [ | ||
@@ -21,5 +31,14 @@ "Node", | ||
], | ||
"author": "Rod Simpson", | ||
"tags": [ | ||
"usergrid", | ||
"API", | ||
"Apigee" | ||
], | ||
"author": { | ||
"name": "Rod Simpson", | ||
"email": "rod@rodsimpson.com", | ||
"url" : "http://rodsimpson.com" | ||
}, | ||
"license": "Apache 2.0" | ||
} | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
2
556
0
1
69020
1
2
9
1776
+ Addedrequest@2.12.x
+ Addedrequest@2.12.0(transitive)