diff --git a/lib/storage/file.js b/lib/storage/file.js index d58c94ec2cb..df6e60d57b7 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -104,7 +104,43 @@ var STORAGE_UPLOAD_BASE_URL = 'https://www.googleapis.com/upload/storage/v1/b'; * var file = myBucket.file('my-file'); */ function File(bucket, name, options) { + options = options || {}; + + this.bucket = bucket; + this.storage = bucket.parent; + + Object.defineProperty(this, 'name', { + enumerable: true, + value: name + }); + + var requestQueryObject = {}; + + if (is.number(options.generation)) { + requestQueryObject.generation = options.generation; + this.generation = options.generation; + } + var methods = { + /** + * Delete the file. + * + * @resource [Objects: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/delete} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * file.delete(function(err, apiResponse) {}); + */ + delete: { + reqOpts: { + qs: requestQueryObject + } + }, + /** * Check if the file exists. * @@ -126,7 +162,72 @@ function File(bucket, name, options) { * // file.metadata` has been populated. * }); */ - get: true + get: true, + + /** + * Get the file's metadata. + * + * @resource [Objects: get API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/get} + * + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The File's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * file.getMetadata(function(err, metadata, apiResponse) {}); + */ + getMetadata: { + reqOpts: { + qs: requestQueryObject + } + }, + + /** + * Merge the given metadata with the current remote file's metadata. This + * will set metadata if it was previously unset or update previously set + * metadata. To unset previously set metadata, set its value to null. + * + * You can set custom key/value pairs in the metadata key of the given + * object, however the other properties outside of this object must adhere + * to the [official API documentation](https://goo.gl/BOnnCK). + * + * See the examples below for more information. + * + * @resource [Objects: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/patch} + * + * @param {object} metadata - The metadata you wish to update. + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * file.setMetadata({ + * contentType: 'application/x-font-ttf', + * metadata: { + * my: 'custom', + * properties: 'go here' + * } + * }, function(err, apiResponse) {}); + * + * // Assuming current metadata = { hello: 'world', unsetMe: 'will do' } + * file.setMetadata({ + * metadata: { + * abc: '123', // will be set. + * unsetMe: null, // will be unset (deleted). + * hello: 'goodbye' // will be updated from 'hello' to 'goodbye'. + * } + * }, function(err, apiResponse) { + * // metadata should now be { abc: '123', hello: 'goodbye' } + * }); + */ + setMetadata: { + reqOpts: { + qs: requestQueryObject + } + } }; ServiceObject.call(this, { @@ -136,17 +237,6 @@ function File(bucket, name, options) { methods: methods }); - options = options || {}; - - this.bucket = bucket; - this.generation = parseInt(options.generation, 10); - this.storage = bucket.parent; - - Object.defineProperty(this, 'name', { - enumerable: true, - value: name - }); - /** * Google Cloud Storage uses access control lists (ACLs) to manage object and * bucket access. ACLs are the mechanism you use to share objects with other @@ -282,7 +372,7 @@ File.prototype.copy = function(destination, callback) { } var query = {}; - if (this.generation) { + if (is.number(this.generation)) { query.sourceGeneration = this.generation; } @@ -772,41 +862,6 @@ File.prototype.createWriteStream = function(options) { return stream; }; -/** - * Delete the file. - * - * @resource [Objects: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/delete} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.apiResponse - The full API response. - * - * @example - * file.delete(function(err, apiResponse) {}); - */ -File.prototype.delete = function(callback) { - callback = callback || util.noop; - - var query = {}; - - if (this.generation) { - query.generation = this.generation; - } - - this.request({ - method: 'DELETE', - uri: '', - qs: query - }, function(err, resp) { - if (err) { - callback(err, resp); - return; - } - - callback(null, resp); - }); -}; - /** * Convenience method to download a file into memory or to a local destination. * @@ -858,43 +913,6 @@ File.prototype.download = function(options, callback) { } }; -/** - * Get the file's metadata. - * - * @resource [Objects: get API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.metadata - The File's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * file.getMetadata(function(err, metadata, apiResponse) {}); - */ -File.prototype.getMetadata = function(callback) { - var self = this; - - callback = callback || util.noop; - - var query = {}; - if (this.generation) { - query.generation = this.generation; - } - - this.request({ - uri: '', - qs: query - }, function(err, resp) { - if (err) { - callback(err, null, resp); - return; - } - - self.metadata = resp; - callback(null, self.metadata, resp); - }); -}; - /** * Get a signed policy document to allow a user to upload data with a POST * request. @@ -1389,71 +1407,6 @@ File.prototype.move = function(destination, callback) { }); }; -/** - * Merge the given metadata with the current remote file's metadata. This will - * set metadata if it was previously unset or update previously set metadata. To - * unset previously set metadata, set its value to null. - * - * You can set custom key/value pairs in the metadata key of the given object, - * however the other properties outside of this object must adhere to the - * [official API documentation](https://goo.gl/BOnnCK). - * - * See the examples below for more information. - * - * @resource [Objects: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/patch} - * - * @param {object} metadata - The metadata you wish to update. - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this request - * @param {object} callback.apiResponse - The full API response. - * - * @example - * file.setMetadata({ - * contentType: 'application/x-font-ttf', - * metadata: { - * my: 'custom', - * properties: 'go here' - * } - * }, function(err, apiResponse) {}); - * - * // Assuming current metadata = { hello: 'world', unsetMe: 'will do' } - * file.setMetadata({ - * metadata: { - * abc: '123', // will be set. - * unsetMe: null, // will be unset (deleted). - * hello: 'goodbye' // will be updated from 'hello' to 'goodbye'. - * } - * }, function(err, apiResponse) { - * // metadata should now be { abc: '123', hello: 'goodbye' } - * }); - */ -File.prototype.setMetadata = function(metadata, callback) { - var self = this; - - callback = callback || util.noop; - - var query = {}; - if (this.generation) { - query.generation = this.generation; - } - - this.request({ - method: 'PATCH', - uri: '', - qs: query, - json: metadata - }, function(err, resp) { - if (err) { - callback(err, resp); - return; - } - - self.metadata = resp; - - callback(null, resp); - }); -}; - /** * This creates a gcs-resumable-upload upload stream. * @@ -1511,7 +1464,7 @@ File.prototype.startSimpleUpload_ = function(dup, metadata) { }) }; - if (this.generation) { + if (is.number(this.generation)) { reqOpts.qs.ifGenerationMatch = this.generation; } diff --git a/test/storage/file.js b/test/storage/file.js index 2765e5fde19..0e8229a7d85 100644 --- a/test/storage/file.js +++ b/test/storage/file.js @@ -164,8 +164,59 @@ describe('File', function() { assert.strictEqual(calledWith.baseUrl, '/o'); assert.strictEqual(calledWith.id, encodeURIComponent(FILE_NAME)); assert.deepEqual(calledWith.methods, { + delete: { + reqOpts: { + qs: {} + } + }, exists: true, - get: true + get: true, + getMetadata: { + reqOpts: { + qs: {} + } + }, + setMetadata: { + reqOpts: { + qs: {} + } + } + }); + }); + + it('should set generation on the request methods', function() { + var options = { + generation: 82834 + }; + + var file = new File(BUCKET, 'name', options); + + var calledWith = file.calledWith_[0]; + + assert.deepEqual(calledWith.methods, { + delete: { + reqOpts: { + qs: { + generation: options.generation + } + } + }, + exists: true, + get: true, + getMetadata: { + reqOpts: { + qs: { + generation: options.generation + } + } + }, + setMetadata: { + reqOpts: { + qs: { + generation: options.generation + } + } + } }); }); }); @@ -1148,69 +1199,6 @@ describe('File', function() { }); }); - describe('delete', function() { - it('should delete the file', function(done) { - file.request = function(reqOpts) { - assert.strictEqual(reqOpts.method, 'DELETE'); - assert.equal(reqOpts.uri, ''); - assert.deepEqual(reqOpts.qs, {}); - - done(); - }; - - file.delete(); - }); - - it('should execute callback with error & API response', function(done) { - var error = new Error('Error.'); - var apiResponse = {}; - - file.request = function(reqOpts, callback) { - callback(error, apiResponse); - }; - - file.delete(function(err, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); - - done(); - }); - }); - - it('should send query.generation if File has one', function(done) { - var versionedFile = new File(BUCKET, 'new-file.txt', { generation: 1 }); - - versionedFile.request = function(reqOpts) { - assert.strictEqual(reqOpts.qs.generation, 1); - - done(); - }; - - versionedFile.delete(); - }); - - it('should execute callback', function(done) { - file.request = function(reqOpts, callback) { - callback(); - }; - - file.delete(done); - }); - - it('should execute callback with apiResponse', function(done) { - var resp = { success: true }; - - file.request = function(reqOpts, callback) { - callback(null, resp); - }; - - file.delete(function(err, apiResponse) { - assert.deepEqual(resp, apiResponse); - done(); - }); - }); - }); - describe('download', function() { var fileReadStream; @@ -1343,72 +1331,6 @@ describe('File', function() { }); }); - describe('getMetadata', function() { - var metadata = { a: 'b', c: 'd' }; - - it('should get the metadata of a file', function(done) { - file.request = function(reqOpts) { - assert.strictEqual(reqOpts.uri, ''); - assert.deepEqual(reqOpts.qs, {}); - - done(); - }; - - file.getMetadata(); - }); - - it('should send query.generation if File has one', function(done) { - var versionedFile = new File(BUCKET, 'new-file.txt', { generation: 1 }); - - versionedFile.request = function(reqOpts) { - assert.strictEqual(reqOpts.qs.generation, 1); - done(); - }; - - versionedFile.getMetadata(); - }); - - it('should execute callback', function(done) { - file.request = function(reqOpts, callback) { - callback(); - }; - - file.getMetadata(done); - }); - - it('should execute callback with apiResponse', function(done) { - var resp = { success: true }; - file.request = function(reqOpts, callback) { - callback(null, resp); - }; - file.getMetadata(function(err, metadata, apiResponse) { - assert.deepEqual(resp, apiResponse); - done(); - }); - }); - - it('should update metadata property on object', function() { - file.request = function(reqOpts, callback) { - callback(null, metadata); - }; - assert.deepEqual(file.metadata, {}); - file.getMetadata(function(err, newMetadata) { - assert.deepEqual(newMetadata, metadata); - }); - assert.deepEqual(file.metadata, metadata); - }); - - it('should pass metadata to callback', function(done) { - file.request = function(reqOpts, callback) { - callback(null, metadata); - }; - file.getMetadata(function(err, fileMetadata) { - assert.deepEqual(fileMetadata, metadata); - done(); - }); - }); - }); - describe('getSignedPolicy', function() { var credentials = require('../testdata/privateKeyFile.json'); @@ -2042,73 +1964,6 @@ describe('File', function() { }); }); - describe('setMetadata', function() { - var metadata = { fake: 'metadata' }; - - it('should set metadata', function(done) { - file.request = function(reqOpts) { - assert.strictEqual(reqOpts.method, 'PATCH'); - assert.strictEqual(reqOpts.uri, ''); - assert.deepEqual(reqOpts.json, metadata); - done(); - }; - file.setMetadata(metadata); - }); - - it('should send query.generation if File has one', function(done) { - var versionedFile = new File(BUCKET, 'new-file.txt', { generation: 1 }); - - versionedFile.request = function(reqOpts) { - assert.strictEqual(reqOpts.qs.generation, 1); - done(); - }; - - versionedFile.setMetadata(); - }); - - it('should execute callback', function(done) { - file.request = function(reqOpts, callback) { - callback(); - }; - file.setMetadata(metadata, done); - }); - - it('should execute callback with error & API response', function(done) { - var error = new Error('Error.'); - var apiResponse = {}; - - file.request = function(reqOpts, callback) { - callback(error, apiResponse); - }; - - file.setMetadata(metadata, function(err, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); - done(); - }); - }); - - it('should execute callback with apiResponse', function(done) { - var resp = { success: true }; - file.request = function(reqOpts, callback) { - callback(null, resp); - }; - file.setMetadata(metadata, function(err, apiResponse) { - assert.deepEqual(resp, apiResponse); - done(); - }); - }); - - it('should update internal metadata property', function() { - file.request = function(reqOpts, callback) { - callback(null, metadata); - }; - file.setMetadata(metadata, function() { - assert.deepEqual(file.metadata, metadata); - }); - }); - }); - describe('startResumableUpload_', function() { describe('starting', function() { it('should start a resumable upload', function(done) {