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

HMAC key: projectId option #801

Merged
merged 3 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/hmacKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
import {Metadata, ServiceObject} from '@google-cloud/common';
import {Storage} from './storage';

export interface HmacKeyOptions {
projectId?: string;
}

export interface HmacKeyMetadata {
accessId: string;
etag?: string;
Expand Down Expand Up @@ -58,6 +62,12 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata];
export class HmacKey extends ServiceObject<HmacKeyMetadata | undefined> {
metadata: HmacKeyMetadata | undefined;

/**
* @typedef {object} HmacKeyOptions
* @property {string} [projectId] The project ID of the project that owns
* the service account of the requested HMAC key. If not provided,
* the project ID used to instantiate the Storage client will be used.
*/
/**
* Constructs an HmacKey object.
*
Expand All @@ -67,12 +77,13 @@ export class HmacKey extends ServiceObject<HmacKeyMetadata | undefined> {
* @param {Storage} storage The Storage instance this HMAC key is
* attached to.
* @param {string} accessId The unique accessId for this HMAC key.
* @param {HmacKeyOptions} options Constructor configurations.
* @example
* const {Storage} = require('@google-cloud/storage');
* const storage = new Storage();
* const hmacKey = storage.hmacKey('access-id');
*/
constructor(storage: Storage, accessId: string) {
constructor(storage: Storage, accessId: string, options?: HmacKeyOptions) {
const methods = {
/**
* @typedef {object} DeleteHmacKeyOptions
Expand Down Expand Up @@ -303,10 +314,12 @@ export class HmacKey extends ServiceObject<HmacKeyMetadata | undefined> {
},
};

const projectId = (options && options.projectId) || storage.projectId;

super({
parent: storage,
baseUrl: `/projects/${storage.projectId}/hmacKeys`,
id: accessId,
baseUrl: `/projects/${projectId}/hmacKeys`,
methods,
});
}
Expand Down
50 changes: 41 additions & 9 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {Bucket} from './bucket';
import {Channel} from './channel';
import {File} from './file';
import {normalize} from './util';
import {HmacKey, HmacKeyMetadata} from './hmacKey';
import {HmacKey, HmacKeyMetadata, HmacKeyOptions} from './hmacKey';

export interface GetServiceAccountOptions {
userProject?: string;
Expand Down Expand Up @@ -108,6 +108,7 @@ export interface HmacKeyResourceResponse {
export type CreateHmacKeyResponse = [HmacKey, string, HmacKeyResourceResponse];

export interface CreateHmacKeyOptions {
projectId?: string;
userProject?: string;
}

Expand All @@ -121,6 +122,7 @@ export interface CreateHmacKeyCallback {
}

export interface GetHmacKeysOptions {
projectId?: string;
serviceAccountEmail?: string;
showDeletedKeys?: boolean;
autoPaginate?: boolean;
Expand Down Expand Up @@ -190,6 +192,15 @@ export class Storage extends Service {
*/
static File: typeof File = File;

/**
* {@link HmacKey} class.
*
* @name Storage.HmacKey
* @see HmacKey
* @type {Constructor}
*/
static HmacKey: typeof HmacKey = HmacKey;

/**
* Cloud Storage uses access control lists (ACLs) to manage object and
* bucket access. ACLs are the mechanism you use to share objects with other
Expand Down Expand Up @@ -637,6 +648,9 @@ export class Storage extends Service {
): void;
/**
* @typedef {object} CreateHmacKeyOptions
* @property {string} [projectId] The project ID of the project that owns
* the service account of the requested HMAC key. If not provided,
* the project ID used to instantiate the Storage client will be used.
* @property {string} [userProject] This parameter is currently ignored.
*/
/**
Expand Down Expand Up @@ -719,11 +733,13 @@ export class Storage extends Service {
CreateHmacKeyCallback
>(optionsOrCb, cb);
const query = Object.assign({}, options, {serviceAccountEmail});
const projectId = query.projectId || this.projectId;
delete query.projectId;

this.request(
{
method: 'POST',
uri: `/projects/${this.projectId}/hmacKeys`,
uri: `/projects/${projectId}/hmacKeys`,
qs: query,
},
(err, resp: HmacKeyResourceResponse) => {
Expand All @@ -732,7 +748,10 @@ export class Storage extends Service {
return;
}

const hmacKey = new HmacKey(this, resp.metadata.accessId);
const metadata = resp.metadata;
const hmacKey = this.hmacKey(metadata.accessId, {
projectId: metadata.projectId,
});
hmacKey.metadata = resp.metadata;

callback!(null, hmacKey, resp.secret, resp);
Expand Down Expand Up @@ -858,6 +877,9 @@ export class Storage extends Service {
* Query object for listing HMAC keys.
*
* @typedef {object} GetHmacKeysOptions
* @property {string} [projectId] The project ID of the project that owns
* the service account of the requested HMAC key. If not provided,
* the project ID used to instantiate the Storage client will be used.
* @property {string} [serviceAccountEmail] If present, only HMAC keys for the
* given service account are returned.
* @property {boolean} [showDeletedKeys=false] If true, include keys in the DELETE
Expand Down Expand Up @@ -932,20 +954,25 @@ export class Storage extends Service {
cb?: GetHmacKeysCallback
): Promise<GetHmacKeysResponse> | void {
const {options, callback} = normalize<GetHmacKeysOptions>(optionsOrCb, cb);
const query = Object.assign({}, options);
const projectId = query.projectId || this.projectId;
delete query.projectId;

this.request(
{
uri: `/projects/${this.projectId}/hmacKeys`,
qs: options,
uri: `/projects/${projectId}/hmacKeys`,
qs: query,
},
(err, resp) => {
if (err) {
callback(err, null, null, resp);
return;
}

const hmacKeys = arrify(resp.items).map((hmacKey: Metadata) => {
const hmacKeyInstance = this.hmacKey(hmacKey.accessId);
const hmacKeys = arrify(resp.items).map((hmacKey: HmacKeyMetadata) => {
const hmacKeyInstance = this.hmacKey(hmacKey.accessId, {
projectId: hmacKey.projectId,
});
hmacKeyInstance.metadata = hmacKey;
return hmacKeyInstance;
});
Expand Down Expand Up @@ -1058,7 +1085,12 @@ export class Storage extends Service {
* Note: this does not fetch the HMAC key's metadata. Use HmacKey#get() to
* retrieve and populate the metadata.
*
* To get a reference to an HMAC key that's not created for a service
* account in the same project used to instantiate the Storage client,
* supply the project's ID as `projectId` in the `options` argument.
*
* @param {string} accessId The HMAC key's access ID.
* @param {HmacKeyOptions} options HmacKey constructor owptions.
* @returns {HmacKey}
* @see HmacKey
*
Expand All @@ -1067,12 +1099,12 @@ export class Storage extends Service {
* const storage = new Storage();
* const hmacKey = storage.hmacKey('ACCESS_ID');
*/
hmacKey(accessId: string) {
hmacKey(accessId: string, options?: HmacKeyOptions) {
if (!accessId) {
throw new Error('An access ID is needed to create an HmacKey object.');
}

return new HmacKey(this, accessId);
return new HmacKey(this, accessId, options);
}
}

Expand Down
8 changes: 8 additions & 0 deletions test/hmacKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('HmacKey', () => {

STORAGE = {
request: util.noop,
projectId: 'my-project',
};

hmacKey = new HmacKey(STORAGE, ACCESS_ID);
Expand All @@ -65,6 +66,7 @@ describe('HmacKey', () => {
const ctorArg = serviceObjectSpy.firstCall.args[0];
assert(ctorArg.parent, STORAGE);
assert(ctorArg.id, ACCESS_ID);
assert(ctorArg.baseUrl, '/projects/my-project/hmacKeys');
assert.deepStrictEqual(ctorArg.methods, {
delete: true,
get: true,
Expand All @@ -76,5 +78,11 @@ describe('HmacKey', () => {
},
});
});

it('should form baseUrl using options.projectId if given', () => {
hmacKey = new HmacKey(STORAGE, ACCESS_ID, {projectId: 'another-project'});
const ctorArg = serviceObjectSpy.firstCall.args[0];
assert(ctorArg.baseUrl, '/projects/another-project/hmacKeys');
});
});
});
52 changes: 50 additions & 2 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import sinon = require('sinon');
import {HmacKey} from '../src/hmacKey';
import {HmacKeyResourceResponse} from '../src/storage';

const hmacKeyModule = require('../src/hmacKey');

class FakeChannel {
calledWith_: Array<{}>;
constructor(...args: Array<{}>) {
Expand Down Expand Up @@ -98,6 +100,7 @@ describe('Storage', () => {
Service: FakeService,
},
'./channel.js': {Channel: FakeChannel},
'./hmacKey': hmacKeyModule,
}).Storage;
Bucket = Storage.Bucket;
});
Expand Down Expand Up @@ -194,11 +197,30 @@ describe('Storage', () => {
});

describe('hmacKey', () => {
let hmacKeyCtor: sinon.SinonSpy;
beforeEach(() => {
hmacKeyCtor = sinon.spy(hmacKeyModule, 'HmacKey');
});

afterEach(() => {
hmacKeyCtor.restore();
});

it('should throw if accessId is not provided', () => {
assert.throws(() => {
storage.hmacKey();
}, /An access ID is needed to create an HmacKey object./);
});

it('should pass options object to HmacKey constructor', () => {
const options = {myOpts: 'a'};
storage.hmacKey('access-id', options);
assert.deepStrictEqual(hmacKeyCtor.getCall(0).args, [
storage,
'access-id',
options,
]);
});
});

describe('createHmacKey', () => {
Expand All @@ -222,6 +244,15 @@ describe('Storage', () => {
some: 'value',
};

let hmacKeyCtor: sinon.SinonSpy;
beforeEach(() => {
hmacKeyCtor = sinon.spy(hmacKeyModule, 'HmacKey');
});

afterEach(() => {
hmacKeyCtor.restore();
});

it('should make correct API request', done => {
storage.request = (
reqOpts: DecorateRequestOptions,
Expand Down Expand Up @@ -296,7 +327,11 @@ describe('Storage', () => {
(err: Error, hmacKey: HmacKey, secret: string) => {
assert.ifError(err);
assert.strictEqual(secret, response.secret);
assert(hmacKey instanceof HmacKey);
assert.deepStrictEqual(hmacKeyCtor.getCall(0).args, [
storage,
response.metadata.accessId,
{projectId: response.metadata.projectId},
]);
assert.strictEqual(hmacKey.metadata, metadataResponse);
done();
}
Expand Down Expand Up @@ -683,6 +718,15 @@ describe('Storage', () => {
});
});

let hmacKeyCtor: sinon.SinonSpy;
beforeEach(() => {
hmacKeyCtor = sinon.spy(hmacKeyModule, 'HmacKey');
});

afterEach(() => {
hmacKeyCtor.restore();
});

it('should get HmacKeys without a query', done => {
storage.getHmacKeys(() => {
const firstArg = storage.request.firstCall.args[0];
Expand Down Expand Up @@ -790,7 +834,11 @@ describe('Storage', () => {

storage.getHmacKeys((err: Error, hmacKeys: HmacKey[]) => {
assert.ifError(err);
assert(hmacKeys![0] instanceof HmacKey);
assert.deepStrictEqual(hmacKeyCtor.getCall(0).args, [
storage,
metadataResponse.accessId,
{projectId: metadataResponse.projectId},
]);
assert.deepStrictEqual(hmacKeys[0].metadata, metadataResponse);
done();
});
Expand Down