From 9e85219afa074c6804adac79cb4f28a944f5957a Mon Sep 17 00:00:00 2001
From: Alvaro Viebrantz <aviebrantz@google.com>
Date: Wed, 24 Jan 2024 10:11:46 -0400
Subject: [PATCH] fix: prefer usage of projectId from the Dataset (#1326)

---
 src/bigquery.ts  |  2 +-
 src/dataset.ts   |  9 ++++---
 src/model.ts     |  2 +-
 src/table.ts     | 18 +++++++-------
 test/bigquery.ts |  2 +-
 test/dataset.ts  | 61 ++++++++++++++++++++++++++++++++++++++++++++----
 test/model.ts    |  4 ++--
 test/table.ts    | 22 ++++++++---------
 8 files changed, 87 insertions(+), 33 deletions(-)

diff --git a/src/bigquery.ts b/src/bigquery.ts
index 411897ce..d7adaa6a 100644
--- a/src/bigquery.ts
+++ b/src/bigquery.ts
@@ -1420,7 +1420,7 @@ export class BigQuery extends Service {
 
       query.destinationTable = {
         datasetId: options.destination.dataset.id,
-        projectId: options.destination.dataset.bigQuery.projectId,
+        projectId: options.destination.dataset.projectId,
         tableId: options.destination.id,
       };
 
diff --git a/src/dataset.ts b/src/dataset.ts
index 9bc8fd44..e2347436 100644
--- a/src/dataset.ts
+++ b/src/dataset.ts
@@ -123,7 +123,7 @@ export type TableCallback = ResourceCallback<Table, bigquery.ITable>;
 class Dataset extends ServiceObject {
   bigQuery: BigQuery;
   location?: string;
-  projectId?: string;
+  projectId: string;
   getModelsStream(options?: GetModelsOptions): ResourceStream<Model> {
     // placeholder body, overwritten in constructor
     return new ResourceStream<Model>({}, () => {});
@@ -389,6 +389,8 @@ class Dataset extends ServiceObject {
 
     if (options?.projectId) {
       this.projectId = options.projectId;
+    } else {
+      this.projectId = bigQuery.projectId;
     }
 
     this.bigQuery = bigQuery;
@@ -642,7 +644,7 @@ class Dataset extends ServiceObject {
       routineReference: {
         routineId: id,
         datasetId: this.id,
-        projectId: this.bigQuery.projectId,
+        projectId: this.projectId,
       },
     });
 
@@ -740,7 +742,7 @@ class Dataset extends ServiceObject {
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     (body as any).tableReference = {
       datasetId: this.id,
-      projectId: this.bigQuery.projectId,
+      projectId: this.projectId,
       tableId: id,
     };
 
@@ -1303,6 +1305,7 @@ class Dataset extends ServiceObject {
     options = extend(
       {
         location: this.location,
+        projectId: this.projectId,
       },
       options
     );
diff --git a/src/model.ts b/src/model.ts
index 3bab7ee6..ea0ea73a 100644
--- a/src/model.ts
+++ b/src/model.ts
@@ -430,7 +430,7 @@ class Model extends ServiceObject {
         extract: extend(true, options, {
           sourceModel: {
             datasetId: this.dataset.id,
-            projectId: this.bigQuery.projectId,
+            projectId: this.dataset.projectId,
             modelId: this.id,
           },
         }),
diff --git a/src/table.ts b/src/table.ts
index c555e41e..be19d18a 100644
--- a/src/table.ts
+++ b/src/table.ts
@@ -929,12 +929,12 @@ class Table extends ServiceObject {
         copy: extend(true, metadata, {
           destinationTable: {
             datasetId: destination.dataset.id,
-            projectId: destination.bigQuery.projectId,
+            projectId: destination.dataset.projectId,
             tableId: destination.id,
           },
           sourceTable: {
             datasetId: this.dataset.id,
-            projectId: this.bigQuery.projectId,
+            projectId: this.dataset.projectId,
             tableId: this.id,
           },
         }),
@@ -1051,14 +1051,14 @@ class Table extends ServiceObject {
         copy: extend(true, metadata, {
           destinationTable: {
             datasetId: this.dataset.id,
-            projectId: this.bigQuery.projectId,
+            projectId: this.dataset.projectId,
             tableId: this.id,
           },
 
           sourceTables: sourceTables.map(sourceTable => {
             return {
               datasetId: sourceTable.dataset.id,
-              projectId: sourceTable.bigQuery.projectId,
+              projectId: sourceTable.dataset.projectId,
               tableId: sourceTable.id,
             };
           }),
@@ -1224,7 +1224,7 @@ class Table extends ServiceObject {
         extract: extend(true, options, {
           sourceTable: {
             datasetId: this.dataset.id,
-            projectId: this.bigQuery.projectId,
+            projectId: this.dataset.projectId,
             tableId: this.id,
           },
         }),
@@ -1404,7 +1404,7 @@ class Table extends ServiceObject {
       configuration: {
         load: {
           destinationTable: {
-            projectId: this.bigQuery.projectId,
+            projectId: this.dataset.projectId,
             datasetId: this.dataset.id,
             tableId: this.id,
           },
@@ -1510,7 +1510,7 @@ class Table extends ServiceObject {
       true,
       {
         destinationTable: {
-          projectId: this.bigQuery.projectId,
+          projectId: this.dataset.projectId,
           datasetId: this.dataset.id,
           tableId: this.id,
         },
@@ -1542,12 +1542,12 @@ class Table extends ServiceObject {
             },
             jobReference: {
               jobId,
-              projectId: this.bigQuery.projectId,
+              projectId: this.dataset.projectId,
               location: this.location,
             },
           } as {},
           request: {
-            uri: `${this.bigQuery.apiEndpoint}/upload/bigquery/v2/projects/${this.bigQuery.projectId}/jobs`,
+            uri: `${this.bigQuery.apiEndpoint}/upload/bigquery/v2/projects/${this.dataset.projectId}/jobs`,
           },
         },
         // eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/test/bigquery.ts b/test/bigquery.ts
index 8f0693aa..41491ef1 100644
--- a/test/bigquery.ts
+++ b/test/bigquery.ts
@@ -1977,7 +1977,7 @@ describe('BigQuery', () => {
             reqOpts.json.configuration.query.destinationTable,
             {
               datasetId: dataset.id,
-              projectId: dataset.bigQuery.projectId,
+              projectId: dataset.projectId,
               tableId: TABLE_ID,
             }
           );
diff --git a/test/dataset.ts b/test/dataset.ts
index 0472f90b..5d77e33d 100644
--- a/test/dataset.ts
+++ b/test/dataset.ts
@@ -87,6 +87,7 @@ describe('BigQuery/Dataset', () => {
   } as {} as _root.BigQuery;
   const DATASET_ID = 'kittens';
   const LOCATION = 'asia-northeast1';
+  const ANOTHER_PROJECT_ID = 'another-test-project';
 
   // tslint:disable-next-line variable-name
   let Dataset: typeof _root.Dataset;
@@ -148,6 +149,12 @@ describe('BigQuery/Dataset', () => {
       assert.strictEqual(ds.location, LOCATION);
     });
 
+    it('should set the client projectId by default', () => {
+      const ds = new Dataset(BIGQUERY, DATASET_ID);
+
+      assert.strictEqual(ds.projectId, BIGQUERY.projectId);
+    });
+
     it('should capture user provided projectId', () => {
       const projectIdOverride = 'octavia';
       const options = {projectId: projectIdOverride};
@@ -171,7 +178,9 @@ describe('BigQuery/Dataset', () => {
       });
 
       it('should call through to BigQuery#createDataset', done => {
-        const OPTIONS = {};
+        const OPTIONS = {
+          projectId: BIGQUERY.projectId,
+        };
 
         bq.createDataset = (id: string, options: {}, callback: Function) => {
           assert.strictEqual(id, DATASET_ID);
@@ -249,6 +258,7 @@ describe('BigQuery/Dataset', () => {
           json: {
             etag: FAKE_ETAG,
           },
+          uri: `/projects/${BIGQUERY.projectId}/`,
         };
 
         const reqOpts = interceptor.request(fakeReqOpts);
@@ -266,6 +276,7 @@ describe('BigQuery/Dataset', () => {
           json: {
             etag: FAKE_ETAG,
           },
+          uri: `/projects/${BIGQUERY.projectId}/`,
         };
 
         const expectedHeaders = Object.assign({}, fakeReqOpts.headers, {
@@ -284,6 +295,7 @@ describe('BigQuery/Dataset', () => {
           json: {
             etag: FAKE_ETAG,
           },
+          uri: `/projects/${BIGQUERY.projectId}/`,
         };
 
         const reqOpts = interceptor.request(fakeReqOpts);
@@ -428,6 +440,7 @@ describe('BigQuery/Dataset', () => {
     const API_RESPONSE = {
       tableReference: {
         tableId: TABLE_ID,
+        projectId: BIGQUERY.projectId,
       },
     };
 
@@ -443,10 +456,7 @@ describe('BigQuery/Dataset', () => {
         const body = reqOpts.json;
         assert.deepStrictEqual(body.schema, SCHEMA_OBJECT);
         assert.strictEqual(body.tableReference.datasetId, DATASET_ID);
-        assert.strictEqual(
-          body.tableReference.projectId,
-          ds.bigQuery.projectId
-        );
+        assert.strictEqual(body.tableReference.projectId, ds.projectId);
         assert.strictEqual(body.tableReference.tableId, TABLE_ID);
 
         done();
@@ -455,6 +465,31 @@ describe('BigQuery/Dataset', () => {
       ds.createTable(TABLE_ID, options, assert.ifError);
     });
 
+    it('should create a table on a different project', done => {
+      const options = {
+        schema: SCHEMA_OBJECT,
+      };
+      const anotherDs = new Dataset(BIGQUERY, DATASET_ID, {
+        projectId: ANOTHER_PROJECT_ID,
+      }) as any;
+      anotherDs.request = (reqOpts: DecorateRequestOptions) => {
+        assert.strictEqual(reqOpts.method, 'POST');
+        assert.strictEqual(reqOpts.uri, '/tables');
+
+        const body = reqOpts.json;
+        assert.deepStrictEqual(body.schema, SCHEMA_OBJECT);
+        assert.strictEqual(body.tableReference.datasetId, DATASET_ID);
+        assert.strictEqual(body.tableReference.projectId, ANOTHER_PROJECT_ID);
+        assert.strictEqual(body.tableReference.tableId, TABLE_ID);
+
+        done();
+      };
+
+      // Under the hood dataset.createTable is called
+      const table = anotherDs.table(TABLE_ID);
+      table.create(options, assert.ifError);
+    });
+
     it('should not require options', done => {
       ds.request = (reqOpts: DecorateRequestOptions, callback: Function) => {
         callback(null, API_RESPONSE);
@@ -577,6 +612,22 @@ describe('BigQuery/Dataset', () => {
       ds.createTable(TABLE_ID, {schema: SCHEMA_OBJECT}, assert.ifError);
     });
 
+    it('should pass the projectId to the Table', done => {
+      const response = Object.assign({location: LOCATION}, API_RESPONSE);
+
+      ds.request = (reqOpts: DecorateRequestOptions, callback: Function) => {
+        callback(null, response);
+      };
+
+      ds.table = (id: string, options: TableOptions) => {
+        assert.strictEqual(options.location, LOCATION);
+        setImmediate(done);
+        return {};
+      };
+
+      ds.createTable(TABLE_ID, {schema: SCHEMA_OBJECT}, assert.ifError);
+    });
+
     it('should return an apiResponse', done => {
       const opts = {id: TABLE_ID, schema: SCHEMA_OBJECT};
 
diff --git a/test/model.ts b/test/model.ts
index 73059b40..f9dabeb8 100644
--- a/test/model.ts
+++ b/test/model.ts
@@ -56,9 +56,9 @@ describe('BigQuery/Model', () => {
 
   const DATASET = {
     id: 'dataset-id',
+    projectId: 'project-id',
     createTable: util.noop,
     bigQuery: {
-      projectId: 'project-id',
       job: (id: string) => {
         return {id};
       },
@@ -137,7 +137,7 @@ describe('BigQuery/Model', () => {
       model.bigQuery.createJob = (reqOpts: JobOptions) => {
         assert.deepStrictEqual(reqOpts.configuration!.extract!.sourceModel, {
           datasetId: model.dataset.id,
-          projectId: model.bigQuery.projectId,
+          projectId: model.dataset.projectId,
           modelId: model.id,
         });
 
diff --git a/test/table.ts b/test/table.ts
index 14578594..bfafcae6 100644
--- a/test/table.ts
+++ b/test/table.ts
@@ -173,9 +173,9 @@ describe('BigQuery/Table', () => {
 
   const DATASET = {
     id: 'dataset-id',
+    projectId: 'project-id',
     createTable: util.noop,
     bigQuery: {
-      projectId: 'project-id',
       job: (id: string) => {
         return {id};
       },
@@ -721,12 +721,12 @@ describe('BigQuery/Table', () => {
               c: 'd',
               destinationTable: {
                 datasetId: DEST_TABLE.dataset.id,
-                projectId: DEST_TABLE.bigQuery.projectId,
+                projectId: DEST_TABLE.dataset.projectId,
                 tableId: DEST_TABLE.id,
               },
               sourceTable: {
                 datasetId: table.dataset.id,
-                projectId: table.bigQuery.projectId,
+                projectId: table.dataset.projectId,
                 tableId: table.id,
               },
             },
@@ -842,13 +842,13 @@ describe('BigQuery/Table', () => {
               c: 'd',
               destinationTable: {
                 datasetId: table.dataset.id,
-                projectId: table.bigQuery.projectId,
+                projectId: table.dataset.projectId,
                 tableId: table.id,
               },
               sourceTables: [
                 {
                   datasetId: SOURCE_TABLE.dataset.id,
-                  projectId: SOURCE_TABLE.bigQuery.projectId,
+                  projectId: SOURCE_TABLE.dataset.projectId,
                   tableId: SOURCE_TABLE.id,
                 },
               ],
@@ -867,12 +867,12 @@ describe('BigQuery/Table', () => {
         assert.deepStrictEqual(reqOpts.configuration!.copy!.sourceTables, [
           {
             datasetId: SOURCE_TABLE.dataset.id,
-            projectId: SOURCE_TABLE.bigQuery.projectId,
+            projectId: SOURCE_TABLE.dataset.projectId,
             tableId: SOURCE_TABLE.id,
           },
           {
             datasetId: SOURCE_TABLE.dataset.id,
-            projectId: SOURCE_TABLE.bigQuery.projectId,
+            projectId: SOURCE_TABLE.dataset.projectId,
             tableId: SOURCE_TABLE.id,
           },
         ]);
@@ -1002,7 +1002,7 @@ describe('BigQuery/Table', () => {
       table.bigQuery.createJob = (reqOpts: JobOptions) => {
         assert.deepStrictEqual(reqOpts.configuration!.extract!.sourceTable, {
           datasetId: table.dataset.id,
-          projectId: table.bigQuery.projectId,
+          projectId: table.dataset.projectId,
           tableId: table.id,
         });
 
@@ -1685,14 +1685,14 @@ describe('BigQuery/Table', () => {
                 a: 'b',
                 c: 'd',
                 destinationTable: {
-                  projectId: table.bigQuery.projectId,
+                  projectId: table.dataset.projectId,
                   datasetId: table.dataset.id,
                   tableId: table.id,
                 },
               },
             },
             jobReference: {
-              projectId: table.bigQuery.projectId,
+              projectId: table.dataset.projectId,
               jobId: fakeJobId,
               location: undefined,
             },
@@ -1711,7 +1711,7 @@ describe('BigQuery/Table', () => {
           const uri =
             table.bigQuery.apiEndpoint +
             '/upload/bigquery/v2/projects/' +
-            table.bigQuery.projectId +
+            table.dataset.projectId +
             '/jobs';
           assert.strictEqual(options.request.uri, uri);
           done();