<commit_before>'use strict';

var util = require('util'),
	http = require('http'),
	actionhelper = require('./actionhelper'),
	ApiHttpError = require('./errors').ApiHttpError,
	cache = require('./cache'),
	querystring = require('querystring'),
	_ = require('lodash'),
	helpers = require('./helpers'),
	oauthHelper = require('./oauth'),
	USER_AGENT = 'Node.js HTTP Client';

// Formats request parameters as expected by the API.
//
// - @param {Object} data - hash of pararameters
// - @param {String} consumerkey - consumer key
// - @return {String} - Encoded parameter string
function prepare(data, consumerkey) {
	var prop;
	data = data || {};

	for (prop in data) {
		if (data.hasOwnProperty(prop)) {
			if (_.isDate(data[prop])) {
				data[prop] = helpers.toYYYYMMDD(data[prop]);
			}
		}
	}

	data.oauth_consumer_key = consumerkey;

	return data;
}

// Generates the default request headers
//
// - @return {Object}
function createHeaders(host) {
	return {
		'host': host,
		'User-Agent' : USER_AGENT
	};
}

// Makes a GET request to the API.
//
// - @param {Object} endpointInfo - Generic metadata about the endpoint to hit.
// - @param {Object} requestData - Parameters for this specific request.
// - @param {Object} credentials - OAuth consumerkey and consumersecret.
// - @param {Object} logger - An object implementing the npm log levels.
// - @param {Object} theCache - A response cache.
// - @param {Function} callback - The callback to call with the response.
function get(endpointInfo, requestData, credentials, logger, theCache,
	callback) {

	var normalisedData = prepare(requestData, credentials.consumerkey),
		fullUrl = endpointInfo.url + '?' +
			querystring.stringify(normalisedData),
		cacheKey = cache.generateCacheKeyFromUrl(fullUrl),
		hostInfo = {
			host: endpointInfo.host,
			port: endpointInfo.port
		};

	theCache.get(cacheKey, checkCacheResult);
	function checkCacheResult(err, res) {
		if (err) {
			logger.error('Error checking cache for %s', cacheKey);
			logger.error(err);
			return callback(err);
		}

		if (res) {
			logger.info('Cache hit for %s', cacheKey);
			return callback(null, res);
		}

		// Decide whether to make an oauth signed request or not
		if (endpointInfo.authtype) {
			dispatchSecure(endpointInfo.url, 'GET', requestData,
				endpointInfo.authtype, hostInfo, credentials, logger,
				cacheAndCallback);
		} else {
			dispatch(endpointInfo.url, 'GET', requestData, hostInfo,
				credentials, logger, cacheAndCallback);
		}

		function cacheAndCallback(err, requestData, response) {
			if (err) {
				return callback(err);
			}

			var ttl = cache.parseMaxAgeHeader(response);

			if (ttl) {
				theCache.set(cacheKey, requestData, ttl, callbackWithResult);
			} else {
				return callback(null, requestData, response);
			}

			function callbackWithResult(err) {
				if (err) {
					logger.error('Error setting %s to cache', cacheKey);
					logger.error(err);
					return callback(err);
				}

				logger.info('Cached %s for %s seconds', cacheKey, ttl);
				return callback(null, requestData, response);
			}
		}
	}
}

// Makes a POST request to the API.
//
// - @param {Object} endpointInfo - Generic metadata about the endpoint to hit.
// - @param {Object} requestData - Parameters for this specific request.
// - @param {Object} credentials - OAuth consumerkey and consumersecret.
// - @param {Object} logger - An object implementing the npm log levels.
// - @param {Function} callback - The callback to call with the response.
function post(endpointInfo, requestData, credentials, logger, callback) {
	var hostInfo = {
		host: endpointInfo.host,
		port: endpointInfo.port
	};

	if (endpointInfo.authtype) {
		dispatchSecure(endpointInfo.url, 'POST', requestData,
			endpointInfo.authtype, hostInfo, credentials, logger, callback);
	} else {
		dispatch(endpointInfo.url, 'POST', requestData, hostInfo, credentials,
			logger, callback);
	}
}

