Skip to content

Commit e2b5741

Browse files
stephenpluspluscallmehiphop
authored andcommitted
storage: support rewrite operation (#1663)
1 parent f2fd4a5 commit e2b5741

3 files changed

Lines changed: 97 additions & 7 deletions

File tree

handwritten/storage/src/file.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ function File(bucket, name, options) {
269269

270270
util.inherits(File, common.ServiceObject);
271271

272+
/*! Developer Documentation
273+
*
274+
* @param {object=} options - Configuration object.
275+
* @param {string} options.token - A previously-returned `rewriteToken` from an
276+
* unfinished rewrite request.
277+
*/
272278
/**
273279
* Copy this file to another file. By default, this will copy the file to the
274280
* same bucket, but you can choose to copy it to another Bucket by providing
@@ -356,13 +362,21 @@ util.inherits(File, common.ServiceObject);
356362
* // The `copiedFile` parameter is equal to `anotherFile`.
357363
* });
358364
*/
359-
File.prototype.copy = function(destination, callback) {
365+
File.prototype.copy = function(destination, options, callback) {
366+
var self = this;
367+
360368
var noDestinationError = new Error('Destination file should have a name.');
361369

362370
if (!destination) {
363371
throw noDestinationError;
364372
}
365373

374+
if (is.fn(options)) {
375+
callback = options;
376+
options = {};
377+
}
378+
379+
options = options || {};
366380
callback = callback || common.util.noop;
367381

368382
var destBucket;
@@ -394,12 +408,15 @@ File.prototype.copy = function(destination, callback) {
394408
if (is.defined(this.generation)) {
395409
query.sourceGeneration = this.generation;
396410
}
411+
if (is.defined(options.token)) {
412+
query.rewriteToken = options.token;
413+
}
397414

398415
newFile = newFile || destBucket.file(destName);
399416

400417
this.request({
401418
method: 'POST',
402-
uri: format('/copyTo/b/{bucketName}/o/{fileName}', {
419+
uri: format('/rewriteTo/b/{bucketName}/o/{fileName}', {
403420
bucketName: destBucket.name,
404421
fileName: encodeURIComponent(destName)
405422
}),
@@ -410,6 +427,11 @@ File.prototype.copy = function(destination, callback) {
410427
return;
411428
}
412429

430+
if (resp.rewriteToken) {
431+
self.copy(newFile, { token: resp.rewriteToken }, callback);
432+
return;
433+
}
434+
413435
callback(null, newFile, resp);
414436
});
415437
};

handwritten/storage/system-test/storage.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,35 @@ describe('storage', function() {
860860
});
861861
});
862862

863+
it('should copy a large file', function(done) {
864+
var otherBucket = storage.bucket(generateName());
865+
var file = bucket.file('Big');
866+
var copiedFile = otherBucket.file(file.name);
867+
868+
async.series([
869+
function(callback) {
870+
var opts = { destination: file };
871+
bucket.upload(FILES.logo.path, opts, callback);
872+
},
873+
function(callback) {
874+
otherBucket.create({
875+
location: 'ASIA-EAST1',
876+
dra: true
877+
}, callback);
878+
},
879+
function(callback) {
880+
file.copy(copiedFile, callback);
881+
}
882+
], function(err) {
883+
assert.ifError(err);
884+
async.series([
885+
copiedFile.delete.bind(copiedFile),
886+
otherBucket.delete.bind(otherBucket),
887+
file.delete.bind(file)
888+
], done);
889+
});
890+
});
891+
863892
it('should copy to another bucket given a gs:// URL', function(done) {
864893
var opts = { destination: 'CloudLogo' };
865894
bucket.upload(FILES.logo.path, opts, function(err, file) {

handwritten/storage/test/file.js

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ describe('File', function() {
241241
it('should URI encode file names', function(done) {
242242
var newFile = new File(BUCKET, 'nested/file.jpg');
243243

244-
var expectedPath = format('/copyTo/b/{destBucket}/o/{destName}', {
244+
var expectedPath = format('/rewriteTo/b/{destBucket}/o/{destName}', {
245245
destBucket: file.bucket.name,
246246
destName: encodeURIComponent(newFile.name)
247247
});
@@ -295,7 +295,7 @@ describe('File', function() {
295295

296296
it('should allow a string', function(done) {
297297
var newFileName = 'new-file-name.png';
298-
var expectedPath = format('/copyTo/b/{destBucket}/o/{destName}', {
298+
var expectedPath = format('/rewriteTo/b/{destBucket}/o/{destName}', {
299299
destBucket: file.bucket.name,
300300
destName: newFileName
301301
});
@@ -305,7 +305,7 @@ describe('File', function() {
305305

306306
it('should allow a "gs://..." string', function(done) {
307307
var newFileName = 'gs://other-bucket/new-file-name.png';
308-
var expectedPath = format('/copyTo/b/{destBucket}/o/{destName}', {
308+
var expectedPath = format('/rewriteTo/b/{destBucket}/o/{destName}', {
309309
destBucket: 'other-bucket',
310310
destName: 'new-file-name.png'
311311
});
@@ -314,7 +314,7 @@ describe('File', function() {
314314
});
315315

316316
it('should allow a Bucket', function(done) {
317-
var expectedPath = format('/copyTo/b/{destBucket}/o/{destName}', {
317+
var expectedPath = format('/rewriteTo/b/{destBucket}/o/{destName}', {
318318
destBucket: BUCKET.name,
319319
destName: file.name
320320
});
@@ -324,7 +324,7 @@ describe('File', function() {
324324

325325
it('should allow a File', function(done) {
326326
var newFile = new File(BUCKET, 'new-file');
327-
var expectedPath = format('/copyTo/b/{destBucket}/o/{destName}', {
327+
var expectedPath = format('/rewriteTo/b/{destBucket}/o/{destName}', {
328328
destBucket: BUCKET.name,
329329
destName: newFile.name
330330
});
@@ -339,6 +339,45 @@ describe('File', function() {
339339
});
340340
});
341341

342+
describe('not finished copying', function() {
343+
var apiResponse = {
344+
rewriteToken: '...'
345+
};
346+
347+
beforeEach(function() {
348+
file.request = function(reqOpts, callback) {
349+
callback(null, apiResponse);
350+
};
351+
});
352+
353+
it('should continue attempting to copy', function(done) {
354+
var newFile = new File(BUCKET, 'new-file');
355+
356+
file.request = function(reqOpts, callback) {
357+
file.copy = function(newFile_, options, callback) {
358+
assert.strictEqual(newFile_, newFile);
359+
assert.deepEqual(options, { token: apiResponse.rewriteToken });
360+
callback(); // done()
361+
};
362+
363+
callback(null, apiResponse);
364+
};
365+
366+
file.copy(newFile, done);
367+
});
368+
369+
it('should make the subsequent correct API request', function(done) {
370+
var newFile = new File(BUCKET, 'new-file');
371+
372+
file.request = function(reqOpts) {
373+
assert.strictEqual(reqOpts.qs.rewriteToken, apiResponse.rewriteToken);
374+
done();
375+
};
376+
377+
file.copy(newFile, { token: apiResponse.rewriteToken }, assert.ifError);
378+
});
379+
});
380+
342381
describe('returned File object', function() {
343382
beforeEach(function() {
344383
var resp = { success: true };

0 commit comments

Comments
 (0)