Comparing version 1.1.1 to 1.2.0
190
dnsimple.js
@@ -15,4 +15,5 @@ /* | ||
module.exports = function (setup) { | ||
// ! Defaults | ||
var http = require ('httpreq'); | ||
module.exports = function doSetup (setup) { | ||
var api = { | ||
@@ -23,4 +24,4 @@ hostname: setup.hostname || 'api.dnsimple.com', | ||
domainToken: setup.domainToken || null, | ||
twoFactorOTP: setup.twoFactorOTP || null, // one time password (ie. Authy) | ||
twoFactorToken: setup.twoFactorToken || null, // OTP exchange token | ||
twoFactorOTP: setup.twoFactorOTP || null, | ||
twoFactorToken: setup.twoFactorToken || null, | ||
password: setup.password || null, | ||
@@ -31,3 +32,14 @@ timeout: setup.timeout || 30000 | ||
// ! API | ||
return function (method, path, fields, callback) { | ||
return function talk (method, path, fields, callback) { | ||
var complete = false; | ||
var options = { | ||
url: 'https://' + api.hostname + '/v1' + path, | ||
method: method || 'GET', | ||
timeout: api.timeout, | ||
headers: { | ||
'Accept': 'application/json', | ||
'User-Agent': 'Nodejs-DNSimple' | ||
} | ||
}; | ||
if (!callback && typeof fields === 'function') { | ||
@@ -39,3 +51,2 @@ callback = fields; | ||
// process callback data | ||
var complete = false; | ||
function doCallback (err, data, meta) { | ||
@@ -60,3 +71,3 @@ if (!complete) { | ||
// credentials set? | ||
if (! (api.email && api.token) && ! (api.email && api.password) && ! api.domainToken && ! api.twoFactorToken) { | ||
if (!(api.email && api.token) && !(api.email && api.password) && !api.domainToken && !api.twoFactorToken) { | ||
doCallback (new Error ('credentials missing')); | ||
@@ -66,16 +77,9 @@ return; | ||
// prepare | ||
var querystr = JSON.stringify (fields); | ||
var headers = { | ||
'Accept': 'application/json', | ||
'User-Agent': 'Nodejs-DNSimple' | ||
}; | ||
// token in headers | ||
if (api.token) { | ||
headers['X-DNSimple-Token'] = api.email +':'+ api.token; | ||
options.headers['X-DNSimple-Token'] = api.email + ':' + api.token; | ||
} | ||
if (api.domainToken) { | ||
headers['X-DNSimple-Domain-Token'] = api.domainToken; | ||
options.headers['X-DNSimple-Domain-Token'] = api.domainToken; | ||
} | ||
@@ -85,22 +89,15 @@ | ||
if (method.match (/(POST|PUT|DELETE)/)) { | ||
headers['Content-Type'] = 'application/json'; | ||
headers['Content-Length'] = querystr.length; | ||
options.json = fields; | ||
} else { | ||
options.parameters = fields; | ||
} | ||
var options = { | ||
host: api.hostname, | ||
port: 443, | ||
path: '/v1'+ path, | ||
method: method, | ||
headers: headers | ||
}; | ||
// password authentication | ||
if (! api.twoFactorToken && ! api.token && ! api.domainToken && api.password && api.email) { | ||
options.auth = api.email +':'+ api.password; | ||
if (!api.twoFactorToken && !api.token && !api.domainToken && api.password && api.email) { | ||
options.auth = api.email + ':' + api.password; | ||
// two-factor authentication (2FA) | ||
if (api.twoFactorOTP) { | ||
headers['X-DNSimple-2FA-Strict'] = 1; | ||
headers['X-DNSimple-OTP'] = api.twoFactorOTP; | ||
options.headers['X-DNSimple-2FA-Strict'] = 1; | ||
options.headers['X-DNSimple-OTP'] = api.twoFactorOTP; | ||
} | ||
@@ -110,106 +107,61 @@ } | ||
if (api.twoFactorToken) { | ||
options.auth = api.twoFactorToken +':x-2fa-basic'; | ||
headers['X-DNSimple-2FA-Strict'] = 1; | ||
options.auth = api.twoFactorToken + ':x-2fa-basic'; | ||
options.headers['X-DNSimple-2FA-Strict'] = 1; | ||
} | ||
// start request | ||
var request = require ('https').request (options); | ||
http.doRequest (options, function doResponse (err, response) { | ||
var error = null; | ||
var data = response && response.body || ''; | ||
var meta = {}; | ||
// response | ||
request.on ('response', function (response) { | ||
var meta = {statusCode: null}; | ||
var data = []; | ||
var size = 0; | ||
if (err) { | ||
error = new Error ('request failed'); | ||
error.error = err; | ||
doCallback (error); | ||
return; | ||
} | ||
response.on ('data', function (chunk) { | ||
data.push (chunk); | ||
size += chunk.length; | ||
}); | ||
try { | ||
data = JSON.parse (data); | ||
} catch (e) { | ||
error = new Error ('invalid response'); | ||
error.error = e; | ||
} | ||
response.on ('close', function() { | ||
doCallback (new Error('connection dropped')); | ||
}); | ||
meta.statusCode = response.statusCode; | ||
meta.request_id = response.headers ['x-request-id']; | ||
meta.runtime = response.headers ['x-runtime']; | ||
// request finished | ||
response.on ('end', function() { | ||
data = new Buffer.concat (data, size).toString ('utf8').trim (); | ||
var failed = null; | ||
if (typeof response.headers ['x-dnsimple-otp-token'] === 'string') { | ||
meta.twoFactorToken = response.headers ['x-dnsimple-otp-token']; | ||
} | ||
meta.statusCode = response.statusCode; | ||
meta.request_id = response.headers['x-request-id']; | ||
meta.runtime = response.headers['x-runtime']; | ||
// status ok, no data | ||
if (!data && meta.statusCode < 300) { | ||
error = null; | ||
} | ||
if (typeof response.headers['x-dnsimple-otp-token'] === 'string') { | ||
meta.twoFactorToken = response.headers['x-dnsimple-otp-token']; | ||
} | ||
if (response.statusCode !== 204) { | ||
try { | ||
data = JSON.parse (data); | ||
} catch (e) { | ||
doCallback (new Error ('not json'), data); | ||
} | ||
} | ||
// overrides | ||
var noError = false; | ||
var error = null; | ||
// status ok, no data | ||
if (data === '' && meta.statusCode < 300) { | ||
noError = true; | ||
} | ||
// domain check 404 = free | ||
if (path.match (/^domains\/.+\/check$/) && meta.statusCode === 404) { | ||
noError = true; | ||
} | ||
// check HTTP status code | ||
if (noError || (!failed && response.statusCode < 300)) { | ||
doCallback (null, data, meta); | ||
} else { | ||
if (response.statusCode === 401 && response.headers['x-dnsimple-otp'] === 'required') { | ||
error = new Error ('twoFactorOTP required'); | ||
} else { | ||
error = failed || new Error ('API error'); | ||
} | ||
error.code = response.statusCode; | ||
error.error = data.message || data.error || (data.errors && data instanceof Object && Object.keys (data.errors)[0] ? data.errors[ Object.keys (data.errors)[0] ] : null) || null; | ||
error.data = data; | ||
doCallback (error, null, meta); | ||
} | ||
}); | ||
}); | ||
// timeout | ||
request.on ('socket', function (socket) { | ||
if (typeof api.timeout === 'number') { | ||
socket.setTimeout (parseInt (api.timeout)); | ||
socket.on ('timeout', function () { | ||
doCallback (new Error ('request timeout')); | ||
request.abort (); | ||
}); | ||
// domain check 404 = free | ||
if (path.match (/^domains\/.+\/check$/) && meta.statusCode === 404) { | ||
error = null; | ||
} | ||
}); | ||
// error | ||
request.on ('error', function (error) { | ||
var er = null; | ||
if (error.code === 'ECONNRESET') { | ||
er = new Error ('request timeout'); | ||
// check HTTP status code | ||
if (!error && response.statusCode < 300) { | ||
doCallback (null, data, meta); | ||
return; | ||
} else if (response.statusCode === 401 && response.headers ['x-dnsimple-otp'] === 'required') { | ||
error = new Error ('twoFactorOTP required'); | ||
} else { | ||
er = new Error ('request failed'); | ||
error = new Error ('API error'); | ||
} | ||
er.error = error; | ||
doCallback (er); | ||
error.code = response.statusCode; | ||
error.error = data.message || data.error || data.errors || null; | ||
error.data = data; | ||
doCallback (error, null, meta); | ||
}); | ||
// run it | ||
if (method.match (/(POST|PUT|DELETE)/)) { | ||
request.end (querystr); | ||
} else { | ||
request.end (); | ||
} | ||
}; | ||
}; |
{ | ||
"author": { | ||
"name": "Franklin van de Meent", | ||
"email": "fr@nkl.in", | ||
"url": "https://frankl.in" | ||
"name": "Franklin van de Meent", | ||
"email": "fr@nkl.in", | ||
"url": "https://frankl.in" | ||
}, | ||
"name": "dnsimple", | ||
"description": "Wrapper for DNSimple API", | ||
"version": "1.1.1", | ||
"name": "dnsimple", | ||
"description": "Wrapper for DNSimple API", | ||
"version": "1.2.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/fvdm/nodejs-dnsimple.git" | ||
"type": "git", | ||
"url": "git://github.com/fvdm/nodejs-dnsimple.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/fvdm/nodejs-dnsimple/issues" | ||
"url": "https://github.com/fvdm/nodejs-dnsimple/issues" | ||
}, | ||
"main": "dnsimple.js", | ||
"dependencies": {}, | ||
"devDependencies": {}, | ||
"optionalDependencies": {}, | ||
"main": "dnsimple.js", | ||
"dependencies": { | ||
"httpreq": "0.4.x" | ||
}, | ||
"devDependencies": {}, | ||
"optionalDependencies": {}, | ||
"engines": { | ||
"node": ">=0.8.0" | ||
"node": ">=0.8.0" | ||
}, | ||
"keywords": ["dnsimple", "domains", "dns", "api", "unlicense"], | ||
"license": { | ||
"type": "Public Domain", | ||
"url": "https://github.com/fvdm/nodejs-dnsimple/raw/master/UNLICENSE" | ||
}, | ||
"keywords": [ | ||
"dnsimple", | ||
"domains", | ||
"dns", | ||
"api", | ||
"unlicense" | ||
], | ||
"license": "Unlicense", | ||
"scripts": { | ||
"test": "node test.js" | ||
"test": "node test.js" | ||
} | ||
} |
@@ -11,3 +11,3 @@ dnsimple | ||
[DNSimple](https://dnsimple.com/) - | ||
[API documentation](http://developer.dnsimple.com/) | ||
[API documentation](https://developer.dnsimple.com/) | ||
@@ -30,3 +30,3 @@ | ||
```js | ||
var dnsimple = new require('dnsimple')({ email: 'you@web.tld', token: 'abc123' }); | ||
var dnsimple = new require ('dnsimple') ({ email: 'you@web.tld', token: 'abc123' }); | ||
@@ -38,7 +38,7 @@ // Add a domain name | ||
dnsimple( 'POST', '/domains', input, function( err, data ) { | ||
if( err ) { | ||
return console.log( err ); | ||
dnsimple ('POST', '/domains', input, function (err, data) { | ||
if (err) { | ||
return console.log (err); | ||
} | ||
console.log( data.domain.name +' created with ID '+ data.domain.id ); | ||
console.log (data.domain.name + ' created with ID ' + data.domain.id); | ||
}); | ||
@@ -63,3 +63,3 @@ ``` | ||
```js | ||
require('dnsimple')({ email: 'your@email.tld', token: '12345abcde' }) | ||
require ('dnsimple') ({ email: 'your@email.tld', token: '12345abcde' }); | ||
``` | ||
@@ -71,3 +71,3 @@ | ||
```js | ||
require('dnsimple')({ email: 'your@email.tld', password: 'secret' }) | ||
require ('dnsimple') ({ email: 'your@email.tld', password: 'secret' }); | ||
``` | ||
@@ -81,3 +81,3 @@ | ||
```js | ||
require('dnsimple')({ domainToken: 'abc123' }) | ||
require ('dnsimple') ({ domainToken: 'abc123' }); | ||
``` | ||
@@ -98,3 +98,3 @@ | ||
// Set the OTP code on load | ||
var dnsimple = require('dnsimple')({ | ||
var dnsimple = require ('dnsimple') ({ | ||
email: 'my@mail.tld', | ||
@@ -106,9 +106,9 @@ password: 'my-secret', | ||
// Now call a random method to trade the OTP for a longterm token | ||
dnsimple( 'GET', '/subscription', function( err, data, meta ) { | ||
if( err ) { return console.log(err) } | ||
console.log( 'Two-factor token: '+ meta.twoFactorToken ); | ||
dnsimple ('GET', '/subscription', function (err, data, meta) { | ||
if (err) { return console.log (err); } | ||
console.log ('Two-factor token: '+ meta.twoFactorToken); | ||
}); | ||
// From now on only use this token - no email/password | ||
var dnsimple = require('dnsimple')({ | ||
var dnsimple = require ('dnsimple') ({ | ||
twoFactorToken: '22596363b3de40b06f981fb85d82312e' | ||
@@ -127,3 +127,3 @@ }); | ||
name | description | default | ||
---------------|--------------------------------------|----------------- | ||
:--------------|:-------------------------------------|:---------------- | ||
email | Account email address | | ||
@@ -155,3 +155,3 @@ token | Account access token | | ||
name | type | required | description | ||
---------|----------|----------|-------------------------------------- | ||
:--------|:---------|:---------|:------------------------------------- | ||
method | string | yes | GET, POST, PUT, DELETE | ||
@@ -166,14 +166,12 @@ path | string | yes | i.e. `/domains/two.com` | ||
The last argument `callback` receives three arguments: `err`, `data` and `meta`. | ||
* When an error occurs `err` is an instance of `Error` and `data` is _null_. | ||
When an error occurs `err` is an instance of _Error_ and `data` is _null_. | ||
`err` can have additional properties. | ||
When everything is good `err` will be _null_ and `data` will be the parsed result. | ||
* When everything is good `err` will be _null_ and `data` will be the parsed result. | ||
The `meta` parameter is always available and contains extra information from | ||
the API, such as statusCode, request_id, runtime and twoFactorToken. | ||
* DELETE result `data` is _true_ on success, _false_ otherwise. | ||
* DELETE result `data` is boolean _true_ on success, _false_ otherwise. | ||
* The `meta` parameter is always available and contains extra information from | ||
the API, such as statusCode, request_id, runtime and twoFactorToken. | ||
#### Errors | ||
@@ -184,10 +182,8 @@ | ||
message | description | ||
--------------------|--------------------------------------------------------- | ||
credentials missing | No authentication details set | ||
connection dropped | Connection was closed too early | ||
not json | Invalid API response, see `err.code` and `err.data` | ||
API error | The API returned an error, see `err.code` and `err.data` | ||
request timeout | The request took too long | ||
request failed | The request cannot be made, see `err.error` | ||
message | description | additional | ||
:-------------------|:--------------------------------|:---------------------- | ||
credentials missing | No authentication details set | | ||
request failed | The request cannot be made | `err.error` | ||
invalid reponse | Invalid API response | `err.code`, `err.error`, `err.data` | ||
API error | The API returned an error | `err.code`, `err.error`, `err.data` | ||
@@ -194,0 +190,0 @@ |
146
test.js
@@ -1,3 +0,5 @@ | ||
var testStart = Date.now (); | ||
var util = require ('util'); | ||
var pkg = require ('./package.json'); | ||
var errors = 0; | ||
var queue = []; | ||
var next = 0; | ||
@@ -9,29 +11,24 @@ // Setup | ||
timeout: process.env.DNSIMPLE_TIMEOUT || 30000, | ||
email: process.env.DNSIMPLE_EMAIL || null | ||
email: process.env.DNSIMPLE_EMAIL || null, | ||
token: process.env.DNSIMPLE_TOKEN || null, | ||
password: process.env.DNSIMPLE_PASS || null, | ||
twoFactorOTP: process.env.DNSIMPLE_OTP || null | ||
}; | ||
var token = process.env.DNSIMPLE_TOKEN || null; | ||
var pass = process.env.DNSIMPLE_PASS || null; | ||
var otp = process.env.DNSIMPLE_OTP || null; | ||
// fake material to use | ||
var bogus = { | ||
domain: { | ||
name: 'test-' + Date.now () + '-delete.me' | ||
} | ||
}; | ||
if (pass && otp) { | ||
acc.password = pass; | ||
acc.twoFactorOTP = otp; | ||
} else if (token) { | ||
acc.token = token; | ||
} | ||
var app = require ('./') (acc); | ||
// handle exits | ||
var errors = 0; | ||
process.on ('exit', function () { | ||
var testTime = Date.now () - testStart; | ||
if (errors === 0) { | ||
console.log ('\n\033[1mDONE, no errors.\033[0m'); | ||
console.log ('Timing: %s ms\n', testTime); | ||
console.log ('\n\u001b[1mDONE, no errors.\u001b[0m\n'); | ||
process.exit (0); | ||
} else { | ||
console.log ('\n\033[1mFAIL, '+ errors +' error'+ (errors > 1 ? 's' : '') +' occurred!\033[0m'); | ||
console.log ('Timing: %s ms\n', testTime); | ||
console.log ('\n\u001b[1mFAIL, ' + errors + ' error' + (errors > 1 ? 's' : '') + ' occurred!\u001b[0m\n'); | ||
process.exit (1); | ||
@@ -44,5 +41,6 @@ } | ||
console.log (); | ||
console.error (err.stack); | ||
console.trace (); | ||
console.log (err); | ||
console.log (); | ||
console.log (err.stack); | ||
console.log (); | ||
errors++; | ||
@@ -52,36 +50,35 @@ }); | ||
// Queue to prevent flooding | ||
var queue = []; | ||
var next = 0; | ||
function doNext () { | ||
next++; | ||
if (queue[next]) { | ||
queue[next] (); | ||
if (queue [next]) { | ||
queue [next] (); | ||
} | ||
} | ||
// doTest (passErr, 'methods', [ | ||
// doTest( passErr, 'methods', [ | ||
// ['feeds', typeof feeds === 'object'] | ||
// ]); | ||
// ]) | ||
function doTest (err, label, tests) { | ||
var testErrors = []; | ||
var i; | ||
if (err instanceof Error) { | ||
console.error (label +': \033[1m\033[31mERROR\033[0m\n'); | ||
console.error (util.inspect (err, false, 10, true)); | ||
console.log ('\u001b[1m\u001b[31mERROR\u001b[0m - ' + label + '\n'); | ||
console.dir (err, { depth: null, colors: true }); | ||
console.log (); | ||
console.error (err.stack); | ||
console.log (err.stack); | ||
console.log (); | ||
errors++; | ||
} else { | ||
var testErrors = []; | ||
tests.forEach (function (test) { | ||
if (test[1] !== true) { | ||
testErrors.push (test[0]); | ||
for (i = 0; i < tests.length; i++) { | ||
if (tests [i] [1] !== true) { | ||
testErrors.push (tests [i] [0]); | ||
errors++; | ||
} | ||
}); | ||
} | ||
if (testErrors.length === 0) { | ||
console.log (label +': \033[1m\033[32mok\033[0m'); | ||
console.log ('\u001b[1m\u001b[32mgood\u001b[0m - ' + label); | ||
} else { | ||
console.error (label +': \033[1m\033[31mfailed\033[0m ('+ testErrors.join (', ') +')'); | ||
console.log ('\u001b[1m\u001b[31mFAIL\u001b[0m - ' + label + ' (' + testErrors.join (', ') + ')'); | ||
} | ||
@@ -97,3 +94,3 @@ } | ||
if (err) { | ||
console.log ('API access: failed ('+ err.message +')'); | ||
console.log ('API access: failed (' + err.message + ')'); | ||
console.log (err.stack); | ||
@@ -103,3 +100,3 @@ errors++; | ||
} else { | ||
console.log ('API access: \033[1m\033[32mok\033[0m'); | ||
console.log ('\u001b[1m\u001b[32mgood\u001b[0m - API access'); | ||
doNext (); | ||
@@ -113,5 +110,6 @@ } | ||
queue.push (function () { | ||
app ('GET', '/domains/'+ bogus.domain.id, function (err) { | ||
app ('GET', '/invalid-path', function (err) { | ||
doTest (null, 'API error', [ | ||
['type', err && err.message === 'API error'] | ||
['type', err instanceof Error], | ||
['message', err && err.message === 'API error'] | ||
]); | ||
@@ -125,30 +123,17 @@ }); | ||
var tmp_acc = acc; | ||
tmp_acc.timeout = 1; | ||
var tmp_app = require ('./') (tmp_acc); | ||
tmp_app ('GET', '/prices', function (err, data) { | ||
if (err) { | ||
doTest (null, 'Timeout error', [ | ||
['type', err instanceof Error], | ||
['message', err.message && err.message === 'request timeout'], | ||
['data', !data] | ||
]); | ||
} | ||
require ('./') (tmp_acc) ('GET', '/prices', function (err, data) { | ||
doTest (null, 'Timeout error', [ | ||
['type', err instanceof Error], | ||
['code', err.error.code === 'TIMEOUT'], | ||
['data', !data] | ||
]); | ||
}); | ||
delete tmp_app; | ||
delete tmp_acc; | ||
}); | ||
// fake material to use | ||
var bogus = { | ||
domain: { | ||
name: 'test-'+ Date.now () +'-delete.me' | ||
} | ||
}; | ||
// ! POST object | ||
queue.push (function () { | ||
var works = null; | ||
var input = { | ||
@@ -159,8 +144,11 @@ domain: { | ||
}; | ||
app ('POST', '/domains', input, function (err, data, meta) { | ||
bogus.domain = data.domain; | ||
if (data) { | ||
bogus.domain = data.domain; | ||
} | ||
doTest (err, 'POST object', [ | ||
['code', meta.statusCode === 201], | ||
['type', data && data.domain instanceof Object], | ||
['item', data.domain.name === bogus.domain.name] | ||
['type', works = data && data.domain instanceof Object], | ||
['item', works && data.domain.name === bogus.domain.name] | ||
]); | ||
@@ -172,6 +160,8 @@ }); | ||
queue.push (function () { | ||
app ('GET', '/domains/'+ bogus.domain.id, function (err, data) { | ||
var works = null; | ||
app ('GET', '/domains/' + bogus.domain.id, function (err, data) { | ||
doTest (err, 'GET object', [ | ||
['type', data && data.domain instanceof Object], | ||
['name', data && data.domain.name === bogus.domain.name] | ||
['type', works = data && data.domain instanceof Object], | ||
['name', works && data.domain.name === bogus.domain.name] | ||
]); | ||
@@ -184,7 +174,9 @@ }); | ||
app ('GET', '/domains', function (err, data) { | ||
var works = null; | ||
doTest (err, 'GET array object', [ | ||
['data type', data instanceof Array], | ||
['data size', data && data.length >= 1], | ||
['item type', data && data[0] && data[0].domain instanceof Object], | ||
['item name', data && data[0] && data[0].domain && typeof data[0].domain.name === 'string'] | ||
['data type', works = data instanceof Array], | ||
['data size', works = works && data.length >= 1], | ||
['item type', works = works && data[0].domain instanceof Object], | ||
['item name', works && typeof data[0].domain.name === 'string'] | ||
]); | ||
@@ -196,3 +188,3 @@ }); | ||
queue.push (function () { | ||
app ('DELETE', '/domains/'+ bogus.domain.id, function (err, data) { | ||
app ('DELETE', '/domains/' + bogus.domain.id, function (err, data) { | ||
doTest (err, 'DELETE', [ | ||
@@ -206,2 +198,6 @@ ['data', data === true] | ||
// Start the tests | ||
queue[0](); | ||
console.log ('Running tests...'); | ||
console.log ('Node.js v' + process.versions.node); | ||
console.log ('Module v' + pkg.version); | ||
console.log (); | ||
queue [0] (); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
21228
7
1
297
214
+ Addedhttpreq@0.4.x
+ Addedhttpreq@0.4.24(transitive)