diff --git a/lib/common/service-object.js b/lib/common/service-object.js index ae75f3e1a72..45bd5649415 100644 --- a/lib/common/service-object.js +++ b/lib/common/service-object.js @@ -21,6 +21,7 @@ 'use strict'; var exec = require('methmeth'); +var extend = require('extend'); var is = require('is'); /** @@ -49,6 +50,9 @@ var util = require('./util.js'); * name of a Storage bucket or Pub/Sub topic. * @param {object=} config.methods - A map of each method name that should be * inherited. + * @param {object} config.methods[].reqOpts - Default request options for this + * particular method. A common use case is when `setMetadata` requires a + * `PUT` method to override the default `PATCH`. * @param {object} config.parent - The parent service instance. For example, an * instance of Storage if the object is Bucket. */ @@ -61,6 +65,7 @@ function ServiceObject(config) { this.parent = config.parent; // Parent class. this.id = config.id; // Name or ID (e.g. dataset ID, bucket name, etc.) this.createMethod = config.createMethod; + this.methods = config.methods || {}; if (config.methods) { var allMethodNames = Object.keys(ServiceObject.prototype); @@ -130,10 +135,12 @@ ServiceObject.prototype.create = function(options, callback) { * @param {object} callback.apiResponse - The full API response. */ ServiceObject.prototype.delete = function(callback) { - var reqOpts = { + var methodConfig = this.methods.delete || {}; + + var reqOpts = extend({ method: 'DELETE', uri: '' - }; + }, methodConfig.reqOpts); callback = callback || util.noop; @@ -225,9 +232,11 @@ ServiceObject.prototype.get = function(config, callback) { ServiceObject.prototype.getMetadata = function(callback) { var self = this; - var reqOpts = { + var methodConfig = this.methods.getMetadata || {}; + + var reqOpts = extend({ uri: '' - }; + }, methodConfig.reqOpts); // The `request` method may have been overridden to hold any special behavior. // Ensure we call the original `request` method. @@ -257,11 +266,13 @@ ServiceObject.prototype.setMetadata = function(metadata, callback) { callback = callback || util.noop; - var reqOpts = { + var methodConfig = this.methods.setMetadata || {}; + + var reqOpts = extend(true, { method: 'PATCH', uri: '', json: metadata - }; + }, methodConfig.reqOpts); // The `request` method may have been overridden to hold any special behavior. // Ensure we call the original `request` method. diff --git a/test/common/service-object.js b/test/common/service-object.js index 0170be1d3cc..3ea29a79633 100644 --- a/test/common/service-object.js +++ b/test/common/service-object.js @@ -59,6 +59,22 @@ describe('ServiceObject', function() { assert.strictEqual(serviceObject.createMethod, CONFIG.createMethod); }); + it('should localize the methods', function() { + var methods = {}; + + var config = extend({}, CONFIG, { + methods: methods + }); + + var serviceObject = new ServiceObject(config); + + assert.strictEqual(serviceObject.methods, methods); + }); + + it('should default methods to an empty object', function() { + assert.deepEqual(serviceObject.methods, {}); + }); + it('should clear out methods that are not asked for', function() { var config = extend({}, CONFIG, { methods: { @@ -207,6 +223,27 @@ describe('ServiceObject', function() { serviceObject.delete(assert.ifError); }); + it('should extend the request options with defaults', function(done) { + var method = { + reqOpts: { + method: 'override', + qs: { + custom: true + } + } + }; + + ServiceObject.prototype.request = function(reqOpts_) { + assert.strictEqual(reqOpts_.method, method.reqOpts.method); + assert.deepEqual(reqOpts_.qs, method.reqOpts.qs); + done(); + }; + + var serviceObject = new ServiceObject(CONFIG); + serviceObject.methods.delete = method; + serviceObject.delete(); + }); + it('should not require a callback', function() { ServiceObject.prototype.request = function(reqOpts, callback) { callback(); @@ -384,6 +421,27 @@ describe('ServiceObject', function() { serviceObject.getMetadata(); }); + it('should extend the request options with defaults', function(done) { + var method = { + reqOpts: { + method: 'override', + qs: { + custom: true + } + } + }; + + ServiceObject.prototype.request = function(reqOpts_) { + assert.strictEqual(reqOpts_.method, method.reqOpts.method); + assert.deepEqual(reqOpts_.qs, method.reqOpts.qs); + done(); + }; + + var serviceObject = new ServiceObject(CONFIG); + serviceObject.methods.getMetadata = method; + serviceObject.getMetadata(); + }); + it('should execute callback with error & apiResponse', function(done) { var error = new Error('Error.'); var apiResponse = {}; @@ -445,6 +503,39 @@ describe('ServiceObject', function() { serviceObject.setMetadata(metadata); }); + it('should extend the request options with defaults', function(done) { + var metadataDefault = { + a: 'b' + }; + + var metadata = { + c: 'd' + }; + + var method = { + reqOpts: { + method: 'override', + qs: { + custom: true + }, + json: metadataDefault + } + }; + + var expectedJson = extend(true, {}, metadataDefault, metadata); + + ServiceObject.prototype.request = function(reqOpts_) { + assert.strictEqual(reqOpts_.method, method.reqOpts.method); + assert.deepEqual(reqOpts_.qs, method.reqOpts.qs); + assert.deepEqual(reqOpts_.json, expectedJson); + done(); + }; + + var serviceObject = new ServiceObject(CONFIG); + serviceObject.methods.setMetadata = method; + serviceObject.setMetadata(metadata); + }); + it('should execute callback with error & apiResponse', function(done) { var error = new Error('Error.'); var apiResponse = {}; diff --git a/test/common/service.js b/test/common/service.js index de6e8443666..f7aed52c760 100644 --- a/test/common/service.js +++ b/test/common/service.js @@ -32,7 +32,7 @@ util.makeAuthenticatedRequestFactory = function() { } }; -describe('ServiceObject', function() { +describe('Service', function() { var Service; var service;