From cdae75e624123cb85527e31567acaba6a0c649f9 Mon Sep 17 00:00:00 2001 From: Alexandru Scripnic Date: Mon, 24 Jun 2019 17:43:38 +0300 Subject: [PATCH 1/3] add exponential backoff --- package.json | 5 +++-- src/api.js | 7 +++++-- src/endpoint.js | 26 +++++++++++++++++++++----- src/ubidots.js | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index b84b02e..253a495 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,9 @@ "mocha": "^5.2.0" }, "dependencies": { - "axios": "^0.18.0", + "axios": "^0.19.0", "debug": "^4.1.0", - "merge-options": "^1.0.1" + "merge-options": "^1.0.1", + "promise-retry": "^1.1.1" } } diff --git a/src/api.js b/src/api.js index 685c726..0681c6e 100644 --- a/src/api.js +++ b/src/api.js @@ -8,12 +8,14 @@ class Api { * @param {*} endpoints * @param {string} token * @param {string} key + * @param {object} opts */ - constructor(namespace, endpoints, token, key) { + constructor(namespace, endpoints, token, key, opts) { this.namespace = namespace; this._endpoints = endpoints; this.token = token; this.key = key; + this.opts = opts; debug(`endpoints:${this.namespace}`, this.endpoints); } @@ -32,7 +34,8 @@ class Api { `${this.namespace}:${endpoint}`, this._endpoints[endpoint], this.token, - this.key + this.key, + this.opts ); } diff --git a/src/endpoint.js b/src/endpoint.js index 102614b..f60244e 100644 --- a/src/endpoint.js +++ b/src/endpoint.js @@ -1,4 +1,5 @@ const axios = require('axios'); +const promiseRetry = require('promise-retry'); const debug = require('./utils/debug')(__filename); const Response = require('./response'); @@ -8,14 +9,16 @@ class Endpoint { * @param {*} definition * @param {string} token * @param {string} key + * @param {object} opts */ - constructor(name, definition, token, key) { + constructor(name, definition, token, key, opts) { this.name = name; this.definition = definition; this.token = token; this.key = key; + this.opts = opts; - debug(`endpoint:${this.name}`, this.definition); + debug(`endpoint:${ this.name }`, this.definition); } /** @@ -24,6 +27,7 @@ class Endpoint { * @returns {Response} */ async call(...args) { + const { name } = this; const opts = await this.definition.build( this.definition, this.token, @@ -31,17 +35,29 @@ class Endpoint { ...args ); - debug(`call:${this.name}`, opts); + debug(`call:${ name }`, opts); let response = null; try { - response = new Response(this, opts, null, await axios.request(opts)); + const result = await promiseRetry(this.opts.backoff, (retry, number) => { + debug(`call:${ name }:try:${ number }`, opts); + + return axios.request(opts) + .then(response => { + if (response.status === 429) { + retry(response); + } + return response; + }); + }); + + response = new Response(this, opts, null, result); } catch (error) { response = new Response(this, opts, error, null); } - debug(`response:${this.name}`, response.toJSON()); + debug(`response:${ name }`, response.toJSON()); return response; } diff --git a/src/ubidots.js b/src/ubidots.js index e6f0733..971eb9e 100644 --- a/src/ubidots.js +++ b/src/ubidots.js @@ -6,11 +6,17 @@ const MissingApiNamespaceError = require('./error/missing-api-namespace'); class Ubidots { /** * @param {*} api + * @param {object} opts + * @param {object} opts.backoff - backoff options + * @param {number} opts.backoff.retries - amount of retries + * @param {number} opts.backoff.minTimeout - minimum amount of time between retries + * @param {number} opts.backoff.factor - factor of the timeout between retries */ - constructor(api) { + constructor(api, opts) { this._api = api; this.token = null; this.key = null; + this.opts = opts; debug('namespaces', this.apis); } @@ -46,7 +52,8 @@ class Ubidots { namespace, this._api[namespace], this.token, - this.key + this.key, + this.opts ); } @@ -78,14 +85,38 @@ class Ubidots { /** * Create an instance of Ubidots API Client * @param {string} baseURL + * @param {object} opts + * @param {object} opts.backoff - backoff options + * @param {number=} opts.backoff.retries - amount of retries + * @param {number=} opts.backoff.minTimeout - minimum amount of time between retries + * @param {number=} opts.backoff.factor - factor of the timeout between retries * @returns {Ubidots} */ - static create(baseURL = ApiBase.Industrial) { + static create(baseURL = ApiBase.Industrial, opts = { backoff: {}}) { debug('client', baseURL); const api = apigen(baseURL); - return new this(api); + const mergedOpts = { + backoff: {} + }; + + Object.assign(mergedOpts.backoff, this.DEFAULT_CONFIG.backoff, opts.backoff); + + return new this(api, mergedOpts); + } + + /** + * @returns {{backoff: {minTimeout: number, retries: number, factor: number}}} + */ + static get DEFAULT_CONFIG() { + return { + backoff: { + retries: 5, + factor: 1.4, + minTimeout: 500 + } + }; } } From ed66dd4c738a6a24736bafaec5e7555322d05b93 Mon Sep 17 00:00:00 2001 From: Alexandru Scripnic Date: Mon, 24 Jun 2019 17:57:54 +0300 Subject: [PATCH 2/3] use object merger --- src/ubidots.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ubidots.js b/src/ubidots.js index 971eb9e..b4b07ff 100644 --- a/src/ubidots.js +++ b/src/ubidots.js @@ -1,3 +1,5 @@ +const mergeOptions = require('merge-options'); + const { ApiBase, api: apigen } = require('./api-definition'); const Api = require('./api'); const debug = require('./utils/debug')(__filename); @@ -97,11 +99,7 @@ class Ubidots { const api = apigen(baseURL); - const mergedOpts = { - backoff: {} - }; - - Object.assign(mergedOpts.backoff, this.DEFAULT_CONFIG.backoff, opts.backoff); + const mergedOpts = mergeOptions(this.DEFAULT_CONFIG, opts); return new this(api, mergedOpts); } From 97c3fd06b96dededb1590885780e8b2fd346eb13 Mon Sep 17 00:00:00 2001 From: Alexandru Scripnic Date: Tue, 25 Jun 2019 11:16:06 +0300 Subject: [PATCH 3/3] use async await --- src/endpoint.js | 12 ++++-------- src/ubidots.js | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/endpoint.js b/src/endpoint.js index f60244e..eee4fc7 100644 --- a/src/endpoint.js +++ b/src/endpoint.js @@ -40,16 +40,12 @@ class Endpoint { let response = null; try { - const result = await promiseRetry(this.opts.backoff, (retry, number) => { + const result = await promiseRetry(this.opts.backoff, async (retry, number) => { debug(`call:${ name }:try:${ number }`, opts); - return axios.request(opts) - .then(response => { - if (response.status === 429) { - retry(response); - } - return response; - }); + const response = await axios.request(opts); + + return response.status === 429 ? retry(response) : response; }); response = new Response(this, opts, null, result); diff --git a/src/ubidots.js b/src/ubidots.js index b4b07ff..bbc555d 100644 --- a/src/ubidots.js +++ b/src/ubidots.js @@ -94,7 +94,7 @@ class Ubidots { * @param {number=} opts.backoff.factor - factor of the timeout between retries * @returns {Ubidots} */ - static create(baseURL = ApiBase.Industrial, opts = { backoff: {}}) { + static create(baseURL = ApiBase.Industrial, opts = Ubidots.DEFAULT_CONFIG) { debug('client', baseURL); const api = apigen(baseURL); @@ -112,7 +112,7 @@ class Ubidots { backoff: { retries: 5, factor: 1.4, - minTimeout: 500 + minTimeout: 500, } }; }