diff --git a/index.js b/index.js index 47cd049..3657a7e 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,79 @@ var http = require ('httpreq'); + +// Defaults var config = { email: null, apikey: null, timeout: 5000 }; -function talk (props, callback) { + +/** + * Process API response + * + * @callback callback + * @param options {object} - httpreq options + * @param err {Error|null} - httpreq Error + * @param res {object} - httpreq response + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function apiResponse (options, err, res, callback) { + var type = res && res.headers ['content-type'] .split (';') [0] || ''; + var size = res && res.headers ['content-length'] || null; + var code = res && res.statusCode; + var data = res && res.body || null; + var error = null; + + if (err) { + error = new Error ('request failed'); + error.error = err; + callback (error); + return; + } + + if (size && options.binary && type.match (/\/(pdf|jpeg|tar)$/)) { + callback (null, data); + return; + } + + try { + data = JSON.parse (data); + } catch (e) { + error = new Error ('invalid response'); + error.statusCode = code; + error.contentType = type; + error.error = e; + data = null; + } + + if (data && data.error) { + error = new Error ('API error'); + error.statusCode = code; + error.contentType = type; + error.error = data.error; + data = null; + } + + callback (error, data); +} + + +/** + * Send API request + * + * @callback callback + * @param props {object} + * @param [props.binary=false] {boolean} - Expect binary response + * @param [props.method=GET] {string} - HTTP method + * @param [props.params] {object} - Method parameters + * @param props.path {string} - Method path + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function apiRequest (props, callback) { var options = { url: 'https://gtmetrix.com/api/0.1/' + props.path, parameters: props.params || null, @@ -19,46 +87,140 @@ function talk (props, callback) { }; http.doRequest (options, function (err, res) { - var type = res && res.headers ['content-type'] .split (';') [0] || ''; - var size = res && res.headers ['content-length'] || null; - var code = res && res.statusCode; - var data = res && res.body || null; - var error = null; - - if (err) { - error = new Error ('request failed'); - error.error = err; - callback (error); - return; - } + apiResponse (options, err, res, callback); + }); +} - if (size && props.binary && type.match (/\/(pdf|jpeg|tar)$/)) { - callback (null, data); - return; - } - try { - data = JSON.parse (data); - } catch (e) { - error = new Error ('invalid response'); - error.statusCode = code; - error.contentType = type; - error.error = e; - data = null; - } +/** + * Create test + * + * @callback callback + * @param params {object} + * @param callback {function} - `(err, data)` + * @return {void} + */ - if (data && data.error) { - error = new Error ('API error'); - error.statusCode = code; - error.contentType = type; - error.error = data.error; - data = null; - } +function testCreate (params, callback) { + var props = { + method: 'POST', + path: 'test', + params: params + }; - callback (error, data); - }); + apiRequest (props, callback); } + +/** + * Get test result + * + * @callback callback + * @param testId {object} + * @param [resouce] {string} - Resource to get, i.e. `screenshot` + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function testGet (testId, resource, callback) { + var props = { + method: 'GET', + path: 'test/' + testId + }; + + if (typeof resource === 'string') { + props.path += '/' + resource; + props.binary = resource.match (/(screenshot)/); + } else { + callback = resource; + } + + apiRequest (props, callback); +} + +/** + * List locations + * + * @callback callback + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function locationsList (callback) { + var props = { + method: 'GET', + path: 'locations' + }; + + apiRequest (props, callback); +} + + +/** + * List browsers + * + * @callback callback + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function browsersList (callback) { + var props = { + method: 'GET', + path: 'browsers' + }; + + apiRequest (props, callback); +} + + +/** + * Get browser + * + * @callback callback + * @param browserId {string} + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function browsersGet (browserId, callback) { + var props = { + method: 'GET', + path: 'browsers/' + browserId + }; + + apiRequest (props, callback); +} + + +/** + * Get account status + * + * @callback callback + * @param callback {function} - `(err, data)` + * @return {void} + */ + +function accountStatus (callback) { + var props = { + method: 'GET', + path: 'status' + }; + + apiRequest (props, callback); +} + + +/** + * Module interface + * + * @param props {object} + * @param props.email {string} - API email + * @param props.apikey {string} - API key + * @param [props.timeout=5000] {number} - Request timeut in ms + * @return {object} - Methods + */ + module.exports = function (props) { var key; @@ -68,42 +230,18 @@ module.exports = function (props) { return { test: { - create: function (params, callback) { - talk ({ method: 'POST', path: 'test', params: params }, callback); - }, - get: function (testId, resource, callback) { - var params = { - method: 'GET', - path: 'test/' + testId - }; - - if (typeof resource === 'string') { - params.path += '/' + resource; - params.binary = resource.match (/(screenshot)/); - } else { - callback = resource; - } - - talk (params, callback); - } + create: testCreate, + get: testGet }, locations: { - list: function (callback) { - talk ({ method: 'GET', path: 'locations' }, callback); - } + list: locationsList }, browsers: { - list: function (callback) { - talk ({ method: 'GET', path: 'browsers' }, callback); - }, - get: function (browserId, callback) { - talk ({ method: 'GET', path: 'browsers/' + browserId }, callback); - } + list: browsersList, + get: browsersGet }, account: { - status: function (callback) { - talk ({ method: 'GET', path: 'status' }, callback); - } + status: accountStatus } }; };