diff --git a/lib/dns/change.js b/lib/dns/change.js index f069cadd866..6872290e6a6 100644 --- a/lib/dns/change.js +++ b/lib/dns/change.js @@ -20,6 +20,14 @@ 'use strict'; +var nodeutil = require('util'); + +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @constructor * @alias module:dns/change @@ -39,50 +47,107 @@ * var change = zone.change('change-id'); */ function Change(zone, id) { - this.zoneName = zone.name; - this.id = id; + var methods = { + /** + * Check if the change exists. + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {boolean} callback.exists - Whether the change exists or not. + * + * @example + * change.exists(function(err, exists) {}); + */ + exists: true, + + /** + * Get a change 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 + * change.get(function(err, change, apiResponse) { + * // `change.metadata` has been populated. + * }); + */ + get: true, + + /** + * Get the metadata for the change in the zone. + * + * @resource [Changes: get API Documentation]{@link https://cloud.google.com/dns/api/v1/changes/get} + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An API error. + * @param {?object} callback.metadata - Metadata of the change from the API. + * @param {object} callback.apiResponse - Raw API response. + * + * @example + * change.getMetadata(function(err, metadata, apiResponse) { + * if (!err) { + * // metadata = { + * // kind: 'dns#change', + * // additions: [{...}], + * // deletions: [{...}], + * // startTime: '2015-07-21T14:40:06.056Z', + * // id: '1', + * // status: 'done' + * // } + * } + * }); + */ + getMetadata: true + }; - this.metadata = {}; - this.makeReq_ = zone.dns.makeReq_.bind(zone.dns); + ServiceObject.call(this, { + parent: zone, + baseUrl: '/changes', + id: id, + methods: methods + }); } +nodeutil.inherits(Change, ServiceObject); + /** - * Get the metadata for the change in the zone. - * - * @resource [Changes: get API Documentation]{@link https://cloud.google.com/dns/api/v1/changes/get} + * Create a change. * - * @param {function} callback - The callback function. - * @param {?error} callback.err - An API error. - * @param {?object} callback.metadata - Metadata of the change from the API. - * @param {object} callback.apiResponse - Raw API response. + * @param {object} config - See {module:dns/zone#createChange}. * * @example - * change.getMetadata(function(err, metadata, apiResponse) { + * var config = { + * add: { + * // ... + * } + * }; + * + * change.create(config, function(err, change, apiResponse) { * if (!err) { - * // metadata = { - * // kind: 'dns#change', - * // additions: [{...}], - * // deletions: [{...}], - * // startTime: '2015-07-21T14:40:06.056Z', - * // id: '1', - * // status: 'done' - * // } + * // The change was created successfully. * } * }); */ -Change.prototype.getMetadata = function(callback) { +Change.prototype.create = function(config, callback) { var self = this; - var path = '/managedZones/' + this.zoneName + '/changes/' + this.id; - this.makeReq_('GET', path, null, null, function(err, resp) { + this.parent.createChange(config, function(err, change, apiResponse) { if (err) { - callback(err, null, resp); + callback(err, null, apiResponse); return; } - self.metadata = resp; + self.id = change.id; + self.metadata = change.metadata; - callback(null, self.metadata, resp); + callback(null, self, apiResponse); }); }; diff --git a/lib/dns/index.js b/lib/dns/index.js index 24a81b5f942..df66b42b32a 100644 --- a/lib/dns/index.js +++ b/lib/dns/index.js @@ -20,8 +20,16 @@ 'use strict'; +var arrify = require('arrify'); var extend = require('extend'); var is = require('is'); +var nodeutil = require('util'); + +/** + * @type {module:common/service} + * @private + */ +var Service = require('../common/service.js'); /** * @type {module:common/streamrouter} @@ -41,21 +49,6 @@ var util = require('../common/util.js'); */ var Zone = require('./zone.js'); -/** - * @const {string} Base URL for DNS API. - * @private - */ -var DNS_BASE_URL = 'https://www.googleapis.com/dns/v1/projects/'; - -/** - * @const {array} Required scopes for the DNS API. - * @private - */ -var SCOPES = [ - 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', - 'https://www.googleapis.com/auth/cloud-platform' -]; - /** * [Google Cloud DNS](https://cloud.google.com/dns/what-is-cloud-dns) is a high- * performance, resilient, global DNS service that provides a cost-effective way @@ -82,16 +75,19 @@ function DNS(options) { return new DNS(options); } - this.makeAuthenticatedRequest_ = util.makeAuthenticatedRequestFactory({ - credentials: options.credentials, - keyFile: options.keyFilename, - scopes: SCOPES, - email: options.email - }); + var config = { + baseUrl: 'https://www.googleapis.com/dns/v1', + scopes: [ + 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', + 'https://www.googleapis.com/auth/cloud-platform' + ] + }; - this.projectId_ = options.projectId; + Service.call(this, config, options); } +nodeutil.inherits(DNS, Service); + /** * Create a managed zone. * @@ -135,7 +131,11 @@ DNS.prototype.createZone = function(name, config, callback) { // Required by the API. config.description = config.description || ''; - this.makeReq_('POST', '/managedZones', null, config, function(err, resp) { + this.request({ + method: 'POST', + uri: '/managedZones', + json: config + }, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -173,13 +173,16 @@ DNS.prototype.getZones = function(query, callback) { query = {}; } - this.makeReq_('GET', '/managedZones', query, null, function(err, resp) { + this.request({ + uri: '/managedZones', + qs: query + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; } - var zones = (resp.managedZones || []).map(function(zone) { + var zones = arrify(resp.managedZones).map(function(zone) { var zoneInstance = self.zone(zone.name); zoneInstance.metadata = zone; return zoneInstance; @@ -198,7 +201,7 @@ DNS.prototype.getZones = function(query, callback) { }; /** - * Create a zone object representing an existing managed zone. + * Create a zone object representing a managed zone. * * @throws {error} If a zone name is not provided. * @@ -216,32 +219,6 @@ DNS.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. - */ -DNS.prototype.makeReq_ = function(method, path, query, body, callback) { - var reqOpts = { - method: method, - qs: query, - uri: DNS_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/dns/zone.js b/lib/dns/zone.js index af93c55852e..5b2060b7aa3 100644 --- a/lib/dns/zone.js +++ b/lib/dns/zone.js @@ -25,6 +25,7 @@ var exec = require('methmeth'); var extend = require('extend'); var fs = require('fs'); var is = require('is'); +var nodeutil = require('util'); var zonefile = require('dns-zonefile'); /** @@ -39,6 +40,12 @@ var Change = require('./change.js'); */ var Record = require('./record.js'); +/** + * @type {module:common/serviceObject} + * @private + */ +var ServiceObject = require('../common/service-object.js'); + /** * @type {module:common/streamrouter} * @private @@ -64,13 +71,87 @@ var streamRouter = require('../common/stream-router.js'); * var zone = dns.zone('zone-id'); */ function Zone(dns, name) { - this.dns = dns; - this.name = name; - this.metadata = {}; + var methods = { + /** + * Create a zone. + * + * @param {object} config - See {module:dns#createZone}. + * + * @example + * var config = { + * dnsName: 'example.com.', + * // ... + * }; + * + * zone.create(config, function(err, zone, apiResponse) { + * if (!err) { + * // The zone was created successfully. + * } + * }); + */ + create: true, + + /** + * 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 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 + * zone.get(function(err, zone, apiResponse) { + * // `zone.metadata` has been populated. + * }); + */ + get: true, + + /** + * Get the metadata for the zone. + * + * @resource [ManagedZones: get API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/get} + * + * @param {function} callback - The callback function. + * @param {?error} callback.err - An API error. + * @param {?object} callback.metadata - Metadata of the zone from the API. + * @param {object} callback.apiResponse - Raw API response. + * + * @example + * zone.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: true + }; + + ServiceObject.call(this, { + parent: dns, + baseUrl: '/managedZones', + id: name, + createMethod: dns.createZone.bind(dns), + methods: methods + }); - this.makeReq_ = this.dns.makeReq_.bind(dns); + this.name = name; } +nodeutil.inherits(Zone, ServiceObject); + /** * Add records to this zone. This is a convenience wrapper around * {module:dns/zone#createChange}. @@ -90,9 +171,7 @@ Zone.prototype.addRecords = function(records, callback) { }; /** - * Create a reference to an existing change object in this zone. - * - * @throws {error} If an id is not provided. + * Create a reference to a change object in this zone. * * @param {string} id - The change id. * @return {module:dns/change} @@ -101,10 +180,6 @@ Zone.prototype.addRecords = function(records, callback) { * var change = zone.change('change-id'); */ Zone.prototype.change = function(id) { - if (!id) { - throw new Error('A change id is required.'); - } - return new Change(this, id); }; @@ -113,10 +188,10 @@ Zone.prototype.change = function(id) { * * @resource [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create} * - * @param {object} options - The configuration object. - * @param {module:dns/record|module:dns/record[]} options.add - Record objects + * @param {object} config - The configuration object. + * @param {module:dns/record|module:dns/record[]} config.add - Record objects * to add to this zone. - * @param {module:dns/record|module:dns/record[]} options.delete - Record + * @param {module:dns/record|module:dns/record[]} config.delete - Record * objects to delete from this zone. Be aware that the resource records here * must match exactly to be deleted. * @param {function} callback - The callback function. @@ -146,24 +221,26 @@ Zone.prototype.change = function(id) { * } * }); */ -Zone.prototype.createChange = function(options, callback) { +Zone.prototype.createChange = function(config, callback) { var self = this; - if (!options || !options.add && !options.delete) { + if (!config || !config.add && !config.delete) { throw new Error('Cannot create a change with no additions or deletions.'); } - var body = extend({}, options, { - additions: arrify(options.add).map(exec('toJSON')), - deletions: arrify(options.delete).map(exec('toJSON')) + var body = extend({}, config, { + additions: arrify(config.add).map(exec('toJSON')), + deletions: arrify(config.delete).map(exec('toJSON')) }); delete body.add; delete body.delete; - var path = '/managedZones/' + this.name + '/changes'; - - this.makeReq_('POST', path, null, body, function(err, resp) { + this.request({ + method: 'POST', + uri: '/changes', + json: body + }, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -221,10 +298,7 @@ Zone.prototype.delete = function(options, callback) { return; } - var path = '/managedZones/' + this.name; - this.makeReq_('DELETE', path, null, null, function(err, resp) { - callback(err, resp); - }); + ServiceObject.prototype.delete.call(this, callback); }; /** @@ -444,9 +518,10 @@ Zone.prototype.getChanges = function(query, callback) { delete query.sort; } - var path = '/managedZones/' + this.name + '/changes'; - - this.makeReq_('GET', path, query, null, function(err, resp) { + this.request({ + uri: '/changes', + qs: query + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; @@ -469,35 +544,6 @@ Zone.prototype.getChanges = function(query, callback) { }); }; -/** - * Get the metadata for the zone. - * - * @resource [ManagedZones: get API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/get} - * - * @param {function} callback - The callback function. - * @param {?error} callback.err - An API error. - * @param {?object} callback.metadata - Metadata of the zone from the API. - * @param {object} callback.apiResponse - Raw API response. - * - * @example - * zone.getMetadata(function(err, metadata, apiResponse) {}); - */ -Zone.prototype.getMetadata = function(callback) { - var self = this; - var path = '/managedZones/' + this.name; - - this.makeReq_('GET', path, null, null, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - - callback(null, self.metadata, resp); - }); -}; - /** * Get the list of records for this zone. * @@ -606,8 +652,10 @@ Zone.prototype.getRecords = function(query, callback) { var requestQuery = extend({}, query); delete requestQuery.filterByTypes_; - var path = '/managedZones/' + this.name + '/rrsets'; - this.makeReq_('GET', path, requestQuery, true, function(err, resp) { + this.request({ + uri: '/rrsets', + qs: requestQuery + }, function(err, resp) { if (err) { callback(err, null, null, resp); return; diff --git a/system-test/dns.js b/system-test/dns.js index 136ba3655f8..a1a2d7b44be 100644 --- a/system-test/dns.js +++ b/system-test/dns.js @@ -32,61 +32,64 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; // Only run the tests if there is a domain to test with. (DNS_DOMAIN ? describe : describe.skip)('dns', function() { - var ZONE; - var ZONENAME = 'test-zone-' + uuid.v4().substr(0, 18); + if (!DNS_DOMAIN) { + // The test runner still executes this function, even if it is skipped. + return; + } - var records = {}; + var ZONE_NAME = 'test-zone-' + uuid.v4().substr(0, 18); + var ZONE = dns.zone(ZONE_NAME); - function createRecords() { - records.a = ZONE.record('a', { + var records = { + a: ZONE.record('a', { ttl: 86400, name: DNS_DOMAIN, data: '1.2.3.4' - }); + }), - records.aaaa = ZONE.record('aaaa', { + aaaa: ZONE.record('aaaa', { ttl: 86400, name: DNS_DOMAIN, data: '2607:f8b0:400a:801::1005' - }); + }), - records.cname = ZONE.record('cname', { + cname: ZONE.record('cname', { ttl: 86400, name: 'mail.' + DNS_DOMAIN, data: 'example.com.' - }); + }), - records.mx = ZONE.record('mx', { + mx: ZONE.record('mx', { ttl: 86400, name: DNS_DOMAIN, data: [ '10 mail.' + DNS_DOMAIN, '20 mail2.' + DNS_DOMAIN ] - }); + }), - records.naptr = ZONE.record('naptr', { + naptr: ZONE.record('naptr', { ttl: 300, name: '2.1.2.1.5.5.5.0.7.7.1.e164.arpa.', data: [ '100 10 \"u\" \"sip+E2U\" \"!^.*$!sip:information@foo.se!i\" .', '102 10 \"u\" \"smtp+E2U\" \"!^.*$!mailto:information@foo.se!i\" .' ] - }); + }), - records.ns = ZONE.record('ns', { + ns: ZONE.record('ns', { ttl: 86400, name: DNS_DOMAIN, data: 'ns-cloud1.googledomains.com.' - }); + }), - records.ptr = ZONE.record('ptr', { + ptr: ZONE.record('ptr', { ttl: 60, name: '2.1.0.10.in-addr.arpa.', data: 'server.' + DNS_DOMAIN - }); + }), - records.soa = ZONE.record('soa', { + soa: ZONE.record('soa', { ttl: 21600, name: DNS_DOMAIN, data: [ @@ -94,26 +97,26 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; 'dns-admin.google.com.', '1 21600 3600 1209600 300' ].join(' ') - }); + }), - records.spf = ZONE.record('spf', { + spf: ZONE.record('spf', { ttl: 21600, name: DNS_DOMAIN, data: 'v=spf1 mx:' + DNS_DOMAIN.replace(/.$/, '') + ' -all' - }); + }), - records.srv = ZONE.record('srv', { + srv: ZONE.record('srv', { ttl: 21600, name: 'sip.' + DNS_DOMAIN, data: '0 5 5060 sip.' + DNS_DOMAIN - }); + }), - records.txt = ZONE.record('txt', { + txt: ZONE.record('txt', { ttl: 21600, name: DNS_DOMAIN, data: 'google-site-verification=xxxxxxxxxxxxYYYYYYXXX' - }); - } + }) + }; before(function(done) { dns.getZones(function(err, zones) { @@ -128,12 +131,7 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; return; } - dns.createZone(ZONENAME, { dnsName: DNS_DOMAIN }, function(err, zone) { - assert.ifError(err); - ZONE = zone; - createRecords(); - done(); - }); + ZONE.create({ dnsName: DNS_DOMAIN }, done); }); }); }); @@ -142,16 +140,6 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; ZONE.delete({ force: true }, done); }); - it('should create a zone', function(done) { - var tempName = 'test-zone-' + uuid.v4().substr(0, 18); - - dns.createZone(tempName, { dnsName: DNS_DOMAIN }, function(err, zone) { - assert.ifError(err); - assert.equal(zone.name, tempName); - zone.delete(done); - }); - }); - it('should return 0 or more zones', function(done) { dns.getZones(function(err, zones) { assert.ifError(err); @@ -164,20 +152,11 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; it('should get the metadata for a zone', function(done) { ZONE.getMetadata(function(err, metadata) { assert.ifError(err); - assert.equal(metadata.name, ZONENAME); + assert.equal(metadata.name, ZONE_NAME); done(); }); }); - it('should delete a zone', function(done) { - var name = 'test-zone-' + uuid.v4().substr(0, 18); - - dns.createZone(name, { dnsName: DNS_DOMAIN }, function(err, zone) { - assert.ifError(err); - zone.delete(done); - }); - }); - it('should support all types of records', function(done) { var recordsToCreate = [ records.a, @@ -249,23 +228,23 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; assert.ifError(err); async.series([ - function(next) { - ZONE.empty(next); - }, - - function(next) { - var recordsToCreate = [ - records.spf, - records.srv - ]; - - ZONE.addRecords(recordsToCreate, next); - }, - - function(next) { - ZONE.export(tmpFilename, next); - } - ], done); + function(next) { + ZONE.empty(next); + }, + + function(next) { + var recordsToCreate = [ + records.spf, + records.srv + ]; + + ZONE.addRecords(recordsToCreate, next); + }, + + function(next) { + ZONE.export(tmpFilename, next); + } + ], done); }); }); @@ -277,7 +256,9 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; data: '10 0 5222 127.0.0.1.' }); - ZONE.createChange({ add: record }, function(err, change) { + var change = ZONE.change(); + + change.create({ add: record }, function(err) { assert.ifError(err); var addition = change.metadata.additions[0]; @@ -343,25 +324,26 @@ var DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN; ZONE.replaceRecords('cname', newRecords, function(err) { assert.ifError(err); - var callback = function(err, records, nextQuery) { + function onRecordsReceived(err, records, nextQuery) { if (nextQuery) { - ZONE.getRecords(nextQuery, callback); + ZONE.getRecords(nextQuery, onRecordsReceived); return; } ZONE.deleteRecords(newRecords, done); - }; + } ZONE.getRecords({ types: 'cname', maxResults: 2 - }, callback); + }, onRecordsReceived); }); }); it('should replace records', function(done) { var name = 'test-zone-' + uuid.v4().substr(0, 18); + // Do this in a new zone so no existing records are affected. dns.createZone(name, { dnsName: DNS_DOMAIN }, function(err, zone) { assert.ifError(err); diff --git a/test/dns/change.js b/test/dns/change.js index 82714d3d275..b8e7a57cdac 100644 --- a/test/dns/change.js +++ b/test/dns/change.js @@ -17,112 +17,131 @@ '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 Change = require('../../lib/dns/change.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('Change', function() { + var Change; + var change; + var ZONE = { name: 'zone-name', - dns: { - makeReq_: util.noop - } + createChange: util.noop }; var CHANGE_ID = 'change-id'; - var change; + before(function() { + mockery.registerMock('../common/service-object.js', FakeServiceObject); + + mockery.enable({ + useCleanCache: true, + warnOnUnregistered: false + }); + + Change = require('../../lib/dns/change.js'); + }); + + after(function() { + mockery.deregisterAll(); + mockery.disable(); + }); beforeEach(function() { change = new Change(ZONE, CHANGE_ID); }); describe('instantiation', function() { - it('should localize the zone name', function() { - assert.strictEqual(change.zoneName, ZONE.name); - }); - - it('should localize the id', function() { - assert.strictEqual(change.id, CHANGE_ID); - }); - - it('should create a makeReq_ function from the Zone', function(done) { - var zone = extend({}, ZONE, { - dns: { - makeReq_: function() { - assert.strictEqual(this, zone.dns); - done(); - } - } + it('should inherit from ServiceObject', function() { + assert(change instanceof ServiceObject); + + var calledWith = change.calledWith_[0]; + + assert.strictEqual(calledWith.parent, ZONE); + assert.strictEqual(calledWith.baseUrl, '/changes'); + assert.strictEqual(calledWith.id, CHANGE_ID); + assert.deepEqual(calledWith.methods, { + exists: true, + get: true, + getMetadata: true }); - - new Change(zone, CHANGE_ID).makeReq_(); }); }); - describe('getMetadata', function() { - it('should make the correct API request', function(done) { - change.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'GET'); - - var expectedPath = format('/managedZones/{z}/changes/{c}', { - z: ZONE.name, - c: CHANGE_ID - }); - assert.strictEqual(path, expectedPath); - - assert.strictEqual(query, null); - assert.strictEqual(body, null); + describe('change', function() { + it('should call the parent change method', function(done) { + var config = {}; + change.parent.createChange = function(config_) { + assert.strictEqual(config, config_); done(); }; - change.getMetadata(assert.ifError); + change.create(config, assert.ifError); }); describe('error', function() { var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; + var apiResponse = {}; beforeEach(function() { - change.makeReq_ = function(method, path, query, body, callback) { - callback(error, apiResponse); + change.parent.createChange = function(config, callback) { + callback(error, null, apiResponse); }; }); - it('should execute callback with error and API response', function(done) { - change.getMetadata(function(err, metadata, apiResponse_) { + it('should execute callback with error & apiResponse', function(done) { + change.create({}, function(err, change, apiResponse_) { assert.strictEqual(err, error); + assert.strictEqual(change, null); assert.strictEqual(apiResponse_, apiResponse); + done(); }); }); }); describe('success', function() { - var metadata = { e: 'f', g: 'h' }; + var changeInstance = { + id: 'id', + metadata: {} + }; + var apiResponse = {}; beforeEach(function() { - change.makeReq_ = function(method, path, query, body, callback) { - callback(null, metadata, metadata); + change.parent.createChange = function(config, callback) { + callback(null, changeInstance, apiResponse); }; }); - it('should update the metadata', function(done) { - change.getMetadata(function(err) { + it('should execute callback with self & API response', function(done) { + change.create({}, function(err, change_, apiResponse_) { assert.ifError(err); - assert.strictEqual(change.metadata, metadata); + + assert.strictEqual(change_, change); + assert.strictEqual(apiResponse_, apiResponse); + done(); }); }); - it('should execute callback with metadata & API resp', function(done) { - change.getMetadata(function(err, metadata_, apiResponse_) { + it('should assign the ID and metadata from the change', function(done) { + change.create({}, function(err, change_) { assert.ifError(err); - assert.strictEqual(metadata_, metadata); - assert.strictEqual(apiResponse_, metadata); + + assert.strictEqual(change_.id, changeInstance.id); + assert.strictEqual(change_.metadata, changeInstance.metadata); + done(); }); }); diff --git a/test/dns/index.js b/test/dns/index.js index be0e71520f4..8b96f479bfc 100644 --- a/test/dns/index.js +++ b/test/dns/index.js @@ -20,9 +20,15 @@ var arrify = require('arrify'); var assert = require('assert'); var extend = require('extend'); var mockery = require('mockery'); +var nodeutil = require('util'); +var Service = require('../../lib/common/service.js'); var util = require('../../lib/common/util.js'); +var fakeUtil = extend({}, util, { + makeAuthenticatedRequestFactory: util.noop +}); + var extended = false; var fakeStreamRouter = { extend: function(Class, methods) { @@ -37,21 +43,17 @@ var fakeStreamRouter = { } }; -var makeAuthenticatedRequestFactoryOverride; -var fakeUtil = extend({}, util, { - makeAuthenticatedRequestFactory: function() { - if (makeAuthenticatedRequestFactoryOverride) { - return makeAuthenticatedRequestFactoryOverride.apply(null, arguments); - } else { - return util.makeAuthenticatedRequestFactory.apply(null, arguments); - } - } -}); - function FakeZone() { this.calledWith_ = arguments; } +function FakeService() { + this.calledWith_ = arguments; + Service.apply(this, arguments); +} + +nodeutil.inherits(FakeService, Service); + describe('DNS', function() { var DNS; var dns; @@ -59,6 +61,7 @@ describe('DNS', function() { var PROJECT_ID = 'project-id'; before(function() { + mockery.registerMock('../common/service.js', FakeService); mockery.registerMock('../common/stream-router.js', fakeStreamRouter); mockery.registerMock('../common/util.js', fakeUtil); mockery.registerMock('./zone.js', FakeZone); @@ -76,8 +79,6 @@ describe('DNS', function() { }); beforeEach(function() { - makeAuthenticatedRequestFactoryOverride = null; - dns = new DNS({ projectId: PROJECT_ID }); @@ -107,33 +108,17 @@ describe('DNS', function() { fakeUtil.normalizeArguments = normalizeArguments; }); - it('should create an authenticated request function', function(done) { - var options = { - projectId: 'projectId', - credentials: 'credentials', - email: 'email', - keyFilename: 'keyFile' - }; + it('should inherit from Service', function() { + assert(dns instanceof Service); - makeAuthenticatedRequestFactoryOverride = function(options_) { - assert.deepEqual(options_, { - credentials: options.credentials, - email: options.email, - keyFile: options.keyFilename, - scopes: [ - 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', - 'https://www.googleapis.com/auth/cloud-platform' - ] - }); - return done; - }; - - var dns = new DNS(options); - dns.makeAuthenticatedRequest_(); - }); + var calledWith = dns.calledWith_[0]; - it('should localize the projectId', function() { - assert.equal(dns.projectId_, PROJECT_ID); + var baseUrl = 'https://www.googleapis.com/dns/v1'; + assert.strictEqual(calledWith.baseUrl, baseUrl); + assert.deepEqual(calledWith.scopes, [ + 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', + 'https://www.googleapis.com/auth/cloud-platform' + ]); }); }); @@ -160,8 +145,8 @@ describe('DNS', function() { it('should use a provided description', function(done) { var cfg = extend({}, config, { description: 'description' }); - dns.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.description, cfg.description); + dns.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.description, cfg.description); done(); }; @@ -169,8 +154,8 @@ describe('DNS', function() { }); it('should default a description to ""', function(done) { - dns.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.description, ''); + dns.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.description, ''); done(); }; @@ -178,16 +163,15 @@ describe('DNS', function() { }); it('should make the correct API request', function(done) { - dns.makeReq_ = function(method, path, query, body) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/managedZones'); - assert.strictEqual(query, null); + dns.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/managedZones'); var expectedBody = extend({}, config, { name: zoneName, description: '' }); - assert.deepEqual(body, expectedBody); + assert.deepEqual(reqOpts.json, expectedBody); done(); }; @@ -200,7 +184,7 @@ describe('DNS', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - dns.makeReq_ = function(method, path, query, body, callback) { + dns.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -220,7 +204,7 @@ describe('DNS', function() { var zone = {}; beforeEach(function() { - dns.makeReq_ = function(method, path, query, body, callback) { + dns.request = function(reqOpts, callback) { callback(null, apiResponse); }; @@ -262,11 +246,9 @@ describe('DNS', function() { it('should make the correct request', function(done) { var query = { a: 'b', c: 'd' }; - dns.makeReq_ = function(method, path, query_, body) { - assert.strictEqual(method, 'GET'); - assert.strictEqual(path, '/managedZones'); - assert.strictEqual(query, query); - assert.strictEqual(body, null); + dns.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/managedZones'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -275,8 +257,8 @@ describe('DNS', function() { }); it('should use an empty query if one was not provided', function(done) { - dns.makeReq_ = function(method, path, query) { - assert.equal(Object.keys(query).length, 0); + dns.request = function(reqOpts) { + assert.equal(Object.keys(reqOpts.qs).length, 0); done(); }; @@ -288,7 +270,7 @@ describe('DNS', function() { var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - dns.makeReq_ = function(method, path, query, body, callback) { + dns.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -310,7 +292,7 @@ describe('DNS', function() { var apiResponse = { managedZones: [zone] }; beforeEach(function() { - dns.makeReq_ = function(method, path, query, body, callback) { + dns.request = function(reqOpts, callback) { callback(null, apiResponse); }; @@ -337,7 +319,7 @@ describe('DNS', function() { var query = { a: 'b', c: 'd' }; var originalQuery = extend({}, query); - dns.makeReq_ = function(method, path, query, body, callback) { + dns.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -393,27 +375,4 @@ describe('DNS', function() { assert.strictEqual(newZone.calledWith_[1], newZoneName); }); }); - - describe('makeReq_', function() { - it('should make correct authenticated request', function(done) { - var method = 'POST'; - var path = '/'; - var query = 'query'; - var body = 'body'; - - dns.makeAuthenticatedRequest_ = function(reqOpts, callback) { - assert.equal(reqOpts.method, method); - assert.equal(reqOpts.qs, query); - - var baseUri = 'https://www.googleapis.com/dns/v1/'; - assert.equal(reqOpts.uri, baseUri + 'projects/' + PROJECT_ID + path); - - assert.equal(reqOpts.json, body); - - callback(); - }; - - dns.makeReq_(method, path, query, body, done); - }); - }); }); diff --git a/test/dns/zone.js b/test/dns/zone.js index be12e2036c5..134435b40df 100644 --- a/test/dns/zone.js +++ b/test/dns/zone.js @@ -20,8 +20,18 @@ 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'); var util = require('../../lib/common/util.js'); +function FakeServiceObject() { + this.calledWith_ = arguments; + ServiceObject.apply(this, arguments); +} + +nodeutil.inherits(FakeServiceObject, ServiceObject); + var parseOverride; var fakeDnsZonefile = { parse: function() { @@ -72,13 +82,14 @@ describe('Zone', function() { var zone; var DNS = { - makeReq_: function() {} + createZone: util.noop }; var ZONE_NAME = 'zone-name'; before(function() { mockery.registerMock('dns-zonefile', fakeDnsZonefile); mockery.registerMock('fs', fakeFs); + mockery.registerMock('../common/service-object.js', FakeServiceObject); mockery.registerMock('../common/stream-router.js', fakeStreamRouter); mockery.registerMock('./change.js', FakeChange); mockery.registerMock('./record.js', FakeRecord); @@ -107,24 +118,34 @@ describe('Zone', function() { assert(extended); // See `fakeStreamRouter.extend` }); - it('should localize the DNS instance', function() { - assert.strictEqual(zone.dns, DNS); - }); - it('should localize the name', function() { assert.strictEqual(zone.name, ZONE_NAME); }); - it('should create a makeReq_ function', function(done) { - var dns = { - makeReq_: function() { - assert.strictEqual(this, dns); - done(); + it('should inherit from ServiceObject', function(done) { + var dnsInstance = extend({}, DNS, { + createZone: { + bind: function(context) { + assert.strictEqual(context, dnsInstance); + done(); + } } - }; + }); + + var zone = new Zone(dnsInstance, ZONE_NAME); + assert(zone instanceof ServiceObject); + + var calledWith = zone.calledWith_[0]; - var zone = new Zone(dns, ZONE_NAME); - zone.makeReq_(); + assert.strictEqual(calledWith.parent, dnsInstance); + assert.strictEqual(calledWith.baseUrl, '/managedZones'); + assert.strictEqual(calledWith.id, ZONE_NAME); + assert.deepEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true + }); }); }); @@ -142,12 +163,6 @@ describe('Zone', function() { }); describe('change', function() { - it('should throw if an ID is not provided', function() { - assert.throws(function() { - zone.change(); - }, /A change id is required/); - }); - it('should return a Change object', function() { var changeId = 'change-id'; @@ -173,9 +188,9 @@ describe('Zone', function() { ]; var expectedAdditions = ['a', 'a']; - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.add, undefined); - assert.deepEqual(body.additions, expectedAdditions); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.add, undefined); + assert.deepEqual(reqOpts.json.additions, expectedAdditions); done(); }; @@ -189,9 +204,9 @@ describe('Zone', function() { ]; var expectedDeletions = ['a', 'a']; - zone.makeReq_ = function(method, path, query, body) { - assert.strictEqual(body.delete, undefined); - assert.deepEqual(body.deletions, expectedDeletions); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.json.delete, undefined); + assert.deepEqual(reqOpts.json.deletions, expectedDeletions); done(); }; @@ -199,10 +214,10 @@ describe('Zone', function() { }); it('should make correct API request', function(done) { - zone.makeReq_ = function(method, path, query) { - assert.strictEqual(method, 'POST'); - assert.strictEqual(path, '/managedZones/' + ZONE_NAME + '/changes'); - assert.strictEqual(query, null); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/changes'); + done(); }; @@ -214,7 +229,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); }; }); @@ -232,7 +247,7 @@ describe('Zone', function() { var apiResponse = { id: 1, a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -270,7 +285,7 @@ describe('Zone', function() { }); it('should try to delete again after emptying', function(done) { - zone.makeReq_ = function() { + FakeServiceObject.prototype.delete = function() { done(); }; @@ -283,24 +298,12 @@ describe('Zone', function() { }); it('should make the correct API request', function(done) { - var error = new Error('Error.'); - var apiResponse = { a: 'b', c: 'd' }; - var ignoreThisArgument = { e: 'f', g: 'h' }; - - zone.makeReq_ = function(method, path, query, body, callback) { - assert.strictEqual(method, 'DELETE'); - assert.strictEqual(path, '/managedZones/' + ZONE_NAME); - assert.strictEqual(query, null); - assert.strictEqual(body, null); - callback(error, apiResponse, ignoreThisArgument); + FakeServiceObject.prototype.delete = function(callback) { + assert.strictEqual(this, zone); + callback(); }; - zone.delete(function(err, apiResponse_) { - assert.strictEqual(arguments.length, 2); - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); + zone.delete(done); }); }); @@ -492,8 +495,8 @@ describe('Zone', function() { describe('getChanges', function() { it('should accept only a callback', function(done) { - zone.makeReq_ = function(method, path, query) { - assert.strictEqual(Object.keys(query).length, 0); + zone.request = function(reqOpts) { + assert.deepEqual(reqOpts.qs, {}); done(); }; @@ -503,9 +506,9 @@ describe('Zone', function() { it('should accept a sort', function(done) { var query = { sort: 'desc' }; - zone.makeReq_ = function(method, path, query) { - assert.strictEqual(query.sortOrder, 'descending'); - assert.strictEqual(query.sort, undefined); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.qs.sortOrder, 'descending'); + assert.strictEqual(reqOpts.qs.sort, undefined); done(); }; @@ -516,11 +519,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, '/managedZones/' + ZONE_NAME + '/changes'); - assert.strictEqual(query_, query); - assert.strictEqual(body, null); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.uri, '/changes'); + assert.strictEqual(reqOpts.qs, query); done(); }; @@ -533,7 +534,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); }; }); @@ -553,7 +554,7 @@ describe('Zone', function() { }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -567,7 +568,7 @@ describe('Zone', function() { pageToken: nextPageToken }; - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -602,74 +603,13 @@ 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, '/managedZones/' + ZONE_NAME); - 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(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('getRecords', function() { describe('error', function() { var error = new Error('Error.'); var apiResponse = { a: 'b', c: 'd' }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(error, apiResponse); }; }); @@ -696,7 +636,7 @@ describe('Zone', function() { }; beforeEach(function() { - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponse); }; }); @@ -708,7 +648,7 @@ describe('Zone', function() { }); var expectedNextQuery = { pageToken: nextPageToken }; - zone.makeReq_ = function(method, path, query, body, callback) { + zone.request = function(reqOpts, callback) { callback(null, apiResponseWithNextPageToken); }; @@ -771,8 +711,8 @@ describe('Zone', function() { }); it('should not send filterByTypes_ in API request', function(done) { - zone.makeReq_ = function(method, path, query) { - assert.strictEqual(query.filterByTypes_, undefined); + zone.request = function(reqOpts) { + assert.strictEqual(reqOpts.qs.filterByTypes_, undefined); done(); };