@@ -16,6 +16,7 @@ package bigquery
16
16
17
17
import (
18
18
"context"
19
+ "errors"
19
20
"fmt"
20
21
"io"
21
22
"net/http"
@@ -29,6 +30,7 @@ import (
29
30
bq "google.golang.org/api/bigquery/v2"
30
31
"google.golang.org/api/googleapi"
31
32
"google.golang.org/api/option"
33
+ "google.golang.org/api/transport"
32
34
)
33
35
34
36
const (
@@ -56,8 +58,20 @@ type Client struct {
56
58
bqs * bq.Service
57
59
}
58
60
61
+ // DetectProjectID is a sentinel value that instructs NewClient to detect the
62
+ // project ID. It is given in place of the projectID argument. NewClient will
63
+ // use the project ID from the given credentials or the default credentials
64
+ // (https://developers.google.com/accounts/docs/application-default-credentials)
65
+ // if no credentials were provided. When providing credentials, not all
66
+ // options will allow NewClient to extract the project ID. Specifically a JWT
67
+ // does not have the project ID encoded.
68
+ const DetectProjectID = "*detect-project-id*"
69
+
59
70
// NewClient constructs a new Client which can perform BigQuery operations.
60
71
// Operations performed via the client are billed to the specified GCP project.
72
+ //
73
+ // If the project ID is set to DetectProjectID, NewClient will attempt to detect
74
+ // the project ID from credentials.
61
75
func NewClient (ctx context.Context , projectID string , opts ... option.ClientOption ) (* Client , error ) {
62
76
o := []option.ClientOption {
63
77
option .WithScopes (Scope ),
@@ -68,20 +82,45 @@ func NewClient(ctx context.Context, projectID string, opts ...option.ClientOptio
68
82
if err != nil {
69
83
return nil , fmt .Errorf ("bigquery: constructing client: %v" , err )
70
84
}
85
+
86
+ if projectID == DetectProjectID {
87
+ projectID , err = detectProjectID (ctx , opts ... )
88
+ if err != nil {
89
+ return nil , fmt .Errorf ("failed to detect project: %v" , err )
90
+ }
91
+ }
92
+
71
93
c := & Client {
72
94
projectID : projectID ,
73
95
bqs : bqs ,
74
96
}
75
97
return c , nil
76
98
}
77
99
100
+ // Project returns the project ID or number for this instance of the client, which may have
101
+ // either been explicitly specified or autodetected.
102
+ func (c * Client ) Project () string {
103
+ return c .projectID
104
+ }
105
+
78
106
// Close closes any resources held by the client.
79
107
// Close should be called when the client is no longer needed.
80
108
// It need not be called at program exit.
81
109
func (c * Client ) Close () error {
82
110
return nil
83
111
}
84
112
113
+ func detectProjectID (ctx context.Context , opts ... option.ClientOption ) (string , error ) {
114
+ creds , err := transport .Creds (ctx , opts ... )
115
+ if err != nil {
116
+ return "" , fmt .Errorf ("fetching creds: %v" , err )
117
+ }
118
+ if creds .ProjectID == "" {
119
+ return "" , errors .New ("credentials did not provide a valid ProjectID" )
120
+ }
121
+ return creds .ProjectID , nil
122
+ }
123
+
85
124
// Calls the Jobs.Insert RPC and returns a Job.
86
125
func (c * Client ) insertJob (ctx context.Context , job * bq.Job , media io.Reader ) (* Job , error ) {
87
126
call := c .bqs .Jobs .Insert (c .projectID , job ).Context (ctx )
0 commit comments