Coverage

98%
102
100
2

index.js

100%
3
3
0
LineHitsSource
1/*!
2 * top - index.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
7/**
8 * Module dependencies.
9 */
10
111var Client = exports.Client = require('./client').Client;
12
131exports.createClient = function(options) {
145 return new Client(options);
15};

client.js

97%
99
97
2
LineHitsSource
1/*!
2 * top - lib/client.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
7/**
8 * Module dependencies.
9 */
10
111var urllib = require('urllib');
121var crypto = require('crypto');
131var moment = require('moment');
141var Agent = require('agentkeepalive');
151var HttpsAgent = Agent.HttpsAgent;
16
171exports.Client = Client;
18
19/**
20 * API Client.
21 *
22 * @param {Object} options, must set `appkey` and `appsecret`.
23 * @constructor
24 */
251function Client(options) {
265 if (!(this instanceof Client)) {
270 return new Client(options);
28 }
295 options = options || {};
305 if (!options.appkey || !options.appsecret) {
313 throw new Error('appkey or appsecret need!');
32 }
332 this.REST_URL = options.REST_URL || 'http://gw.api.taobao.com/router/rest';
342 this.appkey = options.appkey;
352 this.appsecret = options.appsecret;
362 var isHttps = this.REST_URL.indexOf('https://') === 0;
372 if (isHttps) {
380 this.agent = new HttpsAgent();
39 } else {
402 this.agent = new Agent();
41 }
422 this.agent.maxSockets = 100;
43}
44
45/**
46 * Get taobao user by nick.
47 *
48 * @param {Object} params, `nick` or `session` must set one.
49 * - {String} fields, (required)fields of user.
50 * - {String} nick, (optional)user nick name.
51 * - {String} session, (optional)session id.
52 * @param {Function(err, user)} callback
53 * @public
54 */
551Client.prototype.taobao_user_get = function (params, callback) {
563 var keys = [ 'fields' ];
573 if (!params.session) {
583 keys.push('nick');
59 }
603 checkRequired(params, keys);
612 this.invoke('taobao.user.get', params, ['user_get_response', 'user'], null, 'GET', callback);
62};
63
64/**
65 * Get taobao users by nick list.
66 *
67 * @param {Object} params
68 * - {String} fields, (required)fields of user.
69 * - {String} nicks, (required)user nick name list, separate by (,), e.g.: mk2,aerdeng
70 * @param {Function(err, users)} callback
71 * @public
72 */
731Client.prototype.taobao_users_get = function (params, callback) {
745 checkRequired(params, [ 'fields', 'nicks' ]);
754 this.invoke('taobao.users.get', params,
76 ['users_get_response', 'users', 'user'], [], 'GET', callback);
77};
78
79/**
80 * tmall.selected.items.search 天猫类目精选商品库
81 *
82 * @see http://api.taobao.com/apidoc/api.htm?path=cid:10240-apiId:11116
83 * @param {Object} params
84 * - {Number|String} cid
85 * @param {Function(err, items)} callback
86 * - {Array} items [{
87 * cid: 1101,
88 * num_iid: 13088700250,
89 * shop_id: 59227746,
90 * item_score: "67.33659988217163"
91 * }, ...]
92 * @public
93 */
941Client.prototype.tmall_selected_items_search = function (params, callback) {
951 checkRequired(params, ['cid']);
961 this.invoke('tmall.selected.items.search', params,
97 ['tmall_selected_items_search_response', 'item_list', 'selected_item'], [],
98 'GET', callback);
99};
100
1011Client.prototype.taobao_item_get = function (params, callback) {
1021 checkRequired(params, ['num_iid', 'fields']);
1031 this.invoke('taobao.item.get', params,
104 ['item_get_response', 'item'], {}, 'GET', callback);
105};
106
107/**
108 * Get taobao shop by nick.
109 *
110 * @see http://api.taobao.com/apidoc/api.htm?spm=0.0.0.34.b8913b&path=cid:9-apiId:68
111 * @param {Object} params
112 * - {String} fields, (required)fields of user.
113 * - {String} nick, (optional)user nick name.
114 * - {String} session, (optional)session id.
115 * @param {Function(err, user)} callback
116 * @public
117 */
1181Client.prototype.taobao_shop_get = function (params, callback) {
1192 checkRequired(params, ['nick', 'fields']);
1202 this.invoke('taobao.shop.get', params,
121 ['shop_get_response', 'shop'], null, 'GET', callback);
122};
123
124/**
125 * Invoke an api by method name.
126 *
127 * @param {String} method, method name
128 * @param {Object} params
129 * @param {Array} reponseNames, e.g. ['tmall_selected_items_search_response', 'tem_list', 'selected_item']
130 * @param {Object} defaultResponse
131 * @param {String} type
132 * @param {Function(err, response)} callback
133 */
1341Client.prototype.invoke = function (method, params, reponseNames, defaultResponse, type, callback) {
13512 params.method = method;
13612 this.request(params, type, function (err, result) {
13712 if (err) {
1382 return callback(err);
139 }
14010 var response = result;
14110 if (reponseNames && reponseNames.length > 0) {
14210 for (var i = 0; i < reponseNames.length; i++) {
14320 var name = reponseNames[i];
14420 response = response[name];
14520 if (response === undefined) {
1464 break;
147 }
148 }
149 }
15010 if (response === undefined) {
1514 response = defaultResponse;
152 }
15310 callback(null, response);
154 });
155};
156
157/**
158 * Request API.
159 *
160 * @param {Object} params
161 * @param {String} [type='GET']
162 * @param {Function(err, result)} callback
163 * @public
164 */
1651Client.prototype.request = function (params, type, callback) {
16615 checkRequired(params, 'method');
16714 if (typeof type === 'function') {
1682 callback = type;
1692 type = null;
170 }
17114 var args = {
172 timestamp: this.timestamp(),
173 format: 'json',
174 app_key: this.appkey,
175 v: '2.0',
176 sign_method: 'md5'
177 };
17814 for (var k in params) {
17939 args[k] = params[k];
180 }
18114 args.sign = this.sign(args);
18214 type = type || 'GET';
18314 urllib.request(this.REST_URL, {type: type, data: args, agent: this.agent},
184 function (err, buffer) {
18514 var data;
18614 if (buffer) {
18713 try {
18813 data = JSON.parse(buffer);
189 } catch (e) {
1901 err = e;
1911 e.data = buffer.toString();
1921 data = null;
193 }
194 }
19514 if (data && data.error_response && !data.error_response.sub_msg) {
196 // sub_msg error, let caller handle it.
197 // request() only handle root error, like code 40, 41 error.
1981 var msg = data.error_response.msg;
1991 err = new Error(data.error_response.code + ': ' + msg);
2001 err.code = data.error_response.code;
2011 err.data = buffer.toString();
2021 data = null;
203 }
20414 callback(err, data);
205 }, this);
206};
207
208/**
209 * Get now timestamp with 'yyyy-MM-dd HH:mm:ss' format.
210 * @return {String}
211 */
2121Client.prototype.timestamp = function () {
21315 return moment().format('YYYY-MM-DD HH:mm:ss');
214};
215
216/**
217 * Sign API request.
218 * see http://open.taobao.com/doc/detail.htm?id=111#s6
219 *
220 * @param {Object} params
221 * @return {String} sign string
222 */
2231Client.prototype.sign = function (params) {
22415 var sorted = Object.keys(params).sort();
22515 var basestring = this.appsecret;
226 // (md5(secretkey1value1key2value2...secret))
22715 for (var i = 0, l = sorted.length; i < l; i++) {
228117 var k = sorted[i];
229117 basestring += k + params[k];
230 }
23115 basestring += this.appsecret;
23215 return md5(basestring);
233};
234
2351function checkRequired(params, keys) {
23627 if (!Array.isArray(keys)) {
23715 keys = [keys];
238 }
23927 for (var i = 0, l = keys.length; i < l; i++) {
24038 var k = keys[i];
24138 if (params[k] === undefined) {
2423 throw new Error('`' + k + '` required');
243 }
244 }
245}
246
247/**
248 * MD5 hex upper case string.
249 *
250 * @param {String|Buffer} s
251 * @return {String}
252 */
2531function md5(s) {
25415 var hash = crypto.createHash('md5');
25515 hash.update(Buffer.isBuffer(s) ? s : new Buffer(s));
25615 return hash.digest('hex').toUpperCase();
257}