diff --git a/app.js b/app.js index 1052bbb..42d3f17 100644 --- a/app.js +++ b/app.js @@ -1,17 +1,15 @@ -/** - * Created by harel on 15/08/2017. - */ const request = require("request"); +const RateLimiter = require('request-rate-limiter'); +const limiter = new RateLimiter(120); //120 requests per minute -var api_key, short_domain; - +let api_key, short_domain; const cm_short = { auth: function (apiKey, shortDomain) { api_key = apiKey; short_domain = shortDomain || "smart.short.cm"; }, domainApi: function (apiKey) { - var options = { + const options = { method: 'GET', url: 'https://api.short.cm/api/domains', headers: { @@ -19,23 +17,10 @@ const cm_short = { 'Authorization': apiKey } }; - return new Promise(function (resolve, reject) { - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error? reject(info) : resolve(info);; - } else { - reject({ - msg: "error in domain api request", - error: error, - response: response - }); - } - }) - }); + return makeRateLimitedRequest(options); }, shorten: function (url) { - var options = { + const options = { method: 'POST', url: 'https://api.short.cm/links', form: { @@ -47,20 +32,7 @@ const cm_short = { 'Authorization': api_key } }; - return new Promise(function (resolve, reject) { - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error? reject(info) : resolve(info);; - } else { - reject({ - msg: "error in shorten api request", - error: error, - response: response - }); - } - }) - }); + return makeRateLimitedRequest(options); }, getPathFromUrl: function (url) { return url.replace(short_domain, '').replace(/(http:\/\/|https:\/\/|\/)/g, '') @@ -83,7 +55,7 @@ const cm_short = { }, delete: function (link_id) { return new Promise(function (resolve, reject) { - var options = { + const options = { method: 'DELETE', url: 'https://api.short.cm/links/' + link_id, headers: { @@ -94,7 +66,8 @@ const cm_short = { request(options, function (error, response, body) { if (!error && response.statusCode == 200) { var info = checkIfJsonStringAndParse(body); - info.error? reject(info) : resolve(info);; + info.error ? reject(info) : resolve(info); + ; } else { console.log('Status:', response.statusCode); console.log('Headers:', JSON.stringify(response.headers)); @@ -111,135 +84,73 @@ const cm_short = { }, expand: function (url) { - return new Promise(function (resolve, reject) { - var options = { - method: 'GET', - url: 'https://api.short.cm/links/expand?domain=' + encodeURI(short_domain) + '&path=' + encodeURI(cm_short.getPathFromUrl(url)), - headers: { - 'Content-Type': 'application/json', - 'Authorization': api_key - } - }; - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error? reject(info) : resolve(info);; - } else { - reject({ - msg: "error in expand api request", - error: error, - response: response - }); - } - }) - }); + const options = { + method: 'GET', + url: 'https://api.short.cm/links/expand?domain=' + encodeURI(short_domain) + '&path=' + encodeURI(cm_short.getPathFromUrl(url)), + headers: { + 'Content-Type': 'application/json', + 'Authorization': api_key + } + }; + return makeRateLimitedRequest(options); }, + expandByLongUrl: function (url) { - return new Promise(function (resolve, reject) { - var options = { - method: 'GET', - url: 'https://api.short.cm/links/by-original-url?domain=' + encodeURI(short_domain) + '&originalURL=' + encodeURIComponent(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': api_key - } - }; - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error? reject(info) : resolve(info);; - } else { - reject({ - msg: "error in expand api request", - error: error, - response: response - }); - } - }) - }); + const options = { + method: 'GET', + url: 'https://api.short.cm/links/by-original-url?domain=' + encodeURI(short_domain) + '&originalURL=' + encodeURIComponent(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': api_key + } + }; + return makeRateLimitedRequest(options); }, analyticsById: function (link_id) { - return new Promise(function (resolve, reject) { - var options = { - method: 'GET', - url: 'https://api.short.cm/links/statistics/' + link_id + '?period=total', - // url: 'https://api.short.cm/links/' + link_id + '/statistics', - headers: { - 'Content-Type': 'application/json', - 'Authorization': api_key - } - }; - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error? reject(info) : resolve(info);; - } else { - reject({ - msg: "error in analytics shorten api request", - error: error, - response: response - }); - } - }) - }); + const options = { + method: 'GET', + url: 'https://api.short.cm/links/statistics/' + link_id + '?period=total', + // url: 'https://api.short.cm/links/' + link_id + '/statistics', + headers: { + 'Content-Type': 'application/json', + 'Authorization': api_key + } + }; + return makeRateLimitedRequest(options); }, updateShortUrlByLinkId: function (link_id, newLongUrl) { - return new Promise(function (resolve, reject) { - var options = { - method: 'POST', - url: 'https://api.short.cm/links/' + link_id, - form: { - originalURL: newLongUrl, - // title: title || "title_placeholder" - }, - headers: { - 'Content-Type': 'application/json', - 'Authorization': api_key - } - }; - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error ? reject(info) : resolve(info); - } else { - reject({ - msg: "error in update shorten api request", - error: error, - response: response - }); - } - }) - }); + const options = { + method: 'POST', + url: 'https://api.short.cm/links/' + link_id, + form: { + originalURL: newLongUrl, + // title: title || "title_placeholder" + }, + headers: { + 'Content-Type': 'application/json', + 'Authorization': api_key + } + }; + return makeRateLimitedRequest(options); }, + updateLocaleById: function (link_id, country, url) { - return new Promise(function (resolve, reject) { - var options = { - method: 'POST', - url: 'https://api.short.cm/link_country/' + link_id, - form: { - originalURL: url, - country: country - // title: title || "title_placeholder" - }, - headers: { - 'Content-Type': 'application/json', - 'Authorization': api_key - } - }; - request(options, function (error, response, body) { - if (!error && response.statusCode == 200) { - var info = checkIfJsonStringAndParse(body); - info.error ? reject(info) : resolve(info); - } else { - reject({ - msg: "error in updateLocale api request", - error: error, - response: response - }); - } - }) - }); + const options = { + method: 'POST', + url: 'https://api.short.cm/link_country/' + link_id, + form: { + originalURL: url, + country: country + // title: title || "title_placeholder" + }, + headers: { + 'Content-Type': 'application/json', + 'Authorization': api_key + } + }; + return makeRateLimitedRequest(options); }, + updateShortUrl: function (shortUrl, newLongUrl) { return new Promise(function (resolve, reject) { cm_short.expand(shortUrl).then(function (expand_res) { @@ -284,8 +195,9 @@ const cm_short = { //helper to test if json can be parsed function checkIfJsonStringAndParse(str) { + let x; try { - var x = JSON.parse(str); + x = JSON.parse(str); } catch (e) { return { msg: "error in updateLocale api request", @@ -294,8 +206,32 @@ function checkIfJsonStringAndParse(str) { } return x; } -module.exports = cm_short; +function makeRateLimitedRequest(options) { + return new Promise(function (resolve, reject) { + limiter.request() + .then(function (backoff) { + request(options, function (error, response, body) { + if (response.statusCode === 429) { + // we have to back off. this callback will be called again as soon as the remote enpoint + // should accept requests again. no need to queue your callback another time on the limiter. + backoff(); + } else if (!error && response.statusCode == 200) { + const info = checkIfJsonStringAndParse(body); + info.error ? reject(info) : resolve(info); + } else { + reject({ + msg: "error in domain api request", + error: error, + response: response + }); + } + }) + }).catch((limiterError) => reject({msg: "limiter error", error: limiterError})); + }); +} + +module.exports = cm_short; diff --git a/package.json b/package.json index 5313d7f..7cb763b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shortcm-node", - "version": "0.0.8", + "version": "0.0.9", "description": "", "main": "app.js", "scripts": { @@ -15,6 +15,7 @@ "author": "Harel Levy", "license": "ISC", "dependencies": { - "request": "^2.81.0" + "request": "^2.81.0", + "request-rate-limiter": "^1.0.2" } }