diff --git a/packages/prediction/README.md b/packages/prediction/README.md index ed735915fec..0d95f2dc8cc 100644 --- a/packages/prediction/README.md +++ b/packages/prediction/README.md @@ -45,6 +45,16 @@ model.query('Hello', function(err, results) { // ] } }); + +// Promises are also supported by omitting callbacks. +model.query('Hello').then(function(data) { + var results = data[0]; +}); + +// It's also possible to integrate with third-party Promise libraries. +var prediction = require('@google-cloud/prediction')({ + promise: require('bluebird') +}); ``` diff --git a/packages/prediction/package.json b/packages/prediction/package.json index aff1d507891..bdbe6858270 100644 --- a/packages/prediction/package.json +++ b/packages/prediction/package.json @@ -50,7 +50,7 @@ "prediction" ], "dependencies": { - "@google-cloud/common": "^0.6.0", + "@google-cloud/common": "^0.7.0", "JSONStream": "^1.0.7", "arrify": "^1.0.0", "extend": "^3.0.0", diff --git a/packages/prediction/src/index.js b/packages/prediction/src/index.js index eecb91d930c..f035d692cc7 100644 --- a/packages/prediction/src/index.js +++ b/packages/prediction/src/index.js @@ -121,6 +121,14 @@ util.inherits(Prediction, common.Service); * prediction.createModel('my-model', { * data: modelDataCsv * }, function(err, model, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * prediction.createModel('my-model').then(function(data) { + * var model = data[0]; + * var apiResponse = data[1]; + * }); */ Prediction.prototype.createModel = function(id, options, callback) { var self = this; @@ -219,25 +227,11 @@ Prediction.prototype.createModel = function(id, options, callback) { * }, callback); * * //- - * // Get the models from your project as a readable object stream. + * // If the callback is omitted, we'll return a Promise. * //- - * prediction.getModels() - * .on('error', console.error) - * .on('data', function(model) { - * // model is a Model object. - * }) - * .on('end', function() { - * // All models retrieved. - * }); - * - * //- - * // If you anticipate many results, you can end a stream early to prevent - * // unnecessary processing and API requests. - * //- - * prediction.getModels() - * .on('data', function(model) { - * this.end(); - * }); + * prediction.getModels().then(function(data) { + * var models = data[0]; + * }); */ Prediction.prototype.getModels = function(query, callback) { var self = this; @@ -274,6 +268,35 @@ Prediction.prototype.getModels = function(query, callback) { }); }; +/** + * Gets a list of {module:prediction/model} objects for the project as a + * readable object stream. + * + * @param {object=} query - Configuration object. See + * {module:prediction#getModels} for a complete list of options. + * @return {stream} + * + * @example + * prediction.getModelsStream() + * .on('error', console.error) + * .on('data', function(model) { + * // model is a Model object. + * }) + * .on('end', function() { + * // All models retrieved. + * }); + * + * //- + * // If you anticipate many results, you can end a stream early to prevent + * // unnecessary processing and API requests. + * //- + * prediction.getModelsStream() + * .on('data', function(model) { + * this.end(); + * }); + */ +Prediction.prototype.getModelsStream = common.paginator.streamify('getModels'); + /** * Create a model object representing a trained model. * @@ -295,10 +318,19 @@ Prediction.prototype.model = function(id) { /*! Developer Documentation * - * These methods can be used with either a callback or as a readable object - * stream. `streamRouter` is used to add this dual behavior. + * These methods can be auto-paginated. */ -common.streamRouter.extend(Prediction, 'getModels'); +common.paginator.extend(Prediction, 'getModels'); + +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +common.util.promisifyAll(Prediction, { + exclude: ['model'] +}); + Prediction.Model = Model; diff --git a/packages/prediction/src/model.js b/packages/prediction/src/model.js index 456041b4eb6..e62f3eac3d7 100644 --- a/packages/prediction/src/model.js +++ b/packages/prediction/src/model.js @@ -60,6 +60,14 @@ function Model(prediction, id) { * // The model was created successfully. * } * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.create().then(function(data) { + * var model = data[0]; + * var apiResponse = data[1]; + * }); */ create: true, @@ -75,6 +83,13 @@ function Model(prediction, id) { * * @example * model.delete(function(err, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.delete().then(function(data) { + * var apiResponse = data[0]; + * }); */ delete: true, @@ -88,6 +103,13 @@ function Model(prediction, id) { * * @example * model.exists(function(err, exists) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.exists().then(function(data) { + * var exists = data[0]; + * }); */ exists: true, @@ -107,6 +129,14 @@ function Model(prediction, id) { * model.get(function(err, model, apiResponse) { * // `model.metadata` has been populated. * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.get().then(function(data) { + * var model = data[0]; + * var apiResponse = data[1]; + * }); */ get: true, @@ -122,6 +152,14 @@ function Model(prediction, id) { * * @example * model.getMetadata(function(err, metadata, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.getMetadata().then(function(data) { + * var metadata = data[0]; + * var apiResponse = data[1]; + * }); */ getMetadata: true, @@ -148,6 +186,13 @@ function Model(prediction, id) { * }; * * model.setMetadata(metadata, function(err, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.setMetadata(metadata).then(function(data) { + * var apiResponse = data[0]; + * }); */ setMetadata: { reqOpts: { @@ -188,6 +233,14 @@ util.inherits(Model, common.ServiceObject); * // `analysis.model` == {...} * } * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.analyze().then(function(data) { + * var analysis = data[0]; + * var apiResponse = data[1]; + * }); */ Model.prototype.analyze = function(callback) { this.request({ @@ -241,7 +294,7 @@ Model.prototype.createWriteStream = function(label) { csvInstance: [[]] }).split('[]'); - var requestStream = self.request({ + var requestStream = self.requestStream({ method: 'PUT', uri: '', headers: { @@ -312,6 +365,14 @@ Model.prototype.createWriteStream = function(label) { * // ] * } * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.query('Hello').then(function(data) { + * var results = data[0]; + * var apiResponse = data[1]; + * }); */ Model.prototype.query = function(input, callback) { this.request({ @@ -365,6 +426,13 @@ Model.prototype.query = function(input, callback) { * // New data was inserted successfully. * } * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * model.train('english', 'Hello from Stephen!').then(function(data) { + * var apiResponse = data[0]; + * }); */ Model.prototype.train = function(label, input, callback) { this.setMetadata({ @@ -373,4 +441,11 @@ Model.prototype.train = function(label, input, callback) { }, callback); }; +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +common.util.promisifyAll(Model); + module.exports = Model; diff --git a/packages/prediction/system-test/prediction.js b/packages/prediction/system-test/prediction.js index e8ea6592182..2e97ed65490 100644 --- a/packages/prediction/system-test/prediction.js +++ b/packages/prediction/system-test/prediction.js @@ -138,7 +138,7 @@ describe('Prediction', function() { }); it('should return models in stream mode', function(done) { - prediction.getModels({ maxResults: 1 }) + prediction.getModelsStream({ maxResults: 1 }) .on('error', done) .once('data', function() { done(); diff --git a/packages/prediction/test/index.js b/packages/prediction/test/index.js index 22111064f44..acd66739fd8 100644 --- a/packages/prediction/test/index.js +++ b/packages/prediction/test/index.js @@ -36,7 +36,7 @@ function FakeService() { nodeutil.inherits(FakeService, Service); var extended = false; -var fakeStreamRouter = { +var fakePaginator = { extend: function(Class, methods) { if (Class.name !== 'Prediction') { return; @@ -46,12 +46,23 @@ var fakeStreamRouter = { methods = arrify(methods); assert.equal(Class.name, 'Prediction'); assert.deepEqual(methods, ['getModels']); + }, + streamify: function(methodName) { + return methodName; } }; - +var promisified = false; var fakeUtil = extend({}, util, { - makeAuthenticatedRequestFactory: util.noop + makeAuthenticatedRequestFactory: util.noop, + promisifyAll: function(Class, options) { + if (Class.name !== 'Prediction') { + return; + } + + promisified = true; + assert.deepEqual(options.exclude, ['model']); + } }); describe('Prediction', function() { @@ -64,7 +75,7 @@ describe('Prediction', function() { Prediction = proxyquire('../', { '@google-cloud/common': { Service: FakeService, - streamRouter: fakeStreamRouter, + paginator: fakePaginator, util: fakeUtil }, './model.js': FakeModel, @@ -79,7 +90,15 @@ describe('Prediction', function() { describe('instantiation', function() { it('should extend the correct methods', function() { - assert(extended); // See `fakeStreamRouter.extend` + assert(extended); // See `fakePaginator.extend` + }); + + it('should streamify the correct methods', function() { + assert.strictEqual(prediction.getModelsStream, 'getModels'); + }); + + it('should promisify all the things', function() { + assert(promisified); }); it('should normalize the arguments', function() { diff --git a/packages/prediction/test/model.js b/packages/prediction/test/model.js index 324e5c9ef79..0465890636b 100644 --- a/packages/prediction/test/model.js +++ b/packages/prediction/test/model.js @@ -41,6 +41,13 @@ var fakeUtil = Object.keys(util).reduce(function(fakeUtil, methodName) { return fakeUtil; }, {}); +var promisified = false; +fakeUtil.promisifyAll = function(Class) { + if (Class.name === 'Model') { + promisified = true; + } +}; + describe('Index', function() { var Model; var model; @@ -100,6 +107,10 @@ describe('Index', function() { assert.strictEqual(createMethodBound, true); }); + + it('should promisify all the things', function() { + assert(promisified); + }); }); describe('analyze', function() { @@ -175,7 +186,7 @@ describe('Index', function() { describe('createWriteStream', function() { it('should wait for the write event to make the request', function(done) { - model.request = function() { + model.requestStream = function() { setImmediate(done); return through(); }; @@ -185,7 +196,7 @@ describe('Index', function() { }); it('should make the correct request', function(done) { - model.request = function(reqOpts) { + model.requestStream = function(reqOpts) { assert.strictEqual(reqOpts.method, 'PUT'); assert.strictEqual(reqOpts.uri, ''); assert.deepEqual(reqOpts.headers, { @@ -205,7 +216,7 @@ describe('Index', function() { it('should re-emit the response from the API request', function(done) { var response = {}; - model.request = function() { + model.requestStream = function() { var requestStream = through(); setImmediate(function() { requestStream.emit('response', response); @@ -226,7 +237,7 @@ describe('Index', function() { var writeStream; beforeEach(function() { - model.request = function() { + model.requestStream = function() { requestStream = concat(); return requestStream; }; @@ -319,7 +330,7 @@ describe('Index', function() { }); beforeEach(function(done) { - model.request = function() { + model.requestStream = function() { return concat(); };