Skip to content

Commit 6d07eca

Browse files
authored
feat(bigquery): allow construction of jobs from other projects (#5048)
Adds a `JobfromProject()` method to allow users to get metadata from a job that was created within another project. Fixes: #4228
1 parent fd062be commit 6d07eca

File tree

2 files changed

+80
-5
lines changed

2 files changed

+80
-5
lines changed

Diff for: bigquery/integration_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,70 @@ func TestIntegration_DetectProjectID(t *testing.T) {
250250
}
251251
}
252252

253+
func TestIntegration_JobFrom(t *testing.T) {
254+
if client == nil {
255+
t.Skip("Integration tests skipped")
256+
}
257+
ctx := context.Background()
258+
259+
// Create a job we can use for referencing.
260+
q := client.Query("SELECT 123 as foo")
261+
it, err := q.Read(ctx)
262+
if err != nil {
263+
t.Fatalf("failed to run test query: %v", err)
264+
}
265+
want := it.SourceJob()
266+
267+
// establish a new client that's pointed at an invalid project/location.
268+
otherClient, err := NewClient(ctx, "bad-project-id")
269+
if err != nil {
270+
t.Fatalf("failed to create other client: %v", err)
271+
}
272+
otherClient.Location = "badloc"
273+
274+
for _, tc := range []struct {
275+
description string
276+
f func(*Client) (*Job, error)
277+
wantErr bool
278+
}{
279+
{
280+
description: "JobFromID",
281+
f: func(c *Client) (*Job, error) { return c.JobFromID(ctx, want.jobID) },
282+
wantErr: true,
283+
},
284+
{
285+
description: "JobFromIDLocation",
286+
f: func(c *Client) (*Job, error) { return c.JobFromIDLocation(ctx, want.jobID, want.location) },
287+
wantErr: true,
288+
},
289+
{
290+
description: "JobFromProject",
291+
f: func(c *Client) (*Job, error) { return c.JobFromProject(ctx, want.projectID, want.jobID, want.location) },
292+
},
293+
} {
294+
got, err := tc.f(otherClient)
295+
if err != nil {
296+
if !tc.wantErr {
297+
t.Errorf("case %q errored: %v", tc.description, err)
298+
}
299+
continue
300+
}
301+
if tc.wantErr {
302+
t.Errorf("case %q got success, expected error", tc.description)
303+
}
304+
if got.projectID != want.projectID {
305+
t.Errorf("case %q projectID mismatch, got %s want %s", tc.description, got.projectID, want.projectID)
306+
}
307+
if got.location != want.location {
308+
t.Errorf("case %q location mismatch, got %s want %s", tc.description, got.location, want.location)
309+
}
310+
if got.jobID != want.jobID {
311+
t.Errorf("case %q jobID mismatch, got %s want %s", tc.description, got.jobID, want.jobID)
312+
}
313+
}
314+
315+
}
316+
253317
func TestIntegration_TableCreate(t *testing.T) {
254318
// Check that creating a record field with an empty schema is an error.
255319
if client == nil {

Diff for: bigquery/job.go

+16-5
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,24 @@ type Job struct {
4646
// For jobs whose location is other than "US" or "EU", set Client.Location or use
4747
// JobFromIDLocation.
4848
func (c *Client) JobFromID(ctx context.Context, id string) (*Job, error) {
49-
return c.JobFromIDLocation(ctx, id, c.Location)
49+
return c.JobFromProject(ctx, c.projectID, id, c.Location)
5050
}
5151

5252
// JobFromIDLocation creates a Job which refers to an existing BigQuery job. The job
5353
// need not have been created by this package (for example, it may have
5454
// been created in the BigQuery console), but it must exist in the specified location.
5555
func (c *Client) JobFromIDLocation(ctx context.Context, id, location string) (j *Job, err error) {
56-
ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.JobFromIDLocation")
56+
return c.JobFromProject(ctx, c.projectID, id, location)
57+
}
58+
59+
// JobFromProject creates a Job which refers to an existing BigQuery job. The job
60+
// need not have been created by this package, nor does it need to reside within the same
61+
// project or location as the instantiated client.
62+
func (c *Client) JobFromProject(ctx context.Context, projectID, jobID, location string) (j *Job, err error) {
63+
ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.JobFromProject")
5764
defer func() { trace.EndSpan(ctx, err) }()
5865

59-
bqjob, err := c.getJobInternal(ctx, id, location, "configuration", "jobReference", "status", "statistics")
66+
bqjob, err := c.getJobInternal(ctx, jobID, location, projectID, "configuration", "jobReference", "status", "statistics")
6067
if err != nil {
6168
return nil, err
6269
}
@@ -799,9 +806,13 @@ func convertListedJob(j *bq.JobListJobs, c *Client) (*Job, error) {
799806
return bqToJob2(j.JobReference, j.Configuration, j.Status, j.Statistics, j.UserEmail, c)
800807
}
801808

802-
func (c *Client) getJobInternal(ctx context.Context, jobID, location string, fields ...googleapi.Field) (*bq.Job, error) {
809+
func (c *Client) getJobInternal(ctx context.Context, jobID, location, projectID string, fields ...googleapi.Field) (*bq.Job, error) {
803810
var job *bq.Job
804-
call := c.bqs.Jobs.Get(c.projectID, jobID).Context(ctx)
811+
proj := projectID
812+
if proj == "" {
813+
proj = c.projectID
814+
}
815+
call := c.bqs.Jobs.Get(proj, jobID).Context(ctx)
805816
if location != "" {
806817
call = call.Location(location)
807818
}

0 commit comments

Comments
 (0)