{"action":"created","comment":{"url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/comments/35123536","id":35123536,"diff_hunk":"@@ -0,0 +1,817 @@\n+/*!\n+ * Copyright 2014 Google Inc. All Rights Reserved.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *      http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+/*!\n+ * @module dns/zone\n+ */\n+\n+'use strict';\n+\n+var extend = require('extend');\n+var fs = require('fs');\n+var zonefile = require('dns-zonefile');\n+\n+/**\n+ * @type {module:dns/change}\n+ * @private\n+ */\n+var Change = require('./change.js');\n+\n+/**\n+ * @type {module:dns/record}\n+ * @private\n+ */\n+var Record = require('./record.js');\n+\n+/**\n+ * @type {module:common/streamrouter}\n+ * @private\n+ */\n+var streamRouter = require('../common/stream-router.js');\n+\n+/**\n+ * @type {module:common/util}\n+ * @private\n+ */\n+var util = require('../common/util.js');\n+\n+/**\n+ * A Zone object is used to interact with your project's managed zone. It will\n+ * help you add or delete records, delete your zone, and many other convenience\n+ * methods.\n+ *\n+ * @constructor\n+ * @alias module:dns/zone\n+ *\n+ * @example\n+ * var gcloud = require('gcloud');\n+ *\n+ * var dns = gcloud.dns({\n+ *   keyFilename: '/path/to/keyfile.json',\n+ *   projectId: 'grape-spaceship-123'\n+ * });\n+ *\n+ * var zone = dns.zone('zone-id');\n+ */\n+function Zone(dns, name) {\n+  this.dns = dns;\n+  this.name = name;\n+  this.metadata = {};\n+\n+  this.makeReq_ = this.dns.makeReq_.bind(dns);\n+}\n+\n+/**\n+ * Add records to this zone. This is a convenience wrapper around\n+ * {module:dns/zone#createChange}.\n+ *\n+ * @param {module:dns/record|module:dns/record[]} record - The record objects to\n+ *     add.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/change} callback.change - A {module:dns/change} object.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ */\n+Zone.prototype.addRecords = function(records, callback) {\n+  this.createChange({\n+    add: records\n+  }, callback);\n+};\n+\n+/**\n+ * Create a reference to an existing change object in this zone.\n+ *\n+ * @throws {error} If an id is not provided.\n+ *\n+ * @param {string} id - The change id.\n+ * @return {module:dns/change}\n+ *\n+ * @example\n+ * var change = zone.change('change-id');\n+ */\n+Zone.prototype.change = function(id) {\n+  if (!id) {\n+    throw new Error('A change id is required.');\n+  }\n+\n+  return new Change(this, id);\n+};\n+\n+/**\n+ * Create a change of resource record sets for the zone.\n+ *\n+ * @param {object} options - The configuration object.\n+ * @param {module:dns/record|module:dns/record[]} options.add - Record objects\n+ *     to add to this zone.\n+ * @param {module:dns/record|module:dns/record[]} options.delete - Record\n+ *     objects to delete from this zone. Be aware that the resource records here\n+ *     must match exactly to be deleted.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/change} callback.change - A {module:dns/change} object.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * var oldARecord = zone.record('a', {\n+ *   name: 'example.com.',\n+ *   data: '1.2.3.4',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * var newARecord = zone.record('a', {\n+ *   name: 'example.com.',\n+ *   data: '5.6.7.8',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * zone.createChange({\n+ *   add: newARecord,\n+ *   delete: oldARecord\n+ * }, function(err, change, apiResponse) {\n+ *   if (!err) {\n+ *     // The change was created successfully.\n+ *   }\n+ * });\n+ */\n+Zone.prototype.createChange = function(options, callback) {\n+  var self = this;\n+\n+  if (!options || !options.add && !options.delete) {\n+    throw new Error('Cannot create a change with no additions or deletions.');\n+  }\n+\n+  var body = extend({}, options, {\n+    additions: util.arrayize(options.add).map(util.exec('toJSON')),\n+    deletions: util.arrayize(options.delete).map(util.exec('toJSON'))\n+  });\n+\n+  delete body.add;\n+  delete body.delete;\n+\n+  var path = '/managedZones/' + this.name + '/changes';\n+\n+  this.makeReq_('POST', path, null, body, function(err, resp) {\n+    if (err) {\n+      callback(err, null, resp);\n+      return;\n+    }\n+\n+    var change = self.change(resp.id);\n+    change.metadata = resp;\n+\n+    callback(null, change, resp);\n+  });\n+};\n+\n+/**\n+ * Delete the zone.\n+ *\n+ * Only empty zones can be deleted. Set options.force to `true` to call\n+ * {module:dns/zone#empty} before deleting the zone. Two API calls will then be\n+ * made (one to empty, another to delete), which means <strong> this is not an\n+ * atomic request</strong>.\n+ *\n+ * @param {object=} options - Configuration object.\n+ * @param {boolean} options.force - Empty the zone before deleting.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * zone.delete(function(err, apiResponse) {\n+ *   if (!err) {\n+ *     // The zone is now deleted.\n+ *   }\n+ * });\n+ *\n+ * //-\n+ * // Use `force` to first empty the zone before deleting it.\n+ * //-\n+ * zone.delete({\n+ *   force: true\n+ * }, function(err, apiResponse) {\n+ *   if (!err) {\n+ *     // The zone is now deleted.\n+ *   }\n+ * });\n+ */\n+Zone.prototype.delete = function(options, callback) {\n+  if (util.is(options, 'function')) {\n+    callback = options;\n+    options = {};\n+  }\n+\n+  if (options.force) {\n+    this.empty(this.delete.bind(this, callback));\n+    return;\n+  }\n+\n+  var path = '/managedZones/' + this.name;\n+  this.makeReq_('DELETE', path, null, null, function(err, resp) {\n+    callback(err, resp);\n+  });\n+};\n+\n+/**\n+ * Delete records from this zone. This is a convenience wrapper around\n+ * {module:dns/zone#createChange}.\n+ *\n+ * This method accepts {module:dns/record} objects or string record types in\n+ * its place. This means that you can delete all A records or NS records, etc.\n+ * If used this way, two API requests are made (one to get, then another to\n+ * delete), which means <strong>the operation is not atomic</strong> and could\n+ * result in unexpected changes.\n+ *\n+ * @param {module:dns/record|module:dns/record[]|string} record - If given a\n+ *     string, it is interpreted as a record type. All records that match that\n+ *     type will be retrieved and then deleted. You can also provide a\n+ *     {module:dns/record} object or array of objects.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/change} callback.change - A {module:dns/change} object.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * var oldARecord = zone.record('a', {\n+ *   name: 'example.com.',\n+ *   data: '1.2.3.4',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * var callback = function(err, change, apiResponse) {\n+ *   if (!err) {\n+ *     // Delete change modification was created.\n+ *   }\n+ * };\n+ *\n+ * zone.deleteRecords(oldARecord, callback);\n+ *\n+ * //-\n+ * // Delete multiple records at once.\n+ * //-\n+ * var oldNs1Record = zone.record('ns', {\n+ *   name: 'example.com.',\n+ *   data: 'ns-cloud1.googledomains.com.',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * var oldNs2Record = zone.record('ns', {\n+ *   name: 'example.com.',\n+ *   data: 'ns-cloud2.googledomains.com.',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * zone.deleteRecords([\n+ *   oldNs1Record,\n+ *   oldNs2Record\n+ * ], callback);\n+ *\n+ * //-\n+ * // Possibly a simpler way to perform the above change is deleting records by\n+ * // type.\n+ * //-\n+ * zone.deleteRecords('ns', callback);\n+ *\n+ * //-\n+ * // You can also delete records of multiple types.\n+ * //-\n+ * zone.deleteRecords(['ns', 'a'], callback);\n+ */\n+Zone.prototype.deleteRecords = function(records, callback) {\n+  records = util.arrayize(records);\n+\n+  if (util.is(records[0], 'string')) {\n+    this.deleteRecordsByType_(records, callback);\n+    return;\n+  }\n+\n+  this.createChange({\n+    delete: records\n+  }, callback);\n+};\n+\n+/**\n+ * Emptying your zone means leaving only 'NS' and 'SOA' records. This method\n+ * will first fetch the non-NS and non-SOA records from your zone using\n+ * {module:dns/zone#getRecords}, then use {module:dns/zone#createChange} to\n+ * create a deletion change.\n+ *\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/change} callback.change - A {module:dns/change} object.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ */\n+Zone.prototype.empty = function(callback) {\n+  var self = this;\n+\n+  this.getRecords(function(err, records) {\n+    if (err) {\n+      callback(err);\n+      return;\n+    }\n+\n+    var recordsToDelete = records.filter(function(record) {\n+      return record.type !== 'NS' && record.type !== 'SOA';\n+    });\n+\n+    if (recordsToDelete.length === 0) {\n+      callback();\n+    } else {\n+      self.deleteRecords(recordsToDelete, callback);\n+    }\n+  });\n+};\n+\n+/**\n+ * Provide a path to a zone file to copy records into the zone.\n+ *\n+ * @param {string} localPath - The fully qualified path to the zone file.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API or file system error.\n+ *\n+ * @example\n+ * var zoneFilename = '/Users/stephen/zonefile.zone';\n+ *\n+ * zone.export(zoneFilename, function(err) {\n+ *   if (!err) {\n+ *     // The zone file was created successfully.\n+ *   }\n+ * });\n+ */\n+Zone.prototype.export = function(localPath, callback) {\n+  this.getRecords(function(err, records) {\n+    if (err) {\n+      callback(err);\n+      return;\n+    }\n+\n+    var stringRecords = records.map(util.exec('toString')).join('\\n');\n+\n+    fs.writeFile(localPath, stringRecords, 'utf-8', function(err) {\n+      callback(err || null);\n+    });\n+  });\n+};\n+\n+/**\n+ * Get the list of changes associated with this zone. A change is an atomic\n+ * update to a collection of records.\n+ *\n+ * @param {object=} query - The query object.\n+ * @param {number} query.maxResults - Maximum number of results to return.\n+ * @param {string} query.pageToken - The page token.\n+ * @param {string} query.sort - Set to 'asc' for ascending, and 'desc' for\n+ *     descending or omit for no sorting.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/change[]} callback.changes - An array of\n+ *     {module:dns/change} objects.\n+ * @param {?object} callback.nextQuery - A query object representing the next\n+ *     page of results.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * var callback = function(err, changes, nextQuery, apiResponse) {\n+ *   // The `metadata` property is populated for you with the metadata at the\n+ *   // time of fetching.\n+ *   changes[0].metadata;\n+ *\n+ *   // However, in cases where you are concerned the metadata could have\n+ *   // changed, use the `getMetadata` method.\n+ *   changes[0].getMetadata(function(err, metadata) {});\n+\n+ *   if (nextQuery) {\n+ *     // nextQuery will be non-null if there are more results.\n+ *     zone.getChanges(nextQuery, callback);\n+ *   }\n+ * };\n+ *\n+ * zone.getChanges(callback);\n+ *\n+ * //-\n+ * // Get the changes from your zone as a readable object stream.\n+ * //-\n+ * zone.getChanges()\n+ *   .on('error', console.error)\n+ *   .on('data', function(change) {\n+ *     // change is a Change object.\n+ *   })\n+ *   .on('end', function() {\n+ *     // All changes retrieved.\n+ *   });\n+ *\n+ * //-\n+ * // If you anticipate many results, you can end a stream early to prevent\n+ * // unnecessary processing and API requests.\n+ * //-\n+ * zone.getChanges()\n+ *   .on('data', function(change) {\n+ *     this.end();\n+ *   });\n+ */\n+Zone.prototype.getChanges = function(query, callback) {\n+  var self = this;\n+\n+  if (util.is(query, 'function')) {\n+    callback = query;\n+    query = {};\n+  }\n+\n+  if (query.sort) {\n+    query.sortBy = 'changeSequence';\n+    query.sortOrder = query.sort === 'asc' ? 'ascending' : 'descending';\n+    delete query.sort;\n+  }\n+\n+  var path = '/managedZones/' + this.name + '/changes';\n+\n+  this.makeReq_('GET', path, query, null, function(err, resp) {\n+    if (err) {\n+      callback(err, null, null, resp);\n+      return;\n+    }\n+\n+    var changes = (resp.changes || []).map(function(change) {\n+      var changeInstance = self.change(change.id);\n+      changeInstance.metadata = change;\n+      return changeInstance;\n+    });\n+\n+    var nextQuery = null;\n+    if (resp.nextPageToken) {\n+      nextQuery = extend({}, query, {\n+        pageToken: resp.nextPageToken\n+      });\n+    }\n+\n+    callback(null, changes, nextQuery, resp);\n+  });\n+};\n+\n+/**\n+ * Get the metadata for the zone.\n+ *\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?object} callback.metadata - Metadata of the zone from the API.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * zone.getMetadata(function(err, metadata, apiResponse) {});\n+ */\n+Zone.prototype.getMetadata = function(callback) {\n+  var self = this;\n+  var path = '/managedZones/' + this.name;\n+\n+  this.makeReq_('GET', path, null, null, function(err, resp) {\n+    if (err) {\n+      callback(err, null, resp);\n+      return;\n+    }\n+\n+    self.metadata = resp;\n+\n+    callback(null, self.metadata, resp);\n+  });\n+};\n+\n+/**\n+ * Get the list of records for this zone.\n+ *\n+ * @param {object=} query - The query object.\n+ * @param {string} query.name - Restricts the list to return only records with\n+ *     this fully qualified domain name.\n+ * @param {string} query.type - Restricts the list to return only records of\n+ *     this type. If present, the \"name\" parameter must also be present.\n+ * @param {number} query.maxResults - Maximum number of results to be returned.\n+ * @param {string} query.pageToken - The page token.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/record[]} callback.records - An array of\n+ *     {module:dns/record} objects.\n+ * @param {?object} callback.nextQuery - A query object representing the next\n+ *     page of results.\n+ * @param {object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * var callback = function(err, records, nextQuery, apiResponse) {\n+ *   if (!err) {\n+ *     // records is an array of Record objects.\n+ *   }\n+ *\n+ *   if (nextQuery) {\n+ *     zone.getRecords(nextQuery, callback);\n+ *   }\n+ * };\n+ *\n+ * zone.getRecords(callback);\n+ *\n+ * //-\n+ * // Provide a query for further customization.\n+ * //-\n+ * // Get the namespace records for example.com.\n+ * var query = {\n+ *   name: 'example.com.',\n+ *   type: 'NS'\n+ * };\n+ *\n+ * zone.getRecords(query, callback);\n+ *\n+ * //-\n+ * // Get the records from your zone as a readable object stream.\n+ * //-\n+ * zone.getRecords()\n+ *   .on('error', console.error)\n+ *   .on('data', function(record) {\n+ *     // record is a Record object.\n+ *   })\n+ *   .on('end', function() {\n+ *     // All records retrieved.\n+ *   });\n+ *\n+ * //-\n+ * // If you anticipate many results, you can end a stream early to prevent\n+ * // unnecessary processing and API requests.\n+ * //-\n+ * zone.getRecords()\n+ *   .on('data', function(change) {\n+ *     this.end();\n+ *   });\n+ *\n+ * //-\n+ * // If you only want records of a specific type or types, provide them in\n+ * // place of the query object.\n+ * //-\n+ * zone.getRecords('ns', function(err, records) {\n+ *   if (!err) {\n+ *     // records is an array of NS Record objects in your zone.\n+ *   }\n+ * });\n+ *\n+ * //-\n+ * // You can also specify multiple record types.\n+ * //-\n+ * zone.getRecords(['ns', 'a', 'cname'], function(err, records) {\n+ *   if (!err) {\n+ *     // records is an array of NS, A, and CNAME records in your zone.\n+ *   }\n+ * });\n+ */\n+Zone.prototype.getRecords = function(query, callback) {\n+  var self = this;\n+\n+  if (util.is(query, 'function')) {\n+    callback = query;\n+    query = {};\n+  }\n+\n+  if (util.is(query, 'string') || util.is(query, 'array')) {\n+    var filterByTypes_ = {};\n+\n+    // For faster lookups, store the record types the user wants in an object.\n+    util.arrayize(query).forEach(function(type) {\n+      filterByTypes_[type.toUpperCase()] = true;\n+    });\n+\n+    query = {\n+      filterByTypes_: filterByTypes_\n+    };\n+  }\n+\n+  var requestQuery = extend({}, query);\n+  delete requestQuery.filterByTypes_;\n+\n+  var path = '/managedZones/' + this.name + '/rrsets';\n+  this.makeReq_('GET', path, requestQuery, true, function(err, resp) {\n+    if (err) {\n+      callback(err, null, null, resp);\n+      return;\n+    }\n+\n+    var records = (resp.rrsets || []).map(function(record) {\n+      return self.record(record.type, record);\n+    });\n+\n+    if (query.filterByTypes_) {\n+      records = records.filter(function(record) {\n+        return query.filterByTypes_[record.type];\n+      });\n+    }\n+\n+    var nextQuery = null;\n+    if (resp.nextPageToken) {\n+      nextQuery = extend({}, query, {\n+        pageToken: resp.nextPageToken\n+      });\n+    }\n+\n+    callback(null, records, nextQuery, resp);\n+  });\n+};\n+\n+/**\n+ * Copy the records from a zone file into this zone.\n+ *\n+ * @param {string} localPath - The fully qualified path to the zone file.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API or file system error.\n+ * @param {?module:dns/change} callback.change - A {module:dns/change} object.\n+ * @param {?object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * var zoneFilename = '/Users/dave/zonefile.zone';\n+ *\n+ * zone.import(zoneFilename, function(err, change, apiResponse) {\n+ *   if (!err) {\n+ *     // The change was created successfully.\n+ *   }\n+ * });\n+ */\n+Zone.prototype.import = function(localPath, callback) {\n+  var self = this;\n+\n+  fs.readFile(localPath, 'utf-8', function(err, file) {\n+    if (err) {\n+      callback(err);\n+      return;\n+    }\n+\n+    var parsedZonefile = zonefile.parse(file);\n+\n+    var recordsToCreate = [];\n+    function addRecordToCreate(record) {\n+      var recordInstance = new Record.fromZoneRecord_(self, recordType, record);\n+      recordsToCreate.push(recordInstance);\n+    }\n+\n+    for (var recordType in parsedZonefile) {\n+      var recordTypeSet = util.arrayize(parsedZonefile[recordType]);\n+      recordTypeSet.forEach(addRecordToCreate);\n+    }\n+\n+    self.addRecords(recordsToCreate, callback);\n+  });\n+};\n+\n+/**\n+ * A {module:dns/record} object can be used to construct a record you want to\n+ * add to your zone, or to refer to an existing one.\n+ *\n+ * Note that using this method will not itself make any API requests. You will\n+ * use the object returned in other API calls, for example to add a record to\n+ * your zone or to delete an existing one.\n+ *\n+ * @param {string} type - The type of record to construct or the type of record\n+ *     you are referencing.\n+ * @param {object} metadata - The metadata of this record.\n+ * @param {string} metadata.name - The name of the record, e.g.\n+ *     `www.example.com.`.\n+ * @param {string[]} metadata.data - Defined in\n+ *     [RFC 1035, section 5](https://goo.gl/9EiM0e) and\n+ *     [RFC 1034, section 3.6.1](https://goo.gl/Hwhsu9).\n+ * @param {number} metadata.ttl - Seconds that the resource is cached by\n+ *     resolvers.\n+ * @return {module:dns/record}\n+ *\n+ * @example\n+ * //-\n+ * // Reference an existing record to delete from your zone.\n+ * //-\n+ * var oldARecord = zone.record('a', {\n+ *   name: 'example.com.',\n+ *   data: '1.2.3.4',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * //-\n+ * // Construct a record to add to your zone.\n+ * //-\n+ * var newARecord = zone.record('a', {\n+ *   name: 'example.com.',\n+ *   data: '5.6.7.8',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * //-\n+ * // Use these records together to create a change.\n+ * //-\n+ * zone.createChange({\n+ *   add: newARecord,\n+ *   delete: oldARecord\n+ * }, function(err, change, apiResponse) {});\n+ */\n+Zone.prototype.record = function(type, metadata) {\n+  return new Record(this, type, metadata);\n+};\n+\n+/**\n+ * Provide a record type that should be deleted and replaced with other records.\n+ *\n+ * <strong>This is not an atomic request.</strong> Two API requests are made\n+ * (one to get records of the type that you've requested, then another to\n+ * replace them), which means the operation is not atomic and could result in\n+ * unexpected changes.\n+ *\n+ * @param {string|string[]} recordTypes - Type(s) of records to replace.\n+ * @param {module:dns/record|module:dns/record[]} newRecords - The record\n+ *     objects to add.\n+ * @param {function} callback - The callback function.\n+ * @param {?error} callback.err - An API error.\n+ * @param {?module:dns/change} callback.change - A {module:dns/change} object.\n+ * @param {?object} callback.apiResponse - Raw API response.\n+ *\n+ * @example\n+ * var newNs1Record = zone.record('ns', {\n+ *   name: 'example.com.',\n+ *   data: 'ns-cloud1.googledomains.com.',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * var newNs2Record = zone.record('ns', {\n+ *   name: 'example.com.',\n+ *   data: 'ns-cloud2.googledomains.com.',\n+ *   ttl: 86400\n+ * });\n+ *\n+ * var newNsRecords = [\n+ *   newNs1Record,\n+ *   newNs2Record\n+ * ];\n+ *\n+ * zone.replaceRecords('ns', newNsRecords, function(err, change, apiResponse) {\n+ *   if (!err) {\n+ *     // The change was created successfully.\n+ *   }\n+ * });\n+ */\n+Zone.prototype.replaceRecords = function(recordType, newRecords, callback) {\n+  var self = this;\n+\n+  this.getRecords(recordType, function(err, recordsToDelete) {","path":"lib/dns/zone.js","position":761,"original_position":761,"commit_id":"c42fdfb60f42542d2332e775f76432158d3f13bc","original_commit_id":"c42fdfb60f42542d2332e775f76432158d3f13bc","user":{"login":"callmehiphop","id":1707267,"avatar_url":"https://avatars.githubusercontent.com/u/1707267?v=3","gravatar_id":"","url":"https://api.github.com/users/callmehiphop","html_url":"https://github.com/callmehiphop","followers_url":"https://api.github.com/users/callmehiphop/followers","following_url":"https://api.github.com/users/callmehiphop/following{/other_user}","gists_url":"https://api.github.com/users/callmehiphop/gists{/gist_id}","starred_url":"https://api.github.com/users/callmehiphop/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/callmehiphop/subscriptions","organizations_url":"https://api.github.com/users/callmehiphop/orgs","repos_url":"https://api.github.com/users/callmehiphop/repos","events_url":"https://api.github.com/users/callmehiphop/events{/privacy}","received_events_url":"https://api.github.com/users/callmehiphop/received_events","type":"User","site_admin":false},"body":"Not sure how likely this is to occur, but is it possible that not all of the records would be returned via `getRecords` and there would be additional records within `nextQuery`?","created_at":"2015-07-21T16:33:23Z","updated_at":"2015-07-21T16:33:23Z","html_url":"https://github.com/GoogleCloudPlatform/gcloud-node/pull/706#discussion_r35123536","pull_request_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706","_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/comments/35123536"},"html":{"href":"https://github.com/GoogleCloudPlatform/gcloud-node/pull/706#discussion_r35123536"},"pull_request":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706"}}},"pull_request":{"url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706","id":39305714,"html_url":"https://github.com/GoogleCloudPlatform/gcloud-node/pull/706","diff_url":"https://github.com/GoogleCloudPlatform/gcloud-node/pull/706.diff","patch_url":"https://github.com/GoogleCloudPlatform/gcloud-node/pull/706.patch","issue_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues/706","number":706,"state":"open","locked":false,"title":"Add Google Cloud DNS","user":{"login":"stephenplusplus","id":723048,"avatar_url":"https://avatars.githubusercontent.com/u/723048?v=3","gravatar_id":"","url":"https://api.github.com/users/stephenplusplus","html_url":"https://github.com/stephenplusplus","followers_url":"https://api.github.com/users/stephenplusplus/followers","following_url":"https://api.github.com/users/stephenplusplus/following{/other_user}","gists_url":"https://api.github.com/users/stephenplusplus/gists{/gist_id}","starred_url":"https://api.github.com/users/stephenplusplus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephenplusplus/subscriptions","organizations_url":"https://api.github.com/users/stephenplusplus/orgs","repos_url":"https://api.github.com/users/stephenplusplus/repos","events_url":"https://api.github.com/users/stephenplusplus/events{/privacy}","received_events_url":"https://api.github.com/users/stephenplusplus/received_events","type":"User","site_admin":false},"body":"Fixes #486\r\n\r\n#### To Dos\r\n\r\n- [x] Add support for SPF records in https://github.com/elgs/dns-zonefile/pull/11\r\n- [x] Tests\r\n  - [x] Unit\r\n    - [x] util.exec\r\n    - [x] dns/index\r\n    - [x] dns/zone\r\n    - [x] dns/record\r\n    - [x] dns/change\r\n  - [x] System\r\n- [ ] ~~Add `GCLOUD_TESTS_DNS_DOMAIN` env var to travis (value: `gcloud-node.com.`)~~\r\n- [x] Docs\r\n\r\nDocs: https://stephenplusplus.github.io/gcloud-node-gh-pages-again/#/docs/master/dns\r\n","created_at":"2015-07-06T20:11:37Z","updated_at":"2015-07-21T16:33:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8be4832282e77e73d0fc16a4ac46b4b5a37e92d5","assignee":{"login":"callmehiphop","id":1707267,"avatar_url":"https://avatars.githubusercontent.com/u/1707267?v=3","gravatar_id":"","url":"https://api.github.com/users/callmehiphop","html_url":"https://github.com/callmehiphop","followers_url":"https://api.github.com/users/callmehiphop/followers","following_url":"https://api.github.com/users/callmehiphop/following{/other_user}","gists_url":"https://api.github.com/users/callmehiphop/gists{/gist_id}","starred_url":"https://api.github.com/users/callmehiphop/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/callmehiphop/subscriptions","organizations_url":"https://api.github.com/users/callmehiphop/orgs","repos_url":"https://api.github.com/users/callmehiphop/repos","events_url":"https://api.github.com/users/callmehiphop/events{/privacy}","received_events_url":"https://api.github.com/users/callmehiphop/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706/commits","review_comments_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706/comments","review_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/comments{/number}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues/706/comments","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/statuses/c42fdfb60f42542d2332e775f76432158d3f13bc","head":{"label":"stephenplusplus:spp--dns","ref":"spp--dns","sha":"c42fdfb60f42542d2332e775f76432158d3f13bc","user":{"login":"stephenplusplus","id":723048,"avatar_url":"https://avatars.githubusercontent.com/u/723048?v=3","gravatar_id":"","url":"https://api.github.com/users/stephenplusplus","html_url":"https://github.com/stephenplusplus","followers_url":"https://api.github.com/users/stephenplusplus/followers","following_url":"https://api.github.com/users/stephenplusplus/following{/other_user}","gists_url":"https://api.github.com/users/stephenplusplus/gists{/gist_id}","starred_url":"https://api.github.com/users/stephenplusplus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephenplusplus/subscriptions","organizations_url":"https://api.github.com/users/stephenplusplus/orgs","repos_url":"https://api.github.com/users/stephenplusplus/repos","events_url":"https://api.github.com/users/stephenplusplus/events{/privacy}","received_events_url":"https://api.github.com/users/stephenplusplus/received_events","type":"User","site_admin":false},"repo":{"id":22482767,"name":"gcloud-node","full_name":"stephenplusplus/gcloud-node","owner":{"login":"stephenplusplus","id":723048,"avatar_url":"https://avatars.githubusercontent.com/u/723048?v=3","gravatar_id":"","url":"https://api.github.com/users/stephenplusplus","html_url":"https://github.com/stephenplusplus","followers_url":"https://api.github.com/users/stephenplusplus/followers","following_url":"https://api.github.com/users/stephenplusplus/following{/other_user}","gists_url":"https://api.github.com/users/stephenplusplus/gists{/gist_id}","starred_url":"https://api.github.com/users/stephenplusplus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stephenplusplus/subscriptions","organizations_url":"https://api.github.com/users/stephenplusplus/orgs","repos_url":"https://api.github.com/users/stephenplusplus/repos","events_url":"https://api.github.com/users/stephenplusplus/events{/privacy}","received_events_url":"https://api.github.com/users/stephenplusplus/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stephenplusplus/gcloud-node","description":"Google Cloud Node Client Library","fork":true,"url":"https://api.github.com/repos/stephenplusplus/gcloud-node","forks_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/forks","keys_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/teams","hooks_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/hooks","issue_events_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/issues/events{/number}","events_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/events","assignees_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/assignees{/user}","branches_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/branches{/branch}","tags_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/tags","blobs_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/git/refs{/sha}","trees_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/statuses/{sha}","languages_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/languages","stargazers_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/stargazers","contributors_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/contributors","subscribers_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/subscribers","subscription_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/subscription","commits_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/commits{/sha}","git_commits_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/git/commits{/sha}","comments_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/comments{/number}","issue_comment_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/issues/comments{/number}","contents_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/contents/{+path}","compare_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/merges","archive_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/downloads","issues_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/issues{/number}","pulls_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/pulls{/number}","milestones_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/milestones{/number}","notifications_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/labels{/name}","releases_url":"https://api.github.com/repos/stephenplusplus/gcloud-node/releases{/id}","created_at":"2014-07-31T19:55:20Z","updated_at":"2015-06-20T19:18:37Z","pushed_at":"2015-07-21T14:42:50Z","git_url":"git://github.com/stephenplusplus/gcloud-node.git","ssh_url":"git@github.com:stephenplusplus/gcloud-node.git","clone_url":"https://github.com/stephenplusplus/gcloud-node.git","svn_url":"https://github.com/stephenplusplus/gcloud-node","homepage":"","size":13535,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"GoogleCloudPlatform:master","ref":"master","sha":"b249f364294c2df5e782115da2dee13ae8849471","user":{"login":"GoogleCloudPlatform","id":2810941,"avatar_url":"https://avatars.githubusercontent.com/u/2810941?v=3","gravatar_id":"","url":"https://api.github.com/users/GoogleCloudPlatform","html_url":"https://github.com/GoogleCloudPlatform","followers_url":"https://api.github.com/users/GoogleCloudPlatform/followers","following_url":"https://api.github.com/users/GoogleCloudPlatform/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCloudPlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCloudPlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCloudPlatform/subscriptions","organizations_url":"https://api.github.com/users/GoogleCloudPlatform/orgs","repos_url":"https://api.github.com/users/GoogleCloudPlatform/repos","events_url":"https://api.github.com/users/GoogleCloudPlatform/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCloudPlatform/received_events","type":"Organization","site_admin":false},"repo":{"id":19483273,"name":"gcloud-node","full_name":"GoogleCloudPlatform/gcloud-node","owner":{"login":"GoogleCloudPlatform","id":2810941,"avatar_url":"https://avatars.githubusercontent.com/u/2810941?v=3","gravatar_id":"","url":"https://api.github.com/users/GoogleCloudPlatform","html_url":"https://github.com/GoogleCloudPlatform","followers_url":"https://api.github.com/users/GoogleCloudPlatform/followers","following_url":"https://api.github.com/users/GoogleCloudPlatform/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCloudPlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCloudPlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCloudPlatform/subscriptions","organizations_url":"https://api.github.com/users/GoogleCloudPlatform/orgs","repos_url":"https://api.github.com/users/GoogleCloudPlatform/repos","events_url":"https://api.github.com/users/GoogleCloudPlatform/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCloudPlatform/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/GoogleCloudPlatform/gcloud-node","description":"Google Cloud Client Library for Node.js","fork":false,"url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node","forks_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/forks","keys_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/teams","hooks_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/hooks","issue_events_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues/events{/number}","events_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/events","assignees_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/assignees{/user}","branches_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/branches{/branch}","tags_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/tags","blobs_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/git/refs{/sha}","trees_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/statuses/{sha}","languages_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/languages","stargazers_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/stargazers","contributors_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/contributors","subscribers_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/subscribers","subscription_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/subscription","commits_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/commits{/sha}","git_commits_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/git/commits{/sha}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/comments{/number}","issue_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues/comments{/number}","contents_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/contents/{+path}","compare_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/merges","archive_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/downloads","issues_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues{/number}","pulls_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls{/number}","milestones_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/milestones{/number}","notifications_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/labels{/name}","releases_url":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/releases{/id}","created_at":"2014-05-06T06:18:00Z","updated_at":"2015-07-19T22:36:55Z","pushed_at":"2015-07-21T15:19:10Z","git_url":"git://github.com/GoogleCloudPlatform/gcloud-node.git","ssh_url":"git@github.com:GoogleCloudPlatform/gcloud-node.git","clone_url":"https://github.com/GoogleCloudPlatform/gcloud-node.git","svn_url":"https://github.com/GoogleCloudPlatform/gcloud-node","homepage":"https://googlecloudplatform.github.io/gcloud-node/","size":14981,"stargazers_count":506,"watchers_count":506,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":87,"mirror_url":null,"open_issues_count":60,"forks":87,"open_issues":60,"watchers":506,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706"},"html":{"href":"https://github.com/GoogleCloudPlatform/gcloud-node/pull/706"},"issue":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues/706"},"comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/issues/706/comments"},"review_comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706/comments"},"review_comment":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/pulls/706/commits"},"statuses":{"href":"https://api.github.com/repos/GoogleCloudPlatform/gcloud-node/statuses/c42fdfb60f42542d2332e775f76432158d3f13bc"}}}}