UNPKG

node-parse-api

Version:

A Parse.com REST API client for Node.js

474 lines (400 loc) 17.1 kB
var qs = require('querystring'); qs.escape = function(q){ return q; }; module.exports = Parse; function Parse(options_or_application_id, master_key) { this._options = {}; if (master_key) { this._options.app_id = options_or_application_id; this._options.master_key = master_key; } else { this._options = options_or_application_id; } } Parse.prototype = { _api_protocol: require('https'), _api_host: 'api.parse.com', _api_port: 443, // add object to class store insert: function (className, object, callback) { parseRequest.call(this, 'POST', '/1/classes/' + className, object, callback); }, // add files insertFile: function(fileName, data, contentType, callback){ parseRequest.call(this, 'POST', '/1/files/' + fileName, data, callback, contentType); }, // create a new user insertUser: function (data, callback) { parseRequest.call(this, 'POST', '/1/users/', data, callback); }, findWithObjectId: function (className, objectId, callback){ var url = '/1/' + (className === '_User' ? 'users' : 'classes/' + className + '/' + objectId); parseRequest.call(this, 'GET', url, null, callback); }, // get an object from the class store find: function (className, query, callback) { var url = '/1/' + (className === '_User' ? 'users' : 'classes/' + className); var queryType = typeof query; if ( queryType === 'string' ) { url += '/' + query; } else if ( queryType === 'object' ) { // if the user wants to add 'include' or 'key' (or other types of) constraints while getting only one object // objectId can be added to the query object and is deleted after it's appended to the url if ( query.hasOwnProperty('objectId') ) { url += '/' + query.objectId; delete query.objectId; } // check to see if there is a 'where' object in the query object // the 'where' object need to be stringified by JSON.stringify(), not querystring if ( query.hasOwnProperty('where') ) { url += '?where=' + encodeURIComponent(JSON.stringify(query.where)); delete query.where; } // if there are no more constraints left in the query object 'remainingQuery' will be an empty string var remainingQuery = qs.stringify(query); if ( remainingQuery ) { url += ( url.indexOf('?') === -1 ? '?' : '&' ) + remainingQuery; } } parseRequest.call(this, 'GET', url, null, callback); }, // get a collection of objects findMany: function (className, query, order, limit, skip, callback) { console.warn('"findMany" is deprecated, use "find" instead.'); switch ( arguments.length ) { case 3: callback = order; break; case 5: query.order = order; query.limit = limit; callback = skip; break; case 6: query.order = order; query.limit = limit; query.skip = skip; break; default: throw new Error('Unexpected number of arguments.'); } this.find(className, query, callback); }, // do a batch of requests at once batch: function (requests,callback) { parseRequest.call(this,'POST','/1/batch',{requests:requests},callback); }, // user login loginUser: function (username, password, callback) { parseRequest.call(this, 'GET', '/1/login/?username=' + username + '&password=' + password, null, callback); }, // user logout logoutUser: function (sessionToken, callback) { parseRequest.call(this, 'POST', '/1/logout', null, callback, null, sessionToken); }, // retrieve current user me: function (sessionToken, callback) { parseRequest.call(this, 'GET', '/1/users/me', null, callback, null, sessionToken); }, // retrieve contents of one or more user objects findUser: function (query, callback) { if ( arguments.length !== 2 ) { throw new Error('Unexpected number of arguments.'); } else { this.find('_User', query, callback); } }, // get an object belonging to a certain User findFileByUser: function(userId, className, callback) { queryString = 'where={"user":' + '"' + userId + '"' + '}'; encodedString = encodeURIComponent(queryString); parseRequest.call(this, 'GET', '/1/classes/' + className + '?' + encodedString, null, callback); }, getUser: function (query, callback, deprecatedCallback) { console.warn('getUser is deprecated, user findUser instead.'); if ( arguments.length === 3 ) { console.warn('Logging in with "getUser" is deprecated, use loginUser instead.'); console.warn('Use "findUser" to retrieve one or more user object contents.'); this.loginUser(query, callback, deprecatedCallback); } else { this.findUser(query, callback); } }, getFileByUser: function(userId, className, callback) { console.warn('getFileByUser is deprecated, user findFileByUser instead.'); this.findFileByUser(userId, className, callback); }, // insert an object into Parse insertCustom: function (className, object, callback) { parseRequest.call(this, 'POST', '/1/' + className, object, callback); }, // update an object in the class store update: function (className, objectId, object, callback) { parseRequest.call(this, 'PUT', '/1/classes/' + className + '/' + objectId, object, callback); }, // update a User object's email address updateUserEmail: function(objectId, data, sessionToken, callback) { switch ( arguments.length ) { case 3: callback = sessionToken; break; case 4: break; default: throw new Error('Unexpected number of arguments.'); } data = { email: data }; parseRequest.call(this, 'PUT', '/1/users/' + objectId, data, callback, null, sessionToken); }, // update a User object's username* updateUserName: function(objectId, data, sessionToken, callback) { switch ( arguments.length ) { case 3: callback = sessionToken; break; case 4: break; default: throw new Error('Unexpected number of arguments.'); } data = { username: data }; parseRequest.call(this, 'PUT', '/1/users/' + objectId, data, callback, null, sessionToken); }, // update any keys of a User object updateUser: function (objectId, data, sessionToken, callback) { switch ( arguments.length ) { case 3: callback = sessionToken; break; case 4: break; default: throw new Error('Unexpected number of arguments.'); } parseRequest.call(this, 'PUT', '/1/users/' + objectId, data, callback, null, sessionToken); }, // reset a User object's password passwordReset: function (data, callback) { data = { email: data }; parseRequest.call(this, 'POST', '/1/requestPasswordReset/', data, callback); }, // remove an object from the class store delete: function (className, objectId, callback) { parseRequest.call(this, 'DELETE', '/1/classes/' + className + '/' + objectId, null, callback); }, // remove an object from the class store deleteUser: function (objectId, sessionToken, callback) { switch ( arguments.length ) { case 2: callback = sessionToken; break; case 3: break; default: throw new Error('Unexpected number of arguments'); } parseRequest.call(this, 'DELETE', '/1/users/' + objectId, null, callback, null, sessionToken); }, deleteAll: function(className, callback){ var that = this; this.find(className, '', function (err, response) { var requests = toDeleteOps(className, response.results); that.batch(requests, callback); }); }, deleteAllUsers: function(callback){ var that = this; this.find('_User', '', function (err, response) { var requests = toDeleteOps('_User', response.results); that.batch(requests, callback); }); }, // upload installation data insertInstallationData: function (deviceType, deviceToken, callback) { if (deviceType === 'ios'){ data = { deviceType: deviceType, deviceToken: deviceToken }; } else { data = { deviceType: deviceType, installationId: deviceToken }; } parseRequest.call(this, 'POST', '/1/installations/', data, callback); }, insertInstallationDataWithTimeZone: function (deviceType, deviceToken, timeZone, callback) { if (deviceType === 'ios'){ data = { deviceType: deviceType, deviceToken: deviceToken, timeZone: timeZone }; } else { data = { deviceType: deviceType, installationId: deviceToken, timeZone: timeZone }; } parseRequest.call(this, 'POST', '/1/installations/', data, callback); }, insertInstallationDataWithChannels: function (deviceType, deviceToken, channels, callback) { if (deviceType === 'ios'){ data = { deviceType: deviceType, deviceToken: deviceToken, channels: channels }; } else { data = { deviceType: deviceType, installationId: deviceToken, channels: channels }; } parseRequest.call(this, 'POST', '/1/installations/', data, callback); }, insertInstallationDataWithTimeZoneAndChannels: function (deviceType, deviceToken, timeZone, channels, callback) { if (deviceType === 'ios'){ data = { deviceType: deviceType, deviceToken: deviceToken, timeZone: timeZone, channels: channels }; } else { data = { deviceType: deviceType, installationId: deviceToken, timeZone: timeZone, channels: channels }; } parseRequest.call(this, 'POST', '/1/installations/', data, callback); }, updateInstallationDataChannels: function(objectId, channels, callback) { parseRequest.call(this, 'PUT', '/1/installations/' + objectId, { channels: channels }, callback); }, getInstallationData: function(callback) { parseRequest.call(this, 'GET', '/1/installations', null, callback); }, getInstallationDataForDeviceToken: function(deviceToken, callback) { parseRequest.call(this, 'GET', '/1/installations?where={"deviceToken":"'+deviceToken+'"}', null, callback); }, deleteInstallation: function(objectId, callback) { parseRequest.call(this, 'DELETE', '/1/installations/' + objectId, null, callback); }, upsertInstallation: function(deviceType, deviceToken, data, callback) { data.deviceType = deviceType; data.deviceToken = deviceToken; parseRequest.call(this, 'POST', '/1/installations', data, callback); }, insertOrUpdateInstallationDataWithChannels: function(deviceType, deviceToken, channels, callback) { var that = this; this.getInstallationDataForDeviceToken(deviceToken, function(err, results) { if (!err && results.results.length) { that.updateInstallationDataChannels(results.results[0].objectId, channels); return; } that.insertInstallationDataWithChannels(deviceType, deviceToken, channels, callback); }); }, countObjects: function (className, query, callback) { if (typeof(query) === "function") { parseRequest.call(this, 'GET', '/1/classes/' + className, null, query); } if (typeof(query) === "string") { parseRequest.call(this, 'GET', '/1/classes/' + className + '/' + query, null, callback); } else { parseRequest.call(this, 'GET', '/1/classes/' + className, { where: JSON.stringify(query) }, callback); } }, addRelation: function( relationName, className1, objectId1, className2, objectId2, callback) { data = {}; data[relationName] = { __op:"AddRelation",objects:[{__type:"Pointer",className:className2,objectId:objectId2}]}; parseRequest.call(this,'PUT','/1/classes/' + className1+'/'+objectId1,data,callback); }, removeRelation: function( relationName, className1, objectId1, className2, objectId2, callback) { data = {}; data[relationName] = { __op:"RemoveRelation",objects:[{__type:"Pointer",className:className2,objectId:objectId2}]}; parseRequest.call(this,'PUT','/1/classes/' + className1+'/'+objectId1,data,callback); }, insertRole: function (data, callback) { parseRequest.call(this, 'POST', '/1/roles/', data, callback); }, getRole: function (objectId, callback) { parseRequest.call(this, 'GET', '/1/roles/' + objectId, null, callback); }, getRoles: function (params, callback) { if (typeof(params) === "function") { parseRequest.call(this, 'GET', '/1/roles/', null, params); } if (typeof(params) === "string") { parseRequest.call(this, 'GET', '/1/roles/' + params, null, callback); } else { params = JSON.stringify(params); parseRequest.call(this, 'GET', '/1/roles/', params, callback); } }, updateRole: function (objectId, data, callback) { parseRequest.call(this, 'PUT', '/1/roles/' + objectId, data, callback); }, deleteRole: function (objectId, callback) { parseRequest.call(this, 'DELETE', '/1/roles/' + objectId, callback); }, sendPush: function (data, callback) { parseRequest.call(this, 'POST', '/1/push/', data, callback); }, getCurrentSession: function(objectId, callback) { parseRequest.call(this, 'GET', '/1/sessions/'+ objectId, callback); } }; // Parse.com https api request function parseRequest(method, path, data, callback, contentType, sessionToken) { var headers = { Connection: 'Keep-alive' }; if(this._options.master_key){ var auth = 'Basic ' + new Buffer(this._options.app_id + ':' + this._options.master_key).toString('base64'); headers.Authorization = auth; if ( sessionToken ) { throw new Error('Can\'t use session tokens while using the master key.'); } }else if(this._options.api_key){ headers['X-Parse-Application-Id'] = this._options.app_id; headers['X-Parse-REST-API-Key'] = this._options.api_key; if ( sessionToken ) { headers['X-Parse-Session-Token'] = sessionToken; } } var body = null; switch (method) { case 'GET': if (data) { path += (path.indexOf("?") == -1 ? '?' : '&') + qs.stringify(data); } break; case 'POST': case 'PUT': body = contentType ? data : typeof data === 'object' ? JSON.stringify(data) : data; if ( !contentType ) { headers['Content-length'] = Buffer.byteLength(body); } headers['Content-type'] = contentType || 'application/json'; break; case 'DELETE': headers['Content-length'] = 0; break; default: throw new Error('Unknown method, "' + method + '"'); } var options = { hostname: this._api_host, port: this._api_port, headers: headers, path: path, method: method }; var req = this._api_protocol.request(options, function (res) { if (!callback) { return; } var json = ''; res.setEncoding('utf8'); res.on('data', function (chunk) { json += chunk; }); res.on('end', function () { var data; try { data = JSON.parse(json); if ( data.code || data.error ) { throw (data); } callback(null, data); } catch (err) { callback(err); } }); res.on('close', function (err) { callback(err); }); }); req.setTimeout(5000, function () { req.connection.destroy(); }); body && req.write(body, contentType ? 'binary' : 'utf8'); req.end(); req.on('error', function (err) { callback && callback(err); }); } function toDeleteOps(className, objects){ return objects.map(function(object){ return { method: 'DELETE', path: '/1/' + ( className === '_User' ? 'users/' : 'classes/' + className + '/' ) + object.objectId }; }); }