diff --git a/lib/compute/address.js b/lib/compute/address.js index 45e3b3e8771..e03cda0b0db 100644 --- a/lib/compute/address.js +++ b/lib/compute/address.js @@ -20,6 +20,14 @@ 'use strict'; +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @type {module:common/util} * @private @@ -54,11 +62,86 @@ var util = require('../common/util.js'); * var address = region.address('address1'); */ function Address(region, name) { - this.region = region; + var methods = { + /** + * Create an address. + * + * @param {object=} options - See {module:compute#createAddress}. + * + * @example + * address.create(function(err, address, operation, apiResponse) { + * // `address` is an Address object. + * + * // `operation` is an Operation object that can be used to check the + * // of the request. + * }); + */ + create: true, + + /** + * Check if the address exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the address exists or not. + * + * @example + * address.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get an address if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * address.get(function(err, address, apiResponse) { + * // `address` is an Address object. + * }); + */ + get: true, + + /** + * Get the metadata of this address. + * + * @resource [Address Resource]{@link https://cloud.google.com/compute/docs/reference/v1/addresses} + * @resource [Addresses: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/addresses/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The address's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * address.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: region, + baseUrl: '/addresses', + id: name, + createMethod: region.createAddress.bind(region), + methods: methods + }); + this.name = name; - this.metadata = {}; + this.region = region; } +nodeutil.inherits(Address, ServiceObject); + /** * Delete the address. * @@ -81,7 +164,10 @@ Address.prototype.delete = function(callback) { var region = this.region; - this.makeReq_('DELETE', '', null, null, function(err, resp) { + this.request({ + method: 'DELETE', + uri: '' + }, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -94,52 +180,4 @@ Address.prototype.delete = function(callback) { }); }; -/** - * Get the metadata of this address. - * - * @resource [Address Resource]{@link https://cloud.google.com/compute/docs/reference/v1/addresses} - * @resource [Addresses: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/addresses/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The address's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * address.getMetadata(function(err, metadata, apiResponse) {}); - */ -Address.prototype.getMetadata = function(callback) { - callback = callback || util.noop; - - var self = this; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Address.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/addresses/' + this.name + path; - this.region.makeReq_(method, path, query, body, callback); -}; - module.exports = Address; diff --git a/lib/compute/disk.js b/lib/compute/disk.js index 53fc2a1cbd0..8647479471e 100644 --- a/lib/compute/disk.js +++ b/lib/compute/disk.js @@ -23,6 +23,19 @@ var extend = require('extend'); var format = require('string-format-obj'); var is = require('is'); +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + +/** + * @type {module:compute/snapshot} + * @private + */ +var Snapshot = require('./snapshot.js'); /** * @type {module:common/util} @@ -57,13 +70,92 @@ var util = require('../common/util.js'); * var disk = zone.disk('disk1'); */ function Disk(zone, name) { - this.zone = zone; + var methods = { + /** + * Create a persistent disk. + * + * @param {object} config - See {module:compute/zone#createDisk}. + * + * @example + * var config = { + * // ... + * }; + * + * disk.create(config, function(err, disk, operation, apiResponse) { + * // `disk` is a Disk object. + * + * // `operation` is an Operation object that can be used to check the + * // status of the request. + * }); + */ + create: true, + + /** + * Check if the disk exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the disk exists or not. + * + * @example + * disk.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a disk if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * disk.get(function(err, disk, apiResponse) { + * // `disk` is a Disk object. + * }); + */ + get: true, + + /** + * Get the disk's metadata. + * + * @resource [Disk Resource]{@link https://cloud.google.com/compute/docs/reference/v1/disks} + * @resource [Disks: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/disks/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The disk's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * disk.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: zone, + baseUrl: '/disks', + id: name, + createMethod: zone.createDisk.bind(zone), + methods: methods + }); + this.name = name; - this.metadata = {}; + this.zone = zone; this.formattedName = Disk.formatName_(zone, name); } +nodeutil.inherits(Disk, ServiceObject); + /** * Format a disk's name how the API expects. * @@ -110,6 +202,7 @@ Disk.formatName_ = function(zone, name) { * disk.createSnapshot('new-snapshot-name', callback); */ Disk.prototype.createSnapshot = function(name, options, callback) { + var self = this; var zone = this.zone; if (is.fn(options)) { @@ -117,17 +210,19 @@ Disk.prototype.createSnapshot = function(name, options, callback) { options = {}; } - var body = extend({}, options, { - name: name - }); - - this.makeReq_('POST', '/createSnapshot', null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/createSnapshot', + json: extend({}, options, { + name: name + }) + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; } - var snapshot = zone.compute.snapshot(name); + var snapshot = self.snapshot(name); var operation = zone.operation(resp.name); operation.metadata = resp; @@ -158,7 +253,7 @@ Disk.prototype.delete = function(callback) { callback = callback || util.noop; - this.makeReq_('DELETE', '', null, null, function(err, resp) { + ServiceObject.prototype.delete.call(this, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -172,51 +267,18 @@ Disk.prototype.delete = function(callback) { }; /** - * Get the disk's metadata. + * Get a reference to a snapshot from this disk. * - * @resource [Disk Resource]{@link https://cloud.google.com/compute/docs/reference/v1/disks} - * @resource [Disks: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/disks/get} + * @resource [Snapshots Overview]{@link https://cloud.google.com/compute/docs/disks/persistent-disks#snapshots} * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The disk's metadata. - * @param {object} callback.apiResponse - The full API response. + * @param {string} name - Name of the snapshot. + * @return {module:compute/snapshot} * * @example - * disk.getMetadata(function(err, metadata, apiResponse) {}); - */ -Disk.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. + * var snapshot = disk.snapshot('snapshot-name'); */ -Disk.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/disks/' + this.name + path; - this.zone.makeReq_(method, path, query, body, callback); +Disk.prototype.snapshot = function(name) { + return new Snapshot(this, name); }; module.exports = Disk; diff --git a/lib/compute/firewall.js b/lib/compute/firewall.js index f36fb33f6b1..8832cfc4ca5 100644 --- a/lib/compute/firewall.js +++ b/lib/compute/firewall.js @@ -20,6 +20,14 @@ 'use strict'; +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @type {module:common/util} * @private @@ -52,14 +60,91 @@ var util = require('../common/util.js'); * var firewall = gce.firewall('tcp-3000'); */ function Firewall(compute, name) { + var methods = { + /** + * Create a firewall. + * + * @param {object} config - See {module:compute#createFirewall}. + * + * @example + * var config = { + * // ... + * }; + * + * firewall.create(config, function(err, firewall, operation, apiResponse) { + * // `firewall` is a Firewall object. + * + * // `operation` is an Operation object that can be used to check the + * // status of the request. + * }); + */ + create: true, + + /** + * Check if the firewall exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the firewall exists or not. + * + * @example + * firewall.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a firewall if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * firewall.get(function(err, firewall, apiResponse) { + * // `firewall` is a Firewall object. + * }); + */ + get: true, + + /** + * Get the firewall's metadata. + * + * @resource [Firewalls: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/firewalls/get} + * @resource [Firewall Resource]{@link https://cloud.google.com/compute/docs/reference/v1/firewalls} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The firewall's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * firewall.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: compute, + baseUrl: '/global/firewalls', + id: name, + createMethod: compute.createFirewall.bind(compute), + methods: methods, + }); + this.compute = compute; this.name = name; - - this.metadata = { - network: 'global/networks/default' - }; + this.metadata.network = 'global/networks/default'; } +nodeutil.inherits(Firewall, ServiceObject); + /** * Delete the firewall. * @@ -82,7 +167,7 @@ Firewall.prototype.delete = function(callback) { callback = callback || util.noop; - this.makeReq_('DELETE', '', null, null, function(err, resp) { + ServiceObject.prototype.delete.call(this, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -95,37 +180,6 @@ Firewall.prototype.delete = function(callback) { }); }; -/** - * Get the firewall's metadata. - * - * @resource [Firewalls: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/firewalls/get} - * @resource [Firewall Resource]{@link https://cloud.google.com/compute/docs/reference/v1/firewalls} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The firewall's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * firewall.getMetadata(function(err, metadata, apiResponse) {}); - */ -Firewall.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - /** * Set the firewall's metadata. * @@ -158,7 +212,11 @@ Firewall.prototype.setMetadata = function(metadata, callback) { metadata.name = this.name; metadata.network = this.metadata.network; - this.makeReq_('PATCH', '', null, metadata, function(err, resp) { + this.request({ + method: 'PATCH', + uri: '', + json: metadata + }, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -171,21 +229,4 @@ Firewall.prototype.setMetadata = function(metadata, callback) { }); }; -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Firewall.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/global/firewalls/' + this.name + path; - this.compute.makeReq_(method, path, query, body, callback); -}; - module.exports = Firewall; diff --git a/lib/compute/index.js b/lib/compute/index.js index 3c72196e055..daa7003d427 100644 --- a/lib/compute/index.js +++ b/lib/compute/index.js @@ -23,6 +23,7 @@ var arrify = require('arrify'); var extend = require('extend'); var is = require('is'); +var nodeutil = require('util'); /** * @type {module:compute/firewall} @@ -48,6 +49,12 @@ var Operation = require('./operation.js'); */ var Region = require('./region.js'); +/** + * @type {module:common/service} + * @private + */ +var Service = require('../common/service.js'); + /** * @type {module:compute/snapshot} * @private @@ -72,19 +79,6 @@ var util = require('../common/util.js'); */ var Zone = require('./zone.js'); -/** - * @const {string} - * @private - */ -var COMPUTE_BASE_URL = 'https://www.googleapis.com/compute/v1/projects/'; - -/** - * Required scopes for Google Compute Engine API. - * @const {array} - * @private - */ -var SCOPES = ['https://www.googleapis.com/auth/compute']; - /** * A Compute object allows you to interact with the Google Compute Engine API. * Using this object, you can access your instances with {module:compute/vm}, @@ -110,16 +104,16 @@ function Compute(options) { return new Compute(options); } - this.makeAuthenticatedRequest_ = util.makeAuthenticatedRequestFactory({ - credentials: options.credentials, - keyFile: options.keyFilename, - scopes: SCOPES, - email: options.email - }); + var config = { + baseUrl: 'https://www.googleapis.com/compute/v1', + scopes: ['https://www.googleapis.com/auth/compute'] + }; - this.projectId = options.projectId; + Service.call(this, config, options); } +nodeutil.inherits(Compute, Service); + /** * Create a firewall. * @@ -211,9 +205,11 @@ Compute.prototype.createFirewall = function(name, config, callback) { delete body.tags; } - var path = '/global/firewalls'; - - this.makeReq_('POST', path, null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/global/firewalls', + json: body + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -259,7 +255,7 @@ Compute.prototype.createFirewall = function(name, config, callback) { * function callback(err, network, operation, apiResponse) { * // `network` is a Network object. * - * // `operation` is an Operation object and can be used to check the status + * // `operation` is an Operation object that can be used to check the status * // of network creation. * } * @@ -282,7 +278,11 @@ Compute.prototype.createNetwork = function(name, config, callback) { delete body.gateway; } - this.makeReq_('POST', '/global/networks', null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/global/networks', + json: body + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -305,11 +305,11 @@ Compute.prototype.createNetwork = function(name, config, callback) { * * @resource [Firewalls Overview]{@link https://cloud.google.com/compute/docs/networking#firewalls} * - * @param {string} name - Name of the existing firewall. + * @param {string} name - Name of the firewall. * @return {module:compute/firewall} * * @example - * var firewall = gce.firewall('existing-firewall'); + * var firewall = gce.firewall('firewall-name'); */ Compute.prototype.firewall = function(name) { return new Firewall(this, name); @@ -394,9 +394,10 @@ Compute.prototype.getAddresses = function(options, callback) { options = options || {}; - var path = '/aggregated/addresses'; - - this.makeReq_('GET', path, options, null, function(err, resp) { + this.request({ + uri: '/aggregated/addresses', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -506,7 +507,10 @@ Compute.prototype.getDisks = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/aggregated/disks', options, null, function(err, resp) { + this.request({ + uri: '/aggregated/disks', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -617,7 +621,10 @@ Compute.prototype.getFirewalls = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/global/firewalls', options, null, function(err, resp) { + this.request({ + uri: '/global/firewalls', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -710,6 +717,8 @@ Compute.prototype.getFirewalls = function(options, callback) { * }); */ Compute.prototype.getNetworks = function(options, callback) { + var self = this; + if (is.fn(options)) { callback = options; options = {}; @@ -717,8 +726,10 @@ Compute.prototype.getNetworks = function(options, callback) { options = options || {}; - var self = this; - this.makeReq_('GET', '/global/networks', options, null, function(err, resp) { + this.request({ + uri: '/global/networks', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -820,9 +831,10 @@ Compute.prototype.getOperations = function(options, callback) { options = options || {}; - var path = '/global/operations'; - - this.makeReq_('GET', path, options, null, function(err, resp) { + this.request({ + uri: '/global/operations', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -922,7 +934,10 @@ Compute.prototype.getRegions = function(options, callback) { options = {}; } - this.makeReq_('GET', '/regions', options, null, function(err, resp) { + this.request({ + uri: '/regions', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -1024,8 +1039,10 @@ Compute.prototype.getSnapshots = function(options, callback) { options = options || {}; - - this.makeReq_('GET', '/global/snapshots', options, null, function(err, resp) { + this.request({ + uri: '/global/snapshots', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -1126,9 +1143,10 @@ Compute.prototype.getVMs = function(options, callback) { options = options || {}; - var path = '/aggregated/instances'; - - this.makeReq_('GET', path, options, null, function(err, resp) { + this.request({ + uri: '/aggregated/instances', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -1237,7 +1255,10 @@ Compute.prototype.getZones = function(options, callback) { options = {}; } - this.makeReq_('GET', '/zones', options, null, function(err, resp) { + this.request({ + uri: '/zones', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -1266,7 +1287,7 @@ Compute.prototype.getZones = function(options, callback) { * * @resource [Networks Overview]{@link https://cloud.google.com/compute/docs/networking#networks} * - * @param {string} name - Name of the existing network. + * @param {string} name - Name of the network. * @return {module:compute/network} * * @example @@ -1336,32 +1357,6 @@ Compute.prototype.zone = function(name) { return new Zone(this, name); }; -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Compute.prototype.makeReq_ = function(method, path, query, body, callback) { - var reqOpts = { - method: method, - qs: query, - uri: COMPUTE_BASE_URL + this.projectId + path - }; - - if (body) { - reqOpts.json = body; - } - - this.makeAuthenticatedRequest_(reqOpts, callback); -}; - /*! Developer Documentation * * These methods can be used with either a callback or as a readable object diff --git a/lib/compute/network.js b/lib/compute/network.js index 9158d79a6ba..541a26c406d 100644 --- a/lib/compute/network.js +++ b/lib/compute/network.js @@ -23,6 +23,13 @@ var extend = require('extend'); var format = require('string-format-obj'); var is = require('is'); +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); /** * @type {module:common/util} @@ -55,13 +62,91 @@ var util = require('../common/util.js'); * var network = gce.network('network-name'); */ function Network(compute, name) { - this.compute = compute; - this.name = name; - this.metadata = {}; + var methods = { + /** + * Create a network. + * + * @param {object} config - See {module:compute#createNetwork}. + * + * @example + * var config = { + * // ... + * }; + * + * network.create(config, function(err, network, operation, apiResponse) { + * // `network` is a Network object. + * + * // `operation` is an Operation object that can be used to check the + * // status of network creation. + * }); + */ + create: true, + + /** + * Check if the network exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the network exists or not. + * + * @example + * network.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a network if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * network.get(function(err, network, apiResponse) { + * // `network` is a Network object. + * }); + */ + get: true, + + /** + * Get the network's metadata. + * + * @resource [Network Resource]{@link https://cloud.google.com/compute/docs/reference/v1/networks} + * @resource [Networks: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/networks/delete} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The network's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * network.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: compute, + baseUrl: '/global/networks', + id: name, + createMethod: compute.createNetwork.bind(compute), + methods: methods, + }); + this.compute = compute; this.formattedName = Network.formatName_(compute, name); + this.name = name; } +nodeutil.inherits(Network, ServiceObject); + /** * Format a network's name how the API expects. * @@ -151,7 +236,7 @@ Network.prototype.delete = function(callback) { callback = callback || util.noop; - this.makeReq_('DELETE', '', null, null, function(err, resp) { + ServiceObject.prototype.delete.call(this, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -169,7 +254,7 @@ Network.prototype.delete = function(callback) { * * @resource [Firewalls Overview]{@link https://cloud.google.com/compute/docs/networking#firewalls} * - * @param {string} name - Name of the existing firewall. + * @param {string} name - Name of the firewall. * * @example * var firewall = network.firewall('firewall-name'); @@ -258,52 +343,4 @@ Network.prototype.getFirewalls = function(options, callback) { return this.compute.getFirewalls(options, callback); }; -/** - * Get the network's metadata. - * - * @resource [Network Resource]{@link https://cloud.google.com/compute/docs/reference/v1/networks} - * @resource [Networks: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/networks/delete} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request. - * @param {object} callback.metadata - The network's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * network.getMetadata(function(err, metadata, apiResponse) {}); - */ -Network.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Network.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/global/networks/' + this.name + path; - this.compute.makeReq_(method, path, query, body, callback); -}; - module.exports = Network; diff --git a/lib/compute/operation.js b/lib/compute/operation.js index a26c2cf412d..6c143834c1f 100644 --- a/lib/compute/operation.js +++ b/lib/compute/operation.js @@ -22,6 +22,13 @@ var extend = require('extend'); var is = require('is'); +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); /** * @type {module:common/util} @@ -74,32 +81,61 @@ var util = require('../common/util.js'); * var operation = zone.operation('operation-id'); */ function Operation(scope, name) { - this.scope = scope; - this.name = name; - this.metadata = {}; -} + var isCompute = scope.constructor.name === 'Compute'; -/** - * Delete the operation. - * - * @resource [GlobalOperations: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalOperations/delete} - * @resource [RegionOperations: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/regionOperations/delete} - * @resource [ZoneOperations: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/zoneOperations/delete} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * operation.delete(function(err, apiResponse) {}); - */ -Operation.prototype.delete = function(callback) { - callback = callback || util.noop; + var methods = { + /** + * Delete the operation. + * + * @resource [GlobalOperations: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/globalOperations/delete} + * @resource [RegionOperations: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/regionOperations/delete} + * @resource [ZoneOperations: delete API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/zoneOperations/delete} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * operation.delete(function(err, apiResponse) {}); + */ + delete: true, + + /** + * Check if the operation exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the operation exists or not. + * + * @example + * operation.exists(function(err, exists) {}); + */ + exists: true, - this.makeReq_('DELETE', '', null, null, function(err, resp) { - callback(err, resp); + /** + * Get an operation if it exists. + * + * @example + * operation.get(function(err, operation, apiResponse) { + * // `operation` is an Operation object. + * }); + */ + get: true + }; + + ServiceObject.call(this, { + parent: scope, + baseUrl: isCompute ? '/global/operations' : '/operations', + id: name, + methods: methods }); -}; + + this.name = name; +} + +nodeutil.inherits(Operation, ServiceObject); /** * Get the operation's metadata. For a detailed description of metadata see @@ -125,22 +161,22 @@ Operation.prototype.getMetadata = function(callback) { callback = callback || util.noop; - this.makeReq_('GET', '', null, null, function(err, resp) { + ServiceObject.prototype.getMetadata.call(this, function(err, apiResponse) { // An Operation entity contains a property named `error`. This makes - // `makeReq_` think the operation failed, and will return an ApiError to + // `request` think the operation failed, and will return an ApiError to // this callback. We have to make sure this isn't a false error by seeing if // the response body contains a property that wouldn't exist on a failed API // request (`name`). - var isActualError = err && (!resp || resp.name !== self.name); + var isActualError = err && (!apiResponse || apiResponse.name !== self.name); if (isActualError) { - callback(err, null, resp); + callback(err, null, apiResponse); return; } - self.metadata = resp; + self.metadata = apiResponse; - callback(null, self.metadata, resp); + callback(null, self.metadata, apiResponse); }); }; @@ -221,26 +257,4 @@ Operation.prototype.onComplete = function(options, callback) { checkMetadata(); }; -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Operation.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/operations/' + this.name + path; - - if (this.scope.constructor.name === 'Compute') { - path = '/global' + path; - } - - this.scope.makeReq_(method, path, query, body, callback); -}; - module.exports = Operation; diff --git a/lib/compute/region.js b/lib/compute/region.js index 50178338be3..fb6b90d8319 100644 --- a/lib/compute/region.js +++ b/lib/compute/region.js @@ -22,6 +22,7 @@ var extend = require('extend'); var is = require('is'); +var nodeutil = require('util'); /** * @type {module:compute/address} @@ -36,16 +37,16 @@ var Address = require('./address.js'); var Operation = require('./operation.js'); /** - * @type {module:common/streamrouter} + * @type {module:common/serviceObject} * @private */ -var streamRouter = require('../common/stream-router.js'); +var ServiceObject = require('../common/service-object.js'); /** - * @type {module:common/util} + * @type {module:common/streamrouter} * @private */ -var util = require('../common/util.js'); +var streamRouter = require('../common/stream-router.js'); /*! Developer Documentation * @@ -72,17 +73,66 @@ var util = require('../common/util.js'); * var region = gce.region('us-central1'); */ function Region(compute, name) { - this.compute = compute; + var methods = { + /** + * Check if the region exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the region exists or not. + * + * @example + * region.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a region. + * + * @example + * region.get(function(err, region, apiResponse) { + * // `region` is a Region object. + * }); + */ + get: true, + + /** + * Get the region's metadata. + * + * @resource [Region Resource]{@link https://cloud.google.com/compute/docs/reference/v1/regions} + * @resource [Regions: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/regions/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The region's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * region.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: compute, + baseUrl: '/regions', + id: name, + methods: methods + }); + this.name = name; - this.metadata = {}; } +nodeutil.inherits(Region, ServiceObject); + /** * Get a reference to a Google Compute Engine address in this region. * * @resource [Instances and Networks]{@link https://cloud.google.com/compute/docs/instances-and-network} * - * @param {string} name - Name of the existing address. + * @param {string} name - Name of the address. * @return {module:compute/address} * * @example @@ -128,11 +178,13 @@ Region.prototype.createAddress = function(name, options, callback) { options = {}; } - var body = extend({}, options, { - name: name - }); - - this.makeReq_('POST', '/addresses', null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/addresses', + json: extend({}, options, { + name: name + }) + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -175,7 +227,7 @@ Region.prototype.createAddress = function(name, options, callback) { * @param {object} callback.apiResponse - The full API response. * * @example - * region.getAddresses(function (err, addresses) { + * region.getAddresses(function(err, addresses) { * // `addresses` is an array of `Address` objects. * }); * @@ -225,7 +277,10 @@ Region.prototype.getAddresses = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/addresses', options, null, function(err, resp) { + this.request({ + uri: '/addresses', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -249,37 +304,6 @@ Region.prototype.getAddresses = function(options, callback) { }); }; -/** - * Get the region's metadata. - * - * @resource [Region Resource]{@link https://cloud.google.com/compute/docs/reference/v1/regions} - * @resource [Regions: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/regions/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The region's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * region.getMetadata(function(err, metadata, apiResponse) {}); - */ -Region.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - /** * Get a list of operations for this region. * @@ -358,7 +382,10 @@ Region.prototype.getOperations = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/operations', options, null, function(err, resp) { + this.request({ + uri: '/operations', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -397,23 +424,6 @@ Region.prototype.operation = function(name) { return new Operation(this, name); }; -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Region.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/regions/' + this.name + path; - this.compute.makeReq_(method, path, query, body, callback); -}; - /*! Developer Documentation * * These methods can be used with either a callback or as a readable object diff --git a/lib/compute/snapshot.js b/lib/compute/snapshot.js index 03119618960..9ab5e96b1d8 100644 --- a/lib/compute/snapshot.js +++ b/lib/compute/snapshot.js @@ -20,6 +20,14 @@ 'use strict'; +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @type {module:common/util} * @private @@ -28,7 +36,8 @@ var util = require('../common/util.js'); /*! Developer Documentation * - * @param {module:compute} compute - Compute object this snapshot belongs to. + * @param {module:compute|module:compute/disk} scope - The parent scope this + * snapshot belongs to. If it's a Disk, we expose the `create` methods. * @param {string} name - Snapshot name. */ /** @@ -50,13 +59,104 @@ var util = require('../common/util.js'); * var gce = gcloud.compute(); * * var snapshot = gce.snapshot('snapshot-name'); + * + * //- + * // Or, access through a disk. + * //- + * var disk = gce.zone('us-central1-a').disk('disk-name'); + * var snapshot = disk.snapshot('disk-snapshot-name'); */ -function Snapshot(compute, name) { - this.compute = compute; +function Snapshot(scope, name) { + var isDisk = scope.constructor.name === 'Disk'; + + var methods = { + /** + * Check if the snapshot exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the snapshot exists or not. + * + * @example + * snapshot.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a snapshot if it exists. + * + * If you access this snapshot through a Disk object, this can be used as a + * "get or create" method. Pass an object with `autoCreate` set to `true`. + * Any extra configuration that is normally required for the `create` method + * must be contained within this object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * snapshot.get(function(err, snapshot, apiResponse) { + * // `snapshot` is a Snapshot object. + * }); + */ + get: true, + + /** + * Get the snapshots's metadata. + * + * @resource [Snapshot Resource]{@link https://cloud.google.com/compute/docs/reference/v1/snapshots} + * @resource [Snapshots: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/snapshots/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The snapshot's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * snapshot.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + var config = { + parent: scope, + baseUrl: '/global/snapshots', + id: name, + methods: methods + }; + + if (isDisk) { + config.createMethod = scope.createSnapshot.bind(scope); + + /** + * Create a snapshot. + * + * **This is only available if you accessed this object through + * {module:compute/disk#snapshot}.** + * + * @param {object} config - See {module:compute/disk#createSnapshot}. + * + * @example + * snapshot.create(function(err, snapshot, operation, apiResponse) { + * // `snapshot` is a Snapshot object. + * + * // `operation` is an Operation object that can be used to check the + * // status of the request. + * }); + */ + config.methods.create = true; + } + + ServiceObject.call(this, config); + + this.compute = isDisk ? scope.compute : scope; this.name = name; - this.metadata = {}; } +nodeutil.inherits(Snapshot, ServiceObject); + /** * Delete the snapshot. * @@ -79,7 +179,7 @@ Snapshot.prototype.delete = function(callback) { var compute = this.compute; - this.makeReq_('DELETE', '', null, null, function(err, resp) { + ServiceObject.prototype.delete.call(this, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -92,52 +192,4 @@ Snapshot.prototype.delete = function(callback) { }); }; -/** - * Get the snapshots's metadata. - * - * @resource [Snapshot Resource]{@link https://cloud.google.com/compute/docs/reference/v1/snapshots} - * @resource [Snapshots: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/snapshots/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The snapshot's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * snapshot.getMetadata(function(err, metadata, apiResponse) {}); - */ -Snapshot.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Snapshot.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/global/snapshots/' + this.name + path; - this.compute.makeReq_(method, path, query, body, callback); -}; - module.exports = Snapshot; diff --git a/lib/compute/vm.js b/lib/compute/vm.js index ea387905e70..647b0878e80 100644 --- a/lib/compute/vm.js +++ b/lib/compute/vm.js @@ -23,6 +23,7 @@ var createErrorClass = require('create-error-class'); var extend = require('extend'); var is = require('is'); +var nodeutil = require('util'); /** * @type {module:compute/disk} @@ -30,6 +31,12 @@ var is = require('is'); */ var Disk = require('./disk.js'); +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @type {module:common/util} * @private @@ -74,11 +81,90 @@ var DetachDiskError = createErrorClass('DetachDiskError', function(message) { * var vm = zone.vm('vm-name'); */ function VM(zone, name) { - this.zone = zone; + var methods = { + /** + * Create a virtual machine. + * + * @param {object} config - See {module:compute/zone#createVM}. + * + * @example + * var config = { + * // ... + * }; + * + * vm.create(config, function(err, vm, operation, apiResponse) { + * // `vm` is a VM object. + * + * // `operation` is an Operation object that can be used to check the + * // status of the request. + * }); + */ + create: true, + + /** + * Check if the vm exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the vm exists or not. + * + * @example + * vm.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a virtual machine if it exists. + * + * You may optionally use this to "get or create" an object by providing an + * object with `autoCreate` set to `true`. Any extra configuration that is + * normally required for the `create` method must be contained within this + * object as well. + * + * @param {options=} options - Configuration object. + * @param {boolean} options.autoCreate - Automatically create the object if + * it does not exist. Default: `false` + * + * @example + * vm.get(function(err, vm, apiResponse) { + * // `vm` is a VM object. + * }); + */ + get: true, + + /** + * Get the instance's metadata. + * + * @resource [Instance Resource]{@link https://cloud.google.com/compute/docs/reference/v1/instances} + * @resource [Instance: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/instances/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The instance's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * vm.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: zone, + baseUrl: '/instances', + id: name, + createMethod: zone.createVM.bind(zone), + methods: methods + }); + this.name = name; - this.metadata = {}; + this.zone = zone; } +nodeutil.inherits(VM, ServiceObject); + /** * Attach a disk to the instance. * @@ -130,7 +216,10 @@ VM.prototype.attachDisk = function(disk, options, callback) { options = {}; } - var body = extend({}, options, { + var body = extend({ + // Default the deviceName to the name of the disk, like the Console does. + deviceName: disk.name + }, options, { source: disk.formattedName }); @@ -139,7 +228,11 @@ VM.prototype.attachDisk = function(disk, options, callback) { delete body.readOnly; } - this.makeReq_('POST', '/attachDisk', null, body, callback); + this.request({ + method: 'POST', + uri: '/attachDisk', + json: body + }, callback); }; /** @@ -160,7 +253,10 @@ VM.prototype.attachDisk = function(disk, options, callback) { * }); */ VM.prototype.delete = function(callback) { - this.makeReq_('DELETE', '', null, null, callback || util.noop); + this.request({ + method: 'DELETE', + uri: '' + }, callback || util.noop); }; /** @@ -168,9 +264,9 @@ VM.prototype.delete = function(callback) { * * @resource [Instance: detachDisk API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/instances/detachDisk} * - * @throws {Error} if a {module:compute/disk} is not provided. - * - * @param {module:compute/disk} disk - The disk to detach. + * @param {module:compute/disk|string} deviceName - The device name of the disk + * to detach. If a Disk object is provided, we try to find the device name + * automatically by searching through the attached disks on the instance. * @param {function=} callback - The callback function. * @param {?error} callback.err - An error returned while making this request. * @param {module:compute/operation} callback.operation - An operation object @@ -218,42 +314,13 @@ VM.prototype.detachDisk = function(disk, callback) { return; } - var query = { - deviceName: deviceName - }; - - self.makeReq_('POST', '/detachDisk', query, null, callback || util.noop); - }); -}; - -/** - * Get the instances's metadata. - * - * @resource [Instance Resource]{@link https://cloud.google.com/compute/docs/reference/v1/instances} - * @resource [Instance: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/instances/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The instance's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * vm.getMetadata(function(err, metadata, apiResponse) {}); - */ -VM.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, _, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); + self.request({ + method: 'POST', + uri: '/detachDisk', + qs: { + deviceName: deviceName + } + }, callback || util.noop); }); }; @@ -278,11 +345,14 @@ VM.prototype.getSerialPortOutput = function(port, callback) { port = 1; } - var query = { - port: port + var reqOpts = { + uri: '/serialPort', + qs: { + port: port + } }; - this.makeReq_('GET', '/serialPort', query, null, function(err, _, resp) { + ServiceObject.prototype.request.call(this, reqOpts, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -336,7 +406,10 @@ VM.prototype.getTags = function(callback) { * }); */ VM.prototype.reset = function(callback) { - this.makeReq_('POST', '/reset', null, null, callback || util.noop); + this.request({ + method: 'POST', + uri: '/reset' + }, callback || util.noop); }; /** @@ -369,7 +442,11 @@ VM.prototype.setTags = function(tags, fingerprint, callback) { fingerprint: fingerprint }; - this.makeReq_('POST', '/setTags', null, body, callback || util.noop); + this.request({ + method: 'POST', + uri: '/setTags', + json: body + }, callback || util.noop); }; /** @@ -390,7 +467,10 @@ VM.prototype.setTags = function(tags, fingerprint, callback) { * }); */ VM.prototype.start = function(callback) { - this.makeReq_('POST', '/start', null, null, callback || util.noop); + this.request({ + method: 'POST', + uri: '/start' + }, callback || util.noop); }; /** @@ -411,7 +491,10 @@ VM.prototype.start = function(callback) { * }); */ VM.prototype.stop = function(callback) { - this.makeReq_('POST', '/stop', null, null, callback || util.noop); + this.request({ + method: 'POST', + uri: '/stop' + }, callback || util.noop); }; /** @@ -431,12 +514,10 @@ VM.prototype.stop = function(callback) { * @param {*} body - Request body contents. * @param {function} callback - The callback function. */ -VM.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/instances/' + this.name + path; - +VM.prototype.request = function(reqOpts, callback) { var zone = this.zone; - zone.makeReq_(method, path, query, body, function(err, resp) { + ServiceObject.prototype.request.call(this, reqOpts, function(err, resp) { if (err) { callback(err, null, resp); return; diff --git a/lib/compute/zone.js b/lib/compute/zone.js index f7537aa82ac..2902d977929 100644 --- a/lib/compute/zone.js +++ b/lib/compute/zone.js @@ -25,6 +25,7 @@ var extend = require('extend'); var format = require('string-format-obj'); var gceImages = require('gce-images'); var is = require('is'); +var nodeutil = require('util'); /** * @type {module:compute/disk} @@ -39,16 +40,16 @@ var Disk = require('./disk.js'); var Operation = require('./operation.js'); /** - * @type {module:common/streamrouter} + * @type {module:common/serviceObject} * @private */ -var streamRouter = require('../common/stream-router.js'); +var ServiceObject = require('../common/service-object.js'); /** - * @type {module:common/util} + * @type {module:common/streamrouter} * @private */ -var util = require('../common/util.js'); +var streamRouter = require('../common/stream-router.js'); /** * @type {module:compute/vm} @@ -81,15 +82,65 @@ var VM = require('./vm.js'); * var zone = gce.zone('us-central1-a'); */ function Zone(compute, name) { + var methods = { + /** + * Check if the zone exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the zone exists or not. + * + * @example + * zone.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a zone. + * + * @example + * zone.get(function(err, zone, apiResponse) { + * // `zone` is a Zone object. + * }); + */ + get: true, + + /** + * Get the zone's metadata. + * + * @resource [Zone Resource]{@link https://cloud.google.com/compute/docs/reference/v1/zones} + * @resource [Zones: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/zones/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The zone's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * zone.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: compute, + baseUrl: '/zones', + id: name, + methods: methods + }); + this.compute = compute; this.name = name; - this.metadata = {}; this.gceImages = gceImages({ - authClient: compute.makeAuthenticatedRequest_.authClient + authClient: compute.authClient }); } +nodeutil.inherits(Zone, ServiceObject); + /** * Create a persistent disk in this zone. * @@ -150,7 +201,12 @@ Zone.prototype.createDisk = function(name, config, callback) { return; } - this.makeReq_('POST', '/disks', query, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/disks', + qs: query, + json: body + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -346,7 +402,11 @@ Zone.prototype.createVM = function(name, config, callback) { return; } - this.makeReq_('POST', '/instances', null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/instances', + json: body + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -366,7 +426,7 @@ Zone.prototype.createVM = function(name, config, callback) { * * @resource [Disks Overview]{@link https://cloud.google.com/compute/docs/disks} * - * @param {string} name - Name of the existing disk. + * @param {string} name - Name of the disk. * @return {module:compute/disk} * * @example @@ -453,7 +513,10 @@ Zone.prototype.getDisks = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/disks', options, null, function(err, resp) { + this.request({ + uri: '/disks', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -477,37 +540,6 @@ Zone.prototype.getDisks = function(options, callback) { }); }; -/** - * Get the zone's metadata. - * - * @resource [Zone Resource]{@link https://cloud.google.com/compute/docs/reference/v1/zones} - * @resource [Zones: get API Documentation]{@link https://cloud.google.com/compute/docs/reference/v1/zones/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The zone's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * zone.getMetadata(function(err, metadata, apiResponse) {}); - */ -Zone.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - this.makeReq_('GET', '', null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - /** * Get a list of operations for this zone. * @@ -586,7 +618,10 @@ Zone.prototype.getOperations = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/operations', options, null, function(err, resp) { + this.request({ + uri: '/operations', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -686,7 +721,10 @@ Zone.prototype.getVMs = function(options, callback) { options = options || {}; - this.makeReq_('GET', '/instances', options, null, function(err, resp) { + this.request({ + uri: '/instances', + qs: options + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -719,7 +757,7 @@ Zone.prototype.getVMs = function(options, callback) { * @return {module:compute/operation} * * @example - * var operation = zone.operation('operation-name'); + * var operation = zone.operation('operation-1445532685163-8b137d2a-1822afe7'); */ Zone.prototype.operation = function(name) { return new Operation(this, name); @@ -730,7 +768,7 @@ Zone.prototype.operation = function(name) { * * @resource [Instances and Networks]{@link https://cloud.google.com/compute/docs/instances-and-network} * - * @param {string} name - Name of the existing virtual machine. + * @param {string} name - Name of the virtual machine. * @return {module:compute/vm} * * @example @@ -780,23 +818,6 @@ Zone.prototype.createHttpsServerFirewall_ = function(callback) { }); }; -/** - * Make a new request object from the provided arguments and wrap the callback - * to intercept non-successful responses. - * - * @private - * - * @param {string} method - Action. - * @param {string} path - Request path. - * @param {*} query - Request query object. - * @param {*} body - Request body contents. - * @param {function} callback - The callback function. - */ -Zone.prototype.makeReq_ = function(method, path, query, body, callback) { - path = '/zones/' + this.name + path; - this.compute.makeReq_(method, path, query, body, callback); -}; - /*! Developer Documentation * * These methods can be used with either a callback or as a readable object diff --git a/system-test/compute.js b/system-test/compute.js index f7d73c7a3a1..45196a28d75 100644 --- a/system-test/compute.js +++ b/system-test/compute.js @@ -54,15 +54,12 @@ describe('Compute', function() { }); describe('addresses', function() { - var ADDRESS_NAME; - var address; + var ADDRESS_NAME = generateName(); + var address = region.address(ADDRESS_NAME); before(function(done) { - ADDRESS_NAME = generateName(); - - region.createAddress(ADDRESS_NAME, function(err, address_, operation) { + address.create(function(err, disk, operation) { assert.ifError(err); - address = address_; operation.onComplete(done); }); }); @@ -103,19 +100,16 @@ describe('Compute', function() { }); describe('disks', function() { - var DISK_NAME; - var disk; + var DISK_NAME = generateName(); + var disk = zone.disk(DISK_NAME); before(function(done) { - DISK_NAME = generateName(); - var config = { os: 'ubuntu' }; - zone.createDisk(DISK_NAME, config, function(err, disk_, operation) { + disk.create(config, function(err, disk, operation) { assert.ifError(err); - disk = disk_; operation.onComplete(done); }); }); @@ -155,12 +149,19 @@ describe('Compute', function() { }); it('should take a snapshot', function(done) { - disk.createSnapshot(generateName(), done); + var MAX_TIME_ALLOWED = 90000; + this.timeout(MAX_TIME_ALLOWED); + + disk.snapshot(generateName()).create(function(err, snapshot, operation) { + assert.ifError(err); + operation.onComplete(getOperationOptions(MAX_TIME_ALLOWED), done); + }); }); }); describe('firewalls', function() { - var FIREWALL_NAME; + var FIREWALL_NAME = generateName(); + var firewall = compute.firewall(FIREWALL_NAME); var CONFIG = { protocols: { @@ -185,17 +186,14 @@ describe('Compute', function() { sourceRanges: CONFIG.ranges }; - var firewall; - before(function(done) { - FIREWALL_NAME = generateName(); + var MAX_TIME_ALLOWED = 90000; + this.timeout(MAX_TIME_ALLOWED); - compute.createFirewall( - FIREWALL_NAME, CONFIG, function(err, firewall_, operation) { - assert.ifError(err); - firewall = firewall_; - operation.onComplete(done); - }); + firewall.create(CONFIG, function(err, firewall, operation) { + assert.ifError(err); + operation.onComplete(getOperationOptions(MAX_TIME_ALLOWED), done); + }); }); it('should have opened the correct connections', function(done) { @@ -231,23 +229,18 @@ describe('Compute', function() { }); describe('networks', function() { - var NETWORK_NAME; + var NETWORK_NAME = generateName(); + var network = compute.network(NETWORK_NAME); var CONFIG = { range: '10.240.0.0/16' }; - var network; - before(function(done) { - NETWORK_NAME = generateName(); - - compute.createNetwork( - NETWORK_NAME, CONFIG, function(err, network_, operation) { - assert.ifError(err); - network = network_; - operation.onComplete(done); - }); + network.create(CONFIG, function(err, network, operation) { + assert.ifError(err); + operation.onComplete(done); + }); }); it('should have opened the correct range', function(done) { @@ -398,20 +391,17 @@ describe('Compute', function() { }); describe('vms', function() { - var VM_NAME; - var vm; + var VM_NAME = generateName(); + var vm = zone.vm(VM_NAME); before(function(done) { - VM_NAME = generateName(); - var config = { os: 'ubuntu', http: true }; - zone.createVM(VM_NAME, config, function(err, vm_, operation) { + vm.create(config, function(err, vm, operation) { assert.ifError(err); - vm = vm_; operation.onComplete(done); }); }); @@ -430,10 +420,7 @@ describe('Compute', function() { return; } - operation.onComplete({ - maxAttempts: MAX_TIME_ALLOWED / 10000, - interval: 10000 - }, done); + operation.onComplete(getOperationOptions(MAX_TIME_ALLOWED), done); }); }); @@ -512,32 +499,32 @@ describe('Compute', function() { tags.push(newTagName); - vm.setTags(tags, fingerprint, function(err, operation) { + vm.setTags(tags, fingerprint, execAfterOperationComplete(function(err) { assert.ifError(err); - operation.onComplete(function(err) { + vm.getTags(function(err, tags) { assert.ifError(err); - - vm.getTags(function(err, tags) { - assert.ifError(err); - assert(tags.indexOf(newTagName) > -1); - done(); - }); + assert(tags.indexOf(newTagName) > -1); + done(); }); - }); + })); }); }); it('should reset', function(done) { - vm.reset(done); + vm.reset(execAfterOperationComplete(done)); }); it('should start', function(done) { - vm.start(done); + vm.start(execAfterOperationComplete(done)); }); it('should stop', function(done) { - vm.stop(done); + var MAX_TIME_ALLOWED = 90000 * 2; + this.timeout(MAX_TIME_ALLOWED); + + var options = getOperationOptions(MAX_TIME_ALLOWED); + vm.stop(execAfterOperationComplete(options, done)); }); }); @@ -637,6 +624,15 @@ describe('Compute', function() { }); } + function getOperationOptions(maxTimeAllowed) { + var interval = 10000; + + return { + maxAttempts: maxTimeAllowed / interval, + interval: interval + }; + } + function execAfterOperationComplete(options, callback) { callback = callback || options; diff --git a/test/compute/address.js b/test/compute/address.js index 17d6bbb8d4f..3775b314661 100644 --- a/test/compute/address.js +++ b/test/compute/address.js @@ -17,13 +17,43 @@ 'use strict'; var assert = require('assert'); -var Address = require('../../lib/compute/address'); +var extend = require('extend'); +var mockery = require('mockery'); +var nodeutil = require('util'); + +var ServiceObject = require('../../lib/common/service-object.js'); +var util = require('../../lib/common/util.js'); + +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); describe('Address', function() { + var Address; var address; var ADDRESS_NAME = 'us-central1'; - var REGION = {}; + var REGION = { + createAddress: util.noop + }; + + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Address = require('../../lib/compute/address.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); beforeEach(function() { address = new Address(REGION, ADDRESS_NAME); @@ -38,19 +68,38 @@ describe('Address', function() { assert.strictEqual(address.name, ADDRESS_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof address.metadata, 'object'); - assert.strictEqual(Object.keys(address.metadata).length, 0); + it('should inherit from ServiceObject', function(done) { + var regionInstance = extend({}, REGION, { + createAddress: { + bind: function(context) { + assert.strictEqual(context, regionInstance); + done(); + } + } + }); + + var address = new Address(regionInstance, ADDRESS_NAME); + assert(address instanceof ServiceObject); + + var calledWith = address.calledWith_[0]; + + assert.strictEqual(calledWith.parent, regionInstance); + assert.strictEqual(calledWith.baseUrl, '/addresses'); + assert.strictEqual(calledWith.id, ADDRESS_NAME); + assert.deepEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true + }); }); }); describe('delete', function() { it('should make the correct API request', function(done) { - address.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + address.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'DELETE'); + assert.strictEqual(reqOpts.uri, ''); done(); }; @@ -62,7 +111,7 @@ describe('Address', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - address.makeReq_ = function(method, path, query, body, callback) { + address.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -89,7 +138,7 @@ describe('Address', function() { }; beforeEach(function() { - address.makeReq_ = function(method, path, query, body, callback) { + address.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -117,105 +166,4 @@ describe('Address', function() { }); }); }); - - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - address.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - address.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - address.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - address.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - address.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - address.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - address.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(address.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - address.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - address.getMetadata(); - }); - }); - }); - }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/addresses/' + address.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - address.region.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - address.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/compute/disk.js b/test/compute/disk.js index 27486da5361..49a21ddc217 100644 --- a/test/compute/disk.js +++ b/test/compute/disk.js @@ -17,16 +17,39 @@ 'use strict'; var assert = require('assert'); +var extend = require('extend'); var format = require('string-format-obj'); +var mockery = require('mockery'); +var nodeutil = require('util'); -var Disk = require('../../lib/compute/disk'); -var util = require('../../lib/common/util'); +var ServiceObject = require('../../lib/common/service-object.js'); +var util = require('../../lib/common/util.js'); + +function FakeSnapshot() { + this.calledWith_ = [].slice.call(arguments); +} + +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); describe('Disk', function() { + var Disk; var disk; - var COMPUTE = { projectId: 'project-id' }; - var ZONE = { compute: COMPUTE, name: 'us-central1-a' }; + var COMPUTE = { + projectId: 'project-id' + }; + + var ZONE = { + compute: COMPUTE, + name: 'us-central1-a', + createDisk: util.noop + }; + var DISK_NAME = 'disk-name'; var DISK_FULL_NAME = format('projects/{pId}/zones/{zName}/disks/{dName}', { pId: COMPUTE.projectId, @@ -34,6 +57,22 @@ describe('Disk', function() { dName: DISK_NAME }); + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.registerMock('./snapshot.js', FakeSnapshot); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Disk = require('../../lib/compute/disk.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); + beforeEach(function() { disk = new Disk(ZONE, DISK_NAME); }); @@ -47,11 +86,6 @@ describe('Disk', function() { assert.strictEqual(disk.name, DISK_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof disk.metadata, 'object'); - assert.strictEqual(Object.keys(disk.metadata).length, 0); - }); - it('should format the disk name', function() { var formatName_ = Disk.formatName_; var formattedName = 'projects/a/zones/b/disks/c'; @@ -68,6 +102,32 @@ describe('Disk', function() { var disk = new Disk(ZONE, DISK_NAME); assert(disk.formattedName, formattedName); }); + + it('should inherit from ServiceObject', function(done) { + var zoneInstance = extend({}, ZONE, { + createDisk: { + bind: function(context) { + assert.strictEqual(context, zoneInstance); + done(); + } + } + }); + + var disk = new Disk(zoneInstance, DISK_NAME); + assert(disk instanceof ServiceObject); + + var calledWith = disk.calledWith_[0]; + + assert.strictEqual(calledWith.parent, zoneInstance); + assert.strictEqual(calledWith.baseUrl, '/disks'); + assert.strictEqual(calledWith.id, DISK_NAME); + assert.deepEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true + }); + }); }); describe('formatName_', function() { @@ -79,11 +139,10 @@ describe('Disk', function() { describe('createSnapshot', function() { it('should make the correct API request', function(done) { - disk.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/createSnapshot'); - assert.strictEqual(query, null); - assert.deepEqual(body, { name: 'test', a: 'b' }); + disk.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/createSnapshot'); + assert.deepEqual(reqOpts.json, { name: 'test', a: 'b' }); done(); }; @@ -95,7 +154,7 @@ describe('Disk', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - disk.makeReq_ = function(method, path, query, body, callback) { + disk.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -123,7 +182,7 @@ describe('Disk', function() { }; beforeEach(function() { - disk.makeReq_ = function(method, path, query, body, callback) { + disk.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -132,7 +191,7 @@ describe('Disk', function() { var snapshot = {}; var operation = {}; - disk.zone.compute.snapshot = function(name) { + disk.snapshot = function(name) { assert.strictEqual(name, 'test'); return snapshot; }; @@ -163,16 +222,13 @@ describe('Disk', function() { }); describe('delete', function() { - it('should make the correct API request', function(done) { - disk.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + it('should call ServiceObject.delete', function(done) { + FakeServiceObject.prototype.delete = function() { + assert.strictEqual(this, disk); done(); }; - disk.delete(assert.ifError); + disk.delete(); }); describe('error', function() { @@ -180,7 +236,7 @@ describe('Disk', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - disk.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(error, apiResponse); }; }); @@ -207,7 +263,7 @@ describe('Disk', function() { }; beforeEach(function() { - disk.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(null, apiResponse); }; }); @@ -237,104 +293,14 @@ describe('Disk', function() { }); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - disk.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - disk.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - disk.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - disk.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - disk.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - disk.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - disk.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(disk.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - disk.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - disk.getMetadata(); - }); - }); - }); - }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/disks/' + disk.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - disk.zone.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; + describe('snapshot', function() { + var NAME = 'snapshot-name'; - disk.makeReq_(method, path, query, body, done); + it('should return a Snapshot object', function() { + var snapshot = disk.snapshot(NAME); + assert(snapshot instanceof FakeSnapshot); + assert.strictEqual(snapshot.calledWith_[0], disk); + assert.strictEqual(snapshot.calledWith_[1], NAME); }); }); }); diff --git a/test/compute/firewall.js b/test/compute/firewall.js index f6a8b4c776d..af5e98bcd9a 100644 --- a/test/compute/firewall.js +++ b/test/compute/firewall.js @@ -17,15 +17,46 @@ 'use strict'; var assert = require('assert'); -var Firewall = require('../../lib/compute/firewall'); +var extend = require('extend'); +var mockery = require('mockery'); +var nodeutil = require('util'); + +var ServiceObject = require('../../lib/common/service-object.js'); +var util = require('../../lib/common/util.js'); + +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); describe('Firewall', function() { + var Firewall; var firewall; - var COMPUTE = { projectId: 'project-id' }; + var COMPUTE = { + projectId: 'project-id', + createFirewall: util.noop + }; var FIREWALL_NAME = 'tcp-3000'; var FIREWALL_NETWORK = 'global/networks/default'; + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Firewall = require('../../lib/compute/firewall.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); + beforeEach(function() { firewall = new Firewall(COMPUTE, FIREWALL_NAME); }); @@ -39,22 +70,45 @@ describe('Firewall', function() { assert.strictEqual(firewall.name, FIREWALL_NAME); }); - it('should create default metadata', function() { + it('should default to the global network', function() { assert.deepEqual(firewall.metadata, { network: FIREWALL_NETWORK }); }); + + it('should inherit from ServiceObject', function(done) { + var computeInstance = extend({}, COMPUTE, { + createFirewall: { + bind: function(context) { + assert.strictEqual(context, computeInstance); + done(); + } + } + }); + + var firewall = new Firewall(computeInstance, FIREWALL_NAME); + assert(firewall instanceof ServiceObject); + + var calledWith = firewall.calledWith_[0]; + + assert.strictEqual(calledWith.parent, computeInstance); + assert.strictEqual(calledWith.baseUrl, '/global/firewalls'); + assert.strictEqual(calledWith.id, FIREWALL_NAME); + assert.deepEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true + }); + }); }); describe('delete', function() { - it('should make the correct API request', function(done) { - firewall.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + it('should call ServiceObject.delete', function(done) { + FakeServiceObject.prototype.delete = function() { + assert.strictEqual(this, firewall); done(); }; - firewall.delete(assert.ifError); + firewall.delete(); }); describe('error', function() { @@ -62,7 +116,7 @@ describe('Firewall', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - firewall.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(error, apiResponse); }; }); @@ -89,7 +143,7 @@ describe('Firewall', function() { }; beforeEach(function() { - firewall.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(null, apiResponse); }; }); @@ -105,6 +159,7 @@ describe('Firewall', function() { firewall.delete(function(err, operation_, apiResponse_) { assert.ifError(err); assert.strictEqual(operation_, operation); + assert.strictEqual(operation_.metadata, apiResponse); assert.strictEqual(apiResponse_, apiResponse); done(); }); @@ -118,96 +173,23 @@ describe('Firewall', function() { }); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - firewall.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - firewall.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - firewall.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - firewall.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - firewall.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - firewall.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - firewall.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(firewall.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - firewall.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - firewall.getMetadata(); - }); - }); - }); - }); - describe('setMetadata', function() { it('should make the correct API request', function(done) { - firewall.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'PATCH'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.deepEqual(body, { - name: FIREWALL_NAME, - network: FIREWALL_NETWORK, - a: 'b' + var metadata = {}; + + firewall.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'PATCH'); + assert.strictEqual(reqOpts.uri, ''); + assert.strictEqual(reqOpts.json, metadata); + assert.deepEqual(metadata, { + name: firewall.name, + network: FIREWALL_NETWORK }); done(); }; - firewall.setMetadata({ a: 'b' }, assert.ifError); + firewall.setMetadata(metadata, assert.ifError); }); describe('error', function() { @@ -215,7 +197,7 @@ describe('Firewall', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - firewall.makeReq_ = function(method, path, query, body, callback) { + firewall.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -236,7 +218,7 @@ describe('Firewall', function() { }; beforeEach(function() { - firewall.makeReq_ = function(method, path, query, body, callback) { + firewall.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -266,31 +248,4 @@ describe('Firewall', function() { }); }); }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/global/firewalls/' + firewall.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - firewall.compute.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - firewall.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/compute/index.js b/test/compute/index.js index d5bf538a607..defcb623ba5 100644 --- a/test/compute/index.js +++ b/test/compute/index.js @@ -16,12 +16,15 @@ 'use strict'; +var arrify = require('arrify'); var assert = require('assert'); -var mockery = require('mockery'); var extend = require('extend'); -var arrify = require('arrify'); +var mockery = require('mockery'); +var nodeutil = require('util'); +var Service = require('../../lib/common/service.js'); var util = require('../../lib/common/util.js'); + var slice = Array.prototype.slice; var fakeUtil = extend({}, util, { @@ -78,6 +81,13 @@ function FakeZone() { this.vm = function() { return {}; }; } +function FakeService() { + this.calledWith_ = arguments; + Service.apply(this, arguments); +} + +nodeutil.inherits(FakeService, Service); + describe('Compute', function() { var Compute; var compute; @@ -85,8 +95,9 @@ describe('Compute', function() { var PROJECT_ID = 'project-id'; before(function() { - mockery.registerMock('../common/util.js', fakeUtil); + mockery.registerMock('../common/service.js', FakeService); mockery.registerMock('../common/stream-router.js', fakeStreamRouter); + mockery.registerMock('../common/util.js', fakeUtil); mockery.registerMock('./firewall.js', FakeFirewall); mockery.registerMock('./network.js', FakeNetwork); mockery.registerMock('./operation.js', FakeOperation); @@ -144,24 +155,16 @@ describe('Compute', function() { fakeUtil.normalizeArguments = normalizeArguments; }); - it('should create a makeAuthenticatedRequest method', function(done) { - fakeUtil.makeAuthenticatedRequestFactory = function(options_) { - assert.deepEqual(options_, { - credentials: options.credentials, - email: options.email, - keyFile: options.keyFilename, - scopes: ['https://www.googleapis.com/auth/compute'] - }); - fakeUtil.makeAuthenticatedRequestFactory = util.noop; - return done; - }; + it('should inherit from Service', function() { + assert(compute instanceof Service); - var compute = new Compute(options); - compute.makeAuthenticatedRequest_(); - }); + var calledWith = compute.calledWith_[0]; - it('should localize the project id', function() { - assert.strictEqual(compute.projectId, PROJECT_ID); + var baseUrl = 'https://www.googleapis.com/compute/v1'; + assert.strictEqual(calledWith.baseUrl, baseUrl); + assert.deepEqual(calledWith.scopes, [ + 'https://www.googleapis.com/auth/compute' + ]); }); }); @@ -192,14 +195,14 @@ describe('Compute', function() { } }; - compute.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.allowed, [ + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.allowed, [ { IPProtocol: 'http', ports: [8000] }, { IPProtocol: 'https', ports: [8080, 9000] }, { IPProtocol: 'ssh', ports: [22] }, { IPProtocol: 'ftp' } ]); - assert.strictEqual(body.protocols, undefined); + assert.strictEqual(reqOpts.json.protocols, undefined); done(); }; @@ -213,9 +216,9 @@ describe('Compute', function() { ranges: '0.0.0.0/0' // non-array to test that it's arrified. }; - compute.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.sourceRanges, [options.ranges]); - assert.strictEqual(body.ranges, undefined); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.sourceRanges, [options.ranges]); + assert.strictEqual(reqOpts.json.ranges, undefined); done(); }; @@ -229,9 +232,9 @@ describe('Compute', function() { tags: 'tag' // non-array to test that it's arrified. }; - compute.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.sourceTags, [options.tags]); - assert.strictEqual(body.tags, undefined); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.sourceTags, [options.tags]); + assert.strictEqual(reqOpts.json.tags, undefined); done(); }; @@ -242,11 +245,10 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var name = 'new-firewall-name'; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/global/firewalls'); - assert.strictEqual(query, null); - assert.deepEqual(body, { name: name }); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/global/firewalls'); + assert.deepEqual(reqOpts.json, { name: name }); done(); }; @@ -258,7 +260,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -280,7 +282,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -319,9 +321,9 @@ describe('Compute', function() { range: '10.240.0.0/16' }; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.IPv4Range, options.range); - assert.strictEqual(body.range, undefined); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.IPv4Range, options.range); + assert.strictEqual(reqOpts.json.range, undefined); done(); }; @@ -335,9 +337,9 @@ describe('Compute', function() { gateway: '10.1.1.1' }; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.gatewayIPv4, options.gateway); - assert.strictEqual(body.gateway, undefined); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.gatewayIPv4, options.gateway); + assert.strictEqual(reqOpts.json.gateway, undefined); done(); }; @@ -348,11 +350,10 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var name = 'new-network'; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/global/networks'); - assert.strictEqual(query, null); - assert.deepEqual(body, { name: name }); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/global/networks'); + assert.deepEqual(reqOpts.json, { name: name }); done(); }; @@ -364,7 +365,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -387,7 +388,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -431,8 +432,8 @@ describe('Compute', function() { describe('getAddresses', function() { it('should accept only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -442,11 +443,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/aggregated/addresses'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/aggregated/addresses'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -458,7 +457,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -489,7 +488,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -519,7 +518,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -540,8 +539,8 @@ describe('Compute', function() { describe('getDisks', function() { it('should accept only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -551,11 +550,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/aggregated/disks'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/aggregated/disks'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -567,7 +564,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -598,7 +595,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -628,7 +625,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -649,8 +646,8 @@ describe('Compute', function() { describe('getFirewalls', function() { it('should accept only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -660,11 +657,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/global/firewalls'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/global/firewalls'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -676,7 +671,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -700,7 +695,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -723,7 +718,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -744,8 +739,8 @@ describe('Compute', function() { describe('getNetworks', function() { it('should work with only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -755,11 +750,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/global/networks'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/global/networks'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -771,7 +764,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -795,7 +788,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -818,7 +811,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -839,8 +832,8 @@ describe('Compute', function() { describe('getOperations', function() { it('should work with only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -850,11 +843,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/global/operations'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/global/operations'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -866,7 +857,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -890,7 +881,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -913,7 +904,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -934,8 +925,8 @@ describe('Compute', function() { describe('getRegions', function() { it('should work with only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -945,11 +936,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/regions'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/regions'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -961,7 +950,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -985,7 +974,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -1008,7 +997,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -1029,8 +1018,8 @@ describe('Compute', function() { describe('getSnapshots', function() { it('should work with only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -1040,11 +1029,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/global/snapshots'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/global/snapshots'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -1056,7 +1043,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -1080,7 +1067,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -1103,7 +1090,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -1124,8 +1111,8 @@ describe('Compute', function() { describe('getVMs', function() { it('should work with only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -1135,11 +1122,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/aggregated/instances'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/aggregated/instances'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -1151,7 +1136,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -1182,7 +1167,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -1212,7 +1197,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -1233,8 +1218,8 @@ describe('Compute', function() { describe('getZones', function() { it('should work with only a callback', function(done) { - compute.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + compute.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -1244,11 +1229,9 @@ describe('Compute', function() { it('should make the correct API request', function(done) { var options = {}; - compute.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/zones'); - assert.strictEqual(query, options); - assert.strictEqual(body, null); + compute.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/zones'); + assert.strictEqual(reqOpts.qs, options); done(); }; @@ -1260,7 +1243,7 @@ describe('Compute', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -1284,7 +1267,7 @@ describe('Compute', function() { }; beforeEach(function() { - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -1307,7 +1290,7 @@ describe('Compute', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - compute.makeReq_ = function(method, path, query, body, callback) { + compute.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -1380,27 +1363,4 @@ describe('Compute', function() { assert.strictEqual(zone.calledWith_[1], NAME); }); }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var method = 'POST'; - var path = '/'; - var query = 'query'; - var body = 'body'; - - compute.makeAuthenticatedRequest_ = function(reqOpts, callback) { - assert.equal(reqOpts.method, method); - assert.equal(reqOpts.qs, query); - - var baseUri = 'https://www.googleapis.com/compute/v1/'; - assert.equal(reqOpts.uri, baseUri + 'projects/' + PROJECT_ID + path); - - assert.equal(reqOpts.json, body); - - callback(); - }; - - compute.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/compute/network.js b/test/compute/network.js index 893f46fb344..f4d3334001c 100644 --- a/test/compute/network.js +++ b/test/compute/network.js @@ -19,19 +19,48 @@ var assert = require('assert'); var extend = require('extend'); var format = require('string-format-obj'); +var mockery = require('mockery'); +var nodeutil = require('util'); -var Network = require('../../lib/compute/network.js'); +var ServiceObject = require('../../lib/common/service-object.js'); +var util = require('../../lib/common/util.js'); + +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); describe('Network', function() { + var Network; var network; - var COMPUTE = { projectId: 'project-id' }; + var COMPUTE = { + projectId: 'project-id', + createNetwork: util.noop + }; var NETWORK_NAME = 'network-name'; var NETWORK_FULL_NAME = format('projects/{pId}/global/networks/{name}', { pId: COMPUTE.projectId, name: NETWORK_NAME }); + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Network = require('../../lib/compute/network.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); + beforeEach(function() { network = new Network(COMPUTE, NETWORK_NAME); }); @@ -45,11 +74,6 @@ describe('Network', function() { assert.strictEqual(network.name, NETWORK_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof network.metadata, 'object'); - assert.strictEqual(Object.keys(network.metadata).length, 0); - }); - it('should format the network name', function() { var formatName_ = Network.formatName_; var formattedName = 'projects/a/global/networks/b'; @@ -66,6 +90,32 @@ describe('Network', function() { var network = new Network(COMPUTE, NETWORK_NAME); assert(network.formattedName, formattedName); }); + + it('should inherit from ServiceObject', function(done) { + var computeInstance = extend({}, COMPUTE, { + createNetwork: { + bind: function(context) { + assert.strictEqual(context, computeInstance); + done(); + } + } + }); + + var network = new Network(computeInstance, NETWORK_NAME); + assert(network instanceof ServiceObject); + + var calledWith = network.calledWith_[0]; + + assert.strictEqual(calledWith.parent, computeInstance); + assert.strictEqual(calledWith.baseUrl, '/global/networks'); + assert.strictEqual(calledWith.id, NETWORK_NAME); + assert.deepEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true + }); + }); }); describe('formatName_', function() { @@ -94,16 +144,13 @@ describe('Network', function() { }); describe('delete', function() { - it('should make the correct API request', function(done) { - network.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + it('should call ServiceObject.delete', function(done) { + FakeServiceObject.prototype.delete = function() { + assert.strictEqual(this, network); done(); }; - network.delete(assert.ifError); + network.delete(); }); describe('error', function() { @@ -111,7 +158,7 @@ describe('Network', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - network.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(error, apiResponse); }; }); @@ -138,7 +185,7 @@ describe('Network', function() { }; beforeEach(function() { - network.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(null, apiResponse); }; }); @@ -230,105 +277,4 @@ describe('Network', function() { assert.strictEqual(network.getFirewalls(), resultOfGetFirewalls); }); }); - - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - network.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - network.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - network.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - network.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - network.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - network.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - network.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(network.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - network.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - network.getMetadata(); - }); - }); - }); - }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/global/networks/' + network.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - network.compute.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - network.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/compute/operation.js b/test/compute/operation.js index 54ad6935eb2..02312256349 100644 --- a/test/compute/operation.js +++ b/test/compute/operation.js @@ -17,111 +17,86 @@ 'use strict'; var assert = require('assert'); +var mockery = require('mockery'); +var nodeutil = require('util'); -var Operation = require('../../lib/compute/operation.js'); +var ServiceObject = require('../../lib/common/service-object.js'); var util = require('../../lib/common/util.js'); +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); + describe('Operation', function() { + var Operation; + var operation; + var SCOPE = {}; var OPERATION_NAME = 'operation-name'; - var operation; + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Operation = require('../../lib/compute/operation.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); beforeEach(function() { operation = new Operation(SCOPE, OPERATION_NAME); }); describe('instantiation', function() { - it('should localize the scope', function() { - assert.strictEqual(operation.scope, SCOPE); - }); - it('should localize the name', function() { assert.strictEqual(operation.name, OPERATION_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof operation.metadata, 'object'); - assert.strictEqual(Object.keys(operation.metadata).length, 0); - }); - }); + it('should inherit from ServiceObject', function() { + var operation = new Operation(SCOPE, OPERATION_NAME); + assert(operation instanceof ServiceObject); - describe('delete', function() { - it('should make the correct API request', function(done) { - operation.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + var calledWith = operation.calledWith_[0]; - done(); - }; - - operation.delete(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = {}; - - beforeEach(function() { - operation.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error & API resp', function(done) { - operation.delete(function(err, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - operation.delete(); - }); + assert.strictEqual(calledWith.parent, SCOPE); + assert.strictEqual(calledWith.baseUrl, '/operations'); + assert.strictEqual(calledWith.id, OPERATION_NAME); + assert.deepEqual(calledWith.methods, { + delete: true, + exists: true, + get: true }); }); - describe('success', function() { - var apiResponse = {}; + it('should give the right baseUrl for a global Operation', function() { + var operation = new Operation({ + constructor: { + name: 'Compute' + } + }, OPERATION_NAME); - beforeEach(function() { - operation.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should execute callback with error & API resp', function(done) { - operation.delete(function(err, apiResponse_) { - assert.ifError(err); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - operation.delete(); - }); - }); + var calledWith = operation.calledWith_[0]; + assert.strictEqual(calledWith.baseUrl, '/global/operations'); }); }); describe('getMetadata', function() { - it('should make the correct API request', function(done) { - operation.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - + it('should call ServiceObject.delete', function(done) { + FakeServiceObject.prototype.getMetadata = function() { + assert.strictEqual(this, operation); done(); }; - operation.getMetadata(assert.ifError); + operation.getMetadata(); }); describe('error', function() { @@ -129,7 +104,7 @@ describe('Operation', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - operation.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.getMetadata = function(callback) { callback(error, apiResponse); }; }); @@ -142,7 +117,7 @@ describe('Operation', function() { } }; - operation.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.getMetadata = function(callback) { callback(apiResponse.error, apiResponse); }; @@ -174,7 +149,7 @@ describe('Operation', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - operation.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.getMetadata = function(callback) { callback(null, apiResponse); }; }); @@ -337,45 +312,4 @@ describe('Operation', function() { }); }); }); - - describe('makeReq_', function() { - it('should make the correct request to Scope', function(done) { - var expectedPathPrefix = '/operations/' + operation.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - operation.scope.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - operation.makeReq_(method, path, query, body, done); - }); - - it('should prefix the path with /global if Compute', function(done) { - var expectedPathPrefix = '/global/operations/' + operation.name; - - function Compute() {} - operation.scope = new Compute(); - - operation.scope.makeReq_ = function(method, path) { - assert.strictEqual(path, expectedPathPrefix + '/test'); - done(); - }; - - operation.makeReq_(null, '/test'); - }); - }); }); diff --git a/test/compute/region.js b/test/compute/region.js index f52373d5bb7..4af69e41f42 100644 --- a/test/compute/region.js +++ b/test/compute/region.js @@ -20,6 +20,9 @@ var arrify = require('arrify'); var assert = require('assert'); var extend = require('extend'); var mockery = require('mockery'); +var nodeutil = require('util'); + +var ServiceObject = require('../../lib/common/service-object.js'); function FakeAddress() { this.calledWith_ = [].slice.call(arguments); @@ -29,6 +32,13 @@ function FakeOperation() { this.calledWith_ = [].slice.call(arguments); } +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); + var extended = false; var fakeStreamRouter = { extend: function(Class, methods) { @@ -53,6 +63,7 @@ describe('Region', function() { var REGION_NAME = 'us-central1'; before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); mockery.registerMock('../common/stream-router.js', fakeStreamRouter); mockery.registerMock('./address.js', FakeAddress); mockery.registerMock('./operation.js', FakeOperation); @@ -79,17 +90,23 @@ describe('Region', function() { assert(extended); // See `fakeStreamRouter.extend` }); - it('should localize the compute instance', function() { - assert.strictEqual(region.compute, COMPUTE); - }); - it('should localize the name', function() { assert.strictEqual(region.name, REGION_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof region.metadata, 'object'); - assert.strictEqual(Object.keys(region.metadata).length, 0); + it('should inherit from ServiceObject', function() { + assert(region instanceof ServiceObject); + + var calledWith = region.calledWith_[0]; + + assert.strictEqual(calledWith.parent, COMPUTE); + assert.strictEqual(calledWith.baseUrl, '/regions'); + assert.strictEqual(calledWith.id, REGION_NAME); + assert.deepEqual(calledWith.methods, { + exists: true, + get: true, + getMetadata: true + }); }); }); @@ -112,8 +129,8 @@ describe('Region', function() { it('should not require any options', function(done) { var expectedBody = { name: NAME }; - region.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body, expectedBody); + region.request = function(reqOpts) { + assert.deepEqual(reqOpts.json, expectedBody); done(); }; @@ -121,11 +138,10 @@ describe('Region', function() { }); it('should make the correct API request', function(done) { - region.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/addresses'); - assert.strictEqual(query, null); - assert.deepEqual(body, EXPECTED_BODY); + region.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/addresses'); + assert.deepEqual(reqOpts.json, EXPECTED_BODY); done(); }; @@ -138,7 +154,7 @@ describe('Region', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -158,7 +174,7 @@ describe('Region', function() { var apiResponse = { name: 'operation-name' }; beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -194,8 +210,8 @@ describe('Region', function() { describe('getAddresses', function() { it('should accept only a callback', function(done) { - region.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + region.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -205,11 +221,9 @@ describe('Region', function() { it('should make the correct API request', function(done) { var query = { a: 'b', c: 'd' }; - region.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/addresses'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + region.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/addresses'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -222,7 +236,7 @@ describe('Region', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -246,7 +260,7 @@ describe('Region', function() { }; beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -260,7 +274,7 @@ describe('Region', function() { pageToken: nextPageToken }; - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -295,84 +309,10 @@ describe('Region', function() { }); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - region.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - region.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - region.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - region.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - region.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(region.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - region.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - region.getMetadata(); - }); - }); - }); - }); - describe('getOperations', function() { it('should accept only a callback', function(done) { - region.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + region.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -382,11 +322,9 @@ describe('Region', function() { it('should make the correct API request', function(done) { var query = { a: 'b', c: 'd' }; - region.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/operations'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + region.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/operations'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -399,7 +337,7 @@ describe('Region', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -423,7 +361,7 @@ describe('Region', function() { }; beforeEach(function() { - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -437,7 +375,7 @@ describe('Region', function() { pageToken: nextPageToken }; - region.makeReq_ = function(method, path, query, body, callback) { + region.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -482,31 +420,4 @@ describe('Region', function() { assert.strictEqual(operation.calledWith_[1], NAME); }); }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/regions/' + region.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - region.compute.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - region.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/compute/snapshot.js b/test/compute/snapshot.js index 6dc6dbb9ecc..55016042b8f 100644 --- a/test/compute/snapshot.js +++ b/test/compute/snapshot.js @@ -17,13 +17,39 @@ 'use strict'; var assert = require('assert'); -var Snapshot = require('../../lib/compute/snapshot.js'); +var mockery = require('mockery'); +var nodeutil = require('util'); + +var ServiceObject = require('../../lib/common/service-object.js'); + +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); describe('Snapshot', function() { + var Snapshot; + var snapshot; + var COMPUTE = {}; var SNAPSHOT_NAME = 'snapshot-name'; - var snapshot; + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Snapshot = require('../../lib/compute/snapshot.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); beforeEach(function() { snapshot = new Snapshot(COMPUTE, SNAPSHOT_NAME); @@ -38,24 +64,48 @@ describe('Snapshot', function() { assert.strictEqual(snapshot.name, SNAPSHOT_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof snapshot.metadata, 'object'); - assert.strictEqual(Object.keys(snapshot.metadata).length, 0); + it('should inherit from ServiceObject', function() { + var calledWith = snapshot.calledWith_[0]; + + assert.strictEqual(calledWith.parent, COMPUTE); + assert.strictEqual(calledWith.baseUrl, '/global/snapshots'); + assert.strictEqual(calledWith.id, SNAPSHOT_NAME); + assert.deepEqual(calledWith.methods, { + exists: true, + get: true, + getMetadata: true + }); + }); + + it('should allow creating for a Disk object snapshot', function(done) { + var scope = { + constructor: { + name: 'Disk' + }, + createSnapshot: function() { + assert.strictEqual(this, scope); + done(); + } + }; + + var snapshot = new Snapshot(scope, SNAPSHOT_NAME); + assert(snapshot instanceof ServiceObject); + + var calledWith = snapshot.calledWith_[0]; + assert.strictEqual(calledWith.methods.create, true); + + calledWith.createMethod(); // (scope.createSnapshot) }); }); describe('delete', function() { - it('should make the correct API request', function(done) { - snapshot.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - + it('should call ServiceObject.delete', function(done) { + FakeServiceObject.prototype.delete = function() { + assert.strictEqual(this, snapshot); done(); }; - snapshot.delete(assert.ifError); + snapshot.delete(); }); describe('error', function() { @@ -63,7 +113,7 @@ describe('Snapshot', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - snapshot.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(error, apiResponse); }; }); @@ -88,7 +138,7 @@ describe('Snapshot', function() { var apiResponse = { name: 'operation-name' }; beforeEach(function() { - snapshot.makeReq_ = function(method, path, query, body, callback) { + FakeServiceObject.prototype.delete = function(callback) { callback(null, apiResponse); }; }); @@ -119,105 +169,4 @@ describe('Snapshot', function() { }); }); }); - - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - snapshot.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - snapshot.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - snapshot.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - snapshot.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - snapshot.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - snapshot.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - snapshot.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(snapshot.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - snapshot.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - snapshot.getMetadata(); - }); - }); - }); - }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/global/snapshots/' + snapshot.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - snapshot.compute.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - snapshot.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/compute/vm.js b/test/compute/vm.js index 2c1fdf1fff7..72c77c48edd 100644 --- a/test/compute/vm.js +++ b/test/compute/vm.js @@ -18,23 +18,54 @@ var assert = require('assert'); var extend = require('extend'); -var format = require('string-format-obj'); +var mockery = require('mockery'); +var nodeutil = require('util'); -var Disk = require('../../lib/compute/disk.js'); var util = require('../../lib/common/util.js'); -var VM = require('../../lib/compute/vm.js'); +var ServiceObject = require('../../lib/common/service-object.js'); + +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); describe('VM', function() { + var VM; var vm; + var Disk; + var DISK; + var COMPUTE = { projectId: 'project-id' }; - var ZONE = { compute: COMPUTE, name: 'us-central1-a' }; + var ZONE = { + compute: COMPUTE, + name: 'us-central1-a', + createDisk: util.noop, + createVM: util.noop + }; var VM_NAME = 'vm-name'; - var DISK = new Disk(ZONE, 'disk-name'); + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Disk = require('../../lib/compute/disk.js'); + VM = require('../../lib/compute/vm.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); beforeEach(function() { vm = new VM(ZONE, VM_NAME); + DISK = new Disk(ZONE, 'disk-name'); }); describe('instantiation', function() { @@ -45,11 +76,44 @@ describe('VM', function() { it('should localize the name', function() { assert.strictEqual(vm.name, VM_NAME); }); + + it('should inherit from ServiceObject', function(done) { + var zoneInstance = extend({}, ZONE, { + createVM: { + bind: function(context) { + assert.strictEqual(context, zoneInstance); + done(); + } + } + }); + + var vm = new VM(zoneInstance, VM_NAME); + assert(vm instanceof ServiceObject); + + var calledWith = vm.calledWith_[0]; + + assert.strictEqual(calledWith.parent, zoneInstance); + assert.strictEqual(calledWith.baseUrl, '/instances'); + assert.strictEqual(calledWith.id, VM_NAME); + assert.deepEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true + }); + }); }); describe('attachDisk', function() { var CONFIG = {}; - var EXPECTED_BODY = { source: DISK.formattedName }; + var EXPECTED_BODY; + + beforeEach(function() { + EXPECTED_BODY = { + deviceName: DISK.name, + source: DISK.formattedName + }; + }); it('should throw if a Disk object is not provided', function() { assert.throws(function() { @@ -57,14 +121,14 @@ describe('VM', function() { }, /A Disk object must be provided/); assert.doesNotThrow(function() { - vm.makeReq_ = util.noop; + vm.request = util.noop; vm.attachDisk(DISK, CONFIG, assert.ifError); }); }); it('should not require an options object', function(done) { - vm.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body, EXPECTED_BODY); + vm.request = function(reqOpts) { + assert.deepEqual(reqOpts.json, EXPECTED_BODY); done(); }; @@ -77,8 +141,8 @@ describe('VM', function() { it('should set the correct mode', function(done) { var expectedBody = extend({}, EXPECTED_BODY, { mode: 'READ_ONLY' }); - vm.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body, expectedBody); + vm.request = function(reqOpts) { + assert.deepEqual(reqOpts.json, expectedBody); done(); }; @@ -86,8 +150,8 @@ describe('VM', function() { }); it('should delete the readOnly property', function(done) { - vm.makeReq_ = function(method, path, query, body) { - assert.strictEqual(typeof body.readOnly, 'undefined'); + vm.request = function(reqOpts) { + assert.strictEqual(typeof reqOpts.json.readOnly, 'undefined'); done(); }; @@ -96,11 +160,10 @@ describe('VM', function() { }); it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/attachDisk'); - assert.strictEqual(query, null); - assert.deepEqual(body, EXPECTED_BODY); + vm.request = function(reqOpts, callback) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/attachDisk'); + assert.deepEqual(reqOpts.json, EXPECTED_BODY); callback(); }; @@ -111,11 +174,9 @@ describe('VM', function() { describe('delete', function() { it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + vm.request = function(reqOpts, callback) { + assert.strictEqual(reqOpts.method, 'DELETE'); + assert.strictEqual(reqOpts.uri, ''); callback(); }; @@ -124,7 +185,7 @@ describe('VM', function() { }); it('should not require a callback', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { + vm.request = function(reqOpts, callback) { assert.doesNotThrow(function() { callback(); done(); @@ -136,21 +197,21 @@ describe('VM', function() { }); describe('detachDisk', function() { - var DEVICE_NAME = format('{uri}/{name}', { - uri: 'https://www.googleapis.com/compute/v1', - name: DISK.formattedName - }); - - var METADATA = { - disks: [ - { - source: DEVICE_NAME, - deviceName: DEVICE_NAME - } - ] - }; + var DEVICE_NAME; + var METADATA; beforeEach(function() { + DEVICE_NAME = DISK.formattedName; + + METADATA = METADATA = { + disks: [ + { + source: DEVICE_NAME, + deviceName: DEVICE_NAME + } + ] + }; + vm.getMetadata = function(callback) { callback(null, METADATA, METADATA); }; @@ -162,7 +223,7 @@ describe('VM', function() { }, /A Disk object must be provided/); assert.doesNotThrow(function() { - vm.makeReq_ = util.noop; + vm.getMetadata = util.noop; vm.detachDisk(DISK); }); }); @@ -192,11 +253,10 @@ describe('VM', function() { }); it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/detachDisk'); - assert.deepEqual(query, { deviceName: DEVICE_NAME }); - assert.strictEqual(body, null); + vm.request = function(reqOpts, callback) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/detachDisk'); + assert.deepEqual(reqOpts.qs, { deviceName: DEVICE_NAME }); callback(); }; @@ -205,7 +265,7 @@ describe('VM', function() { }); it('should not require a callback', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { + vm.request = function(reqOpts, callback) { assert.doesNotThrow(function() { callback(); done(); @@ -236,86 +296,12 @@ describe('VM', function() { }); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - vm.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - vm.makeReq_ = function(method, path, query, body, callback) { - callback(error, null/*usually an operation*/, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - vm.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - vm.getMetadata(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - vm.makeReq_ = function(method, path, query, body, callback) { - callback(null, null/*usually an operation*/, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - vm.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(vm.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - vm.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should not require a callback', function() { - assert.doesNotThrow(function() { - vm.getMetadata(); - }); - }); - }); - }); - describe('getSerialPortOutput', function() { var EXPECTED_QUERY = { port: 1 }; it('should default to port 1', function(done) { - vm.makeReq_ = function(method, path, query) { - assert.strictEqual(query.port, 1); + FakeServiceObject.prototype.request = function(reqOpts) { + assert.strictEqual(reqOpts.qs.port, 1); done(); }; @@ -325,8 +311,8 @@ describe('VM', function() { it('should override the default port', function(done) { var port = 8001; - vm.makeReq_ = function(method, path, query) { - assert.strictEqual(query.port, port); + FakeServiceObject.prototype.request = function(reqOpts) { + assert.strictEqual(reqOpts.qs.port, port); done(); }; @@ -334,11 +320,9 @@ describe('VM', function() { }); it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/serialPort'); - assert.deepEqual(query, EXPECTED_QUERY); - assert.strictEqual(body, null); + FakeServiceObject.prototype.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/serialPort'); + assert.deepEqual(reqOpts.qs, EXPECTED_QUERY); done(); }; @@ -351,8 +335,8 @@ describe('VM', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - vm.makeReq_ = function(method, path, query, body, callback) { - callback(error, null/*usually an operation*/, apiResponse); + FakeServiceObject.prototype.request = function(reqOpts, callback) { + callback(error, apiResponse); }; }); @@ -371,8 +355,8 @@ describe('VM', function() { var apiResponse = { contents: 'contents' }; beforeEach(function() { - vm.makeReq_ = function(method, path, query, body, callback) { - callback(null, null/*usually an operation*/, apiResponse); + FakeServiceObject.prototype.request = function(reqOpts, callback) { + callback(null, apiResponse); }; }); @@ -451,11 +435,9 @@ describe('VM', function() { describe('reset', function() { it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/reset'); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + vm.request = function(reqOpts, callback) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/reset'); callback(); }; @@ -464,7 +446,7 @@ describe('VM', function() { }); it('should not require a callback', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { + vm.request = function(reqOpts, callback) { assert.doesNotThrow(function() { callback(); done(); @@ -477,11 +459,9 @@ describe('VM', function() { describe('start', function() { it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/start'); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + vm.request = function(reqOpts, callback) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/start'); callback(); }; @@ -490,7 +470,7 @@ describe('VM', function() { }); it('should not require a callback', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { + vm.request = function(reqOpts, callback) { assert.doesNotThrow(function() { callback(); done(); @@ -503,11 +483,9 @@ describe('VM', function() { describe('stop', function() { it('should make the correct API request', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/stop'); - assert.strictEqual(query, null); - assert.strictEqual(body, null); + vm.request = function(reqOpts, callback) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/stop'); callback(); }; @@ -516,7 +494,7 @@ describe('VM', function() { }); it('should not require a callback', function(done) { - vm.makeReq_ = function(method, path, query, body, callback) { + vm.request = function(reqOpts, callback) { assert.doesNotThrow(function() { callback(); done(); @@ -527,76 +505,63 @@ describe('VM', function() { }); }); - describe('makeReq_', function() { + describe('request', function() { it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/instances/' + vm.name; + var reqOpts = {}; - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - vm.zone.makeReq_ = function(method_, path_, query_, body_) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); + FakeServiceObject.prototype.request = function(reqOpts_) { + assert.strictEqual(this, vm); + assert.strictEqual(reqOpts_, reqOpts); done(); }; - vm.makeReq_(method, path, query, body, assert.ifError); + vm.request(reqOpts, assert.ifError); }); - }); - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; + describe('error', function() { + var error = new Error('Error.'); + var apiResponse = { a: 'b', c: 'd' }; - beforeEach(function() { - vm.zone.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); + beforeEach(function() { + FakeServiceObject.prototype.request = function(reqOpts, callback) { + callback(error, apiResponse); + }; + }); - it('should execute callback with error & API response', function(done) { - vm.makeReq_('POST', '/', {}, {}, function(err, operation, resp) { - assert.strictEqual(err, error); - assert.strictEqual(operation, null); - assert.strictEqual(resp, apiResponse); - done(); + it('should execute callback with error & API response', function(done) { + vm.request({}, function(err, operation, resp) { + assert.strictEqual(err, error); + assert.strictEqual(operation, null); + assert.strictEqual(resp, apiResponse); + done(); + }); }); }); - }); - describe('success', function() { - var apiResponse = { name: 'operation-name' }; + describe('success', function() { + var apiResponse = { name: 'operation-name' }; - beforeEach(function() { - vm.zone.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); + beforeEach(function() { + FakeServiceObject.prototype.request = function(reqOpts, callback) { + callback(null, apiResponse); + }; + }); - it('should execute callback with a Zone object & API resp', function(done) { - var operation = {}; + it('should execute callback with Zone object & API resp', function(done) { + var operation = {}; - vm.zone.operation = function(name) { - assert.strictEqual(name, apiResponse.name); - return operation; - }; + vm.zone.operation = function(name) { + assert.strictEqual(name, apiResponse.name); + return operation; + }; - vm.makeReq_('POST', '/', {}, {}, function(err, operation_, resp) { - assert.ifError(err); - assert.strictEqual(operation_, operation); - assert.strictEqual(resp, apiResponse); - done(); + vm.request({}, function(err, operation_, resp) { + assert.ifError(err); + assert.strictEqual(operation_, operation); + assert.strictEqual(resp, apiResponse); + done(); + }); }); }); }); diff --git a/test/compute/zone.js b/test/compute/zone.js index bd769beb8b4..26387ef0eb4 100644 --- a/test/compute/zone.js +++ b/test/compute/zone.js @@ -21,7 +21,9 @@ var assert = require('assert'); var extend = require('extend'); var gceImages = require('gce-images'); var mockery = require('mockery'); +var nodeutil = require('util'); +var ServiceObject = require('../../lib/common/service-object.js'); var util = require('../../lib/common/util.js'); var gceImagesOverride = null; @@ -41,6 +43,13 @@ function FakeVM() { this.calledWith_ = [].slice.call(arguments); } +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); + var extended = false; var fakeStreamRouter = { extend: function(Class, methods) { @@ -60,17 +69,13 @@ describe('Zone', function() { var zone; var COMPUTE = { - makeAuthenticatedRequest_: { - authClient: { - a: 'b', - c: 'd' - } - } + authClient: {} }; var ZONE_NAME = 'us-central1-a'; before(function() { mockery.registerMock('gce-images', fakeGceImages); + mockery.registerMock('../common/service-object.js', FakeServiceObject); mockery.registerMock('../common/stream-router.js', fakeStreamRouter); mockery.registerMock('./disk.js', FakeDisk); mockery.registerMock('./operation.js', FakeOperation); @@ -107,30 +112,39 @@ describe('Zone', function() { assert.strictEqual(zone.name, ZONE_NAME); }); - it('should default metadata to an empty object', function() { - assert.strictEqual(typeof zone.metadata, 'object'); - assert.strictEqual(Object.keys(zone.metadata).length, 0); - }); - it('should create a gceImages instance', function() { var gceVal = 'ok'; gceImagesOverride = function(authConfig) { - var expectedAuthClient = COMPUTE.makeAuthenticatedRequest_.authClient; - assert.strictEqual(authConfig.authClient, expectedAuthClient); + assert.strictEqual(authConfig.authClient, COMPUTE.authClient); return gceVal; }; var newZone = new Zone(COMPUTE, ZONE_NAME); assert.strictEqual(newZone.gceImages, gceVal); }); + + it('should inherit from ServiceObject', function() { + assert(zone instanceof ServiceObject); + + var calledWith = zone.calledWith_[0]; + + assert.strictEqual(calledWith.parent, COMPUTE); + assert.strictEqual(calledWith.baseUrl, '/zones'); + assert.strictEqual(calledWith.id, ZONE_NAME); + assert.deepEqual(calledWith.methods, { + exists: true, + get: true, + getMetadata: true + }); + }); }); describe('createDisk', function() { var NAME = 'disk-name'; beforeEach(function() { - zone.makeReq_ = util.noop; + zone.request = util.noop; }); it('should use the image property as qs.sourceImages', function(done) { @@ -138,8 +152,8 @@ describe('Zone', function() { image: 'abc' }; - zone.makeReq_ = function(method, path, query) { - assert.strictEqual(query.sourceImage, config.image); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.qs.sourceImage, config.image); done(); }; @@ -216,11 +230,11 @@ describe('Zone', function() { }; it('should make the correct API request', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/disks'); - assert.deepEqual(query, {}); - assert.deepEqual(body, expectedBody); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/disks'); + assert.deepEqual(reqOpts.qs, {}); + assert.deepEqual(reqOpts.json, expectedBody); done(); }; @@ -233,7 +247,7 @@ describe('Zone', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -253,7 +267,7 @@ describe('Zone', function() { var apiResponse = { name: 'operation-name' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -309,9 +323,9 @@ describe('Zone', function() { }; it('should format a given machine type', function(done) { - zone.makeReq_ = function(method, path, query, body) { + zone.request = function(reqOpts) { assert.strictEqual( - body.machineType, + reqOpts.json.machineType, 'zones/' + ZONE_NAME + '/machineTypes/' + CONFIG.machineType ); done(); @@ -327,8 +341,8 @@ describe('Zone', function() { }; it('should accept an array of tags', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.tags.items, CONFIG.tags); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.tags.items, CONFIG.tags); done(); }; @@ -356,8 +370,8 @@ describe('Zone', function() { }); it('should add a network interface accessConfig', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.networkInterfaces[0].accessConfigs[0], { + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.networkInterfaces[0].accessConfigs[0], { type: 'ONE_TO_ONE_NAT' }); done(); @@ -367,8 +381,8 @@ describe('Zone', function() { }); it('should add an http tag', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert(body.tags.items.indexOf('http-server') > -1); + zone.request = function(reqOpts) { + assert(reqOpts.json.tags.items.indexOf('http-server') > -1); done(); }; @@ -385,8 +399,8 @@ describe('Zone', function() { var expectedTags = ['a', 'b', 'http-server']; - zone.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.tags.items, expectedTags); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.tags.items, expectedTags); done(); }; @@ -394,8 +408,8 @@ describe('Zone', function() { }); it('should delete the https property', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.https, undefined); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.https, undefined); done(); }; @@ -423,8 +437,8 @@ describe('Zone', function() { }); it('should add a network interface accessConfig', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.networkInterfaces[0].accessConfigs[0], { + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.networkInterfaces[0].accessConfigs[0], { type: 'ONE_TO_ONE_NAT' }); done(); @@ -434,8 +448,8 @@ describe('Zone', function() { }); it('should add an https tag', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert(body.tags.items.indexOf('https-server') > -1); + zone.request = function(reqOpts) { + assert(reqOpts.json.tags.items.indexOf('https-server') > -1); done(); }; @@ -452,8 +466,8 @@ describe('Zone', function() { var expectedTags = ['a', 'b', 'https-server']; - zone.makeReq_ = function(method, path, query, body) { - assert.deepEqual(body.tags.items, expectedTags); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.json.tags.items, expectedTags); done(); }; @@ -461,8 +475,8 @@ describe('Zone', function() { }); it('should delete the https property', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.https, undefined); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.https, undefined); done(); }; @@ -536,11 +550,10 @@ describe('Zone', function() { describe('API request', function() { it('should make the correct API request', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/instances'); - assert.deepEqual(query, null); - assert.deepEqual(body, EXPECTED_CONFIG); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/instances'); + assert.deepEqual(reqOpts.json, EXPECTED_CONFIG); done(); }; @@ -553,7 +566,7 @@ describe('Zone', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -573,7 +586,7 @@ describe('Zone', function() { var apiResponse = { name: 'operation-name' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -621,8 +634,8 @@ describe('Zone', function() { describe('getDisks', function() { it('should accept only a callback', function(done) { - zone.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -632,11 +645,9 @@ describe('Zone', function() { it('should make the correct API request', function(done) { var query = { a: 'b', c: 'd' }; - zone.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/disks'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/disks'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -649,7 +660,7 @@ describe('Zone', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -673,7 +684,7 @@ describe('Zone', function() { }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -687,7 +698,7 @@ describe('Zone', function() { pageToken: nextPageToken }; - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -722,72 +733,10 @@ describe('Zone', function() { }); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, ''); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - - done(); - }; - - zone.getMetadata(assert.ifError); - }); - - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); - }; - }); - - it('should execute callback with error and API response', function(done) { - zone.getMetadata(function(err, metadata, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(metadata, null); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - }); - - describe('success', function() { - var apiResponse = { a: 'b', c: 'd' }; - - beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { - callback(null, apiResponse); - }; - }); - - it('should update the metadata to the API response', function(done) { - zone.getMetadata(function(err) { - assert.ifError(err); - assert.strictEqual(zone.metadata, apiResponse); - done(); - }); - }); - - it('should exec callback with metadata and API response', function(done) { - zone.getMetadata(function(err, metadata, apiResponse_) { - assert.ifError(err); - assert.strictEqual(metadata, apiResponse); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - }); - }); - describe('getOperations', function() { it('should accept only a callback', function(done) { - zone.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -797,11 +746,9 @@ describe('Zone', function() { it('should make the correct API request', function(done) { var query = { a: 'b', c: 'd' }; - zone.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/operations'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/operations'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -814,7 +761,7 @@ describe('Zone', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -837,7 +784,7 @@ describe('Zone', function() { }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -851,7 +798,7 @@ describe('Zone', function() { pageToken: nextPageToken }; - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -888,8 +835,8 @@ describe('Zone', function() { describe('getVMs', function() { it('should accept only a callback', function(done) { - zone.makeReq_ = function(method, path, query) { - assert.deepEqual(query, {}); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -899,11 +846,9 @@ describe('Zone', function() { it('should make the correct API request', function(done) { var query = { a: 'b', c: 'd' }; - zone.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/instances'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/instances'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -916,7 +861,7 @@ describe('Zone', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -939,7 +884,7 @@ describe('Zone', function() { }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -953,7 +898,7 @@ describe('Zone', function() { pageToken: nextPageToken }; - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -1103,31 +1048,4 @@ describe('Zone', function() { }); }); }); - - describe('makeReq_', function() { - it('should make the correct request to Compute', function(done) { - var expectedPathPrefix = '/zones/' + zone.name; - - var method = 'POST'; - var path = '/test'; - var query = { - a: 'b', - c: 'd' - }; - var body = { - a: 'b', - c: 'd' - }; - - zone.compute.makeReq_ = function(method_, path_, query_, body_, cb) { - assert.strictEqual(method_, method); - assert.strictEqual(path_, expectedPathPrefix + path); - assert.strictEqual(query_, query); - assert.strictEqual(body_, body); - cb(); - }; - - zone.makeReq_(method, path, query, body, done); - }); - }); });