Skip to content
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
106 changes: 103 additions & 3 deletions packages/bigquery/src/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ Table.mergeSchemaWithRows_ = function(BigQuery, schema, rows) {
* });
*
* //-
* // See the <a href="http://goo.gl/dKWIyS">`configuration.copy`</a> object for all
* // available options.
* // See the <a href="http://goo.gl/dKWIyS">`configuration.copy`</a> object for
* // all available options.
* //-
* var metadata = {
* createDisposition: 'CREATE_NEVER',
Expand Down Expand Up @@ -410,7 +410,107 @@ Table.prototype.copy = function(destination, metadata, callback) {
this.bigQuery.request({
method: 'POST',
uri: '/jobs',
json: body,
json: body
}, function(err, resp) {
if (err) {
callback(err, null, resp);
return;
}

var job = self.bigQuery.job(resp.jobReference.jobId);
job.metadata = resp;

callback(null, job, resp);
});
};

/**
* Copy data from multiple tables into this table.
*
* @resource [Jobs: insert API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/insert}
*
* @param {module:bigquery/table|module:bigquery/table[]} sourceTables - The
* source table(s) to copy data from.
* @param {object=} metadata - Metadata to set with the copy operation. The
* metadata object should be in the format of the
* [`configuration.copy`](http://goo.gl/dKWIyS) property of a Jobs resource.
* @param {function} callback - The callback function.
* @param {?error} callback.err - An error returned while making this request
* @param {module:bigquery/job} callback.job - The job used to copy your table.
* @param {object} callback.apiResponse - The full API response.
*
* @throws {Error} If a source other than a Table object is provided.
*
* @example
* var sourceTables = [
* dataset.table('your-table'),
* dataset.table('your-second-table')
* ];
*
* table.copyFrom(sourceTables, function(err, job, apiResponse) {
* // `job` is a Job object that can be used to check the status of the
* // request.
* });
*
* //-
* // See the <a href="http://goo.gl/dKWIyS">`configuration.copy`</a> object for
* // all available options.
* //-
* var metadata = {
* createDisposition: 'CREATE_NEVER',
* writeDisposition: 'WRITE_TRUNCATE'
* };
*
* table.copyFrom(sourceTables, metadata, function(err, job, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* table.copyFrom(sourceTables, metadata).then(function(data) {
* var job = data[0];
* var apiResponse = data[1];
* });
*/
Table.prototype.copyFrom = function(sourceTables, metadata, callback) {
var self = this;

sourceTables = arrify(sourceTables);

sourceTables.forEach(function(sourceTable) {
if (!(sourceTable instanceof Table)) {
throw new Error('Source must be a Table object.');
}
});

if (is.fn(metadata)) {
callback = metadata;
metadata = {};
}

var body = {
configuration: {
copy: extend(true, metadata || {}, {
destinationTable: {
datasetId: this.dataset.id,
projectId: this.bigQuery.projectId,
tableId: this.id
},

sourceTables: sourceTables.map(function(sourceTable) {
return {
datasetId: sourceTable.dataset.id,
projectId: sourceTable.bigQuery.projectId,
tableId: sourceTable.id
};
})
})
}
};

this.bigQuery.request({
method: 'POST',
uri: '/jobs',
json: body
}, function(err, resp) {
if (err) {
callback(err, null, resp);
Expand Down
77 changes: 77 additions & 0 deletions packages/bigquery/system-test/bigquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,83 @@ describe('BigQuery', function() {
});
});

describe('copying', function() {
var TABLES = {
1: {
data: {
tableId: 1
}
},

2: {}
};

var SCHEMA = 'tableId:integer';

before(function(done) {
TABLES[1].table = dataset.table(generateName('table'));
TABLES[2].table = dataset.table(generateName('table'));

async.each(TABLES, function(tableObject, next) {
var tableInstance = tableObject.table;

tableInstance.create({
schema: SCHEMA
}, next);
}, function(err) {
if (err) {
done(err);
return;
}

var table1Instance = TABLES[1].table;
table1Instance.insert(TABLES[1].data, done);
});
});

it('should copy data from current table', function(done) {
var table1 = TABLES[1];
var table1Instance = table1.table;

var table2 = TABLES[2];
var table2Instance = table2.table;

table1Instance.copy(table2Instance, function(err, job) {
assert.ifError(err);

job
.on('error', done)
.on('complete', function() {
// Data may take up to 90 minutes to be copied*, so we cannot test
// to see that anything but the request was successful.
// *https://cloud.google.com/bigquery/streaming-data-into-bigquery
done();
});
});
});

it('should copy data from another table', function(done) {
var table1 = TABLES[1];
var table1Instance = table1.table;

var table2 = TABLES[2];
var table2Instance = table2.table;

table2Instance.copyFrom(table1Instance, function(err, job) {
assert.ifError(err);

job
.on('error', done)
.on('complete', function() {
// Data may take up to 90 minutes to be copied*, so we cannot test
// to see that anything but the request was successful.
// *https://cloud.google.com/bigquery/streaming-data-into-bigquery
done();
});
});
});
});

describe('importing & exporting', function() {
var file = bucket.file('kitten-test-data-backup.json');

Expand Down
144 changes: 144 additions & 0 deletions packages/bigquery/test/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,150 @@ describe('BigQuery/Table', function() {
});
});

describe('copyFrom', function() {
var SOURCE_TABLE;

before(function() {
SOURCE_TABLE = new Table(DATASET, 'source-table');
});

it('should throw if a source is not a Table', function() {
assert.throws(function() {
table.copyFrom(['table']);
}, /Source must be a Table/);

assert.throws(function() {
table.copyFrom([SOURCE_TABLE, 'table']);
}, /Source must be a Table/);

assert.throws(function() {
table.copyFrom({});
}, /Source must be a Table/);

assert.throws(function() {
table.copyFrom(function() {});
}, /Source must be a Table/);
});

it('should send correct request to the API', function(done) {
table.bigQuery.request = function(reqOpts) {
assert.equal(reqOpts.method, 'POST');
assert.equal(reqOpts.uri, '/jobs');
assert.deepEqual(reqOpts.json, {
configuration: {
copy: {
a: 'b',
c: 'd',
destinationTable: {
datasetId: table.dataset.id,
projectId: table.bigQuery.projectId,
tableId: table.id
},
sourceTables: [
{
datasetId: SOURCE_TABLE.dataset.id,
projectId: SOURCE_TABLE.bigQuery.projectId,
tableId: SOURCE_TABLE.id
}
]
}
}
});

done();
};

table.copyFrom(SOURCE_TABLE, { a: 'b', c: 'd' }, assert.ifError);
});

it('should accept multiple source tables', function(done) {
table.bigQuery.request = function(reqOpts) {
assert.deepEqual(reqOpts.json.configuration.copy.sourceTables, [
{
datasetId: SOURCE_TABLE.dataset.id,
projectId: SOURCE_TABLE.bigQuery.projectId,
tableId: SOURCE_TABLE.id
},
{
datasetId: SOURCE_TABLE.dataset.id,
projectId: SOURCE_TABLE.bigQuery.projectId,
tableId: SOURCE_TABLE.id
}
]);

done();
};

table.copyFrom([
SOURCE_TABLE,
SOURCE_TABLE
], assert.ifError);
});

it('should create and return a Job', function(done) {
var jobId = 'job-id';

table.bigQuery.request = function(reqOpts, callback) {
callback(null, { jobReference: { jobId: jobId } });
};

table.copy(SOURCE_TABLE, function(err, job) {
assert.ifError(err);
assert.equal(job.id, jobId);
done();
});
});

it('should assign metadata on the job', function(done) {
var jobMetadata = { jobReference: { jobId: 'job-id' }, a: 'b', c: 'd' };

table.bigQuery.request = function(reqOpts, callback) {
callback(null, jobMetadata);
};

table.copy(SOURCE_TABLE, function(err, job) {
assert.ifError(err);
assert.deepEqual(job.metadata, jobMetadata);
done();
});
});

it('should accept just a source and callback', function(done) {
table.bigQuery.request = function(reqOpts, callback) {
callback(null, { jobReference: { jobId: 'job-id' } });
};

table.copy(SOURCE_TABLE, done);
});

it('should pass an error to the callback', function(done) {
var error = new Error('Error.');

table.bigQuery.request = function(reqOpts, callback) {
callback(error);
};

table.copy(SOURCE_TABLE, function(err) {
assert.equal(err, error);
done();
});
});

it('should pass an apiResponse to the callback', function(done) {
var jobMetadata = { jobReference: { jobId: 'job-id' }, a: 'b', c: 'd' };

table.bigQuery.request = function(reqOpts, callback) {
callback(null, jobMetadata);
};

table.copy(SOURCE_TABLE, function(err, job, apiResponse) {
assert.ifError(err);
assert.deepEqual(apiResponse, jobMetadata);
done();
});
});
});

describe('createQueryStream', function() {
it('should call datasetInstance.createQueryStream()', function(done) {
table.dataset.createQueryStream = function(a) {
Expand Down