Comparing version 1.0.0 to 1.1.0
@@ -17,3 +17,3 @@ var crypto = require('crypto'); | ||
function Canduit (opts, cb) { | ||
this.client = opts.client || 'duit'; | ||
this.client = opts.client || 'canduit'; | ||
this.logger = opts.logger || { | ||
@@ -64,2 +64,3 @@ log: function silent () { } | ||
self.cert = arcrc.hosts[host].cert; | ||
self.token = arcrc.hosts[host].token; | ||
self.api = host; | ||
@@ -73,5 +74,16 @@ cb(null); | ||
Canduit.prototype.exec = function exec (route, params, cb) { | ||
var logger = this.logger; | ||
Canduit.prototype.requestWithToken = function requestWithToken (route, params, cb) { | ||
params['api.token'] = this.token; | ||
var req = request.get(this.api + route, { | ||
json: true, | ||
qs: params, | ||
}, cb); | ||
this.logger.log('GET %s', this.api + route); | ||
return req; | ||
}; | ||
Canduit.prototype.requestWithCert = function requestWithCert (route, params, cb) { | ||
if (this.session) { | ||
@@ -87,7 +99,26 @@ params.__conduit__ = this.session; | ||
} | ||
}, function (err, response, data) { | ||
if (err) return cb(err, null); | ||
}, cb); | ||
this.logger.log('POST to %s with %s', | ||
this.api + route, req.body.toString()); | ||
return req; | ||
}; | ||
Canduit.prototype.exec = function exec (route, params, cb) { | ||
var logger = this.logger; | ||
var request = this.token ? this.requestWithToken : this.requestWithCert; | ||
var req = request.call(this, route, params || {}, processResponse); | ||
function processResponse(error, response, data) { | ||
if (error) return cb(error, null); | ||
if (response.statusCode >= 400) { | ||
return cb(Canduit.serverError(response), null); | ||
} | ||
if (data.result) { | ||
data = data.result; | ||
} | ||
if (data.error_info) { | ||
@@ -100,11 +131,10 @@ return cb(Canduit.conduitError(data), null); | ||
cb(null, data.result); | ||
}); | ||
cb(null, data); | ||
} | ||
logger.log('POST to %s with %s', | ||
this.api + route, req.body.toString()); | ||
return req; | ||
}; | ||
Canduit.prototype.authenticate = function authenticate (cb) { | ||
if (!this.cert) return cb(null, this); | ||
if (!this.cert || this.token) return cb(null, this); | ||
@@ -111,0 +141,0 @@ var authToken = Date.now() / 1000; |
{ | ||
"name": "canduit", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Node.js Phabricator Conduit API client", | ||
@@ -5,0 +5,0 @@ "main": "canduit.js", |
@@ -27,3 +27,3 @@ # canduit [![Build status](https://travis-ci.org/uber/canduit.png?branch=master)](https://travis-ci.org/uber/canduit) | ||
The Canduit constructor takes an optional `config` parameter, and a `callback(error, conduit)` function. The callback fires after the canduit client instance have successfully authenticated with the server. | ||
The Canduit constructor takes an optional `config` parameter, and a `callback(error, conduit)` function. The callback fires after the canduit client instance has successfully authenticated with the server. | ||
@@ -34,3 +34,3 @@ The first argument of the callback function is an error (if present), and the second is the reference to the conduit client instance for convenience. | ||
- `configFile` - file to read the Arcanist configuration parameters. By default, phab reads `~/.arcrc` for host and authentication configuration. | ||
- `configFile` - file to read the Arcanist configuration parameters. By default, canduit reads `~/.arcrc` for host and authentication configuration. | ||
@@ -42,4 +42,5 @@ You can also programmatically override the Conduit host and credentials: | ||
- `cert` - conduit certificate | ||
- `token` - token obtined via arc `install-certificate` | ||
#### `canduit.exec(route, params)` | ||
#### `canduit.exec(route, params, callback)` | ||
@@ -49,3 +50,11 @@ Call a [conduit API endpoint](https://secure.phabricator.com/book/phabdev/article/conduit/). | ||
- `params` object contains the parameters to pass. | ||
- `callback(error, result)` a callback function that fires when the conduit server responds. | ||
In case of client, server, or conduit-specific errors, the `error` parameter holds the Error instance, and the `result` is falsy. | ||
If the call suceeds, the `result` is set to an object containing response data. | ||
#### `canduit.authenticate(callback)` | ||
Re-authenticate the client with the server. Called internally by the constructor, but exposed in case you encounter an authentication timeout. | ||
## Contributing | ||
@@ -52,0 +61,0 @@ |
@@ -9,67 +9,76 @@ var fs = require('fs'); | ||
function Fixtures (test) { | ||
test('setup', this.setup.bind(this)); | ||
function Fixtures(test) { | ||
test('setup', this.setup.bind(this)); | ||
} | ||
Fixtures.prototype.teardown = function (test) { | ||
var self = this; | ||
var self = this; | ||
test('teardown', function (t) { | ||
self.fixedServer.destroy(t.end); | ||
}); | ||
test('teardown', function (t) { | ||
self.fixedServer.destroy(t.end); | ||
}); | ||
}; | ||
Fixtures.prototype.addFixture = function addFixture (route, response) { | ||
var self = this; | ||
Fixtures.prototype.addFixture = function addFixture(route, response, useToken) { | ||
var self = this; | ||
self.fixtureNames.push(route); | ||
self.fixedServer.installFixture({ | ||
method: 'post', | ||
route: route, | ||
response: function(req, res) { | ||
res.json(response); | ||
} | ||
}); | ||
self.fixtureNames.push(route); | ||
self.fixedServer.installFixture({ | ||
method: !useToken ? 'post' : 'get', | ||
route: route, | ||
response: function (req, res) { | ||
res.json(response); | ||
} | ||
}); | ||
}; | ||
Fixtures.prototype.setup = function setup (t) { | ||
var self = this; | ||
Fixtures.prototype.setup = function setup(t) { | ||
var self = this; | ||
parallel({ | ||
tmpName: tmp.tmpName, | ||
port: getport | ||
}, function (err, results) { | ||
if (err) throw err; | ||
parallel({ | ||
tmpName: tmp.tmpName, | ||
tokenTmpName: tmp.tmpName, | ||
port: getport | ||
}, function (err, results) { | ||
if (err) throw err; | ||
self.configFile = results.tmpName; | ||
self.port = results.port; | ||
self.configFile = results.tmpName; | ||
self.tokenConfigFile = results.tokenTmpName | ||
self.port = results.port; | ||
self.fixedServer = new FixedServer({ | ||
port: self.port | ||
}); | ||
self.fixedServer = new FixedServer({ | ||
port: self.port | ||
}); | ||
self.host = 'http:\/\/localhost:' + self.port + '\/api\/'; | ||
self.arcConfig = { hosts: { } }; | ||
self.arcConfig.hosts[self.host] = { | ||
'user': 'test', | ||
'cert': 'test-certificate' | ||
}; | ||
self.host = 'http:\/\/localhost:' + self.port + '\/api\/'; | ||
self.arcConfig = {hosts: {}}; | ||
self.arcConfig.hosts[self.host] = { | ||
'user': 'test', | ||
'cert': 'test-certificate' | ||
}; | ||
fs.writeFileSync(self.configFile, JSON.stringify(self.arcConfig)); | ||
self.tokenArcConfig = {hosts:{}}; | ||
self.tokenArcConfig.hosts[self.host] = { | ||
token: 'test-token' | ||
}; | ||
self.fixtureNames = []; | ||
fs.writeFileSync(self.configFile, JSON.stringify(self.arcConfig)); | ||
fs.writeFileSync(self.tokenConfigFile, JSON.stringify(self.tokenArcConfig)); | ||
self.addFixture('/api/conduit.connect', { | ||
'result': { | ||
'connectionID': 12345, | ||
'sessionKey': 'secret-key', | ||
'userPHID': 'PHID-USER-12345' | ||
}, | ||
'error_code': null, | ||
'error_info': null | ||
self.fixtureNames = []; | ||
self.addFixture('/api/conduit.connect', { | ||
'result': { | ||
'connectionID': 12345, | ||
'sessionKey': 'secret-key', | ||
'userPHID': 'PHID-USER-12345' | ||
}, | ||
'error_code': null, | ||
'error_info': null | ||
}); | ||
self.fixedServer.listen(); | ||
t.end(); | ||
}); | ||
self.fixedServer.listen(); | ||
t.end(); | ||
}); | ||
}; |
@@ -119,2 +119,55 @@ var test = require('tape'); | ||
test('create canduit instance with token', function (t) { | ||
t.doesNotThrow(function () { | ||
createCanduit({api: 'somelink', token: 'some-token'}, function noop ( ) { }); | ||
}, 'should not throw an error'); | ||
t.end(); | ||
}); | ||
test('requesting conduit api with token', function (t) { | ||
fixtures.addFixture('/api/user.query', { | ||
'result': [{ | ||
'phid': 'PHID-USER-12345', | ||
'userName': 'test', | ||
}], | ||
'error_code': null, | ||
'error_info': null | ||
}, true); | ||
createCanduit({ | ||
configFile: fixtures.tokenConfigFile | ||
}, function (err, canduit) { | ||
t.error(err); | ||
canduit.exec('user.query', { | ||
usernames: ['aleksey'] | ||
}, function (err, users) { | ||
t.error(err); | ||
t.ok(users, 'should call back with canduit API response'); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('requesting conduit api with token', function (t) { | ||
fixtures.addFixture('/api/user.query', { | ||
'result': [{ | ||
'phid': 'PHID-USER-12345', | ||
'userName': 'test', | ||
}], | ||
'error_code': null, | ||
'error_info': null | ||
}, true); | ||
createCanduit({ | ||
configFile: fixtures.tokenConfigFile | ||
}, function (err, canduit) { | ||
t.error(err); | ||
canduit.exec('user.query', null, function (err, users) { | ||
t.error(err); | ||
t.ok(users, 'should call back with canduit API response'); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('attempting to request a non-existing api', function (t) { | ||
@@ -136,2 +189,57 @@ createCanduit({ | ||
test('requesting conduit api route that returns an error with token', function (t) { | ||
fixtures.addFixture('/api/error', { | ||
'error_code': 400, | ||
'error_info': 'test error' | ||
}, true); | ||
createCanduit({ | ||
configFile: fixtures.tokenConfigFile | ||
}, function (err, canduit) { | ||
t.error(err); | ||
canduit.exec('error', { | ||
data: ['test'] | ||
}, function (err, result) { | ||
t.ok(err, 'should call back with a error'); | ||
t.ok(err.code === 400, 'should match the canduit error'); | ||
t.ok(!result, 'should not call back with data'); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('requesting a non-existing route with token', function (t) { | ||
createCanduit({ | ||
configFile: fixtures.tokenConfigFile | ||
}, function (err, canduit) { | ||
t.error(err); | ||
canduit.exec('404', { | ||
data: ['test'] | ||
}, function (err, result) { | ||
t.ok(err, 'should call back with an error'); | ||
t.ok(err.code === 404, 'should match the server error'); | ||
t.ok(!result, 'should not call back with data'); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('attempting to request a non-existing api with token', function (t) { | ||
createCanduit({ | ||
api: 'http://localhost/does/not/exist', | ||
token: 'test-token' | ||
}, function (err, canduit) { | ||
t.error(err); | ||
canduit.exec('404', { | ||
data: ['test'] | ||
}, function (err, result) { | ||
t.ok(err, 'should call back with an error'); | ||
t.ok(err.code === 'ECONNREFUSED', 'should match the client error'); | ||
t.ok(!result, 'should not call back with data'); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
fixtures.teardown(test); |
17242
412
65