transport: populate QuotaProject from Creds.JSON["quota_project_id"]
Automatically populates the quota project from the quota_project_id specified
by JSON credentials.
Change-Id: I1e56ed7955fc5e84fae287d3958f7079214d67ee
Reviewed-on: https://code-review.googlesource.com/c/google-api-go-client/+/43150
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jean de Klerk <deklerk@google.com>
diff --git a/internal/creds.go b/internal/creds.go
index a6f9a2d..75e9445 100644
--- a/internal/creds.go
+++ b/internal/creds.go
@@ -90,3 +90,16 @@
}
return google.JWTAccessTokenSourceFromJSON(data, audience)
}
+
+// QuotaProjectFromCreds returns the quota project from the JSON blob in the provided credentials.
+//
+// NOTE(cbro): consider promoting this to a field on google.Credentials.
+func QuotaProjectFromCreds(cred *google.Credentials) string {
+ var v struct {
+ QuotaProject string `json:"quota_project_id"`
+ }
+ if err := json.Unmarshal(cred.JSON, &v); err != nil {
+ return ""
+ }
+ return v.QuotaProject
+}
diff --git a/internal/creds_test.go b/internal/creds_test.go
index 11c7dcc..c80a5df 100644
--- a/internal/creds_test.go
+++ b/internal/creds_test.go
@@ -115,3 +115,29 @@
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dumba-504%40appspot.gserviceaccount.com"
}`
+
+func TestQuotaProjectFromCreds(t *testing.T) {
+ ctx := context.Background()
+
+ cred, err := credentialsFromJSON(ctx, []byte(validServiceAccountJSON), "foo.googleapis.com", nil, nil)
+ if err != nil {
+ t.Fatalf("got %v, wanted no error", err)
+ }
+ if want, got := "", QuotaProjectFromCreds(cred); want != got {
+ t.Errorf("QuotaProjectFromCreds(validServiceAccountJSON): want %q, got %q", want, got)
+ }
+
+ quotaProjectJSON := []byte(`
+{
+ "type": "authorized_user",
+ "quota_project_id": "foobar"
+}`)
+
+ cred, err = credentialsFromJSON(ctx, []byte(quotaProjectJSON), "foo.googleapis.com", nil, nil)
+ if err != nil {
+ t.Fatalf("got %v, wanted no error", err)
+ }
+ if want, got := "foobar", QuotaProjectFromCreds(cred); want != got {
+ t.Errorf("QuotaProjectFromCreds(quotaProjectJSON): want %q, got %q", want, got)
+ }
+}
diff --git a/transport/grpc/dial.go b/transport/grpc/dial.go
index 7526e68..e823f78 100644
--- a/transport/grpc/dial.go
+++ b/transport/grpc/dial.go
@@ -71,6 +71,11 @@
if err != nil {
return nil, err
}
+
+ if o.QuotaProject == "" {
+ o.QuotaProject = internal.QuotaProjectFromCreds(creds)
+ }
+
// Attempt Direct Path only if:
// * The endpoint is a host:port (or dns:///host:port).
// * Credentials are obtained via GCE metadata server, using the default
diff --git a/transport/http/dial.go b/transport/http/dial.go
index 1ef67ce..c2ca3b5 100644
--- a/transport/http/dial.go
+++ b/transport/http/dial.go
@@ -53,13 +53,13 @@
}
func newTransport(ctx context.Context, base http.RoundTripper, settings *internal.DialSettings) (http.RoundTripper, error) {
- trans := base
- trans = parameterTransport{
- base: trans,
+ paramTransport := ¶meterTransport{
+ base: base,
userAgent: settings.UserAgent,
quotaProject: settings.QuotaProject,
requestReason: settings.RequestReason,
}
+ var trans http.RoundTripper = paramTransport
trans = addOCTransport(trans, settings)
switch {
case settings.NoAuth:
@@ -74,6 +74,9 @@
if err != nil {
return nil, err
}
+ if paramTransport.quotaProject == "" {
+ paramTransport.quotaProject = internal.QuotaProjectFromCreds(creds)
+ }
trans = &oauth2.Transport{
Base: trans,
Source: creds.TokenSource,
@@ -104,7 +107,7 @@
base http.RoundTripper
}
-func (t parameterTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+func (t *parameterTransport) RoundTrip(req *http.Request) (*http.Response, error) {
rt := t.base
if rt == nil {
return nil, errors.New("transport: no Transport specified")