Skip to content

Commit

Permalink
storage: support specifying a generation
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenplusplus committed Feb 24, 2015
1 parent 6938342 commit d8e3b84
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 32 deletions.
7 changes: 5 additions & 2 deletions lib/storage/bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,16 @@ Bucket.prototype.delete = function(callback) {
* the different use cases you may have.
*
* @param {string} name - The name of the file in this bucket.
* @param {object=} options - Configuration options.
* @param {string|number} options.generation - Only use a specific revision of
* this file.
* @return {module:storage/file}
*
* @example
* var file = bucket.file('my-existing-file.png');
*/
Bucket.prototype.file = function(name) {
return new File(this, name);
Bucket.prototype.file = function(name, options) {
return new File(this, name, options);
};

/**
Expand Down
108 changes: 84 additions & 24 deletions lib/storage/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ var STORAGE_UPLOAD_BASE_URL = 'https://www.googleapis.com/upload/storage/v1/b';
* @alias module:storage/file
* @constructor
*/
function File(bucket, name, metadata) {
function File(bucket, name, options) {
if (!name) {
throw Error('A file name must be specified.');
}

options = options || {};

this.bucket = bucket;
this.explicitGeneration = options.generation;
this.makeReq_ = bucket.makeReq_.bind(bucket);
this.metadata = metadata || {};
this.metadata = {};

Object.defineProperty(this, 'name', {
enumerable: true,
Expand Down Expand Up @@ -180,39 +183,55 @@ function File(bucket, name, metadata) {
*/
File.prototype.copy = function(destination, callback) {
var noDestinationError = new Error('Destination file should have a name.');

if (!destination) {
throw noDestinationError;
}

callback = callback || util.noop;

var destBucket;
var destName;
var newFile;

if (util.is(destination, 'string')) {
destBucket = this.bucket;
destName = destination;
}

if (destination.constructor && destination.constructor.name === 'Bucket') {
destBucket = destination;
destName = this.name;
}

if (destination instanceof File) {
destBucket = destination.bucket;
destName = destination.name;
newFile = destination;
}

if (!destName) {
throw noDestinationError;
}

var path = util.format('/o/{srcName}/copyTo/b/{destBucket}/o/{destName}', {
srcName: encodeURIComponent(this.name),
destBucket: destBucket.name,
destName: encodeURIComponent(destName)
});
this.makeReq_('POST', path, null, {}, function(err) {

var query = {};

if (this.explicitGeneration) {
query.sourceGeneration = this.explicitGeneration;
}

this.makeReq_('POST', path, query, {}, function(err) {
if (err) {
callback(err);
return;
}

callback(null, newFile || destBucket.file(destName));
});
};
Expand Down Expand Up @@ -324,6 +343,12 @@ File.prototype.createReadStream = function(options) {
uri: uri
};

if (that.explicitGeneration) {
reqOpts.qs = {
generation: that.explicitGeneration
};
}

if (rangeRequest) {
reqOpts.headers = {
Range: 'bytes=' + [options.start || '', options.end || ''].join('-')
Expand Down Expand Up @@ -611,14 +636,22 @@ File.prototype.createWriteStream = function(options) {
*/
File.prototype.delete = function(callback) {
callback = callback || util.noop;

var path = '/o/' + encodeURIComponent(this.name);
this.makeReq_('DELETE', path, null, true, function(err) {
var query = {};

if (this.explicitGeneration) {
query.generation = this.explicitGeneration;
}

this.makeReq_('DELETE', path, query, true, function(err) {
if (err) {
callback(err);
return;
}

callback();
}.bind(this));
});
};

/**
Expand Down Expand Up @@ -687,15 +720,24 @@ File.prototype.download = function(options, callback) {
*/
File.prototype.getMetadata = function(callback) {
callback = callback || util.noop;

var that = this;
var path = '/o/' + encodeURIComponent(this.name);
this.makeReq_('GET', path, null, true, function(err, resp) {
var query = {};

if (this.explicitGeneration) {
query.generation = this.explicitGeneration;
}

this.makeReq_('GET', path, query, true, function(err, resp) {
if (err) {
callback(err);
return;
}
this.metadata = resp;
callback(null, this.metadata);
}.bind(this));

that.metadata = resp;
callback(null, that.metadata);
});
};

/**
Expand Down Expand Up @@ -781,11 +823,17 @@ File.prototype.getSignedUrl = function(options, callback) {
* }, function(err, metadata) {});
*/
File.prototype.setMetadata = function(metadata, callback) {
callback = callback || util.noop;

var that = this;
var path = '/o/' + encodeURIComponent(this.name);
callback = callback || util.noop;
var query = {};

if (this.explicitGeneration) {
query.generation = this.explicitGeneration;
}

this.makeReq_('PATCH', path, null, metadata, function(err, resp) {
this.makeReq_('PATCH', path, query, metadata, function(err, resp) {
if (err) {
callback(err);
return;
Expand Down Expand Up @@ -864,19 +912,25 @@ File.prototype.startResumableUpload_ = function(stream, metadata) {
headers['X-Upload-Content-Type'] = metadata.contentType;
}

makeAuthorizedRequest({
var reqOpts = {
method: 'POST',
uri: util.format('{base}/{bucket}/o', {
base: STORAGE_UPLOAD_BASE_URL,
bucket: that.bucket.name
}),
qs: {
name: that.name,
uploadType: 'resumable'
uploadType: 'resumable',
},
headers: headers,
json: metadata
}, function(err, res, body) {
};

if (that.explicitGeneration) {
reqOpts.qs.ifGenerationMatch = that.explicitGeneration;
}

makeAuthorizedRequest(reqOpts, function(err, res, body) {
if (err) {
handleError(err);
return;
Expand Down Expand Up @@ -932,7 +986,7 @@ File.prototype.startResumableUpload_ = function(stream, metadata) {
// by caching a slice of the first chunk, then comparing it with the first
// byte of incoming data.
if (bytesWritten === 0) {
var cachedFirstChunk = configStore.get(that.name).firstChunk;
var cachedFirstChunk = config && config.firstChunk;
var firstChunk = chunk.slice(0, 16);

if (!cachedFirstChunk) {
Expand Down Expand Up @@ -1070,18 +1124,24 @@ File.prototype.startResumableUpload_ = function(stream, metadata) {
File.prototype.startSimpleUpload_ = function(stream, metadata) {
var that = this;

var reqOpts = {
qs: {
name: that.name
},
uri: util.format('{base}/{bucket}/o', {
base: STORAGE_UPLOAD_BASE_URL,
bucket: that.bucket.name
})
};

if (this.explicitGeneration) {
reqOpts.qs.ifGenerationMatch = this.explicitGeneration;
}

util.makeWritableStream(stream, {
makeAuthorizedRequest: that.bucket.storage.makeAuthorizedRequest_,
metadata: metadata,
request: {
qs: {
name: that.name
},
uri: util.format('{base}/{bucket}/o', {
base: STORAGE_UPLOAD_BASE_URL,
bucket: that.bucket.name
})
}
request: reqOpts
}, function(data) {
that.metadata = data;

Expand Down
11 changes: 5 additions & 6 deletions test/storage/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,9 @@ describe('File', function() {
assert.equal(file.name, FILE_NAME);
});

it('should assign metadata if provided', function() {
var metadata = { a: 'b', c: 'd' };
var newFile = new File(bucket, FILE_NAME, metadata);
assert.deepEqual(newFile.metadata, metadata);
it('should accept specifying a generation', function() {
var file = new File(bucket, 'name', { generation: 2 });
assert.equal(file.explicitGeneration, 2);
});
});

Expand Down Expand Up @@ -719,7 +718,7 @@ describe('File', function() {
file.makeReq_ = function(method, path, query, body) {
assert.equal(method, 'DELETE');
assert.equal(path, '/o/' + FILE_NAME);
assert.strictEqual(query, null);
assert.deepEqual(query, {});
assert.strictEqual(body, true);
done();
};
Expand Down Expand Up @@ -882,7 +881,7 @@ describe('File', function() {
file.makeReq_ = function(method, path, query, body) {
assert.equal(method, 'GET');
assert.equal(path, '/o/' + FILE_NAME);
assert.strictEqual(query, null);
assert.deepEqual(query, {});
assert.strictEqual(body, true);
done();
};
Expand Down

0 comments on commit d8e3b84

Please sign in to comment.