Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support create/list datasets on a different project #1230

Merged
merged 4 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions samples/listDatasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@

'use strict';

function main() {
function main(projectId) {
// [START bigquery_list_datasets]
// Import the Google Cloud client library
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function listDatasets() {
// Lists all datasets in current GCP project.
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = "my_project_id";

// Lists all datasets in the specified project
const [datasets] = await bigquery.getDatasets();
// Lists all datasets in the specified project.
// If projectId is not specified, this method will take
// the projectId from the authenticated BigQuery Client.
const [datasets] = await bigquery.getDatasets({projectId});
console.log('Datasets:');
datasets.forEach(dataset => console.log(dataset.id));
}
Expand Down
6 changes: 6 additions & 0 deletions samples/test/datasets.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ describe('Datasets', () => {
assert.match(output, new RegExp(datasetId));
});

it('should list datasets on a different project', async () => {
const output = execSync('node listDatasets.js bigquery-public-data');
assert.match(output, /Datasets:/);
assert.match(output, new RegExp('usa_names'));
});

it('should retrieve a dataset if it exists', async () => {
const output = execSync(`node getDataset.js ${datasetId}`);
assert.include(output, 'Dataset:');
Expand Down
127 changes: 70 additions & 57 deletions src/bigquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,15 @@ export type QueryStreamOptions = {
wrapIntegers?: boolean | IntegerTypeCastOptions;
parseJSON?: boolean;
};
export type DatasetResource = bigquery.IDataset;
export type DatasetResource = bigquery.IDataset & {
projectId?: string;
};
export type ValueType = bigquery.IQueryParameterType;

export type GetDatasetsOptions = PagedRequest<bigquery.datasets.IListParams>;
export type GetDatasetsOptions = PagedRequest<bigquery.datasets.IListParams> & {
projectId?: string;
};

export type DatasetsResponse = PagedResponse<
Dataset,
GetDatasetsOptions,
Expand Down Expand Up @@ -1257,35 +1262,36 @@ export class BigQuery extends Service {
const callback =
typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;

this.request(
{
method: 'POST',
uri: '/datasets',
json: extend(
true,
{
location: this.location,
const reqOpts: DecorateRequestOptions = {
method: 'POST',
uri: '/datasets',
json: extend(
true,
{
location: this.location,
},
options,
{
datasetReference: {
datasetId: id,
},
options,
{
datasetReference: {
datasetId: id,
},
}
),
},
(err, resp) => {
if (err) {
callback!(err, null, resp);
return;
}
),
};
if (options.projectId) {
reqOpts.projectId = options.projectId;
}
this.request(reqOpts, (err, resp) => {
if (err) {
callback!(err, null, resp);
return;
}

const dataset = this.dataset(id);
dataset.metadata = resp;
const dataset = this.dataset(id, options);
dataset.metadata = resp;

callback!(null, dataset, resp);
}
);
callback!(null, dataset, resp);
});
}

/**
Expand Down Expand Up @@ -1664,6 +1670,7 @@ export class BigQuery extends Service {
*
* @param {string} id ID of the dataset.
* @param {object} [options] Dataset options.
* @param {string} [options.projectId] The GCP project ID.
* @param {string} [options.location] The geographic location of the dataset.
* Required except for US and EU.
*
Expand All @@ -1686,12 +1693,13 @@ export class BigQuery extends Service {
}

/**
* List all or some of the datasets in your project.
* List all or some of the datasets in a project.
*
* See {@link https://cloud.google.com/bigquery/docs/reference/v2/datasets/list| Datasets: list API Documentation}
*
* @param {object} [options] Configuration object.
* @param {boolean} [options.all] List all datasets, including hidden ones.
* @param {string} [options.projectId] The GCP project ID.
* @param {boolean} [options.autoPaginate] Have pagination handled automatically.
* Default: true.
* @param {number} [options.maxApiCalls] Maximum number of API calls to make.
Expand Down Expand Up @@ -1746,40 +1754,45 @@ export class BigQuery extends Service {
const callback =
typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;

this.request(
{
uri: '/datasets',
qs: options,
},
(err, resp) => {
if (err) {
callback!(err, null, null, resp);
return;
}
const reqOpts: DecorateRequestOptions = {
uri: '/datasets',
qs: options,
};
if (options.projectId) {
reqOpts.projectId = options.projectId;
}
this.request(reqOpts, (err, resp) => {
if (err) {
callback!(err, null, null, resp);
return;
}

let nextQuery: GetDatasetsOptions | null = null;
let nextQuery: GetDatasetsOptions | null = null;

if (resp.nextPageToken) {
nextQuery = Object.assign({}, options, {
pageToken: resp.nextPageToken,
});
}
if (resp.nextPageToken) {
nextQuery = Object.assign({}, options, {
pageToken: resp.nextPageToken,
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const datasets = (resp.datasets || []).map(
(dataset: bigquery.IDataset) => {
const ds = this.dataset(dataset.datasetReference!.datasetId!, {
location: dataset.location!,
});

ds.metadata = dataset!;
return ds;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const datasets = (resp.datasets || []).map(
(dataset: bigquery.IDataset) => {
const dsOpts: DatasetOptions = {
location: dataset.location!,
};
if (options.projectId) {
dsOpts.projectId = options.projectId;
}
);
const ds = this.dataset(dataset.datasetReference!.datasetId!, dsOpts);

callback!(null, datasets, nextQuery, resp);
}
);
ds.metadata = dataset!;
return ds;
}
);

callback!(null, datasets, nextQuery, resp);
});
}

/**
Expand Down
8 changes: 7 additions & 1 deletion src/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export type TableCallback = ResourceCallback<Table, bigquery.ITable>;
* @param {BigQuery} bigQuery {@link BigQuery} instance.
* @param {string} id The ID of the Dataset.
* @param {object} [options] Dataset options.
* @param {string} [options.projectId] The GCP project ID.
* @param {string} [options.location] The geographic location of the dataset.
* Defaults to US.
*
Expand Down Expand Up @@ -372,7 +373,12 @@ class Dataset extends ServiceObject {
typeof optionsOrCallback === 'function'
? (optionsOrCallback as DatasetCallback)
: cb;
options = extend({}, options, {location: this.location});
if (this.location) {
options = extend({}, options, {location: this.location});
}
if (this.projectId) {
options = extend({}, options, {projectId: this.projectId});
}
return bigQuery.createDataset(id, options, callback!);
},
});
Expand Down
39 changes: 39 additions & 0 deletions test/bigquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ afterEach(() => sandbox.restore());
describe('BigQuery', () => {
const JOB_ID = 'JOB_ID';
const PROJECT_ID = 'test-project';
const ANOTHER_PROJECT_ID = 'another-test-project';
const LOCATION = 'asia-northeast1';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -1592,6 +1593,30 @@ describe('BigQuery', () => {
bq.createDataset(DATASET_ID, assert.ifError);
});

it('should create a dataset on a different project', done => {
bq.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => {
assert.strictEqual(reqOpts.method, 'POST');
assert.strictEqual(reqOpts.projectId, ANOTHER_PROJECT_ID);
assert.strictEqual(
reqOpts.uri,
`https://bigquery.googleapis.com/bigquery/v2/projects/${ANOTHER_PROJECT_ID}/datasets`
);
assert.deepStrictEqual(reqOpts.json.datasetReference, {
datasetId: DATASET_ID,
});

done();
};

bq.createDataset(
DATASET_ID,
{
projectId: ANOTHER_PROJECT_ID,
},
assert.ifError
);
});

it('should send the location if available', done => {
const bq = new BigQuery({
projectId: PROJECT_ID,
Expand Down Expand Up @@ -2514,6 +2539,20 @@ describe('BigQuery', () => {
done();
});
});

it('should fetch datasets from a different project', done => {
const queryObject = {projectId: ANOTHER_PROJECT_ID};

bq.makeAuthenticatedRequest = (reqOpts: DecorateRequestOptions) => {
assert.strictEqual(
reqOpts.uri,
`https://bigquery.googleapis.com/bigquery/v2/projects/${ANOTHER_PROJECT_ID}/datasets`
);
done();
};

bq.getDatasets(queryObject, assert.ifError);
});
});

describe('getJobs', () => {
Expand Down
14 changes: 14 additions & 0 deletions test/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,20 @@ describe('BigQuery/Dataset', () => {
ds.location = LOCATION;
config.createMethod(DATASET_ID, done);
});

it('should pass the projectId', done => {
bq.createDataset = (
id: string,
options: DatasetOptions,
callback: Function
) => {
assert.strictEqual(options.projectId, 'project-id');
callback(); // the done fn
};

ds.projectId = 'project-id';
config.createMethod(DATASET_ID, done);
});
});

describe('projectId override interceptor', () => {
Expand Down