zone.js

"use strict";
/*!
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("@google-cloud/common");
const paginator_1 = require("@google-cloud/paginator");
const promisify_1 = require("@google-cloud/promisify");
const arrify = require("arrify");
const fs = require("fs");
const groupBy = require("lodash.groupby");
const zonefile = require('dns-zonefile');
const change_1 = require("./change");
const record_1 = require("./record");
// This is used purely for making TypeScript think that the object we are
// subclassing does not contain a signature mismatch for methods we are
// overriding.
// tslint:disable-next-line variable-name
const ZoneServiceObject = common_1.ServiceObject;
/**
 * A Zone object is used to interact with your project's managed zone. It will
 * help you add or delete records, delete your zone, and many other convenience
 * methods.
 *
 * @class
 *
 * @example
 * const {DNS} = require('@google-cloud/dns');
 * const dns = new DNS();
 *
 * const zone = dns.zone('zone-id');
 */
class Zone extends ZoneServiceObject {
    constructor(dns, name) {
        const methods = {
            /**
             * Create a zone.
             *
             * @method Zone#create
             * @param {CreateZoneRequest} metadata Metadata to set for the zone.
             * @param {CreateZoneCallback} [callback] Callback function.
             * @returns {Promise<CreateZoneResponse>}
             *
             * @example
             * const {DNS} = require('@google-cloud/dns');
             * const dns = new DNS();
             *
             * const config = {
             *   dnsName: 'example.com.',
             *   // ...
             * };
             *
             * zone.create(config, (err, zone, apiResponse) => {
             *   if (!err) {
             *     // The zone was created successfully.
             *   }
             * });
             *
             * //-
             * // If the callback is omitted, we'll return a Promise.
             * //-
             * zone.create(config).then((data) => {
             *   const zone = data[0];
             *   const apiResponse = data[1];
             * });
             */
            create: true,
            /**
             * @typedef {array} ZoneExistsResponse
             * @property {boolean} 0 Whether the {@link Zone} exists.
             */
            /**
             * @callback ZoneExistsCallback
             * @param {?Error} err Request error, if any.
             * @param {boolean} exists Whether the {@link Zone} exists.
             */
            /**
             * Check if the zone exists.
             *
             * @method Zone#exists
             * @param {ZoneExistsCallback} [callback] Callback function.
             * @returns {Promise<ZoneExistsResponse>}
             *
             * @example
             * const {DNS} = require('@google-cloud/dns');
             * const dns = new DNS();
             * const zone = dns.zone('zone-id');
             *
             * zone.exists((err, exists) => {});
             *
             * //-
             * // If the callback is omitted, we'll return a Promise.
             * //-
             * zone.exists().then((data) => {
             *   const exists = data[0];
             * });
             */
            exists: true,
            /**
             * @typedef {array} GetZoneResponse
             * @property {Zone} 0 The {@link Zone}.
             * @property {object} 1 The full API response.
             */
            /**
             * @callback GetZoneCallback
             * @param {?Error} err Request error, if any.
             * @param {Zone} zone The {@link Zone}.
             * @param {object} apiResponse The full API response.
             */
            /**
             * 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.
             *
             * @method Zone#get
             * @param {options} [options] Configuration object.
             * @param {boolean} [options.autoCreate=false] Automatically create the
             *     object if it does not exist.
             * @param {GetZoneCallback} [callback] Callback function.
             * @returns {Promise<GetZoneResponse>}
             *
             * @example
             * const {DNS} = require('@google-cloud/dns');
             * const dns = new DNS();
             * const zone = dns.zone('zone-id');
             *
             * zone.get((err, zone, apiResponse) => {
             *   // `zone.metadata` has been populated.
             * });
             *
             * //-
             * // If the callback is omitted, we'll return a Promise.
             * //-
             * zone.get().then(data => {
             *   const zone = data[0];
             *   const apiResponse = data[1];
             * });
             */
            get: true,
            /**
             * @typedef {array} GetZoneMetadataResponse
             * @property {object} 0 The {@link Zone} metadata.
             * @property {object} 1 The full API response.
             */
            /**
             * @callback GetZoneMetadataCallback
             * @param {?Error} err Request error, if any.
             * @param {object} metadata The {@link Zone} metadata.
             * @param {object} apiResponse The full API response.
             */
            /**
             * Get the metadata for the zone.
             *
             * @see [ManagedZones: get API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/get}
             *
             * @method Zone#getMetadata
             * @param {GetZoneMetadataCallback} [callback] Callback function.
             * @returns {Promise<GetZoneMetadataResponse>}
             *
             * @example
             * const {DNS} = require('@google-cloud/dns');
             * const dns = new DNS();
             * const zone = dns.zone('zone-id');
             *
             * zone.getMetadata((err, metadata, apiResponse) => {});
             *
             * //-
             * // If the callback is omitted, we'll return a Promise.
             * //-
             * zone.getMetadata().then((data) => {
             *   const metadata = data[0];
             *   const apiResponse = data[1];
             * });
             */
            getMetadata: true,
        };
        /**
         * @name Zone#metadata
         * @type {object}
         */
        super({
            parent: dns,
            /**
             * @name Zone#baseUrl
             * @type {string}
             * @default "/managedZones"
             */
            baseUrl: '/managedZones',
            /**
             * @name Zone#id
             * @type {string}
             */
            id: name,
            createMethod: dns.createZone.bind(dns),
            methods,
        });
        /**
         * @name Zone#name
         * @type {string}
         */
        this.name = name;
        this.getRecordsStream = paginator_1.paginator.streamify('getRecords');
        this.getChangesStream = paginator_1.paginator.streamify('getChanges');
    }
    create(config, callback) {
        // tslint:disable-next-line no-any
        const args = [config, callback];
        common_1.ServiceObject.prototype.create.apply(this, args);
    }
    get(configOrCallback, callback) {
        const config = typeof configOrCallback === 'object' ? configOrCallback : {};
        callback =
            typeof configOrCallback === 'function' ? configOrCallback : callback;
        common_1.ServiceObject.prototype.get.call(this, config, callback);
    }
    /**
     * Add records to this zone. This is a convenience wrapper around
     * {@link Zone#createChange}.
     *
     * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create}
     *
     * @param {Record|Record[]} record The {@link Record} object(s) to add.
     * @param {CreateChangeCallback} [callback] Callback function.
     * @returns {Promise<CreateChangeResponse>}
     */
    addRecords(records, callback) {
        this.createChange({ add: records }, callback);
    }
    /**
     * Create a reference to a {@link Change} object in this zone.
     *
     * @param {string} id The change id.
     * @returns {Change}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     * const change = zone.change('change-id');
     */
    change(id) {
        return new change_1.Change(this, id);
    }
    /**
     * Create a change of resource record sets for the zone.
     *
     * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create}
     *
     * @param {CreateChangeRequest} config The configuration object.
     * @param {CreateChangeCallback} [callback] Callback function.
     * @returns {Promise<CreateChangeResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     *
     * const oldARecord = zone.record('a', {
     *   name: 'example.com.',
     *   data: '1.2.3.4',
     *   ttl: 86400
     * });
     *
     * const newARecord = zone.record('a', {
     *   name: 'example.com.',
     *   data: '5.6.7.8',
     *   ttl: 86400
     * });
     *
     * const config = {
     *   add: newARecord,
     *   delete: oldARecord
     * };
     *
     * zone.createChange(config, (err, change, apiResponse) => {
     *   if (!err) {
     *     // The change was created successfully.
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.createChange(config).then((data) => {
     *   const change = data[0];
     *   const apiResponse = data[1];
     * });
     */
    createChange(config, callback) {
        if (!config || (!config.add && !config.delete)) {
            throw new Error('Cannot create a change with no additions or deletions.');
        }
        const groupByType = (recordsIn) => {
            const recordsByType = groupBy(recordsIn, 'type');
            const recordsOut = [];
            // tslint:disable-next-line:forin
            for (const recordType in recordsByType) {
                const recordsByName = groupBy(recordsByType[recordType], 'name');
                // tslint:disable-next-line:forin
                for (const recordName in recordsByName) {
                    const records = recordsByName[recordName];
                    const templateRecord = Object.assign({}, records[0]);
                    if (records.length > 1) {
                        // Combine the `rrdatas` values from all records of the same type.
                        templateRecord.rrdatas = records
                            .map(x => x.rrdatas)
                            .reduce((acc, rrdata) => acc.concat(rrdata), []);
                    }
                    recordsOut.push(templateRecord);
                }
            }
            return recordsOut;
        };
        const body = Object.assign({
            additions: groupByType(arrify(config.add).map(x => x.toJSON())),
            deletions: groupByType(arrify(config.delete).map(x => x.toJSON())),
        }, config);
        delete body.add;
        delete body.delete;
        this.request({
            method: 'POST',
            uri: '/changes',
            json: body,
        }, (err, resp) => {
            if (err) {
                callback(err, null, resp);
                return;
            }
            const change = this.change(resp.id);
            change.metadata = resp;
            callback(null, change, resp);
        });
    }
    /**
     * Delete the zone.
     *
     * Only empty zones can be deleted. Set `options.force` to `true` to call
     * {@link Zone#empty} before deleting the zone. Two API calls will then be
     * made (one to empty, another to delete), which means <strong> this is not an
     * atomic request</strong>.
     *
     * @see [ManagedZones: delete API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/delete}
     *
     * @param {object} [options] Configuration object.
     * @param {boolean} [options.force=false] Empty the zone before deleting.
     * @param {DeleteZoneCallback} [callback] Callback function.
     * @returns {Promise<DeleteZoneResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     *
     * zone.delete((err, apiResponse) => {
     *   if (!err) {
     *     // The zone is now deleted.
     *   }
     * });
     *
     * //-
     * // Use `force` to first empty the zone before deleting it.
     * //-
     * zone.delete({
     *   force: true
     * }, (err, apiResponse) => {
     *   if (!err) {
     *     // The zone is now deleted.
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.delete().then(data => {
     *   const apiResponse = data[0];
     * });
     */
    delete(optionsOrCallback, callback) {
        const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
        callback =
            typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
        if (options.force) {
            this.empty(err => {
                if (err) {
                    callback(err);
                    return;
                }
                this.delete(callback);
            });
            return;
        }
        common_1.ServiceObject.prototype.delete.call(this, callback);
    }
    /**
     * Delete records from this zone. This is a convenience wrapper around
     * {@link Zone#createChange}.
     *
     * This method accepts {@link Record} objects or string record types in
     * its place. This means that you can delete all A records or NS records, etc.
     * If used this way, two API requests are made (one to get, then another to
     * delete), which means **the operation is not atomic** and could result in
     * unexpected changes.
     *
     * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create}
     *
     * @param {Record|Record[]|string} record If given a string, it is interpreted
     *     as a record type. All records that match that type will be retrieved
     * and then deleted. You can also provide a {@link Record} object or array of
     *     {@link Record} objects.
     * @param {CreateChangeCallback} [callback] Callback function.
     * @returns {Promise<CreateChangeResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     *
     * const oldARecord = zone.record('a', {
     *   name: 'example.com.',
     *   data: '1.2.3.4',
     *   ttl: 86400
     * });
     *
     * const callback = (err, change, apiResponse) => {
     *   if (!err) {
     *     // Delete change modification was created.
     *   }
     * };
     *
     * zone.deleteRecords(oldARecord, callback);
     *
     * //-
     * // Delete multiple records at once.
     * //-
     * const oldNs1Record = zone.record('ns', {
     *   name: 'example.com.',
     *   data: 'ns-cloud1.googledomains.com.',
     *   ttl: 86400
     * });
     *
     * const oldNs2Record = zone.record('ns', {
     *   name: 'example.com.',
     *   data: 'ns-cloud2.googledomains.com.',
     *   ttl: 86400
     * });
     *
     * zone.deleteRecords([
     *   oldNs1Record,
     *   oldNs2Record
     * ], callback);
     *
     * //-
     * // Possibly a simpler way to perform the above change is deleting records
     * by
     * // type.
     * //-
     * zone.deleteRecords('ns', callback);
     *
     * //-
     * // You can also delete records of multiple types.
     * //-
     * zone.deleteRecords(['ns', 'a'], callback);
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.deleteRecords(oldARecord).then((data) => {
     *   const change = data[0];
     *   const apiResponse = data[1];
     * });
     */
    deleteRecords(recordsOrCallback, callback) {
        let records;
        if (typeof recordsOrCallback === 'function') {
            callback = recordsOrCallback;
            records = [];
        }
        else {
            records = arrify(recordsOrCallback);
        }
        if (typeof records[0] === 'string') {
            this.deleteRecordsByType_(records, callback);
            return;
        }
        this.createChange({ delete: records }, callback);
    }
    /**
     * Emptying your zone means leaving only 'NS' and 'SOA' records. This method
     * will first fetch the non-NS and non-SOA records from your zone using
     * {@link Zone#getRecords}, then use {@link Zone#createChange} to
     * create a deletion change.
     *
     * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create}
     *
     * @param {CreateChangeCallback} [callback] Callback function.
     * @returns {Promise<CreateChangeResponse>}
     */
    empty(callback) {
        this.getRecords((err, records) => {
            if (err) {
                callback(err);
                return;
            }
            const recordsToDelete = records.filter(record => {
                return record.type !== 'NS' && record.type !== 'SOA';
            });
            if (recordsToDelete.length === 0) {
                callback(null);
            }
            else {
                this.deleteRecords(recordsToDelete, callback);
            }
        });
    }
    /**
     * Provide a path to a zone file to copy records into the zone.
     *
     * @see [ResourceRecordSets: list API Documentation]{@link https://cloud.google.com/dns/api/v1/resourceRecordSets/list}
     *
     * @param {string} localPath The fully qualified path to the zone file.
     * @param {ZoneExportCallback} [callback] Callback function.
     * @returns {Promise<ZoneExportResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     *
     * const zoneFilename = '/Users/stephen/zonefile.zone';
     *
     * zone.export(zoneFilename, err => {
     *   if (!err) {
     *     // The zone file was created successfully.
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.export(zoneFilename).then(() => {});
     */
    export(localPath, callback) {
        this.getRecords((err, records) => {
            if (err) {
                callback(err);
                return;
            }
            const stringRecords = records.map(x => x.toString()).join('\n');
            fs.writeFile(localPath, stringRecords, 'utf-8', err => {
                callback(err || null);
            });
        });
    }
    /**
     * Get the list of changes associated with this zone. A change is an atomic
     * update to a collection of records.
     *
     * @see [Changes: get API Documentation]{@link https://cloud.google.com/dns/api/v1/changes/get}
     *
     * @param {GetChangesRequest} [query] Query object for listing changes.
     * @param {GetChangesCallback} [callback] Callback function.
     * @returns {Promise<GetChangesResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     *
     * const callback = (err, changes, nextQuery, apiResponse) => {
     *   // The `metadata` property is populated for you with the metadata at the
     *   // time of fetching.
     *   changes[0].metadata;
     *
     *   // However, in cases where you are concerned the metadata could have
     *   // changed, use the `getMetadata` method.
     *   changes[0].getMetadata((err, metadata) => {});
     *   if (nextQuery) {
     *     // nextQuery will be non-null if there are more results.
     *     zone.getChanges(nextQuery, callback);
     *   }
     * };
     *
     * const zone = dns.zone('zone-id');
     *
     * zone.getChanges(callback);
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.getChanges().then((data) => {
     *   const changes = data[0];
     * });
     */
    getChanges(queryOrCallback, callback) {
        let query = queryOrCallback;
        if (typeof query === 'function') {
            callback = query;
            query = {};
        }
        if (query.sort) {
            query.sortOrder = query.sort === 'asc' ? 'ascending' : 'descending';
            delete query.sort;
        }
        this.request({
            uri: '/changes',
            qs: query,
        }, (err, resp) => {
            if (err) {
                callback(err, null, null, resp);
                return;
            }
            const changes = (resp.changes || []).map((change) => {
                const changeInstance = this.change(change.id);
                changeInstance.metadata = change;
                return changeInstance;
            });
            let nextQuery = null;
            if (resp.nextPageToken) {
                nextQuery = Object.assign({}, query, {
                    pageToken: resp.nextPageToken,
                });
            }
            callback(null, changes, nextQuery, resp);
        });
    }
    /**
     * Query object for listing records.
     *
     * @typedef {object} GetRecordsRequest
     * @property {boolean} [autoPaginate=true] Have pagination handled automatically.
     * @property {number} [maxApiCalls] Maximum number of API calls to make.
     * @property {number} [maxResults] Maximum number of items plus prefixes to
     *     return.
     * @property {string} [name] Restricts the list to return only records with this
     *     fully qualified domain name.
     * @property {string} [pageToken] A previously-returned page token
     *     representing part of the larger set of results to view.
     * @property {string} [type] Restricts the list to return only records of this
     *     type. If present, the "name" parameter must also be present.
     */
    /**
     * @typedef {array} GetRecordsResponse
     * @property {Record[]} 0 Array of {@link Record} instances.
     * @property {object} 1 The full API response.
     */
    /**
     * @callback GetRecordsCallback
     * @param {?Error} err Request error, if any.
     * @param {Record[]} records Array of {@link Record} instances.
     * @param {object} apiResponse The full API response.
     */
    /**
     * Get the list of records for this zone.
     *
     * @see [ResourceRecordSets: list API Documentation]{@link https://cloud.google.com/dns/api/v1/resourceRecordSets/list}
     *
     * @param {GetRecordsRequest} [query] Query object for listing records.
     * @param {GetRecordsCallback} [callback] Callback function.
     * @returns {Promise<GetRecordsResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     *
     * const callback = (err, records, nextQuery, apiResponse) => {
     *   if (!err) {
     *     // records is an array of Record objects.
     *   }
     *
     *   if (nextQuery) {
     *     zone.getRecords(nextQuery, callback);
     *   }
     * };
     *
     * const zone = dns.zone('zone-id');
     *
     * zone.getRecords(callback);
     *
     * //-
     * // Provide a query for further customization.
     * //-
     *
     * // Get the namespace records for example.com.
     * const query = {
     *   name: 'example.com.',
     *   type: 'NS'
     * };
     *
     * zone.getRecords(query, callback);
     *
     * //-
     * // If you only want records of a specific type or types, provide them in
     * // place of the query object.
     * //-
     * zone.getRecords('ns', (err, records) => {
     *   if (!err) {
     *     // records is an array of NS Record objects in your zone.
     *   }
     * });
     *
     * //-
     * // You can also specify multiple record types.
     * //-
     * zone.getRecords(['ns', 'a', 'cname'], (err, records) => {
     *   if (!err) {
     *     // records is an array of NS, A, and CNAME records in your zone.
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.getRecords(query).then(data => {
     *   const records = data[0];
     * });
     */
    getRecords(queryOrCallback, callback) {
        let query;
        if (typeof queryOrCallback === 'function') {
            callback = queryOrCallback;
            query = [];
        }
        else {
            query = queryOrCallback;
        }
        if (typeof query === 'string' || Array.isArray(query)) {
            const filterByTypes_ = {};
            // For faster lookups, store the record types the user wants in an object.
            arrify(query).forEach(type => {
                filterByTypes_[type.toUpperCase()] = true;
            });
            query = {
                filterByTypes_,
            };
        }
        const requestQuery = Object.assign({}, query);
        delete requestQuery.filterByTypes_;
        this.request({
            uri: '/rrsets',
            qs: requestQuery,
        }, (err, resp) => {
            if (err) {
                callback(err, null, null, resp);
                return;
            }
            let records = (resp.rrsets || []).map((record) => {
                return this.record(record.type, record);
            });
            if (query.filterByTypes_) {
                records = records.filter((record) => {
                    return query.filterByTypes_[record.type];
                });
            }
            let nextQuery = null;
            if (resp.nextPageToken) {
                nextQuery = Object.assign({}, query, {
                    pageToken: resp.nextPageToken,
                });
            }
            callback(null, records, nextQuery, resp);
        });
    }
    /**
     * Copy the records from a zone file into this zone.
     *
     * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create}
     *
     * @param {string} localPath The fully qualified path to the zone file.
     * @param {CreateChangeCallback} [callback] Callback function.
     * @returns {Promise<CreateChangeResponse>}
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     *
     * const zoneFilename = '/Users/dave/zonefile.zone';
     *
     * zone.import(zoneFilename, (err, change, apiResponse) => {
     *   if (!err) {
     *     // The change was created successfully.
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.import(zoneFilename).then(data => {
     *   const change = data[0];
     *   const apiResponse = data[1];
     * });
     */
    import(localPath, callback) {
        fs.readFile(localPath, 'utf-8', (err, file) => {
            if (err) {
                callback(err);
                return;
            }
            const parsedZonefile = zonefile.parse(file);
            const defaultTTL = parsedZonefile.$ttl;
            delete parsedZonefile.$ttl;
            const recordTypes = Object.keys(parsedZonefile);
            const recordsToCreate = [];
            recordTypes.forEach(recordType => {
                const recordTypeSet = arrify(parsedZonefile[recordType]);
                // tslint:disable-next-line no-any
                recordTypeSet.forEach((record) => {
                    record.ttl = record.ttl || defaultTTL;
                    recordsToCreate.push(record_1.Record.fromZoneRecord_(this, recordType, record));
                });
            });
            this.addRecords(recordsToCreate, callback);
        });
    }
    /**
     * A {@link Record} object can be used to construct a record you want to
     * add to your zone, or to refer to an existing one.
     *
     * Note that using this method will not itself make any API requests. You will
     * use the object returned in other API calls, for example to add a record to
     * your zone or to delete an existing one.
     *
     * @param {string} type The type of record to construct or the type of record
     *     you are referencing.
     * @param {object} metadata The metadata of this record.
     * @param {string} metadata.name The name of the record, e.g.
     *     `www.example.com.`.
     * @param {string[]} metadata.data Defined in
     *     [RFC 1035, section 5](https://goo.gl/9EiM0e) and
     *     [RFC 1034, section 3.6.1](https://goo.gl/Hwhsu9).
     * @param {number} metadata.ttl Seconds that the resource is cached by
     *     resolvers.
     * @returns {Record}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     *
     * const zone = dns.zone('zone-id');
     *
     * //-
     * // Reference an existing record to delete from your zone.
     * //-
     * const oldARecord = zone.record('a', {
     *   name: 'example.com.',
     *   data: '1.2.3.4',
     *   ttl: 86400
     * });
     *
     * //-
     * // Construct a record to add to your zone.
     * //-
     * const newARecord = zone.record('a', {
     *   name: 'example.com.',
     *   data: '5.6.7.8',
     *   ttl: 86400
     * });
     *
     * //-
     * // Use these records together to create a change.
     * //-
     * zone.createChange({
     *   add: newARecord,
     *   delete: oldARecord
     * }, (err, change, apiResponse) => {});
     */
    record(type, metadata) {
        return new record_1.Record(this, type, metadata);
    }
    /**
     * Provide a record type that should be deleted and replaced with other
     * records.
     *
     * **This is not an atomic request.** Two API requests are made
     * (one to get records of the type that you've requested, then another to
     * replace them), which means the operation is not atomic and could result in
     * unexpected changes.
     *
     * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create}
     *
     * @param {string|string[]} recordTypes The type(s) of records to replace.
     * @param {Record|Record[]} newRecords The {@link Record} object(s) to add.
     * @param {CreateChangeCallback} [callback] Callback function.
     * @returns {Promise<CreateChangeResponse>}
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     *
     * const zone = dns.zone('zone-id');
     *
     * const newNs1Record = zone.record('ns', {
     *   name: 'example.com.',
     *   data: 'ns-cloud1.googledomains.com.',
     *   ttl: 86400
     * });
     *
     * const newNs2Record = zone.record('ns', {
     *   name: 'example.com.',
     *   data: 'ns-cloud2.googledomains.com.',
     *   ttl: 86400
     * });
     *
     * const newNsRecords = [
     *   newNs1Record,
     *   newNs2Record
     * ];
     *
     * zone.replaceRecords('ns', newNsRecords, (err, change, apiResponse) => {
     *   if (!err) {
     *     // The change was created successfully.
     *   }
     * });
     *
     * //-
     * // If the callback is omitted, we'll return a Promise.
     * //-
     * zone.replaceRecords('ns', newNsRecords).then(data => {
     *   const change = data[0];
     *   const apiResponse = data[1];
     * });
     */
    replaceRecords(recordType, newRecords, callback) {
        this.getRecords(recordType, (err, recordsToDelete) => {
            if (err) {
                callback(err);
                return;
            }
            this.createChange({
                add: newRecords,
                delete: recordsToDelete,
            }, callback);
        });
    }
    /**
     * Delete records from the zone matching an array of types.
     *
     * @private
     *
     * @param {string[]} recordTypes Types of records to delete. Ex: 'NS', 'A'.
     * @param {function} callback Callback function.
     *
     * @example
     * const {DNS} = require('@google-cloud/dns');
     * const dns = new DNS();
     * const zone = dns.zone('zone-id');
     * zone.deleteRecordsByType_(['NS', 'A'], (err, change, apiResponse) => {
     *   if (!err) {
     *     // The change was created successfully.
     *   }
     * });
     */
    deleteRecordsByType_(recordTypes, callback) {
        this.getRecords(recordTypes, (err, records) => {
            if (err) {
                callback(err);
                return;
            }
            if (records.length === 0) {
                callback(null);
                return;
            }
            this.deleteRecords(records, callback);
        });
    }
}
exports.Zone = Zone;
/**
 * Get the list of {@link Change} objects associated with this zone as a
 * readable object stream.
 *
 * @method Zone#getChangesStream
 * @param {GetChangesRequest} [query] Query object for listing changes.
 * @returns {ReadableStream} A readable stream that emits {@link Change}
 *     instances.
 *
 * @example
 * zone.getChangesStream()
 *   .on('error', console.error)
 *   .on('data', change => {
 *     // change is a Change object.
 *   })
 *   .on('end', () => {
 *     // All changes retrieved.
 *   });
 *
 * //-
 * // If you anticipate many results, you can end a stream early to prevent
 * // unnecessary processing and API requests.
 * //-
 * zone.getChangesStream()
 *   .on('data', function(change) {
 *     this.end();
 *   });
 */
