diff --git a/README.md b/README.md index b31a7cb..a61149e 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,20 @@ restify HTTP errors). If `obj` looks like a `RestError`: then `err` gets "upconverted" into a `RestError` for you. Otherwise it will be an `HttpError`. +**Promise interface:** + +Without passing a callback as a last parameter to the method call, it returns +a Promise. + +This returned Promise will be resolved with the same arguments as the callback +version would, except that arguments will be wrapped in an object, for example: + +```js +client.get('/foo/bar') + .then(function({ req, res, obj }) {}) + .catch(function(err) {}); +``` + #### createJsonClient(options) ```javascript @@ -228,6 +242,20 @@ the source for `JsonClient`. Effectively, you extend it, and set the appropriate options in the constructor and implement a `write` (for put/post) and `parse` method (for all HTTP bodies), and that's it. +**Promise interface:** + +Without passing a callback as a last parameter to the method call, it returns +a Promise. + +This returned Promise will be resolved with the same arguments as the callback +version would, except that arguments will be wrapped in an object, for example: + +```js +client.get('/foo/bar') + .then(function({ req, res, data }) {}) + .catch(function(err) {}); +``` + #### createStringClient(options) ```javascript diff --git a/lib/StringClient.js b/lib/StringClient.js index f7cafed..776c37c 100644 --- a/lib/StringClient.js +++ b/lib/StringClient.js @@ -8,9 +8,55 @@ var zlib = require('zlib'); var assert = require('assert-plus'); var qs = require('querystring'); var util = require('util'); +var Promise = require('es6-promise'); var HttpClient = require('./HttpClient'); +/** + * Returns a Promise when callback fn is undefined + * @function promisify + * @returns {Promise|*} + */ +function promisify () { + var args = Array.prototype.slice.call(arguments); + var fn = args.shift(); + var _this = this; + + assert.func(fn, 'args[0]'); + + // Is callback presented? + if (typeof args[args.length - 1] === 'function') { + return (fn.apply(this, args)); + } + + return new Promise(function (resolve, reject) { + // callback is passed as last argument (undefined) + args[args.length - 1] = function (err, req, res, data) { + if (err) { + reject(err); + return; + } + + var result = { + req: req, + res: res + }; + + if (data) { + if (_this.constructor.name === 'JsonClient') { + result.obj = data; + } else { + result.data = data; + } + } + + resolve(result); + } + + fn.apply(_this, args); + }); +} + // --- API @@ -57,27 +103,58 @@ function normalizeArgs(arg1, arg2, arg3) { }; } +StringClient.prototype.del = function del(options, callback) { + var opts = this._options('DELETE', options); + var read = promisify.bind(this, this.read); + return (read(opts, callback)); +}; + + +StringClient.prototype.get = function get(options, callback) { + var opts = this._options('GET', options); + var read = promisify.bind(this, this.read); + return (read(opts, callback)); +}; + + +StringClient.prototype.head = function head(options, callback) { + var opts = this._options('HEAD', options); + var read = promisify.bind(this, this.read); + return (read(opts, callback)); +}; + + +StringClient.prototype.opts = function httpOptions(options, callback) { + var opts = this._options('OPTIONS', options); + var read = promisify.bind(this, this.read); + return (read(opts, callback)); +}; + + StringClient.prototype.post = function post(options, body, callback) { var opts = this._options('POST', options); + var write = promisify.bind(this, this.write); var args = normalizeArgs.apply(null, arguments); - return (this.write(opts, args.body, args.callback)); + return (write(opts, args.body, args.callback)); }; StringClient.prototype.put = function put(options, body, callback) { var opts = this._options('PUT', options); + var write = promisify.bind(this, this.write); var args = normalizeArgs.apply(null, arguments); - return (this.write(opts, args.body, args.callback)); + return (write(opts, args.body, args.callback)); }; StringClient.prototype.patch = function patch(options, body, callback) { var opts = this._options('PATCH', options); + var write = promisify.bind(this, this.write); var args = normalizeArgs.apply(null, arguments); - return (this.write(opts, args.body, args.callback)); + return (write(opts, args.body, args.callback)); }; diff --git a/package-lock.json b/package-lock.json index 6c67b23..7e40291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "JSV": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", + "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=", + "dev": true + }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -700,6 +706,11 @@ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, + "es6-promise": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", + "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng==" + }, "escape-regexp-component": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", @@ -1777,12 +1788,6 @@ "verror": "1.10.0" } }, - "JSV": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", - "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=", - "dev": true - }, "keep-alive-agent": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz", @@ -4281,14 +4286,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -4299,6 +4296,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -5165,15 +5170,6 @@ "integrity": "sha1-fY5rTgOsR4F3j4x5UXUBv7B2Kp8=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -5201,6 +5197,15 @@ } } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", diff --git a/package.json b/package.json index 4637912..beb926c 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "assert-plus": "^1.0.0", "backoff": "^2.4.1", "bunyan": "^1.8.3", + "es6-promise": "4.1.1", "fast-safe-stringify": "^1.1.3", "keep-alive-agent": "0.0.1", "lodash": "^4.7.0", diff --git a/test/index.js b/test/index.js index 1df07c3..61ee84c 100644 --- a/test/index.js +++ b/test/index.js @@ -273,6 +273,15 @@ describe('restify-client tests', function () { }); }); + it('GET json via Promise interface', function () { + return JSON_CLIENT.get('/json/mcavage') + .then(function (result) { + assert.ok(result.req); + assert.ok(result.res); + assert.deepEqual(result.obj, {hello: 'mcavage'}); + }); + }); + it('GH-778 GET jsonp', function (done) { // Using variables here to keep lines under 80 chars var jsonpUrl = '/json/jsonp?callback=testCallback'; @@ -354,6 +363,16 @@ describe('restify-client tests', function () { }); }); + it('POST json via Promise interface', function () { + var data = { hello: 'foo' }; + return JSON_CLIENT.post('/json/mcavage', data) + .then(function (result) { + assert.ok(result.req); + assert.ok(result.res); + assert.deepEqual(result.obj, {hello: 'foo'}); + }); + }); + it('POST with circular JSON', function (done) { var data = { hello: 'foo' @@ -463,6 +482,16 @@ describe('restify-client tests', function () { }); }); + it('GET text via Promise interface', function () { + return STR_CLIENT.get('/str/mcavage') + .then(function (result) { + assert.ok(result.req); + assert.ok(result.res); + assert.equal(result.res.body, result.data); + assert.equal(result.data, 'hello mcavage'); + }); + }); + it('GET PARTIAL text', function (done) { var opts = { path: '/str/mcavage',