// Dispatches an oauth signed request to the API
//
// - @param {String} url - the path of the API url to request.
// - @param {String} httpMethod
// - @param {Object} requestData - hash of the parameters for the request.
// - @param {String} authType - OAuth request type: '2-legged' or '3-legged'
// - @param {Object} hostInfo - API host information
// - @param {Function} callback - The callback to call with the response.
function dispatchSecure(url, httpMethod, requestData, authtype, hostInfo,
		credentials, logger, callback) {

	var fullUrl = 'https://' + hostInfo.host + url,
		is2Legged = authtype === '2-legged',
		token = is2Legged ? null : requestData.accesstoken,
		secret = is2Legged ? null : requestData.accesssecret,
		oauthClient = oauthHelper.createOAuthWrapper(
			credentials.consumerkey, credentials.consumersecret);

	requestData = prepare(requestData, credentials.consumerkey);


	if (!is2Legged) {
		delete requestData.accesstoken;
		delete requestData.accesssecret;
	}

	if (httpMethod === 'GET') {
		logger.info('token: ' + token + ' secret: ' + secret);

		fullUrl = actionhelper.template(fullUrl, requestData);
		fullUrl = fullUrl + '?' + querystring.stringify(requestData);
		logger.info(httpMethod + ': ' + fullUrl + ' (' + authtype + ' oauth)');

		oauthClient.get(fullUrl, token, secret, callbackWithDataAndResponse);
	} else {
		logger.info(httpMethod + ': ' + fullUrl + ' (' + authtype + ' oauth)');
		// FIXME: This will not work with the playlist API which expects a
		// JSON payload
		oauthClient[httpMethod.toLowerCase()](fullUrl, token, secret,
			requestData, 'application/x-www-form-urlencoded',
			callbackWithDataAndResponse);
	}

	function callbackWithDataAndResponse(err, data, response) {
		if (err) {
			logger.error(err);
			return callback(err);
		}

		callback(null, data, response);
	}

	logger.info('DATA: ' + querystring.stringify(requestData));
}

