-
Notifications
You must be signed in to change notification settings - Fork 662
/
client.js
181 lines (153 loc) · 4.71 KB
/
client.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
*
*/
var EventEmitter = require('eventemitter3');
var Promise = require('bluebird');
var async = require('async');
var bind = require('lodash').bind;
var inherits = require('inherits');
var partial = require('lodash').partial;
var retry = require('retry');
var urlJoin = require('url-join');
var SlackAPIError = require('./errors').SlackAPIError;
var getLogger = require('../helpers').getLogger;
var helpers = require('./helpers');
var requestsTransport = require('./transports/request').requestTransport;
var callTransport = require('./transports/call-transport');
/**
* Base client for both the RTM and web APIs.
* @param {string} token The Slack API token to use with this client.
* @param {Object} opts
* @param {String} opts.slackAPIUrl The Slack API URL.
* @param {String} opts.userAgent The user-agent to use, defaults to node-slack.
* @param {Function} opts.transport Function to call to make an HTTP call to the Slack API.
* @param {string=} opts.logLevel The log level for the logger.
* @param {Function=} opts.logger Function to use for log calls, takes (logLevel, logString) params.
* @param {Number} opts.maxRequestConcurrency The max # of concurrent requests to make to Slack's
* API's, defaults to 3.
* @param {Object} opts.retryConfig The configuration to use for the retry operation,
* {@see https://github.com/SEAPUNK/node-retry}
* @constructor
*/
function BaseAPIClient(token, opts) {
var clientOpts = opts || {};
EventEmitter.call(this);
/**
* @type {string}
* @private
*/
this._token = token;
/** @type {string} */
this.slackAPIUrl = clientOpts.slackAPIUrl || 'https://slack.com/api/';
/** @type {Function} */
this.transport = clientOpts.transport || requestsTransport;
/** @type {string} */
this.userAgent = clientOpts.userAgent || 'node-slack';
/**
* Default to attempting 5 retries within 5 minutes, with exponential backoff.
*/
this.retryConfig = clientOpts.retryConfig || {
retries: 5,
factor: 3.9
};
/**
*
* @type {Object}
* @private
*/
this.requestQueue = async.priorityQueue(
bind(this._callTransport, this),
clientOpts.maxRequestConcurrency || 3
);
/**
* The logger function attached to this client.
* @type {Function}
*/
this.logger = clientOpts.logger || getLogger(clientOpts.logLevel);
this._createFacets();
}
inherits(BaseAPIClient, EventEmitter);
BaseAPIClient.prototype.emit = function emit() {
BaseAPIClient.super_.prototype.emit.apply(this, arguments);
this.logger('debug', arguments);
};
/**
* Initializes each of the API facets.
* @protected
*/
BaseAPIClient.prototype._createFacets = function _createFacets() {
};
/**
* Attaches a data-store to the client instance.
* @param {SlackDataStore} dataStore
*/
BaseAPIClient.prototype.registerDataStore = function registerDataStore(dataStore) {
this.dataStore = dataStore;
};
/**
* Calls the supplied transport function and processes the results.
*
* This will also manage 429 responses and retry failed operations.
*
* @param {object} task The arguments to pass to the transport.
* @param {function} queueCb Callback to signal to the request queue that the request has completed.
* @protected
*/
BaseAPIClient.prototype._callTransport = function _callTransport(task, queueCb) {
var _this = this;
var retryOp = retry.operation(_this.retryConfig);
var retryArgs = {
client: _this,
task: task,
queueCb: queueCb,
retryOp: retryOp
};
retryOp.attempt(function attemptTransportCall() {
_this.transport(task.args, partial(callTransport.handleTransportResponse, retryArgs));
});
};
/**
* Makes a call to the Slack API.
*
* @param {String} endpoint The API endpoint to send to.
* @param {Object=} apiArgs
* @param {Object=} apiOptArgs
* @param {function=} optCb The callback to run on completion.
* @private
*/
BaseAPIClient.prototype._makeAPICall = function _makeAPICall(endpoint, apiArgs, apiOptArgs, optCb) {
var promise;
var _this = this;
var args = {
url: urlJoin(this.slackAPIUrl, endpoint),
data: helpers.getApiCallData(this._token, apiArgs, apiOptArgs),
headers: {
'User-Agent': this.userAgent
}
};
if (!optCb) {
promise = new Promise(function makeAPICallPromiseResolver(resolve, reject) {
_this.requestQueue.push({
args: args,
cb: function makeAPICallPromiseResolverInner(err, res) {
if (err) {
reject(err);
} else {
if (!res.ok) {
reject(new SlackAPIError(res.error));
} else {
resolve(res);
}
}
}
});
});
} else {
this.requestQueue.push({
args: args,
cb: optCb
});
}
return promise;
};
module.exports = BaseAPIClient;