/**
 * Get the list of {module:dns/record} objects for this zone as a readable
 * object stream.
 *
 * @method Zone#getRecordsStream
 * @param {GetRecordsRequest} [query] Query object for listing records.
 * @returns {ReadableStream} A readable stream that emits {@link Record}
 *     instances.
 *
 * @example
 * const {DNS} = require('@google-cloud/dns');
 * const dns = new DNS();
 * const zone = dns.zone('zone-id');
 *
 * zone.getRecordsStream()
 *   .on('error', console.error)
 *   .on('data', record => {
 *     // record is a Record object.
 *   })
 *   .on('end', () => {
 *     // All records retrieved.
 *   });
 *
 * //-
 * // If you anticipate many results, you can end a stream early to prevent
 * // unnecessary processing and API requests.
 * //-
 * zone.getRecordsStream()
 *   .on('data', function(change) {
 *     this.end();
 *   });
 */
/*! Developer Documentation
 *
 * These methods can be auto-paginated.
 */
paginator_1.paginator.extend(Zone, ['getChanges', 'getRecords']);
/*! Developer Documentation
 *
 * All async methods (except for streams) will return a Promise in the event
 * that a callback is omitted.
 */
promisify_1.promisifyAll(Zone, {
    exclude: ['change', 'record'],
});
//# sourceMappingURL=zone.js.map