ran-boilerplate
Version:
React . Apollo (GraphQL) . Next.js Toolkit
1,810 lines (1,679 loc) • 67.9 kB
JavaScript
/**
* Copyright 2014-2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var arrify = require('arrify');
var async = require('async');
var common = require('@google-cloud/common');
var extend = require('extend');
var fs = require('fs');
var is = require('is');
var mime = require('mime-types');
var path = require('path');
var snakeize = require('snakeize');
var util = require('util');
var request = require('request');
var Acl = require('./acl.js');
var File = require('./file.js');
var Iam = require('./iam.js');
var Notification = require('./notification.js');
/**
* The size of a file (in bytes) must be greater than this number to
* automatically trigger a resumable upload.
*
* @const {number}
* @private
*/
var RESUMABLE_THRESHOLD = 5000000;
/**
* Create a Bucket object to interact with a Cloud Storage bucket.
*
* @class
* @hideconstructor
*
* @param {Storage} storage A {@link Storage} instance.
* @param {string} name The name of the bucket.
* @param {object} [options] Configuration object.
* @param {string} [options.userProject] User project.
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*/
function Bucket(storage, name, options) {
options = options || {};
var methods = {
/**
* Create a bucket.
*
* @method Bucket#create
* @param {CreateBucketRequest} [metadata] Metadata to set for the bucket.
* @param {CreateBucketCallback} [callback] Callback function.
* @returns {Promise<CreateBucketResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
* bucket.create(function(err, bucket, apiResponse) {
* if (!err) {
* // The bucket was created successfully.
* }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.create().then(function(data) {
* var bucket = data[0];
* var apiResponse = data[1];
* });
*/
create: true,
};
common.ServiceObject.call(this, {
parent: storage,
baseUrl: '/b',
id: name,
createMethod: storage.createBucket.bind(storage),
methods: methods,
});
/**
* The bucket's name.
* @name Bucket#name
* @type {string}
*/
this.name = name;
/**
* A reference to the {@link Storage} associated with this {@link Bucket}
* instance.
* @name Bucket#storage
* @type {string}
*/
this.storage = storage;
/**
* A user project to apply to each request from this bucket.
* @name Bucket#userProject
* @type {string}
*/
this.userProject = options.userProject;
/**
* Cloud Storage uses access control lists (ACLs) to manage object and
* bucket access. ACLs are the mechanism you use to share objects with other
* users and allow other users to access your buckets and objects.
*
* An ACL consists of one or more entries, where each entry grants permissions
* to an entity. Permissions define the actions that can be performed against
* an object or bucket (for example, `READ` or `WRITE`); the entity defines
* who the permission applies to (for example, a specific user or group of
* users).
*
* The `acl` object on a Bucket instance provides methods to get you a list of
* the ACLs defined on your bucket, as well as set, update, and delete them.
*
* Buckets also have
* [default ACLs](https://cloud.google.com/storage/docs/access-control/lists#default)
* for all created files. Default ACLs specify permissions that all new
* objects added to the bucket will inherit by default. You can add, delete,
* get, and update entities and permissions for these as well with
* {@link Bucket#acl.default}.
*
* @see [About Access Control Lists]{@link http://goo.gl/6qBBPO}
* @see [Default ACLs]{@link https://cloud.google.com/storage/docs/access-control/lists#default}
*
* @name Bucket#acl
* @mixes Acl
* @property {Acl} default Cloud Storage Buckets have
* [default ACLs](https://cloud.google.com/storage/docs/access-control/lists#default)
* for all created files. You can add, delete, get, and update entities and
* permissions for these as well. The method signatures and examples are all
* the same, after only prefixing the method call with `default`.
*
* @example
* var storage = require('@google-cloud/storage')();
*
* //-
* // Make a bucket's contents publicly readable.
* //-
* var myBucket = storage.bucket('my-bucket');
*
* var options = {
* entity: 'allUsers',
* role: storage.acl.READER_ROLE
* };
*
* myBucket.acl.add(options, function(err, aclObject) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* myBucket.acl.add(options).then(function(data) {
* var aclObject = data[0];
* var apiResponse = data[1];
* });
*
* @example <caption>include:samples/acl.js</caption>
* region_tag:storage_print_bucket_acl
* Example of printing a bucket's ACL:
*
* @example <caption>include:samples/acl.js</caption>
* region_tag:storage_print_bucket_acl_for_user
* Example of printing a bucket's ACL for a specific user:
*
* @example <caption>include:samples/acl.js</caption>
* region_tag:storage_add_bucket_owner
* Example of adding an owner to a bucket:
*
* @example <caption>include:samples/acl.js</caption>
* region_tag:storage_remove_bucket_owner
* Example of removing an owner from a bucket:
*
* @example <caption>include:samples/acl.js</caption>
* region_tag:storage_add_bucket_default_owner
* Example of adding a default owner to a bucket:
*
* @example <caption>include:samples/acl.js</caption>
* region_tag:storage_remove_bucket_default_owner
* Example of removing a default owner from a bucket:
*/
this.acl = new Acl({
request: this.request.bind(this),
pathPrefix: '/acl',
});
this.acl.default = new Acl({
request: this.request.bind(this),
pathPrefix: '/defaultObjectAcl',
});
/**
* Get and set IAM policies for your bucket.
*
* @name Bucket#iam
* @mixes Iam
*
* @see [Cloud Storage IAM Management](https://cloud.google.com/storage/docs/access-control/iam#short_title_iam_management)
* @see [Granting, Changing, and Revoking Access](https://cloud.google.com/iam/docs/granting-changing-revoking-access)
* @see [IAM Roles](https://cloud.google.com/iam/docs/understanding-roles)
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* //-
* // Get the IAM policy for your bucket.
* //-
* bucket.iam.getPolicy(function(err, policy) {
* console.log(policy);
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.iam.getPolicy().then(function(data) {
* var policy = data[0];
* var apiResponse = data[1];
* });
*
* @example <caption>include:samples/iam.js</caption>
* region_tag:storage_view_bucket_iam_members
* Example of retrieving a bucket's IAM policy:
*
* @example <caption>include:samples/iam.js</caption>
* region_tag:storage_add_bucket_iam_member
* Example of adding to a bucket's IAM policy:
*
* @example <caption>include:samples/iam.js</caption>
* region_tag:storage_remove_bucket_iam_member
* Example of removing from a bucket's IAM policy:
*/
this.iam = new Iam(this);
}
util.inherits(Bucket, common.ServiceObject);
/**
* @typedef {array} CombineResponse
* @property {File} 0 The new {@link File}.
* @property {object} 1 The full API response.
*/
/**
* @callback CombineCallback
* @param {?Error} err Request error, if any.
* @param {File} newFile The new {@link File}.
* @param {object} apiResponse The full API response.
*/
/**
* Combine multiple files into one new file.
*
* @see [Objects: compose API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/compose}
*
* @throws {Error} if a non-array is provided as sources argument.
* @throws {Error} if less than two sources are provided.
* @throws {Error} if no destination is provided.
* @throws {Error} if content type can't be determined for the destination file.
*
* @param {string[]|File[]} sources The source files that will be
* combined.
* @param {string|File} destination The file you would like the
* source files combined into.
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {CombineCallback} [callback] Callback function.
* @returns {Promise<CombineResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var logBucket = storage.bucket('log-bucket');
*
* var sources = [
* logBucket.file('2013-logs.txt'),
* logBucket.file('2014-logs.txt')
* ];
*
* var allLogs = logBucket.file('all-logs.txt');
*
* logBucket.combine(sources, allLogs, function(err, newFile, apiResponse) {
* // newFile === allLogs
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* logBucket.combine(sources, allLogs).then(function(data) {
* var newFile = data[0];
* var apiResponse = data[1];
* });
*/
Bucket.prototype.combine = function(sources, destination, options, callback) {
if (!is.array(sources) || sources.length < 2) {
throw new Error('You must provide at least two source files.');
}
if (!destination) {
throw new Error('A destination file must be specified.');
}
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
sources = sources.map(convertToFile);
destination = convertToFile(destination);
callback = callback || common.util.noop;
if (!destination.metadata.contentType) {
var destinationContentType = mime.contentType(destination.name);
if (destinationContentType) {
destination.metadata.contentType = destinationContentType;
} else {
throw new Error(
'A content type could not be detected for the destination file.'
);
}
}
// Make the request from the destination File object.
destination.request(
{
method: 'POST',
uri: '/compose',
json: {
destination: {
contentType: destination.metadata.contentType,
},
sourceObjects: sources.map(function(source) {
var sourceObject = {
name: source.name,
};
if (source.metadata && source.metadata.generation) {
sourceObject.generation = source.metadata.generation;
}
return sourceObject;
}),
},
qs: options,
},
function(err, resp) {
if (err) {
callback(err, null, resp);
return;
}
callback(null, destination, resp);
}
);
function convertToFile(file) {
if (file instanceof File) {
return file;
}
return self.file(file);
}
};
/**
* @typedef {array} CreateChannelResponse
* @property {Channel} 0 The new {@link Channel}.
* @property {object} 1 The full API response.
*/
/**
* @callback CreateChannelCallback
* @param {?Error} err Request error, if any.
* @param {Channel} channel The new {@link Channel}.
* @param {object} apiResponse The full API response.
*/
/**
* Create a channel that will be notified when objects in this bucket changes.
*
* @throws {Error} If an ID is not provided.
* @throws {Error} If an address is not provided.
*
* @see [Objects: watchAll API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/watchAll}
*
* @param {string} id The ID of the channel to create.
* @param {object} config See a
* [Objects: watchAll request body](https://cloud.google.com/storage/docs/json_api/v1/objects/watchAll).
* @param {string} config.address The address where notifications are
* delivered for this channel.
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {CreateChannelCallback} [callback] Callback function.
* @returns {Promise<CreateChannelResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
* var id = 'new-channel-id';
*
* var config = {
* address: 'https://...'
* };
*
* bucket.createChannel(id, config, function(err, channel, apiResponse) {
* if (!err) {
* // Channel created successfully.
* }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.createChannel(id, config).then(function(data) {
* var channel = data[0];
* var apiResponse = data[1];
* });
*/
Bucket.prototype.createChannel = function(id, config, options, callback) {
var self = this;
if (!is.string(id)) {
throw new Error('An ID is required to create a channel.');
}
if (!is.string(config.address)) {
throw new Error('An address is required to create a channel.');
}
if (is.fn(options)) {
callback = options;
options = {};
}
this.request(
{
method: 'POST',
uri: '/o/watch',
json: extend(
{
id: id,
type: 'web_hook',
},
config
),
qs: options,
},
function(err, apiResponse) {
if (err) {
callback(err, null, apiResponse);
return;
}
var resourceId = apiResponse.resourceId;
var channel = self.storage.channel(id, resourceId);
channel.metadata = apiResponse;
callback(null, channel, apiResponse);
}
);
};
/**
* Metadata to set for the Notification.
*
* @typedef {object} CreateNotificationRequest
* @property {object} [customAttributes] An optional list of additional
* attributes to attach to each Cloud PubSub message published for this
* notification subscription.
* @property {string[]} [eventTypes] If present, only send notifications about
* listed event types. If empty, sent notifications for all event types.
* @property {string} [objectNamePrefix] If present, only apply this
* notification configuration to object names that begin with this prefix.
* @property {string} [payloadFormat] The desired content of the Payload.
* Defaults to `JSON_API_V1`.
*
* Acceptable values are:
* - `JSON_API_V1`
*
* - `NONE`
* @property {string} [options.userProject] The ID of the project which will be
* billed for the request.
*/
/**
* @typedef {array} CreateNotificationResponse
* @property {Notification} 0 The new {@link Notification}.
* @property {object} 1 The full API response.
*/
/**
* @callback CreateNotificationCallback
* @param {?Error} err Request error, if any.
* @param {Notification} notification The new {@link Notification}.
* @param {object} apiResponse The full API response.
*/
/**
* Creates a notification subscription for the bucket.
*
* @see [Notifications: insert]{@link https://cloud.google.com/storage/docs/json_api/v1/notifications/insert}
*
* @param {Topic|string} topic The Cloud PubSub topic to which this
* subscription publishes. If the project ID is omitted, the current project
* ID will be used.
*
* Acceptable formats are:
* - `projects/grape-spaceship-123/topics/my-topic`
*
* - `my-topic`
* @param {CreateNotificationRequest} [options] Metadata to set for the
* notification.
* @param {CreateNotificationCallback} [callback] Callback function.
* @returns {Promise<CreateNotificationResponse>}
* @throws {Error} If a valid topic is not provided.
* @see Notification#create
*
* @example
* var storage = require('@google-cloud/storage')();
* var myBucket = storage.bucket('my-bucket');
*
* var callback = function(err, notification, apiResponse) {
* if (!err) {
* // The notification was created successfully.
* }
* };
*
* myBucket.createNotification('my-topic', callback);
*
* //-
* // Configure the nofiication by providing Notification metadata.
* //-
* var metadata = {
* objectNamePrefix: 'prefix-'
* };
*
* myBucket.createNotification('my-topic', metadata, callback);
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* myBucket.createNotification('my-topic').then(function(data) {
* var notification = data[0];
* var apiResponse = data[1];
* });
*
* @example <caption>include:samples/notifications.js</caption>
* region_tag:storage_create_notification
* Another example:
*/
Bucket.prototype.createNotification = function(topic, options, callback) {
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
if (is.object(topic) && common.util.isCustomType(topic, 'pubsub/topic')) {
topic = topic.name;
}
if (!is.string(topic)) {
throw new Error('A valid topic name is required.');
}
var body = extend({topic: topic}, options);
if (body.topic.indexOf('projects') !== 0) {
body.topic = 'projects/{{projectId}}/topics/' + body.topic;
}
body.topic = '//pubsub.googleapis.com/' + body.topic;
if (!body.payloadFormat) {
body.payloadFormat = 'JSON_API_V1';
}
var query = {};
if (body.userProject) {
query.userProject = body.userProject;
delete body.userProject;
}
this.request(
{
method: 'POST',
uri: '/notificationConfigs',
json: snakeize(body),
qs: query,
},
function(err, apiResponse) {
if (err) {
callback(err, null, apiResponse);
return;
}
var notification = self.notification(apiResponse.id);
notification.metadata = apiResponse;
callback(null, notification, apiResponse);
}
);
};
/**
* @typedef {array} DeleteBucketResponse
* @property {object} 0 The full API response.
*/
/**
* @callback DeleteBucketCallback
* @param {?Error} err Request error, if any.
* @param {object} apiResponse The full API response.
*/
/**
* Delete the bucket.
*
* @see [Buckets: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/delete}
*
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {DeleteBucketCallback} [callback] Callback function.
* @returns {Promise<DeleteBucketResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
* bucket.delete(function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.delete().then(function(data) {
* var apiResponse = data[0];
* });
*
* @example <caption>include:samples/buckets.js</caption>
* region_tag:storage_delete_bucket
* Another example:
*/
Bucket.prototype.delete = function(options, callback) {
if (is.fn(options)) {
callback = options;
options = {};
}
this.request(
{
method: 'DELETE',
uri: '',
qs: options,
},
callback || common.util.noop
);
};
/**
* @callback DeleteFilesCallback
* @param {?Error|?Error[]} err Request error, if any, or array of errors from
* files that were not able to be deleted.
* @param {object} apiResponse The full API response.
*/
/**
* Iterate over the bucket's files, calling `file.delete()` on each.
*
* <strong>This is not an atomic request.</strong> A delete attempt will be made
* for each file individually. Any one can fail, in which case only a portion of
* the files you intended to be deleted would have.
*
* Operations are performed in parallel, up to 10 at once. The first error
* breaks the loop and will execute the provided callback with it. Specify
* `{ force: true }` to suppress the errors until all files have had a chance to
* be processed.
*
* The `query` object passed as the first argument will also be passed to
* {@link Bucket#getFiles}.
*
* @see [Objects: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/delete}
*
* @param {object} [query] Query object. See {@link Bucket#getFiles}
* for all of the supported properties.
* @param {boolean} [query.force] Suppress errors until all files have been
* processed.
* @param {string} [query.userProject] The ID of the project which will be
* billed for the request.
* @param {DeleteFilesCallback} [callback] Callback function.
* @returns {Promise}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* //-
* // Delete all of the files in the bucket.
* //-
* bucket.deleteFiles(function(err) {});
*
* //-
* // By default, if a file cannot be deleted, this method will stop deleting
* // files from your bucket. You can override this setting with `force: true`.
* //-
* bucket.deleteFiles({
* force: true
* }, function(errors) {
* // `errors`:
* // Array of errors if any occurred, otherwise null.
* });
*
* //-
* // The first argument to this method acts as a query to
* // {@link Bucket#getFiles}. As an example, you can delete files
* // which match a prefix.
* //-
* bucket.deleteFiles({
* prefix: 'images/'
* }, function(err) {
* if (!err) {
* // All files in the `images` directory have been deleted.
* }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.deleteFiles().then(function() {});
*/
Bucket.prototype.deleteFiles = function(query, callback) {
if (is.fn(query)) {
callback = query;
query = {};
}
query = query || {};
var MAX_PARALLEL_LIMIT = 10;
var errors = [];
this.getFiles(query, function(err, files) {
if (err) {
callback(err);
return;
}
function deleteFile(file, callback) {
file.delete(query, function(err) {
if (err) {
if (query.force) {
errors.push(err);
callback();
return;
}
callback(err);
return;
}
callback();
});
}
// Iterate through each file and attempt to delete it.
async.eachLimit(files, MAX_PARALLEL_LIMIT, deleteFile, function(err) {
if (err || errors.length > 0) {
callback(err || errors);
return;
}
callback();
});
});
};
/**
* @typedef {array} DeleteLabelsResponse
* @property {object} 0 The full API response.
*/
/**
* @callback DeleteLabelsCallback
* @param {?Error} err Request error, if any.
* @param {object} apiResponse The full API response.
*/
/**
* Delete one or more labels from this bucket.
*
* @param {string|string[]} labels The labels to delete. If no labels are
* provided, all of the labels are removed.
* @param {DeleteLabelsCallback} [callback] Callback function.
* @returns {Promise<DeleteLabelsResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* //-
* // Delete all of the labels from this bucket.
* //-
* bucket.deleteLabels(function(err, apiResponse) {});
*
* //-
* // Delete a single label.
* //-
* bucket.deleteLabels('labelone', function(err, apiResponse) {});
*
* //-
* // Delete a specific set of labels.
* //-
* bucket.deleteLabels([
* 'labelone',
* 'labeltwo'
* ], function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.deleteLabels().then(function(data) {
* var apiResponse = data[0];
* });
*/
Bucket.prototype.deleteLabels = function(labels, callback) {
var self = this;
if (is.fn(labels)) {
callback = labels;
labels = [];
}
labels = arrify(labels);
if (labels.length === 0) {
this.getLabels(function(err, labels) {
if (err) {
callback(err);
return;
}
deleteLabels(Object.keys(labels));
});
} else {
deleteLabels(labels);
}
function deleteLabels(labels) {
var nullLabelMap = labels.reduce(function(nullLabelMap, labelKey) {
nullLabelMap[labelKey] = null;
return nullLabelMap;
}, {});
self.setLabels(nullLabelMap, callback);
}
};
/**
* @typedef {array} DisableRequesterPaysResponse
* @property {object} 0 The full API response.
*/
/**
* @callback DisableRequesterPaysCallback
* @param {?Error} err Request error, if any.
* @param {object} apiResponse The full API response.
*/
/**
* <div class="notice">
* <strong>Early Access Testers Only</strong>
* <p>
* This feature is not yet widely-available.
* </p>
* </div>
*
* Disable `requesterPays` functionality from this bucket.
*
* @param {DisableRequesterPaysCallback} [callback] Callback function.
* @returns {Promise<DisableRequesterPaysCallback>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.disableRequesterPays(function(err, apiResponse) {
* if (!err) {
* // requesterPays functionality disabled successfully.
* }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.disableRequesterPays().then(function(data) {
* var apiResponse = data[0];
* });
*
* @example <caption>include:samples/requesterPays.js</caption>
* region_tag:storage_disable_requester_pays
* Example of disabling requester pays:
*/
Bucket.prototype.disableRequesterPays = function(callback) {
this.setMetadata(
{
billing: {
requesterPays: false,
},
},
callback || common.util.noop
);
};
/**
* @typedef {array} EnableRequesterPaysResponse
* @property {object} 0 The full API response.
*/
/**
* @callback EnableRequesterPaysCallback
* @param {?Error} err Request error, if any.
* @param {object} apiResponse The full API response.
*/
/**
* <div class="notice">
* <strong>Early Access Testers Only</strong>
* <p>
* This feature is not yet widely-available.
* </p>
* </div>
*
* Enable `requesterPays` functionality for this bucket. This enables you, the
* bucket owner, to have the requesting user assume the charges for the access
* to your bucket and its contents.
*
* @param {EnableRequesterPaysCallback} [callback] Callback function.
* @returns {Promise<EnableRequesterPaysResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.enableRequesterPays(function(err, apiResponse) {
* if (!err) {
* // requesterPays functionality enabled successfully.
* }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.enableRequesterPays().then(function(data) {
* var apiResponse = data[0];
* });
*
* @example <caption>include:samples/requesterPays.js</caption>
* region_tag:storage_enable_requester_pays
* Example of enabling requester pays:
*/
Bucket.prototype.enableRequesterPays = function(callback) {
this.setMetadata(
{
billing: {
requesterPays: true,
},
},
callback || common.util.noop
);
};
/**
* @typedef {array} BucketExistsResponse
* @property {boolean} 0 Whether the {@link Bucket} exists.
*/
/**
* @callback BucketExistsCallback
* @param {?Error} err Request error, if any.
* @param {boolean} exists Whether the {@link Bucket} exists.
*/
/**
* Check if the bucket exists.
*
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {BucketExistsCallback} [callback] Callback function.
* @returns {Promise<BucketExistsResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.exists(function(err, exists) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.exists().then(function(data) {
* var exists = data[0];
* });
*/
Bucket.prototype.exists = function(options, callback) {
if (is.fn(options)) {
callback = options;
options = {};
}
options = options || {};
this.get(options, function(err) {
if (err) {
if (err.code === 404) {
callback(null, false);
} else {
callback(err);
}
return;
}
callback(null, true);
});
};
/**
* Create a {@link File} object. See {@link File} to see how to handle
* 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.
* @param {string} [options.key] A custom encryption key. See
* [Customer-supplied Encryption Keys](https://cloud.google.com/storage/docs/encryption#customer-supplied).
* @returns {File}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
* var file = bucket.file('my-existing-file.png');
*/
Bucket.prototype.file = function(name, options) {
if (!name) {
throw Error('A file name must be specified.');
}
return new File(this, name, options);
};
/**
* @typedef {array} GetBucketResponse
* @property {Bucket} 0 The {@link Bucket}.
* @property {object} 1 The full API response.
*/
/**
* @callback GetBucketCallback
* @param {?Error} err Request error, if any.
* @param {Bucket} bucket The {@link Bucket}.
* @param {object} apiResponse The full API response.
*/
/**
* Get a bucket if it exists.
*
* You may optionally use this to "get or create" an object by providing an
* object with `autoCreate` set to `true`. Any extra configuration that is
* normally required for the `create` method must be contained within this
* object as well.
*
* @param {object} [options] Configuration options.
* @param {boolean} [options.autoCreate] Automatically create the object if
* it does not exist. Default: `false`
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {GetBucketCallback} [callback] Callback function.
* @returns {Promise<GetBucketResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.get(function(err, bucket, apiResponse) {
* // `bucket.metadata` has been populated.
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.get().then(function(data) {
* var bucket = data[0];
* var apiResponse = data[1];
* });
*/
Bucket.prototype.get = function(options, callback) {
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
options = options || {};
var autoCreate = options.autoCreate;
delete options.autoCreate;
function onCreate(err, bucket, apiResponse) {
if (err) {
if (err.code === 409) {
self.get(options, callback);
return;
}
callback(err, null, apiResponse);
return;
}
callback(null, bucket, apiResponse);
}
this.getMetadata(options, function(err, metadata) {
if (err) {
if (err.code === 404 && autoCreate) {
var args = [];
if (!is.empty(options)) {
args.push(options);
}
args.push(onCreate);
self.create.apply(self, args);
return;
}
callback(err, null, metadata);
return;
}
callback(null, self, metadata);
});
};
/**
* Query object for listing files.
*
* @typedef {object} GetFilesRequest
* @property {boolean} [autoPaginate=true] Have pagination handled
* automatically.
* @property {string} [delimiter] Results will contain only objects whose
* names, aside from the prefix, do not contain delimiter. Objects whose
* names, aside from the prefix, contain delimiter will have their name
* truncated after the delimiter, returned in `apiResponse.prefixes`.
* Duplicate prefixes are omitted.
* @property {string} [prefix] Filter results to objects whose names begin
* with this prefix.
* @property {number} [maxApiCalls] Maximum number of API calls to make.
* @property {number} [maxResults] Maximum number of items plus prefixes to
* return.
* @property {string} [pageToken] A previously-returned page token
* representing part of the larger set of results to view.
* @property {string} [userProject] The ID of the project which will be
* billed for the request.
* @property {boolean} [versions] If true, returns File objects scoped to
* their versions.
*/
/**
* @typedef {array} GetFilesResponse
* @property {File[]} 0 Array of {@link File} instances.
*/
/**
* @callback GetFilesCallback
* @param {?Error} err Request error, if any.
* @param {File[]} files Array of {@link File} instances.
*/
/**
* Get {@link File} objects for the files currently in the bucket.
*
* @see [Objects: list API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/list}
*
* @param {GetFilesRequest} [query] Query object for listing files.
* @param {GetFilesCallback} [callback] Callback function.
* @returns {Promise<GetFilesResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.getFiles(function(err, files) {
* if (!err) {
* // files is an array of File objects.
* }
* });
*
* //-
* // If your bucket has versioning enabled, you can get all of your files
* // scoped to their generation.
* //-
* bucket.getFiles({
* versions: true
* }, function(err, files) {
* // Each file is scoped to its generation.
* });
*
* //-
* // To control how many API requests are made and page through the results
* // manually, set `autoPaginate` to `false`.
* //-
* var callback = function(err, files, nextQuery, apiResponse) {
* if (nextQuery) {
* // More results exist.
* bucket.getFiles(nextQuery, callback);
* }
*
* // The `metadata` property is populated for you with the metadata at the
* // time of fetching.
* files[0].metadata;
*
* // However, in cases where you are concerned the metadata could have
* // changed, use the `getMetadata` method.
* files[0].getMetadata(function(err, metadata) {});
* };
*
* bucket.getFiles({
* autoPaginate: false
* }, callback);
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.getFiles().then(function(data) {
* var files = data[0];
* });
*
* @example <caption>include:samples/files.js</caption>
* region_tag:storage_list_files
* Another example:
*
* @example <caption>include:samples/files.js</caption>
* region_tag:storage_list_files_with_prefix
* Example of listing files, filtered by a prefix:
*/
Bucket.prototype.getFiles = function(query, callback) {
var self = this;
if (!callback) {
callback = query;
query = {};
}
this.request(
{
uri: '/o',
qs: query,
},
function(err, resp) {
if (err) {
callback(err, null, null, resp);
return;
}
var files = arrify(resp.items).map(function(file) {
var options = {};
if (query.versions) {
options.generation = file.generation;
}
var fileInstance = self.file(file.name, options);
fileInstance.metadata = file;
return fileInstance;
});
var nextQuery = null;
if (resp.nextPageToken) {
nextQuery = extend({}, query, {
pageToken: resp.nextPageToken,
});
}
callback(null, files, nextQuery, resp);
}
);
};
/**
* Get {@link File} objects for the files currently in the bucket as a
* readable object stream.
*
* @method Bucket#getFilesStream
* @param {GetFilesRequest} [query] Query object for listing files.
* @returns {ReadableStream} A readable stream that emits {@link File} instances.
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.getFilesStream()
* .on('error', console.error)
* .on('data', function(file) {
* // file is a File object.
* })
* .on('end', function() {
* // All files retrieved.
* });
*
* //-
* // If you anticipate many results, you can end a stream early to prevent
* // unnecessary processing and API requests.
* //-
* bucket.getFilesStream()
* .on('data', function(file) {
* this.end();
* });
*/
Bucket.prototype.getFilesStream = common.paginator.streamify('getFiles');
/**
* @typedef {array} GetLabelsResponse
* @property {object} 0 Object of labels currently set on this bucket.
*/
/**
* @callback GetLabelsCallback
* @param {?Error} err Request error, if any.
* @param {object} labels Object of labels currently set on this bucket.
*/
/**
* Get the labels currently set on this bucket.
*
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {GetLabelsCallback} [callback] Callback function.
* @returns {Promise<GetLabelsCallback>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.getLabels(function(err, labels) {
* if (err) {
* // Error handling omitted.
* }
*
* // labels = {
* // label: 'labelValue',
* // ...
* // }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.getLabels().then(function(data) {
* var labels = data[0];
* });
*/
Bucket.prototype.getLabels = function(options, callback) {
if (is.fn(options)) {
callback = options;
options = {};
}
this.getMetadata(options, function(err, metadata) {
if (err) {
callback(err);
return;
}
callback(null, metadata.labels || {});
});
};
/**
* @typedef {array} GetBucketMetadataResponse
* @property {object} 0 The bucket metadata.
* @property {object} 1 The full API response.
*/
/**
* @callback GetBucketMetadataCallback
* @param {?Error} err Request error, if any.
* @param {object} files The bucket metadata.
* @param {object} apiResponse The full API response.
*/
/**
* Get the bucket's metadata.
*
* To set metadata, see {@link Bucket#setMetadata}.
*
* @see [Buckets: get API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/get}
*
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {GetBucketMetadataCallback} [callback] Callback function.
* @returns {Promise<GetBucketMetadataResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* bucket.getMetadata(function(err, metadata, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.getMetadata().then(function(data) {
* var metadata = data[0];
* var apiResponse = data[1];
* });
*
* @example <caption>include:samples/requesterPays.js</caption>
* region_tag:storage_get_requester_pays_status
* Example of retrieving the requester pays status of a bucket:
*/
Bucket.prototype.getMetadata = function(options, callback) {
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
this.request(
{
uri: '',
qs: options,
},
function(err, resp) {
if (err) {
callback(err, null, resp);
return;
}
self.metadata = resp;
callback(null, self.metadata, resp);
}
);
};
/**
* @typedef {array} GetNotificationsResponse
* @property {Notification[]} 0 Array of {@link Notification} instances.
* @property {object} 1 The full API response.
*/
/**
* @callback GetNotificationsCallback
* @param {?Error} err Request error, if any.
* @param {Notification[]} notifications Array of {@link Notification}
* instances.
* @param {object} apiResponse The full API response.
*/
/**
* Retrieves a list of notification subscriptions for a given bucket.
*
* @see [Notifications: list]{@link https://cloud.google.com/storage/docs/json_api/v1/notifications/list}
*
* @param {object} [options] Configuration options.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {GetNotificationsCallback} [callback] Callback function.
* @returns {Promise<GetNotificationsResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('my-bucket');
*
* bucket.getNotifications(function(err, notifications, apiResponse) {
* if (!err) {
* // notifications is an array of Notification objects.
* }
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.getNotifications().then(function(data) {
* var notifications = data[0];
* var apiResponse = data[1];
* });
*
* @example <caption>include:samples/notifications.js</caption>
* region_tag:storage_list_notifications
* Another example:
*/
Bucket.prototype.getNotifications = function(options, callback) {
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
this.request(
{
uri: '/notificationConfigs',
qs: options,
},
function(err, resp) {
if (err) {
callback(err, null, resp);
return;
}
var notifications = arrify(resp.items).map(function(notification) {
var notificationInstance = self.notification(notification.id);
notificationInstance.metadata = notification;
return notificationInstance;
});
callback(null, notifications, resp);
}
);
};
/**
* @typedef {array} MakeBucketPrivateResponse
* @property {File[]} 0 List of files made private.
*/
/**
* @callback MakeBucketPrivateCallback
* @param {?Error} err Request error, if any.
* @param {File[]} files List of files made private.
*/
/**
* Make the bucket listing private.
*
* You may also choose to make the contents of the bucket private by specifying
* `includeFiles: true`. This will automatically run
* {@link File#makePrivate} for every file in the bucket.
*
* When specifying `includeFiles: true`, use `force: true` to delay execution of
* your callback until all files have been processed. By default, the callback
* is executed after the first error. Use `force` to queue such errors until all
* files have been processed, after which they will be returned as an array as
* the first argument to your callback.
*
* NOTE: This may cause the process to be long-running and use a high number of
* requests. Use with caution.
*
* @see [Buckets: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/patch}
*
* @param {object} [options] Configuration options.
* @param {boolean} [options.includeFiles=false] Make each file in the bucket
* private.
* @param {boolean} [options.force] Queue errors occurred while making files
* private until all files have been processed.
* @param {string} [options.userProject] The ID of the project which will be
* billed for the request.
* @param {MakeBucketPrivateCallback} [callback] Callback function.
* @returns {Promise<MakeBucketPrivateResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* //-
* // Make the bucket private.
* //-
* bucket.makePrivate(function(err) {});
*
* //-
* // Make the bucket and its contents private.
* //-
* var opts = {
* includeFiles: true
* };
*
* bucket.makePrivate(opts, function(err, files) {
* // `err`:
* // The first error to occur, otherwise null.
* //
* // `files`:
* // Array of files successfully made private in the bucket.
* });
*
* //-
* // Make the bucket and its contents private, using force to suppress errors
* // until all files have been processed.
* //-
* var opts = {
* includeFiles: true,
* force: true
* };
*
* bucket.makePrivate(opts, function(errors, files) {
* // `errors`:
* // Array of errors if any occurred, otherwise null.
* //
* // `files`:
* // Array of files successfully made private in the bucket.
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.makePrivate(opts).then(function(data) {
* var files = data[0];
* });
*/
Bucket.prototype.makePrivate = function(options, callback) {
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
options = options || {};
options.private = true;
async.series([setPredefinedAcl, makeFilesPrivate], callback);
function setPredefinedAcl(done) {
var query = {
predefinedAcl: 'projectPrivate',
};
if (options.userProject) {
query.userProject = options.userProject;
}
self.setMetadata(
{
// You aren't allowed to set both predefinedAcl & acl properties on a
// bucket so acl must explicitly be nullified.
acl: null,
},
query,
done
);
}
function makeFilesPrivate(done) {
if (!options.includeFiles) {
done();
return;
}
self.makeAllFilesPublicPrivate_(options, done);
}
};
/**
* @typedef {array} MakeBucketPublicResponse
* @property {File[]} 0 List of files made public.
*/
/**
* @callback MakeBucketPublicCallback
* @param {?Error} err Request error, if any.
* @param {File[]} files List of files made public.
*/
/**
* Make the bucket publicly readable.
*
* You may also choose to make the contents of the bucket publicly readable by
* specifying `includeFiles: true`. This will automatically run
* {@link File#makePublic} for every file in the bucket.
*
* When specifying `includeFiles: true`, use `force: true` to delay execution of
* your callback until all files have been processed. By default, the callback
* is executed after the first error. Use `force` to queue such errors until all
* files have been processed, after which they will be returned as an array as
* the first argument to your callback.
*
* NOTE: This may cause the process to be long-running and use a high number of
* requests. Use with caution.
*
* @see [Buckets: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/patch}
*
* @param {object} [options] Configuration options.
* @param {boolean} [options.includeFiles=false] Make each file in the bucket
* publicly readable.
* @param {boolean} [options.force] Queue errors occurred while making files
* public until all files have been processed.
* @param {MakeBucketPublicCallback} [callback] Callback function.
* @returns {Promise<MakeBucketPublicResponse>}
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('albums');
*
* //-
* // Make the bucket publicly readable.
* //-
* bucket.makePublic(function(err) {});
*
* //-
* // Make the bucket and its contents publicly readable.
* //-
* var opts = {
* includeFiles: true
* };
*
* bucket.makePublic(opts, function(err, files) {
* // `err`:
* // The first error to occur, otherwise null.
* //
* // `files`:
* // Array of files successfully made public in the bucket.
* });
*
* //-
* // Make the bucket and its contents publicly readable, using force to
* // suppress errors until all files have been processed.
* //-
* var opts = {
* includeFiles: true,
* force: true
* };
*
* bucket.makePublic(opts, function(errors, files) {
* // `errors`:
* // Array of errors if any occurred, otherwise null.
* //
* // `files`:
* // Array of files successfully made public in the bucket.
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.makePublic(opts).then(function(data) {
* var files = data[0];
* });
*/
Bucket.prototype.makePublic = function(options, callback) {
var self = this;
if (is.fn(options)) {
callback = options;
options = {};
}
options = options || {};
options.public = true;
async.series(
[addAclPermissions, addDefaultAclPermissions, makeFilesPublic],
callback
);
function addAclPermissions(done) {
// Allow reading bucket contents while preserving original permissions.
self.acl.add(
{
entity: 'allUsers',
role: 'READER',
},
done
);
}
function addDefaultAclPermissions(done) {
self.acl.default.add(
{
entity: 'allUsers',
role: 'READER',
},
done
);
}
function makeFilesPublic(done) {
if (!options.includeFiles) {
done();
return;
}
self.makeAllFilesPublicPrivate_(options, done);
}
};
/**
* Get a reference to a Cloud Pub/Sub Notification.
*
* @param {string} id ID of notification.
* @returns {Notification}
* @see Notification
*
* @example
* var storage = require('@google-cloud/storage')();
* var bucket = storage.bucket('my-bucket');
* var notification = bucket.notification('1');
*/
Bucket.prototype.notification = function(id) {
if (!id) {
throw new Error('You must supply a notification ID.');
}
return new Notification(this, id);
};
/**
* Makes request and applies userProject query parameter if necessary.
*
* @private
*
* @