diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d79b129c56a..aba7e116cdb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -56,6 +56,7 @@ "packages/google-cloud-dialogflow": "6.1.0", "packages/google-cloud-dialogflow-cx": "4.1.0", "packages/google-cloud-discoveryengine": "1.1.0", + "packages/google-cloud-dns": "4.0.0", "packages/google-cloud-documentai": "8.0.1", "packages/google-cloud-domains": "3.0.1", "packages/google-cloud-essentialcontacts": "3.0.1", diff --git a/packages/google-cloud-dns/.OwlBot.yaml b/packages/google-cloud-dns/.OwlBot.yaml new file mode 100644 index 00000000000..46bbeee9f9f --- /dev/null +++ b/packages/google-cloud-dns/.OwlBot.yaml @@ -0,0 +1,17 @@ +# Copyright 2021 Google LLC +# +# 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. + + +begin-after-commit-hash: 397c0bfd367a2427104f988d5329bc117caafd95 + diff --git a/packages/google-cloud-dns/.eslintignore b/packages/google-cloud-dns/.eslintignore new file mode 100644 index 00000000000..ea5b04aebe6 --- /dev/null +++ b/packages/google-cloud-dns/.eslintignore @@ -0,0 +1,7 @@ +**/node_modules +**/coverage +test/fixtures +build/ +docs/ +protos/ +samples/generated/ diff --git a/packages/google-cloud-dns/.eslintrc.json b/packages/google-cloud-dns/.eslintrc.json new file mode 100644 index 00000000000..78215349546 --- /dev/null +++ b/packages/google-cloud-dns/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/gts" +} diff --git a/packages/google-cloud-dns/.gitattributes b/packages/google-cloud-dns/.gitattributes new file mode 100644 index 00000000000..33739cb74e4 --- /dev/null +++ b/packages/google-cloud-dns/.gitattributes @@ -0,0 +1,4 @@ +*.ts text eol=lf +*.js text eol=lf +protos/* linguist-generated +**/api-extractor.json linguist-language=JSON-with-Comments diff --git a/packages/google-cloud-dns/.gitignore b/packages/google-cloud-dns/.gitignore new file mode 100644 index 00000000000..2036b7c6583 --- /dev/null +++ b/packages/google-cloud-dns/.gitignore @@ -0,0 +1,13 @@ +**/*.log +**/node_modules +.coverage +.nyc_output +docs/ +out/ +system-test/secrets.js +system-test/*key.json +*.lock +package-lock.json +build/ +__pycache__ +.DS_Store \ No newline at end of file diff --git a/packages/google-cloud-dns/.jsdoc.js b/packages/google-cloud-dns/.jsdoc.js new file mode 100644 index 00000000000..605cb7c914b --- /dev/null +++ b/packages/google-cloud-dns/.jsdoc.js @@ -0,0 +1,51 @@ +// Copyright 2019 Google LLC +// +// 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 +// +// https://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. +// + +'use strict'; + +module.exports = { + opts: { + readme: './README.md', + package: './package.json', + template: './node_modules/jsdoc-fresh', + recurse: true, + verbose: true, + destination: './docs/' + }, + plugins: [ + 'plugins/markdown', + 'jsdoc-region-tag' + ], + source: { + excludePattern: '(^|\\/|\\\\)[._]', + include: [ + 'build/src' + ], + includePattern: '\\.js$' + }, + templates: { + copyright: 'Copyright 2019 Google, LLC.', + includeDate: false, + sourceFiles: false, + systemName: '@google-cloud/dns', + theme: 'lumen', + default: { + "outputSourceFiles": false + } + }, + markdown: { + idInHeadings: true + } +}; diff --git a/packages/google-cloud-dns/.mocharc.js b/packages/google-cloud-dns/.mocharc.js new file mode 100644 index 00000000000..49e7e228701 --- /dev/null +++ b/packages/google-cloud-dns/.mocharc.js @@ -0,0 +1,29 @@ +// Copyright 2023 Google LLC +// +// 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. +const config = { + "enable-source-maps": true, + "throw-deprecation": true, + "timeout": 10000, + "recursive": true +} +if (process.env.MOCHA_THROW_DEPRECATION === 'false') { + delete config['throw-deprecation']; +} +if (process.env.MOCHA_REPORTER) { + config.reporter = process.env.MOCHA_REPORTER; +} +if (process.env.MOCHA_REPORTER_OUTPUT) { + config['reporter-option'] = `output=${process.env.MOCHA_REPORTER_OUTPUT}`; +} +module.exports = config diff --git a/packages/google-cloud-dns/.nycrc b/packages/google-cloud-dns/.nycrc new file mode 100644 index 00000000000..b18d5472b62 --- /dev/null +++ b/packages/google-cloud-dns/.nycrc @@ -0,0 +1,24 @@ +{ + "report-dir": "./.coverage", + "reporter": ["text", "lcov"], + "exclude": [ + "**/*-test", + "**/.coverage", + "**/apis", + "**/benchmark", + "**/conformance", + "**/docs", + "**/samples", + "**/scripts", + "**/protos", + "**/test", + "**/*.d.ts", + ".jsdoc.js", + "**/.jsdoc.js", + "karma.conf.js", + "webpack-tests.config.js", + "webpack.config.js" + ], + "exclude-after-remap": false, + "all": true +} diff --git a/packages/google-cloud-dns/.prettierignore b/packages/google-cloud-dns/.prettierignore new file mode 100644 index 00000000000..9340ad9b86d --- /dev/null +++ b/packages/google-cloud-dns/.prettierignore @@ -0,0 +1,6 @@ +**/node_modules +**/coverage +test/fixtures +build/ +docs/ +protos/ diff --git a/packages/google-cloud-dns/.prettierrc.js b/packages/google-cloud-dns/.prettierrc.js new file mode 100644 index 00000000000..1e6cec783e4 --- /dev/null +++ b/packages/google-cloud-dns/.prettierrc.js @@ -0,0 +1,17 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +module.exports = { + ...require('gts/.prettierrc.json') +} diff --git a/packages/google-cloud-dns/.repo-metadata.json b/packages/google-cloud-dns/.repo-metadata.json new file mode 100644 index 00000000000..d2f8c93fe56 --- /dev/null +++ b/packages/google-cloud-dns/.repo-metadata.json @@ -0,0 +1,15 @@ +{ + "name": "dns", + "name_pretty": "Cloud DNS", + "product_documentation": "https://cloud.google.com/dns", + "client_documentation": "https://cloud.google.com/nodejs/docs/reference/dns/latest", + "issue_tracker": "https://issuetracker.google.com/savedsearches/559772", + "release_level": "stable", + "language": "nodejs", + "repo": "googleapis/google-cloud-node", + "distribution_name": "@google-cloud/dns", + "api_id": "dns.googleapis.com", + "requires_billing": true, + "api_shortname": "dns", + "library_type": "REST" +} diff --git a/packages/google-cloud-dns/CHANGELOG.md b/packages/google-cloud-dns/CHANGELOG.md new file mode 100644 index 00000000000..5675bb3c88c --- /dev/null +++ b/packages/google-cloud-dns/CHANGELOG.md @@ -0,0 +1,446 @@ +# Changelog + +[npm history][1] + +[1]: https://www.npmjs.com/package/@google-cloud/dns?activeTab=versions + +## [4.0.0](https://github.com/googleapis/nodejs-dns/compare/v3.0.2...v4.0.0) (2023-08-10) + + +### ⚠ BREAKING CHANGES + +* upgrade to Node 14 ([#556](https://github.com/googleapis/nodejs-dns/issues/556)) + +### Miscellaneous Chores + +* Upgrade to Node 14 ([#556](https://github.com/googleapis/nodejs-dns/issues/556)) ([75b5a56](https://github.com/googleapis/nodejs-dns/commit/75b5a56993e74826b69650c8c7601e977802b15f)) + +## [3.0.2](https://github.com/googleapis/nodejs-dns/compare/v3.0.1...v3.0.2) (2022-08-23) + + +### Bug Fixes + +* remove pip install statements ([#1546](https://github.com/googleapis/nodejs-dns/issues/1546)) ([#534](https://github.com/googleapis/nodejs-dns/issues/534)) ([b3a77f0](https://github.com/googleapis/nodejs-dns/commit/b3a77f071df17739c2fcd682f181ac5bdddffae6)) + +## [3.0.1](https://github.com/googleapis/nodejs-dns/compare/v3.0.0...v3.0.1) (2022-06-09) + + +### Bug Fixes + +* **deps:** update dependency @google-cloud/common to v4 ([#529](https://github.com/googleapis/nodejs-dns/issues/529)) ([4e8b02f](https://github.com/googleapis/nodejs-dns/commit/4e8b02ff70ce3158d10cf37970f4d9ea371a2fcb)) + +## [3.0.0](https://github.com/googleapis/nodejs-dns/compare/v2.2.4...v3.0.0) (2022-06-06) + + +### ⚠ BREAKING CHANGES + +* update library to use Node 12 (#524) + +### Bug Fixes + +* **deps:** update dependency @google-cloud/paginator to v4 ([#525](https://github.com/googleapis/nodejs-dns/issues/525)) ([e82ead7](https://github.com/googleapis/nodejs-dns/commit/e82ead7460db28e4a434ba321e1e1d0e3f7b12ed)) +* **deps:** update dependency @google-cloud/promisify to v3 ([#523](https://github.com/googleapis/nodejs-dns/issues/523)) ([7cfb445](https://github.com/googleapis/nodejs-dns/commit/7cfb445dd432ccdc71d13ef1eea43797dc898157)) + + +### Build System + +* update library to use Node 12 ([#524](https://github.com/googleapis/nodejs-dns/issues/524)) ([ab3ce39](https://github.com/googleapis/nodejs-dns/commit/ab3ce39f2257cde3740bd51302667d2f24111aa8)) + +### [2.2.4](https://www.github.com/googleapis/nodejs-dns/compare/v2.2.3...v2.2.4) (2022-01-06) + + +### Bug Fixes + +* **deps:** update dependency dns-zonefile to v0.2.10 ([#503](https://www.github.com/googleapis/nodejs-dns/issues/503)) ([dc59d1c](https://www.github.com/googleapis/nodejs-dns/commit/dc59d1c8c733348e8500a751a71ccb0a9b13656d)) + +### [2.2.3](https://www.github.com/googleapis/nodejs-dns/compare/v2.2.2...v2.2.3) (2021-09-22) + + +### Bug Fixes + +* **deps:** update dependency dns-zonefile to v0.2.9 ([#487](https://www.github.com/googleapis/nodejs-dns/issues/487)) ([53788bd](https://www.github.com/googleapis/nodejs-dns/commit/53788bd5676bf0f5dfd35dde18c912eb98247aa1)) + +### [2.2.2](https://www.github.com/googleapis/nodejs-dns/compare/v2.2.1...v2.2.2) (2021-08-09) + + +### Bug Fixes + +* **build:** migrate to using main branch ([#478](https://www.github.com/googleapis/nodejs-dns/issues/478)) ([2f19b00](https://www.github.com/googleapis/nodejs-dns/commit/2f19b0066a26910262abff94974c55cda41b3fae)) + +### [2.2.1](https://www.github.com/googleapis/nodejs-dns/compare/v2.2.0...v2.2.1) (2021-08-06) + + +### Bug Fixes + +* **deps:** update dependency dns-zonefile to v0.2.8 ([#469](https://www.github.com/googleapis/nodejs-dns/issues/469)) ([3a9bb0e](https://www.github.com/googleapis/nodejs-dns/commit/3a9bb0ebcb9a77fbfe7ee911929c780d5ae2d28b)) + +## [2.2.0](https://www.github.com/googleapis/nodejs-dns/compare/v2.1.0...v2.2.0) (2021-06-07) + + +### Features + +* add `gcf-owl-bot[bot]` to `ignoreAuthors` ([#452](https://www.github.com/googleapis/nodejs-dns/issues/452)) ([6148f03](https://www.github.com/googleapis/nodejs-dns/commit/6148f03b37be1960bd5ed6ce32d696a8c0acdd2f)) + +## [2.1.0](https://www.github.com/googleapis/nodejs-dns/compare/v2.0.2...v2.1.0) (2020-12-22) + + +### Features + +* add support for setting DNS Sec ([#439](https://www.github.com/googleapis/nodejs-dns/issues/439)) ([32792e1](https://www.github.com/googleapis/nodejs-dns/commit/32792e1659bb7df5b15a65b5ff3f0c6f5e80b949)) + +### [2.0.2](https://www.github.com/googleapis/nodejs-dns/compare/v2.0.1...v2.0.2) (2020-07-09) + + +### Bug Fixes + +* typeo in nodejs .gitattribute ([#401](https://www.github.com/googleapis/nodejs-dns/issues/401)) ([c02a9da](https://www.github.com/googleapis/nodejs-dns/commit/c02a9daa5290922928f313a59a59af0f244d8eb9)) + +### [2.0.1](https://www.github.com/googleapis/nodejs-dns/compare/v2.0.0...v2.0.1) (2020-06-04) + + +### Bug Fixes + +* **autosynth:** synthesis was failing due to breaking TypeScript changes ([#392](https://www.github.com/googleapis/nodejs-dns/issues/392)) ([cf32c50](https://www.github.com/googleapis/nodejs-dns/commit/cf32c506c878052867abbc148818f1f3e2747c1c)) + +## [2.0.0](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.9...v2.0.0) (2020-04-14) + + +### ⚠ BREAKING CHANGES + +* update to latest version of gts and typescript (#376) +* require node 10 in engines field (#374) + +### Features + +* require node 10 in engines field ([#374](https://www.github.com/googleapis/nodejs-dns/issues/374)) ([aba0475](https://www.github.com/googleapis/nodejs-dns/commit/aba047555b12b8c6a243581fc65579381d497b1f)) + + +### Bug Fixes + +* apache license URL ([#468](https://www.github.com/googleapis/nodejs-dns/issues/468)) ([#372](https://www.github.com/googleapis/nodejs-dns/issues/372)) ([32e13a1](https://www.github.com/googleapis/nodejs-dns/commit/32e13a10bdcbea7abaa25add7cb133e5bf854c60)) +* **deps:** update dependency @google-cloud/common to v3 ([#367](https://www.github.com/googleapis/nodejs-dns/issues/367)) ([3d8b083](https://www.github.com/googleapis/nodejs-dns/commit/3d8b08380e56dfb62d7b596fde9624119d08b4ec)) +* **deps:** update dependency @google-cloud/paginator to v3 ([#365](https://www.github.com/googleapis/nodejs-dns/issues/365)) ([25ea868](https://www.github.com/googleapis/nodejs-dns/commit/25ea86858f27afbcda7bf8370ac742d0a8abbcb6)) +* **deps:** update dependency @google-cloud/promisify to v2 ([#364](https://www.github.com/googleapis/nodejs-dns/issues/364)) ([2f732bf](https://www.github.com/googleapis/nodejs-dns/commit/2f732bf6fdc44c0f7c471126935ddb62cba5fbd3)) + + +### Build System + +* update to latest version of gts and typescript ([#376](https://www.github.com/googleapis/nodejs-dns/issues/376)) ([c35b94e](https://www.github.com/googleapis/nodejs-dns/commit/c35b94e4a49c5d2644aa64fe4ad85b2de4002e01)) + +### [1.2.9](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.8...v1.2.9) (2020-03-17) + + +### Bug Fixes + +* **deps:** update dependency dns-zonefile to v0.2.6 ([#351](https://www.github.com/googleapis/nodejs-dns/issues/351)) ([437505b](https://www.github.com/googleapis/nodejs-dns/commit/437505b52ab43d7beeb5ce3f5bd6266c1067db96)) + +### [1.2.8](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.7...v1.2.8) (2019-12-05) + + +### Bug Fixes + +* **deps:** pin TypeScript below 3.7.0 ([609120a](https://www.github.com/googleapis/nodejs-dns/commit/609120a5cf39a54f0187fb0a6e4907548a488aa0)) + +### [1.2.7](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.6...v1.2.7) (2019-11-19) + + +### Bug Fixes + +* **docs:** bump release level to GA ([#319](https://www.github.com/googleapis/nodejs-dns/issues/319)) ([8de7f06](https://www.github.com/googleapis/nodejs-dns/commit/8de7f06c178ce07e106f21b922fc9a86ada7964c)) + +### [1.2.6](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.5...v1.2.6) (2019-11-15) + + +### Bug Fixes + +* **docs:** bump release level to beta ([f8ffa40](https://www.github.com/googleapis/nodejs-dns/commit/f8ffa401842cce93565ab8f5aedc47289f62cf39)) +* **docs:** snippets are now replaced in jsdoc comments ([#314](https://www.github.com/googleapis/nodejs-dns/issues/314)) ([06cb858](https://www.github.com/googleapis/nodejs-dns/commit/06cb858d1623b7382c794805571b8311916e20e2)) + +### [1.2.5](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.4...v1.2.5) (2019-10-21) + + +### Bug Fixes + +* **typescript:** make dnsName a required field ([#310](https://www.github.com/googleapis/nodejs-dns/issues/310)) ([887f42a](https://www.github.com/googleapis/nodejs-dns/commit/887f42a7a9389460cc566293d7525b94eccb39e4)) + +### [1.2.4](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.3...v1.2.4) (2019-10-16) + + +### Bug Fixes + +* **typescript:** add correct type safety to zone#create ([#307](https://www.github.com/googleapis/nodejs-dns/issues/307)) ([d5b78b8](https://www.github.com/googleapis/nodejs-dns/commit/d5b78b836d02e7c990b9ae51c932a8b93b451520)) + +### [1.2.3](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.2...v1.2.3) (2019-09-11) + + +### Bug Fixes + +* **deps:** update dependency dns-zonefile to v0.2.3 ([#296](https://www.github.com/googleapis/nodejs-dns/issues/296)) ([06b3f04](https://www.github.com/googleapis/nodejs-dns/commit/06b3f04)) + +### [1.2.2](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.1...v1.2.2) (2019-09-06) + + +### Bug Fixes + +* **docs:** remove anchor from reference doc link ([8e68f6e](https://www.github.com/googleapis/nodejs-dns/commit/8e68f6e)) + +### [1.2.1](https://www.github.com/googleapis/nodejs-dns/compare/v1.2.0...v1.2.1) (2019-07-26) + + +### Bug Fixes + +* **deps:** update dependency @google-cloud/paginator to v2 ([#286](https://www.github.com/googleapis/nodejs-dns/issues/286)) ([bc30fcc](https://www.github.com/googleapis/nodejs-dns/commit/bc30fcc)) + +## [1.2.0](https://www.github.com/googleapis/nodejs-dns/compare/v1.1.2...v1.2.0) (2019-07-14) + + +### Features + +* switch to dns endpoint (options.apiEndpoint is available if time is needed to migrate) ([#283](https://www.github.com/googleapis/nodejs-dns/issues/283)) ([5c180b4](https://www.github.com/googleapis/nodejs-dns/commit/5c180b4)) + +### [1.1.2](https://www.github.com/googleapis/nodejs-dns/compare/v1.1.1...v1.1.2) (2019-06-26) + + +### Bug Fixes + +* **docs:** link to reference docs section on googleapis.dev ([#279](https://www.github.com/googleapis/nodejs-dns/issues/279)) ([a13e5a1](https://www.github.com/googleapis/nodejs-dns/commit/a13e5a1)) + +### [1.1.1](https://www.github.com/googleapis/nodejs-dns/compare/v1.1.0...v1.1.1) (2019-06-14) + + +### Bug Fixes + +* **docs:** move to new client docs URL ([#276](https://www.github.com/googleapis/nodejs-dns/issues/276)) ([53cebe1](https://www.github.com/googleapis/nodejs-dns/commit/53cebe1)) + +## [1.1.0](https://www.github.com/googleapis/nodejs-dns/compare/v1.0.1...v1.1.0) (2019-06-05) + + +### Features + +* support apiEndpoint overrides ([#272](https://www.github.com/googleapis/nodejs-dns/issues/272)) ([2c42f20](https://www.github.com/googleapis/nodejs-dns/commit/2c42f20)) + +### [1.0.1](https://www.github.com/googleapis/nodejs-dns/compare/v1.0.0...v1.0.1) (2019-05-24) + + +### Bug Fixes + +* **types:** use Metadata type instead of r.Response ([#266](https://www.github.com/googleapis/nodejs-dns/issues/266)) ([cf32552](https://www.github.com/googleapis/nodejs-dns/commit/cf32552)) + +## [1.0.0](https://www.github.com/googleapis/nodejs-dns/compare/v0.9.2...v1.0.0) (2019-05-20) + + +### ⚠ BREAKING CHANGES + +* upgrade engines field to >=8.10.0 (#246) + +### Bug Fixes + +* **deps:** update dependency @google-cloud/common to ^0.32.0 ([#238](https://www.github.com/googleapis/nodejs-dns/issues/238)) ([e1ba124](https://www.github.com/googleapis/nodejs-dns/commit/e1ba124)) +* **deps:** update dependency @google-cloud/common to v1 ([#257](https://www.github.com/googleapis/nodejs-dns/issues/257)) ([f8f7398](https://www.github.com/googleapis/nodejs-dns/commit/f8f7398)) +* **deps:** update dependency @google-cloud/paginator to v1 ([#250](https://www.github.com/googleapis/nodejs-dns/issues/250)) ([002e85a](https://www.github.com/googleapis/nodejs-dns/commit/002e85a)) +* **deps:** update dependency @google-cloud/promisify to v1 ([#249](https://www.github.com/googleapis/nodejs-dns/issues/249)) ([25319bf](https://www.github.com/googleapis/nodejs-dns/commit/25319bf)) +* **deps:** update dependency arrify to v2 ([#240](https://www.github.com/googleapis/nodejs-dns/issues/240)) ([5f86971](https://www.github.com/googleapis/nodejs-dns/commit/5f86971)) + + +### Build System + +* upgrade engines field to >=8.10.0 ([#246](https://www.github.com/googleapis/nodejs-dns/issues/246)) ([30d840b](https://www.github.com/googleapis/nodejs-dns/commit/30d840b)) + +## v0.9.2 + +03-12-2019 13:47 PDT + +This patch release has a few bug fixes, and dependency updates. Enjoy! + +### Bug Fixes +- fix: improve types for zone.get ([#201](https://github.com/googleapis/nodejs-dns/pull/201)) + +### Dependencies +- fix(deps): update dependency @google-cloud/paginator to ^0.2.0 +- fix(deps): update dependency @google-cloud/promisify to ^0.4.0 ([#218](https://github.com/googleapis/nodejs-dns/pull/218)) +- fix(deps): update dependency @google-cloud/common to ^0.31.0 ([#209](https://github.com/googleapis/nodejs-dns/pull/209)) + +### Documentation +- docs: update links in contrib guide ([#219](https://github.com/googleapis/nodejs-dns/pull/219)) +- docs: update contributing path in README ([#212](https://github.com/googleapis/nodejs-dns/pull/212)) +- docs: move CONTRIBUTING.md to root ([#211](https://github.com/googleapis/nodejs-dns/pull/211)) +- docs: add lint/fix example to contributing guide ([#208](https://github.com/googleapis/nodejs-dns/pull/208)) + +### Internal / Testing Changes +- build: Add docuploader credentials to node publish jobs ([#223](https://github.com/googleapis/nodejs-dns/pull/223)) +- build: use node10 to run samples-test, system-test etc ([#222](https://github.com/googleapis/nodejs-dns/pull/222)) +- build: update release configuration +- chore(deps): update dependency mocha to v6 +- build: use linkinator for docs test ([#216](https://github.com/googleapis/nodejs-dns/pull/216)) +- chore(deps): update dependency @types/tmp to v0.0.34 ([#217](https://github.com/googleapis/nodejs-dns/pull/217)) +- fix(deps): update dependency yargs to v13 ([#215](https://github.com/googleapis/nodejs-dns/pull/215)) +- build: create docs test npm scripts ([#214](https://github.com/googleapis/nodejs-dns/pull/214)) +- build: test using @grpc/grpc-js in CI ([#213](https://github.com/googleapis/nodejs-dns/pull/213)) +- chore(deps): update dependency eslint-config-prettier to v4 ([#206](https://github.com/googleapis/nodejs-dns/pull/206)) +- build: ignore googleapis.com in doc link check ([#204](https://github.com/googleapis/nodejs-dns/pull/204)) +- build: check broken links in generated docs ([#202](https://github.com/googleapis/nodejs-dns/pull/202)) +- refactor: modernize the sample tests ([#199](https://github.com/googleapis/nodejs-dns/pull/199)) + +## v0.9.1 + +### Dependencies +- chore(deps): update dependency typescript to ~3.2.0 ([#177](https://github.com/googleapis/nodejs-dns/pull/177)) +- fix(deps): update dependency @google-cloud/common to ^0.27.0 ([#176](https://github.com/googleapis/nodejs-dns/pull/176)) + +### Documentation +- fix(docs): place doc comment above the last overload ([#187](https://github.com/googleapis/nodejs-dns/pull/187)) +- docs: update readme badges ([#180](https://github.com/googleapis/nodejs-dns/pull/180)) + +### Internal / Testing Changes +- chore: always nyc report before calling codecov ([#185](https://github.com/googleapis/nodejs-dns/pull/185)) +- chore: nyc ignore build/test by default ([#184](https://github.com/googleapis/nodejs-dns/pull/184)) +- chore: update license file ([#182](https://github.com/googleapis/nodejs-dns/pull/182)) +- fix(build): fix system key decryption ([#178](https://github.com/googleapis/nodejs-dns/pull/178)) +- chore: add synth.metadata + +## v0.9.0 + +11-16-2018 14:11 PST + +### Bug fixes +- fix: fix typescript build ([#171](https://github.com/googleapis/nodejs-dns/pull/171)) +- refactor: remove dependency on extend ([#163](https://github.com/googleapis/nodejs-dns/pull/163)) +- refactor: reduce the number of deps ([#161](https://github.com/googleapis/nodejs-dns/pull/161)) +- fix(deps): update dependency @google-cloud/common to ^0.26.0 ([#145](https://github.com/googleapis/nodejs-dns/pull/145)) +- fix(types): A few more promisified method overloads ([#134](https://github.com/googleapis/nodejs-dns/pull/134)) +- fix(types): Consolidate "createChange" wrappers, add overloads ([#133](https://github.com/googleapis/nodejs-dns/pull/133)) +- fix(docs): add missing methods jsdoc declaration ([#129](https://github.com/googleapis/nodejs-dns/pull/129)) +- docs: First argument of Zone#create is required ([#125](https://github.com/googleapis/nodejs-dns/pull/125)) +- fix: Don't publish sourcemaps ([#120](https://github.com/googleapis/nodejs-dns/pull/120)) + +### Internal / Testing Changes +- chore(deps): update dependency gts to ^0.9.0 ([#169](https://github.com/googleapis/nodejs-dns/pull/169)) +- chore: update eslintignore config ([#168](https://github.com/googleapis/nodejs-dns/pull/168)) +- refactor(samples): convert samples test from ava to mocha ([#154](https://github.com/googleapis/nodejs-dns/pull/154)) +- chore(deps): update dependency @google-cloud/nodejs-repo-tools to v3 ([#165](https://github.com/googleapis/nodejs-dns/pull/165)) +- chore: drop contributors from multiple places ([#164](https://github.com/googleapis/nodejs-dns/pull/164)) +- chore(deps): update dependency @types/is to v0.0.21 ([#162](https://github.com/googleapis/nodejs-dns/pull/162)) +- chore: use latest npm on Windows ([#160](https://github.com/googleapis/nodejs-dns/pull/160)) +- docs(samples): Update samples to use async/await ([#159](https://github.com/googleapis/nodejs-dns/pull/159)) +- chore: update CircleCI config ([#158](https://github.com/googleapis/nodejs-dns/pull/158)) +- chore: include build in eslintignore ([#155](https://github.com/googleapis/nodejs-dns/pull/155)) +- chore(deps): update dependency eslint-plugin-node to v8 ([#150](https://github.com/googleapis/nodejs-dns/pull/150)) +- chore: update issue templates ([#149](https://github.com/googleapis/nodejs-dns/pull/149)) +- chore: remove old issue template ([#147](https://github.com/googleapis/nodejs-dns/pull/147)) +- build: run tests on node11 ([#146](https://github.com/googleapis/nodejs-dns/pull/146)) +- chores(build): do not collect sponge.xml from windows builds ([#144](https://github.com/googleapis/nodejs-dns/pull/144)) +- chores(build): run codecov on continuous builds ([#143](https://github.com/googleapis/nodejs-dns/pull/143)) +- refactor: Use @types/lodash.groupBy ([#136](https://github.com/googleapis/nodejs-dns/pull/136)) +- chore: update new issue template ([#141](https://github.com/googleapis/nodejs-dns/pull/141)) +- chore(deps): update dependency sinon to v7 ([#137](https://github.com/googleapis/nodejs-dns/pull/137)) +- build: fix codecov uploading on Kokoro ([#138](https://github.com/googleapis/nodejs-dns/pull/138)) +- Update kokoro config ([#130](https://github.com/googleapis/nodejs-dns/pull/130)) +- chore(deps): update dependency eslint-plugin-prettier to v3 ([#128](https://github.com/googleapis/nodejs-dns/pull/128)) +- chore(deps): update dependency typescript to ~3.1.0 ([#123](https://github.com/googleapis/nodejs-dns/pull/123)) +- Update CI config ([#122](https://github.com/googleapis/nodejs-dns/pull/122)) +- build: prevent system/sample-test from leaking credentials + +## v0.8.0 + +**THIS RELEASE HAS BREAKING CHANGES**. It also has a few new great features like TypeScript support. Enjoy 🎉 + +#### Support for node.js 4.x and 9.x has ended +Please upgrade to an LTS version of node. + +#### es-style imports +This module now supports es module style imports. This provides forward compatibility with TypeScript, Babel, and the new es module spec. + +#### Old code +```js +const DNS = require('@google-cloud/dns'); +const dns = new DNS(); +// OR... +const dns = require('@google-cloud/dns')(); +``` + +#### New code +```js +const {DNS} = require('@google-cloud/dns'); +const dns = new DNS(); +``` + +### Bug fixes +- fix: improve the types ([#114](https://github.com/googleapis/nodejs-dns/pull/114)) +- fix(DNS): Refine type of getZonesStream ([#113](https://github.com/googleapis/nodejs-dns/pull/113)) +- fix(DNS): Add post-promisify type overloads to DNS methods ([#112](https://github.com/googleapis/nodejs-dns/pull/112)) +- fix: Improve typescript types ([#109](https://github.com/googleapis/nodejs-dns/pull/109)) +- fix: drop support for node.js 4.x and 9.x ([#68](https://github.com/googleapis/nodejs-dns/pull/68)) +- fix: update all dependencies ([#43](https://github.com/googleapis/nodejs-dns/pull/43)) + +### New Features +- feat: use small HTTP dependency ([#93](https://github.com/googleapis/nodejs-dns/pull/93)) +- feat: Convert to TypeScript ([#97](https://github.com/googleapis/nodejs-dns/pull/97)) + +### Dependencies +- fix(deps): update dependency @google-cloud/common to ^0.25.0 ([#107](https://github.com/googleapis/nodejs-dns/pull/107)) +- chore(deps): update dependency @google-cloud/common to ^0.23.0 ([#92](https://github.com/googleapis/nodejs-dns/pull/92)) +- fix(deps): update dependency @google-cloud/common to ^0.22.0 ([#87](https://github.com/googleapis/nodejs-dns/pull/87)) +- fix(deps): update dependency @google-cloud/common to ^0.21.0 ([#82](https://github.com/googleapis/nodejs-dns/pull/82)) +- fix(package): update @google-cloud/common to version 0.20.0 ([#52](https://github.com/googleapis/nodejs-dns/pull/52)) +- Update @google-cloud/common to the latest version 🚀 ([#31](https://github.com/googleapis/nodejs-dns/pull/31)) + +### Documentation +- docs(DNS): Remove extra space in "high- performance" ([#111](https://github.com/googleapis/nodejs-dns/pull/111)) + +### Internal / Testing Changes +- fix: Remove "prettier" from "npm run fix" ([#116](https://github.com/googleapis/nodejs-dns/pull/116)) +- Update kokoro config ([#117](https://github.com/googleapis/nodejs-dns/pull/117)) +- test: remove appveyor config ([#115](https://github.com/googleapis/nodejs-dns/pull/115)) +- Update kokoro config ([#110](https://github.com/googleapis/nodejs-dns/pull/110)) +- Enable prefer-const in the eslint config ([#108](https://github.com/googleapis/nodejs-dns/pull/108)) +- Enable no-var in eslint ([#106](https://github.com/googleapis/nodejs-dns/pull/106)) +- Enable noImplicitAny ([#105](https://github.com/googleapis/nodejs-dns/pull/105)) +- Improve the types ([#103](https://github.com/googleapis/nodejs-dns/pull/103)) +- Update CI config ([#104](https://github.com/googleapis/nodejs-dns/pull/104)) +- Enable noImplicitThis ([#102](https://github.com/googleapis/nodejs-dns/pull/102)) +- chore(deps): update dependency typescript to v3 ([#101](https://github.com/googleapis/nodejs-dns/pull/101)) +- Enable gts fix ([#98](https://github.com/googleapis/nodejs-dns/pull/98)) +- Add synth script and update CI ([#96](https://github.com/googleapis/nodejs-dns/pull/96)) +- Retry npm install in CI ([#95](https://github.com/googleapis/nodejs-dns/pull/95)) +- chore(deps): update dependency nyc to v13 ([#90](https://github.com/googleapis/nodejs-dns/pull/90)) +- chore: use es classes ([#88](https://github.com/googleapis/nodejs-dns/pull/88)) +- test: fail when system-tests do not run ([#86](https://github.com/googleapis/nodejs-dns/pull/86)) +- chore(deps): update dependency eslint-config-prettier to v3 ([#85](https://github.com/googleapis/nodejs-dns/pull/85)) +- chore: split the usage of common ([#84](https://github.com/googleapis/nodejs-dns/pull/84)) +- chore: ignore package-lock.json ([#83](https://github.com/googleapis/nodejs-dns/pull/83)) +- chore(deps): lock file maintenance ([#81](https://github.com/googleapis/nodejs-dns/pull/81)) +- chore: update renovate config ([#80](https://github.com/googleapis/nodejs-dns/pull/80)) +- remove that whitespace ([#79](https://github.com/googleapis/nodejs-dns/pull/79)) +- chore(deps): lock file maintenance ([#78](https://github.com/googleapis/nodejs-dns/pull/78)) +- chore: assert.deelEqual => assert.deepStrictEqual ([#77](https://github.com/googleapis/nodejs-dns/pull/77)) +- chore: move mocha options to mocha.opts ([#75](https://github.com/googleapis/nodejs-dns/pull/75)) +- chore: require node 8 for samples ([#76](https://github.com/googleapis/nodejs-dns/pull/76)) +- chore(deps): lock file maintenance ([#74](https://github.com/googleapis/nodejs-dns/pull/74)) +- chore(deps): update dependency eslint-plugin-node to v7 ([#72](https://github.com/googleapis/nodejs-dns/pull/72)) +- test: use strictEqual in tests ([#73](https://github.com/googleapis/nodejs-dns/pull/73)) +- chore(deps): lock file maintenance ([#71](https://github.com/googleapis/nodejs-dns/pull/71)) +- chore(deps): lock file maintenance ([#70](https://github.com/googleapis/nodejs-dns/pull/70)) +- chore(deps): lock file maintenance ([#69](https://github.com/googleapis/nodejs-dns/pull/69)) +- chore(deps): lock file maintenance ([#67](https://github.com/googleapis/nodejs-dns/pull/67)) +- chore(deps): lock file maintenance ([#66](https://github.com/googleapis/nodejs-dns/pull/66)) +- chore(deps): lock file maintenance ([#65](https://github.com/googleapis/nodejs-dns/pull/65)) +- chore(deps): lock file maintenance ([#64](https://github.com/googleapis/nodejs-dns/pull/64)) +- fix(deps): update dependency yargs to v12 ([#63](https://github.com/googleapis/nodejs-dns/pull/63)) +- chore(deps): update dependency uuid to v3.3.0 ([#59](https://github.com/googleapis/nodejs-dns/pull/59)) +- chore(deps): update dependency sinon to v6 ([#60](https://github.com/googleapis/nodejs-dns/pull/60)) +- Configure Renovate ([#54](https://github.com/googleapis/nodejs-dns/pull/54)) +- refactor: drop repo-tool as an exec wrapper ([#58](https://github.com/googleapis/nodejs-dns/pull/58)) +- chore: update sample lockfiles ([#57](https://github.com/googleapis/nodejs-dns/pull/57)) +- fix: update linking for samples ([#56](https://github.com/googleapis/nodejs-dns/pull/56)) +- chore(package): update eslint to version 5.0.0 ([#55](https://github.com/googleapis/nodejs-dns/pull/55)) +- refactor: switch from var => let/const ([#53](https://github.com/googleapis/nodejs-dns/pull/53)) +- chore(package): update nyc to version 12.0.2 ([#42](https://github.com/googleapis/nodejs-dns/pull/42)) +- chore: lock files maintenance ([#40](https://github.com/googleapis/nodejs-dns/pull/40)) +- chore: timeout for system test ([#38](https://github.com/googleapis/nodejs-dns/pull/38)) +- chore: lock files maintenance ([#37](https://github.com/googleapis/nodejs-dns/pull/37)) +- chore: test on node10 ([#36](https://github.com/googleapis/nodejs-dns/pull/36)) +- chore: lock files maintenance ([#35](https://github.com/googleapis/nodejs-dns/pull/35)) +- chore: one more workaround for repo-tools EPERM ([#33](https://github.com/googleapis/nodejs-dns/pull/33)) +- chore: workaround for repo-tools EPERM ([#32](https://github.com/googleapis/nodejs-dns/pull/32)) +- chore: setup nighty build in CircleCI ([#30](https://github.com/googleapis/nodejs-dns/pull/30)) +- Upgrade repo-tools and regenerate scaffolding. ([#29](https://github.com/googleapis/nodejs-dns/pull/29)) diff --git a/packages/google-cloud-dns/CODE_OF_CONDUCT.md b/packages/google-cloud-dns/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..2add2547a81 --- /dev/null +++ b/packages/google-cloud-dns/CODE_OF_CONDUCT.md @@ -0,0 +1,94 @@ + +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project +Steward has a reasonable belief that an individual's behavior may have a +negative impact on the project or its community. + +## Conflict Resolution + +We do not believe that all conflict is bad; healthy debate and disagreement +often yield positive results. However, it is never okay to be disrespectful or +to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address +the behavior directly with those involved. Many issues can be resolved quickly +and easily, and this gives people more control over the outcome of their +dispute. If you are unable to resolve the matter for any reason, or if the +behavior is threatening or harassing, report it. We are dedicated to providing +an environment where participants feel welcome and safe. + +Reports should be directed to *googleapis-stewards@google.com*, the +Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to +receive and address reported violations of the code of conduct. They will then +work with a committee consisting of representatives from the Open Source +Programs Office and the Google Open Source Strategy team. If for any reason you +are uncomfortable reaching out to the Project Steward, please email +opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is taken. +The identity of the reporter will be omitted from the details of the report +supplied to the accused. In potentially harmful situations, such as ongoing +harassment or threats to anyone's safety, we may take action without notice. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html \ No newline at end of file diff --git a/packages/google-cloud-dns/CONTRIBUTING.md b/packages/google-cloud-dns/CONTRIBUTING.md new file mode 100644 index 00000000000..cbf3017bb6a --- /dev/null +++ b/packages/google-cloud-dns/CONTRIBUTING.md @@ -0,0 +1,76 @@ +# How to become a contributor and submit your own code + +**Table of contents** + +* [Contributor License Agreements](#contributor-license-agreements) +* [Contributing a patch](#contributing-a-patch) +* [Running the tests](#running-the-tests) +* [Releasing the library](#releasing-the-library) + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement +(CLA). + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing A Patch + +1. Submit an issue describing your proposed change to the repo in question. +1. The repo owner will respond to your issue promptly. +1. If your proposed change is accepted, and you haven't already done so, sign a + Contributor License Agreement (see details above). +1. Fork the desired repo, develop and test your code changes. +1. Ensure that your code adheres to the existing style in the code to which + you are contributing. +1. Ensure that your code has an appropriate set of tests which all pass. +1. Title your pull request following [Conventional Commits](https://www.conventionalcommits.org/) styling. +1. Submit a pull request. + +### Before you begin + +1. [Select or create a Cloud Platform project][projects]. +1. [Enable billing for your project][billing]. +1. [Enable the Cloud DNS API][enable_api]. +1. [Set up authentication with a service account][auth] so you can access the + API from your local workstation. + + +## Running the tests + +1. [Prepare your environment for Node.js setup][setup]. + +1. Install dependencies: + + npm install + +1. Run the tests: + + # Run unit tests. + npm test + + # Run sample integration tests. + npm run samples-test + + # Run all system tests. + npm run system-test + +1. Lint (and maybe fix) any changes: + + npm run fix + +[setup]: https://cloud.google.com/nodejs/docs/setup +[projects]: https://console.cloud.google.com/project +[billing]: https://support.google.com/cloud/answer/6293499#enable-billing +[enable_api]: https://console.cloud.google.com/flows/enableapi?apiid=dns.googleapis.com +[auth]: https://cloud.google.com/docs/authentication/getting-started \ No newline at end of file diff --git a/packages/google-cloud-dns/LICENSE b/packages/google-cloud-dns/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/packages/google-cloud-dns/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/packages/google-cloud-dns/README.md b/packages/google-cloud-dns/README.md new file mode 100644 index 00000000000..5097a551443 --- /dev/null +++ b/packages/google-cloud-dns/README.md @@ -0,0 +1,154 @@ +[//]: # "This README.md file is auto-generated, all changes to this file will be lost." +[//]: # "To regenerate it, use `python -m synthtool`." +Google Cloud Platform logo + +# [Cloud DNS: Node.js Client](https://github.com/googleapis/google-cloud-node/tree/main/packages/google-cloud-dns) + +[![release level](https://img.shields.io/badge/release%20level-stable-brightgreen.svg?style=flat)](https://cloud.google.com/terms/launch-stages) +[![npm version](https://img.shields.io/npm/v/@google-cloud/dns.svg)](https://www.npmjs.org/package/@google-cloud/dns) + + + + +Cloud DNS Client Library for Node.js + + +A comprehensive list of changes in each version may be found in +[the CHANGELOG](https://github.com/googleapis/google-cloud-node/tree/main/packages/google-cloud-dns/CHANGELOG.md). + +* [Cloud DNS Node.js Client API Reference][client-docs] +* [Cloud DNS Documentation][product-docs] +* [github.com/googleapis/google-cloud-node/packages/google-cloud-dns](https://github.com/googleapis/google-cloud-node/tree/main/packages/google-cloud-dns) + +Read more about the client libraries for Cloud APIs, including the older +Google APIs Client Libraries, in [Client Libraries Explained][explained]. + +[explained]: https://cloud.google.com/apis/docs/client-libraries-explained + +**Table of contents:** + + +* [Quickstart](#quickstart) + * [Before you begin](#before-you-begin) + * [Installing the client library](#installing-the-client-library) + * [Using the client library](#using-the-client-library) +* [Samples](#samples) +* [Versioning](#versioning) +* [Contributing](#contributing) +* [License](#license) + +## Quickstart + +### Before you begin + +1. [Select or create a Cloud Platform project][projects]. +1. [Enable billing for your project][billing]. +1. [Enable the Cloud DNS API][enable_api]. +1. [Set up authentication with a service account][auth] so you can access the + API from your local workstation. + +### Installing the client library + +```bash +npm install @google-cloud/dns +``` + + +### Using the client library + +```javascript +// Imports the Google Cloud client library +const {DNS} = require('@google-cloud/dns'); + +// Creates a client +const dns = new DNS(); + +async function quickstart() { + // Lists all zones in the current project + const [zones] = await dns.getZones(); + console.log('Zones:'); + zones.forEach(zone => console.log(zone.name)); +} +quickstart(); + +``` + + + +## Samples + +Samples are in the [`samples/`](https://github.com/googleapis/google-cloud-node/tree/main/packages/google-cloud-dns/samples) directory. Each sample's `README.md` has instructions for running its sample. + +| Sample | Source Code | Try it | +| --------------------------- | --------------------------------- | ------ | +| Quickstart | [source code](https://github.com/googleapis/google-cloud-node/blob/main/packages/google-cloud-dns/samples/quickstart.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-cloud-node&page=editor&open_in_editor=packages/google-cloud-dns/samples/quickstart.js,packages/google-cloud-dns/samples/README.md) | + + + +The [Cloud DNS Node.js Client API Reference][client-docs] documentation +also contains samples. + +## Supported Node.js Versions + +Our client libraries follow the [Node.js release schedule](https://github.com/nodejs/release#release-schedule). +Libraries are compatible with all current _active_ and _maintenance_ versions of +Node.js. +If you are using an end-of-life version of Node.js, we recommend that you update +as soon as possible to an actively supported LTS version. + +Google's client libraries support legacy versions of Node.js runtimes on a +best-efforts basis with the following warnings: + +* Legacy versions are not tested in continuous integration. +* Some security patches and features cannot be backported. +* Dependencies cannot be kept up-to-date. + +Client libraries targeting some end-of-life versions of Node.js are available, and +can be installed through npm [dist-tags](https://docs.npmjs.com/cli/dist-tag). +The dist-tags follow the naming convention `legacy-(version)`. +For example, `npm install @google-cloud/dns@legacy-8` installs client libraries +for versions compatible with Node.js 8. + +## Versioning + +This library follows [Semantic Versioning](http://semver.org/). + + + +This library is considered to be **stable**. The code surface will not change in backwards-incompatible ways +unless absolutely necessary (e.g. because of critical security issues) or with +an extensive deprecation period. Issues and requests against **stable** libraries +are addressed with the highest priority. + + + + + + +More Information: [Google Cloud Platform Launch Stages][launch_stages] + +[launch_stages]: https://cloud.google.com/terms/launch-stages + +## Contributing + +Contributions welcome! See the [Contributing Guide](https://github.com/googleapis/google-cloud-node/blob/main/CONTRIBUTING.md). + +Please note that this `README.md`, the `samples/README.md`, +and a variety of configuration files in this repository (including `.nycrc` and `tsconfig.json`) +are generated from a central template. To edit one of these files, make an edit +to its templates in +[directory](https://github.com/googleapis/synthtool). + +## License + +Apache Version 2.0 + +See [LICENSE](https://github.com/googleapis/google-cloud-node/blob/main/LICENSE) + +[client-docs]: https://cloud.google.com/nodejs/docs/reference/dns/latest +[product-docs]: https://cloud.google.com/dns +[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png +[projects]: https://console.cloud.google.com/project +[billing]: https://support.google.com/cloud/answer/6293499#enable-billing +[enable_api]: https://console.cloud.google.com/flows/enableapi?apiid=dns.googleapis.com +[auth]: https://cloud.google.com/docs/authentication/getting-started diff --git a/packages/google-cloud-dns/linkinator.config.json b/packages/google-cloud-dns/linkinator.config.json new file mode 100644 index 00000000000..30840bc10c3 --- /dev/null +++ b/packages/google-cloud-dns/linkinator.config.json @@ -0,0 +1,9 @@ +{ + "recurse": true, + "skip": [ + "https://codecov.io/gh/googleapis/", + "img.shields.io" + ], + "silent": true, + "concurrency": 10 +} diff --git a/packages/google-cloud-dns/package.json b/packages/google-cloud-dns/package.json new file mode 100644 index 00000000000..2a39f921043 --- /dev/null +++ b/packages/google-cloud-dns/package.json @@ -0,0 +1,82 @@ +{ + "name": "@google-cloud/dns", + "description": "Cloud DNS Client Library for Node.js", + "version": "4.0.0", + "license": "Apache-2.0", + "author": "Google Inc.", + "engines": { + "node": ">=14.0.0" + }, + "repository": { + "type": "git", + "directory": "packages/google-cloud-dns", + "url": "https://github.com/googleapis/google-cloud-node.git" + }, + "main": "./build/src/index.js", + "types": "./build/src/index.d.ts", + "files": [ + "build/src", + "!build/src/**/*.map" + ], + "keywords": [ + "google apis client", + "google api client", + "google apis", + "google api", + "google", + "google cloud platform", + "google cloud", + "cloud", + "google dns", + "dns" + ], + "scripts": { + "docs": "jsdoc -c .jsdoc.js", + "lint": "gts check", + "test": "c8 mocha build/test", + "samples-test": "npm run compile && cd samples/ && npm link ../ && npm i && npm test", + "presystem-test": "npm run compile", + "system-test": "npm run compile && c8 mocha build/system-test", + "clean": "gts clean", + "compile": "tsc -p .", + "fix": "gts fix", + "prepare": "npm run compile", + "pretest": "npm run compile", + "docs-test": "linkinator docs", + "predocs-test": "npm run docs", + "prelint": "cd samples; npm link ../; npm install", + "precompile": "gts clean" + }, + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.0", + "dns-zonefile": "0.2.10", + "lodash.groupby": "^4.6.0", + "string-format-obj": "^1.1.1" + }, + "devDependencies": { + "@types/lodash.groupby": "^4.6.4", + "@types/mocha": "^9.0.0", + "@types/node": "^20.4.9", + "@types/proxyquire": "^1.3.28", + "@types/request": "^2.48.1", + "@types/tmp": "0.2.4", + "@types/uuid": "^9.0.0", + "c8": "^8.0.1", + "codecov": "^3.0.2", + "gts": "^5.0.0", + "jsdoc": "^4.0.0", + "jsdoc-fresh": "^3.0.0", + "jsdoc-region-tag": "^3.0.0", + "linkinator": "4.1.2", + "long": "^5.2.3", + "mocha": "^9.2.2", + "proxyquire": "^2.0.1", + "tmp": "^0.2.0", + "typescript": "^5.1.6", + "uuid": "^9.0.0" + }, + "homepage": "https://github.com/googleapis/google-cloud-node/tree/main/packages/google-cloud-dns" +} diff --git a/packages/google-cloud-dns/samples/.eslintrc.yml b/packages/google-cloud-dns/samples/.eslintrc.yml new file mode 100644 index 00000000000..282535f55f6 --- /dev/null +++ b/packages/google-cloud-dns/samples/.eslintrc.yml @@ -0,0 +1,3 @@ +--- +rules: + no-console: off diff --git a/packages/google-cloud-dns/samples/README.md b/packages/google-cloud-dns/samples/README.md new file mode 100644 index 00000000000..8ae6c20a689 --- /dev/null +++ b/packages/google-cloud-dns/samples/README.md @@ -0,0 +1,52 @@ +[//]: # "This README.md file is auto-generated, all changes to this file will be lost." +[//]: # "To regenerate it, use `python -m synthtool`." +Google Cloud Platform logo + +# [Cloud DNS: Node.js Samples](https://github.com/googleapis/google-cloud-node) + +[![Open in Cloud Shell][shell_img]][shell_link] + + + +## Table of Contents + +* [Before you begin](#before-you-begin) +* [Samples](#samples) + * [Quickstart](#quickstart) + +## Before you begin + +Before running the samples, make sure you've followed the steps outlined in +[Using the client library](https://github.com/googleapis/google-cloud-node#using-the-client-library). + +`cd samples` + +`npm install` + +`cd ..` + +## Samples + + + +### Quickstart + +Fetches a list of all available zones. + +View the [source code](https://github.com/googleapis/google-cloud-node/blob/main/packages/google-cloud-dns/samples/quickstart.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-cloud-node&page=editor&open_in_editor=packages/google-cloud-dns/samples/quickstart.js,samples/README.md) + +__Usage:__ + + +`node quickstart` + + + + + + +[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png +[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-cloud-node&page=editor&open_in_editor=samples/README.md +[product-docs]: https://cloud.google.com/dns diff --git a/packages/google-cloud-dns/samples/package.json b/packages/google-cloud-dns/samples/package.json new file mode 100644 index 00000000000..d4a25ed9c44 --- /dev/null +++ b/packages/google-cloud-dns/samples/package.json @@ -0,0 +1,25 @@ +{ + "name": "nodejs-docs-samples-dns", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": "googleapis/nodejs-dns", + "engines": { + "node": ">=14.0.0" + }, + "files": [ + "*.js", + "!test/" + ], + "scripts": { + "test": "mocha --timeout 600000" + }, + "dependencies": { + "@google-cloud/dns": "^4.0.0" + }, + "devDependencies": { + "chai": "^4.2.0", + "mocha": "^8.0.0", + "uuid": "^9.0.0" + } +} \ No newline at end of file diff --git a/packages/google-cloud-dns/samples/quickstart.js b/packages/google-cloud-dns/samples/quickstart.js new file mode 100644 index 00000000000..2084222e717 --- /dev/null +++ b/packages/google-cloud-dns/samples/quickstart.js @@ -0,0 +1,40 @@ +// Copyright 2016 Google LLC +// +// 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. + +'use strict'; + +// sample-metadata: +// name: Quickstart +// description: Fetches a list of all available zones. +// usage: node quickstart + +async function main() { + // [START dns_quickstart] + // Imports the Google Cloud client library + const {DNS} = require('@google-cloud/dns'); + + // Creates a client + const dns = new DNS(); + + async function quickstart() { + // Lists all zones in the current project + const [zones] = await dns.getZones(); + console.log('Zones:'); + zones.forEach(zone => console.log(zone.name)); + } + quickstart(); + // [END dns_quickstart] +} + +main(); diff --git a/packages/google-cloud-dns/samples/test/quickstart.test.js b/packages/google-cloud-dns/samples/test/quickstart.test.js new file mode 100644 index 00000000000..836c9f094b9 --- /dev/null +++ b/packages/google-cloud-dns/samples/test/quickstart.test.js @@ -0,0 +1,47 @@ +// Copyright 2017 Google LLC +// +// 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. + +'use strict'; + +const {execSync} = require('child_process'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const {DNS} = require('@google-cloud/dns'); +const {v4} = require('uuid'); + +const exec = cmd => execSync(cmd, {encoding: 'utf8'}); + +describe(__filename, () => { + const zoneName = `test-${v4().substr(0, 13)}`; + const dns = new DNS(); + + before(async () => { + const projectId = await dns.getProjectId(); + await dns.createZone(zoneName, { + dnsName: `${projectId}.appspot.com.`, + dnssecConfig: { + state: 'on', + }, + }); + }); + + after(async () => { + await dns.zone(zoneName).delete(); + }); + + it('should run the quickstart', () => { + const output = exec('node quickstart'); + assert.include(output, 'Zones:'); + }); +}); diff --git a/packages/google-cloud-dns/src/change.ts b/packages/google-cloud-dns/src/change.ts new file mode 100644 index 00000000000..0d5e3a29532 --- /dev/null +++ b/packages/google-cloud-dns/src/change.ts @@ -0,0 +1,259 @@ +/*! + * 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. + */ + +import {Metadata, ServiceObject} from '@google-cloud/common'; +import {promisifyAll} from '@google-cloud/promisify'; + +import {Record} from './record'; +import {Zone} from './zone'; + +export interface CreateChangeRequest { + add?: Record | Record[]; + delete?: Record | Record[]; +} + +export type CreateChangeResponse = [Change, Metadata]; + +export interface CreateChangeCallback { + (err: Error | null, change?: Change | null, apiResponse?: Metadata): void; +} + +/** + * @class + * + * @param {Zone} zone The parent zone object. + * @param {string} id ID of the change. + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * const change = zone.change('change-id'); + */ +export class Change extends ServiceObject { + parent!: Zone; + constructor(zone: Zone, id?: string) { + const methods = { + /** + * @typedef {array} ChangeExistsResponse + * @property {boolean} 0 Whether the {@link Change} exists. + */ + /** + * @callback ChangeExistsCallback + * @param {?Error} err Request error, if any. + * @param {boolean} exists Whether the {@link Change} exists. + */ + /** + * Check if the change exists. + * + * @method Change#exists + * @param {ChangeExistsCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * const change = zone.change('change-id'); + * + * change.exists((err, exists) => {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * change.exists().then((data) => { + * const exists = data[0]; + * }); + */ + exists: true, + /** + * @typedef {array} GetChangeResponse + * @property {Change} 0 The {@link Change}. + * @property {object} 1 The full API response. + */ + /** + * @callback GetChangeCallback + * @param {?Error} err Request error, if any. + * @param {Change} change The {@link Change}. + * @param {object} apiResponse The full API response. + */ + /** + * 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. + * + * @method Change#get + * @param {options} [options] Configuration object. + * @param {boolean} [options.autoCreate=false] Automatically create the + * object if it does not exist. + * @param {GetChangeCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * const change = zone.change('change-id'); + * + * change.get((err, change, apiResponse) => { + * // `change.metadata` has been populated. + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * change.get().then((data) => { + * const change = data[0]; + * const apiResponse = data[1]; + * }); + */ + get: true, + /** + * @typedef {array} GetChangeMetadataResponse + * @property {object} 0 The {@link Change} metadata. + * @property {object} 1 The full API response. + */ + /** + * @callback GetChangeMetadataCallback + * @param {?Error} err Request error, if any. + * @param {object} metadata The {@link Change} metadata. + * @param {object} apiResponse The full API response. + */ + /** + * Get the metadata for the change in the zone. + * + * @see [Changes: get API Documentation]{@link https://cloud.google.com/dns/api/v1/changes/get} + * + * @method Change#getMetadata + * @param {GetChangeMetadataCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * const change = zone.change('change-id'); + * + * change.getMetadata((err, metadata, apiResponse) => { + * if (!err) { + * // metadata = { + * // kind: 'dns#change', + * // additions: [{...}], + * // deletions: [{...}], + * // startTime: '2015-07-21T14:40:06.056Z', + * // id: '1', + * // status: 'done' + * // } + * } + * }); + * + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * change.getMetadata().then((data) => { + * const metadata = data[0]; + * const apiResponse = data[1]; + * }); + */ + getMetadata: true, + }; + /** + * @name Change#metadata + * @type {object} + */ + super({ + parent: zone, + /** + * @name Zone#baseUrl + * @type {string} + * @default "/changes" + */ + baseUrl: '/changes', + /** + * @name Change#id + * @type {string} + */ + id, + methods, + }); + } + + create(config?: CreateChangeRequest): Promise; + create(config: CreateChangeRequest, callback: CreateChangeCallback): void; + create(callback: CreateChangeCallback): void; + /** + * Create a change. + * + * @method Change#create + * @param {CreateChangeRequest} config The configuration object. + * @param {CreateChangeCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * const change = zone.change('change-id'); + * + * const config = { + * add: { + * // ... + * } + * }; + * + * change.create(config, (err, change, apiResponse) => { + * if (!err) { + * // The change was created successfully. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * change.create(config).then((data) => { + * const change = data[0]; + * const apiResponse = data[1]; + * }); + */ + create( + configOrCallback?: CreateChangeRequest | CreateChangeCallback, + callback?: CreateChangeCallback + ): void | Promise { + const config = typeof configOrCallback === 'object' ? configOrCallback : {}; + callback = + typeof configOrCallback === 'function' ? configOrCallback! : callback; + this.parent.createChange(config, (err, change, apiResponse) => { + if (err) { + callback!(err, null, apiResponse); + return; + } + this.id = change!.id; + this.metadata = change!.metadata; + callback!(null, this, apiResponse); + }); + } +} + +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +promisifyAll(Change); diff --git a/packages/google-cloud-dns/src/index.ts b/packages/google-cloud-dns/src/index.ts new file mode 100644 index 00000000000..3bcf8ff9af0 --- /dev/null +++ b/packages/google-cloud-dns/src/index.ts @@ -0,0 +1,468 @@ +/*! + * 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. + */ + +import {GoogleAuthOptions, Metadata, Service} from '@google-cloud/common'; +import {paginator} from '@google-cloud/paginator'; +import {promisifyAll} from '@google-cloud/promisify'; +import arrify = require('arrify'); +import {Stream} from 'stream'; + +import {Zone} from './zone'; + +export {Record, RecordMetadata} from './record'; + +export interface GetZonesRequest { + autoPaginate?: boolean; + maxApiCalls?: number; + maxResults?: number; + pageToken?: string; +} + +export interface DNSConfig extends GoogleAuthOptions { + autoRetry?: boolean; + maxRetries?: number; +} + +export interface GetZonesCallback { + ( + err: Error | null, + zones: Zone[] | null, + nextQuery?: GetZonesRequest | null, + apiResponse?: Metadata + ): void; +} + +export type GetZonesResponse = [Zone[], GetZonesRequest | null, Metadata]; + +export interface GetZoneCallback { + (err: Error | null, zone?: Zone | null, apiResponse?: Metadata): void; +} + +export interface CreateZoneRequest { + dnsName: string; + description?: string; + name?: string; + dnssecConfig?: ManagedZoneDnsSecConfig; +} + +export interface ManagedZoneDnsSecConfig { + /** + * Specifies parameters for generating initial DnsKeys for this ManagedZone. Can only be changed while the state is OFF. + */ + defaultKeySpecs?: DnsKeySpec[]; + kind?: string | null; + /** + * Specifies the mechanism for authenticated denial-of-existence responses. Can only be changed while the state is OFF. + */ + nonExistence?: string | null; + /** + * Specifies whether DNSSEC is enabled, and what mode it is in. + */ + state?: 'on' | 'off' | null; +} + +export interface DnsKeySpec { + /** + * String mnemonic specifying the DNSSEC algorithm of this key. + */ + algorithm?: string | null; + /** + * Length of the keys in bits. + */ + keyLength?: number | null; + /** + * Specifies whether this is a key signing key (KSK) or a zone signing key (ZSK). Key signing keys have the Secure Entry Point flag set and, when active, will only be used to sign resource record sets of type DNSKEY. Zone signing keys do not have the Secure Entry Point flag set and will be used to sign all other types of resource record sets. + */ + keyType?: string | null; + kind?: string | null; +} + +export type CreateZoneResponse = [Zone, Metadata]; +export type CreateZoneCallback = GetZoneCallback; + +export interface DNSOptions extends GoogleAuthOptions { + /** + * The API endpoint of the service used to make requests. + * Defaults to `dns.googleapis.com`. + */ + apiEndpoint?: string; +} + +/** + * @typedef {object} ClientConfig + * @property {string} [projectId] The project ID from the Google Developer's + * Console, e.g. 'grape-spaceship-123'. We will also check the environment + * variable `GCLOUD_PROJECT` for your project ID. If your app is running in + * an environment which supports {@link + * https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application + * Application Default Credentials}, your project ID will be detected + * automatically. + * @property {string} [keyFilename] Full path to the a .json, .pem, or .p12 key + * downloaded from the Google Developers Console. If you provide a path to a + * JSON file, the `projectId` option above is not necessary. NOTE: .pem and + * .p12 require you to specify the `email` option as well. + * @property {string} [email] Account email address. Required when using a .pem + * or .p12 keyFilename. + * @property {object} [credentials] Credentials object. + * @property {string} [credentials.client_email] + * @property {string} [credentials.private_key] + * @property {boolean} [autoRetry=true] Automatically retry requests if the + * response is related to rate limits or certain intermittent server errors. + * We will exponentially backoff subsequent requests by default. + * @property {number} [maxRetries=3] Maximum number of automatic retries + * attempted before returning the error. + * @property {Constructor} [promise] Custom promise module to use instead of + * native Promises. + */ +/** + * [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 to make your applications and services available to your + * users. This programmable, authoritative DNS service can be used to easily + * publish and manage DNS records using the same infrastructure relied upon by + * Google. + * + * @class + * + * @see [What is Cloud DNS?]{@link https://cloud.google.com/dns/what-is-cloud-dns} + * + * @param {ClientConfig} [options] Configuration options. + * + * @example Import the client library + * const {DNS} = require('@google-cloud/dns'); + * + * @example Create a client that uses Application + * Default Credentials (ADC): const dns = new DNS(); + * + * @example Create a client with explicit + * credentials: const dns = new DNS({ projectId: + * 'your-project-id', keyFilename: '/path/to/keyfile.json' + * }); + * + * @example include:samples/quickstart.js + * region_tag:dns_quickstart + * Full quickstart example: + */ +class DNS extends Service { + getZonesStream: (query: GetZonesRequest) => Stream; + constructor(options: DNSOptions = {}) { + options.apiEndpoint = options.apiEndpoint || 'dns.googleapis.com'; + const config = { + apiEndpoint: options.apiEndpoint, + baseUrl: `https://${options.apiEndpoint}/dns/v1`, + scopes: [ + 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', + 'https://www.googleapis.com/auth/cloud-platform', + ], + packageJson: require('../../package.json'), + }; + super(config, options); + + /** + * Get {@link Zone} objects for all of the zones in your project as + * a readable object stream. + * + * @method DNS#getZonesStream + * @param {GetZonesRequest} [query] Query object for listing zones. + * @returns {ReadableStream} A readable stream that emits {@link Zone} instances. + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * + * dns.getZonesStream() + * .on('error', console.error) + * .on('data', function(zone) { + * // zone is a Zone object. + * }) + * .on('end', () => { + * // All zones retrieved. + * }); + * + * //- + * // If you anticipate many results, you can end a stream early to prevent + * // unnecessary processing and API requests. + * //- + * dns.getZonesStream() + * .on('data', function(zone) { + * this.end(); + * }); + */ + this.getZonesStream = paginator.streamify('getZones'); + } + + createZone( + name: string, + config: CreateZoneRequest + ): Promise; + createZone( + name: string, + config: CreateZoneRequest, + callback: GetZoneCallback + ): void; + /** + * Config to set for the zone. + * + * @typedef {object} CreateZoneRequest + * @property {string} dnsName DNS name for the zone. E.g. "example.com." + * @property {string} [description] Description text for the zone. + */ + /** + * @typedef {array} CreateZoneResponse + * @property {Zone} 0 The new {@link Zone}. + * @property {object} 1 The full API response. + */ + /** + * @callback CreateZoneCallback + * @param {?Error} err Request error, if any. + * @param {Zone} zone The new {@link Zone}. + * @param {object} apiResponse The full API response. + */ + /** + * Create a managed zone. + * + * @method DNS#createZone + * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create} + * + * @throws {error} If a zone name is not provided. + * @throws {error} If a zone dnsName is not provided. + * + * @param {string} name Name of the zone to create, e.g. "my-zone". + * @param {CreateZoneRequest} config Config to set for the zone. + * @param {CreateZoneCallback} [callback] Callback function. + * @returns {Promise} + * @throws {Error} If a name is not provided. + * @see Zone#create + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * + * const config = { + * dnsName: 'example.com.', // note the period at the end of the domain. + * description: 'This zone is awesome!' + * }; + * + * dns.createZone('my-awesome-zone', config, (err, zone, apiResponse) => { + * if (!err) { + * // The zone was created successfully. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * dns.createZone('my-awesome-zone', config).then((data) => { + * const zone = data[0]; + * const apiResponse = data[1]; + * }); + */ + createZone( + name: string, + config: CreateZoneRequest, + callback?: GetZoneCallback + ): void | Promise { + if (!name) { + throw new Error('A zone name is required.'); + } + if (!config || !config.dnsName) { + throw new Error('A zone dnsName is required.'); + } + config.name = name; + // Required by the API. + config.description = config.description || ''; + this.request( + { + method: 'POST', + uri: '/managedZones', + json: config, + }, + (err, resp) => { + if (err) { + callback!(err, null, resp); + return; + } + const zone = this.zone(resp.name); + zone.metadata = resp; + callback!(null, zone, resp); + } + ); + } + + getZones(query?: GetZonesRequest): Promise; + getZones(callback: GetZonesCallback): void; + getZones(query: GetZonesRequest, callback: GetZonesCallback): void; + /** + * Query object for listing zones. + * + * @typedef {object} GetZonesRequest + * @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} [pageToken] A previously-returned page token + * representing part of the larger set of results to view. + */ + /** + * @typedef {array} GetZonesResponse + * @property {Zone[]} 0 Array of {@link Zone} instances. + * @property {object} 1 The full API response. + */ + /** + * @callback GetZonesCallback + * @param {?Error} err Request error, if any. + * @param {Zone[]} zones Array of {@link Zone} instances. + * @param {object} apiResponse The full API response. + */ + /** + * Gets a list of managed zones for the project. + * + * @method DNS#getZones + * @see [ManagedZones: list API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/list} + * + * @param {GetZonesRequest} [query] Query object for listing zones. + * @param {GetZonesCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * + * dns.getZones((err, zones, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * dns.getZones().then(data => { + * const zones = data[0]; + * }); + */ + getZones( + queryOrCallback?: GetZonesRequest | GetZonesCallback, + callback?: GetZonesCallback + ): void | Promise { + const query = typeof queryOrCallback === 'object' ? queryOrCallback : {}; + callback = + typeof queryOrCallback === 'function' ? queryOrCallback : callback; + this.request( + { + uri: '/managedZones', + qs: query, + }, + (err, resp) => { + if (err) { + callback!(err, null, null, resp); + return; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const zones = arrify(resp.managedZones).map((zone: any) => { + const zoneInstance = this.zone(zone.name); + zoneInstance.metadata = zone; + return zoneInstance; + }); + let nextQuery: GetZonesRequest | null = null; + if (resp.nextPageToken) { + nextQuery = Object.assign({}, query, { + pageToken: resp.nextPageToken, + }); + } + callback!(null, zones, nextQuery, resp); + } + ); + } + + /** + * Get a reference to a Zone. + * + * @param {string} name The unique name of the zone. + * @returns {Zone} + * @see Zone + * + * @throws {error} If a zone name is not provided. + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * + * const zone = dns.zone('my-zone'); + */ + zone(name: string) { + if (!name) { + throw new Error('A zone name is required.'); + } + return new Zone(this, name); + } +} + +/*! Developer Documentation + * + * These methods can be auto-paginated. + */ +paginator.extend(DNS, 'getZones'); + +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +promisifyAll(DNS, { + exclude: ['zone'], +}); + +/** + * {@link Zone} class. + * + * @name DNS.Zone + * @see Zone + * @type {Constructor} + */ +export {Zone}; + +/** + * The default export of the `@google-cloud/dns` package is the {@link DNS} + * class. + * + * See {@link DNS} and {@link ClientConfig} for client methods and + * configuration options. + * + * @module {DNS} @google-cloud/dns + * @alias nodejs-dns + * + * @example Install the client library with npm: npm install --save + * @google-cloud/dns + * + * @example Import the client library + * const {DNS} = require('@google-cloud/dns'); + * + * @example Create a client that uses Application + * Default Credentials (ADC): const dns = new DNS(); + * + * @example Create a client with explicit + * credentials: const dns = new DNS({ projectId: + * 'your-project-id', keyFilename: '/path/to/keyfile.json' + * }); + * + * @example include:samples/quickstart.js + * region_tag:dns_quickstart + * Full quickstart example: + */ +export {DNS}; diff --git a/packages/google-cloud-dns/src/record.ts b/packages/google-cloud-dns/src/record.ts new file mode 100644 index 00000000000..fabebb42620 --- /dev/null +++ b/packages/google-cloud-dns/src/record.ts @@ -0,0 +1,228 @@ +/*! + * 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. + */ + +import {promisifyAll} from '@google-cloud/promisify'; +import arrify = require('arrify'); +import {Change, CreateChangeCallback} from './change'; +import {Zone} from './zone'; +import {Metadata} from '@google-cloud/common'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const format = require('string-format-obj'); + +export interface RecordObject { + rrdatas?: Array<{}>; + rrdata?: {}; + data?: {}; + type?: string; +} + +export interface RecordMetadata { + name: string; + data: string | string[]; + ttl: number; + type?: string; + signatureRrdatas?: string[]; +} + +/** + * @typedef {array} DeleteRecordResponse + * @property {Change} 0 A {@link Change} object. + * @property {object} 1 The full API response. + */ +export type DeleteRecordResponse = [Change, Metadata]; + +/** + * @callback DeleteRecordCallback + * @param {?Error} err Request error, if any. + * @param {?Change} change A {@link Change} object. + * @param {object} apiResponse The full API response. + */ +export interface DeleteRecordCallback { + (err: Error | null, change?: Change, apiResponse?: Metadata): void; +} + +/** + * Create a Resource Record object. + * + * @class + * + * @param {string} type The record type, e.g. `A`, `AAAA`, `MX`. + * @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. + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('my-awesome-zone'); + * + * const record = zone.record('a', { + * name: 'example.com.', + * ttl: 86400, + * data: '1.2.3.4' + * }); + */ +export class Record implements RecordObject { + zone_: Zone; + type: string; + metadata: RecordMetadata; + rrdatas?: Array<{}>; + data?: {}; + constructor(zone: Zone, type: string, metadata: RecordMetadata) { + this.zone_ = zone; + /** + * @name Record#type + * @type {string} + */ + this.type = type; + /** + * @name Record#metadata + * @type {object} + * @property {string} name + * @property {string[]} data + * @property {number} metadata.ttl + */ + this.metadata = metadata; + Object.assign(this, this.toJSON()); + if (this.rrdatas) { + /** + * @name Record#data + * @type {?object[]} + */ + this.data = this.rrdatas; + delete this.rrdatas; + } + } + + delete(): Promise; + delete(callback: CreateChangeCallback): void; + /** + * Delete this record by creating a change on your zone. This is a convenience + * method for: + * + * zone.createChange({ + * delete: record + * }, (err, change, apiResponse) => {}); + * + * @see [ManagedZones: create API Documentation]{@link https://cloud.google.com/dns/api/v1/managedZones/create} + * + * @param {DeleteRecordCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * const record = zone.record('a', { + * name: 'example.com.', + * ttl: 86400, + * data: '1.2.3.4' + * }); + * + * record.delete((err, change, apiResponse) => { + * if (!err) { + * // Delete change modification was created. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * record.delete().then((data) => { + * const change = data[0]; + * const apiResponse = data[1]; + * }); + */ + delete( + callback?: CreateChangeCallback + ): void | Promise { + this.zone_.deleteRecords(this, callback!); + } + /** + * Serialize the record instance to the format the API expects. + * + * @returns {object} + */ + toJSON() { + const recordObject: RecordObject = Object.assign({}, this.metadata, { + type: this.type.toUpperCase(), + }); + if (recordObject.data) { + recordObject.rrdatas = arrify(recordObject.data); + delete recordObject.data; + } + return recordObject; + } + /** + * Convert the record to a string, formatted for a zone file. + * + * @returns {string} + */ + toString() { + const json = this.toJSON(); + return (json.rrdatas || [{}]) + .map(data => { + json.rrdata = data; + return format('{name} {ttl} IN {type} {rrdata}', json); + }) + .join('\n'); + } + /** + * Create a Record instance from a resource record set in a zone file. + * + * @private + * + * @param {Zone} zone The zone. + * @param {string} type The record type, e.g. `A`, `AAAA`, `MX`. + * @param {object} bindData Metadata parsed from dns-zonefile. Properties vary + * based on the type of record. + * @returns {Record} + */ + static fromZoneRecord_(zone: Zone, type: string, bindData: RecordMetadata) { + const typeToZoneFormat = { + a: '{ip}', + aaaa: '{ip}', + cname: '{alias}', + mx: '{preference} {host}', + ns: '{host}', + soa: '{mname} {rname} {serial} {retry} {refresh} {expire} {minimum}', + spf: '{data}', + srv: '{priority} {weight} {port} {target}', + txt: '{txt}', + } as {[index: string]: string}; + const metadata = { + data: format(typeToZoneFormat[type.toLowerCase()], bindData), + name: bindData.name, + ttl: bindData.ttl, + }; + return new Record(zone, type, metadata); + } +} + +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +promisifyAll(Record, { + exclude: ['toJSON', 'toString'], +}); diff --git a/packages/google-cloud-dns/src/zone.ts b/packages/google-cloud-dns/src/zone.ts new file mode 100644 index 00000000000..03df70aca66 --- /dev/null +++ b/packages/google-cloud-dns/src/zone.ts @@ -0,0 +1,1363 @@ +/*! + * 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. + */ + +import { + DeleteCallback, + GetConfig, + Metadata, + ServiceObject, + ServiceObjectConfig, +} from '@google-cloud/common'; +import {paginator} from '@google-cloud/paginator'; +import {promisifyAll} from '@google-cloud/promisify'; +import arrify = require('arrify'); +import * as fs from 'fs'; + +import groupBy = require('lodash.groupby'); + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const zonefile = require('dns-zonefile'); + +import {Change} from './change'; +import {Record, RecordMetadata, RecordObject} from './record'; +import { + DNS, + CreateZoneRequest, + CreateZoneResponse, + CreateZoneCallback, +} from '.'; +import {Readable} from 'stream'; +import {InstanceResponseCallback} from '@google-cloud/common'; +import {GetResponse} from '@google-cloud/common/build/src/service-object'; + +/** + * Config to set for the change. + * + * @typedef {object} CreateChangeRequest + * @property {Record|Record[]} add {@link Record} objects to add to this zone. + * @property {Record|Record[]} delete {@link Record} objects to delete + * from this zone. Be aware that the resource records here must match + * exactly to be deleted. + */ +export interface CreateChangeRequest { + add?: Record | Record[]; + delete?: Record | Record[]; +} + +/** + * @typedef {array} CreateChangeResponse + * @property {Change} 0 A {@link Change} object. + * @property {object} 1 The full API response. + */ +export type CreateChangeResponse = [Change, Metadata]; + +/** + * @callback CreateChangeCallback + * @param {?Error} err Request error, if any. + * @param {?Change} change A {@link Change} object. + * @param {object} apiResponse The full API response. + */ +export interface CreateChangeCallback { + (err: Error | null, change?: Change, apiResponse?: Metadata): void; +} + +/** + * @typedef {array} DeleteZoneResponse + * @property {object} 0 The full API response. + */ +export type DeleteZoneResponse = [Metadata]; + +/** + * @callback DeleteZoneCallback + * @param {?Error} err Request error, if any. + * @param {object} apiResponse The full API response. + */ +export type DeleteZoneCallback = DeleteCallback; + +export interface DeleteZoneConfig { + force?: boolean; +} + +export interface GetRecordsCallback { + ( + err: Error | null, + records?: Record[] | null, + nextQuery?: {} | null, + apiResponse?: Metadata + ): void; +} + +export type GetRecordsResponse = [Record[], Metadata]; + +export interface GetRecordsRequest { + autoPaginate?: boolean; + maxApiCalls?: number; + maxResults?: number; + name?: string; + pageToken?: string; + type?: string; + filterByTypes_?: {[index: string]: boolean}; +} + +export interface GetZoneRequest extends CreateZoneRequest, GetConfig {} + +/** + * Query object for listing changes. + * + * @typedef {object} GetChangesRequest + * @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} [pageToken] A previously-returned page token + * representing part of the larger set of results to view. + * @property {string} [sort] Set to 'asc' for ascending, and 'desc' for + * descending or omit for no sorting. + */ +export interface GetChangesRequest { + autoPaginate?: boolean; + maxApiCalls?: number; + maxResults?: number; + pageToken?: string; + sort?: string; + sortOrder?: string; +} + +/** + * @typedef {array} GetChangesResponse + * @property {Change[]} 0 Array of {@link Change} instances. + * @property {object} 1 The full API response. + */ +export type GetChangesResponse = [Change[], Metadata]; + +/** + * @callback GetChangesCallback + * @param {?Error} err Request error, if any. + * @param {Change[]} changes Array of {@link Change} instances. + * @param {object} apiResponse The full API response. + */ + +export interface GetChangesCallback { + ( + err: Error | null, + changes?: Change[] | null, + nextQuery?: {} | null, + apiResponse?: Metadata + ): void; +} + +/** + * @typedef {array} ZoneExportResponse + * @property {object} 0 The full API response. + */ +export type ZoneExportResponse = [Metadata]; + +/** + * @callback ZoneExportCallback + * @param {?Error} err Request error, if any. + * @param {object} apiResponse The full API response. + */ +export interface ZoneExportCallback { + (err: Error | null): void; +} + +// This type essentially just clones a class but allows us to exclude methods +// from the type itself. In this case it is useful because we want to override +// the Zone#create signature that is defined in the common library. +type Without = { + [P in Exclude]: T[P]; +}; + +// Using the Without type, we essentially make a new ServiceObject type that +// doesn't contain any of the methods that have signatures we wish to override. +type ZoneServiceObject = new ( + config: ServiceObjectConfig +) => Without, 'create' | 'delete' | 'get'>; + +// 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 = ServiceObject as ZoneServiceObject; + +/** + * 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 { + name: string; + getRecordsStream: (query?: GetRecordsRequest | string | string[]) => Readable; + getChangesStream: (query?: GetChangesRequest) => Readable; + constructor(dns: DNS, name: string) { + const methods = { + /** + * Create a zone. + * + * @method Zone#create + * @param {CreateZoneRequest} metadata Metadata to set for the zone. + * @param {CreateZoneCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {DNS} = require('@google-cloud/dns'); + * const dns = new DNS(); + * const zone = dns.zone('zone-id'); + * + * 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} + * + * @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} + * + * @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} + * + * @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.streamify('getRecords'); + this.getChangesStream = paginator.streamify('getChanges'); + } + + create(config: CreateZoneRequest): Promise; + create(config: CreateZoneRequest, callback: CreateZoneCallback): void; + create( + config: CreateZoneRequest, + callback?: CreateZoneCallback + ): void | Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const args = [config, callback!] as any; + ServiceObject.prototype.create.apply(this, args); + } + + get(config?: GetZoneRequest): Promise>; + get(callback: InstanceResponseCallback): void; + get(config: GetZoneRequest, callback: InstanceResponseCallback): void; + get( + configOrCallback?: GetZoneRequest | InstanceResponseCallback, + callback?: InstanceResponseCallback + ): void | Promise> { + const config = typeof configOrCallback === 'object' ? configOrCallback : {}; + callback = + typeof configOrCallback === 'function' ? configOrCallback : callback; + ServiceObject.prototype.get.call(this, config, callback!); + } + + addRecords(records: Record | Record[]): Promise; + addRecords(records: Record | Record[], callback: CreateChangeCallback): void; + /** + * 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} + */ + addRecords( + records: Record | Record[], + callback?: CreateChangeCallback + ): void | Promise { + 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?: string) { + return new Change(this, id); + } + + createChange(config: CreateChangeRequest): Promise; + createChange( + config: CreateChangeRequest, + callback: CreateChangeCallback + ): void; + /** + * 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} + * + * @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: CreateChangeRequest, + callback?: CreateChangeCallback + ): void | Promise { + if (!config || (!config.add && !config.delete)) { + throw new Error('Cannot create a change with no additions or deletions.'); + } + const groupByType = (recordsIn: RecordObject[]) => { + const recordsByType = groupBy(recordsIn, 'type'); + const recordsOut: RecordObject[] = []; + // 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) as Record[]).map(x => x.toJSON()) + ), + deletions: groupByType( + (arrify(config.delete) as Record[]).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, undefined, resp); + return; + } + const change = this.change(resp.id); + change.metadata = resp; + callback!(null, change, resp); + } + ); + } + + delete(options?: DeleteZoneConfig): Promise; + delete(callback: DeleteZoneCallback): void; + delete(options: DeleteZoneConfig, callback: DeleteZoneCallback): void; + /** + * 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 this is not an + * atomic request. + * + * @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} + * + * @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?: DeleteZoneConfig | DeleteZoneCallback, + callback?: DeleteZoneCallback + ): void | Promise { + 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; + } + + ServiceObject.prototype.delete.call(this, callback!); + } + + deleteRecords( + records?: Record | Record[] | string + ): Promise; + deleteRecords(callback: CreateChangeCallback): void; + deleteRecords( + records: Record | Record[] | string, + callback: CreateChangeCallback + ): void; + /** + * 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} + * + * @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?: Record | Record[] | string | CreateChangeCallback, + callback?: CreateChangeCallback + ): void | Promise { + let records: Array; + if (typeof recordsOrCallback === 'function') { + callback = recordsOrCallback; + records = []; + } else { + records = arrify(recordsOrCallback) as Array; + } + + if (typeof records[0] === 'string') { + this.deleteRecordsByType_(records as string[], callback!); + return; + } + this.createChange({delete: records as Record[]}, callback!); + } + + empty(): Promise; + empty(callback: CreateChangeCallback): void; + /** + * 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} + */ + empty( + callback?: CreateChangeCallback + ): void | Promise { + 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!); + } + }); + } + + export(localPath: string): Promise; + export(localPath: string, callback: ZoneExportCallback): void; + /** + * 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} + * + * @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: string, + callback?: ZoneExportCallback + ): void | Promise { + 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); + }); + }); + } + + getChanges(query?: GetChangesRequest): Promise; + getChanges(callback: GetChangesCallback): void; + getChanges(query: GetChangesRequest, callback: GetChangesCallback): void; + /** + * 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} + * + * @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?: GetChangesRequest | GetChangesCallback, + callback?: GetChangesCallback + ): void | Promise { + let query = queryOrCallback as GetChangesRequest; + 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: 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); + } + ); + } + + getRecords( + query?: GetRecordsRequest | string | string[] + ): Promise; + getRecords(callback: GetRecordsCallback): void; + getRecords( + query: GetRecordsRequest | string | string[], + callback: GetRecordsCallback + ): void; + /** + * 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} + * + * @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?: + | GetRecordsRequest + | GetRecordsCallback + | string + | string[], + callback?: GetRecordsCallback + ): void | Promise { + let query: string | string[] | GetRecordsRequest; + if (typeof queryOrCallback === 'function') { + callback = queryOrCallback; + query = []; + } else { + query = queryOrCallback!; + } + + if (typeof query === 'string' || Array.isArray(query)) { + const filterByTypes_: {[index: string]: boolean} = {}; + // For faster lookups, store the record types the user wants in an object. + arrify(query as string).forEach(type => { + filterByTypes_[type.toUpperCase()] = true; + }); + query = { + filterByTypes_, + }; + } + const requestQuery = Object.assign({}, query) as GetRecordsRequest; + 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: RecordMetadata) => { + return this.record(record.type!, record); + }); + if ((query as GetRecordsRequest).filterByTypes_) { + records = records.filter((record: RecordMetadata) => { + return (query as GetRecordsRequest).filterByTypes_![record.type!]; + }); + } + let nextQuery: {} | null = null; + if (resp.nextPageToken) { + nextQuery = Object.assign({}, query, { + pageToken: resp.nextPageToken, + }); + } + callback!(null, records, nextQuery, resp); + } + ); + } + + import(localPath: string): Promise; + import(localPath: string, callback: CreateChangeCallback): void; + /** + * 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} + * @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: string, + callback?: CreateChangeCallback + ): void | Promise { + 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: Record[] = []; + recordTypes.forEach(recordType => { + const recordTypeSet = arrify(parsedZonefile[recordType]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + recordTypeSet.forEach((record: any) => { + record.ttl = record.ttl || defaultTTL; + recordsToCreate.push( + 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: string, metadata: RecordMetadata) { + return new Record(this, type, metadata); + } + + replaceRecords( + recordType: string | string[], + newRecords: Record | Record[] + ): Promise; + replaceRecords( + recordType: string | string[], + newRecords: Record | Record[], + callback: CreateChangeCallback + ): void; + /** + * 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} + * + * @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: string | string[], + newRecords: Record | Record[], + callback?: CreateChangeCallback + ): void | Promise { + this.getRecords(recordType, (err, recordsToDelete) => { + if (err) { + callback!(err); + return; + } + this.createChange( + { + add: newRecords, + delete: recordsToDelete!, + }, + callback! + ); + }); + } + + deleteRecordsByType_(recordTypes: string[]): Promise; + deleteRecordsByType_( + recordTypes: string[], + callback: CreateChangeCallback + ): void; + /** + * 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: string[], + callback?: CreateChangeCallback + ): void | Promise { + this.getRecords(recordTypes, (err, records) => { + if (err) { + callback!(err); + return; + } + if (records!.length === 0) { + callback!(null); + return; + } + this.deleteRecords(records!, callback!); + }); + } +} + +/** + * 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.extend(Zone, ['getChanges', 'getRecords']); + +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +promisifyAll(Zone, { + exclude: ['change', 'record'], +}); + +/** + * Reference to the {@link Zone} class. + * @name module:@google-cloud/dns.Zone + * @see Zone + */ +export {Zone}; diff --git a/packages/google-cloud-dns/system-test/data/zonefile.zone b/packages/google-cloud-dns/system-test/data/zonefile.zone new file mode 100644 index 00000000000..cd68c741f90 --- /dev/null +++ b/packages/google-cloud-dns/system-test/data/zonefile.zone @@ -0,0 +1,9 @@ +$TTL 3600 +{DNS_DOMAIN} 21600 IN SPF "v=spf1" "mx:{DNS_DOMAIN}" "-all" +{DNS_DOMAIN} IN TXT "google-site-verification=xxxxxxxxxxxxYYYYYYXXX" +{DNS_DOMAIN} 21600 MX 10 mail.example.com. +{DNS_DOMAIN} 21600 MX 30 mail.example.com. +{DNS_DOMAIN} A 128.9.0.32 + A 10.1.0.52 +{DNS_DOMAIN} A 10.2.0.27 + A 128.9.0.33 diff --git a/packages/google-cloud-dns/system-test/dns.ts b/packages/google-cloud-dns/system-test/dns.ts new file mode 100644 index 00000000000..aa56cb2c1de --- /dev/null +++ b/packages/google-cloud-dns/system-test/dns.ts @@ -0,0 +1,347 @@ +// Copyright 2015 Google LLC +// +// 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. + +import * as assert from 'assert'; +import {describe, it, before, after} from 'mocha'; +import * as fs from 'fs'; +import * as tmp from 'tmp'; +import * as util from 'util'; +import * as uuid from 'uuid'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const format = require('string-format-obj'); + +import {DNS, Record} from '../src'; +import {Metadata} from '@google-cloud/common'; + +const dns = new DNS(); +const DNS_DOMAIN = process.env.GCLOUD_TESTS_DNS_DOMAIN || 'gitnpm.com.'; + +const delayMs = async (ms = 1000) => { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +}; + +// Only run the tests if there is a domain to test with. +describe('dns', () => { + const ZONE_NAME = 'test-zone-' + uuid.v4().substr(0, 18); + const ZONE = dns.zone(ZONE_NAME); + + const records = { + a: ZONE.record('a', { + ttl: 86400, + name: DNS_DOMAIN, + data: '1.2.3.4', + }), + + aaaa: ZONE.record('aaaa', { + ttl: 86400, + name: DNS_DOMAIN, + data: '2607:f8b0:400a:801::1005', + }), + + cname: ZONE.record('cname', { + ttl: 86400, + name: 'mail.' + DNS_DOMAIN, + data: 'example.com.', + }), + + mx: ZONE.record('mx', { + ttl: 86400, + name: DNS_DOMAIN, + data: ['10 mail.' + DNS_DOMAIN, '20 mail2.' + DNS_DOMAIN], + }), + + 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" .', + ], + }), + + ns: ZONE.record('ns', { + ttl: 86400, + name: DNS_DOMAIN, + data: 'ns-cloud1.googledomains.com.', + }), + + ptr: ZONE.record('ptr', { + ttl: 60, + name: '2.1.0.10.in-addr.arpa.', + data: 'server.' + DNS_DOMAIN, + }), + + soa: ZONE.record('soa', { + ttl: 21600, + name: DNS_DOMAIN, + data: [ + 'ns-cloud1.googledomains.com.', + 'dns-admin.google.com.', + '1 21600 3600 1209600 300', + ].join(' '), + }), + + spf: ZONE.record('spf', { + ttl: 21600, + name: DNS_DOMAIN, + data: 'v=spf1 mx:' + DNS_DOMAIN.replace(/.$/, '') + ' -all', + }), + + srv: ZONE.record('srv', { + ttl: 21600, + name: 'sip.' + DNS_DOMAIN, + data: '0 5 5060 sip.' + DNS_DOMAIN, + }), + + txt: ZONE.record('txt', { + ttl: 21600, + name: DNS_DOMAIN, + data: 'google-site-verification=xxxxxxxxxxxxYYYYYYXXX', + }), + }; + + before(async () => { + // Clean up any leaked resources + const [zones] = await dns.getZones(); + await Promise.all( + zones.map(async zone => { + const hoursOld = + (Date.now() - new Date(zone.metadata.creationTime).getTime()) / + 1000 / + 60 / + 60; + if (hoursOld > 1) { + await zone.delete({force: true}); + } + }) + ); + await ZONE.create({ + dnsName: DNS_DOMAIN, + dnssecConfig: { + state: 'on', + }, + }); + }); + + after(done => { + ZONE.delete({force: true}, done); + }); + + // deal with eventual consistency of ZONE.create(): + it('should return 0 or more zones', async function () { + this.retries(3); + await delayMs(1000); + const zones = await dns.getZones(); + assert(zones!.length >= 0); + }); + + describe('Zones', () => { + it('should get the metadata for a zone', done => { + ZONE.getMetadata((err: Error, metadata: Metadata) => { + assert.ifError(err); + assert.strictEqual(metadata.name, ZONE_NAME); + done(); + }); + }); + + it('should support all types of records', done => { + const recordsToCreate = [ + records.a, + records.aaaa, + records.cname, + records.mx, + // records.naptr, + records.ns, + // records.ptr, + records.soa, + records.spf, + records.srv, + records.txt, + ]; + + ZONE.replaceRecords(['ns', 'soa'], recordsToCreate, done); + }); + + it('should import records from a zone file', done => { + const zoneFilename = require.resolve( + '../../system-test/data/zonefile.zone' + ); + let zoneFileTemplate = fs.readFileSync(zoneFilename, 'utf-8'); + zoneFileTemplate = format(zoneFileTemplate, { + DNS_DOMAIN, + }); + + tmp.setGracefulCleanup(); + tmp.file((err, tmpFilePath) => { + assert.ifError(err); + fs.writeFileSync(tmpFilePath, zoneFileTemplate, 'utf-8'); + ZONE.empty(err => { + assert.ifError(err); + ZONE.import(tmpFilePath, err => { + assert.ifError(err); + + ZONE.getRecords(['spf', 'txt'], (err, records) => { + assert.ifError(err); + + const spfRecord = records!.filter(record => { + return record.type === 'SPF'; + })[0]; + + assert.strictEqual( + spfRecord.toJSON().rrdatas![0], + '"v=spf1" "mx:' + DNS_DOMAIN + '" "-all"' + ); + + const txtRecord = records!.filter(record => { + return record.type === 'TXT'; + })[0]; + + assert.strictEqual( + txtRecord.toJSON().rrdatas![0], + '"google-site-verification=xxxxxxxxxxxxYYYYYYXXX"' + ); + + done(); + }); + }); + }); + }); + }); + + it('should export records to a zone file', async () => { + tmp.setGracefulCleanup(); + const tmpFile: Function = util.promisify(tmp.file); + const tmpFilename = await tmpFile(); + await ZONE.empty(); + await ZONE.addRecords([records.spf, records.srv]); + await ZONE.export(tmpFilename); + }); + + describe('changes', () => { + it('should create a change', done => { + const record = ZONE.record('srv', { + ttl: 3600, + name: DNS_DOMAIN, + data: '10 0 5222 127.0.0.1.', + signatureRrdatas: [], + }); + const change = ZONE.change(); + change.create({add: record}, err => { + assert.ifError(err); + const addition = change.metadata.additions[0]; + delete addition.kind; + assert.deepStrictEqual(addition, record.toJSON()); + done(); + }); + }); + + it('should get a list of changes', done => { + ZONE.getChanges((err, changes) => { + assert.ifError(err); + assert(changes!.length >= 0); + done(); + }); + }); + + it('should get metadata', done => { + ZONE.getChanges((err, changes) => { + assert.ifError(err); + const change = changes![0]; + const expectedMetadata = change.metadata; + change.getMetadata((err: Error, metadata: Metadata) => { + assert.ifError(err); + delete metadata.status; + delete expectedMetadata.status; + assert.deepStrictEqual(metadata, expectedMetadata); + done(); + }); + }); + }); + }); + }); + + describe('Records', () => { + it('should return 0 or more records', done => { + ZONE.getRecords((err, records) => { + assert.ifError(err); + assert(records!.length >= 0); + done(); + }); + }); + + it('should cursor through records by type', done => { + const newRecords = [ + ZONE.record('cname', { + ttl: 86400, + name: '1.' + DNS_DOMAIN, + data: DNS_DOMAIN, + }), + ZONE.record('cname', { + ttl: 86400, + name: '2.' + DNS_DOMAIN, + data: DNS_DOMAIN, + }), + ]; + + ZONE.replaceRecords('cname', newRecords, err => { + assert.ifError(err); + const onRecordsReceived = ( + err?: Error | null, + records?: Record[] | null, + nextQuery?: {} | null + ) => { + if (nextQuery) { + ZONE.getRecords(nextQuery, onRecordsReceived); + return; + } + ZONE.deleteRecords(newRecords, done); + }; + ZONE.getRecords( + { + type: 'cname', + maxResults: 2, + }, + onRecordsReceived + ); + }); + }); + + it('should replace records', async () => { + const name = 'test-zone-' + uuid.v4().substr(0, 18); + // Do this in a new zone so no existing records are affected. + const [zone] = await dns.createZone(name, { + dnsName: DNS_DOMAIN, + dnssecConfig: { + state: 'on', + }, + }); + const [originalRecords] = await zone.getRecords('ns'); + const originalData = originalRecords[0].data; + const newRecord = zone.record('ns', { + ttl: 3600, + name: DNS_DOMAIN, + data: ['ns1.nameserver.net.', 'ns2.nameserver.net.'], + }); + const [change] = await zone.replaceRecords('ns', newRecord); + const deleted = change.metadata.deletions[0].rrdatas; + const added = change.metadata.additions[0].rrdatas; + assert.deepStrictEqual(deleted, originalData); + assert.deepStrictEqual(added, newRecord.data); + await zone.delete({force: true}); + }); + }); +}); diff --git a/packages/google-cloud-dns/test/change.ts b/packages/google-cloud-dns/test/change.ts new file mode 100644 index 00000000000..f2d7d37844c --- /dev/null +++ b/packages/google-cloud-dns/test/change.ts @@ -0,0 +1,162 @@ +// Copyright 2015 Google LLC +// +// 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. + +import { + Metadata, + ServiceObject, + ServiceObjectConfig, +} from '@google-cloud/common'; +import * as promisify from '@google-cloud/promisify'; +import * as assert from 'assert'; +import {describe, it, before, beforeEach} from 'mocha'; +import * as proxyquire from 'proxyquire'; +import {Change} from '../src/change'; + +let promisified = false; +const fakePromisify = Object.assign({}, promisify, { + promisifyAll(esClass: Function) { + if (esClass.name === 'Change') { + promisified = true; + } + }, +}); + +class FakeServiceObject extends ServiceObject { + calledWith_: IArguments; + constructor(config: ServiceObjectConfig) { + super(config); + // eslint-disable-next-line prefer-rest-params + this.calledWith_ = arguments; + } +} + +describe('Change', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let Change: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let change: any; + + const ZONE = { + name: 'zone-name', + createChange() {}, + }; + + const CHANGE_ID = 'change-id'; + + before(() => { + Change = proxyquire('../src/change', { + '@google-cloud/common': { + ServiceObject: FakeServiceObject, + }, + '@google-cloud/promisify': fakePromisify, + }).Change; + }); + + beforeEach(() => { + change = new Change(ZONE, CHANGE_ID); + }); + + describe('instantiation', () => { + it('should inherit from ServiceObject', () => { + assert(change instanceof ServiceObject); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const calledWith = (change as any).calledWith_[0]; + + assert.strictEqual(calledWith.parent, ZONE); + assert.strictEqual(calledWith.baseUrl, '/changes'); + assert.strictEqual(calledWith.id, CHANGE_ID); + assert.deepStrictEqual(calledWith.methods, { + exists: true, + get: true, + getMetadata: true, + }); + }); + + it('should promisify all the things', () => { + assert(promisified); + }); + }); + + describe('change', () => { + it('should call the parent change method', done => { + const config = {}; + + change.parent.createChange = (config_: {}) => { + assert.strictEqual(config, config_); + done(); + }; + + change.create(config, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + const apiResponse = {}; + + beforeEach(() => { + change.parent.createChange = (config: {}, callback: Function) => { + callback(error, null, apiResponse); + }; + }); + + it('should execute callback with error & apiResponse', done => { + change.create( + {}, + (err: Error, change: Change, apiResponse_: Metadata) => { + assert.strictEqual(err, error); + assert.strictEqual(change, null); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + }); + + describe('success', () => { + const changeInstance = { + id: 'id', + metadata: {}, + }; + const apiResponse = {}; + + beforeEach(() => { + change.parent.createChange = (config: {}, callback: Function) => { + callback(null, changeInstance, apiResponse); + }; + }); + + it('should execute callback with self & API response', done => { + change.create( + {}, + (err: Error, change_: Change, apiResponse_: Metadata) => { + assert.ifError(err); + assert.strictEqual(change_, change); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + + it('should assign the ID and metadata from the change', done => { + change.create({}, (err: Error, change_: Change) => { + assert.ifError(err); + assert.strictEqual(change_.id, changeInstance.id); + assert.strictEqual(change_.metadata, changeInstance.metadata); + done(); + }); + }); + }); + }); +}); diff --git a/packages/google-cloud-dns/test/index.ts b/packages/google-cloud-dns/test/index.ts new file mode 100644 index 00000000000..9a105b8cc00 --- /dev/null +++ b/packages/google-cloud-dns/test/index.ts @@ -0,0 +1,431 @@ +// Copyright 2015 Google LLC +// +// 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. + +import { + Service, + ServiceConfig, + ServiceOptions, + util, +} from '@google-cloud/common'; +import * as promisify from '@google-cloud/promisify'; +import arrify = require('arrify'); +import * as assert from 'assert'; +import {describe, it, before, beforeEach} from 'mocha'; +import * as proxyquire from 'proxyquire'; +import {CoreOptions, OptionsWithUri, Response} from 'request'; + +import {Zone} from '../src'; + +let extended = false; +const fakePaginator = { + paginator: { + extend(esClass: Function, methods: string[]) { + if (esClass.name !== 'DNS') { + return; + } + extended = true; + methods = arrify(methods); + assert.strictEqual(esClass.name, 'DNS'); + assert.deepStrictEqual(methods, ['getZones']); + }, + streamify(methodName: string) { + return methodName; + }, + }, +}; + +class FakeService extends Service { + calledWith_: IArguments; + constructor(config: ServiceConfig, options?: ServiceOptions) { + super(config, options); + // eslint-disable-next-line prefer-rest-params + this.calledWith_ = arguments; + } +} + +const fakeUtil = Object.assign({}, util, { + makeAuthenticatedRequestFactory() {}, +}); +const originalFakeUtil = Object.assign({}, fakeUtil); + +let promisified = false; +const fakePromisify = Object.assign({}, promisify, { + // tslint:disable-next-line:variable-name + promisifyAll(esClass: Function, options: promisify.PromisifyAllOptions) { + if (esClass.name !== 'DNS') { + return; + } + promisified = true; + assert.deepStrictEqual(options.exclude, ['zone']); + }, +}); + +class FakeZone { + calledWith_: IArguments; + constructor() { + // eslint-disable-next-line prefer-rest-params + this.calledWith_ = arguments; + } +} + +describe('DNS', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let DNS: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let dns: any; + + const PROJECT_ID = 'project-id'; + + before(() => { + DNS = proxyquire('../src', { + '@google-cloud/common': { + Service: FakeService, + }, + '@google-cloud/paginator': fakePaginator, + '@google-cloud/promisify': fakePromisify, + './zone': { + Zone: FakeZone, + }, + }).DNS; + }); + + beforeEach(() => { + Object.assign(fakeUtil, originalFakeUtil); + dns = new DNS({ + projectId: PROJECT_ID, + }); + }); + + describe('instantiation', () => { + it('should extend the correct methods', () => { + assert(extended); // See `fakePaginator.extend` + }); + + it('should streamify the correct methods', () => { + assert.strictEqual(dns.getZonesStream, 'getZones'); + }); + + it('should promisify all the things', () => { + assert(promisified); + }); + + it('should inherit from Service', () => { + assert(dns instanceof Service); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const calledWith = (dns as any).calledWith_[0]; + + const baseUrl = 'https://dns.googleapis.com/dns/v1'; + assert.strictEqual(calledWith.baseUrl, baseUrl); + assert.deepStrictEqual(calledWith.scopes, [ + 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', + 'https://www.googleapis.com/auth/cloud-platform', + ]); + assert.deepStrictEqual( + calledWith.packageJson, + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../../package.json') + ); + }); + + it('should enable apiEndpoint override', () => { + const apiEndpoint = 'fake.endpoint'; + dns = new DNS({ + projectId: PROJECT_ID, + apiEndpoint, + }); + const calledWith = dns.calledWith_[0]; + assert.strictEqual(calledWith.apiEndpoint, apiEndpoint); + assert.strictEqual(calledWith.baseUrl, `https://${apiEndpoint}/dns/v1`); + }); + }); + + describe('createZone', () => { + const zoneName = 'zone-name'; + const config = {dnsName: 'dns-name'}; + + it('should throw if a zone name is not provided', () => { + assert.throws(() => { + dns.createZone(); + }, /A zone name is required/); + }); + + it('should throw if a zone dnsname is not provided', () => { + assert.throws(() => { + dns.createZone(zoneName); + }, /A zone dnsName is required/); + + assert.throws(() => { + dns.createZone(zoneName, {}); + }, /A zone dnsName is required/); + }); + + it('should use a provided description', done => { + const cfg = Object.assign({}, config, {description: 'description'}); + + dns.request = (reqOpts: CoreOptions) => { + assert.strictEqual(reqOpts.json.description, cfg.description); + done(); + }; + + dns.createZone(zoneName, cfg, assert.ifError); + }); + + it('should default a description to ""', done => { + dns.request = (reqOpts: CoreOptions) => { + assert.strictEqual(reqOpts.json.description, ''); + done(); + }; + + dns.createZone(zoneName, config, assert.ifError); + }); + + it('should make the correct API request', done => { + dns.request = (reqOpts: OptionsWithUri) => { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/managedZones'); + const expectedBody = Object.assign({}, config, { + name: zoneName, + description: '', + }); + assert.deepStrictEqual(reqOpts.json, expectedBody); + + done(); + }; + + dns.createZone(zoneName, config, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + const apiResponse = {a: 'b', c: 'd'}; + + beforeEach(() => { + dns.request = (reqOpts: {}, callback: Function) => { + callback(error, apiResponse); + }; + }); + + it('should execute callback with error and API response', done => { + dns.createZone( + zoneName, + config, + (err: Error, zone: Zone, apiResponse_: Response) => { + assert.strictEqual(err, error); + assert.strictEqual(zone, null); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + }); + + describe('success', () => { + const apiResponse = {name: zoneName}; + const zone = {}; + + beforeEach(() => { + dns.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponse); + }; + + dns.zone = () => { + return zone; + }; + }); + + it('should create a zone from the response', done => { + dns.zone = (name: string) => { + assert.strictEqual(name, apiResponse.name); + setImmediate(done); + return zone; + }; + + dns.createZone(zoneName, config, assert.ifError); + }); + + it('should execute callback with zone and API response', done => { + dns.createZone( + zoneName, + config, + (err: Error, zone_: Zone, apiResponse_: Response) => { + assert.ifError(err); + assert.strictEqual(zone_, zone); + assert.strictEqual(apiResponse_, apiResponse); + + done(); + } + ); + }); + + it('should set the metadata to the response', done => { + dns.createZone(zoneName, config, (err: Error, zone: Zone) => { + assert.strictEqual(zone.metadata, apiResponse); + done(); + }); + }); + }); + }); + + describe('getZones', () => { + it('should make the correct request', done => { + const query = {a: 'b', c: 'd'}; + + dns.request = (reqOpts: OptionsWithUri) => { + assert.strictEqual(reqOpts.uri, '/managedZones'); + assert.strictEqual(reqOpts.qs, query); + + done(); + }; + + dns.getZones(query, assert.ifError); + }); + + it('should use an empty query if one was not provided', done => { + dns.request = (reqOpts: CoreOptions) => { + assert.strictEqual(Object.keys(reqOpts.qs).length, 0); + done(); + }; + + dns.getZones(assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + const apiResponse = {a: 'b', c: 'd'}; + + beforeEach(() => { + dns.request = (reqOpts: {}, callback: Function) => { + callback(error, apiResponse); + }; + }); + + it('should execute callback with error and API response', done => { + dns.getZones( + {}, + ( + err: Error, + zones: Zone[], + nextQuery: {}, + apiResponse_: Response + ) => { + assert.strictEqual(err, error); + assert.strictEqual(zones, null); + assert.strictEqual(nextQuery, null); + assert.strictEqual(apiResponse_, apiResponse); + + done(); + } + ); + }); + }); + + describe('success', () => { + const zone = {name: 'zone-1', a: 'b', c: 'd'}; + const apiResponse = {managedZones: [zone]}; + + beforeEach(() => { + dns.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponse); + }; + + dns.zone = () => { + return zone; + }; + }); + + it('should create zones from the response', done => { + dns.zone = (zoneName: string) => { + assert.strictEqual(zoneName, zone.name); + setImmediate(done); + return zone; + }; + + dns.getZones({}, assert.ifError); + }); + + it('should set a nextQuery if necessary', done => { + const apiResponseWithNextPageToken = Object.assign({}, apiResponse, { + nextPageToken: 'next-page-token', + }); + + const query = {a: 'b', c: 'd'}; + const originalQuery = Object.assign({}, query); + + dns.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponseWithNextPageToken); + }; + + dns.getZones(query, (err: Error, zones: Zone[], nextQuery: {}) => { + assert.ifError(err); + + // Check the original query wasn't modified. + assert.deepStrictEqual(query, originalQuery); + + assert.deepStrictEqual( + nextQuery, + Object.assign({}, query, { + pageToken: apiResponseWithNextPageToken.nextPageToken, + }) + ); + + done(); + }); + }); + + it('should execute callback with zones and API response', done => { + dns.getZones( + {}, + ( + err: Error, + zones: Zone[], + nextQuery: {}, + apiResponse_: Response + ) => { + assert.ifError(err); + assert.strictEqual(zones[0], zone); + assert.strictEqual(nextQuery, null); + assert.strictEqual(apiResponse_, apiResponse); + + done(); + } + ); + }); + + it('should assign metadata to zones', done => { + dns.getZones({}, (err: Error, zones: Zone[]) => { + assert.ifError(err); + assert.strictEqual(zones[0].metadata, zone); + done(); + }); + }); + }); + }); + + describe('zone', () => { + it('should throw if a name is not provided', () => { + assert.throws(() => { + dns.zone(); + }, /A zone name is required/); + }); + + it('should return a Zone', () => { + const newZoneName = 'new-zone-name'; + const newZone = dns.zone(newZoneName); + assert(newZone instanceof FakeZone); + assert.strictEqual(newZone.calledWith_[0], dns); + assert.strictEqual(newZone.calledWith_[1], newZoneName); + }); + }); +}); diff --git a/packages/google-cloud-dns/test/record.ts b/packages/google-cloud-dns/test/record.ts new file mode 100644 index 00000000000..23125baa24c --- /dev/null +++ b/packages/google-cloud-dns/test/record.ts @@ -0,0 +1,357 @@ +// Copyright 2015 Google LLC +// +// 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. + +import * as promisify from '@google-cloud/promisify'; +import * as assert from 'assert'; +import {describe, it, before, beforeEach} from 'mocha'; +import * as proxyquire from 'proxyquire'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import {Record} from '../src'; + +interface Metadata { + name: string; + data?: string[]; + ttl: number; +} +let promisified = false; +const fakePromisify = Object.assign({}, promisify, { + promisifyAll(esClass: Function, options: promisify.PromisifyAllOptions) { + if (esClass.name !== 'Record') { + return; + } + promisified = true; + assert.deepStrictEqual(options.exclude, ['toJSON', 'toString']); + }, +}); + +describe('Record', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let Record: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let record: any; + + const ZONE = { + deleteRecords() {}, + }; + const TYPE = 'A'; + const METADATA = { + name: 'name', + data: [], + ttl: 86400, + }; + + before(() => { + Record = proxyquire('../src/record', { + '@google-cloud/promisify': fakePromisify, + }).Record; + }); + + beforeEach(() => { + record = new Record(ZONE, TYPE, METADATA); + }); + + describe('instantiation', () => { + it('should promisify all the things', () => { + assert(promisified); + }); + + it('should localize the zone instance', () => { + assert.strictEqual(record.zone_, ZONE); + }); + + it('should localize the type', () => { + assert.strictEqual(record.type, TYPE); + }); + + it('should localize the metadata', () => { + assert.strictEqual(record.metadata, METADATA); + }); + + it('should assign the parsed metadata', () => { + const parsedMetadata = record.toJSON(); + delete parsedMetadata.rrdatas; + // tslint:disable-next-line:forin + for (const prop in parsedMetadata) { + assert.strictEqual(record[prop], parsedMetadata[prop]); + } + }); + + it('should re-assign rrdatas to data', () => { + const originalRrdatas = new Array(); + + const recordThatHadRrdatas = new Record(ZONE, TYPE, { + rrdatas: originalRrdatas, + }); + + assert.strictEqual(recordThatHadRrdatas.rrdatas, undefined); + assert.strictEqual(recordThatHadRrdatas.data, originalRrdatas); + }); + }); + + describe('fromZoneRecord_', () => { + describe('a', () => { + const aRecord = { + ip: '0.0.0.0', + name: 'name', + ttl: 86400, + }; + + const expectedData = aRecord.ip; + + it('should parse an A record', () => { + const record = Record.fromZoneRecord_(ZONE, 'a', aRecord); + + assert.strictEqual(record.type, 'A'); + assert.deepStrictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, aRecord.name); + assert.strictEqual(record.metadata.ttl, aRecord.ttl); + }); + }); + + describe('aaaa', () => { + const aaaaRecord = { + ip: '2607:f8b0:400a:801::1005', + name: 'name', + ttl: 86400, + }; + + const expectedData = aaaaRecord.ip; + + it('should parse an AAAA record', () => { + const record = Record.fromZoneRecord_(ZONE, 'aaaa', aaaaRecord); + + assert.strictEqual(record.type, 'AAAA'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, aaaaRecord.name); + assert.strictEqual(record.metadata.ttl, aaaaRecord.ttl); + }); + }); + + describe('cname', () => { + const cnameRecord = { + alias: 'example.com.', + name: 'name', + ttl: 86400, + }; + + const expectedData = cnameRecord.alias; + + it('should parse a CNAME record', () => { + const record = Record.fromZoneRecord_(ZONE, 'cname', cnameRecord); + + assert.strictEqual(record.type, 'CNAME'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, cnameRecord.name); + assert.strictEqual(record.metadata.ttl, cnameRecord.ttl); + }); + }); + + describe('mx', () => { + const mxRecord = { + preference: 0, + host: 'mail', + name: 'name', + ttl: 86400, + }; + + const expectedData = mxRecord.preference + ' ' + mxRecord.host; + + it('should parse an MX record', () => { + const record = Record.fromZoneRecord_(ZONE, 'mx', mxRecord); + + assert.strictEqual(record.type, 'MX'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, mxRecord.name); + assert.strictEqual(record.metadata.ttl, mxRecord.ttl); + }); + }); + + describe('ns', () => { + const nsRecord = { + host: 'example.com', + name: 'name', + ttl: 86400, + }; + + const expectedData = nsRecord.host; + + it('should parse an NS record', () => { + const record = Record.fromZoneRecord_(ZONE, 'ns', nsRecord); + + assert.strictEqual(record.type, 'NS'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, nsRecord.name); + assert.strictEqual(record.metadata.ttl, nsRecord.ttl); + }); + }); + + describe('soa', () => { + const soaRecord = { + mname: 'ns1.nameserver.net.', + rname: 'hostmaster.mydomain.com.', + serial: 86400, + retry: 600, + refresh: 3600, + expire: 604800, + minimum: 86400, + name: 'name', + ttl: 86400, + }; + + const expectedData = [ + soaRecord.mname, + soaRecord.rname, + soaRecord.serial, + soaRecord.retry, + soaRecord.refresh, + soaRecord.expire, + soaRecord.minimum, + ].join(' '); + + it('should parse an SOA record', () => { + const record = Record.fromZoneRecord_(ZONE, 'soa', soaRecord); + + assert.strictEqual(record.type, 'SOA'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, soaRecord.name); + assert.strictEqual(record.metadata.ttl, soaRecord.ttl); + }); + }); + + describe('spf', () => { + const spfRecord = { + data: '"v=spf1" "mx:example.com"', + name: 'name', + ttl: 86400, + }; + + const expectedData = spfRecord.data; + + it('should parse an SPF record', () => { + const record = Record.fromZoneRecord_(ZONE, 'spf', spfRecord); + + assert.strictEqual(record.type, 'SPF'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, spfRecord.name); + assert.strictEqual(record.metadata.ttl, spfRecord.ttl); + }); + }); + + describe('srv', () => { + const srvRecord = { + priority: 10, + weight: 0, + port: 5222, + target: 'jabber', + name: 'name', + ttl: 86400, + }; + + const expectedData = [ + srvRecord.priority, + srvRecord.weight, + srvRecord.port, + srvRecord.target, + ].join(' '); + + it('should parse an SRV record', () => { + const record = Record.fromZoneRecord_(ZONE, 'srv', srvRecord); + + assert.strictEqual(record.type, 'SRV'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, srvRecord.name); + assert.strictEqual(record.metadata.ttl, srvRecord.ttl); + }); + }); + + describe('txt', () => { + const txtRecord = { + txt: 'txt-record-txt', + name: 'name', + ttl: 86400, + }; + + const expectedData = txtRecord.txt; + + it('should parse a TXT record', () => { + const record = Record.fromZoneRecord_(ZONE, 'txt', txtRecord); + + assert.strictEqual(record.type, 'TXT'); + assert.strictEqual(record.metadata.data, expectedData); + assert.strictEqual(record.metadata.name, txtRecord.name); + assert.strictEqual(record.metadata.ttl, txtRecord.ttl); + }); + }); + }); + + describe('delete', () => { + it('should call zone.deleteRecords', (done: any) => { + record.zone_.deleteRecords = (records: Record[], callback: Function) => { + assert.strictEqual(records, record); + callback(); + }; + record.delete(done); + }); + }); + + describe('toJSON', () => { + it('should format the data for the API', () => { + const expectedRecord: Metadata = Object.assign({}, METADATA, { + type: 'A', + rrdatas: METADATA.data, + }); + delete expectedRecord.data; + + assert.deepStrictEqual(record.toJSON(), expectedRecord); + }); + }); + + describe('toString', () => { + it('should format the data for a zonefile', () => { + const jsonRecord = Object.assign({}, METADATA, { + type: TYPE, + rrdatas: ['example.com.', 'example2.com.'], + }); + + record.toJSON = () => { + return jsonRecord; + }; + + const expectedRecordString = [ + [ + jsonRecord.name, + jsonRecord.ttl, + 'IN', + TYPE, + jsonRecord.rrdatas[0], + ].join(' '), + + [ + jsonRecord.name, + jsonRecord.ttl, + 'IN', + TYPE, + jsonRecord.rrdatas[1], + ].join(' '), + ].join('\n'); + + // That's a bunch of silliness, but it generates simply: + // name 86400 IN A example.com. + // name 86400 IN A example2.com. + + assert.strictEqual(record.toString(), expectedRecordString); + }); + }); +}); diff --git a/packages/google-cloud-dns/test/zone.ts b/packages/google-cloud-dns/test/zone.ts new file mode 100644 index 00000000000..6968b68baad --- /dev/null +++ b/packages/google-cloud-dns/test/zone.ts @@ -0,0 +1,1067 @@ +// Copyright 2015 Google LLC +// +// 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. + +import {ServiceObject, ServiceObjectConfig} from '@google-cloud/common'; +import * as promisify from '@google-cloud/promisify'; +import arrify = require('arrify'); +import * as assert from 'assert'; +import {describe, it, before, beforeEach} from 'mocha'; +import * as proxyquire from 'proxyquire'; +import {CoreOptions, OptionsWithUri, Response} from 'request'; +import * as uuid from 'uuid'; + +import {Change, CreateChangeRequest} from '../src/change'; +import {Record, RecordObject, RecordMetadata} from '../src/record'; + +let promisified = false; +const fakePromisify = Object.assign({}, promisify, { + promisifyAll(esClass: Function, options: promisify.PromisifyAllOptions) { + if (esClass.name !== 'Zone') { + return; + } + promisified = true; + assert.deepStrictEqual(options.exclude, ['change', 'record']); + }, +}); + +let parseOverride: Function | null; +const fakeDnsZonefile = { + parse() { + // eslint-disable-next-line prefer-spread, prefer-rest-params + return (parseOverride || (() => {})).apply(null, arguments); + }, +}; + +let writeFileOverride: Function | null; +let readFileOverride: Function | null; +const fakeFs = { + readFile() { + // eslint-disable-next-line prefer-spread, prefer-rest-params + return (readFileOverride || (() => {})).apply(null, arguments); + }, + writeFile() { + // eslint-disable-next-line prefer-spread, prefer-rest-params + return (writeFileOverride || (() => {})).apply(null, arguments); + }, +}; + +class FakeChange { + calledWith_: Array<{}>; + constructor(...args: Array<{}>) { + this.calledWith_ = args; + } +} + +class FakeRecord { + calledWith_: Array<{}>; + constructor(...args: Array<{}>) { + this.calledWith_ = args; + } + static fromZoneRecord_(...args: Array<{}>) { + const record = new FakeRecord(); + record.calledWith_ = args; + return record; + } +} + +class FakeServiceObject extends ServiceObject { + calledWith_: Array<{}>; + constructor(config: ServiceObjectConfig, ...args: Array<{}>) { + super(config); + this.calledWith_ = args; + } +} + +let extended = false; +const fakePaginator = { + paginator: { + extend(esClass: Function, methods: string[]) { + if (esClass.name !== 'Zone') { + return; + } + extended = true; + methods = arrify(methods); + assert.strictEqual(esClass.name, 'Zone'); + assert.deepStrictEqual(methods, ['getChanges', 'getRecords']); + }, + streamify(methodName: string) { + return methodName; + }, + }, +}; + +describe('Zone', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let Zone: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let zone: any; + + const DNS = { + createZone() {}, + }; + const ZONE_NAME = 'zone-name'; + + before(() => { + Zone = proxyquire('../src/zone.js', { + 'dns-zonefile': fakeDnsZonefile, + fs: fakeFs, + '@google-cloud/common': { + ServiceObject: FakeServiceObject, + }, + '@google-cloud/promisify': fakePromisify, + '@google-cloud/paginator': fakePaginator, + './change': { + Change: FakeChange, + }, + './record': {Record: FakeRecord}, + }).Zone; + }); + + beforeEach(() => { + parseOverride = null; + readFileOverride = null; + writeFileOverride = null; + zone = new Zone(DNS, ZONE_NAME); + }); + + describe('instantiation', () => { + it('should promisify all the things', () => { + assert(promisified); + }); + + it('should extend the correct methods', () => { + assert(extended); // See `fakePaginator.extend` + }); + + it('should streamify the correct methods', () => { + assert.strictEqual(zone.getChangesStream, 'getChanges'); + assert.strictEqual(zone.getRecordsStream, 'getRecords'); + }); + + it('should localize the name', () => { + assert.strictEqual(zone.name, ZONE_NAME); + }); + + it('should inherit from ServiceObject', done => { + const dnsInstance = Object.assign({}, DNS, { + createZone: { + bind(context: {}) { + assert.strictEqual(context, dnsInstance); + done(); + }, + }, + }); + + const zone = new Zone(dnsInstance, ZONE_NAME); + assert(zone instanceof ServiceObject); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const calledWith = (zone as any).calledWith_[0]; + + assert.strictEqual(calledWith.parent, dnsInstance); + assert.strictEqual(calledWith.baseUrl, '/managedZones'); + assert.strictEqual(calledWith.id, ZONE_NAME); + assert.deepStrictEqual(calledWith.methods, { + create: true, + exists: true, + get: true, + getMetadata: true, + }); + }); + }); + + describe('addRecords', () => { + it('should create a change with additions', done => { + const records = ['a', 'b', 'c']; + + zone.createChange = ( + options: CreateChangeRequest, + callback: Function + ) => { + assert.strictEqual(options.add, records); + callback(); + }; + + zone.addRecords(records, done); + }); + }); + + describe('change', () => { + it('should return a Change object', () => { + const changeId = 'change-id'; + const change = zone.change(changeId); + assert(change instanceof FakeChange); + assert.strictEqual(change.calledWith_[0], zone); + assert.strictEqual(change.calledWith_[1], changeId); + }); + }); + + describe('createChange', () => { + function generateRecord(recordJson?: {}) { + recordJson = Object.assign( + { + name: uuid.v1(), + type: uuid.v1(), + rrdatas: [uuid.v1(), uuid.v1()], + }, + recordJson + ); + + return { + toJSON() { + return recordJson! as {rrdatas: Array<{}>}; + }, + }; + } + + it('should throw error if add or delete is not provided', () => { + assert.throws(() => { + zone.createChange({}, () => {}); + }, /Cannot create a change with no additions or deletions/); + }); + + it('should parse and rename add to additions', done => { + const recordsToAdd = [generateRecord(), generateRecord()]; + + const expectedAdditions = recordsToAdd.map(x => x.toJSON()); + + zone.request = (reqOpts: CoreOptions) => { + assert.strictEqual(reqOpts.json.add, undefined); + assert.deepStrictEqual(reqOpts.json.additions, expectedAdditions); + done(); + }; + + zone.createChange({add: recordsToAdd}, assert.ifError); + }); + + it('should parse and rename delete to deletions', done => { + const recordsToDelete = [generateRecord(), generateRecord()]; + + const expectedDeletions = recordsToDelete.map(x => x.toJSON()); + + zone.request = (reqOpts: CoreOptions) => { + assert.strictEqual(reqOpts.json.delete, undefined); + assert.deepStrictEqual(reqOpts.json.deletions, expectedDeletions); + done(); + }; + + zone.createChange({delete: recordsToDelete}, assert.ifError); + }); + + it('should group changes by name and type', done => { + const recordsToAdd = [ + generateRecord({name: 'name.com.', type: 'mx'}), + generateRecord({name: 'name.com.', type: 'mx'}), + ]; + + zone.request = (reqOpts: CoreOptions) => { + const expectedRRDatas = recordsToAdd + .map(x => x.toJSON().rrdatas) + .reduce((acc, rrdata) => acc.concat(rrdata), []); + + assert.deepStrictEqual(reqOpts.json.additions, [ + { + name: 'name.com.', + type: 'mx', + rrdatas: expectedRRDatas, + }, + ]); + + done(); + }; + + zone.createChange({add: recordsToAdd}, assert.ifError); + }); + + it('should make correct API request', done => { + zone.request = (reqOpts: OptionsWithUri) => { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual(reqOpts.uri, '/changes'); + + done(); + }; + + zone.createChange({add: []}, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + const apiResponse = {a: 'b', c: 'd'}; + + beforeEach(() => { + zone.request = (reqOpts: {}, callback: Function) => { + callback(error, apiResponse); + }; + }); + + it('should execute callback with error & API response', done => { + zone.createChange( + {add: []}, + (err: Error, change: Change, apiResponse_: Response) => { + assert.strictEqual(err, error); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + }); + + describe('success', () => { + const apiResponse = {id: 1, a: 'b', c: 'd'}; + + beforeEach(() => { + zone.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponse); + }; + }); + + it('should execute callback with Change & API response', done => { + const change = {}; + + zone.change = (id: string) => { + assert.strictEqual(id, apiResponse.id); + return change; + }; + + zone.createChange( + {add: []}, + (err: Error, change_: Change, apiResponse_: Response) => { + assert.ifError(err); + assert.strictEqual(change_, change); + assert.strictEqual(change_.metadata, apiResponse); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + }); + }); + + describe('delete', () => { + describe('force', () => { + it('should empty the zone', done => { + zone.empty = () => { + done(); + }; + + zone.delete({force: true}, assert.ifError); + }); + + it('should try to delete again after emptying', done => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (FakeServiceObject.prototype as any).delete = () => { + done(); + }; + + zone.empty = (callback: Function) => { + callback(); + }; + + zone.delete({force: true}, assert.ifError); + }); + }); + }); + + describe('deleteRecords', () => { + it('should delete records by type if a string is given', done => { + const recordsToDelete = 'ns'; + + zone.deleteRecordsByType_ = (types: string[], callback: Function) => { + assert.deepStrictEqual(types, [recordsToDelete]); + callback(); + }; + + zone.deleteRecords(recordsToDelete, done); + }); + + it('should create a change if record objects given', done => { + const recordsToDelete = {a: 'b', c: 'd'}; + + zone.createChange = ( + options: CreateChangeRequest, + callback: Function + ) => { + assert.deepStrictEqual(options.delete, [recordsToDelete]); + callback(); + }; + + zone.deleteRecords(recordsToDelete, done); + }); + }); + + describe('empty', () => { + it('should get all records', done => { + zone.getRecords = () => { + done(); + }; + + zone.empty(assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + + beforeEach(() => { + zone.getRecords = (callback: Function) => { + callback(error); + }; + }); + + it('should execute callback with error', done => { + zone.empty((err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + const records = [ + {type: 'A'}, + {type: 'AAAA'}, + {type: 'CNAME'}, + {type: 'MX'}, + {type: 'NAPTR'}, + {type: 'NS'}, + {type: 'PTR'}, + {type: 'SOA'}, + {type: 'SPF'}, + {type: 'SRV'}, + {type: 'TXT'}, + ]; + + const expectedRecordsToDelete = records.filter(record => { + return record.type !== 'NS' && record.type !== 'SOA'; + }); + + beforeEach(() => { + zone.getRecords = (callback: Function) => { + callback(null, records); + }; + }); + + it('should execute callback if no records matched', done => { + zone.getRecords = (callback: Function) => { + callback(null, []); + }; + + zone.empty(done); + }); + + it('should delete non-NS and non-SOA records', done => { + zone.deleteRecords = ( + recordsToDelete: string[], + callback: Function + ) => { + assert.deepStrictEqual(recordsToDelete, expectedRecordsToDelete); + callback(); + }; + + zone.empty(done); + }); + }); + }); + + describe('export', () => { + const path = './zonefile'; + + const records = [ + { + toString() { + return 'a'; + }, + }, + { + toString() { + return 'a'; + }, + }, + { + toString() { + return 'a'; + }, + }, + { + toString() { + return 'a'; + }, + }, + ]; + + const expectedZonefileContents = 'a\na\na\na'; + + beforeEach(() => { + zone.getRecords = (callback: Function) => { + callback(null, records); + }; + }); + + describe('get records', () => { + describe('error', () => { + const error = new Error('Error.'); + + it('should execute callback with error', done => { + zone.getRecords = (callback: Function) => { + callback(error); + }; + + zone.export(path, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + it('should get all records', done => { + zone.getRecords = () => { + done(); + }; + + zone.export(path, assert.ifError); + }); + }); + }); + + describe('write file', () => { + it('should write correct zone file', done => { + writeFileOverride = ( + path_: string, + content: string, + encoding: string + ) => { + assert.strictEqual(path_, path); + assert.strictEqual(content, expectedZonefileContents); + assert.strictEqual(encoding, 'utf-8'); + + done(); + }; + + zone.export(path, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + + beforeEach(() => { + writeFileOverride = ( + path: string, + content: string, + encoding: string, + callback: Function + ) => { + callback(error); + }; + }); + + it('should execute the callback with an error', done => { + zone.export(path, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + beforeEach(() => { + writeFileOverride = ( + path: string, + content: string, + encoding: string, + callback: Function + ) => { + callback(); + }; + }); + + it('should execute the callback', done => { + zone.export(path, (err: Error) => { + assert.ifError(err); + done(); + }); + }); + }); + }); + }); + + describe('getChanges', () => { + it('should accept only a callback', done => { + zone.request = (reqOpts: CoreOptions) => { + assert.deepStrictEqual(reqOpts.qs, {}); + done(); + }; + + zone.getChanges(assert.ifError); + }); + + it('should accept a sort', done => { + const query = {sort: 'desc'}; + + zone.request = (reqOpts: CoreOptions) => { + assert.strictEqual(reqOpts.qs.sortOrder, 'descending'); + assert.strictEqual(reqOpts.qs.sort, undefined); + + done(); + }; + + zone.getChanges(query, assert.ifError); + }); + + it('should make the correct API request', done => { + const query = {a: 'b', c: 'd'}; + + zone.request = (reqOpts: OptionsWithUri) => { + assert.strictEqual(reqOpts.uri, '/changes'); + assert.strictEqual(reqOpts.qs, query); + + done(); + }; + + zone.getChanges(query, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + const apiResponse = {a: 'b', c: 'd'}; + + beforeEach(() => { + zone.request = (reqOpts: {}, callback: Function) => { + callback(error, apiResponse); + }; + }); + + it('should execute callback with error & API response', done => { + zone.getChanges( + {}, + ( + err: Error, + changes: Change[], + nextQuery: {}, + apiResponse_: Response + ) => { + assert.strictEqual(err, error); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + }); + + describe('success', () => { + const apiResponse = { + changes: [{id: 1}], + }; + + beforeEach(() => { + zone.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponse); + }; + }); + + it('should build a nextQuery if necessary', done => { + const nextPageToken = 'next-page-token'; + const apiResponseWithNextPageToken = Object.assign({}, apiResponse, { + nextPageToken, + }); + const expectedNextQuery = { + pageToken: nextPageToken, + }; + + zone.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponseWithNextPageToken); + }; + + zone.getChanges({}, (err: Error, changes: Change[], nextQuery: {}) => { + assert.ifError(err); + assert.deepStrictEqual(nextQuery, expectedNextQuery); + done(); + }); + }); + + it('should execute callback with Changes & API response', done => { + const change = {}; + + zone.change = (id: string) => { + assert.strictEqual(id, apiResponse.changes[0].id); + return change; + }; + + zone.getChanges( + {}, + ( + err: Error, + changes: Change[], + nextQuery: {}, + apiResponse_: Response + ) => { + assert.ifError(err); + assert.strictEqual(changes[0], change); + assert.strictEqual(changes[0].metadata, apiResponse.changes[0]); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + }); + }); + + describe('getRecords', () => { + describe('error', () => { + const error = new Error('Error.'); + const apiResponse = {a: 'b', c: 'd'}; + + beforeEach(() => { + zone.request = (reqOpts: {}, callback: Function) => { + callback(error, apiResponse); + }; + }); + + it('should execute callback with error & API response', done => { + zone.getRecords( + {}, + ( + err: Error, + changes: Change[], + nextQuery: {}, + apiResponse_: Response + ) => { + assert.strictEqual(err, error); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + }); + + it('should not require a query', done => { + zone.getRecords((err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + const apiResponse = { + rrsets: [{type: 'NS'}], + }; + + beforeEach(() => { + zone.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponse); + }; + }); + + it('should execute callback with nextQuery if necessary', done => { + const nextPageToken = 'next-page-token'; + const apiResponseWithNextPageToken = Object.assign({}, apiResponse, { + nextPageToken, + }); + const expectedNextQuery = {pageToken: nextPageToken}; + + zone.request = (reqOpts: {}, callback: Function) => { + callback(null, apiResponseWithNextPageToken); + }; + + zone.getRecords({}, (err: Error, records: Record[], nextQuery: {}) => { + assert.ifError(err); + assert.deepStrictEqual(nextQuery, expectedNextQuery); + done(); + }); + }); + + it('should execute callback with Records & API response', done => { + const record = {}; + + zone.record = (type: string, recordObject: RecordObject) => { + assert.strictEqual(type, apiResponse.rrsets[0].type); + assert.strictEqual(recordObject, apiResponse.rrsets[0]); + return record; + }; + + zone.getRecords( + {}, + ( + err: Error, + records: Record[], + nextQuery: {}, + apiResponse_: Response + ) => { + assert.ifError(err); + assert.strictEqual(records[0], record); + assert.strictEqual(apiResponse_, apiResponse); + done(); + } + ); + + it('should not require a query', done => { + zone.getRecords(done); + }); + }); + + describe('filtering', () => { + it('should accept a string type', done => { + const types = ['MX', 'CNAME']; + + zone.getRecords(types, (err: Error, records: Record[]) => { + assert.ifError(err); + + assert.strictEqual(records.length, 0); + + done(); + }); + }); + + it('should accept an array of types', done => { + const type = 'MX'; + + zone.getRecords(type, (err: Error, records: Record[]) => { + assert.ifError(err); + + assert.strictEqual(records.length, 0); + + done(); + }); + }); + + it('should not send filterByTypes_ in API request', done => { + zone.request = (reqOpts: CoreOptions) => { + assert.strictEqual(reqOpts.qs.filterByTypes_, undefined); + done(); + }; + + zone.getRecords('NS', assert.ifError); + }); + }); + }); + }); + + describe('import', () => { + const path = './zonefile'; + + it('should read from the file', done => { + readFileOverride = (path_: string, encoding: string) => { + assert.strictEqual(path, path); + assert.strictEqual(encoding, 'utf-8'); + done(); + }; + + zone.import(path, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + + beforeEach(() => { + readFileOverride = ( + path: string, + encoding: string, + callback: Function + ) => { + callback(error); + }; + }); + + it('should execute the callback', done => { + zone.import(path, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + const recordType = 'ns'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let parsedZonefile: any = {}; + + beforeEach(() => { + parsedZonefile = { + [recordType]: {a: 'b', c: 'd'}, + }; + + parseOverride = () => { + return parsedZonefile; + }; + + readFileOverride = ( + path: string, + encoding: string, + callback: Function + ) => { + callback(); + }; + }); + + it('should add records', done => { + zone.addRecords = ( + recordsToCreate: FakeRecord[], + callback: Function + ) => { + assert.strictEqual(recordsToCreate.length, 1); + const recordToCreate = recordsToCreate[0]; + assert(recordToCreate instanceof FakeRecord); + const args = recordToCreate.calledWith_; + assert.strictEqual(args[0], zone); + assert.strictEqual(args[1], recordType); + assert.strictEqual(args[2], parsedZonefile[recordType]); + callback(); + }; + zone.import(path, done); + }); + + it('should use the default ttl', done => { + const defaultTTL = '90'; + parsedZonefile.$ttl = defaultTTL; + parsedZonefile[recordType] = {}; + parsedZonefile.mx = {ttl: '180'}; + zone.addRecords = (recordsToCreate: FakeRecord[]) => { + const record1 = recordsToCreate[0].calledWith_[2]; + assert.strictEqual((record1 as RecordMetadata).ttl, defaultTTL); + const record2 = recordsToCreate[1].calledWith_[2]; + assert.strictEqual((record2 as RecordMetadata).ttl, '180'); + done(); + }; + zone.import(path, done); + }); + }); + }); + + describe('record', () => { + it('should return a Record object', () => { + const type = 'a'; + const metadata = {a: 'b', c: 'd'}; + const record = zone.record(type, metadata); + assert(record instanceof FakeRecord); + const args = record.calledWith_; + assert.strictEqual(args[0], zone); + assert.strictEqual(args[1], type); + assert.strictEqual(args[2], metadata); + }); + }); + + describe('replaceRecords', () => { + it('should get records', done => { + const recordType = 'ns'; + zone.getRecords = (recordType_: string) => { + assert.strictEqual(recordType_, recordType); + done(); + }; + zone.replaceRecords(recordType, [], assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + beforeEach(() => { + zone.getRecords = (recordType: string, callback: Function) => { + callback(error); + }; + }); + + it('should execute callback with error', done => { + zone.replaceRecords('a', [], (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + const recordsToCreate = [ + {a: 'b', c: 'd'}, + {a: 'b', c: 'd'}, + {a: 'b', c: 'd'}, + ]; + + const recordsToDelete = [ + {a: 'b', c: 'd'}, + {a: 'b', c: 'd'}, + {a: 'b', c: 'd'}, + ]; + + beforeEach(() => { + zone.getRecords = (recordType: string, callback: Function) => { + callback(null, recordsToDelete); + }; + }); + + it('should create a change', done => { + zone.createChange = ( + options: CreateChangeRequest, + callback: Function + ) => { + assert.strictEqual(options.add, recordsToCreate); + assert.strictEqual(options.delete, recordsToDelete); + callback(); + }; + zone.replaceRecords('a', recordsToCreate, done); + }); + }); + }); + + describe('deleteRecordsByType_', () => { + it('should get records', done => { + const recordType = 'ns'; + zone.getRecords = (recordType_: string) => { + assert.strictEqual(recordType_, recordType); + done(); + }; + zone.deleteRecordsByType_(recordType, assert.ifError); + }); + + describe('error', () => { + const error = new Error('Error.'); + beforeEach(() => { + zone.getRecords = (recordType: string, callback: Function) => { + callback(error); + }; + }); + + it('should execute callback with error', done => { + zone.deleteRecordsByType_('a', (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + + describe('success', () => { + const recordsToDelete = [ + {a: 'b', c: 'd'}, + {a: 'b', c: 'd'}, + {a: 'b', c: 'd'}, + ]; + + beforeEach(() => { + zone.getRecords = (recordType: string, callback: Function) => { + callback(null, recordsToDelete); + }; + }); + + it('should execute callback if no records matched', done => { + zone.getRecords = (recordType: string, callback: Function) => { + callback(null, []); + }; + zone.deleteRecordsByType_('a', done); + }); + + it('should delete records', done => { + zone.deleteRecords = (records: Record[], callback: Function) => { + assert.strictEqual(records, recordsToDelete); + callback(); + }; + zone.deleteRecordsByType_('a', done); + }); + }); + }); +}); diff --git a/packages/google-cloud-dns/tsconfig.json b/packages/google-cloud-dns/tsconfig.json new file mode 100644 index 00000000000..8b14ad98550 --- /dev/null +++ b/packages/google-cloud-dns/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "./node_modules/gts/tsconfig-google.json", + "compilerOptions": { + "lib": ["es2018", "dom"], + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/*.ts", + "test/*.ts", + "system-test/*.ts" + ] +} diff --git a/release-please-config.json b/release-please-config.json index 6633bf96b5e..005772e44e3 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -42,6 +42,7 @@ "packages/google-cloud-compute": {}, "packages/google-cloud-confidentialcomputing": {}, "packages/google-cloud-config": {}, + "packages/google-cloud-connectors": {}, "packages/google-cloud-contactcenterinsights": {}, "packages/google-cloud-contentwarehouse": {}, "packages/google-cloud-datacatalog": {}, @@ -57,6 +58,7 @@ "packages/google-cloud-dialogflow": {}, "packages/google-cloud-dialogflow-cx": {}, "packages/google-cloud-discoveryengine": {}, + "packages/google-cloud-dns": {}, "packages/google-cloud-documentai": {}, "packages/google-cloud-domains": {}, "packages/google-cloud-edgecontainer": {}, @@ -152,8 +154,7 @@ "packages/google-privacy-dlp": {}, "packages/google-storagetransfer": {}, "packages/grafeas": {}, - "packages/typeless-sample-bot": {}, - "packages/google-cloud-connectors": {} + "packages/typeless-sample-bot": {} }, "plugins": [ {