top
Version:
Taobao Open API Client.
192 lines (179 loc) • 4.9 kB
JavaScript
/*!
* top - lib/client.js
* Copyright(c) 2012 - 2013 fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utility = require('utility');
var urllib = require('urllib');
var util = require('./util');
var Agent = require('agentkeepalive');
var HttpsAgent = Agent.HttpsAgent;
/**
* API Client.
*
* @param {Object} options, must set `appkey` and `appsecret`.
* @constructor
*/
function Client(options) {
if (!(this instanceof Client)) {
return new Client(options);
}
options = options || {};
if (!options.appkey || !options.appsecret) {
throw new Error('appkey or appsecret need!');
}
this.REST_URL = options.REST_URL || 'http://gw.api.taobao.com/router/rest';
this.appkey = options.appkey;
this.appsecret = options.appsecret;
this.isHttps = this.REST_URL.indexOf('https://') === 0;
if (this.isHttps) {
this.agent = new HttpsAgent();
} else {
this.agent = new Agent();
}
this.agent.maxSockets = 100;
}
/**
* Invoke an api by method name.
*
* @param {String} method, method name
* @param {Object} params
* @param {Array} reponseNames, e.g. ['tmall_selected_items_search_response', 'tem_list', 'selected_item']
* @param {Object} defaultResponse
* @param {String} type
* @param {Function(err, response)} callback
*/
Client.prototype.invoke = function (method, params, reponseNames, defaultResponse, type, callback) {
params.method = method;
this.request(params, type, function (err, result) {
if (err) {
return callback(err);
}
var response = result;
if (reponseNames && reponseNames.length > 0) {
for (var i = 0; i < reponseNames.length; i++) {
var name = reponseNames[i];
response = response[name];
if (response === undefined) {
break;
}
}
}
if (response === undefined) {
response = defaultResponse;
}
callback(null, response);
});
};
Client.prototype._wrapJSON = function (s) {
// warp fix #8: https://github.com/fengmk2/top/issues/8
var matchs = s.match(/\"id\"\:\s?\d{16,}/g);
if (matchs) {
for (var i = 0; i < matchs.length; i++) {
var m = matchs[i];
s = s.replace(m, '"id":"' + m.split(':')[1].trim() + '"');
}
}
return s;
};
var IGNORE_ERROR_CODES = {
'isv.user-not-exist:invalid-nick': 1,
// 'isv.user-not-exist:invalid-nick': 1,
};
/**
* Request API.
*
* @param {Object} params
* @param {String} [type='GET']
* @param {Function(err, result)} callback
* @public
*/
Client.prototype.request = function (params, type, callback) {
if (typeof type === 'function') {
callback = type;
type = null;
}
var err = util.checkRequired(params, 'method');
if (err) {
return callback(err);
}
var args = {
timestamp: this.timestamp(),
format: 'json',
app_key: this.appkey,
v: '2.0',
sign_method: 'md5'
};
for (var k in params) {
args[k] = params[k];
}
args.sign = this.sign(args);
type = type || 'GET';
var options = {type: type, data: args, agent: this.agent};
var that = this;
urllib.request(that.REST_URL, options, function (err, buffer) {
var data;
if (buffer) {
buffer = that._wrapJSON(buffer.toString());
try {
data = JSON.parse(buffer);
} catch (e) {
err = e;
e.data = buffer.toString();
data = null;
}
}
var errRes = data && data.error_response;
if (errRes) {
if (!errRes.sub_msg || !IGNORE_ERROR_CODES[errRes.sub_code]) {
// no sub_msg error, let caller handle it.
// request() only handle root error, like code 40, 41 error.
var msg = errRes.msg + ', code ' + errRes.code;
if (errRes.sub_msg) {
msg += '; ' + errRes.sub_code + ': ' + errRes.sub_msg;
}
err = new Error(msg);
err.name = 'TOPClientError';
err.code = errRes.code;
err.sub_code = errRes.sub_code;
err.data = buffer.toString();
data = null;
}
}
callback(err, data);
});
};
/**
* Get now timestamp with 'yyyy-MM-dd HH:mm:ss' format.
* @return {String}
*/
Client.prototype.timestamp = function () {
return utility.YYYYMMDDHHmmss();
};
/**
* Sign API request.
* see http://open.taobao.com/doc/detail.htm?id=111#s6
*
* @param {Object} params
* @return {String} sign string
*/
Client.prototype.sign = function (params) {
var sorted = Object.keys(params).sort();
var basestring = this.appsecret;
// (md5(secretkey1value1key2value2...secret))
for (var i = 0, l = sorted.length; i < l; i++) {
var k = sorted[i];
basestring += k + params[k];
}
basestring += this.appsecret;
return utility.md5(basestring).toUpperCase();
};
var api = require('./api');
// 扩展原型方法
Object.keys(api).forEach(function (key) {
Client.prototype[key] = api[key];
});
exports.Client = Client;