// Dispatches requests to the API.  Serializes the data in keeping with the API
// specification and applies approriate HTTP headers.
//
// - @param {String} url - the URL on the API to make the request to.
// - @param {String} httpMethod
// - @param {Object} data - hash of the parameters for the request.
// - @param {Object} hostInfo - hash of host, port and prefix
// - @param {Object} credentials - hash of oauth consumer key and secret
// - @param {Object} logger - an object implementing the npm log levels
// - @param {Function} callback
function dispatch(url, httpMethod, data, hostInfo, credentials, logger,
	callback) {

	var apiRequest, headers, prop, hasErrored;<commit_msg>Tidy up dispatchSecure<commit_after>'use strict';

var util = require('util'),
	http = require('http'),
	actionhelper = require('./actionhelper'),
	ApiHttpError = require('./errors').ApiHttpError,
	cache = require('./cache'),
	querystring = require('querystring'),
	_ = require('lodash'),
	helpers = require('./helpers'),
	oauthHelper = require('./oauth'),
	USER_AGENT = 'Node.js HTTP Client';

// Formats request parameters as expected by the API.
//
// - @param {Object} data - hash of pararameters
// - @param {String} consumerkey - consumer key
// - @return {String} - Encoded parameter string
function prepare(data, consumerkey) {
	var prop;
	data = data || {};

	for (prop in data) {
		if (data.hasOwnProperty(prop)) {
			if (_.isDate(data[prop])) {
				data[prop] = helpers.toYYYYMMDD(data[prop]);
			}
		}
	}

	data.oauth_consumer_key = consumerkey;

	return data;
}

// Generates the default request headers
//
// - @return {Object}
function createHeaders(host) {
	return {
		'host': host,
		'User-Agent' : USER_AGENT
	};
}

// Makes a GET request to the API.
//
// - @param {Object} endpointInfo - Generic metadata about the endpoint to hit.
// - @param {Object} requestData - Parameters for this specific request.
// - @param {Object} credentials - OAuth consumerkey and consumersecret.
// - @param {Object} logger - An object implementing the npm log levels.
// - @param {Object} theCache - A response cache.
// - @param {Function} callback - The callback to call with the response.
function get(endpointInfo, requestData, credentials, logger, theCache,
	callback) {

	var normalisedData = prepare(requestData, credentials.consumerkey),
		fullUrl = endpointInfo.url + '?' +
			querystring.stringify(normalisedData),
		cacheKey = cache.generateCacheKeyFromUrl(fullUrl),
		hostInfo = {
			host: endpointInfo.host,
			port: endpointInfo.port
		};

	theCache.get(cacheKey, checkCacheResult);
	function checkCacheResult(err, res) {
		if (err) {
			logger.error('Error checking cache for %s', cacheKey);
			logger.error(err);
			return callback(err);
		}

		if (res) {
			logger.info('Cache hit for %s', cacheKey);
			return callback(null, res);
		}

		// Decide whether to make an oauth signed request or not
		if (endpointInfo.authtype) {
			dispatchSecure(endpointInfo.url, 'GET', requestData,
				endpointInfo.authtype, hostInfo, credentials, logger,
				cacheAndCallback);
		} else {
			dispatch(endpointInfo.url, 'GET', requestData, hostInfo,
				credentials, logger, cacheAndCallback);
		}

		function cacheAndCallback(err, requestData, response) {
			if (err) {
				return callback(err);
			}

			var ttl = cache.parseMaxAgeHeader(response);

			if (ttl) {
				theCache.set(cacheKey, requestData, ttl, callbackWithResult);
			} else {
				return callback(null, requestData, response);
			}

			function callbackWithResult(err) {
				if (err) {
					logger.error('Error setting %s to cache', cacheKey);
					logger.error(err);
					return callback(err);
				}

				logger.info('Cached %s for %s seconds', cacheKey, ttl);
				return callback(null, requestData, response);
			}
		}
	}
}

// Makes a POST request to the API.
//
// - @param {Object} endpointInfo - Generic metadata about the endpoint to hit.
// - @param {Object} requestData - Parameters for this specific request.
// - @param {Object} credentials - OAuth consumerkey and consumersecret.
// - @param {Object} logger - An object implementing the npm log levels.
// - @param {Function} callback - The callback to call with the response.
function post(endpointInfo, requestData, credentials, logger, callback) {
	var hostInfo = {
		host: endpointInfo.host,
		port: endpointInfo.port
	};

	if (endpointInfo.authtype) {
		dispatchSecure(endpointInfo.url, 'POST', requestData,
			endpointInfo.authtype, hostInfo, credentials, logger, callback);
	} else {
		dispatch(endpointInfo.url, 'POST', requestData, hostInfo, credentials,
			logger, callback);
	}
}

// Dispatches an oauth signed request to the API
//
// - @param {String} url - the path of the API url to request.
// - @param {String} httpMethod
// - @param {Object} requestData - hash of the parameters for the request.
// - @param {String} authType - OAuth request type: '2-legged' or '3-legged'
// - @param {Object} hostInfo - API host information
// - @param {Function} callback - The callback to call with the response.
function dispatchSecure(url, httpMethod, requestData, authtype, hostInfo,
		credentials, logger, callback) {

	var fullUrl = 'https://' + hostInfo.host + url,
		is2Legged = authtype === '2-legged',
		token = is2Legged ? null : requestData.accesstoken,
		secret = is2Legged ? null : requestData.accesssecret,
		oauthClient = oauthHelper.createOAuthWrapper(
			credentials.consumerkey, credentials.consumersecret);

	requestData = prepare(requestData, credentials.consumerkey);


	if (!is2Legged) {
		delete requestData.accesstoken;
		delete requestData.accesssecret;
	}

	if (httpMethod === 'GET') {
		logger.info('token: ' + token + ' secret: ' + secret);

		fullUrl = actionhelper.template(fullUrl, requestData);
		fullUrl = fullUrl + '?' + querystring.stringify(requestData);
		logger.info(httpMethod + ': ' + fullUrl + ' (' + authtype + ' oauth)');

		return oauthClient.get(fullUrl, token, secret,
			callbackWithDataAndResponse);
	}

	if (httpMethod === 'POST') {
		logger.info(httpMethod + ': ' + fullUrl + ' (' + authtype + ' oauth)');
		logger.info('DATA: ' + querystring.stringify(requestData));

		return oauthClient.post(fullUrl, token, secret, requestData,
			'application/x-www-form-urlencoded', callbackWithDataAndResponse);
	}

	function callbackWithDataAndResponse(err, data, response) {
		if (err) {
			logger.error(err);
			return callback(err);
		}

		return callback(null, data, response);
	}

}

// Dispatches requests to the API.  Serializes the data in keeping with the API
// specification and applies approriate HTTP headers.
//
// - @param {String} url - the URL on the API to make the request to.
// - @param {String} httpMethod
// - @param {Object} data - hash of the parameters for the request.
// - @param {Object} hostInfo - hash of host, port and prefix
// - @param {Object} credentials - hash of oauth consumer key and secret
// - @param {Object} logger - an object implementing the npm log levels
// - @param {Function} callback
function dispatch(url, httpMethod, data, hostInfo, credentials, logger,
	callback) {

	var apiRequest, headers, prop, hasErrored;
{'fraction_non_alphanumeric': 0.12998454404945906, 'fraction_numerical': 0.0010819165378670788, 'mean_word_length': 3.848632446609217, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1946497', 'n_tokens_mistral': 4267, 'n_tokens_neox': 3896, 'n_words': 1170}