helpers.js (3881B)
1 var querystring = require('querystring'); 2 var request = require('request'); 3 4 var endpoints = require('./endpoints'); 5 6 /** 7 * Encodes object as a querystring, to be used as the suffix of request URLs. 8 * @param {Object} obj 9 * @return {String} 10 */ 11 exports.makeQueryString = function (obj) { 12 var qs = querystring.stringify(obj) 13 qs = qs.replace(/\!/g, "%21") 14 .replace(/\'/g, "%27") 15 .replace(/\(/g, "%28") 16 .replace(/\)/g, "%29") 17 .replace(/\*/g, "%2A"); 18 return qs 19 } 20 21 /** 22 * For each `/:param` fragment in path, move the value in params 23 * at that key to path. If the key is not found in params, throw. 24 * Modifies both params and path values. 25 * 26 * @param {Objet} params Object used to build path. 27 * @param {String} path String to transform. 28 * @return {Undefined} 29 * 30 */ 31 exports.moveParamsIntoPath = function (params, path) { 32 var rgxParam = /\/:(\w+)/g 33 var missingParamErr = null 34 35 path = path.replace(rgxParam, function (hit) { 36 var paramName = hit.slice(2) 37 var suppliedVal = params[paramName] 38 if (!suppliedVal) { 39 throw new Error('Twit: Params object is missing a required parameter for this request: `'+paramName+'`') 40 } 41 var retVal = '/' + suppliedVal 42 delete params[paramName] 43 return retVal 44 }) 45 return path 46 } 47 48 /** 49 * When Twitter returns a response that looks like an error response, 50 * use this function to attach the error info in the response body to `err`. 51 * 52 * @param {Error} err Error instance to which body info will be attached 53 * @param {Object} body JSON object that is the deserialized HTTP response body received from Twitter 54 * @return {Undefined} 55 */ 56 exports.attachBodyInfoToError = function (err, body) { 57 err.twitterReply = body; 58 if (!body) { 59 return 60 } 61 if (body.error) { 62 // the body itself is an error object 63 err.message = body.error 64 err.allErrors = err.allErrors.concat([body]) 65 } else if (body.errors && body.errors.length) { 66 // body contains multiple error objects 67 err.message = body.errors[0].message; 68 err.code = body.errors[0].code; 69 err.allErrors = err.allErrors.concat(body.errors) 70 } 71 } 72 73 exports.makeTwitError = function (message) { 74 var err = new Error() 75 if (message) { 76 err.message = message 77 } 78 err.code = null 79 err.allErrors = [] 80 err.twitterReply = null 81 return err 82 } 83 84 /** 85 * Get a bearer token for OAuth2 86 * @param {String} consumer_key 87 * @param {String} consumer_secret 88 * @param {Function} cb 89 * 90 * Calls `cb` with Error, String 91 * 92 * Error (if it exists) is guaranteed to be Twit error-formatted. 93 * String (if it exists) is the bearer token received from Twitter. 94 */ 95 exports.getBearerToken = function (consumer_key, consumer_secret, cb) { 96 // use OAuth 2 for app-only auth (Twitter requires this) 97 // get a bearer token using our app's credentials 98 var b64Credentials = new Buffer(consumer_key + ':' + consumer_secret).toString('base64'); 99 request.post({ 100 url: endpoints.API_HOST + 'oauth2/token', 101 headers: { 102 'Authorization': 'Basic ' + b64Credentials, 103 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' 104 }, 105 body: 'grant_type=client_credentials', 106 json: true, 107 }, function (err, res, body) { 108 if (err) { 109 var error = exports.makeTwitError(err.toString()); 110 exports.attachBodyInfoToError(error, body); 111 return cb(error, body, res); 112 } 113 114 if ( !body ) { 115 var error = exports.makeTwitError('Not valid reply from Twitter upon obtaining bearer token'); 116 exports.attachBodyInfoToError(error, body); 117 return cb(error, body, res); 118 } 119 120 if (body.token_type !== 'bearer') { 121 var error = exports.makeTwitError('Unexpected reply from Twitter upon obtaining bearer token'); 122 exports.attachBodyInfoToError(error, body); 123 return cb(error, body, res); 124 } 125 126 return cb(err, body.access_token); 127 }) 128 }