Skip to content

Commit

Permalink
feat(bucket): add enableLogging method (#876)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenplusplus committed Oct 14, 2019
1 parent fc1feb7 commit b09ecac
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/bucket.ts
Expand Up @@ -99,6 +99,11 @@ export interface LifecycleRule {
storageClass?: string;
}

export interface EnableLoggingOptions {
bucket?: string | Bucket;
prefix: string;
}

export interface GetFilesOptions {
autoPaginate?: boolean;
delimiter?: string;
Expand Down Expand Up @@ -1789,6 +1794,99 @@ class Bucket extends ServiceObject {
);
}

enableLogging(
config: EnableLoggingOptions
): Promise<SetBucketMetadataResponse>;
enableLogging(
config: EnableLoggingOptions,
callback: SetBucketMetadataCallback
): void;
/**
* Configuration object for enabling logging.
*
* @typedef {object} EnableLoggingOptions
* @property {string|Bucket} [bucket] The bucket for the log entries. By
* default, the current bucket is used.
* @property {string} prefix A unique prefix for log object names.
*/
/**
* Enable logging functionality for this bucket. This will make two API
* requests, first to grant Cloud Storage WRITE permission to the bucket, then
* to set the appropriate configuration on the Bucket's metadata.
*
* @param {EnableLoggingOptions} config Configuration options.
* @param {SetBucketMetadataCallback} [callback] Callback function.
* @returns {Promise<SetBucketMetadataResponse>}
*
* @example
* const {Storage} = require('@google-cloud/storage');
* const storage = new Storage();
* const bucket = storage.bucket('albums');
*
* const config = {
* prefix: 'log'
* };
*
* bucket.enableLogging(config, function(err, apiResponse) {
* if (!err) {
* // Logging functionality enabled successfully.
* }
* });
*
* @example <caption>Optionally, provide a destination bucket.</caption>
* const config = {
* prefix: 'log',
* bucket: 'destination-bucket'
* };
*
* bucket.enableLogging(config, function(err, apiResponse) {});
*
* @example <caption>If the callback is omitted, we'll return a Promise.</caption>
* bucket.enableLogging(config).then(function(data) {
* const apiResponse = data[0];
* });
*/
enableLogging(
config: EnableLoggingOptions,
callback?: SetBucketMetadataCallback
): Promise<SetBucketMetadataResponse> | void {
if (
!config ||
typeof config === 'function' ||
typeof config.prefix === 'undefined'
) {
throw new Error('A configuration object with a prefix is required.');
}

const logBucket = config.bucket
? (config.bucket as Bucket).id || config.bucket
: this.id;

(async () => {
let setMetadataResponse;

try {
const [policy] = await this.iam.getPolicy();
policy.bindings.push({
members: ['group:cloud-storage-analytics@google.com'],
role: 'roles/storage.objectCreator',
});
await this.iam.setPolicy(policy);
[setMetadataResponse] = await this.setMetadata({
logging: {
logBucket,
logObjectPrefix: config.prefix,
},
});
} catch (e) {
callback!(e);
return;
}

callback!(null, setMetadataResponse);
})();
}

enableRequesterPays(): Promise<EnableRequesterPaysResponse>;
enableRequesterPays(callback: EnableRequesterPaysCallback): void;
/**
Expand Down
26 changes: 26 additions & 0 deletions system-test/storage.ts
Expand Up @@ -1367,6 +1367,32 @@ describe('storage', () => {
});
});

describe('bucket logging', () => {
const PREFIX = 'sys-test';

it('should enable logging on current bucket by default', async () => {
const [metadata] = await bucket.enableLogging({prefix: PREFIX});
assert.deepStrictEqual(metadata.logging, {
logBucket: bucket.id,
logObjectPrefix: PREFIX,
});
});

it('should enable logging on another bucket', async () => {
const bucketForLogging = storage.bucket(generateName());
await bucketForLogging.create();

const [metadata] = await bucket.enableLogging({
bucket: bucketForLogging,
prefix: PREFIX,
});
assert.deepStrictEqual(metadata.logging, {
logBucket: bucketForLogging.id,
logObjectPrefix: PREFIX,
});
});
});

describe('requester pays', () => {
const HAS_2ND_PROJECT =
process.env.GCN_STORAGE_2ND_PROJECT_ID !== undefined;
Expand Down
163 changes: 163 additions & 0 deletions test/bucket.ts
Expand Up @@ -44,8 +44,10 @@ import {
GetFilesOptions,
MakeAllFilesPublicPrivateOptions,
SetBucketMetadataCallback,
SetBucketMetadataResponse,
} from '../src/bucket';
import {AddAclOptions} from '../src/acl';
import {Policy} from '../src/iam';

class FakeFile {
calledWith_: IArguments;
Expand Down Expand Up @@ -1172,6 +1174,167 @@ describe('Bucket', () => {
});
});

describe('enableLogging', () => {
const PREFIX = 'prefix';

beforeEach(() => {
bucket.iam = {
getPolicy: () => Promise.resolve([{bindings: []}]),
setPolicy: () => Promise.resolve(),
};
bucket.setMetadata = () => Promise.resolve([]);
});

it('should throw if a config object is not provided', () => {
assert.throws(() => {
bucket.enableLogging();
}, /A configuration object with a prefix is required\./);
});

it('should throw if config is a function', () => {
assert.throws(() => {
bucket.enableLogging(assert.ifError);
}, /A configuration object with a prefix is required\./);
});

it('should throw if a prefix is not provided', () => {
assert.throws(() => {
bucket.enableLogging(
{
bucket: 'bucket-name',
},
assert.ifError
);
}, /A configuration object with a prefix is required\./);
});

it('should add IAM permissions', done => {
const policy = {
bindings: [{}],
};
bucket.iam = {
getPolicy: () => Promise.resolve([policy]),
setPolicy: (policy_: Policy) => {
assert.deepStrictEqual(policy, policy_);
assert.deepStrictEqual(policy_.bindings, [
policy.bindings[0],
{
members: ['group:cloud-storage-analytics@google.com'],
role: 'roles/storage.objectCreator',
},
]);
setImmediate(done);
return Promise.resolve();
},
};

bucket.enableLogging({prefix: PREFIX}, assert.ifError);
});

it('should return an error from getting the IAM policy', done => {
const error = new Error('Error.');

bucket.iam.getPolicy = () => {
throw error;
};

bucket.enableLogging({prefix: PREFIX}, (err: Error | null) => {
assert.strictEqual(err, error);
done();
});
});

it('should return an error from setting the IAM policy', done => {
const error = new Error('Error.');

bucket.iam.setPolicy = () => {
throw error;
};

bucket.enableLogging({prefix: PREFIX}, (err: Error | null) => {
assert.strictEqual(err, error);
done();
});
});

it('should update the logging metadata configuration', done => {
bucket.setMetadata = (metadata: Metadata) => {
assert.deepStrictEqual(metadata.logging, {
logBucket: bucket.id,
logObjectPrefix: PREFIX,
});
setImmediate(done);
return Promise.resolve([]);
};

bucket.enableLogging({prefix: PREFIX}, assert.ifError);
});

it('should allow a custom bucket to be provided', done => {
const bucketName = 'bucket-name';

bucket.setMetadata = (metadata: Metadata) => {
assert.deepStrictEqual(metadata.logging.logBucket, bucketName);
setImmediate(done);
return Promise.resolve([]);
};

bucket.enableLogging(
{
prefix: PREFIX,
bucket: bucketName,
},
assert.ifError
);
});

it('should accept a Bucket object', done => {
const bucketForLogging = new Bucket(STORAGE, 'bucket-name');

bucket.setMetadata = (metadata: Metadata) => {
assert.deepStrictEqual(metadata.logging.logBucket, bucketForLogging.id);
setImmediate(done);
return Promise.resolve([]);
};

bucket.enableLogging(
{
prefix: PREFIX,
bucket: bucketForLogging,
},
assert.ifError
);
});

it('should execute the callback with the setMetadata response', done => {
const setMetadataResponse = {};

bucket.setMetadata = () => Promise.resolve([setMetadataResponse]);

bucket.enableLogging(
{prefix: PREFIX},
(err: Error | null, response: SetBucketMetadataResponse) => {
assert.ifError(err);
assert.strictEqual(response, setMetadataResponse);
done();
}
);
});

it('should return an error from the setMetadata call failing', done => {
const error = new Error('Error.');

bucket.setMetadata = () => {
throw error;
};

bucket.enableLogging({prefix: PREFIX}, (err: Error | null) => {
assert.strictEqual(err, error);
done();
});
});
});

describe('enableRequesterPays', () => {
it('should call setMetadata correctly', done => {
bucket.setMetadata = (metadata: {}, callback: Function) => {
Expand Down

0 comments on commit b09ecac

Please sign in to comment.