Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Bucket/Object lock operations #320

Merged
merged 12 commits into from
Sep 10, 2018
122 changes: 121 additions & 1 deletion src/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1455,7 +1455,7 @@ class Bucket extends ServiceObject {
/**
* @callback GetBucketMetadataCallback
* @param {?Error} err Request error, if any.
* @param {object} files The bucket metadata.
* @param {object} metadata The bucket metadata.
* @param {object} apiResponse The full API response.
*/
/**
Expand Down Expand Up @@ -1586,6 +1586,46 @@ class Bucket extends ServiceObject {
});
}

/**
* Lock a previously-defined retention policy. This will prevent changes to
* the policy.
*
* @throws {Error} if a metageneration is not provided.
*
* @param {Number|String} metageneration The bucket's metageneration. This is
* accesssible from calling {@link File#getMetadata}.
* @param {SetBucketMetadataCallback} [callback] Callback function.
* @returns {Promise<SetBucketMetadataResponse>}
*
* @example
* const storage = require('@google-cloud/storage')();
* const bucket = storage.bucket('albums');
*
* bucket.lock(function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.lock().then(function(data) {

This comment was marked as spam.

This comment was marked as spam.

* const apiResponse = data[0];
* });
*/
lock(metageneration, callback) {
if (!is.number(metageneration) && !is.string(metageneration)) {
throw new Error('A metageneration must be provided.');
}

this.request(
{
method: 'POST',
uri: '/lockRetentionPolicy',
qs: {
ifMetagenerationMatch: metageneration,
},
},
callback);
}

/**
* @typedef {array} MakeBucketPrivateResponse
* @property {File[]} 0 List of files made private.
Expand Down Expand Up @@ -1858,6 +1898,34 @@ class Bucket extends ServiceObject {
return new Notification(this, id);
}

/**
* Remove an already-existing retention policy from this bucket, if it is not
* locked.
*
* @param {SetBucketMetadataCallback} [callback] Callback function.
* @returns {Promise<SetBucketMetadataResponse>}
*
* @example
* const storage = require('@google-cloud/storage')();
* const bucket = storage.bucket('albums');
*
* bucket.removeRetentionPeriod(function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.removeRetentionPeriod().then(function(data) {
* const apiResponse = data[0];
* });
*/
removeRetentionPeriod(callback) {
this.setMetadata(
{
retentionPolicy: null,
},
callback);
}

/**
* Makes request and applies userProject query parameter if necessary.
*
Expand Down Expand Up @@ -1989,6 +2057,13 @@ class Bucket extends ServiceObject {
* }, function(err, apiResponse) {});
*
* //-
* // Set the default event-based hold value for new objects in this bucket.
* //-
* bucket.setMetadata({
* defaultEventBasedHold: true
* }, function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.setMetadata(metadata).then(function(data) {
Expand Down Expand Up @@ -2022,6 +2097,51 @@ class Bucket extends ServiceObject {
});
}

/**
* Lock all objects contained in the bucket, based on their creation time. Any
* attempt to overwrite or delete objects younger than the retention period
* will result in a `PERMISSION_DENIED` error.
*
* An unlocked retention policy can be modified or removed from the bucket via
* {@link File#removeRetentionPeriod} and {@link File#setRetentionPeriod}. A
* locked retention policy cannot be removed or shortened in duration for the
* lifetime of the bucket. Attempting to remove or decrease period of a locked
* retention policy will result in a `PERMISSION_DENIED` error. You can still
* increase the policy.
*
* @param {*} duration In seconds, the minimum retention time for all objects
* contained in this bucket.
* @param {SetBucketMetadataCallback} [callback] Callback function.
* @returns {Promise<SetBucketMetadataResponse>}
*
* @example
* const storage = require('@google-cloud/storage')();
* const bucket = storage.bucket('albums');
*
* const DURATION_SECONDS = 15780000; // 6 months.
*
* //-
* // Lock the objects in this bucket for 6 months.
* //-
* bucket.setRetentionPeriod(DURATION_SECONDS, function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* bucket.setRetentionPeriod(DURATION_SECONDS).then(function(data) {
* const apiResponse = data[0];
* });
*/
setRetentionPeriod(duration, callback) {
this.setMetadata(
{
retentionPolicy: {
retentionPeriod: duration,
},
},
callback);
}

/**
* @callback SetStorageClassCallback
* @param {?Error} err Request error, if any.
Expand Down
63 changes: 63 additions & 0 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,51 @@ class File extends ServiceObject {
(this.parent as any).get.call(this, options, callback);
}

/**
* @typedef {array} GetExpirationDateResponse
* @property {date} 0 A Date object representing the earliest time this file's
* retention policy will expire.
*/
/**
* @callback GetExpirationDateCallback
* @param {?Error} err Request error, if any.
* @param {date} expirationDate A Date object representing the earliest time
* this file's retention policy will expire.
*/
/**
* If this bucket has a retention policy defined, use this method to get a
* Date object representing the earliest time this file will expire.
*
* @param {GetExpirationDateCallback} [callback] Callback function.
* @returns {Promise<GetExpirationDateResponse>}
*
* @example
* const storage = require('@google-cloud/storage')();
* const myBucket = storage.bucket('my-bucket');
*
* const file = myBucket.file('my-file');
*
* file.getExpirationDate(function(err, expirationDate) {
* // expirationDate is a Date object.
* });
*/
getExpirationDate(callback) {
this.getMetadata((err, metadata, apiResponse) => {
if (err) {
callback(err, null, apiResponse);
return;
}

if (!metadata.retentionExpirationTime) {
const error = new Error('An expiration time is not available.');
callback(error, null, apiResponse);
return;
}

callback(null, new Date(metadata.retentionExpirationTime), apiResponse);
});
}

/**
* @typedef {array} GetFileMetadataResponse
* @property {object} 0 The {@link File} metadata.
Expand Down Expand Up @@ -2377,6 +2422,24 @@ class File extends ServiceObject {
* });
*
* //-
* // Set a temporary hold on this file from its bucket's retention period
* // configuration.
* //
* file.setMetadata({
* temporaryHold: true
* }, function(err, apiResponse) {});
*
* //-
* // Alternatively, you may set a temporary hold. This will follow the same
* // behavior as an event-based hold, with the exception that the bucket's
* // retention policy will not renew for this file from the time the hold is
* // released.
* //-
* file.setMetadata({
* eventBasedHold: true
* }, function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* file.setMetadata(metadata).then(function(data) {
Expand Down
11 changes: 11 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,17 @@ class Storage extends Service {
* storage.createBucket('new-bucket', metadata, callback);
*
* //-
* // Create a bucket with a retention policy of 6 months.
* //-
* const metadata = {
* retentionPolicy: {
* retentionPeriod: 15780000 // 6 months in seconds.
* }
* };
*
* storage.createBucket('new-bucket', metadata, callback);
*
* //-
* // Enable versioning on a new bucket.
* //-
* const metadata = {
Expand Down
Loading