Skip to content

Commit de08857

Browse files
author
Peter Marton
committed
feat(promise): add promise interface
1 parent bbdeb79 commit de08857

6 files changed

Lines changed: 184 additions & 26 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ restify HTTP errors). If `obj` looks like a `RestError`:
124124
then `err` gets "upconverted" into a `RestError` for you. Otherwise
125125
it will be an `HttpError`.
126126

127+
**Promise interface:**
128+
129+
Without passing a callback as a last parameter to the method call, it returns
130+
a Promise.
131+
132+
This returned Promise will be resolved with the same arguments as the callback
133+
version would, except that arguments will be wrapped in an object, for example:
134+
135+
```js
136+
client.get('/foo/bar')
137+
.then(function({ req, res, obj }) {})
138+
.catch(function(err) {});
139+
```
140+
127141
#### createJsonClient(options)
128142

129143
```javascript
@@ -228,6 +242,20 @@ the source for `JsonClient`. Effectively, you extend it, and set the
228242
appropriate options in the constructor and implement a `write` (for
229243
put/post) and `parse` method (for all HTTP bodies), and that's it.
230244

245+
**Promise interface:**
246+
247+
Without passing a callback as a last parameter to the method call, it returns
248+
a Promise.
249+
250+
This returned Promise will be resolved with the same arguments as the callback
251+
version would, except that arguments will be wrapped in an object, for example:
252+
253+
```js
254+
client.get('/foo/bar')
255+
.then(function({ req, res, data }) {})
256+
.catch(function(err) {});
257+
```
258+
231259
#### createStringClient(options)
232260

233261
```javascript

lib/StringClient.js

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,55 @@ var zlib = require('zlib');
88
var assert = require('assert-plus');
99
var qs = require('querystring');
1010
var util = require('util');
11+
var Promise = require('es6-promise');
1112

1213
var HttpClient = require('./HttpClient');
1314

15+
/**
16+
* Returns a Promise when callback fn is undefined
17+
* @function promisify
18+
* @returns {Promise|*}
19+
*/
20+
function promisify () {
21+
var args = Array.prototype.slice.call(arguments);
22+
var fn = args.shift();
23+
var self = this;
24+
25+
assert.func(fn, 'args[0]');
26+
27+
// Is callback presented?
28+
if (typeof args[args.length - 1] === 'function') {
29+
return (fn.apply(self, args));
30+
}
31+
32+
return new Promise(function (resolve, reject) {
33+
// callback is passed as last argument (undefined)
34+
args[args.length - 1] = function (err, req, res, data) {
35+
if (err) {
36+
reject(err);
37+
return;
38+
}
39+
40+
var result = {
41+
req: req,
42+
res: res
43+
};
44+
45+
if (data) {
46+
if (self.constructor.name === 'JsonClient') {
47+
result.obj = data;
48+
} else {
49+
result.data = data;
50+
}
51+
}
52+
53+
resolve(result);
54+
};
55+
56+
fn.apply(self, args);
57+
});
58+
}
59+
1460

1561
// --- API
1662

@@ -57,27 +103,58 @@ function normalizeArgs(arg1, arg2, arg3) {
57103
};
58104
}
59105

106+
StringClient.prototype.del = function del(options, callback) {
107+
var opts = this._options('DELETE', options);
108+
var read = promisify.bind(this, this.read);
109+
return (read(opts, callback));
110+
};
111+
112+
113+
StringClient.prototype.get = function get(options, callback) {
114+
var opts = this._options('GET', options);
115+
var read = promisify.bind(this, this.read);
116+
return (read(opts, callback));
117+
};
118+
119+
120+
StringClient.prototype.head = function head(options, callback) {
121+
var opts = this._options('HEAD', options);
122+
var read = promisify.bind(this, this.read);
123+
return (read(opts, callback));
124+
};
125+
126+
127+
StringClient.prototype.opts = function httpOptions(options, callback) {
128+
var opts = this._options('OPTIONS', options);
129+
var read = promisify.bind(this, this.read);
130+
return (read(opts, callback));
131+
};
132+
133+
60134
StringClient.prototype.post = function post(options, body, callback) {
61135
var opts = this._options('POST', options);
136+
var write = promisify.bind(this, this.write);
62137

63138
var args = normalizeArgs.apply(null, arguments);
64-
return (this.write(opts, args.body, args.callback));
139+
return (write(opts, args.body, args.callback));
65140
};
66141

