Skip to content

Commit e486745

Browse files
committed
add property apiCallRate
1 parent 42d6144 commit e486745

File tree

9 files changed

+9277
-8610
lines changed

9 files changed

+9277
-8610
lines changed

.travis.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
language: node_js
22
node_js:
3-
- "4"
4-
- "5"
5-
- "6"
6-
- "7"
7-
- "8"
3+
- "11"
84
sudo: false
95
after_success:
106
- npm run codecov

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### 1.10.0 (2022-06-20)
2+
3+
* Add `apiCallRate` property
4+
* Update tests
5+
16
### 1.9.1 (2022-06-20)
27

38
* Fix https request error handling when no `callback` parameter (promises)

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ The optional `options` argument can be used to override the default options. The
2525
}
2626
```
2727

28+
## Properties
29+
30+
### apiCallRate
31+
32+
Gives the current rate per minute of API calls. The property is read only.
33+
It can be used by applications to limit the API call rate to prevent HTTP error code `429` (too many requests) responses from Luno server.
34+
See Luno API documentation for applicable rate limitations: https://www.luno.com/en/developers/api#tag/Rate-Limiting
35+
36+
> API calls which result in error code `429` or `ErrTooManyRequests` are considered failed/unprocessed server request and are not added to the `apiCallRate` counter
37+
38+
Example:
39+
40+
```javascript
41+
console.log(bitx.apiCallRate)
42+
```
43+
44+
2845
## Methods
2946
For details about the API endpoints see https://www.luno.com/en/developers/api.
3047

lib/BitX.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,37 @@ function BitX (keyId, keySecret, options) {
2525
this.port = options.port || 443
2626
this.ca = options.ca
2727
this.pair = options.pair || 'XBTZAR'
28+
this._requestMts = []
29+
this._requestsRefresh = this._requestsRefresh.bind(this)
30+
this._timeoutId = null
31+
this._timeoutId = setTimeout(this._requestsRefresh, 60000)
32+
}
33+
34+
Object.defineProperty(BitX.prototype, 'apiCallRate', {
35+
get: function () {
36+
return this._requestsRefresh()
37+
}
38+
})
39+
40+
BitX.prototype._requestsRefresh = function () {
41+
var maxMts = Date.now() - 60000
42+
while (this._requestMts.length > 0) {
43+
if (this._requestMts[0] < maxMts) {
44+
this._requestMts.shift()
45+
} else {
46+
break
47+
}
48+
}
49+
50+
if (this._requestMts.length > 0) {
51+
this._timeoutId.refresh()
52+
this._timeoutId.unref()
53+
} else {
54+
clearTimeout(this._timeoutId)
55+
this._timeoutId = null
56+
}
57+
58+
return this._requestMts.length
2859
}
2960

3061
BitX.prototype._request = function (method, resourcePath, data, callback) {
@@ -51,6 +82,14 @@ BitX.prototype._request = function (method, resourcePath, data, callback) {
5182
options.agent = new https.Agent(options)
5283
}
5384

85+
var mts = Date.now()
86+
var requestMts = this._requestMts
87+
requestMts.push(mts)
88+
if (this._timeoutId === null) {
89+
this._timeoutId = setTimeout(this._requestsRefresh, 60000)
90+
this._timeoutId.unref()
91+
}
92+
5493
var promise = new Promise(function (resolve, reject) {
5594
var req = https.request(options)
5695

@@ -70,6 +109,16 @@ BitX.prototype._request = function (method, resourcePath, data, callback) {
70109
response = JSON.parse(response)
71110
lunoApiError = new Error('luno API error ' + response.error_code + ': ' + response.error)
72111
lunoApiError.error_code = response.error_code
112+
if (response.error_code.includes('429') || response.error_code.includes('ErrTooManyRequests')) {
113+
var i = requestMts.length - 1
114+
while (i >= 0) {
115+
if (requestMts[i] === mts) {
116+
requestMts.splice(i, 1)
117+
break
118+
}
119+
i -= 1
120+
}
121+
}
73122
} catch (err) {
74123
lunoApiError = new Error('luno API error ' + res.statusCode + ': ' + response)
75124
}

0 commit comments

Comments
 (0)