UNPKG

ran-boilerplate

Version:

React . Apollo (GraphQL) . Next.js Toolkit

1,810 lines (1,679 loc) 67.9 kB
/** * 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 * * @