chore: migrate to gitea
Some checks failed
golangci-lint / lint (push) Failing after 1m30s
Test / test (push) Failing after 2m17s

This commit is contained in:
2026-01-27 00:40:46 +01:00
parent 1e05874160
commit f8df24c29d
3169 changed files with 1216434 additions and 1587 deletions

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,219 @@
// Copyright 2022 Google LLC.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package client is a cross-platform client for the signer binary (a.k.a."EnterpriseCertSigner").
//
// The signer binary is OS-specific, but exposes a standard set of APIs for the client to use.
package client
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/gob"
"errors"
"fmt"
"io"
"net/rpc"
"os"
"os/exec"
"github.com/googleapis/enterprise-certificate-proxy/client/util"
)
const signAPI = "EnterpriseCertSigner.Sign"
const certificateChainAPI = "EnterpriseCertSigner.CertificateChain"
const publicKeyAPI = "EnterpriseCertSigner.Public"
const encryptAPI = "EnterpriseCertSigner.Encrypt"
const decryptAPI = "EnterpriseCertSigner.Decrypt"
// A Connection wraps a pair of unidirectional streams as an io.ReadWriteCloser.
type Connection struct {
io.ReadCloser
io.WriteCloser
}
// Close closes c's underlying ReadCloser and WriteCloser.
func (c *Connection) Close() error {
rerr := c.ReadCloser.Close()
werr := c.WriteCloser.Close()
if rerr != nil {
return rerr
}
return werr
}
func init() {
gob.Register(crypto.SHA256)
gob.Register(crypto.SHA384)
gob.Register(crypto.SHA512)
gob.Register(&rsa.PSSOptions{})
gob.Register(&rsa.OAEPOptions{})
}
// SignArgs contains arguments for a Sign API call.
type SignArgs struct {
Digest []byte // The content to sign.
Opts crypto.SignerOpts // Options for signing. Must implement HashFunc().
}
// EncryptArgs contains arguments for an Encrypt API call.
type EncryptArgs struct {
Plaintext []byte // The plaintext to encrypt.
Opts any // Options for encryption. Ex: an instance of crypto.Hash.
}
// DecryptArgs contains arguments to for a Decrypt API call.
type DecryptArgs struct {
Ciphertext []byte // The ciphertext to decrypt.
Opts crypto.DecrypterOpts // Options for decryption. Ex: an instance of *rsa.OAEPOptions.
}
// Key implements credential.Credential by holding the executed signer subprocess.
type Key struct {
cmd *exec.Cmd // Pointer to the signer subprocess.
client *rpc.Client // Pointer to the rpc client that communicates with the signer subprocess.
publicKey crypto.PublicKey // Public key of loaded certificate.
chain [][]byte // Certificate chain of loaded certificate.
}
// CertificateChain returns the credential as a raw X509 cert chain. This contains the public key.
func (k *Key) CertificateChain() [][]byte {
return k.chain
}
// Close closes the RPC connection and kills the signer subprocess.
// Call this to free up resources when the Key object is no longer needed.
func (k *Key) Close() error {
if err := k.cmd.Process.Kill(); err != nil {
return fmt.Errorf("failed to kill signer process: %w", err)
}
// Wait for cmd to exit and release resources. Since the process is forcefully killed, this
// will return a non-nil error (varies by OS), which we will ignore.
_ = k.cmd.Wait()
// The Pipes connecting the RPC client should have been closed when the signer subprocess was killed.
// Calling `k.client.Close()` before `k.cmd.Process.Kill()` or `k.cmd.Wait()` _will_ cause a segfault.
if err := k.client.Close(); err.Error() != "close |0: file already closed" {
return fmt.Errorf("failed to close RPC connection: %w", err)
}
return nil
}
// Public returns the public key for this Key.
func (k *Key) Public() crypto.PublicKey {
return k.publicKey
}
// Sign signs a message digest, using the specified signer opts. Implements crypto.Signer interface.
func (k *Key) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signed []byte, err error) {
if opts != nil && opts.HashFunc() != 0 && len(digest) != opts.HashFunc().Size() {
return nil, fmt.Errorf("Digest length of %v bytes does not match Hash function size of %v bytes", len(digest), opts.HashFunc().Size())
}
err = k.client.Call(signAPI, SignArgs{Digest: digest, Opts: opts}, &signed)
return
}
// Encrypt encrypts a plaintext msg into ciphertext, using the specified encrypt opts.
func (k *Key) Encrypt(_ io.Reader, msg []byte, opts any) (ciphertext []byte, err error) {
err = k.client.Call(encryptAPI, EncryptArgs{Plaintext: msg, Opts: opts}, &ciphertext)
return
}
// Decrypt decrypts a ciphertext msg into plaintext, using the specified decrypter opts. Implements crypto.Decrypter interface.
func (k *Key) Decrypt(_ io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
err = k.client.Call(decryptAPI, DecryptArgs{Ciphertext: msg, Opts: opts}, &plaintext)
return
}
// ErrCredUnavailable is a sentinel error that indicates ECP Cred is unavailable,
// possibly due to missing config or missing binary path.
var ErrCredUnavailable = errors.New("Cred is unavailable")
// Cred spawns a signer subprocess that listens on stdin/stdout to perform certificate
// related operations, including signing messages with the private key.
//
// The signer binary path is read from the specified configFilePath, if provided.
// Otherwise, use the default config file path.
//
// The config file also specifies which certificate the signer should use.
func Cred(configFilePath string) (*Key, error) {
if configFilePath == "" {
envFilePath := util.GetConfigFilePathFromEnv()
if envFilePath != "" {
configFilePath = envFilePath
} else {
configFilePath = util.GetDefaultConfigFilePath()
}
}
enterpriseCertSignerPath, err := util.LoadSignerBinaryPath(configFilePath)
if err != nil {
if errors.Is(err, util.ErrConfigUnavailable) {
return nil, ErrCredUnavailable
}
return nil, err
}
k := &Key{
cmd: exec.Command(enterpriseCertSignerPath, configFilePath),
}
// Redirect errors from subprocess to parent process.
k.cmd.Stderr = os.Stderr
// RPC client will communicate with subprocess over stdin/stdout.
kin, err := k.cmd.StdinPipe()
if err != nil {
return nil, err
}
kout, err := k.cmd.StdoutPipe()
if err != nil {
return nil, err
}
k.client = rpc.NewClient(&Connection{kout, kin})
if err := k.cmd.Start(); err != nil {
return nil, fmt.Errorf("starting enterprise cert signer subprocess: %w", err)
}
if err := k.client.Call(certificateChainAPI, struct{}{}, &k.chain); err != nil {
return nil, fmt.Errorf("failed to retrieve certificate chain: %w", err)
}
var publicKeyBytes []byte
if err := k.client.Call(publicKeyAPI, struct{}{}, &publicKeyBytes); err != nil {
return nil, fmt.Errorf("failed to retrieve public key: %w", err)
}
publicKey, err := x509.ParsePKIXPublicKey(publicKeyBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
var ok bool
k.publicKey, ok = publicKey.(crypto.PublicKey)
if !ok {
return nil, fmt.Errorf("invalid public key type: %T", publicKey)
}
switch pub := k.publicKey.(type) {
case *rsa.PublicKey:
if pub.Size() < 256 {
return nil, fmt.Errorf("RSA modulus size is less than 2048 bits: %v", pub.Size()*8)
}
case *ecdsa.PublicKey:
default:
return nil, fmt.Errorf("unsupported public key type: %v", pub)
}
return k, nil
}

View File

@@ -0,0 +1,100 @@
// Copyright 2022 Google LLC.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package util provides helper functions for the client.
package util
import (
"encoding/json"
"errors"
"io"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"
)
const configFileName = "certificate_config.json"
// EnterpriseCertificateConfig contains parameters for initializing signer.
type EnterpriseCertificateConfig struct {
Libs Libs `json:"libs"`
}
// Libs specifies the locations of helper libraries.
type Libs struct {
ECP string `json:"ecp"`
}
// ErrConfigUnavailable is a sentinel error that indicates ECP config is unavailable,
// possibly due to entire config missing or missing binary path.
var ErrConfigUnavailable = errors.New("Config is unavailable")
// LoadSignerBinaryPath retrieves the path of the signer binary from the config file.
func LoadSignerBinaryPath(configFilePath string) (path string, err error) {
jsonFile, err := os.Open(configFilePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return "", ErrConfigUnavailable
}
return "", err
}
byteValue, err := io.ReadAll(jsonFile)
if err != nil {
return "", err
}
var config EnterpriseCertificateConfig
err = json.Unmarshal(byteValue, &config)
if err != nil {
return "", err
}
signerBinaryPath := config.Libs.ECP
if signerBinaryPath == "" {
return "", ErrConfigUnavailable
}
signerBinaryPath = strings.ReplaceAll(signerBinaryPath, "~", guessHomeDir())
signerBinaryPath = strings.ReplaceAll(signerBinaryPath, "$HOME", guessHomeDir())
return signerBinaryPath, nil
}
func guessHomeDir() string {
// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470
if v := os.Getenv("HOME"); v != "" {
return v
}
// Else, fall back to user.Current:
if u, err := user.Current(); err == nil {
return u.HomeDir
}
return ""
}
func getDefaultConfigFileDirectory() (directory string) {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "gcloud")
}
return filepath.Join(guessHomeDir(), ".config/gcloud")
}
// GetDefaultConfigFilePath returns the default path of the enterprise certificate config file created by gCloud.
func GetDefaultConfigFilePath() (path string) {
return filepath.Join(getDefaultConfigFileDirectory(), configFileName)
}
// GetConfigFilePathFromEnv returns the path associated with environment variable GOOGLE_API_CERTIFICATE_CONFIG
func GetConfigFilePathFromEnv() (path string) {
return os.Getenv("GOOGLE_API_CERTIFICATE_CONFIG")
}