67142

68143
StringClient.prototype.put = function put(options, body, callback) {
69144
var opts = this._options('PUT', options);
145+
var write = promisify.bind(this, this.write);
70146

71147
var args = normalizeArgs.apply(null, arguments);
72-
return (this.write(opts, args.body, args.callback));
148+
return (write(opts, args.body, args.callback));
73149
};
74150

75151

76152
StringClient.prototype.patch = function patch(options, body, callback) {
77153
var opts = this._options('PATCH', options);
154+
var write = promisify.bind(this, this.write);
78155

79156
var args = normalizeArgs.apply(null, arguments);
80-
return (this.write(opts, args.body, args.callback));
157+
return (write(opts, args.body, args.callback));
81158
};
82159

83160

package-lock.json

Lines changed: 28 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"assert-plus": "^1.0.0",
6161
"backoff": "^2.4.1",
6262
"bunyan": "^1.8.3",
63+
"es6-promise": "4.1.1",
6364
"fast-safe-stringify": "^1.1.3",
6465
"keep-alive-agent": "0.0.1",
6566
"lodash": "^4.7.0",

test/index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ describe('restify-client tests', function () {
273273
});
274274
});
275275

276+
it('GET json via Promise interface', function () {
277+
return JSON_CLIENT.get('/json/mcavage')
278+
.then(function (result) {
279+
assert.ok(result.req);
280+
assert.ok(result.res);
281+
assert.deepEqual(result.obj, {hello: 'mcavage'});
282+
});
283+
});
284+
276285
it('GH-778 GET jsonp', function (done) {
277286
// Using variables here to keep lines under 80 chars
278287
var jsonpUrl = '/json/jsonp?callback=testCallback';
@@ -354,6 +363,16 @@ describe('restify-client tests', function () {
354363
});
355364
});
356365

366+
it('POST json via Promise interface', function () {
367+
var data = { hello: 'foo' };
368+
return JSON_CLIENT.post('/json/mcavage', data)
369+
.then(function (result) {
370+
assert.ok(result.req);
371+
assert.ok(result.res);
372+
assert.deepEqual(result.obj, {hello: 'foo'});
373+
});
374+
});
375+
357376
it('POST with circular JSON', function (done) {
358377
var data = {
359378
hello: 'foo'
@@ -463,6 +482,16 @@ describe('restify-client tests', function () {
463482
});
464483
});
465484

485+
it('GET text via Promise interface', function () {
486+
return STR_CLIENT.get('/str/mcavage')
487+
.then(function (result) {
488+
assert.ok(result.req);
489+
assert.ok(result.res);
490+
assert.equal(result.res.body, result.data);
491+
assert.equal(result.data, 'hello mcavage');
492+
});
493+
});
494+
466495
it('GET PARTIAL text', function (done) {
467496
var opts = {
468497
path: '/str/mcavage',

test/nock.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,23 @@ describe('restify-client tests against nock', function () {
4444
client.get('/nock', done);
4545
});
4646

47+
it('reject with promise interface', function (done) {
48+
var nockErr = new Error('My Error');
49+
50+
nock('http://127.0.0.1', {allowUnmocked: true})
51+
.get('/nock')
52+
.replyWithError(nockErr);
53+
54+
var client = clients.createJsonClient({
55+
url: 'http://127.0.0.1'
56+
});
57+
58+
client.get('/nock')
59+
.catch(function (err) {
60+
assert.equal(err, nockErr);
61+
done();
62+
});
63+
});
64+
4765
});
4866

0 commit comments

Comments
 (0)