├── .compodocrc ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github ├── .OwlBot.lock.yaml ├── .OwlBot.yaml ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── bug_report.yml │ ├── config.yml │ ├── documentation_request.yml │ ├── feature_request.md │ ├── feature_request.yml │ ├── processs_request.md │ ├── question.md │ ├── questions.md │ └── support_request.md ├── PULL_REQUEST_TEMPLATE.md ├── auto-approve.yml ├── auto-label.yaml ├── generated-files-bot.yml ├── lock.yml ├── publish.yml ├── release-please.yml ├── release-trigger.yml ├── scripts │ ├── close-invalid-link.cjs │ ├── close-unresponsive.cjs │ ├── fixtures │ │ ├── invalidIssueBody.txt │ │ ├── validIssueBody.txt │ │ └── validIssueBodyDifferentLinkLocation.txt │ ├── package.json │ ├── remove-response-label.cjs │ └── tests │ │ ├── close-invalid-link.test.cjs │ │ └── close-or-remove-response-label.test.cjs ├── sync-repo-settings.yaml └── workflows │ ├── ci.yaml │ ├── codeql-analysis.yml │ ├── issues-no-repro.yaml │ └── response.yaml ├── .gitignore ├── .jsdoc.js ├── .kokoro ├── .gitattributes ├── browser-test.sh ├── common.cfg ├── continuous │ └── node18 │ │ ├── common.cfg │ │ ├── lint.cfg │ │ ├── samples-test.cfg │ │ ├── system-test.cfg │ │ └── test.cfg ├── docs.sh ├── lint.sh ├── populate-secrets.sh ├── presubmit │ ├── node18 │ │ ├── common.cfg │ │ ├── samples-test.cfg │ │ ├── system-test.cfg │ │ └── test.cfg │ └── windows │ │ ├── common.cfg │ │ └── test.cfg ├── publish.sh ├── release │ ├── common.cfg │ ├── docs-devsite.cfg │ ├── docs-devsite.sh │ ├── docs.cfg │ ├── docs.sh │ └── publish.cfg ├── samples-test.sh ├── system-test.sh ├── test.bat ├── test.sh ├── trampoline.sh └── trampoline_v2.sh ├── .mocharc.js ├── .nycrc ├── .prettierignore ├── .prettierrc.js ├── .repo-metadata.json ├── .trampolinerc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── browser-test ├── .eslintrc.json ├── browser-test-runner.ts └── test.browser.ts ├── karma.conf.js ├── linkinator.config.json ├── owlbot.py ├── package.json ├── renovate.json ├── samples ├── package.json ├── quickstart.js └── test │ └── test.samples.js ├── src ├── .eslintrc.json ├── common.ts ├── gaxios.ts ├── index.ts ├── interceptor.ts ├── retry.ts └── util.cts ├── system-test ├── fixtures │ └── sample │ │ ├── package.json │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js └── test.install.ts ├── test ├── fixtures │ ├── fake.cert │ └── fake.key ├── test.getch.ts ├── test.index.ts └── test.retry.ts ├── tsconfig.base.json ├── tsconfig.cjs.json ├── tsconfig.json ├── utils └── enable-esm.mjs ├── webpack-tests.config.js └── webpack.config.js /.compodocrc: -------------------------------------------------------------------------------- 1 | --- 2 | tsconfig: ./tsconfig.json 3 | output: ./docs 4 | theme: material 5 | hideGenerator: true 6 | disablePrivate: true 7 | disableProtected: true 8 | disableInternal: true 9 | disableCoverage: true 10 | disableGraph: true 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/coverage 3 | test/fixtures 4 | build/ 5 | docs/ 6 | protos/ 7 | samples/generated/ 8 | system-test/**/fixtures 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ts text eol=lf 2 | *.js text eol=lf 3 | protos/* linguist-generated 4 | **/api-extractor.json linguist-language=JSON-with-Comments 5 | -------------------------------------------------------------------------------- /.github/.OwlBot.lock.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | docker: 15 | image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest 16 | digest: sha256:da69f1fd77b825b0520b1b0a047c270a3f7e3a42e4d46a5321376281cef6e62b 17 | # created: 2025-06-02T21:06:54.667555755Z 18 | -------------------------------------------------------------------------------- /.github/.OwlBot.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | docker: 15 | image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest 16 | 17 | 18 | begin-after-commit-hash: 397c0bfd367a2427104f988d5329bc117caafd95 19 | 20 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners file. 2 | # This file controls who is tagged for review for any given pull request. 3 | # 4 | # For syntax help see: 5 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax 6 | 7 | 8 | # Unless specified, the jsteam is the default owner for nodejs repositories. 9 | * @googleapis/jsteam -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: 'type: bug, priority: p2' 5 | --- 6 | 7 | Thanks for stopping by to let us know something could be better! 8 | 9 | **PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. 10 | 11 | 1) Is this a client library issue or a product issue? 12 | This is the client library for . We will only be able to assist with issues that pertain to the behaviors of this library. If the issue you're experiencing is due to the behavior of the product itself, please visit the [ Support page]() to reach the most relevant engineers. 13 | 14 | 2) Did someone already solve this? 15 | - Search the issues already opened: https://github.com/googleapis/gaxios/issues 16 | - Search the issues on our "catch-all" repository: https://github.com/googleapis/google-cloud-node 17 | - Search or ask on StackOverflow (engineers monitor these tags): http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js 18 | 19 | 3) Do you have a support contract? 20 | Please create an issue in the [support console](https://cloud.google.com/support/) to ensure a timely response. 21 | 22 | If the support paths suggested above still do not result in a resolution, please provide the following details. 23 | 24 | #### Environment details 25 | 26 | - OS: 27 | - Node.js version: 28 | - npm version: 29 | - `gaxios` version: 30 | 31 | #### Steps to reproduce 32 | 33 | 1. ? 34 | 2. ? 35 | 36 | Making sure to follow these steps will guarantee the quickest resolution possible. 37 | 38 | Thanks! 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve 3 | labels: 4 | - bug 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: > 9 | **PLEASE READ**: If you have a support contract with Google, please 10 | create an issue in the [support 11 | console](https://cloud.google.com/support/) instead of filing on GitHub. 12 | This will ensure a timely response. Otherwise, please make sure to 13 | follow the steps below. 14 | - type: checkboxes 15 | attributes: 16 | label: Please make sure you have searched for information in the following 17 | guides. 18 | options: 19 | - label: "Search the issues already opened: 20 | https://github.com/GoogleCloudPlatform/google-cloud-node/issues" 21 | required: true 22 | - label: "Search StackOverflow: 23 | http://stackoverflow.com/questions/tagged/google-cloud-platform+nod\ 24 | e.js" 25 | required: true 26 | - label: "Check our Troubleshooting guide: 27 | https://github.com/googleapis/google-cloud-node/blob/main/docs/trou\ 28 | bleshooting.md" 29 | required: true 30 | - label: "Check our FAQ: 31 | https://github.com/googleapis/google-cloud-node/blob/main/docs/faq.\ 32 | md" 33 | required: true 34 | - label: "Check our libraries HOW-TO: 35 | https://github.com/googleapis/gax-nodejs/blob/main/client-libraries\ 36 | .md" 37 | required: true 38 | - label: "Check out our authentication guide: 39 | https://github.com/googleapis/google-auth-library-nodejs" 40 | required: true 41 | - label: "Check out handwritten samples for many of our APIs: 42 | https://github.com/GoogleCloudPlatform/nodejs-docs-samples" 43 | required: true 44 | - type: textarea 45 | attributes: 46 | label: > 47 | A screenshot that you have tested with "Try this API". 48 | description: > 49 | As our client libraries are mostly autogenerated, we kindly request 50 | that you test whether your issue is with the client library, or with the 51 | API itself. To do so, please search for your API 52 | here: https://developers.google.com/apis-explorer and attempt to 53 | reproduce the issue in the given method. Please include a screenshot of 54 | the response in "Try this API". This response should NOT match the current 55 | behavior you are experiencing. If the behavior is the same, it means 56 | that you are likely experiencing a bug with the API itself. In that 57 | case, please submit an issue to the API team, either by submitting an 58 | issue in its issue tracker (https://cloud.google.com/support/docs/issue-trackers), or by 59 | submitting an issue in its linked tracker in the .repo-metadata.json 60 | file 61 | validations: 62 | required: true 63 | - type: input 64 | attributes: 65 | label: > 66 | Link to the code that reproduces this issue. A link to a **public** Github Repository or gist with a minimal 67 | reproduction. 68 | description: > 69 | **Skipping this or providing an invalid link will result in the issue being closed** 70 | validations: 71 | required: true 72 | - type: textarea 73 | attributes: 74 | label: > 75 | A step-by-step description of how to reproduce the issue, based on 76 | the linked reproduction. 77 | description: > 78 | Screenshots can be provided in the issue body below. 79 | placeholder: | 80 | 1. Start the application in development (next dev) 81 | 2. Click X 82 | 3. Y will happen 83 | validations: 84 | required: true 85 | - type: textarea 86 | attributes: 87 | label: A clear and concise description of what the bug is, and what you 88 | expected to happen. 89 | placeholder: Following the steps from the previous section, I expected A to 90 | happen, but I observed B instead 91 | validations: 92 | required: true 93 | 94 | - type: textarea 95 | attributes: 96 | label: A clear and concise description WHY you expect this behavior, i.e., was it a recent change, there is documentation that points to this behavior, etc. ** 97 | placeholder: 'Documentation here(link) states that B should happen instead of A' 98 | validations: 99 | required: true 100 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Google Cloud Support 3 | url: https://cloud.google.com/support/ 4 | about: If you have a support contract with Google, please use the Google Cloud Support portal. 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation_request.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Requests 2 | description: Requests for more information 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: > 7 | Please use this issue type to log documentation requests against the library itself. 8 | These requests should involve documentation on Github (`.md` files), and should relate to the library 9 | itself. If you have questions or documentation requests for an API, please 10 | reach out to the API tracker itself. 11 | 12 | Please submit an issue to the API team, either by submitting an 13 | issue in its issue tracker https://cloud.google.com/support/docs/issue-trackers), or by 14 | submitting an issue in its linked tracker in the .repo-metadata.json 15 | file in the API under packages/* ([example]()). 16 | You can also submit a request to documentation on cloud.google.com itself with the "Send Feedback" 17 | on the bottom of the page. 18 | 19 | 20 | Please note that documentation requests and questions for specific APIs 21 | will be closed. 22 | - type: checkboxes 23 | attributes: 24 | label: Please make sure you have searched for information in the following 25 | guides. 26 | options: 27 | - label: "Search the issues already opened: 28 | https://github.com/GoogleCloudPlatform/google-cloud-node/issues" 29 | required: true 30 | - label: "Check our Troubleshooting guide: 31 | https://googlecloudplatform.github.io/google-cloud-node/#/docs/guid\ 32 | es/troubleshooting" 33 | required: true 34 | - label: "Check our FAQ: 35 | https://googlecloudplatform.github.io/google-cloud-node/#/docs/guid\ 36 | es/faq" 37 | required: true 38 | - label: "Check our libraries HOW-TO: 39 | https://github.com/googleapis/gax-nodejs/blob/main/client-libraries\ 40 | .md" 41 | required: true 42 | - label: "Check out our authentication guide: 43 | https://github.com/googleapis/google-auth-library-nodejs" 44 | required: true 45 | - label: "Check out handwritten samples for many of our APIs: 46 | https://github.com/GoogleCloudPlatform/nodejs-docs-samples" 47 | required: true 48 | - type: textarea 49 | attributes: 50 | label: > 51 | Documentation Request 52 | validations: 53 | required: true 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this library 4 | labels: 'type: feature request, priority: p3' 5 | --- 6 | 7 | Thanks for stopping by to let us know something could be better! 8 | 9 | **PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. 10 | 11 | **Is your feature request related to a problem? Please describe.** 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | **Additional context** 18 | Add any other context or screenshots about the feature request here. 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this library 3 | labels: 4 | - feature request 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: > 9 | **PLEASE READ**: If you have a support contract with Google, please 10 | create an issue in the [support 11 | console](https://cloud.google.com/support/) instead of filing on GitHub. 12 | This will ensure a timely response. Otherwise, please make sure to 13 | follow the steps below. 14 | - type: textarea 15 | attributes: 16 | label: > 17 | A screenshot that you have tested with "Try this API". 18 | description: > 19 | As our client libraries are mostly autogenerated, we kindly request 20 | that you test whether your feature request is with the client library, or with the 21 | API itself. To do so, please search for your API 22 | here: https://developers.google.com/apis-explorer and attempt to 23 | reproduce the issue in the given method. Please include a screenshot of 24 | the response in "Try this API". This response should NOT match the current 25 | behavior you are experiencing. If the behavior is the same, it means 26 | that you are likely requesting a feature for the API itself. In that 27 | case, please submit an issue to the API team, either by submitting an 28 | issue in its issue tracker https://cloud.google.com/support/docs/issue-trackers, or by 29 | submitting an issue in its linked tracker in the .repo-metadata.json 30 | file in the API under packages/* ([example]()) 31 | 32 | Example of library specific issues would be: retry strategies, authentication questions, or issues with typings. 33 | Examples of API issues would include: expanding method parameter types, adding functionality to an API. 34 | validations: 35 | required: true 36 | - type: textarea 37 | attributes: 38 | label: > 39 | What would you like to see in the library? 40 | description: > 41 | Screenshots can be provided in the issue body below. 42 | placeholder: | 43 | 1. Set up authentication like so 44 | 2. Run the program like so 45 | 3. X would be nice to happen 46 | 47 | - type: textarea 48 | attributes: 49 | label: Describe alternatives you've considered 50 | 51 | - type: textarea 52 | attributes: 53 | label: Additional context/notes -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/processs_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Process Request 3 | about: Submit a process request to the library. Process requests are any requests related to library infrastructure, for example CI/CD, publishing, releasing, broken links. 4 | --- 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question 4 | labels: 'type: question, priority: p3' 5 | --- 6 | 7 | Thanks for stopping by to ask us a question! Please make sure to include: 8 | - What you're trying to do 9 | - What code you've already tried 10 | - Any error messages you're getting 11 | 12 | **PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/questions.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: If you have a question, please use Discussions 4 | 5 | --- 6 | 7 | If you have a general question that goes beyond the library itself, we encourage you to use [Discussions](https://github.com//discussions) 8 | to engage with fellow community members! 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support request 3 | about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. 4 | 5 | --- 6 | 7 | **PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: 2 | 3 | ## Description 4 | 5 | > Please provide a detailed description for the change. 6 | > As much as possible, please try to keep changes separate by purpose. For example, try not to make a one-line bug fix in a feature request, or add an irrelevant README change to a bug fix. 7 | 8 | ## Impact 9 | 10 | > What's the impact of this change? 11 | 12 | ## Testing 13 | 14 | > Have you added unit and integration tests if necessary? 15 | > Were any tests changed? Are any breaking changes necessary? 16 | 17 | ## Additional Information 18 | 19 | > Any additional details that we should be aware of? 20 | 21 | ## Checklist 22 | 23 | - [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/gaxios/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea 24 | - [ ] Ensure the tests and linter pass 25 | - [ ] Code coverage does not decrease 26 | - [ ] Appropriate docs were updated 27 | - [ ] Appropriate comments were added, particularly in complex areas or places that require background 28 | - [ ] No new warnings or issues will be generated from this change 29 | 30 | Fixes #issue_number_goes_here 🦕 31 | -------------------------------------------------------------------------------- /.github/auto-approve.yml: -------------------------------------------------------------------------------- 1 | processes: 2 | - "NodeDependency" -------------------------------------------------------------------------------- /.github/auto-label.yaml: -------------------------------------------------------------------------------- 1 | requestsize: 2 | enabled: true 3 | -------------------------------------------------------------------------------- /.github/generated-files-bot.yml: -------------------------------------------------------------------------------- 1 | generatedFiles: 2 | - path: '.kokoro/**' 3 | message: '`.kokoro` files are templated and should be updated in [`synthtool`](https://github.com/googleapis/synthtool)' 4 | - path: '.github/CODEOWNERS' 5 | message: 'CODEOWNERS should instead be modified via the `codeowner_team` property in .repo-metadata.json' 6 | - path: '.github/workflows/ci.yaml' 7 | message: '`.github/workflows/ci.yaml` (GitHub Actions) should be updated in [`synthtool`](https://github.com/googleapis/synthtool)' 8 | - path: '.github/generated-files-bot.+(yml|yaml)' 9 | message: '`.github/generated-files-bot.(yml|yaml)` should be updated in [`synthtool`](https://github.com/googleapis/synthtool)' 10 | - path: 'README.md' 11 | message: '`README.md` is managed by [`synthtool`](https://github.com/googleapis/synthtool). However, a partials file can be used to update the README, e.g.: https://github.com/googleapis/nodejs-storage/blob/main/.readme-partials.yaml' 12 | - path: 'samples/README.md' 13 | message: '`samples/README.md` is managed by [`synthtool`](https://github.com/googleapis/synthtool). However, a partials file can be used to update the README, e.g.: https://github.com/googleapis/nodejs-storage/blob/main/.readme-partials.yaml' 14 | ignoreAuthors: 15 | - 'gcf-owl-bot[bot]' 16 | - 'yoshi-automation' 17 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads 2 | 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 7 5 | 6 | # Skip issues and pull requests created before a given timestamp. Timestamp must 7 | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable 8 | # skipCreatedBefore: false 9 | 10 | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable 11 | # exemptLabels: [] 12 | 13 | # Label to add before locking, such as `outdated`. Set to `false` to disable 14 | # lockLabel: false 15 | 16 | # Comment to post before locking. Set to `false` to disable 17 | # lockComment: > 18 | # This thread has been automatically locked since there has not been 19 | # any recent activity after it was closed. Please open a new issue for 20 | # related bugs. 21 | 22 | # Assign `resolved` as the reason for locking. Set to `false` to disable 23 | # setLockReason: true 24 | 25 | # Limit to only `issues` or `pulls` 26 | # only: issues 27 | 28 | # Optionally, specify configuration settings just for `issues` or `pulls` 29 | # issues: 30 | # exemptLabels: 31 | # - help-wanted 32 | # lockLabel: outdated 33 | 34 | # pulls: 35 | # daysUntilLock: 30 36 | 37 | # Repository to extend settings from 38 | # _extends: repo -------------------------------------------------------------------------------- /.github/publish.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleapis/gaxios/410aa8ead501361968b392c4160ff54a85e5d420/.github/publish.yml -------------------------------------------------------------------------------- /.github/release-please.yml: -------------------------------------------------------------------------------- 1 | handleGHRelease: true 2 | releaseType: node 3 | -------------------------------------------------------------------------------- /.github/release-trigger.yml: -------------------------------------------------------------------------------- 1 | enabled: true 2 | multiScmName: gaxios -------------------------------------------------------------------------------- /.github/scripts/close-invalid-link.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | const fs = require('fs'); 16 | const yaml = require('js-yaml'); 17 | const path = require('path'); 18 | const TEMPLATE_FILE_PATH = path.resolve(__dirname, '../ISSUE_TEMPLATE/bug_report.yml') 19 | 20 | async function closeIssue(github, owner, repo, number) { 21 | await github.rest.issues.createComment({ 22 | owner: owner, 23 | repo: repo, 24 | issue_number: number, 25 | body: "Issue was opened with an invalid reproduction link. Please make sure the repository is a valid, publicly-accessible github repository, and make sure the url is complete (example: https://github.com/googleapis/google-cloud-node)" 26 | }); 27 | await github.rest.issues.update({ 28 | owner: owner, 29 | repo: repo, 30 | issue_number: number, 31 | state: "closed" 32 | }); 33 | } 34 | module.exports = async ({ github, context }) => { 35 | const owner = context.repo.owner; 36 | const repo = context.repo.repo; 37 | const number = context.issue.number; 38 | 39 | const issue = await github.rest.issues.get({ 40 | owner: owner, 41 | repo: repo, 42 | issue_number: number, 43 | }); 44 | 45 | const yamlData = fs.readFileSync(TEMPLATE_FILE_PATH, 'utf8'); 46 | const obj = yaml.load(yamlData); 47 | const linkMatchingText = (obj.body.find(x => {return x.type === 'input' && x.validations.required === true && x.attributes.label.includes('link')})).attributes.label; 48 | const isBugTemplate = issue.data.body.includes(linkMatchingText); 49 | 50 | if (isBugTemplate) { 51 | console.log(`Issue ${number} is a bug template`) 52 | try { 53 | const text = issue.data.body; 54 | const match = text.indexOf(linkMatchingText); 55 | if (match !== -1) { 56 | const nextLineIndex = text.indexOf('http', match); 57 | if (nextLineIndex == -1) { 58 | await closeIssue(github, owner, repo, number); 59 | return; 60 | } 61 | const link = text.substring(nextLineIndex, text.indexOf('\n', nextLineIndex)); 62 | console.log(`Issue ${number} contains this link: ${link}`); 63 | const isValidLink = (await fetch(link)).ok; 64 | console.log(`Issue ${number} has a ${isValidLink ? "valid" : "invalid"} link`) 65 | if (!isValidLink) { 66 | await closeIssue(github, owner, repo, number); 67 | } 68 | } 69 | } catch (err) { 70 | await closeIssue(github, owner, repo, number); 71 | } 72 | } 73 | }; -------------------------------------------------------------------------------- /.github/scripts/close-unresponsive.cjs: -------------------------------------------------------------------------------- 1 | /// Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | function labeledEvent(data) { 16 | return data.event === "labeled" && data.label.name === "needs more info"; 17 | } 18 | 19 | const numberOfDaysLimit = 15; 20 | const close_message = `This has been closed since a request for information has \ 21 | not been answered for ${numberOfDaysLimit} days. It can be reopened when the \ 22 | requested information is provided.`; 23 | 24 | module.exports = async ({ github, context }) => { 25 | const owner = context.repo.owner; 26 | const repo = context.repo.repo; 27 | 28 | const issues = await github.rest.issues.listForRepo({ 29 | owner: owner, 30 | repo: repo, 31 | labels: "needs more info", 32 | }); 33 | const numbers = issues.data.map((e) => e.number); 34 | 35 | for (const number of numbers) { 36 | const events = await github.paginate( 37 | github.rest.issues.listEventsForTimeline, 38 | { 39 | owner: owner, 40 | repo: repo, 41 | issue_number: number, 42 | }, 43 | (response) => response.data.filter(labeledEvent) 44 | ); 45 | 46 | const latest_response_label = events[events.length - 1]; 47 | 48 | const created_at = new Date(latest_response_label.created_at); 49 | const now = new Date(); 50 | const diff = now - created_at; 51 | const diffDays = diff / (1000 * 60 * 60 * 24); 52 | 53 | if (diffDays > numberOfDaysLimit) { 54 | await github.rest.issues.update({ 55 | owner: owner, 56 | repo: repo, 57 | issue_number: number, 58 | state: "closed", 59 | }); 60 | 61 | await github.rest.issues.createComment({ 62 | owner: owner, 63 | repo: repo, 64 | issue_number: number, 65 | body: close_message, 66 | }); 67 | } 68 | } 69 | }; -------------------------------------------------------------------------------- /.github/scripts/fixtures/invalidIssueBody.txt: -------------------------------------------------------------------------------- 1 | ### Please make sure you have searched for information in the following guides. 2 | 3 | - [X] Search the issues already opened: https://github.com/GoogleCloudPlatform/google-cloud-node/issues 4 | - [X] Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js 5 | - [X] Check our Troubleshooting guide: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/troubleshooting 6 | - [X] Check our FAQ: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/faq 7 | - [X] Check our libraries HOW-TO: https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md 8 | - [X] Check out our authentication guide: https://github.com/googleapis/google-auth-library-nodejs 9 | - [X] Check out handwritten samples for many of our APIs: https://github.com/GoogleCloudPlatform/nodejs-docs-samples 10 | 11 | ### A screenshot that you have tested with "Try this API". 12 | 13 | 14 | N/A 15 | 16 | ### Link to the code that reproduces this issue. A link to a **public** Github Repository or gist with a minimal reproduction. 17 | 18 | not-a-link 19 | 20 | ### A step-by-step description of how to reproduce the issue, based on the linked reproduction. 21 | 22 | 23 | Change MY_PROJECT to your project name, add credentials if needed and run. 24 | 25 | ### A clear and concise description of what the bug is, and what you expected to happen. 26 | 27 | The application crashes with the following exception (which there is no way to catch). It should just emit error, and allow graceful handling. 28 | TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Object 29 | at _write (node:internal/streams/writable:474:13) 30 | at Writable.write (node:internal/streams/writable:502:10) 31 | at Duplexify._write (/project/node_modules/duplexify/index.js:212:22) 32 | at doWrite (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:390:139) 33 | at writeOrBuffer (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:381:5) 34 | at Writable.write (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:302:11) 35 | at Pumpify. (/project/node_modules/@google-cloud/speech/build/src/helpers.js:79:27) 36 | at Object.onceWrapper (node:events:633:26) 37 | at Pumpify.emit (node:events:518:28) 38 | at obj. [as _write] (/project/node_modules/stubs/index.js:28:22) 39 | at doWrite (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:390:139) 40 | at writeOrBuffer (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:381:5) 41 | at Writable.write (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:302:11) 42 | at PassThrough.ondata (node:internal/streams/readable:1007:22) 43 | at PassThrough.emit (node:events:518:28) 44 | at addChunk (node:internal/streams/readable:559:12) { 45 | code: 'ERR_INVALID_ARG_TYPE' 46 | 47 | 48 | ### A clear and concise description WHY you expect this behavior, i.e., was it a recent change, there is documentation that points to this behavior, etc. ** 49 | 50 | No library should crash an application this way. -------------------------------------------------------------------------------- /.github/scripts/fixtures/validIssueBody.txt: -------------------------------------------------------------------------------- 1 | ### Please make sure you have searched for information in the following guides. 2 | 3 | - [X] Search the issues already opened: https://github.com/GoogleCloudPlatform/google-cloud-node/issues 4 | - [X] Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js 5 | - [X] Check our Troubleshooting guide: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/troubleshooting 6 | - [X] Check our FAQ: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/faq 7 | - [X] Check our libraries HOW-TO: https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md 8 | - [X] Check out our authentication guide: https://github.com/googleapis/google-auth-library-nodejs 9 | - [X] Check out handwritten samples for many of our APIs: https://github.com/GoogleCloudPlatform/nodejs-docs-samples 10 | 11 | ### A screenshot that you have tested with "Try this API". 12 | 13 | 14 | N/A 15 | 16 | ### Link to the code that reproduces this issue. A link to a **public** Github Repository or gist with a minimal reproduction. 17 | 18 | https://gist.github.com/orgads/13cbf44c91923da27d8772b5f10489c9 19 | 20 | ### A step-by-step description of how to reproduce the issue, based on the linked reproduction. 21 | 22 | 23 | Change MY_PROJECT to your project name, add credentials if needed and run. 24 | 25 | ### A clear and concise description of what the bug is, and what you expected to happen. 26 | 27 | The application crashes with the following exception (which there is no way to catch). It should just emit error, and allow graceful handling. 28 | TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Object 29 | at _write (node:internal/streams/writable:474:13) 30 | at Writable.write (node:internal/streams/writable:502:10) 31 | at Duplexify._write (/project/node_modules/duplexify/index.js:212:22) 32 | at doWrite (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:390:139) 33 | at writeOrBuffer (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:381:5) 34 | at Writable.write (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:302:11) 35 | at Pumpify. (/project/node_modules/@google-cloud/speech/build/src/helpers.js:79:27) 36 | at Object.onceWrapper (node:events:633:26) 37 | at Pumpify.emit (node:events:518:28) 38 | at obj. [as _write] (/project/node_modules/stubs/index.js:28:22) 39 | at doWrite (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:390:139) 40 | at writeOrBuffer (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:381:5) 41 | at Writable.write (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:302:11) 42 | at PassThrough.ondata (node:internal/streams/readable:1007:22) 43 | at PassThrough.emit (node:events:518:28) 44 | at addChunk (node:internal/streams/readable:559:12) { 45 | code: 'ERR_INVALID_ARG_TYPE' 46 | 47 | 48 | ### A clear and concise description WHY you expect this behavior, i.e., was it a recent change, there is documentation that points to this behavior, etc. ** 49 | 50 | No library should crash an application this way. -------------------------------------------------------------------------------- /.github/scripts/fixtures/validIssueBodyDifferentLinkLocation.txt: -------------------------------------------------------------------------------- 1 | ### Please make sure you have searched for information in the following guides. 2 | 3 | - [X] Search the issues already opened: https://github.com/GoogleCloudPlatform/google-cloud-node/issues 4 | - [X] Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js 5 | - [X] Check our Troubleshooting guide: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/troubleshooting 6 | - [X] Check our FAQ: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/faq 7 | - [X] Check our libraries HOW-TO: https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md 8 | - [X] Check out our authentication guide: https://github.com/googleapis/google-auth-library-nodejs 9 | - [X] Check out handwritten samples for many of our APIs: https://github.com/GoogleCloudPlatform/nodejs-docs-samples 10 | 11 | ### A screenshot that you have tested with "Try this API". 12 | 13 | 14 | N/A 15 | 16 | ### A step-by-step description of how to reproduce the issue, based on the linked reproduction. 17 | 18 | 19 | Change MY_PROJECT to your project name, add credentials if needed and run. 20 | 21 | ### A clear and concise description of what the bug is, and what you expected to happen. 22 | 23 | The application crashes with the following exception (which there is no way to catch). It should just emit error, and allow graceful handling. 24 | TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Object 25 | at _write (node:internal/streams/writable:474:13) 26 | at Writable.write (node:internal/streams/writable:502:10) 27 | at Duplexify._write (/project/node_modules/duplexify/index.js:212:22) 28 | at doWrite (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:390:139) 29 | at writeOrBuffer (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:381:5) 30 | at Writable.write (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:302:11) 31 | at Pumpify. (/project/node_modules/@google-cloud/speech/build/src/helpers.js:79:27) 32 | at Object.onceWrapper (node:events:633:26) 33 | at Pumpify.emit (node:events:518:28) 34 | at obj. [as _write] (/project/node_modules/stubs/index.js:28:22) 35 | at doWrite (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:390:139) 36 | at writeOrBuffer (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:381:5) 37 | at Writable.write (/project/node_modules/duplexify/node_modules/readable-stream/lib/_stream_writable.js:302:11) 38 | at PassThrough.ondata (node:internal/streams/readable:1007:22) 39 | at PassThrough.emit (node:events:518:28) 40 | at addChunk (node:internal/streams/readable:559:12) { 41 | code: 'ERR_INVALID_ARG_TYPE' 42 | 43 | ### Link to the code that reproduces this issue. A link to a **public** Github Repository with a minimal reproduction. 44 | 45 | 46 | https://gist.github.com/orgads/13cbf44c91923da27d8772b5f10489c9 47 | 48 | ### A clear and concise description WHY you expect this behavior, i.e., was it a recent change, there is documentation that points to this behavior, etc. ** 49 | 50 | No library should crash an application this way. -------------------------------------------------------------------------------- /.github/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "private": true, 4 | "description": "tests for script", 5 | "scripts": { 6 | "test": "mocha tests/close-invalid-link.test.cjs && mocha tests/close-or-remove-response-label.test.cjs" 7 | }, 8 | "author": "Google Inc.", 9 | "license": "Apache-2.0", 10 | "engines": { 11 | "node": ">=18" 12 | }, 13 | "dependencies": { 14 | "js-yaml": "^4.1.0" 15 | }, 16 | "devDependencies": { 17 | "@octokit/rest": "^21.0.0", 18 | "mocha": "^10.0.0", 19 | "sinon": "^18.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /.github/scripts/remove-response-label.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | module.exports = async ({ github, context }) => { 16 | const commenter = context.actor; 17 | const issue = await github.rest.issues.get({ 18 | owner: context.repo.owner, 19 | repo: context.repo.repo, 20 | issue_number: context.issue.number, 21 | }); 22 | const author = issue.data.user.login; 23 | const labels = issue.data.labels.map((e) => e.name); 24 | 25 | if (author === commenter && labels.includes("needs more info")) { 26 | await github.rest.issues.removeLabel({ 27 | owner: context.repo.owner, 28 | repo: context.repo.repo, 29 | issue_number: context.issue.number, 30 | name: "needs more info", 31 | }); 32 | } 33 | }; -------------------------------------------------------------------------------- /.github/scripts/tests/close-invalid-link.test.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 'use strict'; 16 | 17 | const { describe, it } = require('mocha'); 18 | const closeInvalidLink = require('../close-invalid-link.cjs'); 19 | const fs = require('fs'); 20 | const sinon = require('sinon'); 21 | 22 | describe('close issues with invalid links', () => { 23 | let octokitStub; 24 | let issuesStub; 25 | 26 | beforeEach(() => { 27 | issuesStub = { 28 | get: sinon.stub(), 29 | createComment: sinon.stub(), 30 | update: sinon.stub(), 31 | }; 32 | octokitStub = { 33 | rest: { 34 | issues: issuesStub, 35 | }, 36 | }; 37 | }); 38 | 39 | afterEach(() => { 40 | sinon.restore(); 41 | }); 42 | 43 | it('does not do anything if it is not a bug', async () => { 44 | const context = { repo: { owner: 'testOrg', repo: 'testRepo' }, issue: { number: 1 } }; 45 | issuesStub.get.resolves({ data: { body: "I'm having a problem with this." } }); 46 | 47 | await closeInvalidLink({ github: octokitStub, context }); 48 | 49 | sinon.assert.calledOnce(issuesStub.get); 50 | sinon.assert.notCalled(issuesStub.createComment); 51 | sinon.assert.notCalled(issuesStub.update); 52 | }); 53 | 54 | it('does not do anything if it is a bug with an appropriate link', async () => { 55 | const context = { repo: { owner: 'testOrg', repo: 'testRepo' }, issue: { number: 1 } }; 56 | issuesStub.get.resolves({ data: { body: fs.readFileSync('./fixtures/validIssueBody.txt', 'utf-8') } }); 57 | 58 | await closeInvalidLink({ github: octokitStub, context }); 59 | 60 | sinon.assert.calledOnce(issuesStub.get); 61 | sinon.assert.notCalled(issuesStub.createComment); 62 | sinon.assert.notCalled(issuesStub.update); 63 | }); 64 | 65 | it('does not do anything if it is a bug with an appropriate link and the template changes', async () => { 66 | const context = { repo: { owner: 'testOrg', repo: 'testRepo' }, issue: { number: 1 } }; 67 | issuesStub.get.resolves({ data: { body: fs.readFileSync('./fixtures/validIssueBodyDifferentLinkLocation.txt', 'utf-8') } }); 68 | 69 | await closeInvalidLink({ github: octokitStub, context }); 70 | 71 | sinon.assert.calledOnce(issuesStub.get); 72 | sinon.assert.notCalled(issuesStub.createComment); 73 | sinon.assert.notCalled(issuesStub.update); 74 | }); 75 | 76 | it('closes the issue if the link is invalid', async () => { 77 | const context = { repo: { owner: 'testOrg', repo: 'testRepo' }, issue: { number: 1 } }; 78 | issuesStub.get.resolves({ data: { body: fs.readFileSync('./fixtures/invalidIssueBody.txt', 'utf-8') } }); 79 | 80 | await closeInvalidLink({ github: octokitStub, context }); 81 | 82 | sinon.assert.calledOnce(issuesStub.get); 83 | sinon.assert.calledOnce(issuesStub.createComment); 84 | sinon.assert.calledOnce(issuesStub.update); 85 | }); 86 | }); -------------------------------------------------------------------------------- /.github/scripts/tests/close-or-remove-response-label.test.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 'use strict'; 16 | 17 | const { describe, it, beforeEach, afterEach } = require('mocha'); 18 | const removeResponseLabel = require('../remove-response-label.cjs'); 19 | const closeUnresponsive = require('../close-unresponsive.cjs'); 20 | const sinon = require('sinon'); 21 | 22 | function getISODateDaysAgo(days) { 23 | const today = new Date(); 24 | const daysAgo = new Date(today.setDate(today.getDate() - days)); 25 | return daysAgo.toISOString(); 26 | } 27 | 28 | describe('close issues or remove needs more info labels', () => { 29 | let octokitStub; 30 | let issuesStub; 31 | let paginateStub; 32 | 33 | beforeEach(() => { 34 | issuesStub = { 35 | listForRepo: sinon.stub(), 36 | update: sinon.stub(), 37 | createComment: sinon.stub(), 38 | get: sinon.stub(), 39 | removeLabel: sinon.stub(), 40 | }; 41 | paginateStub = sinon.stub(); 42 | octokitStub = { 43 | rest: { 44 | issues: issuesStub, 45 | }, 46 | paginate: paginateStub, 47 | }; 48 | }); 49 | 50 | afterEach(() => { 51 | sinon.restore(); 52 | }); 53 | 54 | it('closes the issue if the OP has not responded within the allotted time and there is a needs-more-info label', async () => { 55 | const context = { owner: 'testOrg', repo: 'testRepo' }; 56 | const issuesInRepo = [{ user: { login: 'OP' }, labels: [{ name: 'needs more info' }] }]; 57 | const eventsInIssue = [{ event: 'labeled', label: { name: 'needs more info' }, created_at: getISODateDaysAgo(16) }]; 58 | 59 | issuesStub.listForRepo.resolves({ data: issuesInRepo }); 60 | paginateStub.resolves(eventsInIssue); 61 | 62 | await closeUnresponsive({ github: octokitStub, context }); 63 | 64 | sinon.assert.calledOnce(issuesStub.listForRepo); 65 | sinon.assert.calledOnce(paginateStub); 66 | sinon.assert.calledOnce(issuesStub.update); 67 | sinon.assert.calledOnce(issuesStub.createComment); 68 | }); 69 | 70 | it('does nothing if not enough time has passed and there is a needs-more-info label', async () => { 71 | const context = { owner: 'testOrg', repo: 'testRepo' }; 72 | const issuesInRepo = [{ user: { login: 'OP' }, labels: [{ name: 'needs more info' }] }]; 73 | const eventsInIssue = [{ event: 'labeled', label: { name: 'needs more info' }, created_at: getISODateDaysAgo(14) }]; 74 | 75 | issuesStub.listForRepo.resolves({ data: issuesInRepo }); 76 | paginateStub.resolves(eventsInIssue); 77 | 78 | await closeUnresponsive({ github: octokitStub, context }); 79 | 80 | sinon.assert.calledOnce(issuesStub.listForRepo); 81 | sinon.assert.calledOnce(paginateStub); 82 | sinon.assert.notCalled(issuesStub.update); 83 | sinon.assert.notCalled(issuesStub.createComment); 84 | }); 85 | 86 | it('removes the label if OP responded', async () => { 87 | const context = { actor: 'OP', repo: { owner: 'testOrg', repo: 'testRepo' }, issue: { number: 1 } }; 88 | const issueContext = { user: {login: 'OP'}, labels: [{ name: 'needs more info' }] }; 89 | 90 | issuesStub.get.resolves({ data: issueContext }); 91 | 92 | await removeResponseLabel({ github: octokitStub, context }); 93 | 94 | sinon.assert.calledOnce(issuesStub.get); 95 | sinon.assert.calledOnce(issuesStub.removeLabel); 96 | }); 97 | 98 | it('does not remove the label if author responded', async () => { 99 | const context = { actor: 'repo-maintainer', repo: { owner: 'testOrg', repo: 'testRepo' }, issue: { number: 1 } }; 100 | const issueContext = { user: {login: 'OP'}, labels: [{ name: 'needs more info' }] }; 101 | 102 | issuesStub.get.resolves({ data: issueContext }); 103 | 104 | await removeResponseLabel({ github: octokitStub, context }); 105 | 106 | sinon.assert.calledOnce(issuesStub.get); 107 | sinon.assert.notCalled(issuesStub.removeLabel); 108 | }); 109 | }); -------------------------------------------------------------------------------- /.github/sync-repo-settings.yaml: -------------------------------------------------------------------------------- 1 | branchProtectionRules: 2 | - pattern: main 3 | isAdminEnforced: true 4 | requiredApprovingReviewCount: 1 5 | requiresCodeOwnerReviews: true 6 | requiresStrictStatusChecks: true 7 | requiredStatusCheckContexts: 8 | - "ci/kokoro: Samples test" 9 | - "ci/kokoro: System test" 10 | - lint 11 | - test (18) 12 | - test (20) 13 | - test (22) 14 | - cla/google 15 | - windows 16 | - OwlBot Post Processor 17 | permissionRules: 18 | - team: yoshi-admins 19 | permission: admin 20 | - team: jsteam-admins 21 | permission: admin 22 | - team: jsteam 23 | permission: push 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | pull_request: 6 | name: ci 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | node: [18, 20, 22] 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: ${{ matrix.node }} 18 | - run: node --version 19 | # The first installation step ensures that all of our production 20 | # dependencies work on the given Node.js version, this helps us find 21 | # dependencies that don't match our engines field: 22 | - run: npm install --production --engine-strict --ignore-scripts --no-package-lock 23 | # Clean up the production install, before installing dev/production: 24 | - run: rm -rf node_modules 25 | - run: npm install --engine-strict 26 | - run: npm test 27 | env: 28 | MOCHA_THROW_DEPRECATION: false 29 | test-script: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/setup-node@v4 34 | with: 35 | node-version: 18 36 | - run: node --version 37 | - run: npm install --engine-strict 38 | working-directory: .github/scripts 39 | - run: npm test 40 | working-directory: .github/scripts 41 | env: 42 | MOCHA_THROW_DEPRECATION: false 43 | windows: 44 | runs-on: windows-latest 45 | steps: 46 | - uses: actions/checkout@v4 47 | - uses: actions/setup-node@v4 48 | with: 49 | node-version: 18 50 | - run: npm install --engine-strict 51 | - run: npm test 52 | env: 53 | MOCHA_THROW_DEPRECATION: false 54 | lint: 55 | runs-on: ubuntu-latest 56 | steps: 57 | - uses: actions/checkout@v4 58 | - uses: actions/setup-node@v4 59 | with: 60 | node-version: 18 61 | - run: npm install 62 | - run: npm run lint 63 | docs: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v4 67 | - uses: actions/setup-node@v4 68 | with: 69 | node-version: 18 70 | - run: npm install 71 | - run: npm run docs 72 | - uses: JustinBeckwith/linkinator-action@v1 73 | with: 74 | paths: docs/ 75 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '34 12 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v3 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v3 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v3 72 | -------------------------------------------------------------------------------- /.github/workflows/issues-no-repro.yaml: -------------------------------------------------------------------------------- 1 | name: invalid_link 2 | on: 3 | issues: 4 | types: [opened, reopened] 5 | 6 | jobs: 7 | close: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 18 17 | - run: npm install 18 | working-directory: ./.github/scripts 19 | - uses: actions/github-script@v7 20 | with: 21 | script: | 22 | const script = require('./.github/scripts/close-invalid-link.cjs') 23 | await script({github, context}) 24 | -------------------------------------------------------------------------------- /.github/workflows/response.yaml: -------------------------------------------------------------------------------- 1 | name: no_response 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' # Run every day at 01:30 5 | workflow_dispatch: 6 | issue_comment: 7 | 8 | jobs: 9 | close: 10 | if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | pull-requests: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/github-script@v7 18 | with: 19 | script: | 20 | const script = require('./.github/scripts/close-unresponsive.cjs') 21 | await script({github, context}) 22 | 23 | remove_label: 24 | if: github.event_name == 'issue_comment' 25 | runs-on: ubuntu-latest 26 | permissions: 27 | issues: write 28 | pull-requests: write 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: actions/github-script@v7 32 | with: 33 | script: | 34 | const script = require('./.github/scripts/remove-response-label.cjs') 35 | await script({github, context}) 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | docs/ 3 | build/ 4 | .nyc_output 5 | coverage 6 | yarn.lock 7 | .vscode 8 | dist/ 9 | __pycache__ 10 | .coverage 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /.jsdoc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | 16 | 'use strict'; 17 | 18 | module.exports = { 19 | opts: { 20 | readme: './README.md', 21 | package: './package.json', 22 | template: './node_modules/jsdoc-fresh', 23 | recurse: true, 24 | verbose: true, 25 | destination: './docs/', 26 | }, 27 | plugins: ['plugins/markdown', 'jsdoc-region-tag'], 28 | source: { 29 | excludePattern: '(^|\\/|\\\\)[._]', 30 | include: ['build/esm/src/'], 31 | includePattern: '\\.js$', 32 | }, 33 | templates: { 34 | copyright: 'Copyright 2019 Google, LLC.', 35 | includeDate: false, 36 | sourceFiles: false, 37 | systemName: 'gaxios', 38 | theme: 'lumen', 39 | default: { 40 | outputSourceFiles: false, 41 | }, 42 | }, 43 | markdown: { 44 | idInHeadings: true, 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /.kokoro/.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-generated=true 2 | -------------------------------------------------------------------------------- /.kokoro/browser-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | # Setup service account credentials. 20 | export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json 21 | export GCLOUD_PROJECT=long-door-651 22 | 23 | cd $(dirname $0)/.. 24 | 25 | npm install 26 | 27 | npm run browser-test 28 | -------------------------------------------------------------------------------- /.kokoro/common.cfg: -------------------------------------------------------------------------------- 1 | # Format: //devtools/kokoro/config/proto/build.proto 2 | 3 | # Build logs will be here 4 | action { 5 | define_artifacts { 6 | regex: "**/*sponge_log.xml" 7 | } 8 | } 9 | 10 | # Download trampoline resources. 11 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" 12 | 13 | # Use the trampoline script to run in docker. 14 | build_file: "gaxios/.kokoro/trampoline_v2.sh" 15 | 16 | # Configure the docker image for kokoro-trampoline. 17 | env_vars: { 18 | key: "TRAMPOLINE_IMAGE" 19 | value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" 20 | } 21 | env_vars: { 22 | key: "TRAMPOLINE_BUILD_FILE" 23 | value: "github/gaxios/.kokoro/test.sh" 24 | } 25 | -------------------------------------------------------------------------------- /.kokoro/continuous/node18/common.cfg: -------------------------------------------------------------------------------- 1 | # Format: //devtools/kokoro/config/proto/build.proto 2 | 3 | # Build logs will be here 4 | action { 5 | define_artifacts { 6 | regex: "**/*sponge_log.xml" 7 | } 8 | } 9 | 10 | # Download trampoline resources. 11 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" 12 | 13 | # Use the trampoline script to run in docker. 14 | build_file: "gaxios/.kokoro/trampoline_v2.sh" 15 | 16 | # Configure the docker image for kokoro-trampoline. 17 | env_vars: { 18 | key: "TRAMPOLINE_IMAGE" 19 | value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" 20 | } 21 | env_vars: { 22 | key: "TRAMPOLINE_BUILD_FILE" 23 | value: "github/gaxios/.kokoro/test.sh" 24 | } 25 | -------------------------------------------------------------------------------- /.kokoro/continuous/node18/lint.cfg: -------------------------------------------------------------------------------- 1 | env_vars: { 2 | key: "TRAMPOLINE_BUILD_FILE" 3 | value: "github/gaxios/.kokoro/lint.sh" 4 | } 5 | -------------------------------------------------------------------------------- /.kokoro/continuous/node18/samples-test.cfg: -------------------------------------------------------------------------------- 1 | # Download resources for system tests (service account key, etc.) 2 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-nodejs" 3 | 4 | env_vars: { 5 | key: "TRAMPOLINE_BUILD_FILE" 6 | value: "github/gaxios/.kokoro/samples-test.sh" 7 | } 8 | 9 | env_vars: { 10 | key: "SECRET_MANAGER_KEYS" 11 | value: "long-door-651-kokoro-system-test-service-account" 12 | } -------------------------------------------------------------------------------- /.kokoro/continuous/node18/system-test.cfg: -------------------------------------------------------------------------------- 1 | # Download resources for system tests (service account key, etc.) 2 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-nodejs" 3 | 4 | env_vars: { 5 | key: "TRAMPOLINE_BUILD_FILE" 6 | value: "github/gaxios/.kokoro/system-test.sh" 7 | } 8 | 9 | env_vars: { 10 | key: "SECRET_MANAGER_KEYS" 11 | value: "long-door-651-kokoro-system-test-service-account" 12 | } -------------------------------------------------------------------------------- /.kokoro/continuous/node18/test.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleapis/gaxios/410aa8ead501361968b392c4160ff54a85e5d420/.kokoro/continuous/node18/test.cfg -------------------------------------------------------------------------------- /.kokoro/docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 20 | 21 | cd $(dirname $0)/.. 22 | 23 | npm install 24 | 25 | npm run docs-test 26 | -------------------------------------------------------------------------------- /.kokoro/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 20 | 21 | cd $(dirname $0)/.. 22 | 23 | npm install 24 | 25 | # Install and link samples 26 | if [ -f samples/package.json ]; then 27 | cd samples/ 28 | npm link ../ 29 | npm install 30 | cd .. 31 | fi 32 | 33 | npm run lint 34 | -------------------------------------------------------------------------------- /.kokoro/populate-secrets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 Google LLC. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This file is called in the early stage of `trampoline_v2.sh` to 17 | # populate secrets needed for the CI builds. 18 | 19 | set -eo pipefail 20 | 21 | function now { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' ;} 22 | function msg { println "$*" >&2 ;} 23 | function println { printf '%s\n' "$(now) $*" ;} 24 | 25 | # Populates requested secrets set in SECRET_MANAGER_KEYS 26 | 27 | # In Kokoro CI builds, we use the service account attached to the 28 | # Kokoro VM. This means we need to setup auth on other CI systems. 29 | # For local run, we just use the gcloud command for retrieving the 30 | # secrets. 31 | 32 | if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then 33 | GCLOUD_COMMANDS=( 34 | "docker" 35 | "run" 36 | "--entrypoint=gcloud" 37 | "--volume=${KOKORO_GFILE_DIR}:${KOKORO_GFILE_DIR}" 38 | "gcr.io/google.com/cloudsdktool/cloud-sdk" 39 | ) 40 | if [[ "${TRAMPOLINE_CI:-}" == "kokoro" ]]; then 41 | SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" 42 | else 43 | echo "Authentication for this CI system is not implemented yet." 44 | exit 2 45 | # TODO: Determine appropriate SECRET_LOCATION and the GCLOUD_COMMANDS. 46 | fi 47 | else 48 | # For local run, use /dev/shm or temporary directory for 49 | # KOKORO_GFILE_DIR. 50 | if [[ -d "/dev/shm" ]]; then 51 | export KOKORO_GFILE_DIR=/dev/shm 52 | else 53 | export KOKORO_GFILE_DIR=$(mktemp -d -t ci-XXXXXXXX) 54 | fi 55 | SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" 56 | GCLOUD_COMMANDS=("gcloud") 57 | fi 58 | 59 | msg "Creating folder on disk for secrets: ${SECRET_LOCATION}" 60 | mkdir -p ${SECRET_LOCATION} 61 | 62 | for key in $(echo ${SECRET_MANAGER_KEYS} | sed "s/,/ /g") 63 | do 64 | msg "Retrieving secret ${key}" 65 | "${GCLOUD_COMMANDS[@]}" \ 66 | secrets versions access latest \ 67 | --project cloud-devrel-kokoro-resources \ 68 | --secret $key > \ 69 | "$SECRET_LOCATION/$key" 70 | if [[ $? == 0 ]]; then 71 | msg "Secret written to ${SECRET_LOCATION}/${key}" 72 | else 73 | msg "Error retrieving secret ${key}" 74 | exit 2 75 | fi 76 | done 77 | -------------------------------------------------------------------------------- /.kokoro/presubmit/node18/common.cfg: -------------------------------------------------------------------------------- 1 | # Format: //devtools/kokoro/config/proto/build.proto 2 | 3 | # Build logs will be here 4 | action { 5 | define_artifacts { 6 | regex: "**/*sponge_log.xml" 7 | } 8 | } 9 | 10 | # Download trampoline resources. 11 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" 12 | 13 | # Use the trampoline script to run in docker. 14 | build_file: "gaxios/.kokoro/trampoline_v2.sh" 15 | 16 | # Configure the docker image for kokoro-trampoline. 17 | env_vars: { 18 | key: "TRAMPOLINE_IMAGE" 19 | value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" 20 | } 21 | env_vars: { 22 | key: "TRAMPOLINE_BUILD_FILE" 23 | value: "github/gaxios/.kokoro/test.sh" 24 | } 25 | -------------------------------------------------------------------------------- /.kokoro/presubmit/node18/samples-test.cfg: -------------------------------------------------------------------------------- 1 | # Download resources for system tests (service account key, etc.) 2 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-nodejs" 3 | 4 | env_vars: { 5 | key: "TRAMPOLINE_BUILD_FILE" 6 | value: "github/gaxios/.kokoro/samples-test.sh" 7 | } 8 | 9 | env_vars: { 10 | key: "SECRET_MANAGER_KEYS" 11 | value: "long-door-651-kokoro-system-test-service-account" 12 | } -------------------------------------------------------------------------------- /.kokoro/presubmit/node18/system-test.cfg: -------------------------------------------------------------------------------- 1 | # Download resources for system tests (service account key, etc.) 2 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-nodejs" 3 | 4 | env_vars: { 5 | key: "TRAMPOLINE_BUILD_FILE" 6 | value: "github/gaxios/.kokoro/system-test.sh" 7 | } 8 | 9 | env_vars: { 10 | key: "SECRET_MANAGER_KEYS" 11 | value: "long-door-651-kokoro-system-test-service-account" 12 | } -------------------------------------------------------------------------------- /.kokoro/presubmit/node18/test.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleapis/gaxios/410aa8ead501361968b392c4160ff54a85e5d420/.kokoro/presubmit/node18/test.cfg -------------------------------------------------------------------------------- /.kokoro/presubmit/windows/common.cfg: -------------------------------------------------------------------------------- 1 | # Format: //devtools/kokoro/config/proto/build.proto 2 | 3 | -------------------------------------------------------------------------------- /.kokoro/presubmit/windows/test.cfg: -------------------------------------------------------------------------------- 1 | # Use the test file directly 2 | build_file: "gaxios/.kokoro/test.bat" 3 | -------------------------------------------------------------------------------- /.kokoro/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 20 | 21 | # Start the releasetool reporter 22 | python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script 23 | 24 | cd $(dirname $0)/.. 25 | 26 | NPM_TOKEN=$(cat $KOKORO_KEYSTORE_DIR/73713_google-cloud-npm-token-1) 27 | echo "//wombat-dressing-room.appspot.com/:_authToken=${NPM_TOKEN}" > ~/.npmrc 28 | 29 | npm install 30 | npm pack . 31 | # npm provides no way to specify, observe, or predict the name of the tarball 32 | # file it generates. We have to look in the current directory for the freshest 33 | # .tgz file. 34 | TARBALL=$(ls -1 -t *.tgz | head -1) 35 | 36 | npm publish --access=public --registry=https://wombat-dressing-room.appspot.com "$TARBALL" 37 | 38 | # Kokoro collects *.tgz and package-lock.json files and stores them in Placer 39 | # so we can generate SBOMs and attestations. 40 | # However, we *don't* want Kokoro to collect package-lock.json and *.tgz files 41 | # that happened to be installed with dependencies. 42 | find node_modules -name package-lock.json -o -name "*.tgz" | xargs rm -f -------------------------------------------------------------------------------- /.kokoro/release/common.cfg: -------------------------------------------------------------------------------- 1 | before_action { 2 | fetch_keystore { 3 | keystore_resource { 4 | keystore_config_id: 73713 5 | keyname: "yoshi-automation-github-key" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.kokoro/release/docs-devsite.cfg: -------------------------------------------------------------------------------- 1 | # service account used to publish up-to-date docs. 2 | before_action { 3 | fetch_keystore { 4 | keystore_resource { 5 | keystore_config_id: 73713 6 | keyname: "docuploader_service_account" 7 | } 8 | } 9 | } 10 | 11 | # doc publications use a Python image. 12 | env_vars: { 13 | key: "TRAMPOLINE_IMAGE" 14 | value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" 15 | } 16 | 17 | # Download trampoline resources. 18 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" 19 | 20 | # Use the trampoline script to run in docker. 21 | build_file: "gaxios/.kokoro/trampoline_v2.sh" 22 | 23 | env_vars: { 24 | key: "TRAMPOLINE_BUILD_FILE" 25 | value: "github/gaxios/.kokoro/release/docs-devsite.sh" 26 | } 27 | -------------------------------------------------------------------------------- /.kokoro/release/docs-devsite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | if [[ -z "$CREDENTIALS" ]]; then 20 | # if CREDENTIALS are explicitly set, assume we're testing locally 21 | # and don't set NPM_CONFIG_PREFIX. 22 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 23 | export PATH="$PATH:${NPM_CONFIG_PREFIX}/bin" 24 | cd $(dirname $0)/../.. 25 | fi 26 | 27 | npm install 28 | npm install --no-save @google-cloud/cloud-rad@^0.4.0 29 | # publish docs to devsite 30 | npx @google-cloud/cloud-rad . cloud-rad 31 | -------------------------------------------------------------------------------- /.kokoro/release/docs.cfg: -------------------------------------------------------------------------------- 1 | # service account used to publish up-to-date docs. 2 | before_action { 3 | fetch_keystore { 4 | keystore_resource { 5 | keystore_config_id: 73713 6 | keyname: "docuploader_service_account" 7 | } 8 | } 9 | } 10 | 11 | # doc publications use a Python image. 12 | env_vars: { 13 | key: "TRAMPOLINE_IMAGE" 14 | value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" 15 | } 16 | 17 | # Download trampoline resources. 18 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" 19 | 20 | # Use the trampoline script to run in docker. 21 | build_file: "gaxios/.kokoro/trampoline_v2.sh" 22 | 23 | env_vars: { 24 | key: "TRAMPOLINE_BUILD_FILE" 25 | value: "github/gaxios/.kokoro/release/docs.sh" 26 | } 27 | -------------------------------------------------------------------------------- /.kokoro/release/docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | # build jsdocs (Python is installed on the Node 18 docker image). 20 | if [[ -z "$CREDENTIALS" ]]; then 21 | # if CREDENTIALS are explicitly set, assume we're testing locally 22 | # and don't set NPM_CONFIG_PREFIX. 23 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 24 | export PATH="$PATH:${NPM_CONFIG_PREFIX}/bin" 25 | cd $(dirname $0)/../.. 26 | fi 27 | npm install 28 | npm run docs 29 | 30 | # create docs.metadata, based on package.json and .repo-metadata.json. 31 | npm i json@9.0.6 -g 32 | python3 -m docuploader create-metadata \ 33 | --name=$(cat .repo-metadata.json | json name) \ 34 | --version=$(cat package.json | json version) \ 35 | --language=$(cat .repo-metadata.json | json language) \ 36 | --distribution-name=$(cat .repo-metadata.json | json distribution_name) \ 37 | --product-page=$(cat .repo-metadata.json | json product_documentation) \ 38 | --github-repository=$(cat .repo-metadata.json | json repo) \ 39 | --issue-tracker=$(cat .repo-metadata.json | json issue_tracker) 40 | cp docs.metadata ./docs/docs.metadata 41 | 42 | # deploy the docs. 43 | if [[ -z "$CREDENTIALS" ]]; then 44 | CREDENTIALS=${KOKORO_KEYSTORE_DIR}/73713_docuploader_service_account 45 | fi 46 | if [[ -z "$BUCKET" ]]; then 47 | BUCKET=docs-staging 48 | fi 49 | python3 -m docuploader upload ./docs --credentials $CREDENTIALS --staging-bucket $BUCKET 50 | -------------------------------------------------------------------------------- /.kokoro/release/publish.cfg: -------------------------------------------------------------------------------- 1 | before_action { 2 | fetch_keystore { 3 | keystore_resource { 4 | keystore_config_id: 73713 5 | keyname: "docuploader_service_account" 6 | } 7 | } 8 | } 9 | 10 | before_action { 11 | fetch_keystore { 12 | keystore_resource { 13 | keystore_config_id: 73713 14 | keyname: "google-cloud-npm-token-1" 15 | } 16 | } 17 | } 18 | 19 | env_vars: { 20 | key: "SECRET_MANAGER_KEYS" 21 | value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" 22 | } 23 | 24 | # Download trampoline resources. 25 | gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" 26 | 27 | # Use the trampoline script to run in docker. 28 | build_file: "gaxios/.kokoro/trampoline_v2.sh" 29 | 30 | # Configure the docker image for kokoro-trampoline. 31 | env_vars: { 32 | key: "TRAMPOLINE_IMAGE" 33 | value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" 34 | } 35 | 36 | env_vars: { 37 | key: "TRAMPOLINE_BUILD_FILE" 38 | value: "github/gaxios/.kokoro/publish.sh" 39 | } 40 | 41 | # Store the packages we uploaded to npmjs.org and their corresponding 42 | # package-lock.jsons in Placer. That way, we have a record of exactly 43 | # what we published, and which version of which tools we used to publish 44 | # it, which we can use to generate SBOMs and attestations. 45 | action { 46 | define_artifacts { 47 | regex: "github/**/*.tgz" 48 | regex: "github/**/package-lock.json" 49 | strip_prefix: "github" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.kokoro/samples-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | # Ensure the npm global directory is writable, otherwise rebuild `npm` 20 | mkdir -p $NPM_CONFIG_PREFIX 21 | npm config -g ls || npm i -g npm@`npm --version` 22 | 23 | # Setup service account credentials. 24 | export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/secret_manager/long-door-651-kokoro-system-test-service-account 25 | export GCLOUD_PROJECT=long-door-651 26 | 27 | cd $(dirname $0)/.. 28 | 29 | # Run a pre-test hook, if a pre-samples-test.sh is in the project 30 | if [ -f .kokoro/pre-samples-test.sh ]; then 31 | set +x 32 | . .kokoro/pre-samples-test.sh 33 | set -x 34 | fi 35 | 36 | if [ -f samples/package.json ]; then 37 | npm install 38 | 39 | # Install and link samples 40 | cd samples/ 41 | npm link ../ 42 | npm install 43 | cd .. 44 | # If tests are running against main branch, configure flakybot 45 | # to open issues on failures: 46 | if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]] || [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"nightly"* ]]; then 47 | export MOCHA_REPORTER_OUTPUT=test_output_sponge_log.xml 48 | export MOCHA_REPORTER=xunit 49 | cleanup() { 50 | chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot 51 | $KOKORO_GFILE_DIR/linux_amd64/flakybot 52 | } 53 | trap cleanup EXIT HUP 54 | fi 55 | 56 | npm run samples-test 57 | fi 58 | 59 | # codecov combines coverage across integration and unit tests. Include 60 | # the logic below for any environment you wish to collect coverage for: 61 | COVERAGE_NODE=18 62 | if npx check-node-version@3.3.0 --silent --node $COVERAGE_NODE; then 63 | NYC_BIN=./node_modules/nyc/bin/nyc.js 64 | if [ -f "$NYC_BIN" ]; then 65 | $NYC_BIN report || true 66 | fi 67 | bash $KOKORO_GFILE_DIR/codecov.sh 68 | else 69 | echo "coverage is only reported for Node $COVERAGE_NODE" 70 | fi 71 | -------------------------------------------------------------------------------- /.kokoro/system-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 20 | 21 | # Setup service account credentials. 22 | export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/secret_manager/long-door-651-kokoro-system-test-service-account 23 | export GCLOUD_PROJECT=long-door-651 24 | 25 | cd $(dirname $0)/.. 26 | 27 | # Run a pre-test hook, if a pre-system-test.sh is in the project 28 | if [ -f .kokoro/pre-system-test.sh ]; then 29 | set +x 30 | . .kokoro/pre-system-test.sh 31 | set -x 32 | fi 33 | 34 | npm install 35 | 36 | # If tests are running against main branch, configure flakybot 37 | # to open issues on failures: 38 | if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]] || [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"nightly"* ]]; then 39 | export MOCHA_REPORTER_OUTPUT=test_output_sponge_log.xml 40 | export MOCHA_REPORTER=xunit 41 | cleanup() { 42 | chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot 43 | $KOKORO_GFILE_DIR/linux_amd64/flakybot 44 | } 45 | trap cleanup EXIT HUP 46 | fi 47 | 48 | npm run system-test 49 | 50 | # codecov combines coverage across integration and unit tests. Include 51 | # the logic below for any environment you wish to collect coverage for: 52 | COVERAGE_NODE=18 53 | if npx check-node-version@3.3.0 --silent --node $COVERAGE_NODE; then 54 | NYC_BIN=./node_modules/nyc/bin/nyc.js 55 | if [ -f "$NYC_BIN" ]; then 56 | $NYC_BIN report || true 57 | fi 58 | bash $KOKORO_GFILE_DIR/codecov.sh 59 | else 60 | echo "coverage is only reported for Node $COVERAGE_NODE" 61 | fi 62 | -------------------------------------------------------------------------------- /.kokoro/test.bat: -------------------------------------------------------------------------------- 1 | @rem Copyright 2018 Google LLC. All rights reserved. 2 | @rem 3 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 4 | @rem you may not use this file except in compliance with the License. 5 | @rem You may obtain a copy of the License at 6 | @rem 7 | @rem http://www.apache.org/licenses/LICENSE-2.0 8 | @rem 9 | @rem Unless required by applicable law or agreed to in writing, software 10 | @rem distributed under the License is distributed on an "AS IS" BASIS, 11 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | @rem See the License for the specific language governing permissions and 13 | @rem limitations under the License. 14 | 15 | @echo "Starting Windows build" 16 | 17 | cd /d %~dp0 18 | cd .. 19 | 20 | @rem npm path is not currently set in our image, we should fix this next time 21 | @rem we upgrade Node.js in the image: 22 | SET PATH=%PATH%;/cygdrive/c/Program Files/nodejs/npm 23 | 24 | call nvm use 18 25 | call which node 26 | 27 | call npm install || goto :error 28 | call npm run test || goto :error 29 | 30 | goto :EOF 31 | 32 | :error 33 | exit /b 1 34 | -------------------------------------------------------------------------------- /.kokoro/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | export NPM_CONFIG_PREFIX=${HOME}/.npm-global 20 | 21 | cd $(dirname $0)/.. 22 | 23 | npm install 24 | # If tests are running against main branch, configure flakybot 25 | # to open issues on failures: 26 | if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]] || [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"nightly"* ]]; then 27 | export MOCHA_REPORTER_OUTPUT=test_output_sponge_log.xml 28 | export MOCHA_REPORTER=xunit 29 | cleanup() { 30 | chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot 31 | $KOKORO_GFILE_DIR/linux_amd64/flakybot 32 | } 33 | trap cleanup EXIT HUP 34 | fi 35 | # Unit tests exercise the entire API surface, which may include 36 | # deprecation warnings: 37 | export MOCHA_THROW_DEPRECATION=false 38 | npm test 39 | 40 | # codecov combines coverage across integration and unit tests. Include 41 | # the logic below for any environment you wish to collect coverage for: 42 | COVERAGE_NODE=18 43 | if npx check-node-version@3.3.0 --silent --node $COVERAGE_NODE; then 44 | NYC_BIN=./node_modules/nyc/bin/nyc.js 45 | if [ -f "$NYC_BIN" ]; then 46 | $NYC_BIN report || true 47 | fi 48 | bash $KOKORO_GFILE_DIR/codecov.sh 49 | else 50 | echo "coverage is only reported for Node $COVERAGE_NODE" 51 | fi 52 | -------------------------------------------------------------------------------- /.kokoro/trampoline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This file is not used any more, but we keep this file for making it 17 | # easy to roll back. 18 | # TODO: Remove this file from the template. 19 | 20 | set -eo pipefail 21 | 22 | # Always run the cleanup script, regardless of the success of bouncing into 23 | # the container. 24 | function cleanup() { 25 | chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh 26 | ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh 27 | echo "cleanup"; 28 | } 29 | trap cleanup EXIT 30 | 31 | $(dirname $0)/populate-secrets.sh # Secret Manager secrets. 32 | python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" 33 | -------------------------------------------------------------------------------- /.kokoro/trampoline_v2.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2020 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # trampoline_v2.sh 17 | # 18 | # If you want to make a change to this file, consider doing so at: 19 | # https://github.com/googlecloudplatform/docker-ci-helper 20 | # 21 | # This script is for running CI builds. For Kokoro builds, we 22 | # set this script to `build_file` field in the Kokoro configuration. 23 | 24 | # This script does 3 things. 25 | # 26 | # 1. Prepare the Docker image for the test 27 | # 2. Run the Docker with appropriate flags to run the test 28 | # 3. Upload the newly built Docker image 29 | # 30 | # in a way that is somewhat compatible with trampoline_v1. 31 | # 32 | # These environment variables are required: 33 | # TRAMPOLINE_IMAGE: The docker image to use. 34 | # TRAMPOLINE_DOCKERFILE: The location of the Dockerfile. 35 | # 36 | # You can optionally change these environment variables: 37 | # TRAMPOLINE_IMAGE_UPLOAD: 38 | # (true|false): Whether to upload the Docker image after the 39 | # successful builds. 40 | # TRAMPOLINE_BUILD_FILE: The script to run in the docker container. 41 | # TRAMPOLINE_WORKSPACE: The workspace path in the docker container. 42 | # Defaults to /workspace. 43 | # Potentially there are some repo specific envvars in .trampolinerc in 44 | # the project root. 45 | # 46 | # Here is an example for running this script. 47 | # TRAMPOLINE_IMAGE=gcr.io/cloud-devrel-kokoro-resources/node:18-user \ 48 | # TRAMPOLINE_BUILD_FILE=.kokoro/system-test.sh \ 49 | # .kokoro/trampoline_v2.sh 50 | 51 | set -euo pipefail 52 | 53 | TRAMPOLINE_VERSION="2.0.7" 54 | 55 | if command -v tput >/dev/null && [[ -n "${TERM:-}" ]]; then 56 | readonly IO_COLOR_RED="$(tput setaf 1)" 57 | readonly IO_COLOR_GREEN="$(tput setaf 2)" 58 | readonly IO_COLOR_YELLOW="$(tput setaf 3)" 59 | readonly IO_COLOR_RESET="$(tput sgr0)" 60 | else 61 | readonly IO_COLOR_RED="" 62 | readonly IO_COLOR_GREEN="" 63 | readonly IO_COLOR_YELLOW="" 64 | readonly IO_COLOR_RESET="" 65 | fi 66 | 67 | function function_exists { 68 | [ $(LC_ALL=C type -t $1)"" == "function" ] 69 | } 70 | 71 | # Logs a message using the given color. The first argument must be one 72 | # of the IO_COLOR_* variables defined above, such as 73 | # "${IO_COLOR_YELLOW}". The remaining arguments will be logged in the 74 | # given color. The log message will also have an RFC-3339 timestamp 75 | # prepended (in UTC). You can disable the color output by setting 76 | # TERM=vt100. 77 | function log_impl() { 78 | local color="$1" 79 | shift 80 | local timestamp="$(date -u "+%Y-%m-%dT%H:%M:%SZ")" 81 | echo "================================================================" 82 | echo "${color}${timestamp}:" "$@" "${IO_COLOR_RESET}" 83 | echo "================================================================" 84 | } 85 | 86 | # Logs the given message with normal coloring and a timestamp. 87 | function log() { 88 | log_impl "${IO_COLOR_RESET}" "$@" 89 | } 90 | 91 | # Logs the given message in green with a timestamp. 92 | function log_green() { 93 | log_impl "${IO_COLOR_GREEN}" "$@" 94 | } 95 | 96 | # Logs the given message in yellow with a timestamp. 97 | function log_yellow() { 98 | log_impl "${IO_COLOR_YELLOW}" "$@" 99 | } 100 | 101 | # Logs the given message in red with a timestamp. 102 | function log_red() { 103 | log_impl "${IO_COLOR_RED}" "$@" 104 | } 105 | 106 | readonly tmpdir=$(mktemp -d -t ci-XXXXXXXX) 107 | readonly tmphome="${tmpdir}/h" 108 | mkdir -p "${tmphome}" 109 | 110 | function cleanup() { 111 | rm -rf "${tmpdir}" 112 | } 113 | trap cleanup EXIT 114 | 115 | RUNNING_IN_CI="${RUNNING_IN_CI:-false}" 116 | 117 | # The workspace in the container, defaults to /workspace. 118 | TRAMPOLINE_WORKSPACE="${TRAMPOLINE_WORKSPACE:-/workspace}" 119 | 120 | pass_down_envvars=( 121 | # TRAMPOLINE_V2 variables. 122 | # Tells scripts whether they are running as part of CI or not. 123 | "RUNNING_IN_CI" 124 | # Indicates which CI system we're in. 125 | "TRAMPOLINE_CI" 126 | # Indicates the version of the script. 127 | "TRAMPOLINE_VERSION" 128 | # Contains path to build artifacts being executed. 129 | "KOKORO_BUILD_ARTIFACTS_SUBDIR" 130 | ) 131 | 132 | log_yellow "Building with Trampoline ${TRAMPOLINE_VERSION}" 133 | 134 | # Detect which CI systems we're in. If we're in any of the CI systems 135 | # we support, `RUNNING_IN_CI` will be true and `TRAMPOLINE_CI` will be 136 | # the name of the CI system. Both envvars will be passing down to the 137 | # container for telling which CI system we're in. 138 | if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then 139 | # descriptive env var for indicating it's on CI. 140 | RUNNING_IN_CI="true" 141 | TRAMPOLINE_CI="kokoro" 142 | if [[ "${TRAMPOLINE_USE_LEGACY_SERVICE_ACCOUNT:-}" == "true" ]]; then 143 | if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then 144 | log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting." 145 | exit 1 146 | fi 147 | # This service account will be activated later. 148 | TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" 149 | else 150 | if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then 151 | gcloud auth list 152 | fi 153 | log_yellow "Configuring Container Registry access" 154 | gcloud auth configure-docker --quiet 155 | fi 156 | pass_down_envvars+=( 157 | # KOKORO dynamic variables. 158 | "KOKORO_BUILD_NUMBER" 159 | "KOKORO_BUILD_ID" 160 | "KOKORO_JOB_NAME" 161 | "KOKORO_GIT_COMMIT" 162 | "KOKORO_GITHUB_COMMIT" 163 | "KOKORO_GITHUB_PULL_REQUEST_NUMBER" 164 | "KOKORO_GITHUB_PULL_REQUEST_COMMIT" 165 | # For flakybot 166 | "KOKORO_GITHUB_COMMIT_URL" 167 | "KOKORO_GITHUB_PULL_REQUEST_URL" 168 | ) 169 | elif [[ "${TRAVIS:-}" == "true" ]]; then 170 | RUNNING_IN_CI="true" 171 | TRAMPOLINE_CI="travis" 172 | pass_down_envvars+=( 173 | "TRAVIS_BRANCH" 174 | "TRAVIS_BUILD_ID" 175 | "TRAVIS_BUILD_NUMBER" 176 | "TRAVIS_BUILD_WEB_URL" 177 | "TRAVIS_COMMIT" 178 | "TRAVIS_COMMIT_MESSAGE" 179 | "TRAVIS_COMMIT_RANGE" 180 | "TRAVIS_JOB_NAME" 181 | "TRAVIS_JOB_NUMBER" 182 | "TRAVIS_JOB_WEB_URL" 183 | "TRAVIS_PULL_REQUEST" 184 | "TRAVIS_PULL_REQUEST_BRANCH" 185 | "TRAVIS_PULL_REQUEST_SHA" 186 | "TRAVIS_PULL_REQUEST_SLUG" 187 | "TRAVIS_REPO_SLUG" 188 | "TRAVIS_SECURE_ENV_VARS" 189 | "TRAVIS_TAG" 190 | ) 191 | elif [[ -n "${GITHUB_RUN_ID:-}" ]]; then 192 | RUNNING_IN_CI="true" 193 | TRAMPOLINE_CI="github-workflow" 194 | pass_down_envvars+=( 195 | "GITHUB_WORKFLOW" 196 | "GITHUB_RUN_ID" 197 | "GITHUB_RUN_NUMBER" 198 | "GITHUB_ACTION" 199 | "GITHUB_ACTIONS" 200 | "GITHUB_ACTOR" 201 | "GITHUB_REPOSITORY" 202 | "GITHUB_EVENT_NAME" 203 | "GITHUB_EVENT_PATH" 204 | "GITHUB_SHA" 205 | "GITHUB_REF" 206 | "GITHUB_HEAD_REF" 207 | "GITHUB_BASE_REF" 208 | ) 209 | elif [[ "${CIRCLECI:-}" == "true" ]]; then 210 | RUNNING_IN_CI="true" 211 | TRAMPOLINE_CI="circleci" 212 | pass_down_envvars+=( 213 | "CIRCLE_BRANCH" 214 | "CIRCLE_BUILD_NUM" 215 | "CIRCLE_BUILD_URL" 216 | "CIRCLE_COMPARE_URL" 217 | "CIRCLE_JOB" 218 | "CIRCLE_NODE_INDEX" 219 | "CIRCLE_NODE_TOTAL" 220 | "CIRCLE_PREVIOUS_BUILD_NUM" 221 | "CIRCLE_PROJECT_REPONAME" 222 | "CIRCLE_PROJECT_USERNAME" 223 | "CIRCLE_REPOSITORY_URL" 224 | "CIRCLE_SHA1" 225 | "CIRCLE_STAGE" 226 | "CIRCLE_USERNAME" 227 | "CIRCLE_WORKFLOW_ID" 228 | "CIRCLE_WORKFLOW_JOB_ID" 229 | "CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS" 230 | "CIRCLE_WORKFLOW_WORKSPACE_ID" 231 | ) 232 | fi 233 | 234 | # Configure the service account for pulling the docker image. 235 | function repo_root() { 236 | local dir="$1" 237 | while [[ ! -d "${dir}/.git" ]]; do 238 | dir="$(dirname "$dir")" 239 | done 240 | echo "${dir}" 241 | } 242 | 243 | # Detect the project root. In CI builds, we assume the script is in 244 | # the git tree and traverse from there, otherwise, traverse from `pwd` 245 | # to find `.git` directory. 246 | if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then 247 | PROGRAM_PATH="$(realpath "$0")" 248 | PROGRAM_DIR="$(dirname "${PROGRAM_PATH}")" 249 | PROJECT_ROOT="$(repo_root "${PROGRAM_DIR}")" 250 | else 251 | PROJECT_ROOT="$(repo_root $(pwd))" 252 | fi 253 | 254 | log_yellow "Changing to the project root: ${PROJECT_ROOT}." 255 | cd "${PROJECT_ROOT}" 256 | 257 | # To support relative path for `TRAMPOLINE_SERVICE_ACCOUNT`, we need 258 | # to use this environment variable in `PROJECT_ROOT`. 259 | if [[ -n "${TRAMPOLINE_SERVICE_ACCOUNT:-}" ]]; then 260 | 261 | mkdir -p "${tmpdir}/gcloud" 262 | gcloud_config_dir="${tmpdir}/gcloud" 263 | 264 | log_yellow "Using isolated gcloud config: ${gcloud_config_dir}." 265 | export CLOUDSDK_CONFIG="${gcloud_config_dir}" 266 | 267 | log_yellow "Using ${TRAMPOLINE_SERVICE_ACCOUNT} for authentication." 268 | gcloud auth activate-service-account \ 269 | --key-file "${TRAMPOLINE_SERVICE_ACCOUNT}" 270 | log_yellow "Configuring Container Registry access" 271 | gcloud auth configure-docker --quiet 272 | fi 273 | 274 | required_envvars=( 275 | # The basic trampoline configurations. 276 | "TRAMPOLINE_IMAGE" 277 | "TRAMPOLINE_BUILD_FILE" 278 | ) 279 | 280 | if [[ -f "${PROJECT_ROOT}/.trampolinerc" ]]; then 281 | source "${PROJECT_ROOT}/.trampolinerc" 282 | fi 283 | 284 | log_yellow "Checking environment variables." 285 | for e in "${required_envvars[@]}" 286 | do 287 | if [[ -z "${!e:-}" ]]; then 288 | log "Missing ${e} env var. Aborting." 289 | exit 1 290 | fi 291 | done 292 | 293 | # We want to support legacy style TRAMPOLINE_BUILD_FILE used with V1 294 | # script: e.g. "github/repo-name/.kokoro/run_tests.sh" 295 | TRAMPOLINE_BUILD_FILE="${TRAMPOLINE_BUILD_FILE#github/*/}" 296 | log_yellow "Using TRAMPOLINE_BUILD_FILE: ${TRAMPOLINE_BUILD_FILE}" 297 | 298 | # ignore error on docker operations and test execution 299 | set +e 300 | 301 | log_yellow "Preparing Docker image." 302 | # We only download the docker image in CI builds. 303 | if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then 304 | # Download the docker image specified by `TRAMPOLINE_IMAGE` 305 | 306 | # We may want to add --max-concurrent-downloads flag. 307 | 308 | log_yellow "Start pulling the Docker image: ${TRAMPOLINE_IMAGE}." 309 | if docker pull "${TRAMPOLINE_IMAGE}"; then 310 | log_green "Finished pulling the Docker image: ${TRAMPOLINE_IMAGE}." 311 | has_image="true" 312 | else 313 | log_red "Failed pulling the Docker image: ${TRAMPOLINE_IMAGE}." 314 | has_image="false" 315 | fi 316 | else 317 | # For local run, check if we have the image. 318 | if docker images "${TRAMPOLINE_IMAGE}" | grep "${TRAMPOLINE_IMAGE%:*}"; then 319 | has_image="true" 320 | else 321 | has_image="false" 322 | fi 323 | fi 324 | 325 | 326 | # The default user for a Docker container has uid 0 (root). To avoid 327 | # creating root-owned files in the build directory we tell docker to 328 | # use the current user ID. 329 | user_uid="$(id -u)" 330 | user_gid="$(id -g)" 331 | user_name="$(id -un)" 332 | 333 | # To allow docker in docker, we add the user to the docker group in 334 | # the host os. 335 | docker_gid=$(cut -d: -f3 < <(getent group docker)) 336 | 337 | update_cache="false" 338 | if [[ "${TRAMPOLINE_DOCKERFILE:-none}" != "none" ]]; then 339 | # Build the Docker image from the source. 340 | context_dir=$(dirname "${TRAMPOLINE_DOCKERFILE}") 341 | docker_build_flags=( 342 | "-f" "${TRAMPOLINE_DOCKERFILE}" 343 | "-t" "${TRAMPOLINE_IMAGE}" 344 | "--build-arg" "UID=${user_uid}" 345 | "--build-arg" "USERNAME=${user_name}" 346 | ) 347 | if [[ "${has_image}" == "true" ]]; then 348 | docker_build_flags+=("--cache-from" "${TRAMPOLINE_IMAGE}") 349 | fi 350 | 351 | log_yellow "Start building the docker image." 352 | if [[ "${TRAMPOLINE_VERBOSE:-false}" == "true" ]]; then 353 | echo "docker build" "${docker_build_flags[@]}" "${context_dir}" 354 | fi 355 | 356 | # ON CI systems, we want to suppress docker build logs, only 357 | # output the logs when it fails. 358 | if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then 359 | if docker build "${docker_build_flags[@]}" "${context_dir}" \ 360 | > "${tmpdir}/docker_build.log" 2>&1; then 361 | if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then 362 | cat "${tmpdir}/docker_build.log" 363 | fi 364 | 365 | log_green "Finished building the docker image." 366 | update_cache="true" 367 | else 368 | log_red "Failed to build the Docker image, aborting." 369 | log_yellow "Dumping the build logs:" 370 | cat "${tmpdir}/docker_build.log" 371 | exit 1 372 | fi 373 | else 374 | if docker build "${docker_build_flags[@]}" "${context_dir}"; then 375 | log_green "Finished building the docker image." 376 | update_cache="true" 377 | else 378 | log_red "Failed to build the Docker image, aborting." 379 | exit 1 380 | fi 381 | fi 382 | else 383 | if [[ "${has_image}" != "true" ]]; then 384 | log_red "We do not have ${TRAMPOLINE_IMAGE} locally, aborting." 385 | exit 1 386 | fi 387 | fi 388 | 389 | # We use an array for the flags so they are easier to document. 390 | docker_flags=( 391 | # Remove the container after it exists. 392 | "--rm" 393 | 394 | # Use the host network. 395 | "--network=host" 396 | 397 | # Run in priviledged mode. We are not using docker for sandboxing or 398 | # isolation, just for packaging our dev tools. 399 | "--privileged" 400 | 401 | # Run the docker script with the user id. Because the docker image gets to 402 | # write in ${PWD} you typically want this to be your user id. 403 | # To allow docker in docker, we need to use docker gid on the host. 404 | "--user" "${user_uid}:${docker_gid}" 405 | 406 | # Pass down the USER. 407 | "--env" "USER=${user_name}" 408 | 409 | # Mount the project directory inside the Docker container. 410 | "--volume" "${PROJECT_ROOT}:${TRAMPOLINE_WORKSPACE}" 411 | "--workdir" "${TRAMPOLINE_WORKSPACE}" 412 | "--env" "PROJECT_ROOT=${TRAMPOLINE_WORKSPACE}" 413 | 414 | # Mount the temporary home directory. 415 | "--volume" "${tmphome}:/h" 416 | "--env" "HOME=/h" 417 | 418 | # Allow docker in docker. 419 | "--volume" "/var/run/docker.sock:/var/run/docker.sock" 420 | 421 | # Mount the /tmp so that docker in docker can mount the files 422 | # there correctly. 423 | "--volume" "/tmp:/tmp" 424 | # Pass down the KOKORO_GFILE_DIR and KOKORO_KEYSTORE_DIR 425 | # TODO(tmatsuo): This part is not portable. 426 | "--env" "TRAMPOLINE_SECRET_DIR=/secrets" 427 | "--volume" "${KOKORO_GFILE_DIR:-/dev/shm}:/secrets/gfile" 428 | "--env" "KOKORO_GFILE_DIR=/secrets/gfile" 429 | "--volume" "${KOKORO_KEYSTORE_DIR:-/dev/shm}:/secrets/keystore" 430 | "--env" "KOKORO_KEYSTORE_DIR=/secrets/keystore" 431 | ) 432 | 433 | # Add an option for nicer output if the build gets a tty. 434 | if [[ -t 0 ]]; then 435 | docker_flags+=("-it") 436 | fi 437 | 438 | # Passing down env vars 439 | for e in "${pass_down_envvars[@]}" 440 | do 441 | if [[ -n "${!e:-}" ]]; then 442 | docker_flags+=("--env" "${e}=${!e}") 443 | fi 444 | done 445 | 446 | # If arguments are given, all arguments will become the commands run 447 | # in the container, otherwise run TRAMPOLINE_BUILD_FILE. 448 | if [[ $# -ge 1 ]]; then 449 | log_yellow "Running the given commands '" "${@:1}" "' in the container." 450 | readonly commands=("${@:1}") 451 | if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then 452 | echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}" 453 | fi 454 | docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}" 455 | else 456 | log_yellow "Running the tests in a Docker container." 457 | docker_flags+=("--entrypoint=${TRAMPOLINE_BUILD_FILE}") 458 | if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then 459 | echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" 460 | fi 461 | docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" 462 | fi 463 | 464 | 465 | test_retval=$? 466 | 467 | if [[ ${test_retval} -eq 0 ]]; then 468 | log_green "Build finished with ${test_retval}" 469 | else 470 | log_red "Build finished with ${test_retval}" 471 | fi 472 | 473 | # Only upload it when the test passes. 474 | if [[ "${update_cache}" == "true" ]] && \ 475 | [[ $test_retval == 0 ]] && \ 476 | [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]]; then 477 | log_yellow "Uploading the Docker image." 478 | if docker push "${TRAMPOLINE_IMAGE}"; then 479 | log_green "Finished uploading the Docker image." 480 | else 481 | log_red "Failed uploading the Docker image." 482 | fi 483 | # Call trampoline_after_upload_hook if it's defined. 484 | if function_exists trampoline_after_upload_hook; then 485 | trampoline_after_upload_hook 486 | fi 487 | 488 | fi 489 | 490 | exit "${test_retval}" 491 | -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | const config = { 15 | "enable-source-maps": true, 16 | "throw-deprecation": true, 17 | "timeout": 10000, 18 | "recursive": true 19 | } 20 | if (process.env.MOCHA_THROW_DEPRECATION === 'false') { 21 | delete config['throw-deprecation']; 22 | } 23 | if (process.env.MOCHA_REPORTER) { 24 | config.reporter = process.env.MOCHA_REPORTER; 25 | } 26 | if (process.env.MOCHA_REPORTER_OUTPUT) { 27 | config['reporter-option'] = `output=${process.env.MOCHA_REPORTER_OUTPUT}`; 28 | } 29 | module.exports = config 30 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "report-dir": "./.coverage", 3 | "reporter": ["text", "lcov"], 4 | "exclude": [ 5 | "**/*-test", 6 | "**/.coverage", 7 | "**/apis", 8 | "**/benchmark", 9 | "**/conformance", 10 | "**/docs", 11 | "**/samples", 12 | "**/scripts", 13 | "**/protos", 14 | "**/test", 15 | "**/*.d.ts", 16 | ".jsdoc.js", 17 | "**/.jsdoc.js", 18 | "karma.conf.js", 19 | "webpack-tests.config.js", 20 | "webpack.config.js" 21 | ], 22 | "exclude-after-remap": false, 23 | "all": true 24 | } 25 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/coverage 3 | test/fixtures 4 | build/ 5 | docs/ 6 | protos/ 7 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | module.exports = { 16 | ...require('gts/.prettierrc.json') 17 | } 18 | -------------------------------------------------------------------------------- /.repo-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gaxios", 3 | "name_pretty": "Gaxios", 4 | "release_level": "stable", 5 | "client_documentation": "https://github.com/googleapis/gaxios", 6 | "library_type": "CORE", 7 | "language": "nodejs", 8 | "repo": "googleapis/gaxios", 9 | "distribution_name": "gaxios", 10 | "api_shortname": "gaxios" 11 | } 12 | -------------------------------------------------------------------------------- /.trampolinerc: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Template for .trampolinerc 16 | 17 | # Add required env vars here. 18 | required_envvars+=( 19 | ) 20 | 21 | # Add env vars which are passed down into the container here. 22 | pass_down_envvars+=( 23 | "AUTORELEASE_PR" 24 | "VERSION" 25 | ) 26 | 27 | # Prevent unintentional override on the default image. 28 | if [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]] && \ 29 | [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then 30 | echo "Please set TRAMPOLINE_IMAGE if you want to upload the Docker image." 31 | exit 1 32 | fi 33 | 34 | # Define the default value if it makes sense. 35 | if [[ -z "${TRAMPOLINE_IMAGE_UPLOAD:-}" ]]; then 36 | TRAMPOLINE_IMAGE_UPLOAD="" 37 | fi 38 | 39 | if [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then 40 | TRAMPOLINE_IMAGE="" 41 | fi 42 | 43 | if [[ -z "${TRAMPOLINE_DOCKERFILE:-}" ]]; then 44 | TRAMPOLINE_DOCKERFILE="" 45 | fi 46 | 47 | if [[ -z "${TRAMPOLINE_BUILD_FILE:-}" ]]; then 48 | TRAMPOLINE_BUILD_FILE="" 49 | fi 50 | 51 | # Secret Manager secrets. 52 | source ${PROJECT_ROOT}/.kokoro/populate-secrets.sh 53 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [6.7.1](https://github.com/googleapis/gaxios/compare/v6.7.0...v6.7.1) (2024-08-13) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * Release uuid rollback ([#641](https://github.com/googleapis/gaxios/issues/641)) ([2e21115](https://github.com/googleapis/gaxios/commit/2e211158d5351d81de4e84f999ec3b41475ec0cd)) 9 | 10 | ## [6.7.0](https://github.com/googleapis/gaxios/compare/v6.6.0...v6.7.0) (2024-06-27) 11 | 12 | 13 | ### Features 14 | 15 | * Add additional retry configuration options ([#634](https://github.com/googleapis/gaxios/issues/634)) ([cb5c833](https://github.com/googleapis/gaxios/commit/cb5c833a9750bf6d0c0f8e27992bb44bd953566c)) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **deps:** Update dependency uuid to v10 ([#629](https://github.com/googleapis/gaxios/issues/629)) ([6ff684e](https://github.com/googleapis/gaxios/commit/6ff684e6e6e5f4e5e6d270685b2ac0e4d28bc964)) 21 | 22 | ## [6.6.0](https://github.com/googleapis/gaxios/compare/v6.5.0...v6.6.0) (2024-05-15) 23 | 24 | 25 | ### Features 26 | 27 | * Add request and response interceptors ([#619](https://github.com/googleapis/gaxios/issues/619)) ([059fe77](https://github.com/googleapis/gaxios/commit/059fe7708e6d98cc44814ec1fad7d412668a05b9)) 28 | 29 | ## [6.5.0](https://github.com/googleapis/gaxios/compare/v6.4.0...v6.5.0) (2024-04-09) 30 | 31 | 32 | ### Features 33 | 34 | * Retry `408` by Default ([#616](https://github.com/googleapis/gaxios/issues/616)) ([9331f79](https://github.com/googleapis/gaxios/commit/9331f79f9c9d0c1f4f1f995e1928323f4feb5427)) 35 | * Support `proxy` option ([#614](https://github.com/googleapis/gaxios/issues/614)) ([2d14b3f](https://github.com/googleapis/gaxios/commit/2d14b3f54bc97111cb184cecf2379b55ceaca3c2)) 36 | 37 | ## [6.4.0](https://github.com/googleapis/gaxios/compare/v6.3.0...v6.4.0) (2024-04-03) 38 | 39 | 40 | ### Features 41 | 42 | * Enhance Error Redaction ([#609](https://github.com/googleapis/gaxios/issues/609)) ([b1d2875](https://github.com/googleapis/gaxios/commit/b1d28759110f91b37746f9b88aba92bf52df2fcc)) 43 | * Support multipart/related requests ([#610](https://github.com/googleapis/gaxios/issues/610)) ([086c824](https://github.com/googleapis/gaxios/commit/086c8240652bd893dff0dd4c097ef00f5777564e)) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * Error Redactor Case-Insensitive Matching ([#613](https://github.com/googleapis/gaxios/issues/613)) ([05e65ef](https://github.com/googleapis/gaxios/commit/05e65efda6d13e760d4f7f87be7d6cebeba3cc64)) 49 | 50 | ## [6.3.0](https://github.com/googleapis/gaxios/compare/v6.2.0...v6.3.0) (2024-02-01) 51 | 52 | 53 | ### Features 54 | 55 | * Support URL objects ([#598](https://github.com/googleapis/gaxios/issues/598)) ([ef40c61](https://github.com/googleapis/gaxios/commit/ef40c61fabf0a48b2f08be085ee0c56dc32cf78c)) 56 | 57 | ## [6.2.0](https://github.com/googleapis/gaxios/compare/v6.1.1...v6.2.0) (2024-01-31) 58 | 59 | 60 | ### Features 61 | 62 | * Extend `instanceof` Support for GaxiosError ([#593](https://github.com/googleapis/gaxios/issues/593)) ([4fd1fe2](https://github.com/googleapis/gaxios/commit/4fd1fe2c91b9888c4f976cf91e752d407b99ec75)) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * Do Not Mutate Config for Redacted Retries ([#597](https://github.com/googleapis/gaxios/issues/597)) ([4d1a551](https://github.com/googleapis/gaxios/commit/4d1a55134940031a1e0ff2392ab0b08c186166f0)) 68 | * Return text when content type is text/* ([#579](https://github.com/googleapis/gaxios/issues/579)) ([3cc1c76](https://github.com/googleapis/gaxios/commit/3cc1c76a08d98daac01c83bed6f9480320ec0a37)) 69 | 70 | ## [6.1.1](https://github.com/googleapis/gaxios/compare/v6.1.0...v6.1.1) (2023-09-07) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * Don't throw an error within a `GaxiosError` ([#569](https://github.com/googleapis/gaxios/issues/569)) ([035d9dd](https://github.com/googleapis/gaxios/commit/035d9dd833c3ad63148bc50facaee421f4792192)) 76 | 77 | ## [6.1.0](https://github.com/googleapis/gaxios/compare/v6.0.4...v6.1.0) (2023-08-11) 78 | 79 | 80 | ### Features 81 | 82 | * Prevent Auth Logging by Default ([#565](https://github.com/googleapis/gaxios/issues/565)) ([b28e562](https://github.com/googleapis/gaxios/commit/b28e5628f5964e2ecc04cc1df8a54948567a4b48)) 83 | 84 | ## [6.0.4](https://github.com/googleapis/gaxios/compare/v6.0.3...v6.0.4) (2023-08-03) 85 | 86 | 87 | ### Bug Fixes 88 | 89 | * **deps:** Update https-proxy-agent to 7.0.1 and fix imports and tests ([#560](https://github.com/googleapis/gaxios/issues/560)) ([5c877e2](https://github.com/googleapis/gaxios/commit/5c877e28c3c9336a87b50536c074cb215b779d8e)) 90 | 91 | ## [6.0.3](https://github.com/googleapis/gaxios/compare/v6.0.2...v6.0.3) (2023-07-24) 92 | 93 | 94 | ### Bug Fixes 95 | 96 | * Handle invalid json when Content-Type=application/json ([#558](https://github.com/googleapis/gaxios/issues/558)) ([71602eb](https://github.com/googleapis/gaxios/commit/71602ebc2ab18d5af904b152723756f57fb13bce)) 97 | 98 | ## [6.0.2](https://github.com/googleapis/gaxios/compare/v6.0.1...v6.0.2) (2023-07-20) 99 | 100 | 101 | ### Bug Fixes 102 | 103 | * Revert attempting to convert 'text/plain', leave as data in GaxiosError ([#556](https://github.com/googleapis/gaxios/issues/556)) ([d603bde](https://github.com/googleapis/gaxios/commit/d603bde35f698564c028108d24c6891cec3b8ea1)) 104 | 105 | ## [6.0.1](https://github.com/googleapis/gaxios/compare/v6.0.0...v6.0.1) (2023-07-20) 106 | 107 | 108 | ### Bug Fixes 109 | 110 | * Add text to responseType switch statement ([#554](https://github.com/googleapis/gaxios/issues/554)) ([899cf1f](https://github.com/googleapis/gaxios/commit/899cf1fb088f49927624d526f6de212837a31b9d)) 111 | 112 | ## [6.0.0](https://github.com/googleapis/gaxios/compare/v5.1.3...v6.0.0) (2023-07-12) 113 | 114 | 115 | ### ⚠ BREAKING CHANGES 116 | 117 | * add status as a number to GaxiosError, change code to error code as a string ([#552](https://github.com/googleapis/gaxios/issues/552)) 118 | * migrate to Node 14 ([#548](https://github.com/googleapis/gaxios/issues/548)) 119 | * examine response content-type if no contentType is set ([#535](https://github.com/googleapis/gaxios/issues/535)) 120 | 121 | ### Bug Fixes 122 | 123 | * Add status as a number to GaxiosError, change code to error code as a string ([#552](https://github.com/googleapis/gaxios/issues/552)) ([88ba2e9](https://github.com/googleapis/gaxios/commit/88ba2e99e32b66d84725c9ea9ad95152bd1dc653)) 124 | * Examine response content-type if no contentType is set ([#535](https://github.com/googleapis/gaxios/issues/535)) ([cd8ca7b](https://github.com/googleapis/gaxios/commit/cd8ca7b209f0ba932082a80ace8fec608a71facf)) 125 | 126 | 127 | ### Miscellaneous Chores 128 | 129 | * Migrate to Node 14 ([#548](https://github.com/googleapis/gaxios/issues/548)) ([b9b26eb](https://github.com/googleapis/gaxios/commit/b9b26eb2c4af35633efd91770aa24c4b5d9019b4)) 130 | 131 | ## [5.1.3](https://github.com/googleapis/gaxios/compare/v5.1.2...v5.1.3) (2023-07-05) 132 | 133 | 134 | ### Bug Fixes 135 | 136 | * Translate GaxiosError message to object regardless of return type (return data as default) ([#546](https://github.com/googleapis/gaxios/issues/546)) ([adfd570](https://github.com/googleapis/gaxios/commit/adfd57068a98d03921d5383fed11a652a21d59dd)) 137 | 138 | ## [5.1.2](https://github.com/googleapis/gaxios/compare/v5.1.1...v5.1.2) (2023-06-25) 139 | 140 | 141 | ### Bug Fixes 142 | 143 | * Revert changes to error handling due to downstream breakage ([#544](https://github.com/googleapis/gaxios/issues/544)) ([64fbf07](https://github.com/googleapis/gaxios/commit/64fbf07f3697f40b75a9e7dbe8bff7f6243a9e12)) 144 | 145 | ## [5.1.1](https://github.com/googleapis/gaxios/compare/v5.1.0...v5.1.1) (2023-06-23) 146 | 147 | 148 | ### Bug Fixes 149 | 150 | * Translate GaxiosError message to object regardless of return type ([#537](https://github.com/googleapis/gaxios/issues/537)) ([563c653](https://github.com/googleapis/gaxios/commit/563c6537a06bc64d5c6e918090c00ec7a586cecb)) 151 | 152 | ## [5.1.0](https://github.com/googleapis/gaxios/compare/v5.0.2...v5.1.0) (2023-03-06) 153 | 154 | 155 | ### Features 156 | 157 | * Add support for custom backoff ([#498](https://github.com/googleapis/gaxios/issues/498)) ([4a34467](https://github.com/googleapis/gaxios/commit/4a344678110864d97818a8272ebcc5e1c4921b52)) 158 | 159 | ## [5.0.2](https://github.com/googleapis/gaxios/compare/v5.0.1...v5.0.2) (2022-09-09) 160 | 161 | 162 | ### Bug Fixes 163 | 164 | * Do not import the whole google-gax from proto JS ([#1553](https://github.com/googleapis/gaxios/issues/1553)) ([#501](https://github.com/googleapis/gaxios/issues/501)) ([6f58d1e](https://github.com/googleapis/gaxios/commit/6f58d1e80ce6b196d17fbc75dc47d8d20804920a)) 165 | * use google-gax v3.3.0 ([6f58d1e](https://github.com/googleapis/gaxios/commit/6f58d1e80ce6b196d17fbc75dc47d8d20804920a)) 166 | 167 | ## [5.0.1](https://github.com/googleapis/gaxios/compare/v5.0.0...v5.0.1) (2022-07-04) 168 | 169 | 170 | ### Bug Fixes 171 | 172 | * **types:** loosen AbortSignal type ([5a379ca](https://github.com/googleapis/gaxios/commit/5a379ca123f08f286c4774711a7a3293bffc1ea6)) 173 | 174 | ## [5.0.0](https://github.com/googleapis/gaxios/compare/v4.3.3...v5.0.0) (2022-04-20) 175 | 176 | 177 | ### ⚠ BREAKING CHANGES 178 | 179 | * drop node 10 from engines list, update typescript to 4.6.3 (#477) 180 | 181 | ### Features 182 | 183 | * handling missing process global ([c30395b](https://github.com/googleapis/gaxios/commit/c30395bbf34d889e75c7c72a7dff701dc7a98244)) 184 | 185 | 186 | ### Build System 187 | 188 | * drop node 10 from engines list, update typescript to 4.6.3 ([#477](https://github.com/googleapis/gaxios/issues/477)) ([a926962](https://github.com/googleapis/gaxios/commit/a9269624a70aa6305599cc0af079d0225ed6af50)) 189 | 190 | ### [4.3.3](https://github.com/googleapis/gaxios/compare/v4.3.2...v4.3.3) (2022-04-08) 191 | 192 | 193 | ### Bug Fixes 194 | 195 | * do not stringify form data ([#475](https://github.com/googleapis/gaxios/issues/475)) ([17370dc](https://github.com/googleapis/gaxios/commit/17370dcdfd4568d7f3f0855961030d238166836a)) 196 | 197 | ### [4.3.2](https://www.github.com/googleapis/gaxios/compare/v4.3.1...v4.3.2) (2021-09-14) 198 | 199 | 200 | ### Bug Fixes 201 | 202 | * address codeql warning with hostname matches ([#415](https://www.github.com/googleapis/gaxios/issues/415)) ([5a4d060](https://www.github.com/googleapis/gaxios/commit/5a4d06019343aa08e1bcf8e05108e22ac3b12636)) 203 | 204 | ### [4.3.1](https://www.github.com/googleapis/gaxios/compare/v4.3.0...v4.3.1) (2021-09-02) 205 | 206 | 207 | ### Bug Fixes 208 | 209 | * **build:** switch primary branch to main ([#427](https://www.github.com/googleapis/gaxios/issues/427)) ([819219e](https://www.github.com/googleapis/gaxios/commit/819219ed742814259c525bdf5721b28234019c08)) 210 | 211 | ## [4.3.0](https://www.github.com/googleapis/gaxios/compare/v4.2.1...v4.3.0) (2021-05-26) 212 | 213 | 214 | ### Features 215 | 216 | * allow cert and key to be provided for mTLS ([#399](https://www.github.com/googleapis/gaxios/issues/399)) ([d74ab91](https://www.github.com/googleapis/gaxios/commit/d74ab9125d581e46d655614729872e79317c740d)) 217 | 218 | ### [4.2.1](https://www.github.com/googleapis/gaxios/compare/v4.2.0...v4.2.1) (2021-04-20) 219 | 220 | 221 | ### Bug Fixes 222 | 223 | * **deps:** upgrade webpack and karma-webpack ([#379](https://www.github.com/googleapis/gaxios/issues/379)) ([75c9013](https://www.github.com/googleapis/gaxios/commit/75c90132e99c2f960c01e0faf0f8e947c920aa73)) 224 | 225 | ## [4.2.0](https://www.github.com/googleapis/gaxios/compare/v4.1.0...v4.2.0) (2021-03-01) 226 | 227 | 228 | ### Features 229 | 230 | * handle application/x-www-form-urlencoded/Buffer ([#374](https://www.github.com/googleapis/gaxios/issues/374)) ([ce21e9c](https://www.github.com/googleapis/gaxios/commit/ce21e9ccd228578a9f90bb2fddff797cec4a9402)) 231 | 232 | ## [4.1.0](https://www.github.com/googleapis/gaxios/compare/v4.0.1...v4.1.0) (2020-12-08) 233 | 234 | 235 | ### Features 236 | 237 | * add an option to configure the fetch impl ([#342](https://www.github.com/googleapis/gaxios/issues/342)) ([2e081ef](https://www.github.com/googleapis/gaxios/commit/2e081ef161d84aa435788e8d525d393dc7964117)) 238 | * add no_proxy env variable ([#361](https://www.github.com/googleapis/gaxios/issues/361)) ([efe72a7](https://www.github.com/googleapis/gaxios/commit/efe72a71de81d466160dde5da551f7a41acc3ac4)) 239 | 240 | ### [4.0.1](https://www.github.com/googleapis/gaxios/compare/v4.0.0...v4.0.1) (2020-10-27) 241 | 242 | 243 | ### Bug Fixes 244 | 245 | * prevent bonus ? with empty qs params ([#357](https://www.github.com/googleapis/gaxios/issues/357)) ([b155f76](https://www.github.com/googleapis/gaxios/commit/b155f76cbc4c234da1d99c26691296702342c205)) 246 | 247 | ## [4.0.0](https://www.github.com/googleapis/gaxios/compare/v3.2.0...v4.0.0) (2020-10-21) 248 | 249 | 250 | ### ⚠ BREAKING CHANGES 251 | 252 | * parameters in `url` and parameters provided via params will now be combined. 253 | 254 | ### Bug Fixes 255 | 256 | * drop requirement on URL/combine url and params ([#338](https://www.github.com/googleapis/gaxios/issues/338)) ([e166bc6](https://www.github.com/googleapis/gaxios/commit/e166bc6721fd979070ab3d9c69b71ffe9ee061c7)) 257 | 258 | ## [3.2.0](https://www.github.com/googleapis/gaxios/compare/v3.1.0...v3.2.0) (2020-09-14) 259 | 260 | 261 | ### Features 262 | 263 | * add initial retry delay, and set default to 100ms ([#336](https://www.github.com/googleapis/gaxios/issues/336)) ([870326b](https://www.github.com/googleapis/gaxios/commit/870326b8245f16fafde0b0c32cfd2f277946e3a1)) 264 | 265 | ## [3.1.0](https://www.github.com/googleapis/gaxios/compare/v3.0.4...v3.1.0) (2020-07-30) 266 | 267 | 268 | ### Features 269 | 270 | * pass default adapter to adapter option ([#319](https://www.github.com/googleapis/gaxios/issues/319)) ([cf06bd9](https://www.github.com/googleapis/gaxios/commit/cf06bd9f51cbe707ed5973e390d31a091d4537c1)) 271 | 272 | ### [3.0.4](https://www.github.com/googleapis/gaxios/compare/v3.0.3...v3.0.4) (2020-07-09) 273 | 274 | 275 | ### Bug Fixes 276 | 277 | * typeo in nodejs .gitattribute ([#306](https://www.github.com/googleapis/gaxios/issues/306)) ([8514672](https://www.github.com/googleapis/gaxios/commit/8514672f9d56bc6f077dcbab050b3342d4e343c6)) 278 | 279 | ### [3.0.3](https://www.github.com/googleapis/gaxios/compare/v3.0.2...v3.0.3) (2020-04-20) 280 | 281 | 282 | ### Bug Fixes 283 | 284 | * apache license URL ([#468](https://www.github.com/googleapis/gaxios/issues/468)) ([#272](https://www.github.com/googleapis/gaxios/issues/272)) ([cf1b7cb](https://www.github.com/googleapis/gaxios/commit/cf1b7cb66e4c98405236834e63349931b4f35b90)) 285 | 286 | ### [3.0.2](https://www.github.com/googleapis/gaxios/compare/v3.0.1...v3.0.2) (2020-03-24) 287 | 288 | 289 | ### Bug Fixes 290 | 291 | * continue replacing application/x-www-form-urlencoded with application/json ([#263](https://www.github.com/googleapis/gaxios/issues/263)) ([dca176d](https://www.github.com/googleapis/gaxios/commit/dca176df0990f2c22255f9764405c496ea07ada2)) 292 | 293 | ### [3.0.1](https://www.github.com/googleapis/gaxios/compare/v3.0.0...v3.0.1) (2020-03-23) 294 | 295 | 296 | ### Bug Fixes 297 | 298 | * allow an alternate JSON content-type to be set ([#257](https://www.github.com/googleapis/gaxios/issues/257)) ([698a29f](https://www.github.com/googleapis/gaxios/commit/698a29ff3b22f30ea99ad190c4592940bef88f1f)) 299 | 300 | ## [3.0.0](https://www.github.com/googleapis/gaxios/compare/v2.3.2...v3.0.0) (2020-03-19) 301 | 302 | 303 | ### ⚠ BREAKING CHANGES 304 | 305 | * **deps:** TypeScript introduced breaking changes in generated code in 3.7.x 306 | * drop Node 8 from engines field (#254) 307 | 308 | ### Features 309 | 310 | * drop Node 8 from engines field ([#254](https://www.github.com/googleapis/gaxios/issues/254)) ([8c9fff7](https://www.github.com/googleapis/gaxios/commit/8c9fff7f92f70f029292c906c62d194c1d58827d)) 311 | * **deps:** updates to latest TypeScript ([#253](https://www.github.com/googleapis/gaxios/issues/253)) ([054267b](https://www.github.com/googleapis/gaxios/commit/054267bf12e1801c134e3b5cae92dcc5ea041fab)) 312 | 313 | ### [2.3.2](https://www.github.com/googleapis/gaxios/compare/v2.3.1...v2.3.2) (2020-02-28) 314 | 315 | 316 | ### Bug Fixes 317 | 318 | * update github repo in package ([#239](https://www.github.com/googleapis/gaxios/issues/239)) ([7e750cb](https://www.github.com/googleapis/gaxios/commit/7e750cbaaa59812817d725c74fb9d364c4b71096)) 319 | 320 | ### [2.3.1](https://www.github.com/googleapis/gaxios/compare/v2.3.0...v2.3.1) (2020-02-13) 321 | 322 | 323 | ### Bug Fixes 324 | 325 | * **deps:** update dependency https-proxy-agent to v5 ([#233](https://www.github.com/googleapis/gaxios/issues/233)) ([56de0a8](https://www.github.com/googleapis/gaxios/commit/56de0a824a2f9622e3e4d4bdd41adccd812a30b4)) 326 | 327 | ## [2.3.0](https://www.github.com/googleapis/gaxios/compare/v2.2.2...v2.3.0) (2020-01-31) 328 | 329 | 330 | ### Features 331 | 332 | * add promise support for onRetryAttempt and shouldRetry ([#223](https://www.github.com/googleapis/gaxios/issues/223)) ([061afa3](https://www.github.com/googleapis/gaxios/commit/061afa381a51d39823e63accf3dacd16e191f3b9)) 333 | 334 | ### [2.2.2](https://www.github.com/googleapis/gaxios/compare/v2.2.1...v2.2.2) (2020-01-08) 335 | 336 | 337 | ### Bug Fixes 338 | 339 | * **build:** add publication configuration ([#218](https://www.github.com/googleapis/gaxios/issues/218)) ([43e581f](https://www.github.com/googleapis/gaxios/commit/43e581ff4ed5e79d72f6f29748a5eebb6bff1229)) 340 | 341 | ### [2.2.1](https://www.github.com/googleapis/gaxios/compare/v2.2.0...v2.2.1) (2020-01-04) 342 | 343 | 344 | ### Bug Fixes 345 | 346 | * **deps:** update dependency https-proxy-agent to v4 ([#201](https://www.github.com/googleapis/gaxios/issues/201)) ([5cdeef2](https://www.github.com/googleapis/gaxios/commit/5cdeef288a0c5c544c0dc2659aafbb2215d06c4b)) 347 | * remove retryDelay option ([#203](https://www.github.com/googleapis/gaxios/issues/203)) ([d21e08d](https://www.github.com/googleapis/gaxios/commit/d21e08d2aada980d39bc5ca7093d54452be2d646)) 348 | 349 | ## [2.2.0](https://www.github.com/googleapis/gaxios/compare/v2.1.1...v2.2.0) (2019-12-05) 350 | 351 | 352 | ### Features 353 | 354 | * populate GaxiosResponse with raw response information (res.url) ([#189](https://www.github.com/googleapis/gaxios/issues/189)) ([53a7f54](https://www.github.com/googleapis/gaxios/commit/53a7f54cc0f20320d7a6a21a9a9f36050cec2eec)) 355 | 356 | 357 | ### Bug Fixes 358 | 359 | * don't retry a request that is aborted intentionally ([#190](https://www.github.com/googleapis/gaxios/issues/190)) ([ba9777b](https://www.github.com/googleapis/gaxios/commit/ba9777b15b5262f8288a8bb3cca49a1de8427d8e)) 360 | * **deps:** pin TypeScript below 3.7.0 ([5373f07](https://www.github.com/googleapis/gaxios/commit/5373f0793a765965a8221ecad2f99257ed1b7444)) 361 | 362 | ### [2.1.1](https://www.github.com/googleapis/gaxios/compare/v2.1.0...v2.1.1) (2019-11-15) 363 | 364 | 365 | ### Bug Fixes 366 | 367 | * **docs:** snippets are now replaced in jsdoc comments ([#183](https://www.github.com/googleapis/gaxios/issues/183)) ([8dd1324](https://www.github.com/googleapis/gaxios/commit/8dd1324256590bd2f2e9015c813950e1cd8cb330)) 368 | 369 | ## [2.1.0](https://www.github.com/googleapis/gaxios/compare/v2.0.3...v2.1.0) (2019-10-09) 370 | 371 | 372 | ### Bug Fixes 373 | 374 | * **deps:** update dependency https-proxy-agent to v3 ([#172](https://www.github.com/googleapis/gaxios/issues/172)) ([4a38f35](https://www.github.com/googleapis/gaxios/commit/4a38f35)) 375 | 376 | 377 | ### Features 378 | 379 | * **TypeScript:** agent can now be passed as builder method, rather than agent instance ([c84ddd6](https://www.github.com/googleapis/gaxios/commit/c84ddd6)) 380 | 381 | ### [2.0.3](https://www.github.com/googleapis/gaxios/compare/v2.0.2...v2.0.3) (2019-09-11) 382 | 383 | 384 | ### Bug Fixes 385 | 386 | * do not override content-type if its given ([#158](https://www.github.com/googleapis/gaxios/issues/158)) ([f49e0e6](https://www.github.com/googleapis/gaxios/commit/f49e0e6)) 387 | * improve stream detection logic ([6c41537](https://www.github.com/googleapis/gaxios/commit/6c41537)) 388 | * revert header change ([#161](https://www.github.com/googleapis/gaxios/issues/161)) ([b0f6a8b](https://www.github.com/googleapis/gaxios/commit/b0f6a8b)) 389 | 390 | ### [2.0.2](https://www.github.com/googleapis/gaxios/compare/v2.0.1...v2.0.2) (2019-07-23) 391 | 392 | 393 | ### Bug Fixes 394 | 395 | * check for existence of fetch before using it ([#138](https://www.github.com/googleapis/gaxios/issues/138)) ([79eb58d](https://www.github.com/googleapis/gaxios/commit/79eb58d)) 396 | * **docs:** make anchors work in jsdoc ([#139](https://www.github.com/googleapis/gaxios/issues/139)) ([85103bb](https://www.github.com/googleapis/gaxios/commit/85103bb)) 397 | * prevent double option processing ([#142](https://www.github.com/googleapis/gaxios/issues/142)) ([19b4b3c](https://www.github.com/googleapis/gaxios/commit/19b4b3c)) 398 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | In the interest of fostering an open and welcoming environment, we as 7 | contributors and maintainers pledge to making participation in our project and 8 | our community a harassment-free experience for everyone, regardless of age, body 9 | size, disability, ethnicity, gender identity and expression, level of 10 | experience, education, socio-economic status, nationality, personal appearance, 11 | race, religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | * The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | * Trolling, insulting/derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying the standards of acceptable 38 | behavior and are expected to take appropriate and fair corrective action in 39 | response to any instances of unacceptable behavior. 40 | 41 | Project maintainers have the right and responsibility to remove, edit, or reject 42 | comments, commits, code, wiki edits, issues, and other contributions that are 43 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 44 | contributor for other behaviors that they deem inappropriate, threatening, 45 | offensive, or harmful. 46 | 47 | ## Scope 48 | 49 | This Code of Conduct applies both within project spaces and in public spaces 50 | when an individual is representing the project or its community. Examples of 51 | representing a project or community include using an official project e-mail 52 | address, posting via an official social media account, or acting as an appointed 53 | representative at an online or offline event. Representation of a project may be 54 | further defined and clarified by project maintainers. 55 | 56 | This Code of Conduct also applies outside the project spaces when the Project 57 | Steward has a reasonable belief that an individual's behavior may have a 58 | negative impact on the project or its community. 59 | 60 | ## Conflict Resolution 61 | 62 | We do not believe that all conflict is bad; healthy debate and disagreement 63 | often yield positive results. However, it is never okay to be disrespectful or 64 | to engage in behavior that violates the project’s code of conduct. 65 | 66 | If you see someone violating the code of conduct, you are encouraged to address 67 | the behavior directly with those involved. Many issues can be resolved quickly 68 | and easily, and this gives people more control over the outcome of their 69 | dispute. If you are unable to resolve the matter for any reason, or if the 70 | behavior is threatening or harassing, report it. We are dedicated to providing 71 | an environment where participants feel welcome and safe. 72 | 73 | Reports should be directed to *googleapis-stewards@google.com*, the 74 | Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to 75 | receive and address reported violations of the code of conduct. They will then 76 | work with a committee consisting of representatives from the Open Source 77 | Programs Office and the Google Open Source Strategy team. If for any reason you 78 | are uncomfortable reaching out to the Project Steward, please email 79 | opensource@google.com. 80 | 81 | We will investigate every complaint, but you may not receive a direct response. 82 | We will use our discretion in determining when and how to follow up on reported 83 | incidents, which may range from not taking action to permanent expulsion from 84 | the project and project-sponsored spaces. We will notify the accused of the 85 | report and provide them an opportunity to discuss it before any action is taken. 86 | The identity of the reporter will be omitted from the details of the report 87 | supplied to the accused. In potentially harmful situations, such as ongoing 88 | harassment or threats to anyone's safety, we may take action without notice. 89 | 90 | ## Attribution 91 | 92 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 93 | available at 94 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | **Table of contents** 4 | 5 | * [Contributor License Agreements](#contributor-license-agreements) 6 | * [Contributing a patch](#contributing-a-patch) 7 | * [Running the tests](#running-the-tests) 8 | * [Releasing the library](#releasing-the-library) 9 | 10 | ## Contributor License Agreements 11 | 12 | We'd love to accept your sample apps and patches! Before we can take them, we 13 | have to jump a couple of legal hurdles. 14 | 15 | Please fill out either the individual or corporate Contributor License Agreement 16 | (CLA). 17 | 18 | * If you are an individual writing original source code and you're sure you 19 | own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). 20 | * If you work for a company that wants to allow you to contribute your work, 21 | then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). 22 | 23 | Follow either of the two links above to access the appropriate CLA and 24 | instructions for how to sign and return it. Once we receive it, we'll be able to 25 | accept your pull requests. 26 | 27 | ## Contributing A Patch 28 | 29 | 1. Submit an issue describing your proposed change to the repo in question. 30 | 1. The repo owner will respond to your issue promptly. 31 | 1. If your proposed change is accepted, and you haven't already done so, sign a 32 | Contributor License Agreement (see details above). 33 | 1. Fork the desired repo, develop and test your code changes. 34 | 1. Ensure that your code adheres to the existing style in the code to which 35 | you are contributing. 36 | 1. Ensure that your code has an appropriate set of tests which all pass. 37 | 1. Title your pull request following [Conventional Commits](https://www.conventionalcommits.org/) styling. 38 | 1. Submit a pull request. 39 | 40 | ### Before you begin 41 | 42 | 1. [Select or create a Cloud Platform project][projects]. 43 | 1. [Set up authentication with a service account][auth] so you can access the 44 | API from your local workstation. 45 | 46 | 47 | ## Running the tests 48 | 49 | 1. [Prepare your environment for Node.js setup][setup]. 50 | 51 | 1. Install dependencies: 52 | 53 | npm install 54 | 55 | 1. Run the tests: 56 | 57 | # Run unit tests. 58 | npm test 59 | 60 | # Run sample integration tests. 61 | npm run samples-test 62 | 63 | # Run all system tests. 64 | npm run system-test 65 | 66 | 1. Lint (and maybe fix) any changes: 67 | 68 | npm run fix 69 | 70 | [setup]: https://cloud.google.com/nodejs/docs/setup 71 | [projects]: https://console.cloud.google.com/project 72 | [billing]: https://support.google.com/cloud/answer/6293499#enable-billing 73 | 74 | [auth]: https://cloud.google.com/docs/authentication/getting-started -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gaxios 2 | 3 | [![npm version](https://img.shields.io/npm/v/gaxios.svg)](https://www.npmjs.org/package/gaxios) 4 | [![codecov](https://codecov.io/gh/googleapis/gaxios/branch/master/graph/badge.svg)](https://codecov.io/gh/googleapis/gaxios) 5 | [![Code Style: Google](https://img.shields.io/badge/code%20style-google-blueviolet.svg)](https://github.com/google/gts) 6 | 7 | > An HTTP request client that provides an `axios` like interface over top of `node-fetch`. 8 | 9 | ## Install 10 | 11 | ```sh 12 | $ npm install gaxios 13 | ``` 14 | 15 | ## Example 16 | 17 | ```js 18 | import {request} from 'gaxios'; 19 | const res = await request({url: 'https://google.com/'}); 20 | ``` 21 | 22 | ## `fetch`-Compatible API Example 23 | 24 | We offer a drop-in `fetch`-compatible API as well. 25 | 26 | ```js 27 | import {instance} from 'gaxios'; 28 | const res = await instance.fetch('https://google.com/'); 29 | ``` 30 | 31 | To disable the auto-processing of the request body in `res.data`, set `{responseType: 'stream'}` or `.adapter` on a Gaxios instance's defaults or per-request. 32 | 33 | ## Setting Defaults 34 | 35 | Gaxios supports setting default properties both on the default instance, and on additional instances. This is often useful when making many requests to the same domain with the same base settings. For example: 36 | 37 | ```js 38 | import {Gaxios} from 'gaxios'; 39 | 40 | const gaxios = new Gaxios(); 41 | 42 | gaxios.defaults = { 43 | baseURL: 'https://example.com' 44 | headers: new Headers({ 45 | Authorization: 'SOME_TOKEN' 46 | }) 47 | } 48 | 49 | await gaxios.request({url: '/data'}); 50 | ``` 51 | 52 | Note that setting default values will take precedence 53 | over other authentication methods, i.e., application default credentials. 54 | 55 | ## `GaxiosResponse` 56 | 57 | The `GaxiosResponse` object extends the `fetch` API's [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object, with the addition of: 58 | 59 | - `config`: the configuration used for the request. 60 | - `data`: the transformed `.body`, such as JSON, text, arrayBuffer, or more. 61 | 62 | ## Request Options 63 | 64 | ```ts 65 | interface GaxiosOptions = { 66 | // The url to which the request should be sent. Required. 67 | url: string | URL, 68 | 69 | // The HTTP method to use for the request. Defaults to `GET`. 70 | method: 'GET', 71 | 72 | // The base Url to use for the request. 73 | // Resolved as `new URL(url, baseURL)` 74 | baseURL: 'https://example.com/v1/' | URL; 75 | 76 | // The HTTP methods to be sent with the request. 77 | headers: new Headers(), 78 | 79 | // The data to send in the body of the request. Objects will be serialized as JSON 80 | // except for: 81 | // - `ArrayBuffer` 82 | // - `Blob` 83 | // - `Buffer` (Node.js) 84 | // - `DataView` 85 | // - `File` 86 | // - `FormData` 87 | // - `ReadableStream` 88 | // - `stream.Readable` (Node.js) 89 | // - strings 90 | // - `TypedArray` (e.g. `Uint8Array`, `BigInt64Array`) 91 | // - `URLSearchParams` 92 | // - all other objects where: 93 | // - headers.get('Content-Type') === 'application/x-www-form-urlencoded' (as they will be serialized as `URLSearchParams`) 94 | // 95 | // Here are a few examples that would prevent setting `Content-Type: application/json` by default: 96 | // - data: JSON.stringify({some: 'data'}) // a `string` 97 | // - data: fs.readFile('./some-data.jpeg') // a `stream.Readable` 98 | data: { 99 | some: 'data' 100 | }, 101 | 102 | // The max size of the http response content in bytes allowed. 103 | // Defaults to `0`, which is the same as unset. 104 | maxContentLength: 2000, 105 | 106 | // The query parameters that will be encoded using `URLSearchParams` and 107 | // appended to the url 108 | params: { 109 | querystring: 'parameters' 110 | }, 111 | 112 | // The timeout for the HTTP request in milliseconds. No timeout by default. 113 | timeout: 60000, 114 | 115 | // Optional method to override making the actual HTTP request. Useful 116 | // for writing tests and instrumentation 117 | adapter?: async (options, defaultAdapter) => { 118 | const res = await defaultAdapter(options); 119 | res.data = { 120 | ...res.data, 121 | extraProperty: 'your extra property', 122 | }; 123 | return res; 124 | }; 125 | 126 | // The expected return type of the request. Options are: 127 | // 'json' | 'stream' | 'blob' | 'arraybuffer' | 'text' | 'unknown' 128 | // Defaults to `unknown`. 129 | // 130 | // If the `fetchImplementation` is native `fetch`, the 131 | // stream is a `ReadableStream`, otherwise `readable.Stream`. 132 | // 133 | // Note: Setting 'stream' does not consume the `Response#body` - this can 134 | // be useful for passthrough requests, where a consumer would like to 135 | // transform the `Response#body`, or for using Gaxios as a drop-in `fetch` 136 | // replacement. 137 | responseType: 'unknown', 138 | 139 | // The node.js http agent to use for the request. 140 | agent: someHttpsAgent, 141 | 142 | // Custom function to determine if the response is valid based on the 143 | // status code. Defaults to (>= 200 && < 300) 144 | validateStatus: (status: number) => true, 145 | 146 | /** 147 | * Implementation of `fetch` to use when making the API call. Will use 148 | * `node-fetch` by default. 149 | */ 150 | fetchImplementation?: typeof fetch; 151 | 152 | // Configuration for retrying of requests. 153 | retryConfig: { 154 | // The number of times to retry the request. Defaults to 3. 155 | retry?: number; 156 | 157 | // The number of retries already attempted. 158 | currentRetryAttempt?: number; 159 | 160 | // The HTTP Methods that will be automatically retried. 161 | // Defaults to ['GET','PUT','HEAD','OPTIONS','DELETE'] 162 | httpMethodsToRetry?: string[]; 163 | 164 | // The HTTP response status codes that will automatically be retried. 165 | // Defaults to: [[100, 199], [408, 408], [429, 429], [500, 599]] 166 | statusCodesToRetry?: number[][]; 167 | 168 | // Function to invoke when a retry attempt is made. 169 | onRetryAttempt?: (err: GaxiosError) => Promise | void; 170 | 171 | // Function to invoke which determines if you should retry 172 | shouldRetry?: (err: GaxiosError) => Promise | boolean; 173 | 174 | // When there is no response, the number of retries to attempt. Defaults to 2. 175 | noResponseRetries?: number; 176 | 177 | // The amount of time to initially delay the retry, in ms. Defaults to 100ms. 178 | retryDelay?: number; 179 | }, 180 | 181 | // Enables default configuration for retries. 182 | retry: boolean, 183 | 184 | // Enables aborting via AbortController 185 | signal?: AbortSignal 186 | 187 | /** 188 | * A collection of parts to send as a `Content-Type: multipart/related` request. 189 | */ 190 | multipart?: GaxiosMultipartOptions; 191 | 192 | /** 193 | * An optional proxy to use for requests. 194 | * Available via `process.env.HTTP_PROXY` and `process.env.HTTPS_PROXY` as well - with a preference for the this config option when multiple are available. 195 | * The `agent` option overrides this. 196 | * 197 | * @see {@link GaxiosOptions.noProxy} 198 | * @see {@link GaxiosOptions.agent} 199 | */ 200 | proxy?: string | URL; 201 | 202 | /** 203 | * A list for excluding traffic for proxies. 204 | * Available via `process.env.NO_PROXY` as well as a common-separated list of strings - merged with any local `noProxy` rules. 205 | * 206 | * - When provided a string, it is matched by 207 | * - Wildcard `*.` and `.` matching are available. (e.g. `.example.com` or `*.example.com`) 208 | * - When provided a URL, it is matched by the `.origin` property. 209 | * - For example, requesting `https://example.com` with the following `noProxy`s would result in a no proxy use: 210 | * - new URL('https://example.com') 211 | * - new URL('https://example.com:443') 212 | * - The following would be used with a proxy: 213 | * - new URL('http://example.com:80') 214 | * - new URL('https://example.com:8443') 215 | * - When provided a regular expression it is used to match the stringified URL 216 | * 217 | * @see {@link GaxiosOptions.proxy} 218 | */ 219 | noProxy?: (string | URL | RegExp)[]; 220 | 221 | /** 222 | * An experimental, customizable error redactor. 223 | * 224 | * Set `false` to disable. 225 | * 226 | * @remarks 227 | * 228 | * This does not replace the requirement for an active Data Loss Prevention (DLP) provider. For DLP suggestions, see: 229 | * - https://cloud.google.com/sensitive-data-protection/docs/redacting-sensitive-data#dlp_deidentify_replace_infotype-nodejs 230 | * - https://cloud.google.com/sensitive-data-protection/docs/infotypes-reference#credentials_and_secrets 231 | * 232 | * @experimental 233 | */ 234 | errorRedactor?: typeof defaultErrorRedactor | false; 235 | } 236 | ``` 237 | 238 | ## License 239 | 240 | [Apache-2.0](https://github.com/googleapis/gaxios/blob/main/LICENSE) 241 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | To report a security issue, please use [g.co/vulnz](https://g.co/vulnz). 4 | 5 | The Google Security Team will respond within 5 working days of your report on g.co/vulnz. 6 | 7 | We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue. 8 | -------------------------------------------------------------------------------- /browser-test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /browser-test/browser-test-runner.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google, LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import express from 'express'; 15 | import http from 'http'; 16 | import {execFile} from 'child_process'; 17 | import * as multiparty from 'multiparty'; 18 | import cors from 'cors'; 19 | import {promisify} from 'util'; 20 | 21 | const port = 7172; 22 | 23 | const exec = promisify(execFile); 24 | 25 | async function listen( 26 | app: express.Express, 27 | port: number, 28 | ): Promise { 29 | return new Promise((resolve, reject) => { 30 | const server = app.listen(port, (err?: Error) => { 31 | if (err) { 32 | reject(err); 33 | } 34 | resolve(server); 35 | }); 36 | }); 37 | } 38 | 39 | // Starts a web server that browser tests will use, then runs actual browser 40 | // tests. 41 | async function main() { 42 | const app = express(); 43 | 44 | app.use(cors()); 45 | 46 | app.post('/path', (req: express.Request, res: express.Response) => { 47 | if (req.header('origin')) { 48 | res.set('Access-Control-Allow-Origin', req.header('origin')); 49 | } 50 | const form = new multiparty.Form({autoFields: true}); 51 | form.parse(req, (err, fields) => { 52 | if (err) { 53 | res.status(500).send({message: err.message}); 54 | } else { 55 | res.status(200).send(fields.null); 56 | } 57 | }); 58 | }); 59 | app.get('/path', (req: express.Request, res: express.Response) => { 60 | if (req.header('origin')) { 61 | res.set('Access-Control-Allow-Origin', req.header('origin')); 62 | } 63 | res.send('response'); 64 | }); 65 | app.get('/querystring', (req: express.Request, res: express.Response) => { 66 | if (req.header('origin')) { 67 | res.set('Access-Control-Allow-Origin', req.header('origin')); 68 | } 69 | const query = req.query.query; 70 | res.send(query || ''); 71 | }); 72 | 73 | const server = await listen(app, port); 74 | console.log(`[http server] I'm listening on port ${port}! Starting karma.`); 75 | await exec('karma', ['start']); 76 | server.close(); 77 | console.log( 78 | `[http server] Karma has finished! I'm no longer listening on port ${port}!`, 79 | ); 80 | } 81 | 82 | main().catch(err => { 83 | console.log('Error:', err); 84 | }); 85 | -------------------------------------------------------------------------------- /browser-test/test.browser.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google, LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import assert from 'assert'; 15 | import {describe, it} from 'mocha'; 16 | import {request} from '../src/index'; 17 | const port = 7172; // should match the port defined in `webserver.ts` 18 | 19 | describe('💻 browser tests', () => { 20 | it('should just work from browser', async () => { 21 | const result = await request({url: `http://localhost:${port}/path`}); 22 | assert.strictEqual(result.status, 200); 23 | assert.strictEqual(result.data, 'response'); 24 | }); 25 | 26 | it('should pass querystring parameters from browser', async () => { 27 | const result = await request({ 28 | url: `http://localhost:${port}/querystring`, 29 | params: {query: 'value'}, 30 | }); 31 | assert.strictEqual(result.status, 200); 32 | assert.strictEqual(result.data, 'value'); 33 | }); 34 | 35 | it('should allow passing browser fetch explicitly', async () => { 36 | const result = await request({ 37 | url: `http://localhost:${port}/path`, 38 | fetchImplementation: window.fetch, 39 | }); 40 | assert.strictEqual(result.status, 200); 41 | }); 42 | 43 | it('should support multipart post from the browser', async () => { 44 | const headers = new Headers(); 45 | const multipart = [ 46 | { 47 | 'Content-Type': 'application/json', 48 | body: JSON.stringify({hello: 'world'}), 49 | }, 50 | { 51 | 'Content-Type': 'text/plain', 52 | body: 'hello world!', 53 | }, 54 | ]; 55 | const boundary = 56 | globalThis?.crypto.randomUUID() || (await import('crypto')).randomUUID(); 57 | const finale = `--${boundary}--`; 58 | headers.set('Content-Type', `multipart/related; boundary=${boundary}`); 59 | 60 | let content = ''; 61 | for (const part of multipart) { 62 | const preamble = `--${boundary}\r\nContent-Type: ${part['Content-Type']}\r\n\r\n`; 63 | // rStream.push(preamble); 64 | content += preamble; 65 | if (typeof part.body === 'string') { 66 | content += part.body; 67 | content += '\r\n'; 68 | } 69 | } 70 | content += finale; 71 | const result = await request({ 72 | headers, 73 | data: content, 74 | method: 'POST', 75 | url: `http://localhost:${port}/path`, 76 | }); 77 | assert.strictEqual(result.status, 200); 78 | const parts = result.data as string[]; 79 | assert.strictEqual(parts[0], '{"hello":"world"}'); 80 | assert.strictEqual(parts[1], 'hello world!'); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Karma configuration 16 | // Use `npm run browser-test` to run browser tests with Karma. 17 | const fs = require('fs'); 18 | const isDocker = require('is-docker')(); 19 | 20 | const webpackConfig = require('./webpack-tests.config.js'); 21 | process.env.CHROME_BIN = fs.existsSync('/usr/bin/chromium-browser') 22 | ? '/usr/bin/chromium-browser' 23 | : require('puppeteer').executablePath(); 24 | 25 | module.exports = function (config) { 26 | config.set({ 27 | // base path that will be used to resolve all patterns (eg. files, exclude) 28 | basePath: '', 29 | 30 | // frameworks to use 31 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 32 | frameworks: ['mocha', 'webpack'], 33 | 34 | // list of files / patterns to load in the browser 35 | files: ['./browser-test/test.*.ts'], 36 | 37 | // list of files / patterns to exclude 38 | exclude: [], 39 | 40 | // preprocess matching files before serving them to the browser 41 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 42 | preprocessors: { 43 | './src/*.ts': ['coverage'], 44 | './src/**/*.ts': ['coverage'], 45 | './browser-test/*.ts': ['webpack', 'sourcemap'], 46 | }, 47 | 48 | webpack: webpackConfig, 49 | 50 | // test results reporter to use 51 | // possible values: 'dots', 'progress' 52 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 53 | reporters: ['progress', 'coverage', 'remap-coverage'], 54 | 55 | coverageReporter: {type: 'in-memory'}, 56 | remapCoverageReporter: {html: './coverage'}, 57 | 58 | // web server port 59 | port: 9876, 60 | 61 | // enable / disable colors in the output (reporters and logs) 62 | colors: true, 63 | 64 | // level of logging 65 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 66 | logLevel: config.LOG_INFO, 67 | 68 | // enable / disable watching file and executing tests whenever any file changes 69 | autoWatch: false, 70 | 71 | // start these browsers 72 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 73 | browsers: ['ChromeCustom'], 74 | customLaunchers: { 75 | ChromeCustom: { 76 | base: 'ChromeHeadless', 77 | // We must disable the Chrome sandbox when running Chrome inside Docker (Chrome's sandbox needs 78 | // more permissions than Docker allows by default) 79 | flags: isDocker ? ['--no-sandbox'] : [], 80 | }, 81 | }, 82 | 83 | // Continuous Integration mode 84 | // if true, Karma captures browsers, runs the tests and exits 85 | singleRun: true, 86 | 87 | // Concurrency level 88 | // how many browser should be started simultaneous 89 | concurrency: Infinity, 90 | 91 | // set correct MIME type when serving .ts files (already compiled to JavaScript): 92 | mime: { 93 | 'text/javascript': ['ts'], 94 | }, 95 | }); 96 | }; 97 | -------------------------------------------------------------------------------- /linkinator.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "recurse": true, 3 | "skip": [ 4 | "https://codecov.io/gh/googleapis/", 5 | "www.googleapis.com", 6 | "img.shields.io" 7 | ], 8 | "silent": true, 9 | "concurrency": 10 10 | } 11 | -------------------------------------------------------------------------------- /owlbot.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import synthtool.languages.node as node 16 | 17 | node.owlbot_main(templates_excludes=["README.md", "samples/README.md"]) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gaxios", 3 | "version": "6.7.1", 4 | "description": "A simple common HTTP client specifically for Google APIs and services.", 5 | "main": "build/cjs/src/index.js", 6 | "types": "build/cjs/src/index.d.ts", 7 | "files": [ 8 | "build/" 9 | ], 10 | "exports": { 11 | ".": { 12 | "import": { 13 | "types": "./build/esm/src/index.d.ts", 14 | "default": "./build/esm/src/index.js" 15 | }, 16 | "require": { 17 | "types": "./build/cjs/src/index.d.ts", 18 | "default": "./build/cjs/src/index.js" 19 | } 20 | } 21 | }, 22 | "scripts": { 23 | "lint": "gts check --no-inline-config", 24 | "test": "c8 mocha build/esm/test", 25 | "presystem-test": "npm run compile", 26 | "system-test": "mocha build/esm/system-test --timeout 80000", 27 | "compile": "tsc -b ./tsconfig.json ./tsconfig.cjs.json && node utils/enable-esm.mjs", 28 | "fix": "gts fix", 29 | "prepare": "npm run compile", 30 | "pretest": "npm run compile", 31 | "webpack": "webpack", 32 | "prebrowser-test": "npm run compile", 33 | "browser-test": "node build/browser-test/browser-test-runner.js", 34 | "docs": "jsdoc -c .jsdoc.js", 35 | "docs-test": "linkinator docs", 36 | "predocs-test": "npm run docs", 37 | "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", 38 | "prelint": "cd samples; npm link ../; npm install", 39 | "clean": "gts clean" 40 | }, 41 | "repository": "googleapis/gaxios", 42 | "keywords": [ 43 | "google" 44 | ], 45 | "engines": { 46 | "node": ">=18" 47 | }, 48 | "author": "Google, LLC", 49 | "license": "Apache-2.0", 50 | "devDependencies": { 51 | "@babel/plugin-proposal-private-methods": "^7.18.6", 52 | "@types/cors": "^2.8.6", 53 | "@types/express": "^5.0.0", 54 | "@types/extend": "^3.0.1", 55 | "@types/mocha": "^10.0.10", 56 | "@types/multiparty": "4.2.1", 57 | "@types/mv": "^2.1.0", 58 | "@types/ncp": "^2.0.1", 59 | "@types/node": "^22.0.0", 60 | "@types/sinon": "^17.0.0", 61 | "@types/tmp": "0.2.6", 62 | "assert": "^2.0.0", 63 | "browserify": "^17.0.0", 64 | "c8": "^10.0.0", 65 | "cors": "^2.8.5", 66 | "express": "^5.0.0", 67 | "gts": "^6.0.0", 68 | "is-docker": "^3.0.0", 69 | "jsdoc": "^4.0.0", 70 | "jsdoc-fresh": "^4.0.0", 71 | "jsdoc-region-tag": "^3.0.0", 72 | "karma": "^6.0.0", 73 | "karma-chrome-launcher": "^3.0.0", 74 | "karma-coverage": "^2.0.0", 75 | "karma-firefox-launcher": "^2.0.0", 76 | "karma-mocha": "^2.0.0", 77 | "karma-remap-coverage": "^0.1.5", 78 | "karma-sourcemap-loader": "^0.4.0", 79 | "karma-webpack": "^5.0.1", 80 | "linkinator": "^6.1.2", 81 | "mocha": "^11.1.0", 82 | "multiparty": "^4.2.1", 83 | "mv": "^2.1.1", 84 | "ncp": "^2.0.0", 85 | "nock": "^14.0.0-beta.13", 86 | "null-loader": "^4.0.0", 87 | "pack-n-play": "^3.0.0", 88 | "puppeteer": "^24.0.0", 89 | "sinon": "^20.0.0", 90 | "stream-browserify": "^3.0.0", 91 | "tmp": "0.2.3", 92 | "ts-loader": "^9.5.2", 93 | "typescript": "^5.1.6", 94 | "webpack": "^5.35.0", 95 | "webpack-cli": "^6.0.1" 96 | }, 97 | "dependencies": { 98 | "extend": "^3.0.2", 99 | "https-proxy-agent": "^7.0.1", 100 | "node-fetch": "^3.3.2" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | "docker:disable", 5 | ":disableDependencyDashboard" 6 | ], 7 | "constraintsFiltering": "strict", 8 | "pinVersions": false, 9 | "rebaseStalePrs": true, 10 | "schedule": [ 11 | "after 9am and before 3pm" 12 | ], 13 | "gitAuthor": null, 14 | "packageRules": [ 15 | { 16 | "extends": "packages:linters", 17 | "groupName": "linters" 18 | } 19 | ], 20 | "ignoreDeps": ["typescript"] 21 | } 22 | -------------------------------------------------------------------------------- /samples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gaxios-samples", 3 | "private": true, 4 | "files": [ 5 | "*.js" 6 | ], 7 | "scripts": { 8 | "test": "mocha" 9 | }, 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "gaxios": "^6.7.1" 13 | }, 14 | "devDependencies": { 15 | "mocha": "^8.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/quickstart.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // [START gaxios_quickstart] 16 | const {request} = require('gaxios'); 17 | 18 | /** 19 | * Perform a simple `GET` request to a JSON API. 20 | */ 21 | async function quickstart() { 22 | const url = 'https://www.googleapis.com/discovery/v1/apis/'; 23 | const res = await request({url}); 24 | console.log(`status: ${res.status}`); 25 | console.log('data:'); 26 | console.log(res.data); 27 | } 28 | quickstart(); 29 | // [END gaxios_quickstart] 30 | -------------------------------------------------------------------------------- /samples/test/test.samples.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | const {execSync} = require('child_process'); 16 | const assert = require('assert'); 17 | const {describe, it} = require('mocha'); 18 | 19 | const exec = cmd => execSync(cmd, {encoding: 'utf8'}); 20 | 21 | describe(__filename, () => { 22 | it('should run the quickstart', () => { 23 | const output = exec('node quickstart'); 24 | assert.ok(output.includes('status: 200')); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import {GaxiosOptions} from './common.js'; 15 | import {Gaxios} from './gaxios.js'; 16 | 17 | export { 18 | GaxiosError, 19 | GaxiosPromise, 20 | GaxiosResponse, 21 | GaxiosOptionsPrepared, 22 | RetryConfig, 23 | } from './common.js'; 24 | export {Gaxios, GaxiosOptions}; 25 | export * from './interceptor.js'; 26 | 27 | /** 28 | * The default instance used when the `request` method is directly 29 | * invoked. 30 | */ 31 | export const instance = new Gaxios(); 32 | 33 | /** 34 | * Make an HTTP request using the given options. 35 | * @param opts Options for the request 36 | */ 37 | export async function request(opts: GaxiosOptions) { 38 | return instance.request(opts); 39 | } 40 | -------------------------------------------------------------------------------- /src/interceptor.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import {GaxiosError, GaxiosOptionsPrepared, GaxiosResponse} from './common.js'; 15 | 16 | /** 17 | * Interceptors that can be run for requests or responses. These interceptors run asynchronously. 18 | */ 19 | export interface GaxiosInterceptor< 20 | T extends GaxiosOptionsPrepared | GaxiosResponse, 21 | > { 22 | /** 23 | * Function to be run when applying an interceptor. 24 | * 25 | * @param {T} configOrResponse The current configuration or response. 26 | * @returns {Promise} Promise that resolves to the modified set of options or response. 27 | */ 28 | resolved?: (configOrResponse: T) => Promise; 29 | /** 30 | * Function to be run if the previous call to resolved throws / rejects or the request results in an invalid status 31 | * as determined by the call to validateStatus. 32 | * 33 | * @param {GaxiosError} err The error thrown from the previously called resolved function. 34 | */ 35 | rejected?: (err: GaxiosError) => void; 36 | } 37 | 38 | /** 39 | * Class to manage collections of GaxiosInterceptors for both requests and responses. 40 | */ 41 | export class GaxiosInterceptorManager< 42 | T extends GaxiosOptionsPrepared | GaxiosResponse, 43 | > extends Set | null> {} 44 | -------------------------------------------------------------------------------- /src/retry.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import {GaxiosError, RetryConfig} from './common.js'; 15 | 16 | export async function getRetryConfig(err: GaxiosError) { 17 | let config = getConfig(err); 18 | if (!err || !err.config || (!config && !err.config.retry)) { 19 | return {shouldRetry: false}; 20 | } 21 | config = config || {}; 22 | config.currentRetryAttempt = config.currentRetryAttempt || 0; 23 | config.retry = 24 | config.retry === undefined || config.retry === null ? 3 : config.retry; 25 | config.httpMethodsToRetry = config.httpMethodsToRetry || [ 26 | 'GET', 27 | 'HEAD', 28 | 'PUT', 29 | 'OPTIONS', 30 | 'DELETE', 31 | ]; 32 | config.noResponseRetries = 33 | config.noResponseRetries === undefined || config.noResponseRetries === null 34 | ? 2 35 | : config.noResponseRetries; 36 | config.retryDelayMultiplier = config.retryDelayMultiplier 37 | ? config.retryDelayMultiplier 38 | : 2; 39 | config.timeOfFirstRequest = config.timeOfFirstRequest 40 | ? config.timeOfFirstRequest 41 | : Date.now(); 42 | config.totalTimeout = config.totalTimeout 43 | ? config.totalTimeout 44 | : Number.MAX_SAFE_INTEGER; 45 | config.maxRetryDelay = config.maxRetryDelay 46 | ? config.maxRetryDelay 47 | : Number.MAX_SAFE_INTEGER; 48 | 49 | // If this wasn't in the list of status codes where we want 50 | // to automatically retry, return. 51 | const retryRanges = [ 52 | // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes 53 | // 1xx - Retry (Informational, request still processing) 54 | // 2xx - Do not retry (Success) 55 | // 3xx - Do not retry (Redirect) 56 | // 4xx - Do not retry (Client errors) 57 | // 408 - Retry ("Request Timeout") 58 | // 429 - Retry ("Too Many Requests") 59 | // 5xx - Retry (Server errors) 60 | [100, 199], 61 | [408, 408], 62 | [429, 429], 63 | [500, 599], 64 | ]; 65 | config.statusCodesToRetry = config.statusCodesToRetry || retryRanges; 66 | 67 | // Put the config back into the err 68 | err.config.retryConfig = config; 69 | 70 | // Determine if we should retry the request 71 | const shouldRetryFn = config.shouldRetry || shouldRetryRequest; 72 | if (!(await shouldRetryFn(err))) { 73 | return {shouldRetry: false, config: err.config}; 74 | } 75 | 76 | const delay = getNextRetryDelay(config); 77 | 78 | // We're going to retry! Increment the counter. 79 | err.config.retryConfig!.currentRetryAttempt! += 1; 80 | 81 | // Create a promise that invokes the retry after the backOffDelay 82 | const backoff = config.retryBackoff 83 | ? config.retryBackoff(err, delay) 84 | : new Promise(resolve => { 85 | setTimeout(resolve, delay); 86 | }); 87 | 88 | // Notify the user if they added an `onRetryAttempt` handler 89 | if (config.onRetryAttempt) { 90 | await config.onRetryAttempt(err); 91 | } 92 | 93 | // Return the promise in which recalls Gaxios to retry the request 94 | await backoff; 95 | return {shouldRetry: true, config: err.config}; 96 | } 97 | 98 | /** 99 | * Determine based on config if we should retry the request. 100 | * @param err The GaxiosError passed to the interceptor. 101 | */ 102 | function shouldRetryRequest(err: GaxiosError) { 103 | const config = getConfig(err); 104 | 105 | if ( 106 | (err.config.signal?.aborted && err.code !== 'TimeoutError') || 107 | err.code === 'AbortError' 108 | ) { 109 | return false; 110 | } 111 | 112 | // If there's no config, or retries are disabled, return. 113 | if (!config || config.retry === 0) { 114 | return false; 115 | } 116 | 117 | // Check if this error has no response (ETIMEDOUT, ENOTFOUND, etc) 118 | if ( 119 | !err.response && 120 | (config.currentRetryAttempt || 0) >= config.noResponseRetries! 121 | ) { 122 | return false; 123 | } 124 | 125 | // Only retry with configured HttpMethods. 126 | if ( 127 | !config.httpMethodsToRetry || 128 | !config.httpMethodsToRetry.includes( 129 | err.config.method?.toUpperCase() || 'GET', 130 | ) 131 | ) { 132 | return false; 133 | } 134 | 135 | // If this wasn't in the list of status codes where we want 136 | // to automatically retry, return. 137 | if (err.response && err.response.status) { 138 | let isInRange = false; 139 | for (const [min, max] of config.statusCodesToRetry!) { 140 | const status = err.response.status; 141 | if (status >= min && status <= max) { 142 | isInRange = true; 143 | break; 144 | } 145 | } 146 | if (!isInRange) { 147 | return false; 148 | } 149 | } 150 | 151 | // If we are out of retry attempts, return 152 | config.currentRetryAttempt = config.currentRetryAttempt || 0; 153 | if (config.currentRetryAttempt >= config.retry!) { 154 | return false; 155 | } 156 | 157 | return true; 158 | } 159 | 160 | /** 161 | * Acquire the raxConfig object from an GaxiosError if available. 162 | * @param err The Gaxios error with a config object. 163 | */ 164 | function getConfig(err: GaxiosError) { 165 | if (err && err.config && err.config.retryConfig) { 166 | return err.config.retryConfig; 167 | } 168 | return; 169 | } 170 | 171 | /** 172 | * Gets the delay to wait before the next retry. 173 | * 174 | * @param {RetryConfig} config The current set of retry options 175 | * @returns {number} the amount of ms to wait before the next retry attempt. 176 | */ 177 | function getNextRetryDelay(config: RetryConfig) { 178 | // Calculate time to wait with exponential backoff. 179 | // If this is the first retry, look for a configured retryDelay. 180 | const retryDelay = config.currentRetryAttempt 181 | ? 0 182 | : (config.retryDelay ?? 100); 183 | // Formula: retryDelay + ((retryDelayMultiplier^currentRetryAttempt - 1 / 2) * 1000) 184 | const calculatedDelay = 185 | retryDelay + 186 | ((Math.pow(config.retryDelayMultiplier!, config.currentRetryAttempt!) - 1) / 187 | 2) * 188 | 1000; 189 | const maxAllowableDelay = 190 | config.totalTimeout! - (Date.now() - config.timeOfFirstRequest!); 191 | 192 | return Math.min(calculatedDelay, maxAllowableDelay, config.maxRetryDelay!); 193 | } 194 | -------------------------------------------------------------------------------- /src/util.cts: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | const pkg: { 15 | name: string; 16 | version: string; 17 | } = require('../../../package.json'); 18 | 19 | export = {pkg}; 20 | -------------------------------------------------------------------------------- /system-test/fixtures/sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gaxios-sample-fixture", 3 | "description": "An app we're using to test the library.", 4 | "type": "commonjs", 5 | "scripts": { 6 | "check": "gts check", 7 | "clean": "gts clean", 8 | "compile": "tsc -p .", 9 | "fix": "gts fix", 10 | "prepare": "npm run compile", 11 | "pretest": "npm run compile", 12 | "posttest": "npm run check", 13 | "start": "node build/src/index.js" 14 | }, 15 | "license": "Apache-2.0", 16 | "dependencies": { 17 | "gaxios": "file:./gaxios.tgz" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^22.0.0", 21 | "browserify": "^17.0.0", 22 | "gts": "^6.0.0", 23 | "null-loader": "^4.0.0", 24 | "ts-loader": "^8.0.0", 25 | "typescript": "^5.0.0", 26 | "webpack": "^5.35.0", 27 | "webpack-cli": "^4.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /system-test/fixtures/sample/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import {request} from 'gaxios'; 16 | async function main() { 17 | await request({ 18 | url: 'https://www.googleapis.com/discovery/v1/apis/', 19 | }); 20 | } 21 | main(); 22 | -------------------------------------------------------------------------------- /system-test/fixtures/sample/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts/tsconfig-google.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build" 6 | }, 7 | "include": [ 8 | "src/*.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /system-test/fixtures/sample/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Use `npm run webpack` to produce Webpack bundle for this library. 16 | 17 | const path = require('path'); 18 | const webpack = require('webpack'); 19 | 20 | module.exports = { 21 | entry: './src/index.ts', 22 | resolve: { 23 | extensions: ['.ts', '.js', '.json'], 24 | alias: { 25 | '../../package.json': path.resolve(__dirname, 'package.json'), 26 | }, 27 | fallback: { 28 | buffer: 'browserify', 29 | child_process: false, 30 | crypto: false, 31 | fs: false, 32 | http: false, 33 | http2: false, 34 | https: false, 35 | net: false, 36 | os: false, 37 | path: false, 38 | process: false, 39 | stream: 'stream-browserify', 40 | 'stream/web': false, 41 | url: false, 42 | util: false, 43 | worker_threads: false, 44 | zlib: false, 45 | }, 46 | }, 47 | output: { 48 | filename: 'bundle.min.js', 49 | path: path.resolve(__dirname, 'dist'), 50 | }, 51 | module: { 52 | rules: [ 53 | { 54 | test: /node_modules\/https-proxy-agent\//, 55 | use: 'null-loader', 56 | }, 57 | { 58 | test: /\.ts$/, 59 | use: 'ts-loader', 60 | exclude: /node_modules/, 61 | }, 62 | ], 63 | }, 64 | mode: 'production', 65 | plugins: [ 66 | // webpack 5 doesn't know what to do with `node:` imports 67 | new webpack.NormalModuleReplacementPlugin(/node:/, resource => { 68 | resource.request = resource.request.replace(/^node:/, ''); 69 | }), 70 | ], 71 | }; 72 | -------------------------------------------------------------------------------- /system-test/test.install.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import assert from 'assert'; 16 | import {execFile} from 'child_process'; 17 | import fs from 'fs'; 18 | import mv from 'mv'; 19 | import ncp from 'ncp'; 20 | import path from 'path'; 21 | import tmp from 'tmp'; 22 | import {promisify} from 'util'; 23 | import {describe, it, before, after} from 'mocha'; 24 | import {packNTest} from 'pack-n-play'; 25 | 26 | import {createServer, Server} from 'node:http'; 27 | 28 | import util from '../src/util.cjs'; 29 | 30 | /** 31 | * Optionally keep the staging directory between tests. 32 | */ 33 | const KEEP_STAGING_DIRECTORY = false; 34 | 35 | const mvp = promisify(mv) as {} as (...args: string[]) => Promise; 36 | const ncpp = promisify(ncp); 37 | 38 | const pkg = util.pkg; 39 | 40 | const exec = promisify(execFile); 41 | 42 | describe('📦 pack and install', () => { 43 | let stagingDir: tmp.DirResult; 44 | let stagingPath: string; 45 | 46 | before(() => { 47 | stagingDir = tmp.dirSync({ 48 | keep: KEEP_STAGING_DIRECTORY, 49 | unsafeCleanup: true, 50 | }); 51 | stagingPath = stagingDir.name; 52 | }); 53 | 54 | after('cleanup staging', () => { 55 | if (!KEEP_STAGING_DIRECTORY) { 56 | stagingDir.removeCallback(); 57 | } 58 | }); 59 | 60 | describe('pack-n-play', () => { 61 | let server: Server; 62 | let url: string; 63 | 64 | before(async () => { 65 | server = createServer((req, res) => { 66 | res.writeHead(200, {'content-type': 'text/plain'}); 67 | res.end(`Hello, ${req.headers['user-agent'] || 'World'}`); 68 | }); 69 | 70 | await new Promise((resolve, reject) => { 71 | server.on('error', reject); 72 | server.listen(0, resolve); 73 | }); 74 | 75 | const address = server.address()!; 76 | 77 | if (typeof address === 'string') { 78 | url = address; 79 | } else { 80 | const base = new URL('http://localhost'); 81 | base.host = address.address; 82 | base.port = address.port.toString(); 83 | 84 | url = base.toString(); 85 | } 86 | }); 87 | 88 | after(() => { 89 | server.close(); 90 | }); 91 | 92 | it('supports ESM', async () => { 93 | await packNTest({ 94 | sample: { 95 | description: 'import as ESM', 96 | esm: ` 97 | import {Gaxios} from 'gaxios'; 98 | 99 | const gaxios = new Gaxios(); 100 | await gaxios.request({url: '${url}'}); 101 | `, 102 | }, 103 | }); 104 | }); 105 | 106 | it('supports CJS', async () => { 107 | await packNTest({ 108 | sample: { 109 | description: 'require as CJS', 110 | cjs: ` 111 | const {Gaxios} = require('gaxios'); 112 | 113 | const gaxios = new Gaxios(); 114 | gaxios.request({url: '${url}'}).then(console.log); 115 | `, 116 | }, 117 | }); 118 | }); 119 | }); 120 | 121 | describe('webpack', () => { 122 | /** 123 | * Create a staging directory with temp fixtures used to test on a fresh 124 | * application. 125 | */ 126 | before('pack and install', async () => { 127 | await exec('npm', ['pack']); 128 | const tarball = `${pkg.name}-${pkg.version}.tgz`; 129 | await mvp(tarball, `${stagingPath}/gaxios.tgz`); 130 | await ncpp('system-test/fixtures/sample', `${stagingPath}/`); 131 | await exec('npm', ['install'], {cwd: `${stagingPath}/`}); 132 | }); 133 | 134 | it('should be able to webpack the library', async () => { 135 | // we expect npm install is executed in the before hook 136 | await exec('npx', ['webpack'], {cwd: `${stagingPath}/`}); 137 | const bundle = path.join(stagingPath, 'dist', 'bundle.min.js'); 138 | const stat = fs.statSync(bundle); 139 | assert(stat.size < 256 * 1024); 140 | }).timeout(20000); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /test/fixtures/fake.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIBOwIBAAJBAJpix6pMx9uUmQBGf63935alU1VePDCoxESBrq71pVP2khHpjwmh 3 | ZT38iPc0ffJHQTkpg08qOGGTGKRe5pD+G60CAwEAAQJAZog1/MdTcGSHHKS1hVN3 4 | G/CeoLKeHN7owmqyHeZsr46pIak0KgFEdH2XXnnCRyQKZ3w2tmJk7ptkiwnDiTyO 5 | LQIhAMsbxjXfNACSUpwctljlxzETJGTnrLvlpMSLkHNIpuZ3AiEAwpbpaK+uGElh 6 | c7bNTsMkYFSQX0gR04DmKfI+jr5/Q/sCIQCCbWqCS7wUJeKAt3ttaNjaLcWwOonU 7 | +2osgMcZEO2EjwIgDWfeLrMjuFKd3x3x6QIyqFxLEfViHQMdWDhzLNxc4i8CIQDD 8 | tP3fD30Lo/fE+dVxWvsmfhHH2xnQICHN9CaN7GtEWQ== 9 | -----END RSA PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /test/fixtures/fake.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJpix6pMx9uUmQBGf63935alU1VePDCo 3 | xESBrq71pVP2khHpjwmhZT38iPc0ffJHQTkpg08qOGGTGKRe5pD+G60CAwEAAQ== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /test/test.index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import assert from 'assert'; 15 | import {describe, it} from 'mocha'; 16 | import * as main from '../src/index.js'; 17 | 18 | describe('📝 main exports', () => { 19 | it('should export all the types', () => { 20 | assert(main.Gaxios); 21 | assert(main.GaxiosError); 22 | assert(main.GaxiosInterceptorManager); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/test.retry.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | import assert from 'assert'; 15 | import nock from 'nock'; 16 | import {describe, it, afterEach} from 'mocha'; 17 | import {Gaxios, GaxiosError, GaxiosOptions, request} from '../src/index.js'; 18 | 19 | nock.disableNetConnect(); 20 | 21 | const url = 'https://example.com'; 22 | 23 | function getConfig(err: Error) { 24 | const e = err as GaxiosError; 25 | if (e && e.config && e.config.retryConfig) { 26 | return e.config.retryConfig; 27 | } 28 | return; 29 | } 30 | 31 | afterEach(() => { 32 | nock.cleanAll(); 33 | }); 34 | 35 | describe('🛸 retry & exponential backoff', () => { 36 | it('should provide an expected set of defaults', async () => { 37 | const scope = nock(url).get('/').times(4).reply(500); 38 | await assert.rejects(request({url, retry: true}), (e: Error) => { 39 | scope.done(); 40 | const config = getConfig(e); 41 | if (!config) { 42 | assert.fail('no config available'); 43 | } 44 | assert.strictEqual(config!.currentRetryAttempt, 3); 45 | assert.strictEqual(config!.retry, 3); 46 | assert.strictEqual(config!.noResponseRetries, 2); 47 | const expectedMethods = ['GET', 'HEAD', 'PUT', 'OPTIONS', 'DELETE']; 48 | for (const method of config!.httpMethodsToRetry!) { 49 | assert(expectedMethods.indexOf(method) > -1); 50 | } 51 | const expectedStatusCodes = [ 52 | [100, 199], 53 | [408, 408], 54 | [429, 429], 55 | [500, 599], 56 | ]; 57 | const statusCodesToRetry = config!.statusCodesToRetry!; 58 | for (let i = 0; i < statusCodesToRetry.length; i++) { 59 | const [min, max] = statusCodesToRetry[i]; 60 | const [expMin, expMax] = expectedStatusCodes[i]; 61 | assert.strictEqual(min, expMin); 62 | assert.strictEqual(max, expMax); 63 | } 64 | return true; 65 | }); 66 | }); 67 | 68 | it('should retry on 500 on the main export', async () => { 69 | const body = {buttered: '🥖'}; 70 | const scopes = [ 71 | nock(url).get('/').reply(500), 72 | nock(url).get('/').reply(200, body), 73 | ]; 74 | const res = await request({ 75 | url, 76 | retry: true, 77 | }); 78 | assert.deepStrictEqual(res.data, body); 79 | scopes.forEach(s => s.done()); 80 | }); 81 | 82 | it('should not retry on a post', async () => { 83 | const scope = nock(url).post('/').reply(500); 84 | await assert.rejects( 85 | request({url, method: 'POST', retry: true}), 86 | (e: Error) => { 87 | const config = getConfig(e); 88 | return config!.currentRetryAttempt === 0; 89 | }, 90 | ); 91 | scope.done(); 92 | }); 93 | 94 | it('should not retry if user aborted request', async () => { 95 | const ac = new AbortController(); 96 | const config: GaxiosOptions = { 97 | method: 'GET', 98 | url: 'https://google.com', 99 | signal: ac.signal, 100 | retryConfig: {retry: 10, noResponseRetries: 10}, 101 | }; 102 | const req = request(config); 103 | ac.abort(); 104 | try { 105 | await req; 106 | throw Error('unreachable'); 107 | } catch (err) { 108 | assert(err instanceof GaxiosError); 109 | assert(err.config); 110 | assert.strictEqual(err.config.retryConfig?.currentRetryAttempt, 0); 111 | } 112 | }); 113 | 114 | it('should retry at least the configured number of times', async () => { 115 | const body = {dippy: '🥚'}; 116 | const scopes = [ 117 | nock(url).get('/').times(3).reply(500), 118 | nock(url).get('/').reply(200, body), 119 | ]; 120 | const cfg = {url, retryConfig: {retry: 4}}; 121 | const res = await request(cfg); 122 | assert.deepStrictEqual(res.data, body); 123 | scopes.forEach(s => s.done()); 124 | }); 125 | 126 | it('should not retry more than configured', async () => { 127 | const scope = nock(url).get('/').twice().reply(500); 128 | const cfg = {url, retryConfig: {retry: 1}}; 129 | await assert.rejects(request(cfg), (e: Error) => { 130 | return getConfig(e)!.currentRetryAttempt === 1; 131 | }); 132 | scope.done(); 133 | }); 134 | 135 | it('should not retry on 4xx errors', async () => { 136 | const scope = nock(url).get('/').reply(404); 137 | await assert.rejects(request({url, retry: true}), (e: Error) => { 138 | const cfg = getConfig(e); 139 | return cfg!.currentRetryAttempt === 0; 140 | }); 141 | scope.done(); 142 | }); 143 | 144 | it('should retain the baseURL on retry', async () => { 145 | const body = {pumpkin: '🥧'}; 146 | const url = '/path'; 147 | const baseURL = 'http://example.com'; 148 | const scope = nock(baseURL).get(url).reply(500).get(url).reply(200, body); 149 | const gaxios = new Gaxios({baseURL}); 150 | const res = await gaxios.request({ 151 | url, 152 | retry: true, 153 | }); 154 | assert.deepStrictEqual(res.data, body); 155 | scope.done(); 156 | }); 157 | 158 | it('should not retry if retries set to 0', async () => { 159 | const scope = nock(url).get('/').reply(500); 160 | const cfg = {url, retryConfig: {retry: 0}}; 161 | await assert.rejects(request(cfg), (e: Error) => { 162 | const cfg = getConfig(e); 163 | return cfg!.currentRetryAttempt === 0; 164 | }); 165 | scope.done(); 166 | }); 167 | 168 | it('should notify on retry attempts', async () => { 169 | const body = {buttered: '🥖'}; 170 | const scopes = [ 171 | nock(url).get('/').reply(500), 172 | nock(url).get('/').reply(200, body), 173 | ]; 174 | let flipped = false; 175 | const config: GaxiosOptions = { 176 | url, 177 | retryConfig: { 178 | onRetryAttempt: err => { 179 | const cfg = getConfig(err); 180 | assert.strictEqual(cfg!.currentRetryAttempt, 1); 181 | flipped = true; 182 | }, 183 | }, 184 | }; 185 | await request(config); 186 | assert.strictEqual(flipped, true); 187 | scopes.forEach(s => s.done()); 188 | }); 189 | 190 | it('accepts async onRetryAttempt handler', async () => { 191 | const body = {buttered: '🥖'}; 192 | const scopes = [ 193 | nock(url).get('/').reply(500), 194 | nock(url).get('/').reply(200, body), 195 | ]; 196 | let flipped = false; 197 | const config: GaxiosOptions = { 198 | url, 199 | retryConfig: { 200 | onRetryAttempt: async err => { 201 | const cfg = getConfig(err); 202 | assert.strictEqual(cfg!.currentRetryAttempt, 1); 203 | flipped = true; 204 | }, 205 | }, 206 | }; 207 | await request(config); 208 | assert.strictEqual(flipped, true); 209 | scopes.forEach(s => s.done()); 210 | }); 211 | 212 | it('should support overriding the shouldRetry method', async () => { 213 | const scope = nock(url).get('/').reply(500); 214 | const config = { 215 | url, 216 | retryConfig: { 217 | shouldRetry: () => { 218 | return false; 219 | }, 220 | }, 221 | }; 222 | await assert.rejects(request(config), (e: Error) => { 223 | const cfg = getConfig(e); 224 | return cfg!.currentRetryAttempt === 0; 225 | }); 226 | scope.done(); 227 | }); 228 | 229 | it('should support overriding the shouldRetry method with a promise', async () => { 230 | const scope = nock(url).get('/').reply(500); 231 | const config = { 232 | url, 233 | retryConfig: { 234 | shouldRetry: async () => { 235 | return false; 236 | }, 237 | }, 238 | }; 239 | await assert.rejects(request(config), (e: Error) => { 240 | const cfg = getConfig(e); 241 | return cfg!.currentRetryAttempt === 0; 242 | }); 243 | scope.done(); 244 | }); 245 | 246 | it('should retry on ENOTFOUND', async () => { 247 | const body = {spicy: '🌮'}; 248 | const scopes = [ 249 | nock(url).get('/').reply(500, {code: 'ENOTFOUND'}), 250 | nock(url).get('/').reply(200, body), 251 | ]; 252 | const res = await request({url, retry: true}); 253 | assert.deepStrictEqual(res.data, body); 254 | scopes.forEach(s => s.done()); 255 | }); 256 | 257 | it('should retry on ETIMEDOUT', async () => { 258 | const body = {sizzling: '🥓'}; 259 | const scopes = [ 260 | nock(url).get('/').reply(500, {code: 'ETIMEDOUT'}), 261 | nock(url).get('/').reply(200, body), 262 | ]; 263 | const res = await request({url, retry: true}); 264 | assert.deepStrictEqual(res.data, body); 265 | scopes.forEach(s => s.done()); 266 | }); 267 | 268 | it('should allow configuring noResponseRetries', async () => { 269 | // `nock` is not listening, therefore it should fail 270 | const config = {url, retryConfig: {noResponseRetries: 0}}; 271 | await assert.rejects(request(config), (e: GaxiosError) => { 272 | return ( 273 | e.code === 'ENETUNREACH' && 274 | e.config.retryConfig?.currentRetryAttempt === 0 275 | ); 276 | }); 277 | }); 278 | 279 | it('should delay the initial retry by 100ms by default', async () => { 280 | const scope = nock(url).get('/').reply(500).get('/').reply(200, {}); 281 | const start = Date.now(); 282 | await request({ 283 | url, 284 | retry: true, 285 | }); 286 | const delay = Date.now() - start; 287 | assert.ok(delay > 100 && delay < 199); 288 | scope.done(); 289 | }); 290 | 291 | it('should respect the retryDelay if configured', async () => { 292 | const scope = nock(url).get('/').reply(500).get('/').reply(200, {}); 293 | const start = Date.now(); 294 | await request({ 295 | url, 296 | retryConfig: { 297 | retryDelay: 500, 298 | }, 299 | }); 300 | const delay = Date.now() - start; 301 | assert.ok(delay > 500 && delay < 599); 302 | scope.done(); 303 | }); 304 | 305 | it('should respect retryDelayMultiplier if configured', async () => { 306 | const scope = nock(url) 307 | .get('/') 308 | .reply(500) 309 | .get('/') 310 | .reply(500) 311 | .get('/') 312 | .reply(200, {}); 313 | const start = Date.now(); 314 | await request({ 315 | url, 316 | retryConfig: { 317 | retryDelayMultiplier: 3, 318 | }, 319 | }); 320 | const delay = Date.now() - start; 321 | assert.ok(delay > 1000 && delay < 1999); 322 | scope.done(); 323 | }); 324 | 325 | it('should respect totalTimeout if configured', async () => { 326 | const scope = nock(url) 327 | .get('/') 328 | .reply(500) 329 | .get('/') 330 | .reply(500) 331 | .get('/') 332 | .reply(200, {}); 333 | 334 | const start = Date.now(); 335 | await request({ 336 | url, 337 | retryConfig: { 338 | retryDelayMultiplier: 100, 339 | totalTimeout: 3000, 340 | }, 341 | }); 342 | const delay = Date.now() - start; 343 | assert.ok(delay > 3000 && delay < 3999); 344 | scope.done(); 345 | }); 346 | 347 | it('should respect maxRetryDelay if configured', async () => { 348 | const scope = nock(url) 349 | .get('/') 350 | .reply(500) 351 | .get('/') 352 | .reply(500) 353 | .get('/') 354 | .reply(200, {}); 355 | 356 | const start = Date.now(); 357 | await request({ 358 | url, 359 | retryConfig: { 360 | retryDelayMultiplier: 100, 361 | maxRetryDelay: 4000, 362 | }, 363 | }); 364 | const delay = Date.now() - start; 365 | assert.ok(delay > 4000 && delay < 4999); 366 | scope.done(); 367 | }); 368 | 369 | it('should retry on `timeout`', async () => { 370 | const scope = nock(url).get('/').delay(500).reply(400).get('/').reply(204); 371 | 372 | const gaxios = new Gaxios(); 373 | const timeout = 100; 374 | 375 | async function onRetryAttempt({config, message}: GaxiosError) { 376 | assert(config.signal?.reason instanceof DOMException); 377 | assert.equal(config.signal.reason.name, 'TimeoutError'); 378 | assert.match(message, /timeout/i); 379 | 380 | // increase timeout to something higher to avoid time-sensitive flaky tests 381 | // note: the second `nock` GET is not delayed like the first one 382 | config.timeout = 10000; 383 | } 384 | 385 | const res = await gaxios.request({ 386 | url, 387 | timeout, 388 | // NOTE: `node-fetch` does not yet support `TimeoutError` - testing with native `fetch` for now. 389 | fetchImplementation: fetch, 390 | retryConfig: { 391 | onRetryAttempt, 392 | }, 393 | }); 394 | 395 | assert.equal(res.status, 204); 396 | assert.equal(res.config?.retryConfig?.currentRetryAttempt, 1); 397 | 398 | scope.done(); 399 | }); 400 | }); 401 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gts/tsconfig-google.json", 3 | "compilerOptions": { 4 | "lib": ["ES2023", "dom"], 5 | "rootDir": "." 6 | }, 7 | "include": ["src/*.*ts", "test/*.ts", "browser-test/*.ts", "system-test/*.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "build/cjs" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "Preserve", 5 | "outDir": "build/esm" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /utils/enable-esm.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | 3 | // Enables all `.js` files in `build/esm` to be treated as ESM 4 | await fs.writeFile('./build/esm/package.json', '{"type": "module"}'); 5 | -------------------------------------------------------------------------------- /webpack-tests.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Use `npm run webpack` to produce Webpack bundle for this library. 16 | 17 | const path = require('path'); 18 | const webpack = require('webpack'); 19 | 20 | module.exports = { 21 | resolve: { 22 | extensions: ['.ts', '.js', '.json'], 23 | alias: { 24 | '../../package.json': path.resolve(__dirname, 'package.json'), 25 | }, 26 | fallback: { 27 | crypto: false, 28 | child_process: false, 29 | fs: false, 30 | http2: false, 31 | https: false, 32 | buffer: 'browserify', 33 | process: false, 34 | os: false, 35 | path: false, 36 | stream: 'stream-browserify', 37 | url: false, 38 | util: false, 39 | zlib: false, 40 | }, 41 | }, 42 | module: { 43 | rules: [ 44 | { 45 | test: /node_modules\/https-proxy-agent\//, 46 | use: 'null-loader', 47 | }, 48 | { 49 | test: /\.ts$/, 50 | use: 'ts-loader', 51 | exclude: /node_modules/, 52 | }, 53 | ], 54 | }, 55 | mode: 'production', 56 | plugins: [ 57 | new webpack.ProvidePlugin({ 58 | process: 'process/browser', 59 | Buffer: ['buffer', 'Buffer'], 60 | }), 61 | ], 62 | }; 63 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Use `npm run webpack` to produce Webpack bundle for this library. 16 | 17 | const path = require('path'); 18 | 19 | module.exports = { 20 | entry: './src/index.ts', 21 | resolve: { 22 | extensions: ['.ts', '.js', '.json'], 23 | alias: { 24 | '../../package.json': path.resolve(__dirname, 'package.json'), 25 | }, 26 | fallback: { 27 | crypto: false, 28 | child_process: false, 29 | fs: false, 30 | http2: false, 31 | https: false, 32 | buffer: 'browserify', 33 | process: false, 34 | os: false, 35 | path: false, 36 | stream: 'stream-browserify', 37 | url: false, 38 | util: false, 39 | zlib: false, 40 | }, 41 | }, 42 | output: { 43 | library: 'gaxios', 44 | filename: 'gaxios.min.js', 45 | path: path.resolve(__dirname, 'dist'), 46 | }, 47 | module: { 48 | rules: [ 49 | { 50 | test: /node_modules\/https-proxy-agent\//, 51 | use: 'null-loader', 52 | }, 53 | { 54 | test: /\.ts$/, 55 | use: 'ts-loader', 56 | exclude: /node_modules/, 57 | }, 58 | ], 59 | }, 60 | mode: 'production', 61 | plugins: [], 62 | }; 63 | --------------------------------------------------------------------------------