View File

@@ -0,0 +1,3 @@
{
"v2": "2.15.0"
}

188
vendor/github.com/googleapis/gax-go/v2/CHANGES.md generated vendored Normal file
View File

@@ -0,0 +1,188 @@
# Changes
## [2.16.0](https://github.com/googleapis/google-cloud-go/releases/tag/v2.16.0) (2025-12-17)
### Features
* add IsFeatureEnabled (#454) ([2700b8a](https://github.com/googleapis/google-cloud-go/commit/2700b8ab3062c6c6c5a26d0fc6ba1fc064a8fc04))
## [2.15.0](https://github.com/googleapis/gax-go/compare/v2.14.2...v2.15.0) (2025-07-09)
### Features
* **apierror:** improve gRPC status code mapping for HTTP errors ([#431](https://github.com/googleapis/gax-go/issues/431)) ([c207f2a](https://github.com/googleapis/gax-go/commit/c207f2a19ab91d3baee458b57d4aa992519025c7))
## [2.14.2](https://github.com/googleapis/gax-go/compare/v2.14.1...v2.14.2) (2025-05-12)
### Documentation
* **v2:** Fix Backoff doc to accurately explain Multiplier ([#423](https://github.com/googleapis/gax-go/issues/423)) ([16d1791](https://github.com/googleapis/gax-go/commit/16d17917121ea9f5d84ba52b5c7c7f2ec0f9e784)), refs [#422](https://github.com/googleapis/gax-go/issues/422)
## [2.14.1](https://github.com/googleapis/gax-go/compare/v2.14.0...v2.14.1) (2024-12-19)
### Bug Fixes
* update golang.org/x/net to v0.33.0 ([#391](https://github.com/googleapis/gax-go/issues/391)) ([547a5b4](https://github.com/googleapis/gax-go/commit/547a5b43aa6f376f71242da9f18e65fbdfb342f6))
### Documentation
* fix godoc to refer to the proper envvar ([#387](https://github.com/googleapis/gax-go/issues/387)) ([dc6baf7](https://github.com/googleapis/gax-go/commit/dc6baf75c1a737233739630b5af6c9759f08abcd))
## [2.14.0](https://github.com/googleapis/gax-go/compare/v2.13.0...v2.14.0) (2024-11-13)
### Features
* **internallog:** add a logging support package ([#380](https://github.com/googleapis/gax-go/issues/380)) ([c877470](https://github.com/googleapis/gax-go/commit/c87747098135631a3de5865ed03aaf2c79fd9319))
## [2.13.0](https://github.com/googleapis/gax-go/compare/v2.12.5...v2.13.0) (2024-07-22)
### Features
* **iterator:** add package to help work with new iter.Seq types ([#358](https://github.com/googleapis/gax-go/issues/358)) ([6bccdaa](https://github.com/googleapis/gax-go/commit/6bccdaac011fe6fd147e4eb533a8e6520b7d4acc))
## [2.12.5](https://github.com/googleapis/gax-go/compare/v2.12.4...v2.12.5) (2024-06-18)
### Bug Fixes
* **v2/apierror:** fix (*APIError).Error() for unwrapped Status ([#351](https://github.com/googleapis/gax-go/issues/351)) ([22c16e7](https://github.com/googleapis/gax-go/commit/22c16e7bff5402bdc4c25063771cdd01c650b500)), refs [#350](https://github.com/googleapis/gax-go/issues/350)
## [2.12.4](https://github.com/googleapis/gax-go/compare/v2.12.3...v2.12.4) (2024-05-03)
### Bug Fixes
* provide unmarshal options for streams ([#343](https://github.com/googleapis/gax-go/issues/343)) ([ddf9a90](https://github.com/googleapis/gax-go/commit/ddf9a90bf180295d49875e15cb80b2136a49dbaf))
## [2.12.3](https://github.com/googleapis/gax-go/compare/v2.12.2...v2.12.3) (2024-03-14)
### Bug Fixes
* bump protobuf dep to v1.33 ([#333](https://github.com/googleapis/gax-go/issues/333)) ([2892b22](https://github.com/googleapis/gax-go/commit/2892b22c1ae8a70dec3448d82e634643fe6c1be2))
## [2.12.2](https://github.com/googleapis/gax-go/compare/v2.12.1...v2.12.2) (2024-02-23)
### Bug Fixes
* **v2/callctx:** fix SetHeader race by cloning header map ([#326](https://github.com/googleapis/gax-go/issues/326)) ([534311f](https://github.com/googleapis/gax-go/commit/534311f0f163d101f30657736c0e6f860e9c39dc))
## [2.12.1](https://github.com/googleapis/gax-go/compare/v2.12.0...v2.12.1) (2024-02-13)
### Bug Fixes
* add XGoogFieldMaskHeader constant ([#321](https://github.com/googleapis/gax-go/issues/321)) ([666ee08](https://github.com/googleapis/gax-go/commit/666ee08931041b7fed56bed7132649785b2d3dfe))
## [2.12.0](https://github.com/googleapis/gax-go/compare/v2.11.0...v2.12.0) (2023-06-26)
### Features
* **v2/callctx:** add new callctx package ([#291](https://github.com/googleapis/gax-go/issues/291)) ([11503ed](https://github.com/googleapis/gax-go/commit/11503ed98df4ae1bbdedf91ff64d47e63f187d68))
* **v2:** add BuildHeaders and InsertMetadataIntoOutgoingContext to header ([#290](https://github.com/googleapis/gax-go/issues/290)) ([6a4b89f](https://github.com/googleapis/gax-go/commit/6a4b89f5551a40262e7c3caf2e1bdc7321b76ea1))
## [2.11.0](https://github.com/googleapis/gax-go/compare/v2.10.0...v2.11.0) (2023-06-13)
### Features
* **v2:** add GoVersion package variable ([#283](https://github.com/googleapis/gax-go/issues/283)) ([26553cc](https://github.com/googleapis/gax-go/commit/26553ccadb4016b189881f52e6c253b68bb3e3d5))
### Bug Fixes
* **v2:** handle space in non-devel go version ([#288](https://github.com/googleapis/gax-go/issues/288)) ([fd7bca0](https://github.com/googleapis/gax-go/commit/fd7bca029a1c5e63def8f0a5fd1ec3f725d92f75))
## [2.10.0](https://github.com/googleapis/gax-go/compare/v2.9.1...v2.10.0) (2023-05-30)
### Features
* update dependencies ([#280](https://github.com/googleapis/gax-go/issues/280)) ([4514281](https://github.com/googleapis/gax-go/commit/4514281058590f3637c36bfd49baa65c4d3cfb21))
## [2.9.1](https://github.com/googleapis/gax-go/compare/v2.9.0...v2.9.1) (2023-05-23)
### Bug Fixes
* **v2:** drop cloud lro test dep ([#276](https://github.com/googleapis/gax-go/issues/276)) ([c67eeba](https://github.com/googleapis/gax-go/commit/c67eeba0f10a3294b1d93c1b8fbe40211a55ae5f)), refs [#270](https://github.com/googleapis/gax-go/issues/270)
## [2.9.0](https://github.com/googleapis/gax-go/compare/v2.8.0...v2.9.0) (2023-05-22)
### Features
* **apierror:** add method to return HTTP status code conditionally ([#274](https://github.com/googleapis/gax-go/issues/274)) ([5874431](https://github.com/googleapis/gax-go/commit/587443169acd10f7f86d1989dc8aaf189e645e98)), refs [#229](https://github.com/googleapis/gax-go/issues/229)
### Documentation
* add ref to usage with clients ([#272](https://github.com/googleapis/gax-go/issues/272)) ([ea4d72d](https://github.com/googleapis/gax-go/commit/ea4d72d514beba4de450868b5fb028601a29164e)), refs [#228](https://github.com/googleapis/gax-go/issues/228)
## [2.8.0](https://github.com/googleapis/gax-go/compare/v2.7.1...v2.8.0) (2023-03-15)
### Features
* **v2:** add WithTimeout option ([#259](https://github.com/googleapis/gax-go/issues/259)) ([9a8da43](https://github.com/googleapis/gax-go/commit/9a8da43693002448b1e8758023699387481866d1))
## [2.7.1](https://github.com/googleapis/gax-go/compare/v2.7.0...v2.7.1) (2023-03-06)
### Bug Fixes
* **v2/apierror:** return Unknown GRPCStatus when err source is HTTP ([#260](https://github.com/googleapis/gax-go/issues/260)) ([043b734](https://github.com/googleapis/gax-go/commit/043b73437a240a91229207fb3ee52a9935a36f23)), refs [#254](https://github.com/googleapis/gax-go/issues/254)
## [2.7.0](https://github.com/googleapis/gax-go/compare/v2.6.0...v2.7.0) (2022-11-02)
### Features
* update google.golang.org/api to latest ([#240](https://github.com/googleapis/gax-go/issues/240)) ([f690a02](https://github.com/googleapis/gax-go/commit/f690a02c806a2903bdee943ede3a58e3a331ebd6))
* **v2/apierror:** add apierror.FromWrappingError ([#238](https://github.com/googleapis/gax-go/issues/238)) ([9dbd96d](https://github.com/googleapis/gax-go/commit/9dbd96d59b9d54ceb7c025513aa8c1a9d727382f))
## [2.6.0](https://github.com/googleapis/gax-go/compare/v2.5.1...v2.6.0) (2022-10-13)
### Features
* **v2:** copy DetermineContentType functionality ([#230](https://github.com/googleapis/gax-go/issues/230)) ([2c52a70](https://github.com/googleapis/gax-go/commit/2c52a70bae965397f740ed27d46aabe89ff249b3))
## [2.5.1](https://github.com/googleapis/gax-go/compare/v2.5.0...v2.5.1) (2022-08-04)
### Bug Fixes
* **v2:** resolve bad genproto pseudoversion in go.mod ([#218](https://github.com/googleapis/gax-go/issues/218)) ([1379b27](https://github.com/googleapis/gax-go/commit/1379b27e9846d959f7e1163b9ef298b3c92c8d23))
## [2.5.0](https://github.com/googleapis/gax-go/compare/v2.4.0...v2.5.0) (2022-08-04)
### Features
* add ExtractProtoMessage to apierror ([#213](https://github.com/googleapis/gax-go/issues/213)) ([a6ce70c](https://github.com/googleapis/gax-go/commit/a6ce70c725c890533a9de6272d3b5ba2e336d6bb))
## [2.4.0](https://github.com/googleapis/gax-go/compare/v2.3.0...v2.4.0) (2022-05-09)
### Features
* **v2:** add OnHTTPCodes CallOption ([#188](https://github.com/googleapis/gax-go/issues/188)) ([ba7c534](https://github.com/googleapis/gax-go/commit/ba7c5348363ab6c33e1cee3c03c0be68a46ca07c))
### Bug Fixes
* **v2/apierror:** use errors.As in FromError ([#189](https://github.com/googleapis/gax-go/issues/189)) ([f30f05b](https://github.com/googleapis/gax-go/commit/f30f05be583828f4c09cca4091333ea88ff8d79e))
### Miscellaneous Chores
* **v2:** bump release-please processing ([#192](https://github.com/googleapis/gax-go/issues/192)) ([56172f9](https://github.com/googleapis/gax-go/commit/56172f971d1141d7687edaac053ad3470af76719))

27
vendor/github.com/googleapis/gax-go/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright 2016, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,402 @@
// Copyright 2021, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package apierror implements a wrapper error for parsing error details from
// API calls. Both HTTP & gRPC status errors are supported.
//
// For examples of how to use [APIError] with client libraries please reference
// [Inspecting errors](https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors)
// in the client library documentation.
package apierror
import (
"errors"
"fmt"
"net/http"
"strings"
jsonerror "github.com/googleapis/gax-go/v2/apierror/internal/proto"
"google.golang.org/api/googleapi"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
// canonicalMap maps HTTP codes to gRPC status code equivalents.
var canonicalMap = map[int]codes.Code{
http.StatusOK: codes.OK,
http.StatusBadRequest: codes.InvalidArgument,
http.StatusForbidden: codes.PermissionDenied,
http.StatusNotFound: codes.NotFound,
http.StatusConflict: codes.Aborted,
http.StatusRequestedRangeNotSatisfiable: codes.OutOfRange,
http.StatusTooManyRequests: codes.ResourceExhausted,
http.StatusGatewayTimeout: codes.DeadlineExceeded,
http.StatusNotImplemented: codes.Unimplemented,
http.StatusServiceUnavailable: codes.Unavailable,
http.StatusUnauthorized: codes.Unauthenticated,
}
// toCode maps an http code to the most correct equivalent.
func toCode(httpCode int) codes.Code {
if sCode, ok := canonicalMap[httpCode]; ok {
return sCode
}
switch {
case httpCode >= 200 && httpCode < 300:
return codes.OK
case httpCode >= 400 && httpCode < 500:
return codes.FailedPrecondition
case httpCode >= 500 && httpCode < 600:
return codes.Internal
}
return codes.Unknown
}
// ErrDetails holds the google/rpc/error_details.proto messages.
type ErrDetails struct {
ErrorInfo *errdetails.ErrorInfo
BadRequest *errdetails.BadRequest
PreconditionFailure *errdetails.PreconditionFailure
QuotaFailure *errdetails.QuotaFailure
RetryInfo *errdetails.RetryInfo
ResourceInfo *errdetails.ResourceInfo
RequestInfo *errdetails.RequestInfo
DebugInfo *errdetails.DebugInfo
Help *errdetails.Help
LocalizedMessage *errdetails.LocalizedMessage
// Unknown stores unidentifiable error details.
Unknown []interface{}
}
// ErrMessageNotFound is used to signal ExtractProtoMessage found no matching messages.
var ErrMessageNotFound = errors.New("message not found")
// ExtractProtoMessage provides a mechanism for extracting protobuf messages from the
// Unknown error details. If ExtractProtoMessage finds an unknown message of the same type,
// the content of the message is copied to the provided message.
//
// ExtractProtoMessage will return ErrMessageNotFound if there are no message matching the
// protocol buffer type of the provided message.
func (e ErrDetails) ExtractProtoMessage(v proto.Message) error {
if v == nil {
return ErrMessageNotFound
}
for _, elem := range e.Unknown {
if elemProto, ok := elem.(proto.Message); ok {
if v.ProtoReflect().Type() == elemProto.ProtoReflect().Type() {
proto.Merge(v, elemProto)
return nil
}
}
}
return ErrMessageNotFound
}
func (e ErrDetails) String() string {
var d strings.Builder
if e.ErrorInfo != nil {
d.WriteString(fmt.Sprintf("error details: name = ErrorInfo reason = %s domain = %s metadata = %s\n",
e.ErrorInfo.GetReason(), e.ErrorInfo.GetDomain(), e.ErrorInfo.GetMetadata()))
}
if e.BadRequest != nil {
v := e.BadRequest.GetFieldViolations()
var f []string
var desc []string
for _, x := range v {
f = append(f, x.GetField())
desc = append(desc, x.GetDescription())
}
d.WriteString(fmt.Sprintf("error details: name = BadRequest field = %s desc = %s\n",
strings.Join(f, " "), strings.Join(desc, " ")))
}
if e.PreconditionFailure != nil {
v := e.PreconditionFailure.GetViolations()
var t []string
var s []string
var desc []string
for _, x := range v {
t = append(t, x.GetType())
s = append(s, x.GetSubject())
desc = append(desc, x.GetDescription())
}
d.WriteString(fmt.Sprintf("error details: name = PreconditionFailure type = %s subj = %s desc = %s\n", strings.Join(t, " "),
strings.Join(s, " "), strings.Join(desc, " ")))
}
if e.QuotaFailure != nil {
v := e.QuotaFailure.GetViolations()
var s []string
var desc []string
for _, x := range v {
s = append(s, x.GetSubject())
desc = append(desc, x.GetDescription())
}
d.WriteString(fmt.Sprintf("error details: name = QuotaFailure subj = %s desc = %s\n",
strings.Join(s, " "), strings.Join(desc, " ")))
}
if e.RequestInfo != nil {
d.WriteString(fmt.Sprintf("error details: name = RequestInfo id = %s data = %s\n",
e.RequestInfo.GetRequestId(), e.RequestInfo.GetServingData()))
}
if e.ResourceInfo != nil {
d.WriteString(fmt.Sprintf("error details: name = ResourceInfo type = %s resourcename = %s owner = %s desc = %s\n",
e.ResourceInfo.GetResourceType(), e.ResourceInfo.GetResourceName(),
e.ResourceInfo.GetOwner(), e.ResourceInfo.GetDescription()))
}
if e.RetryInfo != nil {
d.WriteString(fmt.Sprintf("error details: retry in %s\n", e.RetryInfo.GetRetryDelay().AsDuration()))
}
if e.Unknown != nil {
var s []string
for _, x := range e.Unknown {
s = append(s, fmt.Sprintf("%v", x))
}
d.WriteString(fmt.Sprintf("error details: name = Unknown desc = %s\n", strings.Join(s, " ")))
}
if e.DebugInfo != nil {
d.WriteString(fmt.Sprintf("error details: name = DebugInfo detail = %s stack = %s\n", e.DebugInfo.GetDetail(),
strings.Join(e.DebugInfo.GetStackEntries(), " ")))
}
if e.Help != nil {
var desc []string
var url []string
for _, x := range e.Help.Links {
desc = append(desc, x.GetDescription())
url = append(url, x.GetUrl())
}
d.WriteString(fmt.Sprintf("error details: name = Help desc = %s url = %s\n",
strings.Join(desc, " "), strings.Join(url, " ")))
}
if e.LocalizedMessage != nil {
d.WriteString(fmt.Sprintf("error details: name = LocalizedMessage locale = %s msg = %s\n",
e.LocalizedMessage.GetLocale(), e.LocalizedMessage.GetMessage()))
}
return d.String()
}
// APIError wraps either a gRPC Status error or a HTTP googleapi.Error. It
// implements error and Status interfaces.
type APIError struct {
err error
status *status.Status
httpErr *googleapi.Error
details ErrDetails
}
// Details presents the error details of the APIError.
func (a *APIError) Details() ErrDetails {
return a.details
}
// Unwrap extracts the original error.
func (a *APIError) Unwrap() error {
return a.err
}
// Error returns a readable representation of the APIError.
func (a *APIError) Error() string {
var msg string
if a.httpErr != nil {
// Truncate the googleapi.Error message because it dumps the Details in
// an ugly way.
msg = fmt.Sprintf("googleapi: Error %d: %s", a.httpErr.Code, a.httpErr.Message)
} else if a.status != nil && a.err != nil {
msg = a.err.Error()
} else if a.status != nil {
msg = a.status.Message()
}
return strings.TrimSpace(fmt.Sprintf("%s\n%s", msg, a.details))
}
// GRPCStatus extracts the underlying gRPC Status error.
// This method is necessary to fulfill the interface
// described in https://pkg.go.dev/google.golang.org/grpc/status#FromError.
//
// For errors that originated as an HTTP-based googleapi.Error, GRPCStatus()
// returns a status that attempts to map from the original HTTP code to an
// equivalent gRPC status code. For use cases where you want to avoid this
// behavior, error unwrapping can be used.
func (a *APIError) GRPCStatus() *status.Status {
return a.status
}
// Reason returns the reason in an ErrorInfo.
// If ErrorInfo is nil, it returns an empty string.
func (a *APIError) Reason() string {
return a.details.ErrorInfo.GetReason()
}
// Domain returns the domain in an ErrorInfo.
// If ErrorInfo is nil, it returns an empty string.
func (a *APIError) Domain() string {
return a.details.ErrorInfo.GetDomain()
}
// Metadata returns the metadata in an ErrorInfo.
// If ErrorInfo is nil, it returns nil.
func (a *APIError) Metadata() map[string]string {
return a.details.ErrorInfo.GetMetadata()
}
// setDetailsFromError parses a Status error or a googleapi.Error
// and sets status and details or httpErr and details, respectively.
// It returns false if neither Status nor googleapi.Error can be parsed.
//
// When err is a googleapi.Error, the status of the returned error will be
// mapped to the closest equivalent gGRPC status code.
func (a *APIError) setDetailsFromError(err error) bool {
st, isStatus := status.FromError(err)
var herr *googleapi.Error
isHTTPErr := errors.As(err, &herr)
switch {
case isStatus:
a.status = st
a.details = parseDetails(st.Details())
case isHTTPErr:
a.httpErr = herr
a.details = parseHTTPDetails(herr)
a.status = status.New(toCode(a.httpErr.Code), herr.Message)
default:
return false
}
return true
}
// FromError parses a Status error or a googleapi.Error and builds an
// APIError, wrapping the provided error in the new APIError. It
// returns false if neither Status nor googleapi.Error can be parsed.
func FromError(err error) (*APIError, bool) {
return ParseError(err, true)
}
// ParseError parses a Status error or a googleapi.Error and builds an
// APIError. If wrap is true, it wraps the error in the new APIError.
// It returns false if neither Status nor googleapi.Error can be parsed.
func ParseError(err error, wrap bool) (*APIError, bool) {
if err == nil {
return nil, false
}
ae := APIError{}
if wrap {
ae = APIError{err: err}
}
if !ae.setDetailsFromError(err) {
return nil, false
}
return &ae, true
}
// parseDetails accepts a slice of interface{} that should be backed by some
// sort of proto.Message that can be cast to the google/rpc/error_details.proto
// types.
//
// This is for internal use only.
func parseDetails(details []interface{}) ErrDetails {
var ed ErrDetails
for _, d := range details {
switch d := d.(type) {
case *errdetails.ErrorInfo:
ed.ErrorInfo = d
case *errdetails.BadRequest:
ed.BadRequest = d
case *errdetails.PreconditionFailure:
ed.PreconditionFailure = d
case *errdetails.QuotaFailure:
ed.QuotaFailure = d
case *errdetails.RetryInfo:
ed.RetryInfo = d
case *errdetails.ResourceInfo:
ed.ResourceInfo = d
case *errdetails.RequestInfo:
ed.RequestInfo = d
case *errdetails.DebugInfo:
ed.DebugInfo = d
case *errdetails.Help:
ed.Help = d
case *errdetails.LocalizedMessage:
ed.LocalizedMessage = d
default:
ed.Unknown = append(ed.Unknown, d)
}
}
return ed
}
// parseHTTPDetails will convert the given googleapi.Error into the protobuf
// representation then parse the Any values that contain the error details.
//
// This is for internal use only.
func parseHTTPDetails(gae *googleapi.Error) ErrDetails {
e := &jsonerror.Error{}
if err := protojson.Unmarshal([]byte(gae.Body), e); err != nil {
// If the error body does not conform to the error schema, ignore it
// altogther. See https://cloud.google.com/apis/design/errors#http_mapping.
return ErrDetails{}
}
// Coerce the Any messages into proto.Message then parse the details.
details := []interface{}{}
for _, any := range e.GetError().GetDetails() {
m, err := any.UnmarshalNew()
if err != nil {
// Ignore malformed Any values.
continue
}
details = append(details, m)
}
return parseDetails(details)
}
// HTTPCode returns the underlying HTTP response status code. This method returns
// `-1` if the underlying error is a [google.golang.org/grpc/status.Status]. To
// check gRPC error codes use [google.golang.org/grpc/status.Code].
func (a *APIError) HTTPCode() int {
if a.httpErr == nil {
return -1
}
return a.httpErr.Code
}

View File

@@ -0,0 +1,30 @@
# HTTP JSON Error Schema
The `error.proto` represents the HTTP-JSON schema used by Google APIs to convey
error payloads as described by https://cloud.google.com/apis/design/errors#http_mapping.
This package is for internal parsing logic only and should not be used in any
other context.
## Regeneration
To regenerate the protobuf Go code you will need the following:
* A local copy of [googleapis], the absolute path to which should be exported to
the environment variable `GOOGLEAPIS`
* The protobuf compiler [protoc]
* The Go [protobuf plugin]
* The [goimports] tool
From this directory run the following command:
```sh
protoc -I $GOOGLEAPIS -I. --go_out=. --go_opt=module=github.com/googleapis/gax-go/v2/apierror/internal/proto error.proto
goimports -w .
```
Note: the `module` plugin option ensures the generated code is placed in this
directory, and not in several nested directories defined by `go_package` option.
[googleapis]: https://github.com/googleapis/googleapis
[protoc]: https://github.com/protocolbuffers/protobuf#protocol-compiler-installation
[protobuf plugin]: https://developers.google.com/protocol-buffers/docs/reference/go-generated
[goimports]: https://pkg.go.dev/golang.org/x/tools/cmd/goimports

View File

@@ -0,0 +1,256 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.17.3
// source: custom_error.proto
package jsonerror
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Error code for `CustomError`.
type CustomError_CustomErrorCode int32
const (
// Default error.
CustomError_CUSTOM_ERROR_CODE_UNSPECIFIED CustomError_CustomErrorCode = 0
// Too many foo.
CustomError_TOO_MANY_FOO CustomError_CustomErrorCode = 1
// Not enough foo.
CustomError_NOT_ENOUGH_FOO CustomError_CustomErrorCode = 2
// Catastrophic error.
CustomError_UNIVERSE_WAS_DESTROYED CustomError_CustomErrorCode = 3
)
// Enum value maps for CustomError_CustomErrorCode.
var (
CustomError_CustomErrorCode_name = map[int32]string{
0: "CUSTOM_ERROR_CODE_UNSPECIFIED",
1: "TOO_MANY_FOO",
2: "NOT_ENOUGH_FOO",
3: "UNIVERSE_WAS_DESTROYED",
}
CustomError_CustomErrorCode_value = map[string]int32{
"CUSTOM_ERROR_CODE_UNSPECIFIED": 0,
"TOO_MANY_FOO": 1,
"NOT_ENOUGH_FOO": 2,
"UNIVERSE_WAS_DESTROYED": 3,
}
)
func (x CustomError_CustomErrorCode) Enum() *CustomError_CustomErrorCode {
p := new(CustomError_CustomErrorCode)
*p = x
return p
}
func (x CustomError_CustomErrorCode) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (CustomError_CustomErrorCode) Descriptor() protoreflect.EnumDescriptor {
return file_custom_error_proto_enumTypes[0].Descriptor()
}
func (CustomError_CustomErrorCode) Type() protoreflect.EnumType {
return &file_custom_error_proto_enumTypes[0]
}
func (x CustomError_CustomErrorCode) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use CustomError_CustomErrorCode.Descriptor instead.
func (CustomError_CustomErrorCode) EnumDescriptor() ([]byte, []int) {
return file_custom_error_proto_rawDescGZIP(), []int{0, 0}
}
// CustomError is an example of a custom error message which may be included
// in an rpc status. It is not meant to reflect a standard error.
type CustomError struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Error code specific to the custom API being invoked.
Code CustomError_CustomErrorCode `protobuf:"varint,1,opt,name=code,proto3,enum=error.CustomError_CustomErrorCode" json:"code,omitempty"`
// Name of the failed entity.
Entity string `protobuf:"bytes,2,opt,name=entity,proto3" json:"entity,omitempty"`
// Message that describes the error.
ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
}
func (x *CustomError) Reset() {
*x = CustomError{}
if protoimpl.UnsafeEnabled {
mi := &file_custom_error_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CustomError) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CustomError) ProtoMessage() {}
func (x *CustomError) ProtoReflect() protoreflect.Message {
mi := &file_custom_error_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CustomError.ProtoReflect.Descriptor instead.
func (*CustomError) Descriptor() ([]byte, []int) {
return file_custom_error_proto_rawDescGZIP(), []int{0}
}
func (x *CustomError) GetCode() CustomError_CustomErrorCode {
if x != nil {
return x.Code
}
return CustomError_CUSTOM_ERROR_CODE_UNSPECIFIED
}
func (x *CustomError) GetEntity() string {
if x != nil {
return x.Entity
}
return ""
}
func (x *CustomError) GetErrorMessage() string {
if x != nil {
return x.ErrorMessage
}
return ""
}
var File_custom_error_proto protoreflect.FileDescriptor
var file_custom_error_proto_rawDesc = []byte{
0x0a, 0x12, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xfa, 0x01, 0x0a, 0x0b,
0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x36, 0x0a, 0x04, 0x63,
0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x43, 0x75,
0x73, 0x74, 0x6f, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63,
0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x22, 0x76, 0x0a, 0x0f, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43,
0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x45, 0x52,
0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x4f, 0x4f, 0x5f, 0x4d, 0x41,
0x4e, 0x59, 0x5f, 0x46, 0x4f, 0x4f, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4e, 0x4f, 0x54, 0x5f,
0x45, 0x4e, 0x4f, 0x55, 0x47, 0x48, 0x5f, 0x46, 0x4f, 0x4f, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16,
0x55, 0x4e, 0x49, 0x56, 0x45, 0x52, 0x53, 0x45, 0x5f, 0x57, 0x41, 0x53, 0x5f, 0x44, 0x45, 0x53,
0x54, 0x52, 0x4f, 0x59, 0x45, 0x44, 0x10, 0x03, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69,
0x73, 0x2f, 0x67, 0x61, 0x78, 0x2d, 0x67, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x70, 0x69, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x3b, 0x6a, 0x73, 0x6f, 0x6e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_custom_error_proto_rawDescOnce sync.Once
file_custom_error_proto_rawDescData = file_custom_error_proto_rawDesc
)
func file_custom_error_proto_rawDescGZIP() []byte {
file_custom_error_proto_rawDescOnce.Do(func() {
file_custom_error_proto_rawDescData = protoimpl.X.CompressGZIP(file_custom_error_proto_rawDescData)
})
return file_custom_error_proto_rawDescData
}
var file_custom_error_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_custom_error_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_custom_error_proto_goTypes = []interface{}{
(CustomError_CustomErrorCode)(0), // 0: error.CustomError.CustomErrorCode
(*CustomError)(nil), // 1: error.CustomError
}
var file_custom_error_proto_depIdxs = []int32{
0, // 0: error.CustomError.code:type_name -> error.CustomError.CustomErrorCode
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_custom_error_proto_init() }
func file_custom_error_proto_init() {
if File_custom_error_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_custom_error_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CustomError); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_custom_error_proto_rawDesc,
NumEnums: 1,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_custom_error_proto_goTypes,
DependencyIndexes: file_custom_error_proto_depIdxs,
EnumInfos: file_custom_error_proto_enumTypes,
MessageInfos: file_custom_error_proto_msgTypes,
}.Build()
File_custom_error_proto = out.File
file_custom_error_proto_rawDesc = nil
file_custom_error_proto_goTypes = nil
file_custom_error_proto_depIdxs = nil
}

View File

@@ -0,0 +1,50 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package error;
option go_package = "github.com/googleapis/gax-go/v2/apierror/internal/proto;jsonerror";
// CustomError is an example of a custom error message which may be included
// in an rpc status. It is not meant to reflect a standard error.
message CustomError {
// Error code for `CustomError`.
enum CustomErrorCode {
// Default error.
CUSTOM_ERROR_CODE_UNSPECIFIED = 0;
// Too many foo.
TOO_MANY_FOO = 1;
// Not enough foo.
NOT_ENOUGH_FOO = 2;
// Catastrophic error.
UNIVERSE_WAS_DESTROYED = 3;
}
// Error code specific to the custom API being invoked.
CustomErrorCode code = 1;
// Name of the failed entity.
string entity = 2;
// Message that describes the error.
string error_message = 3;
}

View File

@@ -0,0 +1,280 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.15.8
// source: apierror/internal/proto/error.proto
package jsonerror
import (
reflect "reflect"
sync "sync"
code "google.golang.org/genproto/googleapis/rpc/code"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// The error format v2 for Google JSON REST APIs.
// Copied from https://cloud.google.com/apis/design/errors#http_mapping.
//
// NOTE: This schema is not used for other wire protocols.
type Error struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The actual error payload. The nested message structure is for backward
// compatibility with Google API client libraries. It also makes the error
// more readable to developers.
Error *Error_Status `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
}
func (x *Error) Reset() {
*x = Error{}
if protoimpl.UnsafeEnabled {
mi := &file_apierror_internal_proto_error_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Error) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Error) ProtoMessage() {}
func (x *Error) ProtoReflect() protoreflect.Message {
mi := &file_apierror_internal_proto_error_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Error.ProtoReflect.Descriptor instead.
func (*Error) Descriptor() ([]byte, []int) {
return file_apierror_internal_proto_error_proto_rawDescGZIP(), []int{0}
}
func (x *Error) GetError() *Error_Status {
if x != nil {
return x.Error
}
return nil
}
// This message has the same semantics as `google.rpc.Status`. It uses HTTP
// status code instead of gRPC status code. It has an extra field `status`
// for backward compatibility with Google API Client Libraries.
type Error_Status struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The HTTP status code that corresponds to `google.rpc.Status.code`.
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
// This corresponds to `google.rpc.Status.message`.
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
// This is the enum version for `google.rpc.Status.code`.
Status code.Code `protobuf:"varint,4,opt,name=status,proto3,enum=google.rpc.Code" json:"status,omitempty"`
// This corresponds to `google.rpc.Status.details`.
Details []*anypb.Any `protobuf:"bytes,5,rep,name=details,proto3" json:"details,omitempty"`
}
func (x *Error_Status) Reset() {
*x = Error_Status{}
if protoimpl.UnsafeEnabled {
mi := &file_apierror_internal_proto_error_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Error_Status) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Error_Status) ProtoMessage() {}
func (x *Error_Status) ProtoReflect() protoreflect.Message {
mi := &file_apierror_internal_proto_error_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Error_Status.ProtoReflect.Descriptor instead.
func (*Error_Status) Descriptor() ([]byte, []int) {
return file_apierror_internal_proto_error_proto_rawDescGZIP(), []int{0, 0}
}
func (x *Error_Status) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *Error_Status) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *Error_Status) GetStatus() code.Code {
if x != nil {
return x.Status
}
return code.Code(0)
}
func (x *Error_Status) GetDetails() []*anypb.Any {
if x != nil {
return x.Details
}
return nil
}
var File_apierror_internal_proto_error_proto protoreflect.FileDescriptor
var file_apierror_internal_proto_error_proto_rawDesc = []byte{
0x0a, 0x23, 0x61, 0x70, 0x69, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x19, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc5,
0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x1a, 0x90, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12,
0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f,
0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c,
0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64,
0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f,
0x67, 0x61, 0x78, 0x2d, 0x67, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x70, 0x69, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x3b, 0x6a, 0x73, 0x6f, 0x6e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_apierror_internal_proto_error_proto_rawDescOnce sync.Once
file_apierror_internal_proto_error_proto_rawDescData = file_apierror_internal_proto_error_proto_rawDesc
)
func file_apierror_internal_proto_error_proto_rawDescGZIP() []byte {
file_apierror_internal_proto_error_proto_rawDescOnce.Do(func() {
file_apierror_internal_proto_error_proto_rawDescData = protoimpl.X.CompressGZIP(file_apierror_internal_proto_error_proto_rawDescData)
})
return file_apierror_internal_proto_error_proto_rawDescData
}
var file_apierror_internal_proto_error_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_apierror_internal_proto_error_proto_goTypes = []interface{}{
(*Error)(nil), // 0: error.Error
(*Error_Status)(nil), // 1: error.Error.Status
(code.Code)(0), // 2: google.rpc.Code
(*anypb.Any)(nil), // 3: google.protobuf.Any
}
var file_apierror_internal_proto_error_proto_depIdxs = []int32{
1, // 0: error.Error.error:type_name -> error.Error.Status
2, // 1: error.Error.Status.status:type_name -> google.rpc.Code
3, // 2: error.Error.Status.details:type_name -> google.protobuf.Any
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_apierror_internal_proto_error_proto_init() }
func file_apierror_internal_proto_error_proto_init() {
if File_apierror_internal_proto_error_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_apierror_internal_proto_error_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Error); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_apierror_internal_proto_error_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Error_Status); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_apierror_internal_proto_error_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_apierror_internal_proto_error_proto_goTypes,
DependencyIndexes: file_apierror_internal_proto_error_proto_depIdxs,
MessageInfos: file_apierror_internal_proto_error_proto_msgTypes,
}.Build()
File_apierror_internal_proto_error_proto = out.File
file_apierror_internal_proto_error_proto_rawDesc = nil
file_apierror_internal_proto_error_proto_goTypes = nil
file_apierror_internal_proto_error_proto_depIdxs = nil
}

View File

@@ -0,0 +1,46 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package error;
import "google/protobuf/any.proto";
import "google/rpc/code.proto";
option go_package = "github.com/googleapis/gax-go/v2/apierror/internal/proto;jsonerror";
// The error format v2 for Google JSON REST APIs.
// Copied from https://cloud.google.com/apis/design/errors#http_mapping.
//
// NOTE: This schema is not used for other wire protocols.
message Error {
// This message has the same semantics as `google.rpc.Status`. It uses HTTP
// status code instead of gRPC status code. It has an extra field `status`
// for backward compatibility with Google API Client Libraries.
message Status {
// The HTTP status code that corresponds to `google.rpc.Status.code`.
int32 code = 1;
// This corresponds to `google.rpc.Status.message`.
string message = 2;
// This is the enum version for `google.rpc.Status.code`.
google.rpc.Code status = 4;
// This corresponds to `google.rpc.Status.details`.
repeated google.protobuf.Any details = 5;
}
// The actual error payload. The nested message structure is for backward
// compatibility with Google API client libraries. It also makes the error
// more readable to developers.
Status error = 1;
}

268
vendor/github.com/googleapis/gax-go/v2/call_option.go generated vendored Normal file
View File

@@ -0,0 +1,268 @@
// Copyright 2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"errors"
"math/rand"
"time"
"google.golang.org/api/googleapi"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// CallOption is an option used by Invoke to control behaviors of RPC calls.
// CallOption works by modifying relevant fields of CallSettings.
type CallOption interface {
// Resolve applies the option by modifying cs.
Resolve(cs *CallSettings)
}
// Retryer is used by Invoke to determine retry behavior.
type Retryer interface {
// Retry reports whether a request should be retried and how long to pause before retrying
// if the previous attempt returned with err. Invoke never calls Retry with nil error.
Retry(err error) (pause time.Duration, shouldRetry bool)
}
type retryerOption func() Retryer
func (o retryerOption) Resolve(s *CallSettings) {
s.Retry = o
}
// WithRetry sets CallSettings.Retry to fn.
func WithRetry(fn func() Retryer) CallOption {
return retryerOption(fn)
}
// OnErrorFunc returns a Retryer that retries if and only if the previous attempt
// returns an error that satisfies shouldRetry.
//
// Pause times between retries are specified by bo. bo is only used for its
// parameters; each Retryer has its own copy.
func OnErrorFunc(bo Backoff, shouldRetry func(err error) bool) Retryer {
return &errorRetryer{
shouldRetry: shouldRetry,
backoff: bo,
}
}
type errorRetryer struct {
backoff Backoff
shouldRetry func(err error) bool
}
func (r *errorRetryer) Retry(err error) (time.Duration, bool) {
if r.shouldRetry(err) {
return r.backoff.Pause(), true
}
return 0, false
}
// OnCodes returns a Retryer that retries if and only if
// the previous attempt returns a GRPC error whose error code is stored in cc.
// Pause times between retries are specified by bo.
//
// bo is only used for its parameters; each Retryer has its own copy.
func OnCodes(cc []codes.Code, bo Backoff) Retryer {
return &boRetryer{
backoff: bo,
codes: append([]codes.Code(nil), cc...),
}
}
type boRetryer struct {
backoff Backoff
codes []codes.Code
}
func (r *boRetryer) Retry(err error) (time.Duration, bool) {
st, ok := status.FromError(err)
if !ok {
return 0, false
}
c := st.Code()
for _, rc := range r.codes {
if c == rc {
return r.backoff.Pause(), true
}
}
return 0, false
}
// OnHTTPCodes returns a Retryer that retries if and only if
// the previous attempt returns a googleapi.Error whose status code is stored in
// cc. Pause times between retries are specified by bo.
//
// bo is only used for its parameters; each Retryer has its own copy.
func OnHTTPCodes(bo Backoff, cc ...int) Retryer {
codes := make(map[int]bool, len(cc))
for _, c := range cc {
codes[c] = true
}
return &httpRetryer{
backoff: bo,
codes: codes,
}
}
type httpRetryer struct {
backoff Backoff
codes map[int]bool
}
func (r *httpRetryer) Retry(err error) (time.Duration, bool) {
var gerr *googleapi.Error
if !errors.As(err, &gerr) {
return 0, false
}
if r.codes[gerr.Code] {
return r.backoff.Pause(), true
}
return 0, false
}
// Backoff implements backoff logic for retries. The configuration for retries
// is described in https://google.aip.dev/client-libraries/4221. The current
// retry limit starts at Initial and increases by a factor of Multiplier every
// retry, but is capped at Max. The actual wait time between retries is a
// random value between 1ns and the current retry limit. The purpose of this
// random jitter is explained in
// https://www.awsarchitectureblog.com/2015/03/backoff.html.
//
// Note: MaxNumRetries / RPCDeadline is specifically not provided. These should
// be built on top of Backoff.
type Backoff struct {
// Initial is the initial value of the retry period, defaults to 1 second.
Initial time.Duration
// Max is the maximum value of the retry period, defaults to 30 seconds.
Max time.Duration
// Multiplier is the factor by which the retry period increases.
// It should be greater than 1 and defaults to 2.
Multiplier float64
// cur is the current retry period.
cur time.Duration
}
// Pause returns the next time.Duration that the caller should use to backoff.
func (bo *Backoff) Pause() time.Duration {
if bo.Initial == 0 {
bo.Initial = time.Second
}
if bo.cur == 0 {
bo.cur = bo.Initial
}
if bo.Max == 0 {
bo.Max = 30 * time.Second
}
if bo.Multiplier < 1 {
bo.Multiplier = 2
}
// Select a duration between 1ns and the current max. It might seem
// counterintuitive to have so much jitter, but
// https://www.awsarchitectureblog.com/2015/03/backoff.html argues that
// that is the best strategy.
d := time.Duration(1 + rand.Int63n(int64(bo.cur)))
bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier)
if bo.cur > bo.Max {
bo.cur = bo.Max
}
return d
}
type grpcOpt []grpc.CallOption
func (o grpcOpt) Resolve(s *CallSettings) {
s.GRPC = o
}
type pathOpt struct {
p string
}
func (p pathOpt) Resolve(s *CallSettings) {
s.Path = p.p
}
type timeoutOpt struct {
t time.Duration
}
func (t timeoutOpt) Resolve(s *CallSettings) {
s.timeout = t.t
}
// WithPath applies a Path override to the HTTP-based APICall.
//
// This is for internal use only.
func WithPath(p string) CallOption {
return &pathOpt{p: p}
}
// WithGRPCOptions allows passing gRPC call options during client creation.
func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
return grpcOpt(append([]grpc.CallOption(nil), opt...))
}
// WithTimeout is a convenience option for setting a context.WithTimeout on the
// singular context.Context used for **all** APICall attempts. Calculated from
// the start of the first APICall attempt.
// If the context.Context provided to Invoke already has a Deadline set, that
// will always be respected over the deadline calculated using this option.
func WithTimeout(t time.Duration) CallOption {
return &timeoutOpt{t: t}
}
// CallSettings allow fine-grained control over how calls are made.
type CallSettings struct {
// Retry returns a Retryer to be used to control retry logic of a method call.
// If Retry is nil or the returned Retryer is nil, the call will not be retried.
Retry func() Retryer
// CallOptions to be forwarded to GRPC.
GRPC []grpc.CallOption
// Path is an HTTP override for an APICall.
Path string
// Timeout defines the amount of time that Invoke has to complete.
// Unexported so it cannot be changed by the code in an APICall.
timeout time.Duration
}

View File

@@ -0,0 +1,100 @@
// Copyright 2023, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package callctx provides helpers for storing and retrieving values out of
// [context.Context]. These values are used by our client libraries in various
// ways across the stack.
package callctx
import (
"context"
"fmt"
)
const (
// XGoogFieldMaskHeader is the canonical header key for the [System Parameter]
// that specifies the response read mask. The value(s) for this header
// must adhere to format described in [fieldmaskpb].
//
// [System Parameter]: https://cloud.google.com/apis/docs/system-parameters
// [fieldmaskpb]: https://google.golang.org/protobuf/types/known/fieldmaskpb
XGoogFieldMaskHeader = "x-goog-fieldmask"
headerKey = contextKey("header")
)
// contextKey is a private type used to store/retrieve context values.
type contextKey string
// HeadersFromContext retrieves headers set from [SetHeaders]. These headers
// can then be cast to http.Header or metadata.MD to send along on requests.
func HeadersFromContext(ctx context.Context) map[string][]string {
m, ok := ctx.Value(headerKey).(map[string][]string)
if !ok {
return nil
}
return m
}
// SetHeaders stores key value pairs in the returned context that can later
// be retrieved by [HeadersFromContext]. Values stored in this manner will
// automatically be retrieved by client libraries and sent as outgoing headers
// on all requests. keyvals should have a corresponding value for every key
// provided. If there is an odd number of keyvals this method will panic.
func SetHeaders(ctx context.Context, keyvals ...string) context.Context {
if len(keyvals)%2 != 0 {
panic(fmt.Sprintf("callctx: an even number of key value pairs must be provided, got %d", len(keyvals)))
}
h, ok := ctx.Value(headerKey).(map[string][]string)
if !ok {
h = make(map[string][]string)
} else {
h = cloneHeaders(h)
}
for i := 0; i < len(keyvals); i = i + 2 {
h[keyvals[i]] = append(h[keyvals[i]], keyvals[i+1])
}
return context.WithValue(ctx, headerKey, h)
}
// cloneHeaders makes a new key-value map while reusing the value slices.
// As such, new values should be appended to the value slice, and modifying
// indexed values is not thread safe.
//
// TODO: Replace this with maps.Clone when Go 1.21 is the minimum version.
func cloneHeaders(h map[string][]string) map[string][]string {
c := make(map[string][]string, len(h))
for k, v := range h {
vc := make([]string, len(v))
copy(vc, v)
c[k] = vc
}
return c
}

112
vendor/github.com/googleapis/gax-go/v2/content_type.go generated vendored Normal file
View File

@@ -0,0 +1,112 @@
// Copyright 2022, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"io"
"io/ioutil"
"net/http"
)
const sniffBuffSize = 512
func newContentSniffer(r io.Reader) *contentSniffer {
return &contentSniffer{r: r}
}
// contentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader.
type contentSniffer struct {
r io.Reader
start []byte // buffer for the sniffed bytes.
err error // set to any error encountered while reading bytes to be sniffed.
ctype string // set on first sniff.
sniffed bool // set to true on first sniff.
}
func (cs *contentSniffer) Read(p []byte) (n int, err error) {
// Ensure that the content type is sniffed before any data is consumed from Reader.
_, _ = cs.ContentType()
if len(cs.start) > 0 {
n := copy(p, cs.start)
cs.start = cs.start[n:]
return n, nil
}
// We may have read some bytes into start while sniffing, even if the read ended in an error.
// We should first return those bytes, then the error.
if cs.err != nil {
return 0, cs.err
}
// Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader.
return cs.r.Read(p)
}
// ContentType returns the sniffed content type, and whether the content type was successfully sniffed.
func (cs *contentSniffer) ContentType() (string, bool) {
if cs.sniffed {
return cs.ctype, cs.ctype != ""
}
cs.sniffed = true
// If ReadAll hits EOF, it returns err==nil.
cs.start, cs.err = ioutil.ReadAll(io.LimitReader(cs.r, sniffBuffSize))
// Don't try to detect the content type based on possibly incomplete data.
if cs.err != nil {
return "", false
}
cs.ctype = http.DetectContentType(cs.start)
return cs.ctype, true
}
// DetermineContentType determines the content type of the supplied reader.
// The content of media will be sniffed to determine the content type.
// After calling DetectContentType the caller must not perform further reads on
// media, but rather read from the Reader that is returned.
func DetermineContentType(media io.Reader) (io.Reader, string) {
// For backwards compatibility, allow clients to set content
// type by providing a ContentTyper for media.
// Note: This is an anonymous interface definition copied from googleapi.ContentTyper.
if typer, ok := media.(interface {
ContentType() string
}); ok {
return media, typer.ContentType()
}
sniffer := newContentSniffer(media)
if ctype, ok := sniffer.ContentType(); ok {
return sniffer, ctype
}
// If content type could not be sniffed, reads from sniffer will eventually fail with an error.
return sniffer, ""
}

75
vendor/github.com/googleapis/gax-go/v2/feature.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2025, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"os"
"strings"
"sync"
)
var (
// featureEnabledOnce caches results for IsFeatureEnabled.
featureEnabledOnce sync.Once
featureEnabledStore map[string]bool
)
// IsFeatureEnabled checks if an experimental feature is enabled via
// environment variable. The environment variable must be prefixed with
// "GOOGLE_SDK_GO_EXPERIMENTAL_". The feature name passed to this
// function must be the suffix (e.g., "FOO" for "GOOGLE_SDK_GO_EXPERIMENTAL_FOO").
// To enable the feature, the environment variable's value must be "true",
// case-insensitive. The result for each name is cached on the first call.
func IsFeatureEnabled(name string) bool {
featureEnabledOnce.Do(func() {
featureEnabledStore = make(map[string]bool)
for _, env := range os.Environ() {
if strings.HasPrefix(env, "GOOGLE_SDK_GO_EXPERIMENTAL_") {
// Parse "KEY=VALUE"
kv := strings.SplitN(env, "=", 2)
if len(kv) == 2 && strings.ToLower(kv[1]) == "true" {
key := strings.TrimPrefix(kv[0], "GOOGLE_SDK_GO_EXPERIMENTAL_")
featureEnabledStore[key] = true
}
}
}
})
return featureEnabledStore[name]
}
// TestOnlyResetIsFeatureEnabled is for testing purposes only. It resets the cached
// feature flags, allowing environment variables to be re-read on the next call to IsFeatureEnabled.
// This function is not thread-safe; if another goroutine reads a feature after this
// function is called but before the `featureEnabledOnce` is re-initialized by IsFeatureEnabled,
// it may see an inconsistent state.
func TestOnlyResetIsFeatureEnabled() {
featureEnabledOnce = sync.Once{}
featureEnabledStore = nil
}

41
vendor/github.com/googleapis/gax-go/v2/gax.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
// Copyright 2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package gax contains a set of modules which aid the development of APIs
// for clients and servers based on gRPC and Google API conventions.
//
// Application code will rarely need to use this library directly.
// However, code generated automatically from API definition files can use it
// to simplify code generation and to provide more convenient and idiomatic API surfaces.
package gax
import "github.com/googleapis/gax-go/v2/internal"
// Version specifies the gax-go version being used.
const Version = internal.Version

200
vendor/github.com/googleapis/gax-go/v2/header.go generated vendored Normal file
View File

@@ -0,0 +1,200 @@
// Copyright 2018, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"bytes"
"context"
"fmt"
"net/http"
"runtime"
"strings"
"unicode"
"github.com/googleapis/gax-go/v2/callctx"
"google.golang.org/grpc/metadata"
)
var (
// GoVersion is a header-safe representation of the current runtime
// environment's Go version. This is for GAX consumers that need to
// report the Go runtime version in API calls.
GoVersion string
// version is a package internal global variable for testing purposes.
version = runtime.Version
)
// versionUnknown is only used when the runtime version cannot be determined.
const versionUnknown = "UNKNOWN"
func init() {
GoVersion = goVersion()
}
// goVersion returns a Go runtime version derived from the runtime environment
// that is modified to be suitable for reporting in a header, meaning it has no
// whitespace. If it is unable to determine the Go runtime version, it returns
// versionUnknown.
func goVersion() string {
const develPrefix = "devel +"
s := version()
if strings.HasPrefix(s, develPrefix) {
s = s[len(develPrefix):]
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
return s
} else if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
notSemverRune := func(r rune) bool {
return !strings.ContainsRune("0123456789.", r)
}
if strings.HasPrefix(s, "go1") {
s = s[2:]
var prerelease string
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
s, prerelease = s[:p], s[p:]
}
if strings.HasSuffix(s, ".") {
s += "0"
} else if strings.Count(s, ".") < 2 {
s += ".0"
}
if prerelease != "" {
// Some release candidates already have a dash in them.
if !strings.HasPrefix(prerelease, "-") {
prerelease = "-" + prerelease
}
s += prerelease
}
return s
}
return "UNKNOWN"
}
// XGoogHeader is for use by the Google Cloud Libraries only. See package
// [github.com/googleapis/gax-go/v2/callctx] for help setting/retrieving
// request/response headers.
//
// XGoogHeader formats key-value pairs.
// The resulting string is suitable for x-goog-api-client header.
func XGoogHeader(keyval ...string) string {
if len(keyval) == 0 {
return ""
}
if len(keyval)%2 != 0 {
panic("gax.Header: odd argument count")
}
var buf bytes.Buffer
for i := 0; i < len(keyval); i += 2 {
buf.WriteByte(' ')
buf.WriteString(keyval[i])
buf.WriteByte('/')
buf.WriteString(keyval[i+1])
}
return buf.String()[1:]
}
// InsertMetadataIntoOutgoingContext is for use by the Google Cloud Libraries
// only. See package [github.com/googleapis/gax-go/v2/callctx] for help
// setting/retrieving request/response headers.
//
// InsertMetadataIntoOutgoingContext returns a new context that merges the
// provided keyvals metadata pairs with any existing metadata/headers in the
// provided context. keyvals should have a corresponding value for every key
// provided. If there is an odd number of keyvals this method will panic.
// Existing values for keys will not be overwritten, instead provided values
// will be appended to the list of existing values.
func InsertMetadataIntoOutgoingContext(ctx context.Context, keyvals ...string) context.Context {
return metadata.NewOutgoingContext(ctx, insertMetadata(ctx, keyvals...))
}
// BuildHeaders is for use by the Google Cloud Libraries only. See package
// [github.com/googleapis/gax-go/v2/callctx] for help setting/retrieving
// request/response headers.
//
// BuildHeaders returns a new http.Header that merges the provided
// keyvals header pairs with any existing metadata/headers in the provided
// context. keyvals should have a corresponding value for every key provided.
// If there is an odd number of keyvals this method will panic.
// Existing values for keys will not be overwritten, instead provided values
// will be appended to the list of existing values.
func BuildHeaders(ctx context.Context, keyvals ...string) http.Header {
return http.Header(insertMetadata(ctx, keyvals...))
}
func insertMetadata(ctx context.Context, keyvals ...string) metadata.MD {
if len(keyvals)%2 != 0 {
panic(fmt.Sprintf("gax: an even number of key value pairs must be provided, got %d", len(keyvals)))
}
out, ok := metadata.FromOutgoingContext(ctx)
if !ok {
out = metadata.MD(make(map[string][]string))
}
headers := callctx.HeadersFromContext(ctx)
// x-goog-api-client is a special case that we want to make sure gets merged
// into a single header.
const xGoogHeader = "x-goog-api-client"
var mergedXgoogHeader strings.Builder
for k, vals := range headers {
if k == xGoogHeader {
// Merge all values for the x-goog-api-client header set on the ctx.
for _, v := range vals {
mergedXgoogHeader.WriteString(v)
mergedXgoogHeader.WriteRune(' ')
}
continue
}
out[k] = append(out[k], vals...)
}
for i := 0; i < len(keyvals); i = i + 2 {
out[keyvals[i]] = append(out[keyvals[i]], keyvals[i+1])
if keyvals[i] == xGoogHeader {
// Merge the x-goog-api-client header values set on the ctx with any
// values passed in for it from the client.
mergedXgoogHeader.WriteString(keyvals[i+1])
mergedXgoogHeader.WriteRune(' ')
}
}
// Add the x goog header back in, replacing the separate values that were set.
if mergedXgoogHeader.Len() > 0 {
out[xGoogHeader] = []string{mergedXgoogHeader.String()[:mergedXgoogHeader.Len()-1]}
}
return out
}

View File

@@ -0,0 +1,20 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by gapicgen. DO NOT EDIT.
package internal
// Version is the current tagged release of the library.
const Version = "2.16.0"

View File

@@ -0,0 +1,134 @@
// Copyright 2024, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package internal provides some common logic and types to other logging
// sub-packages.
package internal
import (
"context"
"io"
"log/slog"
"os"
"strings"
"time"
)
const (
// LoggingLevelEnvVar is the environment variable used to enable logging
// at a particular level.
LoggingLevelEnvVar = "GOOGLE_SDK_GO_LOGGING_LEVEL"
googLvlKey = "severity"
googMsgKey = "message"
googSourceKey = "sourceLocation"
googTimeKey = "timestamp"
)
// NewLoggerWithWriter is exposed for testing.
func NewLoggerWithWriter(w io.Writer) *slog.Logger {
lvl, loggingEnabled := checkLoggingLevel()
if !loggingEnabled {
return slog.New(noOpHandler{})
}
return slog.New(newGCPSlogHandler(lvl, w))
}
// checkLoggingLevel returned the configured logging level and whether or not
// logging is enabled.
func checkLoggingLevel() (slog.Leveler, bool) {
sLevel := strings.ToLower(os.Getenv(LoggingLevelEnvVar))
var level slog.Level
switch sLevel {
case "debug":
level = slog.LevelDebug
case "info":
level = slog.LevelInfo
case "warn":
level = slog.LevelWarn
case "error":
level = slog.LevelError
default:
return nil, false
}
return level, true
}
// newGCPSlogHandler returns a Handler that is configured to output in a JSON
// format with well-known keys. For more information on this format see
// https://cloud.google.com/logging/docs/agent/logging/configuration#special-fields.
func newGCPSlogHandler(lvl slog.Leveler, w io.Writer) slog.Handler {
return slog.NewJSONHandler(w, &slog.HandlerOptions{
Level: lvl,
ReplaceAttr: replaceAttr,
})
}
// replaceAttr remaps default Go logging keys to match what is expected in
// cloud logging.
func replaceAttr(groups []string, a slog.Attr) slog.Attr {
if groups == nil {
if a.Key == slog.LevelKey {
a.Key = googLvlKey
return a
} else if a.Key == slog.MessageKey {
a.Key = googMsgKey
return a
} else if a.Key == slog.SourceKey {
a.Key = googSourceKey
return a
} else if a.Key == slog.TimeKey {
a.Key = googTimeKey
if a.Value.Kind() == slog.KindTime {
a.Value = slog.StringValue(a.Value.Time().Format(time.RFC3339))
}
return a
}
}
return a
}
// The handler returned if logging is not enabled.
type noOpHandler struct{}
func (h noOpHandler) Enabled(_ context.Context, _ slog.Level) bool {
return false
}
func (h noOpHandler) Handle(_ context.Context, _ slog.Record) error {
return nil
}
func (h noOpHandler) WithAttrs(_ []slog.Attr) slog.Handler {
return h
}
func (h noOpHandler) WithGroup(_ string) slog.Handler {
return h
}

View File

@@ -0,0 +1,154 @@
// Copyright 2024, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package internallog in intended for internal use by generated clients only.
package internallog
import (
"bytes"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"github.com/googleapis/gax-go/v2/internallog/internal"
)
// New returns a new [slog.Logger] default logger, or the provided logger if
// non-nil. The returned logger will be a no-op logger unless the environment
// variable GOOGLE_SDK_GO_LOGGING_LEVEL is set.
func New(l *slog.Logger) *slog.Logger {
if l != nil {
return l
}
return internal.NewLoggerWithWriter(os.Stderr)
}
// HTTPRequest returns a lazily evaluated [slog.LogValuer] for a
// [http.Request] and the associated body.
func HTTPRequest(req *http.Request, body []byte) slog.LogValuer {
return &request{
req: req,
payload: body,
}
}
type request struct {
req *http.Request
payload []byte
}
func (r *request) LogValue() slog.Value {
if r == nil || r.req == nil {
return slog.Value{}
}
var groupValueAttrs []slog.Attr
groupValueAttrs = append(groupValueAttrs, slog.String("method", r.req.Method))
groupValueAttrs = append(groupValueAttrs, slog.String("url", r.req.URL.String()))
var headerAttr []slog.Attr
for k, val := range r.req.Header {
headerAttr = append(headerAttr, slog.String(k, strings.Join(val, ",")))
}
if len(headerAttr) > 0 {
groupValueAttrs = append(groupValueAttrs, slog.Any("headers", headerAttr))
}
if len(r.payload) > 0 {
if attr, ok := processPayload(r.payload); ok {
groupValueAttrs = append(groupValueAttrs, attr)
}
}
return slog.GroupValue(groupValueAttrs...)
}
// HTTPResponse returns a lazily evaluated [slog.LogValuer] for a
// [http.Response] and the associated body.
func HTTPResponse(resp *http.Response, body []byte) slog.LogValuer {
return &response{
resp: resp,
payload: body,
}
}
type response struct {
resp *http.Response
payload []byte
}
func (r *response) LogValue() slog.Value {
if r == nil {
return slog.Value{}
}
var groupValueAttrs []slog.Attr
groupValueAttrs = append(groupValueAttrs, slog.String("status", fmt.Sprint(r.resp.StatusCode)))
var headerAttr []slog.Attr
for k, val := range r.resp.Header {
headerAttr = append(headerAttr, slog.String(k, strings.Join(val, ",")))
}
if len(headerAttr) > 0 {
groupValueAttrs = append(groupValueAttrs, slog.Any("headers", headerAttr))
}
if len(r.payload) > 0 {
if attr, ok := processPayload(r.payload); ok {
groupValueAttrs = append(groupValueAttrs, attr)
}
}
return slog.GroupValue(groupValueAttrs...)
}
func processPayload(payload []byte) (slog.Attr, bool) {
peekChar := payload[0]
if peekChar == '{' {
// JSON object
var m map[string]any
if err := json.Unmarshal(payload, &m); err == nil {
return slog.Any("payload", m), true
}
} else if peekChar == '[' {
// JSON array
var m []any
if err := json.Unmarshal(payload, &m); err == nil {
return slog.Any("payload", m), true
}
} else {
// Everything else
buf := &bytes.Buffer{}
if err := json.Compact(buf, payload); err != nil {
// Write raw payload incase of error
buf.Write(payload)
}
return slog.String("payload", buf.String()), true
}
return slog.Attr{}, false
}

114
vendor/github.com/googleapis/gax-go/v2/invoke.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
// Copyright 2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"context"
"strings"
"time"
"github.com/googleapis/gax-go/v2/apierror"
)
// APICall is a user defined call stub.
type APICall func(context.Context, CallSettings) error
// Invoke calls the given APICall, performing retries as specified by opts, if
// any.
func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
var settings CallSettings
for _, opt := range opts {
opt.Resolve(&settings)
}
return invoke(ctx, call, settings, Sleep)
}
// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
// If interrupted, Sleep returns ctx.Err().
func Sleep(ctx context.Context, d time.Duration) error {
t := time.NewTimer(d)
select {
case <-ctx.Done():
t.Stop()
return ctx.Err()
case <-t.C:
return nil
}
}
type sleeper func(ctx context.Context, d time.Duration) error
// invoke implements Invoke, taking an additional sleeper argument for testing.
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
var retryer Retryer
// Only use the value provided via WithTimeout if the context doesn't
// already have a deadline. This is important for backwards compatibility if
// the user already set a deadline on the context given to Invoke.
if _, ok := ctx.Deadline(); !ok && settings.timeout != 0 {
c, cc := context.WithTimeout(ctx, settings.timeout)
defer cc()
ctx = c
}
for {
err := call(ctx, settings)
if err == nil {
return nil
}
// Never retry permanent certificate errors. (e.x. if ca-certificates
// are not installed). We should only make very few, targeted
// exceptions: many (other) status=Unavailable should be retried, such
// as if there's a network hiccup, or the internet goes out for a
// minute. This is also why here we are doing string parsing instead of
// simply making Unavailable a non-retried code elsewhere.
if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
return err
}
if apierr, ok := apierror.FromError(err); ok {
err = apierr
}
if settings.Retry == nil {
return err
}
if retryer == nil {
if r := settings.Retry(); r != nil {
retryer = r
} else {
return err
}
}
if d, ok := retryer.Retry(err); !ok {
return err
} else if err = sp(ctx, d); err != nil {
return err
}
}
}

View File

@@ -0,0 +1,127 @@
// Copyright 2022, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"encoding/json"
"errors"
"io"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
var (
arrayOpen = json.Delim('[')
arrayClose = json.Delim(']')
errBadOpening = errors.New("unexpected opening token, expected '['")
)
// ProtoJSONStream represents a wrapper for consuming a stream of protobuf
// messages encoded using protobuf-JSON format. More information on this format
// can be found at https://developers.google.com/protocol-buffers/docs/proto3#json.
// The stream must appear as a comma-delimited, JSON array of obbjects with
// opening and closing square braces.
//
// This is for internal use only.
type ProtoJSONStream struct {
first, closed bool
reader io.ReadCloser
stream *json.Decoder
typ protoreflect.MessageType
}
// NewProtoJSONStreamReader accepts a stream of bytes via an io.ReadCloser that are
// protobuf-JSON encoded protobuf messages of the given type. The ProtoJSONStream
// must be closed when done.
//
// This is for internal use only.
func NewProtoJSONStreamReader(rc io.ReadCloser, typ protoreflect.MessageType) *ProtoJSONStream {
return &ProtoJSONStream{
first: true,
reader: rc,
stream: json.NewDecoder(rc),
typ: typ,
}
}
// Recv decodes the next protobuf message in the stream or returns io.EOF if
// the stream is done. It is not safe to call Recv on the same stream from
// different goroutines, just like it is not safe to do so with a single gRPC
// stream. Type-cast the protobuf message returned to the type provided at
// ProtoJSONStream creation.
// Calls to Recv after calling Close will produce io.EOF.
func (s *ProtoJSONStream) Recv() (proto.Message, error) {
if s.closed {
return nil, io.EOF
}
if s.first {
s.first = false
// Consume the opening '[' so Decode gets one object at a time.
if t, err := s.stream.Token(); err != nil {
return nil, err
} else if t != arrayOpen {
return nil, errBadOpening
}
}
// Capture the next block of data for the item (a JSON object) in the stream.
var raw json.RawMessage
if err := s.stream.Decode(&raw); err != nil {
e := err
// To avoid checking the first token of each stream, just attempt to
// Decode the next blob and if that fails, double check if it is just
// the closing token ']'. If it is the closing, return io.EOF. If it
// isn't, return the original error.
if t, _ := s.stream.Token(); t == arrayClose {
e = io.EOF
}
return nil, e
}
// Initialize a new instance of the protobuf message to unmarshal the
// raw data into.
m := s.typ.New().Interface()
unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
err := unm.Unmarshal(raw, m)
return m, err
}
// Close closes the stream so that resources are cleaned up.
func (s *ProtoJSONStream) Close() error {
// Dereference the *json.Decoder so that the memory is gc'd.
s.stream = nil
s.closed = true
return s.reader.Close()
}

View File

@@ -0,0 +1,10 @@
{
"release-type": "go-yoshi",
"separate-pull-requests": true,
"include-component-in-tag": false,
"packages": {
"v2": {
"component": "v2"
}
}
}