From 1c6bc94f8619e4395cfc09a53c4b5323e84442ad Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 18 Jun 2019 17:37:59 -0700 Subject: [PATCH 01/44] feat: HmacKey CRUD methods --- src/hmacKey.ts | 319 +++++++++++++++++++++++++++++++++ src/storage.ts | 345 +++++++++++++++++++++++++++++++++++- test/hmacKey.ts | 463 ++++++++++++++++++++++++++++++++++++++++++++++++ test/index.ts | 116 ++++++++++++ 4 files changed, 1241 insertions(+), 2 deletions(-) create mode 100644 src/hmacKey.ts create mode 100644 test/hmacKey.ts diff --git a/src/hmacKey.ts b/src/hmacKey.ts new file mode 100644 index 000000000..89018aaed --- /dev/null +++ b/src/hmacKey.ts @@ -0,0 +1,319 @@ +/** + * Copyright 2019 Google LLC. 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. + */ + +import { + Metadata, + ServiceObject, + GetConfig, + DecorateRequestOptions, + ResponseBody, + BodyResponseCallback, +} from '@google-cloud/common'; +import {promisifyAll} from '@google-cloud/promisify'; + +import {Storage} from './storage'; +import {normalize} from './util'; + +export interface HmacKeyMetadata { + accessId: string; + etag?: string; + id?: string; + projectId?: string; + serviceAccountEmail?: string; + state?: string; + timeCreated?: string; + updated?: string; +} + +export interface GetHmacKeyOptions extends GetConfig { + userProject?: string; +} + +export interface UpdateHmacKeyOptions { + userProject?: string; +} + +export interface UpdateHmacKeyMetadata { + state?: 'ACTIVE' | 'INACTIVE'; + etag?: string; +} + +export interface ToggleHmacKeyOptions { + etag?: string; +} + +export interface HmacKeyMetadataCallback { + (err: Error | null, metadata?: HmacKeyMetadata, apiResponse?: Metadata): void; +} + +export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; + +/** + * + */ +export class HmacKey extends ServiceObject { + accessId: string; + metadata: HmacKeyMetadata; + parent: Storage; + + constructor(storage: Storage, accessId: string) { + if (!accessId) { + throw new Error('An access ID is needed to create an HmacKey object.'); + } + + const methods = { + /** + * @typedef {object} DeleteHmacKeyOptions + * @property {string} [userProject] This parameter is currently ignored. + */ + /** + * @typedef {array} DeleteHmacKeyResponse + * @property {object} 0 The full API response. + */ + /** + * @callback DeleteHmacKeyCallback + * @param {?Error} err Request error, if any. + * @param {object} apiResponse The full API response. + */ + /** + * Deletes an HMAC key. + * Key state must be set to `INACTIVE` prior to deletion. + * Caution: HMAC keys cannot be recovered once you delete them. + * + * The authenticated user must have `storage.hmacKeys.delete` permission for the project in which the key exists. + * + * @method HmacKey#delete + * @param {DeleteHmacKeyOptions} [options] Configuration options. + * @param {DeleteHmacKeyCallback} [callback] Callback function. + * @returns {Promise { + * if (err) { + * // The request was an error. + * console.error(err); + * return; + * } + * hmacKey.delete((err) => { + * if (err) { + * console.error(err); + * return; + * } + * // The HMAC key is deleted. + * }) + * }); + * + * //- + * // If the callback is omitted, a promise is returned. + * //- + * const hmacKey = storage.hmacKey('ACCESS_ID'); + * hmacKey + * .update({state: 'INACTIVE'}) + * .then(() => { + * return hmacKey.delete(); + * }); + */ + delete: true, + }; + + super({ + parent: storage, + baseUrl: `/projects/${storage.projectId}/hmacKeys`, + id: accessId, + methods, + }); + + this.accessId = accessId; + this.metadata = {accessId}; + this.parent = storage; + } + + /** + * @typedef {object} GetHmacKeyOptions + * @property {string} userProject This parameter is currently ignored. + */ + /** + * Retrieves and populate an HMAC key's metadata. + * + * HmacKey.get() does not give the HMAC key secret, as + * it is only returned on creation. + * The authenticated user must have `storage.hmacKeys.get` permission for the project in which the key exists. + * + * @param {GetHmacKeyOptions} [options] Configuration options. + * @param {HmacKeyMetadataCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * + * //- + * // Get the HmacKey's Metadata. + * //- + * storage.hmacKey('ACCESS_ID') + * .get((err, hmacKeyMetadata) => { + * if (err) { + * // The request was an error. + * console.error(err); + * return; + * } + * console.log(hmacKeyMetadata); + * }); + * + * //- + * // If the callback is omitted, a promise is returned. + * //- + * storage.hmacKey('ACCESS_ID') + * .get((data) => { + * const hmacKeyMetadata = data[0]; + * console.log(hmacKeyMetadata); + * }); + */ + get(options?: GetHmacKeyOptions): Promise; + get(callback: HmacKeyMetadataCallback): void; + get(options: GetHmacKeyOptions, callback: HmacKeyMetadataCallback): void; + get( + optionsOrCb?: GetHmacKeyOptions | HmacKeyMetadataCallback, + cb?: HmacKeyMetadataCallback + ): Promise | void { + const {options, callback} = normalize< + GetHmacKeyOptions, + HmacKeyMetadataCallback + >(optionsOrCb, cb); + const opts = Object.assign({}, options); + // autoCreate is ignored - key must be created using Storage.createHmacKey. + delete opts.autoCreate; + + const reqOpts = { + uri: '/', + qs: opts, + }; + + this.request(reqOpts, (err, metadata, res) => { + if (err) { + callback!(err); + return; + } + this.metadata = metadata!; + callback!(null, this.metadata, res); + }); + } + + request(reqOpts: DecorateRequestOptions): Promise<[ResponseBody, Metadata]>; + request( + reqOpts: DecorateRequestOptions, + callback: BodyResponseCallback + ): void; + /** + * Makes request and applies userProject query parameter if necessary. + * + * @private + * + * @param {object} reqOpts - The request options. + * @param {function} callback - The callback function. + */ + request( + reqOpts: DecorateRequestOptions, + callback?: BodyResponseCallback + ): void | Promise<[ResponseBody, Metadata]> { + return this.parent.request.call(this, reqOpts, callback!); + } + + update( + metadata: UpdateHmacKeyMetadata, + options?: UpdateHmacKeyOptions + ): Promise; + update( + metadata: UpdateHmacKeyMetadata, + callback: HmacKeyMetadataCallback + ): void; + update( + metadata: UpdateHmacKeyMetadata, + options: UpdateHmacKeyOptions, + callback: HmacKeyMetadataCallback + ): void; + /** + * @typedef {object} UpdateHmacKeyMetadata Subset of {@link HmacKeyMetadata} to update. + * @property {string} state New state of the HmacKey. Either 'ACTIVE' or 'INACTIVE'. + * @property {string} [etag] Include an etag from a previous get HMAC key request + * to perform safe read-modify-write. + */ + /** + * @typedef {object} UpdateHmacKeyOptions + * @property {string} userProject This parameter is currently ignored. + */ + /** + * @callback HmacKeyMetadataCallback + * @param {?Error} err Request error, if any. + * @param {HmacKeyMetadata} metadata The updated HmacKeyMetadata resource. + * @param {object} apiResponse The full API response. + */ + /** + * @typedef {array} HmacKeyMetadataResponse + * @property {HmacKeyMetadata} 0 The updated HmacKeyMetadata resource. + * @property {object} 1 The full API response. + */ + update( + metadata: UpdateHmacKeyMetadata, + optionsOrCb?: UpdateHmacKeyOptions | HmacKeyMetadataCallback, + cb?: HmacKeyMetadataCallback + ): Promise | void { + if ( + typeof metadata !== 'object' || + Object.getOwnPropertyNames(metadata).length === 0 + ) { + throw new Error( + 'Cannot update HmacKey with an undefined/empty options object.' + ); + } + + const {options, callback} = normalize< + UpdateHmacKeyOptions, + HmacKeyMetadataCallback + >(optionsOrCb, cb); + + this.request( + { + uri: '/', + method: 'put', + qs: options, + body: metadata, + }, + (err: Error | null, metadata?: HmacKeyMetadata, res?: Metadata) => { + if (err) { + callback!(err); + return; + } + this.metadata = metadata!; + callback!(err, metadata, res); + } + ); + } +} + +/*! Developer Documentation + * + * All async methods (except for streams) will return a Promise in the event + * that a callback is omitted. + */ +promisifyAll(HmacKey); diff --git a/src/storage.ts b/src/storage.ts index 05370cac2..a22ad016f 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -25,6 +25,7 @@ import {Bucket} from './bucket'; import {Channel} from './channel'; import {File} from './file'; import {normalize} from './util'; +import {HmacKey} from './hmacKey'; export interface GetServiceAccountOptions { userProject?: string; @@ -99,6 +100,61 @@ export interface GetBucketsRequest { userProject?: string; } +export interface HmacKeyMetadata { + accessId: string; + etag: string; + id: string; + projectId: string; + serviceAccountEmail: string; + state: string; + timeCreated: string; +} + +export interface HmacKeyResource { + hmacKey: HmacKey; + secret: string; +} + +export interface HmacKeyResourceResponse { + metadata: HmacKeyMetadata; + secret: string; +} + +export type CreateHmacKeyResponse = [HmacKeyResource]; + +export interface CreateHmacKeyOptions { + userProject?: string; +} + +export interface CreateHmacKeyCallback { + ( + err: Error | null, + resource?: HmacKeyResource | null, + apiResponse?: HmacKeyResourceResponse + ): void; +} + +export interface GetHmacKeysOptions { + serviceAccountEmail?: string; + showDeletedKeys?: boolean; + autoPaginate?: boolean; + maxApiCalls?: number; + maxResults?: number; + pageToken?: string; + userProject?: string; +} + +export interface GetHmacKeysCallback { + ( + err: Error | null, + hmacKeys: HmacKey[] | null, + nextQuery?: {}, + apiResponse?: Metadata + ): void; +} + +export type GetHmacKeysResponse = [HmacKey[]]; + /*! Developer Documentation * * Invoke this method to create a new Storage object bound with pre-determined @@ -247,6 +303,35 @@ export class Storage extends Service { */ getBucketsStream: () => Readable; + /** + * Get {@link HmacKey} objects for all of the HMAC keys in the project in + * a readable object stream. + * + * @method Storage#getHmacKeysStream + * @param {GetHmacKeysOptions} [options] Configuration options. + * @returns {ReadableStream} A readable stream that emits {@link HmacKey} instances. + * + * @example + * storage.getHmacKeysStream() + * .on('error', console.error) + * .on('data', function(hmacKey) { + * // hmacKey is an HmacKey object. + * }) + * .on('end', function() { + * // All HmacKey retrieved. + * }); + * + * //- + * // If you anticipate many results, you can end a stream early to prevent + * // unnecessary processing and API requests. + * //- + * storage.getHmacKeysStream() + * .on('data', function(bucket) { + * this.end(); + * }); + */ + getHmacKeysStream: () => Readable; + /** * @typedef {object} StorageOptions * @property {string} [projectId] The project ID from the Google Developer's @@ -314,6 +399,7 @@ export class Storage extends Service { this.acl = Storage.acl; this.getBucketsStream = paginator.streamify('getBuckets'); + this.getHmacKeysStream = paginator.streamify('getHmacKeys'); } /** @@ -550,6 +636,132 @@ export class Storage extends Service { ); } + /** + * @typedef {object} CreateHmacKeyOptions + * @property {string} [userProject] This parameter is currently ignored. + */ + /** + * @typedef {object} HmacKeyMetadata + * @property {string} accessId The access id identifies which HMAC key was + * used to sign a request when authenticating with HMAC. + * @property {string} etag Used to perform a read-modify-write of the key. + * @property {string} id The resource name of the HMAC key. + * @property {string} projectId The project ID. + * @property {string} serviceAccountEmail The service account's email this + * HMAC key is created for. + * @property {string} state The state of this HMAC key. One of "ACTIVE", + * "INACTIVE" or "DELETED". + * @property {string} timeCreated The creation time of the HMAC key in + * RFC 3339 format. + * @property {string} [updated] The time this HMAC key was last updated in + * RFC 3339 format. + */ + /** + * @typedef {object} HmacKeyResource + * @property {HmacKey} hmacKey The HmacKey object. + * @property {string} secret The HMAC key secret used to access XML API. + */ + /** + * @typedef {array} CreateHmacKeyResponse + * @property {HmacKeyResource} 0 The HMAC key resource. + */ + /** + * @callback CreateHmacKeyCallback Callback function. + * @param {?Error} err Request error, if any. + * @param {HmacKeyResource} resource An object containing the HMAC key's secret and the HmacKey object. + * @param {object} apiResponse The raw API response. + */ + /** + * Create an HMAC key associated with an service account to authenticate + * requests to the Cloud Storage XML API. + * + * @see [HMAC keys documentation]{@linkhttps://cloud.google.com/storage/docs/authentication/hmackeys} + * + * @param {string} serviceAccountEmail The service account's email address + * with which the HMAC key is created for. + * @param {CreateHmacKeyCallback} [callback] Callback function. + * @return {Promise} + * + * @example + * const {Storage} = require('google-cloud/storage'); + * const storage = new Storage(); + * + * // Replace with your service account's email address + * const serviceAccountEmail = + * 'my-service-account@appspot.gserviceaccount.com'; + * + * storage.createHmacKey(serviceAccountEmail, function(err, hmacKeyResource) { + * if (!err) { + * const secret = hmacKey.secret; + * // Securely store the secret for use with the XML API. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * storage.createHmacKey(serviceAccountEmail) + * .then((response) => { + * const [hmacKeyResource] = response; + * const secret = hmacKeyResource.secret; + * // Securely store the secret for use with the XML API. + * }); + */ + createHmacKey( + serviceAccountEmail: string, + options?: CreateHmacKeyOptions + ): Promise; + createHmacKey( + serviceAccountEmail: string, + callback: CreateHmacKeyCallback + ): void; + createHmacKey( + serviceAccountEmail: string, + options: CreateHmacKeyOptions, + callback: CreateHmacKeyCallback + ): void; + createHmacKey( + serviceAccountEmail: string, + optionsOrCb?: CreateHmacKeyOptions | CreateHmacKeyCallback, + cb?: CreateHmacKeyCallback + ): Promise | void { + if (typeof serviceAccountEmail !== 'string') { + throw new Error( + 'The first argument must be a service account email to create an HMAC key.' + ); + } + + const {options, callback} = normalize< + CreateHmacKeyOptions, + CreateHmacKeyCallback + >(optionsOrCb, cb); + const query = Object.assign({}, options, {serviceAccountEmail}); + + this.request( + { + method: 'POST', + uri: `/projects/${this.projectId}/hmacKeys`, + qs: query, + }, + (err, resp: HmacKeyResourceResponse) => { + if (err) { + callback!(err, null, resp); + return; + } + + const hmacKey = new HmacKey(this, resp.metadata.accessId); + hmacKey.metadata = resp.metadata; + + const resource = { + hmacKey, + secret: resp.secret, + }; + + callback!(null, resource, resp); + } + ); + } + getBuckets(options?: GetBucketsRequest): Promise; getBuckets(options: GetBucketsRequest, callback: GetBucketsCallback): void; getBuckets(callback: GetBucketsCallback): void; @@ -664,6 +876,114 @@ export class Storage extends Service { ); } + /** + * Query object for listing HMAC keys. + * + * @typedef {object} GetHmacKeysOptions + * @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 + * state. Default is false. + * @property {boolean} [autoPaginate=true] Have pagination handled + * automatically. + * @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] This parameter is currently ignored. + */ + /** + * @typedef {array} GetHmacKeysResponse + * @property {HmacKey[]} 0 Array of {@link HmacKey} instances. + */ + /** + * @callback GetHmacKeysCallback + * @param {?Error} err Request error, if any. + * @param {HmacKey[]} hmacKeys Array of {@link HmacKey} instances. + */ + /** + * Retrieves a list of HMAC keys matching the criteria. + * + * The authenticated user must have storage.hmacKeys.list permission for the project in which the key exists. + * + * @param {GetHmacKeysOption} options Configuration options. + * @param {GetHmacKeysCallback} callback Callback function. + * @return {Promise} + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * storage.getHmacKeys(function(err, hmacKeys) { + * if (!err) { + * // hmacKeys is an array of HmacKey objects. + * } + * }); + * + * //- + * // To control how many API requests are made and page through the results + * // manually, set `autoPaginate` to `false`. + * //- + * const callback = function(err, hmacKeys, nextQuery, apiResponse) { + * if (nextQuery) { + * // More results exist. + * storage.getHmacKeys(nextQuery, callback); + * } + * + * // The `metadata` property is populated for you with the metadata at the + * // time of fetching. + * hmacKeys[0].metadata; + * }; + * + * storage.getHmacKeys({ + * autoPaginate: false + * }, callback); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * storage.getHmacKeys().then(function(data) { + * const hmacKeys = data[0]; + * }); + */ + getHmacKeys(options?: GetHmacKeysOptions): Promise; + getHmacKeys(callback: GetHmacKeysCallback): void; + getHmacKeys(options: GetHmacKeysOptions, callback: GetHmacKeysCallback): void; + getHmacKeys( + optionsOrCb?: GetHmacKeysOptions | GetHmacKeysCallback, + cb?: GetHmacKeysCallback + ): Promise | void { + const {options, callback} = normalize(optionsOrCb, cb); + + this.request( + { + uri: `/projects/${this.projectId}/hmacKeys`, + qs: options, + }, + (err, resp) => { + if (err) { + callback(err, null, null, resp); + return; + } + + const hmacKeys = arrify(resp.items).map((hmacKey: Metadata) => { + const hmacKeyInstance = this.hmacKey(hmacKey.accessId); + hmacKeyInstance.metadata = hmacKey; + return hmacKeyInstance; + }); + + const nextQuery = resp.nextPageToken + ? Object.assign({}, options, {pageToken: resp.nextPageToken}) + : null; + + callback(null, hmacKeys, nextQuery, resp); + } + ); + } + + getServiceAccount( + options?: GetServiceAccountOptions + ): Promise; getServiceAccount( options?: GetServiceAccountOptions ): Promise; @@ -754,13 +1074,34 @@ export class Storage extends Service { } ); } + + /** + * Get a reference to an HmacKey object. + * Note: this does not fetch the HMAC key's metadata. Use HmacKey#get() to + * retrieve and populate the metadata. + * + * @param {string} accessId The HMAC key's access ID. + * @returns {HmacKey} + * @see HmacKey + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const hmacKey = storage.hmacKey('ACCESS_ID'); + */ + hmacKey(accessId: string) { + return new HmacKey(this, accessId); + } } /*! Developer Documentation * * These methods can be auto-paginated. */ -paginator.extend(Storage, 'getBuckets'); +paginator.extend(Storage, [ + 'getBuckets', + 'getHmacKeys', +]); /*! Developer Documentation * @@ -768,5 +1109,5 @@ paginator.extend(Storage, 'getBuckets'); * that a callback is omitted. */ promisifyAll(Storage, { - exclude: ['bucket', 'channel'], + exclude: ['bucket', 'channel', 'hmacKey'], }); diff --git a/test/hmacKey.ts b/test/hmacKey.ts new file mode 100644 index 000000000..b72e6e1f8 --- /dev/null +++ b/test/hmacKey.ts @@ -0,0 +1,463 @@ +/** + * Copyright 2019 Google LLC. 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. + */ + +import * as sinon from 'sinon'; +import * as proxyquire from 'proxyquire'; +import * as assert from 'assert'; +import {util, ServiceObject} from '@google-cloud/common'; +import {HmacKey} from '../src/hmacKey'; +import {Storage} from '../src'; +import {HmacKeyResource} from '../src/storage'; + +// tslint:disable-next-line: no-any +let sandbox: sinon.SinonSandbox; +// tslint:disable-next-line: no-any +let STORAGE: any; +// tslint:disable-next-line: no-any +let hmacKey: any; + +const ACCESS_ID = 'fake-access-id'; + +const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; +const metadataResponse = { + accessId: ACCESS_ID, + etag: 'etag', + id: ACCESS_ID, + projectId: 'project-id', + serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, + state: 'ACTIVE', + timeCreated: '20190101T00:00:00Z', + updated: '20190101T00:00:00Z', +}; +const hmacKeyResource = { + metadata: metadataResponse, + secret: 'secret-key', +}; + +describe('HmacKey', () => { + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('initialization', () => { + let promisifyAllStub: sinon.SinonStub; + // tslint:disable-next-line: no-any + let serviceObjectSpy: sinon.SinonSpy; + // tslint:disable-next-line: no-any + let commonModule: any; + // tslint:disable-next-line: no-any variable-name + let HmacKey: any; + + beforeEach(() => { + promisifyAllStub = sandbox.stub(); + commonModule = {ServiceObject}; + serviceObjectSpy = sandbox.spy(commonModule, 'ServiceObject'); + + HmacKey = proxyquire('../src/hmacKey', { + '@google-cloud/common': commonModule, + '@google-cloud/promisify': { + promisifyAll: promisifyAllStub, + }, + }).HmacKey; + + STORAGE = { + request: util.noop, + }; + + hmacKey = new HmacKey(STORAGE, ACCESS_ID); + }); + + it('should promisify all the things', () => { + assert(promisifyAllStub.calledOnce); + }); + + it('should assign accessId', () => { + assert.strictEqual(hmacKey.accessId, ACCESS_ID); + }); + + it('should assign Storage instance', () => { + assert.strictEqual(hmacKey.parent, STORAGE); + }); + + it('should inherit from ServiceObject', () => { + assert(hmacKey instanceof ServiceObject); + const ctorArg = serviceObjectSpy.firstCall.args[0]; + assert(ctorArg.parent, STORAGE); + assert(ctorArg.id, ACCESS_ID); + assert.deepStrictEqual(ctorArg.methods, {delete: true}); + }); + + it('should throw if accessId is not provided', () => { + assert.throws(() => { + const _hmacKey = new HmacKey(STORAGE); + }, /access ID is needed/); + }); + }); + + describe('statics', () => { + // tslint:disable-next-line: no-any + let storageRequestStub: sinon.SinonStub; + let STORAGE: Storage; + + describe('createHmacKey', () => { + beforeEach(() => { + STORAGE = new Storage(); + storageRequestStub = sandbox + .stub(STORAGE, 'request') + .callsFake((_opts: {}, callback: Function) => { + callback(null, hmacKeyResource); + }); + }); + + it('should fail if serviceAccountEmail is not provided', () => { + return assert.rejects( + // tslint:disable-next-line: no-any + () => (STORAGE.createHmacKey as any)(undefined), + /first argument must be a service account email/ + ); + }); + + it('should return a promise when a callback is not provided', () => { + const promise = STORAGE.createHmacKey(SERVICE_ACCOUNT_EMAIL); + assert(promise instanceof Promise); + return promise; + }); + + it('should invoke callback with an HmacKeyResource', done => { + STORAGE.createHmacKey( + SERVICE_ACCOUNT_EMAIL, + (err: Error | null, hmacKey?: HmacKeyResource | null) => { + assert.ifError(err); + assert(hmacKey!.secret, hmacKeyResource.secret); + + const hmacKeyInstance = hmacKey!.hmacKey; + assert(hmacKeyInstance instanceof HmacKey); + assert.strictEqual( + hmacKeyInstance.accessId, + hmacKeyResource.metadata.accessId + ); + assert.deepStrictEqual( + hmacKeyInstance.metadata, + hmacKeyResource.metadata + ); + done(); + } + ); + }); + }); + + describe('getHmacKeys', () => { + beforeEach(() => { + STORAGE = new Storage(); + storageRequestStub = sandbox + .stub(STORAGE, 'request') + .callsFake((_opts: {}, callback: Function) => { + callback(null, {}); + }); + }); + + it('should get HmacKeys without a query', done => { + STORAGE.getHmacKeys(() => { + const firstArg = storageRequestStub.firstCall.args[0]; + assert.strictEqual( + firstArg.uri, + `/projects/${STORAGE.projectId}/hmacKeys` + ); + assert.deepStrictEqual(firstArg.qs, {}); + done(); + }); + }); + + it('should get HmacKeys with a query', done => { + const query = { + maxResults: 5, + pageToken: 'next-page-token', + serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, + showDeletedKeys: false, + }; + + STORAGE.getHmacKeys(query, () => { + const firstArg = storageRequestStub.firstCall.args[0]; + assert.strictEqual( + firstArg.uri, + `/projects/${STORAGE.projectId}/hmacKeys` + ); + assert.deepStrictEqual(firstArg.qs, query); + done(); + }); + }); + + it('should execute callback with error', done => { + const error = new Error('Error.'); + const apiResponse = {}; + + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(error, apiResponse); + }); + + STORAGE.getHmacKeys({}, (err, hmacKeys, nextQuery, resp) => { + assert.strictEqual(err, error); + assert.strictEqual(hmacKeys, null); + assert.strictEqual(nextQuery, null); + assert.strictEqual(resp, apiResponse); + done(); + }); + }); + + it('should return nextQuery if more results exist', done => { + const token = 'next-page-token'; + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {nextPageToken: token, items: []}); + }); + + STORAGE.getHmacKeys({maxResults: 5}, (err, _hmacKeys, nextQuery) => { + assert.ifError(err); + // tslint:disable-next-line: no-any + assert.strictEqual((nextQuery as any).pageToken, token); + // tslint:disable-next-line: no-any + assert.strictEqual((nextQuery as any).maxResults, 5); + done(); + }); + }); + + it('should return null nextQuery if there are no more results', done => { + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {items: []}); + }); + + STORAGE.getHmacKeys({maxResults: 5}, (err, _hmacKeys, nextQuery) => { + assert.ifError(err); + assert.strictEqual(nextQuery, null); + done(); + }); + }); + + it('should return HmacKey objects', done => { + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {items: [metadataResponse]}); + }); + + STORAGE.getHmacKeys((err, hmacKeys) => { + assert.ifError(err); + assert(hmacKeys![0] instanceof HmacKey); + done(); + }); + }); + + it('should return apiResponse', done => { + const resp = {items: [metadataResponse]}; + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, resp); + }); + + STORAGE.getHmacKeys((err, _hmacKeys, _nextQuery, apiResponse) => { + assert.ifError(err); + assert.deepStrictEqual(resp, apiResponse); + done(); + }); + }); + + it('should populate returned HmacKey object with accessId and metadata', done => { + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {items: [metadataResponse]}); + }); + + STORAGE.getHmacKeys((err, hmacKeys) => { + assert.ifError(err); + assert.strictEqual(hmacKeys![0].accessId, metadataResponse.accessId); + assert.deepStrictEqual(hmacKeys![0].metadata, metadataResponse); + done(); + }); + }); + }); + }); + + describe('methods', () => { + // tslint:disable-next-line: no-any + let storageRequestStub: sinon.SinonStub; + + beforeEach(() => { + const STORAGE = new Storage(); + storageRequestStub = sandbox + .stub(STORAGE, 'request') + .callsFake((_opts: {}, callback: Function) => { + callback(null, metadataResponse); + }); + + hmacKey = new HmacKey(STORAGE, ACCESS_ID); + }); + + describe('get', () => { + it('should accept just a callback', done => { + hmacKey.get(done); + }); + + it('should accept an options object and callback', done => { + hmacKey.get({userProject: 'my-project'}, done); + }); + + it('should execute callback with request error', done => { + const error = new Error('Request error'); + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(error); + }); + + hmacKey.get({}, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should return a Promise when callback is omitted', () => { + const promise = hmacKey.get(); + assert(promise instanceof Promise); + return promise; + }); + + it('should strip autoCreate field from request options', async () => { + await hmacKey.get({ + autoCreate: true, + otherField: 'value', + }); + const callArgs = storageRequestStub.firstCall.args; + assert.strictEqual(callArgs[0].qs.otherField, 'value'); + assert.strictEqual(callArgs[0].qs.autoCreate, undefined); + }); + + it('should resolve with the HMAC keys metadata and assign to instance', async () => { + const [metadata] = await hmacKey.get(); + + assert.deepStrictEqual(metadata, metadataResponse); + assert.deepStrictEqual(hmacKey.metadata, metadataResponse); + }); + }); + + describe('update', () => { + it('should throw without metadata object', () => { + assert.throws(() => { + hmacKey.update(undefined, assert.ifError); + }, /Cannot update HmacKey/); + }); + + it('should throw with an empty metadata object', () => { + assert.throws(() => { + hmacKey.update({}, assert.ifError); + }, /Cannot update HmacKey/); + }); + + it('should accept an options object', () => { + hmacKey.update({state: 'INACTIVE'}, {}, assert.ifError); + }); + + it('should execute callback with request error', done => { + const error = new Error('Request error'); + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(error); + }); + + hmacKey.update({state: 'INACTIVE'}, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should return a Promise when callback is omitted', () => { + const promise = hmacKey.update({state: 'INACTIVE'}); + assert(promise instanceof Promise); + return promise; + }); + + it('should make a request passing metadata arg as body', async () => { + const newMetadata = { + state: 'INACTIVE', + etag: 'some-etag', + }; + const options = { + userProject: 'my-project', + }; + + await hmacKey.update(newMetadata, options); + + const requestArg = storageRequestStub.firstCall.args[0]; + assert.deepStrictEqual(requestArg, { + uri: '/', + method: 'put', + qs: options, + body: newMetadata, + }); + }); + + it('should resolve with the HMAC keys metadata and assign to instance', async () => { + const newMetadata = { + state: 'INACTIVE', + etag: 'some-etag', + }; + const [metadata] = await hmacKey.update(newMetadata); + + const expectedMetadata = Object.assign( + {}, + newMetadata, + metadataResponse + ); + assert.deepStrictEqual(metadata, expectedMetadata); + assert.deepStrictEqual(hmacKey.metadata, expectedMetadata); + }); + + it('should assign the response metadata to the HmacKey instance', async () => { + await hmacKey.update({state: 'ACTIVE'}); + assert.deepStrictEqual(hmacKey.metadata, metadataResponse); + }); + }); + + describe('delete', () => { + it('should accept just a callback', done => { + hmacKey.delete((err: Error) => { + assert.ifError(err); + + const requestArg = storageRequestStub.firstCall.args[0]; + assert.deepStrictEqual(requestArg.method, 'DELETE'); + done(); + }); + }); + + it('should accept an options object and callback', done => { + hmacKey.delete({userProject: 'my-project'}, done); + }); + + it('should return a Promise when callback is omitted', () => { + const promise = hmacKey.delete(); + assert(promise instanceof Promise); + return promise; + }); + + it('should execute callback with request error', done => { + const error = new Error('Request error'); + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(error); + }); + + hmacKey.delete((err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + }); + }); +}); diff --git a/test/index.ts b/test/index.ts index 076aa803e..286065904 100644 --- a/test/index.ts +++ b/test/index.ts @@ -29,6 +29,9 @@ import * as proxyquire from 'proxyquire'; import {Bucket} from '../src'; import {GetFilesOptions} from '../src/bucket'; +import sinon = require('sinon'); +import {HmacKey} from '../src/hmacKey'; +import {HmacKeyResource, HmacKeyResourceResponse} from '../src/storage'; class FakeChannel { calledWith_: Array<{}>; @@ -189,6 +192,119 @@ describe('Storage', () => { }); }); + describe('createHmacKey', () => { + const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; + const ACCESS_ID = 'some-access-id'; + const metadataResponse = { + accessId: ACCESS_ID, + etag: 'etag', + id: ACCESS_ID, + projectId: 'project-id', + serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, + state: 'ACTIVE', + timeCreated: '20190101T00:00:00Z', + updated: '20190101T00:00:00Z', + }; + const response = { + secret: 'my-secret', + metadata: metadataResponse, + }; + + it('should make correct API request', done => { + storage.request = ( + reqOpts: DecorateRequestOptions, + callback: Function + ) => { + assert.strictEqual(reqOpts.method, 'POST'); + assert.strictEqual( + reqOpts.uri, + `/projects/${storage.projectId}/hmacKeys` + ); + assert.strictEqual( + reqOpts.qs.serviceAccountEmail, + SERVICE_ACCOUNT_EMAIL + ); + + callback(null, response); + }; + + storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, done); + }); + + it('should throw without a serviceAccountEmail', () => { + assert.throws( + () => storage.createHmacKey(), + /must be a service account email/ + ); + }); + + it('should throw when first argument is not a string', () => { + assert.throws( + () => + storage.createHmacKey({ + userProject: 'my-project', + }), + /must be a service account email/ + ); + }); + + it('should honor the userProject option', async () => { + const options = { + userProject: 'my-project', + }; + + storage.request = sinon + .stub() + .returns((_reqOpts: {}, callback: Function) => callback()); + + await storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, options); + const reqArg = storage.request.firstCall.args[0]; + assert.strictEqual(reqArg.qs.userProject, options.userProject); + }); + + it('should invoke callback with a secret and an HmacKey instance', done => { + storage.request = (_reqOpts: {}, callback: Function) => { + callback(null, response); + }; + + storage.createHmacKey( + SERVICE_ACCOUNT_EMAIL, + (err: Error, resource: HmacKeyResource) => { + assert.ifError(err); + assert.strictEqual(resource.secret, response.secret); + // tslint:disable-next-line: no-any + assert.strictEqual((resource as any).metadata, undefined); + assert(resource.hmacKey instanceof HmacKey); + assert.strictEqual(resource.hmacKey.metadata, metadataResponse); + assert.strictEqual( + resource.hmacKey.accessId, + metadataResponse.accessId + ); + done(); + } + ); + }); + + it('should invoke callback with raw apiResponse', done => { + storage.request = (_reqOpts: {}, callback: Function) => { + callback(null, response); + }; + + storage.createHmacKey( + SERVICE_ACCOUNT_EMAIL, + ( + err: Error, + resource: HmacKeyResource, + apiResponse: HmacKeyResourceResponse + ) => { + assert.ifError(err); + assert.strictEqual(apiResponse, response); + done(); + } + ); + }); + }); + describe('createBucket', () => { const BUCKET_NAME = 'new-bucket-name'; const METADATA = {a: 'b', c: {d: 'e'}}; From 1342a17df8e0807ea1580aeddc0ef28dd200e67d Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 18 Jun 2019 18:00:48 -0700 Subject: [PATCH 02/44] export types --- src/index.ts | 19 +++++++++++++++++++ src/storage.ts | 17 ++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/index.ts b/src/index.ts index 3d003d3b5..eee036447 100644 --- a/src/index.ts +++ b/src/index.ts @@ -181,6 +181,16 @@ export { SetStorageClassOptions, SetStorageClassResponse, } from './file'; +export { + GetHmacKeyOptions, + HmacKey, + HmacKeyMetadata, + HmacKeyMetadataCallback, + HmacKeyMetadataResponse, + ToggleHmacKeyOptions, + UpdateHmacKeyMetadata, + UpdateHmacKeyOptions, +} from './hmacKey'; export { GetPolicyCallback, GetPolicyOptions, @@ -210,12 +220,21 @@ export { BucketOptions, CreateBucketQuery, CreateBucketRequest, + CreateBucketResponse, + CreateHmacKeyCallback, + CreateHmacKeyOptions, + CreateHmacKeyResponse, GetBucketsCallback, GetBucketsRequest, GetBucketsResponse, + GetHmacKeysCallback, + GetHmacKeysOptions, + GetHmacKeysResponse, GetServiceAccountCallback, GetServiceAccountOptions, GetServiceAccountResponse, + HmacKeyResource, + HmacKeyResourceResponse, ServiceAccount, Storage, StorageOptions, diff --git a/src/storage.ts b/src/storage.ts index a22ad016f..09495db61 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -25,7 +25,7 @@ import {Bucket} from './bucket'; import {Channel} from './channel'; import {File} from './file'; import {normalize} from './util'; -import {HmacKey} from './hmacKey'; +import {HmacKey, HmacKeyMetadata} from './hmacKey'; export interface GetServiceAccountOptions { userProject?: string; @@ -100,16 +100,6 @@ export interface GetBucketsRequest { userProject?: string; } -export interface HmacKeyMetadata { - accessId: string; - etag: string; - id: string; - projectId: string; - serviceAccountEmail: string; - state: string; - timeCreated: string; -} - export interface HmacKeyResource { hmacKey: HmacKey; secret: string; @@ -1098,10 +1088,7 @@ export class Storage extends Service { * * These methods can be auto-paginated. */ -paginator.extend(Storage, [ - 'getBuckets', - 'getHmacKeys', -]); +paginator.extend(Storage, ['getBuckets', 'getHmacKeys']); /*! Developer Documentation * From 211eefccdf6a9912f251fb636c28d98f8a27a60a Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 19 Jun 2019 11:06:03 -0700 Subject: [PATCH 03/44] fix docs --- src/hmacKey.ts | 70 +++++++++++++++++++++++++++++++++++++++++++++----- src/storage.ts | 26 +++++++++---------- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 89018aaed..f5892375f 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -62,13 +62,31 @@ export interface HmacKeyMetadataCallback { export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; /** + * An HmacKey object contains metadata of an HMAC key created from a + * service account through the {@link Storage} client using + * {@link Storage#createHmacKey}. * + * @class */ export class HmacKey extends ServiceObject { accessId: string; metadata: HmacKeyMetadata; parent: Storage; + /** + * Constructs an HmacKey object. + * + * Note: this only create a local reference to an HMAC key, to create + * an HMAC key, use {@link Storage#createHmacKey}. + * + * @param {Storage} storage The Storage instance this HMAC key is + * attached to. + * @param {string} accessId The unique accessId for this HMAC key. + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * const hmacKey = storage.hmacKey('access-id'); + */ constructor(storage: Storage, accessId: string) { if (!accessId) { throw new Error('An access ID is needed to create an HmacKey object.'); @@ -98,7 +116,7 @@ export class HmacKey extends ServiceObject { * @method HmacKey#delete * @param {DeleteHmacKeyOptions} [options] Configuration options. * @param {DeleteHmacKeyCallback} [callback] Callback function. - * @returns {Promise} * * @example * const {Storage} = require('@google-cloud/storage'); @@ -148,6 +166,9 @@ export class HmacKey extends ServiceObject { this.parent = storage; } + get(options?: GetHmacKeyOptions): Promise; + get(callback: HmacKeyMetadataCallback): void; + get(options: GetHmacKeyOptions, callback: HmacKeyMetadataCallback): void; /** * @typedef {object} GetHmacKeyOptions * @property {string} userProject This parameter is currently ignored. @@ -184,14 +205,12 @@ export class HmacKey extends ServiceObject { * // If the callback is omitted, a promise is returned. * //- * storage.hmacKey('ACCESS_ID') - * .get((data) => { + * .get() + * .then((data) => { * const hmacKeyMetadata = data[0]; * console.log(hmacKeyMetadata); * }); */ - get(options?: GetHmacKeyOptions): Promise; - get(callback: HmacKeyMetadataCallback): void; - get(options: GetHmacKeyOptions, callback: HmacKeyMetadataCallback): void; get( optionsOrCb?: GetHmacKeyOptions | HmacKeyMetadataCallback, cb?: HmacKeyMetadataCallback @@ -265,14 +284,51 @@ export class HmacKey extends ServiceObject { /** * @callback HmacKeyMetadataCallback * @param {?Error} err Request error, if any. - * @param {HmacKeyMetadata} metadata The updated HmacKeyMetadata resource. + * @param {HmacKeyMetadata} metadata The updated {@link HmacKeyMetadata} object. * @param {object} apiResponse The full API response. */ /** * @typedef {array} HmacKeyMetadataResponse - * @property {HmacKeyMetadata} 0 The updated HmacKeyMetadata resource. + * @property {HmacKeyMetadata} 0 The updated {@link HmacKeyMetadata} object. * @property {object} 1 The full API response. */ + /** + * Updates the state of an HMAC key. See {@link UpdateHmacKeyMetadata} for + * valid states. + * + * @param {UpdateHmacKeyMetadata} metadata The new metadata. + * @param {UpdateHmacKeyOptions} [options] Configuration options. + * @param {HmacKeyMetadataCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * + * const metadata = { + * state: 'INACTIVE', + * }; + * + * storage.hmacKey('ACCESS_ID') + * .update(metadata, (err, hmacKeyMetadata) => { + * if (err) { + * // The request was an error. + * console.error(err); + * return; + * } + * console.log(hmacKeyMetadata); + * }); + * + * //- + * // If the callback is omitted, a promise is returned. + * //- + * storage.hmacKey('ACCESS_ID') + * .update(metadata) + * .then((data) => { + * const hmacKeyMetadata = data[0]; + * console.log(hmacKeyMetadata); + * }); + */ update( metadata: UpdateHmacKeyMetadata, optionsOrCb?: UpdateHmacKeyOptions | HmacKeyMetadataCallback, diff --git a/src/storage.ts b/src/storage.ts index 09495db61..289f2f713 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -626,6 +626,19 @@ export class Storage extends Service { ); } + createHmacKey( + serviceAccountEmail: string, + options?: CreateHmacKeyOptions + ): Promise; + createHmacKey( + serviceAccountEmail: string, + callback: CreateHmacKeyCallback + ): void; + createHmacKey( + serviceAccountEmail: string, + options: CreateHmacKeyOptions, + callback: CreateHmacKeyCallback + ): void; /** * @typedef {object} CreateHmacKeyOptions * @property {string} [userProject] This parameter is currently ignored. @@ -697,19 +710,6 @@ export class Storage extends Service { * // Securely store the secret for use with the XML API. * }); */ - createHmacKey( - serviceAccountEmail: string, - options?: CreateHmacKeyOptions - ): Promise; - createHmacKey( - serviceAccountEmail: string, - callback: CreateHmacKeyCallback - ): void; - createHmacKey( - serviceAccountEmail: string, - options: CreateHmacKeyOptions, - callback: CreateHmacKeyCallback - ): void; createHmacKey( serviceAccountEmail: string, optionsOrCb?: CreateHmacKeyOptions | CreateHmacKeyCallback, From 89283e03d07cf455807875288dd6912e8735679f Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 19 Jun 2019 11:27:09 -0700 Subject: [PATCH 04/44] move tests --- test/hmacKey.ts | 182 ------------------------------------------------ test/index.ts | 153 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 184 deletions(-) diff --git a/test/hmacKey.ts b/test/hmacKey.ts index b72e6e1f8..682293ac6 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -42,10 +42,6 @@ const metadataResponse = { timeCreated: '20190101T00:00:00Z', updated: '20190101T00:00:00Z', }; -const hmacKeyResource = { - metadata: metadataResponse, - secret: 'secret-key', -}; describe('HmacKey', () => { beforeEach(() => { @@ -111,184 +107,6 @@ describe('HmacKey', () => { }); }); - describe('statics', () => { - // tslint:disable-next-line: no-any - let storageRequestStub: sinon.SinonStub; - let STORAGE: Storage; - - describe('createHmacKey', () => { - beforeEach(() => { - STORAGE = new Storage(); - storageRequestStub = sandbox - .stub(STORAGE, 'request') - .callsFake((_opts: {}, callback: Function) => { - callback(null, hmacKeyResource); - }); - }); - - it('should fail if serviceAccountEmail is not provided', () => { - return assert.rejects( - // tslint:disable-next-line: no-any - () => (STORAGE.createHmacKey as any)(undefined), - /first argument must be a service account email/ - ); - }); - - it('should return a promise when a callback is not provided', () => { - const promise = STORAGE.createHmacKey(SERVICE_ACCOUNT_EMAIL); - assert(promise instanceof Promise); - return promise; - }); - - it('should invoke callback with an HmacKeyResource', done => { - STORAGE.createHmacKey( - SERVICE_ACCOUNT_EMAIL, - (err: Error | null, hmacKey?: HmacKeyResource | null) => { - assert.ifError(err); - assert(hmacKey!.secret, hmacKeyResource.secret); - - const hmacKeyInstance = hmacKey!.hmacKey; - assert(hmacKeyInstance instanceof HmacKey); - assert.strictEqual( - hmacKeyInstance.accessId, - hmacKeyResource.metadata.accessId - ); - assert.deepStrictEqual( - hmacKeyInstance.metadata, - hmacKeyResource.metadata - ); - done(); - } - ); - }); - }); - - describe('getHmacKeys', () => { - beforeEach(() => { - STORAGE = new Storage(); - storageRequestStub = sandbox - .stub(STORAGE, 'request') - .callsFake((_opts: {}, callback: Function) => { - callback(null, {}); - }); - }); - - it('should get HmacKeys without a query', done => { - STORAGE.getHmacKeys(() => { - const firstArg = storageRequestStub.firstCall.args[0]; - assert.strictEqual( - firstArg.uri, - `/projects/${STORAGE.projectId}/hmacKeys` - ); - assert.deepStrictEqual(firstArg.qs, {}); - done(); - }); - }); - - it('should get HmacKeys with a query', done => { - const query = { - maxResults: 5, - pageToken: 'next-page-token', - serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, - showDeletedKeys: false, - }; - - STORAGE.getHmacKeys(query, () => { - const firstArg = storageRequestStub.firstCall.args[0]; - assert.strictEqual( - firstArg.uri, - `/projects/${STORAGE.projectId}/hmacKeys` - ); - assert.deepStrictEqual(firstArg.qs, query); - done(); - }); - }); - - it('should execute callback with error', done => { - const error = new Error('Error.'); - const apiResponse = {}; - - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(error, apiResponse); - }); - - STORAGE.getHmacKeys({}, (err, hmacKeys, nextQuery, resp) => { - assert.strictEqual(err, error); - assert.strictEqual(hmacKeys, null); - assert.strictEqual(nextQuery, null); - assert.strictEqual(resp, apiResponse); - done(); - }); - }); - - it('should return nextQuery if more results exist', done => { - const token = 'next-page-token'; - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(null, {nextPageToken: token, items: []}); - }); - - STORAGE.getHmacKeys({maxResults: 5}, (err, _hmacKeys, nextQuery) => { - assert.ifError(err); - // tslint:disable-next-line: no-any - assert.strictEqual((nextQuery as any).pageToken, token); - // tslint:disable-next-line: no-any - assert.strictEqual((nextQuery as any).maxResults, 5); - done(); - }); - }); - - it('should return null nextQuery if there are no more results', done => { - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(null, {items: []}); - }); - - STORAGE.getHmacKeys({maxResults: 5}, (err, _hmacKeys, nextQuery) => { - assert.ifError(err); - assert.strictEqual(nextQuery, null); - done(); - }); - }); - - it('should return HmacKey objects', done => { - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(null, {items: [metadataResponse]}); - }); - - STORAGE.getHmacKeys((err, hmacKeys) => { - assert.ifError(err); - assert(hmacKeys![0] instanceof HmacKey); - done(); - }); - }); - - it('should return apiResponse', done => { - const resp = {items: [metadataResponse]}; - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(null, resp); - }); - - STORAGE.getHmacKeys((err, _hmacKeys, _nextQuery, apiResponse) => { - assert.ifError(err); - assert.deepStrictEqual(resp, apiResponse); - done(); - }); - }); - - it('should populate returned HmacKey object with accessId and metadata', done => { - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(null, {items: [metadataResponse]}); - }); - - STORAGE.getHmacKeys((err, hmacKeys) => { - assert.ifError(err); - assert.strictEqual(hmacKeys![0].accessId, metadataResponse.accessId); - assert.deepStrictEqual(hmacKeys![0].metadata, metadataResponse); - done(); - }); - }); - }); - }); - describe('methods', () => { // tslint:disable-next-line: no-any let storageRequestStub: sinon.SinonStub; diff --git a/test/index.ts b/test/index.ts index 286065904..233f4ea0e 100644 --- a/test/index.ts +++ b/test/index.ts @@ -32,6 +32,7 @@ import {GetFilesOptions} from '../src/bucket'; import sinon = require('sinon'); import {HmacKey} from '../src/hmacKey'; import {HmacKeyResource, HmacKeyResourceResponse} from '../src/storage'; +import {any} from 'async'; class FakeChannel { calledWith_: Array<{}>; @@ -59,7 +60,7 @@ const fakePaginator = { methods = arrify(methods); assert.strictEqual(Class.name, 'Storage'); - assert.deepStrictEqual(methods, ['getBuckets']); + assert.deepStrictEqual(methods, ['getBuckets', 'getHmacKeys']); extended = true; }, streamify(methodName: string) { @@ -77,7 +78,7 @@ const fakePromisify = { } promisified = true; - assert.deepStrictEqual(options.exclude, ['bucket', 'channel']); + assert.deepStrictEqual(options.exclude, ['bucket', 'channel', 'hmacKey']); }, }; @@ -624,6 +625,154 @@ describe('Storage', () => { }); }); + describe('getHmacKeys', () => { + // tslint:disable-next-line: no-any + let storageRequestStub: sinon.SinonStub; + const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; + const ACCESS_ID = 'some-access-id'; + const metadataResponse = { + accessId: ACCESS_ID, + etag: 'etag', + id: ACCESS_ID, + projectId: 'project-id', + serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, + state: 'ACTIVE', + timeCreated: '20190101T00:00:00Z', + updated: '20190101T00:00:00Z', + }; + + beforeEach(() => { + storageRequestStub = sinon.stub(storage, 'request'); + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {}); + }); + }); + + it('should get HmacKeys without a query', done => { + storage.getHmacKeys(() => { + const firstArg = storage.request.firstCall.args[0]; + assert.strictEqual( + firstArg.uri, + `/projects/${storage.projectId}/hmacKeys` + ); + assert.deepStrictEqual(firstArg.qs, {}); + done(); + }); + }); + + it('should get HmacKeys with a query', done => { + const query = { + maxResults: 5, + pageToken: 'next-page-token', + serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, + showDeletedKeys: false, + }; + + storage.getHmacKeys(query, () => { + const firstArg = storage.request.firstCall.args[0]; + assert.strictEqual( + firstArg.uri, + `/projects/${storage.projectId}/hmacKeys` + ); + assert.deepStrictEqual(firstArg.qs, query); + done(); + }); + }); + + it('should execute callback with error', done => { + const error = new Error('Error.'); + const apiResponse = {}; + + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(error, apiResponse); + }); + + storage.getHmacKeys( + {}, + (err: Error, hmacKeys: HmacKey[], nextQuery: {}, resp: Metadata) => { + assert.strictEqual(err, error); + assert.strictEqual(hmacKeys, null); + assert.strictEqual(nextQuery, null); + assert.strictEqual(resp, apiResponse); + done(); + } + ); + }); + + it('should return nextQuery if more results exist', done => { + const token = 'next-page-token'; + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {nextPageToken: token, items: []}); + }); + + // tslint:disable-next-line: no-any + storage.getHmacKeys( + {maxResults: 5}, + (err: Error, _hmacKeys: [], nextQuery: any) => { + assert.ifError(err); + assert.strictEqual(nextQuery.pageToken, token); + assert.strictEqual(nextQuery.maxResults, 5); + done(); + } + ); + }); + + it('should return null nextQuery if there are no more results', done => { + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {items: []}); + }); + + storage.getHmacKeys( + {maxResults: 5}, + (err: Error, _hmacKeys: [], nextQuery: {}) => { + assert.ifError(err); + assert.strictEqual(nextQuery, null); + done(); + } + ); + }); + + it('should return HmacKey objects', done => { + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {items: [metadataResponse]}); + }); + + storage.getHmacKeys((err: Error, hmacKeys: HmacKey[]) => { + assert.ifError(err); + assert(hmacKeys![0] instanceof HmacKey); + done(); + }); + }); + + it('should return apiResponse', done => { + const resp = {items: [metadataResponse]}; + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, resp); + }); + + storage.getHmacKeys( + (err: Error, _hmacKeys: [], _nextQuery: {}, apiResponse: Metadata) => { + assert.ifError(err); + assert.deepStrictEqual(resp, apiResponse); + done(); + } + ); + }); + + it('should populate returned HmacKey object with accessId and metadata', done => { + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(null, {items: [metadataResponse]}); + }); + + storage.getHmacKeys((err: Error, hmacKeys: HmacKey[]) => { + assert.ifError(err); + assert.strictEqual(hmacKeys[0].accessId, metadataResponse.accessId); + assert.deepStrictEqual(hmacKeys[0].metadata, metadataResponse); + done(); + }); + }); + }); + describe('getServiceAccount', () => { it('should make the correct request', done => { storage.request = (reqOpts: DecorateRequestOptions) => { From e3297090bb7ff02fcda0655c770e7676bd97e61b Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 19 Jun 2019 14:42:05 -0700 Subject: [PATCH 05/44] fix type --- test/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 233f4ea0e..a4c268e39 100644 --- a/test/index.ts +++ b/test/index.ts @@ -32,7 +32,6 @@ import {GetFilesOptions} from '../src/bucket'; import sinon = require('sinon'); import {HmacKey} from '../src/hmacKey'; import {HmacKeyResource, HmacKeyResourceResponse} from '../src/storage'; -import {any} from 'async'; class FakeChannel { calledWith_: Array<{}>; From 41fb442956c5bb058fb162bfd146a22ba8851b7f Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 20 Jun 2019 15:44:09 -0700 Subject: [PATCH 06/44] json instead of body --- src/hmacKey.ts | 22 +--------------------- test/hmacKey.ts | 12 +++++------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index f5892375f..c51fb4203 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -238,26 +238,6 @@ export class HmacKey extends ServiceObject { }); } - request(reqOpts: DecorateRequestOptions): Promise<[ResponseBody, Metadata]>; - request( - reqOpts: DecorateRequestOptions, - callback: BodyResponseCallback - ): void; - /** - * Makes request and applies userProject query parameter if necessary. - * - * @private - * - * @param {object} reqOpts - The request options. - * @param {function} callback - The callback function. - */ - request( - reqOpts: DecorateRequestOptions, - callback?: BodyResponseCallback - ): void | Promise<[ResponseBody, Metadata]> { - return this.parent.request.call(this, reqOpts, callback!); - } - update( metadata: UpdateHmacKeyMetadata, options?: UpdateHmacKeyOptions @@ -353,7 +333,7 @@ export class HmacKey extends ServiceObject { uri: '/', method: 'put', qs: options, - body: metadata, + json: metadata, }, (err: Error | null, metadata?: HmacKeyMetadata, res?: Metadata) => { if (err) { diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 682293ac6..f4ee4d5b2 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -32,11 +32,12 @@ let hmacKey: any; const ACCESS_ID = 'fake-access-id'; const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; +const PROJECT_ID = 'project-id'; const metadataResponse = { accessId: ACCESS_ID, etag: 'etag', id: ACCESS_ID, - projectId: 'project-id', + projectId: PROJECT_ID, serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, state: 'ACTIVE', timeCreated: '20190101T00:00:00Z', @@ -214,12 +215,9 @@ describe('HmacKey', () => { await hmacKey.update(newMetadata, options); const requestArg = storageRequestStub.firstCall.args[0]; - assert.deepStrictEqual(requestArg, { - uri: '/', - method: 'put', - qs: options, - body: newMetadata, - }); + assert.deepStrictEqual(requestArg.method, 'put'); + assert.deepStrictEqual(requestArg.qs, options); + assert.deepStrictEqual(requestArg.json, newMetadata); }); it('should resolve with the HMAC keys metadata and assign to instance', async () => { From cbdd8650d079abc3bdd4929e4972a94e9e71e90c Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 20 Jun 2019 16:03:56 -0700 Subject: [PATCH 07/44] npm run fix --- test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index a4c268e39..eb1ad6220 100644 --- a/test/index.ts +++ b/test/index.ts @@ -704,9 +704,9 @@ describe('Storage', () => { callback(null, {nextPageToken: token, items: []}); }); - // tslint:disable-next-line: no-any storage.getHmacKeys( {maxResults: 5}, + // tslint:disable-next-line: no-any (err: Error, _hmacKeys: [], nextQuery: any) => { assert.ifError(err); assert.strictEqual(nextQuery.pageToken, token); From e67719152df43836a8f721e609c0b52a61f5b25f Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 20 Jun 2019 17:12:12 -0700 Subject: [PATCH 08/44] add request error test for createHmacKey --- test/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/index.ts b/test/index.ts index eb1ad6220..19d0bec3a 100644 --- a/test/index.ts +++ b/test/index.ts @@ -303,6 +303,19 @@ describe('Storage', () => { } ); }); + + it('should execute callback with request error', done => { + const error = new Error('Request error'); + storage.request = (_reqOpts: {}, callback: Function) => { + callback(error); + }; + + storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, + (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); }); describe('createBucket', () => { From 0c734f13928f9b10fd2030406f029fc6f4e7bb87 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Fri, 21 Jun 2019 14:00:10 -0700 Subject: [PATCH 09/44] fix-lint --- test/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/index.ts b/test/index.ts index 19d0bec3a..3fee7d081 100644 --- a/test/index.ts +++ b/test/index.ts @@ -310,11 +310,10 @@ describe('Storage', () => { callback(error); }; - storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, - (err: Error) => { - assert.strictEqual(err, error); - done(); - }); + storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); }); }); From 645a2313a393907040e3a96b9970fd19f6d07b3d Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 2 Jul 2019 15:15:58 -0700 Subject: [PATCH 10/44] do not stub metadata --- src/hmacKey.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index c51fb4203..57026c625 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -18,9 +18,6 @@ import { Metadata, ServiceObject, GetConfig, - DecorateRequestOptions, - ResponseBody, - BodyResponseCallback, } from '@google-cloud/common'; import {promisifyAll} from '@google-cloud/promisify'; @@ -68,10 +65,9 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; * * @class */ -export class HmacKey extends ServiceObject { +export class HmacKey extends ServiceObject { accessId: string; - metadata: HmacKeyMetadata; - parent: Storage; + metadata: HmacKeyMetadata|undefined; /** * Constructs an HmacKey object. @@ -162,8 +158,6 @@ export class HmacKey extends ServiceObject { }); this.accessId = accessId; - this.metadata = {accessId}; - this.parent = storage; } get(options?: GetHmacKeyOptions): Promise; From d70e0452d246219f03fbf9f88729d03358fd402d Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 3 Jul 2019 15:47:52 -0700 Subject: [PATCH 11/44] expose inherited getMetadata instead of overriden get --- src/hmacKey.ts | 132 ++++++++++++++++++------------------------------ src/index.ts | 1 - test/hmacKey.ts | 27 ++++------ 3 files changed, 59 insertions(+), 101 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 57026c625..182b645dd 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - Metadata, - ServiceObject, - GetConfig, -} from '@google-cloud/common'; +import {Metadata, ServiceObject, GetConfig} from '@google-cloud/common'; import {promisifyAll} from '@google-cloud/promisify'; import {Storage} from './storage'; @@ -35,10 +31,6 @@ export interface HmacKeyMetadata { updated?: string; } -export interface GetHmacKeyOptions extends GetConfig { - userProject?: string; -} - export interface UpdateHmacKeyOptions { userProject?: string; } @@ -65,9 +57,9 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; * * @class */ -export class HmacKey extends ServiceObject { +export class HmacKey extends ServiceObject { accessId: string; - metadata: HmacKeyMetadata|undefined; + metadata: HmacKeyMetadata | undefined; /** * Constructs an HmacKey object. @@ -148,6 +140,52 @@ export class HmacKey extends ServiceObject { * }); */ delete: true, + /** + * @typedef {object} GetHmacKeyMetadataOptions + * @property {string} userProject This parameter is currently ignored. + */ + /** + * Retrieves and populate an HMAC key's metadata. + * + * HmacKey.get() does not give the HMAC key secret, as + * it is only returned on creation. + * + * The authenticated user must have `storage.hmacKeys.get` permission + * for the project in which the key exists. + * + * @method HmacKey#getMetadata + * @param {GetHmacKeyMetadataOptions} [options] Configuration options. + * @param {HmacKeyMetadataCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * + * //- + * // Get the HmacKey's metadata and populate to the metadata property. + * //- + * storage.hmacKey('ACCESS_ID') + * .getMetadata((err, hmacKeyMetadata) => { + * if (err) { + * // The request was an error. + * console.error(err); + * return; + * } + * console.log(hmacKeyMetadata); + * }); + * + * //- + * // If the callback is omitted, a promise is returned. + * //- + * storage.hmacKey('ACCESS_ID') + * .getMetadata() + * .then((data) => { + * const hmacKeyMetadata = data[0]; + * console.log(hmacKeyMetadata); + * }); + */ + getMetadata: true, }; super({ @@ -160,78 +198,6 @@ export class HmacKey extends ServiceObject { this.accessId = accessId; } - get(options?: GetHmacKeyOptions): Promise; - get(callback: HmacKeyMetadataCallback): void; - get(options: GetHmacKeyOptions, callback: HmacKeyMetadataCallback): void; - /** - * @typedef {object} GetHmacKeyOptions - * @property {string} userProject This parameter is currently ignored. - */ - /** - * Retrieves and populate an HMAC key's metadata. - * - * HmacKey.get() does not give the HMAC key secret, as - * it is only returned on creation. - * The authenticated user must have `storage.hmacKeys.get` permission for the project in which the key exists. - * - * @param {GetHmacKeyOptions} [options] Configuration options. - * @param {HmacKeyMetadataCallback} [callback] Callback function. - * @returns {Promise} - * - * @example - * const {Storage} = require('@google-cloud/storage'); - * const storage = new Storage(); - * - * //- - * // Get the HmacKey's Metadata. - * //- - * storage.hmacKey('ACCESS_ID') - * .get((err, hmacKeyMetadata) => { - * if (err) { - * // The request was an error. - * console.error(err); - * return; - * } - * console.log(hmacKeyMetadata); - * }); - * - * //- - * // If the callback is omitted, a promise is returned. - * //- - * storage.hmacKey('ACCESS_ID') - * .get() - * .then((data) => { - * const hmacKeyMetadata = data[0]; - * console.log(hmacKeyMetadata); - * }); - */ - get( - optionsOrCb?: GetHmacKeyOptions | HmacKeyMetadataCallback, - cb?: HmacKeyMetadataCallback - ): Promise | void { - const {options, callback} = normalize< - GetHmacKeyOptions, - HmacKeyMetadataCallback - >(optionsOrCb, cb); - const opts = Object.assign({}, options); - // autoCreate is ignored - key must be created using Storage.createHmacKey. - delete opts.autoCreate; - - const reqOpts = { - uri: '/', - qs: opts, - }; - - this.request(reqOpts, (err, metadata, res) => { - if (err) { - callback!(err); - return; - } - this.metadata = metadata!; - callback!(null, this.metadata, res); - }); - } - update( metadata: UpdateHmacKeyMetadata, options?: UpdateHmacKeyOptions diff --git a/src/index.ts b/src/index.ts index eee036447..1fe836ff9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -182,7 +182,6 @@ export { SetStorageClassResponse, } from './file'; export { - GetHmacKeyOptions, HmacKey, HmacKeyMetadata, HmacKeyMetadataCallback, diff --git a/test/hmacKey.ts b/test/hmacKey.ts index f4ee4d5b2..0e64d8976 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -98,7 +98,10 @@ describe('HmacKey', () => { const ctorArg = serviceObjectSpy.firstCall.args[0]; assert(ctorArg.parent, STORAGE); assert(ctorArg.id, ACCESS_ID); - assert.deepStrictEqual(ctorArg.methods, {delete: true}); + assert.deepStrictEqual(ctorArg.methods, { + delete: true, + getMetadata: true, + }); }); it('should throw if accessId is not provided', () => { @@ -123,13 +126,13 @@ describe('HmacKey', () => { hmacKey = new HmacKey(STORAGE, ACCESS_ID); }); - describe('get', () => { + describe('getMetadata', () => { it('should accept just a callback', done => { - hmacKey.get(done); + hmacKey.getMetadata(done); }); it('should accept an options object and callback', done => { - hmacKey.get({userProject: 'my-project'}, done); + hmacKey.getMetadata({userProject: 'my-project'}, done); }); it('should execute callback with request error', done => { @@ -138,30 +141,20 @@ describe('HmacKey', () => { callback(error); }); - hmacKey.get({}, (err: Error) => { + hmacKey.getMetadata({}, (err: Error) => { assert.strictEqual(err, error); done(); }); }); it('should return a Promise when callback is omitted', () => { - const promise = hmacKey.get(); + const promise = hmacKey.getMetadata(); assert(promise instanceof Promise); return promise; }); - it('should strip autoCreate field from request options', async () => { - await hmacKey.get({ - autoCreate: true, - otherField: 'value', - }); - const callArgs = storageRequestStub.firstCall.args; - assert.strictEqual(callArgs[0].qs.otherField, 'value'); - assert.strictEqual(callArgs[0].qs.autoCreate, undefined); - }); - it('should resolve with the HMAC keys metadata and assign to instance', async () => { - const [metadata] = await hmacKey.get(); + const [metadata] = await hmacKey.getMetadata(); assert.deepStrictEqual(metadata, metadataResponse); assert.deepStrictEqual(hmacKey.metadata, metadataResponse); From 7311019765c1fbac2584aa3882f55fb592339df2 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 3 Jul 2019 16:08:42 -0700 Subject: [PATCH 12/44] Add back get method inherited from ServiceObject --- src/hmacKey.ts | 55 ++++++++++++++++++++++++++++++++++++++++++++++--- test/hmacKey.ts | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 182b645dd..894cf0304 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {Metadata, ServiceObject, GetConfig} from '@google-cloud/common'; +import {Metadata, ServiceObject} from '@google-cloud/common'; import {promisifyAll} from '@google-cloud/promisify'; import {Storage} from './storage'; @@ -32,6 +32,9 @@ export interface HmacKeyMetadata { } export interface UpdateHmacKeyOptions { + /** + * This parameter is currently ignored. + */ userProject?: string; } @@ -145,9 +148,10 @@ export class HmacKey extends ServiceObject { * @property {string} userProject This parameter is currently ignored. */ /** - * Retrieves and populate an HMAC key's metadata. + * Retrieves and populate an HMAC key's metadata, and return + * the HMAC key's metadata as an object. * - * HmacKey.get() does not give the HMAC key secret, as + * HmacKey.getMetadata() does not give the HMAC key secret, as * it is only returned on creation. * * The authenticated user must have `storage.hmacKeys.get` permission @@ -186,6 +190,51 @@ export class HmacKey extends ServiceObject { * }); */ getMetadata: true, + /** + * @typedef {object} GetHmacKeyOptions + * @property {string} userProject This parameter is currently ignored. + */ + /** + * Retrieves and populate an HMAC key's metadata, and return + * the HMAC key object. + * + * HmacKey.get() does not give the HMAC key secret, as + * it is only returned on creation. + * + * The authenticated user must have `storage.hmacKeys.get` permission + * for the project in which the key exists. + * + * @param {GetHmacKeyOptions} [options] Configuration options. + * @param {GetHmacKeyCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * + * //- + * // Get the HmacKey's Metadata. + * //- + * storage.hmacKey('ACCESS_ID') + * .get((err, hmacKey) => { + * if (err) { + * // The request was an error. + * console.error(err); + * return; + * } + * // do something with the returned HmacKey object. + * }); + * + * //- + * // If the callback is omitted, a promise is returned. + * //- + * storage.hmacKey('ACCESS_ID') + * .get() + * .then((data) => { + * const hmacKey = data[0]; + * }); + */ + get: true, }; super({ diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 0e64d8976..596fee904 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -101,6 +101,7 @@ describe('HmacKey', () => { assert.deepStrictEqual(ctorArg.methods, { delete: true, getMetadata: true, + get: true, }); }); @@ -126,6 +127,43 @@ describe('HmacKey', () => { hmacKey = new HmacKey(STORAGE, ACCESS_ID); }); + describe('get', () => { + it('should accept just a callback', done => { + hmacKey.get(done); + }); + + it('should accept an options object and callback', done => { + hmacKey.get({userProject: 'my-project'}, done); + }); + + it('should execute callback with request error', done => { + const error = new Error('Request error'); + storageRequestStub.callsFake((_opts: {}, callback: Function) => { + callback(error); + }); + + hmacKey.get({}, (err: Error) => { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should return a Promise when callback is omitted', async () => { + const promise = hmacKey.get(); + assert(promise instanceof Promise); + const res = await promise; + assert(Array.isArray(res)); + assert(res[0] instanceof HmacKey); + }); + + it('should resolve with the HMAC key and assign HmacKey.metadata', async () => { + const [hmacKey2] = await hmacKey.get(); + + assert.strictEqual(hmacKey2, hmacKey); + assert.deepStrictEqual(hmacKey2.metadata, metadataResponse); + }); + }); + describe('getMetadata', () => { it('should accept just a callback', done => { hmacKey.getMetadata(done); From 9266001abed19a6d013b5b162a34ce6e0e02230a Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 3 Jul 2019 16:17:21 -0700 Subject: [PATCH 13/44] fix docs --- src/hmacKey.ts | 1 + test/hmacKey.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 894cf0304..df17664da 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -204,6 +204,7 @@ export class HmacKey extends ServiceObject { * The authenticated user must have `storage.hmacKeys.get` permission * for the project in which the key exists. * + * @method HmacKey#get * @param {GetHmacKeyOptions} [options] Configuration options. * @param {GetHmacKeyCallback} [callback] Callback function. * @returns {Promise} diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 596fee904..92cd4b0f2 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -20,7 +20,6 @@ import * as assert from 'assert'; import {util, ServiceObject} from '@google-cloud/common'; import {HmacKey} from '../src/hmacKey'; import {Storage} from '../src'; -import {HmacKeyResource} from '../src/storage'; // tslint:disable-next-line: no-any let sandbox: sinon.SinonSandbox; From a4628b8905dfd58db837391ab4f93914bc421ac7 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Mon, 8 Jul 2019 13:46:37 -0700 Subject: [PATCH 14/44] createHmacKey callback (err, hmacKey, secret, apiResponse) --- src/index.ts | 1 - src/storage.ts | 38 +++++++++++++------------------------- test/index.ts | 17 ++++++++--------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1fe836ff9..a910b51c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -232,7 +232,6 @@ export { GetServiceAccountCallback, GetServiceAccountOptions, GetServiceAccountResponse, - HmacKeyResource, HmacKeyResourceResponse, ServiceAccount, Storage, diff --git a/src/storage.ts b/src/storage.ts index 289f2f713..1aa1812c3 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -100,17 +100,12 @@ export interface GetBucketsRequest { userProject?: string; } -export interface HmacKeyResource { - hmacKey: HmacKey; - secret: string; -} - export interface HmacKeyResourceResponse { metadata: HmacKeyMetadata; secret: string; } -export type CreateHmacKeyResponse = [HmacKeyResource]; +export type CreateHmacKeyResponse = [HmacKey, string, HmacKeyResourceResponse]; export interface CreateHmacKeyOptions { userProject?: string; @@ -119,7 +114,8 @@ export interface CreateHmacKeyOptions { export interface CreateHmacKeyCallback { ( err: Error | null, - resource?: HmacKeyResource | null, + hmacKey?: HmacKey | null, + secret?: string | null, apiResponse?: HmacKeyResourceResponse ): void; } @@ -659,19 +655,17 @@ export class Storage extends Service { * @property {string} [updated] The time this HMAC key was last updated in * RFC 3339 format. */ - /** - * @typedef {object} HmacKeyResource - * @property {HmacKey} hmacKey The HmacKey object. - * @property {string} secret The HMAC key secret used to access XML API. - */ /** * @typedef {array} CreateHmacKeyResponse - * @property {HmacKeyResource} 0 The HMAC key resource. + * @property {HmacKey} 0 The HmacKey instance created from API response. + * @property {string} 1 The HMAC key's secret used to access the XML API. + * @property {object} 3 The raw API response. */ /** * @callback CreateHmacKeyCallback Callback function. * @param {?Error} err Request error, if any. - * @param {HmacKeyResource} resource An object containing the HMAC key's secret and the HmacKey object. + * @param {HmacKey} hmacKey The HmacKey instance created from API response. + * @param {string} secret The HMAC key's secret used to access the XML API. * @param {object} apiResponse The raw API response. */ /** @@ -693,9 +687,8 @@ export class Storage extends Service { * const serviceAccountEmail = * 'my-service-account@appspot.gserviceaccount.com'; * - * storage.createHmacKey(serviceAccountEmail, function(err, hmacKeyResource) { + * storage.createHmacKey(serviceAccountEmail, function(err, hmacKey, secret) { * if (!err) { - * const secret = hmacKey.secret; * // Securely store the secret for use with the XML API. * } * }); @@ -705,8 +698,8 @@ export class Storage extends Service { * //- * storage.createHmacKey(serviceAccountEmail) * .then((response) => { - * const [hmacKeyResource] = response; - * const secret = hmacKeyResource.secret; + * const hmacKey = response[0]; + * const secret = response[1]; * // Securely store the secret for use with the XML API. * }); */ @@ -735,19 +728,14 @@ export class Storage extends Service { }, (err, resp: HmacKeyResourceResponse) => { if (err) { - callback!(err, null, resp); + callback!(err, null, null, resp); return; } const hmacKey = new HmacKey(this, resp.metadata.accessId); hmacKey.metadata = resp.metadata; - const resource = { - hmacKey, - secret: resp.secret, - }; - - callback!(null, resource, resp); + callback!(null, hmacKey, resp.secret, resp); } ); } diff --git a/test/index.ts b/test/index.ts index 3fee7d081..c43933953 100644 --- a/test/index.ts +++ b/test/index.ts @@ -31,7 +31,7 @@ import {Bucket} from '../src'; import {GetFilesOptions} from '../src/bucket'; import sinon = require('sinon'); import {HmacKey} from '../src/hmacKey'; -import {HmacKeyResource, HmacKeyResourceResponse} from '../src/storage'; +import {HmacKeyResourceResponse} from '../src/storage'; class FakeChannel { calledWith_: Array<{}>; @@ -269,15 +269,13 @@ describe('Storage', () => { storage.createHmacKey( SERVICE_ACCOUNT_EMAIL, - (err: Error, resource: HmacKeyResource) => { + (err: Error, hmacKey: HmacKey, secret: string) => { assert.ifError(err); - assert.strictEqual(resource.secret, response.secret); - // tslint:disable-next-line: no-any - assert.strictEqual((resource as any).metadata, undefined); - assert(resource.hmacKey instanceof HmacKey); - assert.strictEqual(resource.hmacKey.metadata, metadataResponse); + assert.strictEqual(secret, response.secret); + assert(hmacKey instanceof HmacKey); + assert.strictEqual(hmacKey.metadata, metadataResponse); assert.strictEqual( - resource.hmacKey.accessId, + hmacKey.accessId, metadataResponse.accessId ); done(); @@ -294,7 +292,8 @@ describe('Storage', () => { SERVICE_ACCOUNT_EMAIL, ( err: Error, - resource: HmacKeyResource, + _hmacKey: HmacKey, + _secret: string, apiResponse: HmacKeyResourceResponse ) => { assert.ifError(err); From a9aaac15f43a5712ca1cae0d8f037400feaa5f15 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Mon, 8 Jul 2019 14:00:36 -0700 Subject: [PATCH 15/44] npm run fix --- test/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/index.ts b/test/index.ts index c43933953..5ce8b7770 100644 --- a/test/index.ts +++ b/test/index.ts @@ -274,10 +274,7 @@ describe('Storage', () => { assert.strictEqual(secret, response.secret); assert(hmacKey instanceof HmacKey); assert.strictEqual(hmacKey.metadata, metadataResponse); - assert.strictEqual( - hmacKey.accessId, - metadataResponse.accessId - ); + assert.strictEqual(hmacKey.accessId, metadataResponse.accessId); done(); } ); From d090e389a0a807c35e6163bc1f2e82e9dd593e99 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 9 Jul 2019 11:09:49 -0700 Subject: [PATCH 16/44] fix docs --- src/hmacKey.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index df17664da..be1a31a9e 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -145,7 +145,7 @@ export class HmacKey extends ServiceObject { delete: true, /** * @typedef {object} GetHmacKeyMetadataOptions - * @property {string} userProject This parameter is currently ignored. + * @property {string} [userProject] This parameter is currently ignored. */ /** * Retrieves and populate an HMAC key's metadata, and return @@ -190,13 +190,24 @@ export class HmacKey extends ServiceObject { * }); */ getMetadata: true, + /** + * @callback GetHmacKeyCallback + * @param {?Error} err Request error, if any. + * @param {HmacKey} hmacKey this {@link HmacKey} instance. + * @param {object} apiResponse The full API response. + */ + /** + * @typedef {array} GetHmacKeyResponse + * @property {HmacKey} 0 This {@link HmacKey} instance. + * @property {object} 1 The full API response. + */ /** * @typedef {object} GetHmacKeyOptions - * @property {string} userProject This parameter is currently ignored. + * @property {string} [userProject] This parameter is currently ignored. */ /** * Retrieves and populate an HMAC key's metadata, and return - * the HMAC key object. + * this {@link HmacKey} instance. * * HmacKey.get() does not give the HMAC key secret, as * it is only returned on creation. @@ -269,7 +280,7 @@ export class HmacKey extends ServiceObject { */ /** * @typedef {object} UpdateHmacKeyOptions - * @property {string} userProject This parameter is currently ignored. + * @property {string} [userProject] This parameter is currently ignored. */ /** * @callback HmacKeyMetadataCallback From cc59c289b7f845f285180efdf2f71d8298f7e4ef Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 9 Jul 2019 11:12:39 -0700 Subject: [PATCH 17/44] fix dead link --- src/storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage.ts b/src/storage.ts index 1aa1812c3..48b55f606 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -672,7 +672,7 @@ export class Storage extends Service { * Create an HMAC key associated with an service account to authenticate * requests to the Cloud Storage XML API. * - * @see [HMAC keys documentation]{@linkhttps://cloud.google.com/storage/docs/authentication/hmackeys} + * @see [HMAC keys documentation]{@link https://cloud.google.com/storage/docs/authentication/hmackeys} * * @param {string} serviceAccountEmail The service account's email address * with which the HMAC key is created for. From 9c02842fb24defeedd1944b78083900dfbf2f8ac Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 9 Jul 2019 11:13:17 -0700 Subject: [PATCH 18/44] add link --- src/hmacKey.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index be1a31a9e..4ecad5526 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -58,6 +58,8 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; * service account through the {@link Storage} client using * {@link Storage#createHmacKey}. * + * @see [HMAC keys documentation]{@link https://cloud.google.com/storage/docs/authentication/hmackeys} + * * @class */ export class HmacKey extends ServiceObject { From d6888b234df36941d8e5f9e5ee9e089cdc9ddf5d Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 11 Jul 2019 17:41:45 -0700 Subject: [PATCH 19/44] override setMetadata to send PUT request --- src/hmacKey.ts | 240 +++++++++++++++++++----------------------------- src/index.ts | 5 +- test/hmacKey.ts | 37 +++----- 3 files changed, 113 insertions(+), 169 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 4ecad5526..80a561aaa 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -31,22 +31,18 @@ export interface HmacKeyMetadata { updated?: string; } -export interface UpdateHmacKeyOptions { +export interface SetHmacKeyMetadataOptions { /** * This parameter is currently ignored. */ userProject?: string; } -export interface UpdateHmacKeyMetadata { +export interface SetHmacKeyMetadata { state?: 'ACTIVE' | 'INACTIVE'; etag?: string; } -export interface ToggleHmacKeyOptions { - etag?: string; -} - export interface HmacKeyMetadataCallback { (err: Error | null, metadata?: HmacKeyMetadata, apiResponse?: Metadata): void; } @@ -119,7 +115,7 @@ export class HmacKey extends ServiceObject { * // Delete HMAC key after making the key inactive. * //- * const hmacKey = storage.hmacKey('ACCESS_ID'); - * hmacKey.update({state: 'INACTIVE'}, (err, hmacKeyMetadata) => { + * hmacKey.setMetadata({state: 'INACTIVE'}, (err, hmacKeyMetadata) => { * if (err) { * // The request was an error. * console.error(err); @@ -139,12 +135,69 @@ export class HmacKey extends ServiceObject { * //- * const hmacKey = storage.hmacKey('ACCESS_ID'); * hmacKey - * .update({state: 'INACTIVE'}) + * .setMetadata({state: 'INACTIVE'}) * .then(() => { * return hmacKey.delete(); * }); */ delete: true, + /** + * @callback GetHmacKeyCallback + * @param {?Error} err Request error, if any. + * @param {HmacKey} hmacKey this {@link HmacKey} instance. + * @param {object} apiResponse The full API response. + */ + /** + * @typedef {array} GetHmacKeyResponse + * @property {HmacKey} 0 This {@link HmacKey} instance. + * @property {object} 1 The full API response. + */ + /** + * @typedef {object} GetHmacKeyOptions + * @property {string} [userProject] This parameter is currently ignored. + */ + /** + * Retrieves and populate an HMAC key's metadata, and return + * this {@link HmacKey} instance. + * + * HmacKey.get() does not give the HMAC key secret, as + * it is only returned on creation. + * + * The authenticated user must have `storage.hmacKeys.get` permission + * for the project in which the key exists. + * + * @method HmacKey#get + * @param {GetHmacKeyOptions} [options] Configuration options. + * @param {GetHmacKeyCallback} [callback] Callback function. + * @returns {Promise} + * + * @example + * const {Storage} = require('@google-cloud/storage'); + * const storage = new Storage(); + * + * //- + * // Get the HmacKey's Metadata. + * //- + * storage.hmacKey('ACCESS_ID') + * .get((err, hmacKey) => { + * if (err) { + * // The request was an error. + * console.error(err); + * return; + * } + * // do something with the returned HmacKey object. + * }); + * + * //- + * // If the callback is omitted, a promise is returned. + * //- + * storage.hmacKey('ACCESS_ID') + * .get() + * .then((data) => { + * const hmacKey = data[0]; + * }); + */ + get: true, /** * @typedef {object} GetHmacKeyMetadataOptions * @property {string} [userProject] This parameter is currently ignored. @@ -193,62 +246,69 @@ export class HmacKey extends ServiceObject { */ getMetadata: true, /** - * @callback GetHmacKeyCallback + * @typedef {object} SetHmacKeyMetadata Subset of {@link HmacKeyMetadata} to update. + * @property {string} state New state of the HmacKey. Either 'ACTIVE' or 'INACTIVE'. + * @property {string} [etag] Include an etag from a previous get HMAC key request + * to perform safe read-modify-write. + */ + /** + * @typedef {object} SetHmacKeyMetadataOptions + * @property {string} [userProject] This parameter is currently ignored. + */ + /** + * @callback HmacKeyMetadataCallback * @param {?Error} err Request error, if any. - * @param {HmacKey} hmacKey this {@link HmacKey} instance. + * @param {HmacKeyMetadata} metadata The updated {@link HmacKeyMetadata} object. * @param {object} apiResponse The full API response. */ /** - * @typedef {array} GetHmacKeyResponse - * @property {HmacKey} 0 This {@link HmacKey} instance. + * @typedef {array} HmacKeyMetadataResponse + * @property {HmacKeyMetadata} 0 The updated {@link HmacKeyMetadata} object. * @property {object} 1 The full API response. */ /** - * @typedef {object} GetHmacKeyOptions - * @property {string} [userProject] This parameter is currently ignored. - */ - /** - * Retrieves and populate an HMAC key's metadata, and return - * this {@link HmacKey} instance. - * - * HmacKey.get() does not give the HMAC key secret, as - * it is only returned on creation. + * Updates the state of an HMAC key. See {@link SetHmacKeyMetadata} for + * valid states. * - * The authenticated user must have `storage.hmacKeys.get` permission - * for the project in which the key exists. - * - * @method HmacKey#get - * @param {GetHmacKeyOptions} [options] Configuration options. - * @param {GetHmacKeyCallback} [callback] Callback function. - * @returns {Promise} + * @method HmacKey#setMetadata + * @param {SetHmacKeyMetadata} metadata The new metadata. + * @param {SetHmacKeyMetadataOptions} [options] Configuration options. + * @param {HmacKeyMetadataCallback} [callback] Callback function. + * @returns {Promise} * * @example * const {Storage} = require('@google-cloud/storage'); * const storage = new Storage(); * - * //- - * // Get the HmacKey's Metadata. - * //- + * const metadata = { + * state: 'INACTIVE', + * }; + * * storage.hmacKey('ACCESS_ID') - * .get((err, hmacKey) => { + * .setMetadata(metadata, (err, hmacKeyMetadata) => { * if (err) { * // The request was an error. * console.error(err); * return; * } - * // do something with the returned HmacKey object. + * console.log(hmacKeyMetadata); * }); * * //- * // If the callback is omitted, a promise is returned. * //- * storage.hmacKey('ACCESS_ID') - * .get() + * .setMetadata(metadata) * .then((data) => { - * const hmacKey = data[0]; + * const hmacKeyMetadata = data[0]; + * console.log(hmacKeyMetadata); * }); */ - get: true, + setMetadata: { + reqOpts: { + method: 'PUT', + }, + } }; super({ @@ -260,114 +320,6 @@ export class HmacKey extends ServiceObject { this.accessId = accessId; } - - update( - metadata: UpdateHmacKeyMetadata, - options?: UpdateHmacKeyOptions - ): Promise; - update( - metadata: UpdateHmacKeyMetadata, - callback: HmacKeyMetadataCallback - ): void; - update( - metadata: UpdateHmacKeyMetadata, - options: UpdateHmacKeyOptions, - callback: HmacKeyMetadataCallback - ): void; - /** - * @typedef {object} UpdateHmacKeyMetadata Subset of {@link HmacKeyMetadata} to update. - * @property {string} state New state of the HmacKey. Either 'ACTIVE' or 'INACTIVE'. - * @property {string} [etag] Include an etag from a previous get HMAC key request - * to perform safe read-modify-write. - */ - /** - * @typedef {object} UpdateHmacKeyOptions - * @property {string} [userProject] This parameter is currently ignored. - */ - /** - * @callback HmacKeyMetadataCallback - * @param {?Error} err Request error, if any. - * @param {HmacKeyMetadata} metadata The updated {@link HmacKeyMetadata} object. - * @param {object} apiResponse The full API response. - */ - /** - * @typedef {array} HmacKeyMetadataResponse - * @property {HmacKeyMetadata} 0 The updated {@link HmacKeyMetadata} object. - * @property {object} 1 The full API response. - */ - /** - * Updates the state of an HMAC key. See {@link UpdateHmacKeyMetadata} for - * valid states. - * - * @param {UpdateHmacKeyMetadata} metadata The new metadata. - * @param {UpdateHmacKeyOptions} [options] Configuration options. - * @param {HmacKeyMetadataCallback} [callback] Callback function. - * @returns {Promise} - * - * @example - * const {Storage} = require('@google-cloud/storage'); - * const storage = new Storage(); - * - * const metadata = { - * state: 'INACTIVE', - * }; - * - * storage.hmacKey('ACCESS_ID') - * .update(metadata, (err, hmacKeyMetadata) => { - * if (err) { - * // The request was an error. - * console.error(err); - * return; - * } - * console.log(hmacKeyMetadata); - * }); - * - * //- - * // If the callback is omitted, a promise is returned. - * //- - * storage.hmacKey('ACCESS_ID') - * .update(metadata) - * .then((data) => { - * const hmacKeyMetadata = data[0]; - * console.log(hmacKeyMetadata); - * }); - */ - update( - metadata: UpdateHmacKeyMetadata, - optionsOrCb?: UpdateHmacKeyOptions | HmacKeyMetadataCallback, - cb?: HmacKeyMetadataCallback - ): Promise | void { - if ( - typeof metadata !== 'object' || - Object.getOwnPropertyNames(metadata).length === 0 - ) { - throw new Error( - 'Cannot update HmacKey with an undefined/empty options object.' - ); - } - - const {options, callback} = normalize< - UpdateHmacKeyOptions, - HmacKeyMetadataCallback - >(optionsOrCb, cb); - - this.request( - { - uri: '/', - method: 'put', - qs: options, - json: metadata, - }, - (err: Error | null, metadata?: HmacKeyMetadata, res?: Metadata) => { - if (err) { - callback!(err); - return; - } - this.metadata = metadata!; - callback!(err, metadata, res); - } - ); - } } /*! Developer Documentation diff --git a/src/index.ts b/src/index.ts index a910b51c1..57a77ea0b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -186,9 +186,8 @@ export { HmacKeyMetadata, HmacKeyMetadataCallback, HmacKeyMetadataResponse, - ToggleHmacKeyOptions, - UpdateHmacKeyMetadata, - UpdateHmacKeyOptions, + SetHmacKeyMetadata, + SetHmacKeyMetadataOptions, } from './hmacKey'; export { GetPolicyCallback, diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 92cd4b0f2..0879b68f6 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -99,8 +99,13 @@ describe('HmacKey', () => { assert(ctorArg.id, ACCESS_ID); assert.deepStrictEqual(ctorArg.methods, { delete: true, - getMetadata: true, get: true, + getMetadata: true, + setMetadata: { + reqOpts: { + method: 'PUT', + }, + } }); }); @@ -198,21 +203,9 @@ describe('HmacKey', () => { }); }); - describe('update', () => { - it('should throw without metadata object', () => { - assert.throws(() => { - hmacKey.update(undefined, assert.ifError); - }, /Cannot update HmacKey/); - }); - - it('should throw with an empty metadata object', () => { - assert.throws(() => { - hmacKey.update({}, assert.ifError); - }, /Cannot update HmacKey/); - }); - - it('should accept an options object', () => { - hmacKey.update({state: 'INACTIVE'}, {}, assert.ifError); + describe('setMetadata', () => { + it('should accept an options object', async () => { + await hmacKey.setMetadata({state: 'INACTIVE'}, {}); }); it('should execute callback with request error', done => { @@ -221,14 +214,14 @@ describe('HmacKey', () => { callback(error); }); - hmacKey.update({state: 'INACTIVE'}, (err: Error) => { + hmacKey.setMetadata({state: 'INACTIVE'}, (err: Error) => { assert.strictEqual(err, error); done(); }); }); it('should return a Promise when callback is omitted', () => { - const promise = hmacKey.update({state: 'INACTIVE'}); + const promise = hmacKey.setMetadata({state: 'INACTIVE'}); assert(promise instanceof Promise); return promise; }); @@ -242,10 +235,10 @@ describe('HmacKey', () => { userProject: 'my-project', }; - await hmacKey.update(newMetadata, options); + await hmacKey.setMetadata(newMetadata, options); const requestArg = storageRequestStub.firstCall.args[0]; - assert.deepStrictEqual(requestArg.method, 'put'); + assert.deepStrictEqual(requestArg.method, 'PUT'); assert.deepStrictEqual(requestArg.qs, options); assert.deepStrictEqual(requestArg.json, newMetadata); }); @@ -255,7 +248,7 @@ describe('HmacKey', () => { state: 'INACTIVE', etag: 'some-etag', }; - const [metadata] = await hmacKey.update(newMetadata); + const [metadata] = await hmacKey.setMetadata(newMetadata); const expectedMetadata = Object.assign( {}, @@ -267,7 +260,7 @@ describe('HmacKey', () => { }); it('should assign the response metadata to the HmacKey instance', async () => { - await hmacKey.update({state: 'ACTIVE'}); + await hmacKey.setMetadata({state: 'ACTIVE'}); assert.deepStrictEqual(hmacKey.metadata, metadataResponse); }); }); From 2fef2defdceb0e507be00021f5527382b50ce0e6 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 11 Jul 2019 17:42:26 -0700 Subject: [PATCH 20/44] npm run fix --- src/hmacKey.ts | 2 +- test/hmacKey.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 80a561aaa..5ed464e65 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -308,7 +308,7 @@ export class HmacKey extends ServiceObject { reqOpts: { method: 'PUT', }, - } + }, }; super({ diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 0879b68f6..b6c42b0c1 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -105,7 +105,7 @@ describe('HmacKey', () => { reqOpts: { method: 'PUT', }, - } + }, }); }); From 77630a3ad2cfa822de4471125fca10b3496ce4bc Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Fri, 12 Jul 2019 12:48:28 -0700 Subject: [PATCH 21/44] remove unnecessary tests --- test/hmacKey.ts | 196 ------------------------------------------------ 1 file changed, 196 deletions(-) diff --git a/test/hmacKey.ts b/test/hmacKey.ts index b6c42b0c1..a6f1ccd2e 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -18,8 +18,6 @@ import * as sinon from 'sinon'; import * as proxyquire from 'proxyquire'; import * as assert from 'assert'; import {util, ServiceObject} from '@google-cloud/common'; -import {HmacKey} from '../src/hmacKey'; -import {Storage} from '../src'; // tslint:disable-next-line: no-any let sandbox: sinon.SinonSandbox; @@ -32,16 +30,6 @@ const ACCESS_ID = 'fake-access-id'; const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; const PROJECT_ID = 'project-id'; -const metadataResponse = { - accessId: ACCESS_ID, - etag: 'etag', - id: ACCESS_ID, - projectId: PROJECT_ID, - serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, - state: 'ACTIVE', - timeCreated: '20190101T00:00:00Z', - updated: '20190101T00:00:00Z', -}; describe('HmacKey', () => { beforeEach(() => { @@ -115,188 +103,4 @@ describe('HmacKey', () => { }, /access ID is needed/); }); }); - - describe('methods', () => { - // tslint:disable-next-line: no-any - let storageRequestStub: sinon.SinonStub; - - beforeEach(() => { - const STORAGE = new Storage(); - storageRequestStub = sandbox - .stub(STORAGE, 'request') - .callsFake((_opts: {}, callback: Function) => { - callback(null, metadataResponse); - }); - - hmacKey = new HmacKey(STORAGE, ACCESS_ID); - }); - - describe('get', () => { - it('should accept just a callback', done => { - hmacKey.get(done); - }); - - it('should accept an options object and callback', done => { - hmacKey.get({userProject: 'my-project'}, done); - }); - - it('should execute callback with request error', done => { - const error = new Error('Request error'); - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(error); - }); - - hmacKey.get({}, (err: Error) => { - assert.strictEqual(err, error); - done(); - }); - }); - - it('should return a Promise when callback is omitted', async () => { - const promise = hmacKey.get(); - assert(promise instanceof Promise); - const res = await promise; - assert(Array.isArray(res)); - assert(res[0] instanceof HmacKey); - }); - - it('should resolve with the HMAC key and assign HmacKey.metadata', async () => { - const [hmacKey2] = await hmacKey.get(); - - assert.strictEqual(hmacKey2, hmacKey); - assert.deepStrictEqual(hmacKey2.metadata, metadataResponse); - }); - }); - - describe('getMetadata', () => { - it('should accept just a callback', done => { - hmacKey.getMetadata(done); - }); - - it('should accept an options object and callback', done => { - hmacKey.getMetadata({userProject: 'my-project'}, done); - }); - - it('should execute callback with request error', done => { - const error = new Error('Request error'); - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(error); - }); - - hmacKey.getMetadata({}, (err: Error) => { - assert.strictEqual(err, error); - done(); - }); - }); - - it('should return a Promise when callback is omitted', () => { - const promise = hmacKey.getMetadata(); - assert(promise instanceof Promise); - return promise; - }); - - it('should resolve with the HMAC keys metadata and assign to instance', async () => { - const [metadata] = await hmacKey.getMetadata(); - - assert.deepStrictEqual(metadata, metadataResponse); - assert.deepStrictEqual(hmacKey.metadata, metadataResponse); - }); - }); - - describe('setMetadata', () => { - it('should accept an options object', async () => { - await hmacKey.setMetadata({state: 'INACTIVE'}, {}); - }); - - it('should execute callback with request error', done => { - const error = new Error('Request error'); - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(error); - }); - - hmacKey.setMetadata({state: 'INACTIVE'}, (err: Error) => { - assert.strictEqual(err, error); - done(); - }); - }); - - it('should return a Promise when callback is omitted', () => { - const promise = hmacKey.setMetadata({state: 'INACTIVE'}); - assert(promise instanceof Promise); - return promise; - }); - - it('should make a request passing metadata arg as body', async () => { - const newMetadata = { - state: 'INACTIVE', - etag: 'some-etag', - }; - const options = { - userProject: 'my-project', - }; - - await hmacKey.setMetadata(newMetadata, options); - - const requestArg = storageRequestStub.firstCall.args[0]; - assert.deepStrictEqual(requestArg.method, 'PUT'); - assert.deepStrictEqual(requestArg.qs, options); - assert.deepStrictEqual(requestArg.json, newMetadata); - }); - - it('should resolve with the HMAC keys metadata and assign to instance', async () => { - const newMetadata = { - state: 'INACTIVE', - etag: 'some-etag', - }; - const [metadata] = await hmacKey.setMetadata(newMetadata); - - const expectedMetadata = Object.assign( - {}, - newMetadata, - metadataResponse - ); - assert.deepStrictEqual(metadata, expectedMetadata); - assert.deepStrictEqual(hmacKey.metadata, expectedMetadata); - }); - - it('should assign the response metadata to the HmacKey instance', async () => { - await hmacKey.setMetadata({state: 'ACTIVE'}); - assert.deepStrictEqual(hmacKey.metadata, metadataResponse); - }); - }); - - describe('delete', () => { - it('should accept just a callback', done => { - hmacKey.delete((err: Error) => { - assert.ifError(err); - - const requestArg = storageRequestStub.firstCall.args[0]; - assert.deepStrictEqual(requestArg.method, 'DELETE'); - done(); - }); - }); - - it('should accept an options object and callback', done => { - hmacKey.delete({userProject: 'my-project'}, done); - }); - - it('should return a Promise when callback is omitted', () => { - const promise = hmacKey.delete(); - assert(promise instanceof Promise); - return promise; - }); - - it('should execute callback with request error', done => { - const error = new Error('Request error'); - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(error); - }); - - hmacKey.delete((err: Error) => { - assert.strictEqual(err, error); - done(); - }); - }); - }); - }); }); From 09a61a60872a6e45c32410fed52daaa904beb504 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Fri, 12 Jul 2019 13:26:30 -0700 Subject: [PATCH 22/44] require @google-cloud/2.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5e24fcd0..b0e08df55 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "predocs-test": "npm run docs" }, "dependencies": { - "@google-cloud/common": "^2.0.0", + "@google-cloud/common": "^2.0.4", "@google-cloud/paginator": "^1.0.0", "@google-cloud/promisify": "^1.0.0", "arrify": "^2.0.0", From 7c6d7ab473a55a5f791b5537813a835210cb13ce Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 13:26:58 -0700 Subject: [PATCH 23/44] move accessId argument check to Storage#hmacKey() --- src/hmacKey.ts | 4 ---- src/storage.ts | 4 ++++ test/hmacKey.ts | 6 ------ test/index.ts | 8 ++++++++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 5ed464e65..f47d4d19b 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -77,10 +77,6 @@ export class HmacKey extends ServiceObject { * const hmacKey = storage.hmacKey('access-id'); */ constructor(storage: Storage, accessId: string) { - if (!accessId) { - throw new Error('An access ID is needed to create an HmacKey object.'); - } - const methods = { /** * @typedef {object} DeleteHmacKeyOptions diff --git a/src/storage.ts b/src/storage.ts index 48b55f606..e711b5570 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1068,6 +1068,10 @@ export class Storage extends Service { * const hmacKey = storage.hmacKey('ACCESS_ID'); */ hmacKey(accessId: string) { + if (!accessId) { + throw new Error('An access ID is needed to create an HmacKey object.'); + } + return new HmacKey(this, accessId); } } diff --git a/test/hmacKey.ts b/test/hmacKey.ts index a6f1ccd2e..0434f313f 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -96,11 +96,5 @@ describe('HmacKey', () => { }, }); }); - - it('should throw if accessId is not provided', () => { - assert.throws(() => { - const _hmacKey = new HmacKey(STORAGE); - }, /access ID is needed/); - }); }); }); diff --git a/test/index.ts b/test/index.ts index 5ce8b7770..72c2baa26 100644 --- a/test/index.ts +++ b/test/index.ts @@ -192,6 +192,14 @@ describe('Storage', () => { }); }); + describe('hmacKey', () => { + it('should throw if accessId is not provided', () => { + assert.throws(() => { + storage.hmacKey(); + }, /access ID is needed/); + }); + }); + describe('createHmacKey', () => { const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; const ACCESS_ID = 'some-access-id'; From 893324804734d7db73a2fd8544b9391f229c65f2 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 13:37:15 -0700 Subject: [PATCH 24/44] remove assertion that test the HmacKey class --- test/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 72c2baa26..493141c25 100644 --- a/test/index.ts +++ b/test/index.ts @@ -282,7 +282,6 @@ describe('Storage', () => { assert.strictEqual(secret, response.secret); assert(hmacKey instanceof HmacKey); assert.strictEqual(hmacKey.metadata, metadataResponse); - assert.strictEqual(hmacKey.accessId, metadataResponse.accessId); done(); } ); From 536c8aa71390e11272bfedb8ffc79cc45d465f0b Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 13:52:47 -0700 Subject: [PATCH 25/44] change Storage#createHmacKey option test to be arbitrary object and test user object is not modified --- test/index.ts | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/test/index.ts b/test/index.ts index 493141c25..43e0d8848 100644 --- a/test/index.ts +++ b/test/index.ts @@ -217,6 +217,9 @@ describe('Storage', () => { secret: 'my-secret', metadata: metadataResponse, }; + const OPTIONS = { + some: 'value', + }; it('should make correct API request', done => { storage.request = ( @@ -256,18 +259,30 @@ describe('Storage', () => { ); }); - it('should honor the userProject option', async () => { - const options = { - userProject: 'my-project', - }; - + it('should make request with method options as query parameter', async () => { storage.request = sinon .stub() .returns((_reqOpts: {}, callback: Function) => callback()); - await storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, options); + await storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, OPTIONS); const reqArg = storage.request.firstCall.args[0]; - assert.strictEqual(reqArg.qs.userProject, options.userProject); + assert.deepStrictEqual(reqArg.qs, { + serviceAccountEmail: SERVICE_ACCOUNT_EMAIL, + ...OPTIONS, + }); + }); + + it('should not modify the options object', done => { + storage.request = (_reqOpts: {}, callback: Function) => { + callback(null, response); + }; + const originalOptions = Object.assign({}, OPTIONS); + + storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, OPTIONS, (err: Error) => { + assert.ifError(err); + assert.deepStrictEqual(OPTIONS, originalOptions); + done(); + }); }); it('should invoke callback with a secret and an HmacKey instance', done => { From 5e44f08763b9be1e9e740797f0be9b84d02b7022 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 13:54:03 -0700 Subject: [PATCH 26/44] combine return HmacKey object --- test/index.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/test/index.ts b/test/index.ts index 43e0d8848..b824dc08f 100644 --- a/test/index.ts +++ b/test/index.ts @@ -761,18 +761,6 @@ describe('Storage', () => { ); }); - it('should return HmacKey objects', done => { - storageRequestStub.callsFake((_opts: {}, callback: Function) => { - callback(null, {items: [metadataResponse]}); - }); - - storage.getHmacKeys((err: Error, hmacKeys: HmacKey[]) => { - assert.ifError(err); - assert(hmacKeys![0] instanceof HmacKey); - done(); - }); - }); - it('should return apiResponse', done => { const resp = {items: [metadataResponse]}; storageRequestStub.callsFake((_opts: {}, callback: Function) => { @@ -795,6 +783,7 @@ describe('Storage', () => { storage.getHmacKeys((err: Error, hmacKeys: HmacKey[]) => { assert.ifError(err); + assert(hmacKeys![0] instanceof HmacKey); assert.strictEqual(hmacKeys[0].accessId, metadataResponse.accessId); assert.deepStrictEqual(hmacKeys[0].metadata, metadataResponse); done(); From 355b731e3e1e05a99be2eb79ae0a07b920d56650 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 14:08:27 -0700 Subject: [PATCH 27/44] Storage#createHmacKey assert apiResponse is sent on Error --- test/index.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/index.ts b/test/index.ts index b824dc08f..b9e0e3d96 100644 --- a/test/index.ts +++ b/test/index.ts @@ -324,14 +324,22 @@ describe('Storage', () => { it('should execute callback with request error', done => { const error = new Error('Request error'); + const response = {success: false}; storage.request = (_reqOpts: {}, callback: Function) => { - callback(error); + callback(error, response); }; - storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, (err: Error) => { - assert.strictEqual(err, error); - done(); - }); + storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, + ( + err: Error, + _hmacKey: HmacKey, + _secret: string, + apiResponse: {}, + ) => { + assert.strictEqual(err, error); + assert.strictEqual(apiResponse, response) + done(); + }); }); }); From 8d74689606c5b595382cad5fd2856b7851960a31 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 14:09:44 -0700 Subject: [PATCH 28/44] style --- test/index.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/index.ts b/test/index.ts index b9e0e3d96..7d73c1f98 100644 --- a/test/index.ts +++ b/test/index.ts @@ -329,17 +329,14 @@ describe('Storage', () => { callback(error, response); }; - storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, - ( - err: Error, - _hmacKey: HmacKey, - _secret: string, - apiResponse: {}, - ) => { + storage.createHmacKey( + SERVICE_ACCOUNT_EMAIL, + (err: Error, _hmacKey: HmacKey, _secret: string, apiResponse: {}) => { assert.strictEqual(err, error); - assert.strictEqual(apiResponse, response) + assert.strictEqual(apiResponse, response); done(); - }); + } + ); }); }); From c5a475204862f8a407359ba3288244a907c0f120 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 14:47:12 -0700 Subject: [PATCH 29/44] do not set accessId property of HmacKey instance --- src/hmacKey.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index f47d4d19b..7f1f2559c 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -313,8 +313,6 @@ export class HmacKey extends ServiceObject { id: accessId, methods, }); - - this.accessId = accessId; } } From 6d16dcffc9e01338d2f36ea21b2429f32f8bda2c Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 14:50:17 -0700 Subject: [PATCH 30/44] getHmacKeys test user query is preserved in nextQuery --- src/hmacKey.ts | 1 - test/hmacKey.ts | 4 ---- test/index.ts | 13 ++++++++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 7f1f2559c..27b404c7e 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -59,7 +59,6 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; * @class */ export class HmacKey extends ServiceObject { - accessId: string; metadata: HmacKeyMetadata | undefined; /** diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 0434f313f..5d3ccc16f 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -72,10 +72,6 @@ describe('HmacKey', () => { assert(promisifyAllStub.calledOnce); }); - it('should assign accessId', () => { - assert.strictEqual(hmacKey.accessId, ACCESS_ID); - }); - it('should assign Storage instance', () => { assert.strictEqual(hmacKey.parent, STORAGE); }); diff --git a/test/index.ts b/test/index.ts index 7d73c1f98..d9a44c7d8 100644 --- a/test/index.ts +++ b/test/index.ts @@ -735,17 +735,21 @@ describe('Storage', () => { it('should return nextQuery if more results exist', done => { const token = 'next-page-token'; + const query = { + param1: 'a', + param2: 'b', + }; + const expectedNextQuery = Object.assign({}, query, {pageToken: token}); storageRequestStub.callsFake((_opts: {}, callback: Function) => { callback(null, {nextPageToken: token, items: []}); }); storage.getHmacKeys( - {maxResults: 5}, + query, // tslint:disable-next-line: no-any (err: Error, _hmacKeys: [], nextQuery: any) => { assert.ifError(err); - assert.strictEqual(nextQuery.pageToken, token); - assert.strictEqual(nextQuery.maxResults, 5); + assert.deepStrictEqual(nextQuery, expectedNextQuery); done(); } ); @@ -757,7 +761,7 @@ describe('Storage', () => { }); storage.getHmacKeys( - {maxResults: 5}, + {}, (err: Error, _hmacKeys: [], nextQuery: {}) => { assert.ifError(err); assert.strictEqual(nextQuery, null); @@ -789,7 +793,6 @@ describe('Storage', () => { storage.getHmacKeys((err: Error, hmacKeys: HmacKey[]) => { assert.ifError(err); assert(hmacKeys![0] instanceof HmacKey); - assert.strictEqual(hmacKeys[0].accessId, metadataResponse.accessId); assert.deepStrictEqual(hmacKeys[0].metadata, metadataResponse); done(); }); From 70a03f0778e4f05e7decf6693a676f6e95f45173 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 14:51:56 -0700 Subject: [PATCH 31/44] style fixes --- test/index.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/index.ts b/test/index.ts index d9a44c7d8..f4e1d803e 100644 --- a/test/index.ts +++ b/test/index.ts @@ -760,14 +760,11 @@ describe('Storage', () => { callback(null, {items: []}); }); - storage.getHmacKeys( - {}, - (err: Error, _hmacKeys: [], nextQuery: {}) => { - assert.ifError(err); - assert.strictEqual(nextQuery, null); - done(); - } - ); + storage.getHmacKeys({}, (err: Error, _hmacKeys: [], nextQuery: {}) => { + assert.ifError(err); + assert.strictEqual(nextQuery, null); + done(); + }); }); it('should return apiResponse', done => { From cd42d0043c51f56d4188a98de2189ee4f40ac7e8 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 18 Jul 2019 15:18:38 -0700 Subject: [PATCH 32/44] nit: missing ; --- src/hmacKey.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 27b404c7e..e7bc0b247 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -122,7 +122,7 @@ export class HmacKey extends ServiceObject { * return; * } * // The HMAC key is deleted. - * }) + * }); * }); * * //- From fe38200d4e0ba6cdadc6685bf0752a21e11e731d Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 1 Aug 2019 14:38:51 -0700 Subject: [PATCH 33/44] inherited methods are already promisified --- src/hmacKey.ts | 10 ---------- test/hmacKey.ts | 12 ------------ 2 files changed, 22 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index e7bc0b247..530f9f3ad 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -15,10 +15,7 @@ */ import {Metadata, ServiceObject} from '@google-cloud/common'; -import {promisifyAll} from '@google-cloud/promisify'; - import {Storage} from './storage'; -import {normalize} from './util'; export interface HmacKeyMetadata { accessId: string; @@ -314,10 +311,3 @@ export class HmacKey extends ServiceObject { }); } } - -/*! Developer Documentation - * - * All async methods (except for streams) will return a Promise in the event - * that a callback is omitted. - */ -promisifyAll(HmacKey); diff --git a/test/hmacKey.ts b/test/hmacKey.ts index 5d3ccc16f..cb0a47728 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -28,9 +28,6 @@ let hmacKey: any; const ACCESS_ID = 'fake-access-id'; -const SERVICE_ACCOUNT_EMAIL = 'service-account@gserviceaccount.com'; -const PROJECT_ID = 'project-id'; - describe('HmacKey', () => { beforeEach(() => { sandbox = sinon.createSandbox(); @@ -41,7 +38,6 @@ describe('HmacKey', () => { }); describe('initialization', () => { - let promisifyAllStub: sinon.SinonStub; // tslint:disable-next-line: no-any let serviceObjectSpy: sinon.SinonSpy; // tslint:disable-next-line: no-any @@ -50,15 +46,11 @@ describe('HmacKey', () => { let HmacKey: any; beforeEach(() => { - promisifyAllStub = sandbox.stub(); commonModule = {ServiceObject}; serviceObjectSpy = sandbox.spy(commonModule, 'ServiceObject'); HmacKey = proxyquire('../src/hmacKey', { '@google-cloud/common': commonModule, - '@google-cloud/promisify': { - promisifyAll: promisifyAllStub, - }, }).HmacKey; STORAGE = { @@ -68,10 +60,6 @@ describe('HmacKey', () => { hmacKey = new HmacKey(STORAGE, ACCESS_ID); }); - it('should promisify all the things', () => { - assert(promisifyAllStub.calledOnce); - }); - it('should assign Storage instance', () => { assert.strictEqual(hmacKey.parent, STORAGE); }); From ab11feb54ea6e8b5d607d3a3d4d5b66c92168ee7 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 1 Aug 2019 14:44:50 -0700 Subject: [PATCH 34/44] remove test already done in parent --- test/hmacKey.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/hmacKey.ts b/test/hmacKey.ts index cb0a47728..a11daddf3 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -60,10 +60,6 @@ describe('HmacKey', () => { hmacKey = new HmacKey(STORAGE, ACCESS_ID); }); - it('should assign Storage instance', () => { - assert.strictEqual(hmacKey.parent, STORAGE); - }); - it('should inherit from ServiceObject', () => { assert(hmacKey instanceof ServiceObject); const ctorArg = serviceObjectSpy.firstCall.args[0]; From dec9f1e8240df88dfa4f0d5916944eab2d728161 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 1 Aug 2019 14:47:20 -0700 Subject: [PATCH 35/44] fix tests --- test/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/index.ts b/test/index.ts index f4e1d803e..87cc8be78 100644 --- a/test/index.ts +++ b/test/index.ts @@ -113,6 +113,7 @@ describe('Storage', () => { it('should streamify the correct methods', () => { assert.strictEqual(storage.getBucketsStream, 'getBuckets'); + assert.strictEqual(storage.getHmacKeysStream, 'getHmacKeys'); }); it('should promisify all the things', () => { @@ -196,7 +197,7 @@ describe('Storage', () => { it('should throw if accessId is not provided', () => { assert.throws(() => { storage.hmacKey(); - }, /access ID is needed/); + }, /An access ID is needed to create an HmacKey object./); }); }); @@ -245,7 +246,7 @@ describe('Storage', () => { it('should throw without a serviceAccountEmail', () => { assert.throws( () => storage.createHmacKey(), - /must be a service account email/ + /The first argument must be a service account email to create an HMAC key\./, ); }); @@ -255,7 +256,7 @@ describe('Storage', () => { storage.createHmacKey({ userProject: 'my-project', }), - /must be a service account email/ + /The first argument must be a service account email to create an HMAC key\./ ); }); From f12c4d88cfb0152168c0610cbe77d6f4f25c5ba8 Mon Sep 17 00:00:00 2001 From: Frank Natividad Date: Mon, 5 Aug 2019 09:18:58 -0700 Subject: [PATCH 36/44] docs(samples): HMAC SA Admin Samples (#793) * Add wip * Add untested samples * Add wip hmacKey.test.js * wip * WIP sample tests * Add gimme-acc setup * Split samples into separate files * Add fix for 2/6 hmacKey samples * Update with fixed tests * Lint and clean up * Add context of serviceaccount and project * Update samples based on feedback * Address comments * Add samples hmac credentials * Add todo line * Fix typo --- .kokoro/setup-vars.sh | 5 ++ samples/hmacKeyActivate.js | 52 +++++++++++++++ samples/hmacKeyCreate.js | 59 ++++++++++++++++ samples/hmacKeyDeactivate.js | 52 +++++++++++++++ samples/hmacKeyDelete.js | 51 ++++++++++++++ samples/hmacKeyGet.js | 50 ++++++++++++++ samples/hmacKeysList.js | 54 +++++++++++++++ samples/system-test/hmacKey.test.js | 100 ++++++++++++++++++++++++++++ test/index.ts | 2 +- 9 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 samples/hmacKeyActivate.js create mode 100644 samples/hmacKeyCreate.js create mode 100644 samples/hmacKeyDeactivate.js create mode 100644 samples/hmacKeyDelete.js create mode 100644 samples/hmacKeyGet.js create mode 100644 samples/hmacKeysList.js create mode 100644 samples/system-test/hmacKey.test.js diff --git a/.kokoro/setup-vars.sh b/.kokoro/setup-vars.sh index 0455845df..e316b91de 100644 --- a/.kokoro/setup-vars.sh +++ b/.kokoro/setup-vars.sh @@ -22,3 +22,8 @@ export GCN_STORAGE_2ND_PROJECT_KEY=${KOKORO_GFILE_DIR}/no-whitelist-key.json export GOOGLE_CLOUD_KMS_KEY_ASIA="projects/long-door-651/locations/asia/keyRings/test-key-asia/cryptoKeys/test-key-asia" export GOOGLE_CLOUD_KMS_KEY_US="projects/long-door-651/locations/us/keyRings/test-key-us/cryptoKeys/test-key-us" + +# TODO: Switch with service-account pool +export POOL_SAMPLES_PROJECT_ID=long-door-651 +export POOL_SAMPLES_PROJECT_CREDENTIALS="${KOKORO_GFILE_DIR}/storage-hmac-samples-key.json" +export SAMPLES_HMAC_SERVICE_ACCOUNT="storage-key-hmac-samples@long-door-651.iam.gserviceaccount.com" diff --git a/samples/hmacKeyActivate.js b/samples/hmacKeyActivate.js new file mode 100644 index 000000000..b7e71d864 --- /dev/null +++ b/samples/hmacKeyActivate.js @@ -0,0 +1,52 @@ +/** + * Copyright 2019 Google LLC + * + * 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 + * + * https://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'; + +// sample-metadata: +// title: Activate HMAC SA Key. +// description: Activate HMAC SA Key. +// usage: node hmacKeyActivate.js + +function main(hmacKeyAccessId = 'GOOG0234230X00') { + // [START storage_activate_hmac_key] + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage(); + + // Activate HMAC SA Key + async function activateHmacKey() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const hmacKeyAccessId = 'HMAC Access Key Id to update, e.g. GOOG0234230X00'; + + const hmacKey = storage.hmacKey(hmacKeyAccessId); + const [hmacKeyMetadata] = await hmacKey.setMetadata({state: 'ACTIVE'}); + + console.log(`The HMAC key is now active.`); + console.log(`The HMAC key metadata is:`); + for (const [key, value] of Object.entries(hmacKeyMetadata)) { + console.log(`${key}: ${value}`); + } + } + // [END storage_activate_hmac_key] + activateHmacKey(); +} + +main(...process.argv.slice(2)); diff --git a/samples/hmacKeyCreate.js b/samples/hmacKeyCreate.js new file mode 100644 index 000000000..b60e44a49 --- /dev/null +++ b/samples/hmacKeyCreate.js @@ -0,0 +1,59 @@ +/** + * Copyright 2019 Google LLC + * + * 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 + * + * https://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'; + +// sample-metadata: +// title: Create HMAC SA Key. +// description: Create HMAC SA Key. +// usage: node hmacKeyCreate.js + +function main( + projectId = 'serviceAccountProjectId', + credentialsFile = 'serviceAccountCredentials', + serviceAccountEmail = 'service-account@example.com' +) { + // [START storage_create_hmac_key] + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage({ + projectId: projectId, // ProjectId of where to create a new HMAC SA Keys. + keyFilename: credentialsFile, // Credentials to ProjectId + }); + + // Create HMAC SA Key + async function createHmacKey() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const serviceAccountEmail = 'Service Account Email to associate HMAC Key'; + + const [hmacKey, secret] = await storage.createHmacKey(serviceAccountEmail); + + console.log(`The base64 encoded secret is: ${secret}`); + console.log(`Do not miss that secret, there is no API to recover it.`); + console.log(`The HMAC key metadata is:`); + for (const [key, value] of Object.entries(hmacKey.metadata)) { + console.log(`${key}: ${value}`); + } + } + // [END storage_create_hmac_key] + createHmacKey(); +} + +main(...process.argv.slice(2)); diff --git a/samples/hmacKeyDeactivate.js b/samples/hmacKeyDeactivate.js new file mode 100644 index 000000000..e649466fd --- /dev/null +++ b/samples/hmacKeyDeactivate.js @@ -0,0 +1,52 @@ +/** + * Copyright 2019 Google LLC + * + * 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 + * + * https://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'; + +// sample-metadata: +// title: Deactivate HMAC SA Key. +// description: Deactivate HMAC SA Key. +// usage: node hmacKeyDeactivate.js + +function main(hmacKeyAccessId = 'GOOG0234230X00') { + // [START storage_deactivate_hmac_key] + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage(); + + // Deactivate HMAC SA Key + async function deactivateHmacKey() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const hmacKeyAccessId = 'HMAC Access Key Id to update, e.g. GOOG0234230X00'; + + const hmacKey = storage.hmacKey(hmacKeyAccessId); + const [hmacKeyMetadata] = await hmacKey.setMetadata({state: 'INACTIVE'}); + + console.log(`The HMAC key is now inactive.`); + console.log(`The HMAC key metadata is:`); + for (const [key, value] of Object.entries(hmacKeyMetadata)) { + console.log(`${key}: ${value}`); + } + } + // [END storage_deactivate_hmac_key] + deactivateHmacKey(); +} + +main(...process.argv.slice(2)); diff --git a/samples/hmacKeyDelete.js b/samples/hmacKeyDelete.js new file mode 100644 index 000000000..8e6380bd3 --- /dev/null +++ b/samples/hmacKeyDelete.js @@ -0,0 +1,51 @@ +/** + * Copyright 2019 Google LLC + * + * 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 + * + * https://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'; + +// sample-metadata: +// title: Delete HMAC SA Key. +// description: Delete HMAC SA Key. +// usage: node hmacKeyDelete.js + +function main(hmacKeyAccessId = 'GOOG0234230X00') { + // [START storage_delete_hmac_key] + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage(); + + // Delete HMAC SA Key + async function deleteHmacKey() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const hmacKeyAccessId = 'Inactive HMAC Access Key Id to delete, e.g. GOOG0234230X00'; + + const hmacKey = storage.hmacKey(hmacKeyAccessId); + + await hmacKey.delete(); + + console.log( + `The key is deleted, though it may still appear in getHmacKeys() results.` + ); + } + // [END storage_delete_hmac_key] + deleteHmacKey(); +} + +main(...process.argv.slice(2)); diff --git a/samples/hmacKeyGet.js b/samples/hmacKeyGet.js new file mode 100644 index 000000000..13569777f --- /dev/null +++ b/samples/hmacKeyGet.js @@ -0,0 +1,50 @@ +/** + * Copyright 2019 Google LLC + * + * 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 + * + * https://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'; + +// sample-metadata: +// title: Get HMAC SA Key Metadata. +// description: Get HMAC SA Key Metadata. +// usage: node hmacKeyGet.js + +function main(hmacKeyAccessId = 'GOOG0234230X00') { + // [START storage_deactivate_hmac_key] + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage(); + + // Get HMAC SA Key Metadata + async function getHmacKey() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const hmacKeyAccessId = 'HMAC Access Key Id to get, e.g. GOOG0234230X00'; + + const [hmacKey] = await storage.hmacKey(hmacKeyAccessId).get(); + + console.log(`The HMAC key metadata is:`); + for (const [key, value] of Object.entries(hmacKey.metadata)) { + console.log(`${key}: ${value}`); + } + } + // [END storage_get_hmac_key] + getHmacKey(); +} + +main(...process.argv.slice(2)); diff --git a/samples/hmacKeysList.js b/samples/hmacKeysList.js new file mode 100644 index 000000000..0cca622ca --- /dev/null +++ b/samples/hmacKeysList.js @@ -0,0 +1,54 @@ +/** + * Copyright 2019 Google LLC + * + * 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 + * + * https://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'; + +// sample-metadata: +// title: List HMAC SA Keys Metadata. +// description: List HMAC SA Keys Metadata. +// usage: node hmacKeyList.js + +function main( + projectId = 'serviceAccountProjectId', + credentialsFile = 'serviceAccountCredentials' +) { + // [START storage_list_hmac_keys] + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage({ + projectId: projectId, // ProjectId from where to list HMAC SA Keys + keyFilename: credentialsFile, // Credentials to ProjectId + }); + + // List HMAC SA Keys' Metadata + async function listHmacKeys() { + const [hmacKeys] = await storage.getHmacKeys(); + + // hmacKeys is an array of HmacKey objects. + for (const hmacKey of hmacKeys) { + console.log( + `Service Account Email: ${hmacKey.metadata.serviceAccountEmail}` + ); + console.log(`Access Id: ${hmacKey.metadata.accessId}`); + } + } + // [END storage_list_hmac_keys] + listHmacKeys(); +} + +main(...process.argv.slice(2)); diff --git a/samples/system-test/hmacKey.test.js b/samples/system-test/hmacKey.test.js new file mode 100644 index 000000000..a3b35fcc8 --- /dev/null +++ b/samples/system-test/hmacKey.test.js @@ -0,0 +1,100 @@ +/** + * Copyright 2017, Google, Inc. + * 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'; + +const {Storage} = require(`@google-cloud/storage`); +const {assert} = require('chai'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const poolProjectId = process.env.POOL_SAMPLES_PROJECT_ID; +const poolProjectCredentials = process.env.POOL_SAMPLES_PROJECT_CREDENTIALS; + +const storage = new Storage({ + projectId: poolProjectId, + keyFilename: poolProjectCredentials, +}); +const leasedServiceAccount = process.env.SAMPLES_HMAC_SERVICE_ACCOUNT; + +describe('HMAC SA Key samples', () => { + let hmacKey; + + before(async () => { + await cleanUpHmacKeys(leasedServiceAccount); + [hmacKey] = await storage.createHmacKey(leasedServiceAccount); + }); + + async function cleanUpHmacKeys(serviceAccountEmail) { + // list all HMAC keys for the given service account. + const [hmacKeys] = await storage.getHmacKeys({ + serviceAccountEmail: serviceAccountEmail, + }); + // deactivate and delete the key + for (const hmacKey of hmacKeys) { + await hmacKey.setMetadata({state: 'INACTIVE'}); + await hmacKey.delete(); + } + } + + after(async () => { + await cleanUpHmacKeys(leasedServiceAccount); + }); + + it('should create an HMAC Key', async () => { + const output = execSync( + `node hmacKeyCreate.js ${poolProjectId} ${poolProjectCredentials} ${leasedServiceAccount}` + ); + assert.include(output, 'The base64 encoded secret is:'); + }); + + it('should list HMAC Keys', async () => { + const output = execSync( + `node hmacKeysList.js ${poolProjectId} ${poolProjectCredentials}` + ); + assert.include(output, `Service Account Email: ${leasedServiceAccount}`); + }); + + it('should get HMAC Key', async () => { + const output = execSync(`node hmacKeyGet.js ${hmacKey.metadata.accessId}`); + assert.include(output, 'The HMAC key metadata is:'); + }); + + it('should deactivate HMAC Key', async () => { + const output = execSync( + `node hmacKeyDeactivate.js ${hmacKey.metadata.accessId}` + ); + assert.include(output, 'The HMAC key is now inactive.'); + }); + + it('should activate HMAC Key', async () => { + const output = execSync( + `node hmacKeyActivate.js ${hmacKey.metadata.accessId}` + ); + assert.include(output, 'The HMAC key is now active.'); + }); + + it(`should delete HMAC key`, async () => { + // Deactivate then delete + execSync(`node hmacKeyDeactivate.js ${hmacKey.metadata.accessId}`); + const output = execSync( + `node hmacKeyDelete.js ${hmacKey.metadata.accessId}` + ); + assert.include( + output, + `The key is deleted, though it may still appear in getHmacKeys() results.` + ); + }); +}); diff --git a/test/index.ts b/test/index.ts index 87cc8be78..abd4d6b12 100644 --- a/test/index.ts +++ b/test/index.ts @@ -246,7 +246,7 @@ describe('Storage', () => { it('should throw without a serviceAccountEmail', () => { assert.throws( () => storage.createHmacKey(), - /The first argument must be a service account email to create an HMAC key\./, + /The first argument must be a service account email to create an HMAC key\./ ); }); From 89fd9e684669a2974a450d29300ad8142a56596a Mon Sep 17 00:00:00 2001 From: Frank Natividad Date: Tue, 6 Aug 2019 11:17:50 -0700 Subject: [PATCH 37/44] Fix region tag. --- samples/hmacKeyGet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/hmacKeyGet.js b/samples/hmacKeyGet.js index 13569777f..6d5f07efb 100644 --- a/samples/hmacKeyGet.js +++ b/samples/hmacKeyGet.js @@ -22,7 +22,7 @@ // usage: node hmacKeyGet.js function main(hmacKeyAccessId = 'GOOG0234230X00') { - // [START storage_deactivate_hmac_key] + // [START storage_get_hmac_key] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); From 941cb36f3876b7bed46bb36df2b294dbb97b2881 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 8 Aug 2019 05:00:46 -0700 Subject: [PATCH 38/44] feat(hmacKey): support projectId option (#801) --- src/hmacKey.ts | 17 ++++++++++++++-- src/storage.ts | 50 ++++++++++++++++++++++++++++++++++++++--------- test/hmacKey.ts | 8 ++++++++ test/index.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 114 insertions(+), 13 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index 530f9f3ad..e9d04168e 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -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; @@ -58,6 +62,12 @@ export type HmacKeyMetadataResponse = [HmacKeyMetadata, Metadata]; export class HmacKey extends ServiceObject { 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. * @@ -67,12 +77,13 @@ export class HmacKey extends ServiceObject { * @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 @@ -303,10 +314,12 @@ export class HmacKey extends ServiceObject { }, }; + const projectId = (options && options.projectId) || storage.projectId; + super({ parent: storage, - baseUrl: `/projects/${storage.projectId}/hmacKeys`, id: accessId, + baseUrl: `/projects/${projectId}/hmacKeys`, methods, }); } diff --git a/src/storage.ts b/src/storage.ts index e711b5570..0cc84753d 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -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; @@ -108,6 +108,7 @@ export interface HmacKeyResourceResponse { export type CreateHmacKeyResponse = [HmacKey, string, HmacKeyResourceResponse]; export interface CreateHmacKeyOptions { + projectId?: string; userProject?: string; } @@ -121,6 +122,7 @@ export interface CreateHmacKeyCallback { } export interface GetHmacKeysOptions { + projectId?: string; serviceAccountEmail?: string; showDeletedKeys?: boolean; autoPaginate?: boolean; @@ -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 @@ -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. */ /** @@ -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) => { @@ -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); @@ -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 @@ -932,11 +954,14 @@ export class Storage extends Service { cb?: GetHmacKeysCallback ): Promise | void { const {options, callback} = normalize(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) { @@ -944,8 +969,10 @@ export class Storage extends Service { 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; }); @@ -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 * @@ -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); } } diff --git a/test/hmacKey.ts b/test/hmacKey.ts index a11daddf3..b36424ea8 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -55,6 +55,7 @@ describe('HmacKey', () => { STORAGE = { request: util.noop, + projectId: 'my-project', }; hmacKey = new HmacKey(STORAGE, ACCESS_ID); @@ -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, @@ -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'); + }); }); }); diff --git a/test/index.ts b/test/index.ts index abd4d6b12..54a3f11f1 100644 --- a/test/index.ts +++ b/test/index.ts @@ -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<{}>) { @@ -98,6 +100,7 @@ describe('Storage', () => { Service: FakeService, }, './channel.js': {Channel: FakeChannel}, + './hmacKey': hmacKeyModule, }).Storage; Bucket = Storage.Bucket; }); @@ -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', () => { @@ -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, @@ -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(); } @@ -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]; @@ -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(); }); From ea8266090100d1372f92c915b625d0d34b70edc7 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Fri, 9 Aug 2019 13:37:50 -0700 Subject: [PATCH 39/44] fix(ts): HmacKey Methods --- src/hmacKey.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hmacKey.ts b/src/hmacKey.ts index e9d04168e..e931a971b 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {Metadata, ServiceObject} from '@google-cloud/common'; +import {Metadata, ServiceObject, Methods} from '@google-cloud/common'; import {Storage} from './storage'; export interface HmacKeyOptions { @@ -312,7 +312,7 @@ export class HmacKey extends ServiceObject { method: 'PUT', }, }, - }; + } as Methods; const projectId = (options && options.projectId) || storage.projectId; From a4c4a662571c3f32c71bb5eb3cb250d978936ab9 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Tue, 13 Aug 2019 16:55:51 -0700 Subject: [PATCH 40/44] feat: HMAC System tests (#750) * npm run fix * test: add second service account email for testing multiple SAs HMAC keys * npm run fix * test: add second service account email for testing multiple SAs HMAC keys * fix tests * use HMAC_PROJECT in env variable --- .kokoro/setup-vars.sh | 2 + system-test/storage.ts | 121 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/.kokoro/setup-vars.sh b/.kokoro/setup-vars.sh index e316b91de..cc2b91992 100644 --- a/.kokoro/setup-vars.sh +++ b/.kokoro/setup-vars.sh @@ -23,6 +23,8 @@ export GCN_STORAGE_2ND_PROJECT_KEY=${KOKORO_GFILE_DIR}/no-whitelist-key.json export GOOGLE_CLOUD_KMS_KEY_ASIA="projects/long-door-651/locations/asia/keyRings/test-key-asia/cryptoKeys/test-key-asia" export GOOGLE_CLOUD_KMS_KEY_US="projects/long-door-651/locations/us/keyRings/test-key-us/cryptoKeys/test-key-us" +# Second service account for testing HMAC feature +export HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT="gcs-hmac-system-test-alt@long-door-651.iam.gserviceaccount.com" # TODO: Switch with service-account pool export POOL_SAMPLES_PROJECT_ID=long-door-651 export POOL_SAMPLES_PROJECT_CREDENTIALS="${KOKORO_GFILE_DIR}/storage-hmac-samples-key.json" diff --git a/system-test/storage.ts b/system-test/storage.ts index 4638d8432..55bec11b2 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -2687,6 +2687,116 @@ describe('storage', () => { }); }); + describe('HMAC keys', () => { + // This is generally a valid service account for a project. + const ALTERNATE_SERVICE_ACCOUNT = `${process.env.PROJECT_ID}@appspot.gserviceaccount.com`; + const SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT || ALTERNATE_SERVICE_ACCOUNT; + const HMAC_PROJECT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT ? process.env.HMAC_KEY_TEST_PROJECT : process.env.PROJECT_ID; + // Second service account to test listing HMAC keys from different accounts. + const SECOND_SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT; + + let accessId: string; + + before(async () => { + await deleteHmacKeys(SERVICE_ACCOUNT, HMAC_PROJECT!); + }); + + after(async () => { + await deleteHmacKeys(SERVICE_ACCOUNT, HMAC_PROJECT!); + }); + + it('should create an HMAC key for a service account', async () => { + const [hmacKey, secret] = await storage.createHmacKey(SERVICE_ACCOUNT, {projectId: HMAC_PROJECT}); + // We should always get a 40 character secret, which is valid base64. + assert.strictEqual(secret.length, 40); + accessId = hmacKey.id!; + const metadata = hmacKey.metadata!; + assert.strictEqual(metadata.accessId, accessId); + assert.strictEqual(metadata.state, 'ACTIVE'); + assert.strictEqual(metadata.projectId, HMAC_PROJECT); + assert.strictEqual(metadata.serviceAccountEmail, SERVICE_ACCOUNT); + assert(typeof metadata.etag === 'string'); + assert(typeof metadata.timeCreated === 'string'); + assert(typeof metadata.updated === 'string'); + }); + + it('should get metadata for an HMAC key', async () => { + const hmacKey = storage.hmacKey(accessId, {projectId: HMAC_PROJECT}); + const [metadata] = await hmacKey.getMetadata(); + assert.strictEqual(metadata.accessId, accessId); + }); + + it('should show up from getHmacKeys() without serviceAccountEmail param', async () => { + const [hmacKeys] = await storage.getHmacKeys({projectId: HMAC_PROJECT}); + assert(hmacKeys.length > 0); + assert( + hmacKeys.some((hmacKey) => hmacKey.id === accessId), + 'created HMAC key not found from getHmacKeys result'); + }); + + it('should make the key INACTIVE', async () => { + const hmacKey = storage.hmacKey(accessId, {projectId: HMAC_PROJECT}); + let [metadata] = await hmacKey.setMetadata({state: 'INACTIVE'}); + assert.strictEqual(metadata.state, 'INACTIVE'); + + [metadata] = await hmacKey.getMetadata(); + assert.strictEqual(metadata.state, 'INACTIVE'); + }); + + it('should delete the key', async () => { + const hmacKey = storage.hmacKey(accessId, {projectId: HMAC_PROJECT}); + await hmacKey.delete(); + const [metadata] = await hmacKey.getMetadata(); + assert.strictEqual(metadata.state, 'DELETED'); + assert.strictEqual(hmacKey.metadata!.state, 'DELETED'); + }); + + it('deleted key should not show up from getHmacKeys() by default', async () => { + const [hmacKeys] = await storage.getHmacKeys({serviceAccountEmail: SERVICE_ACCOUNT, projectId: HMAC_PROJECT}); + assert(Array.isArray(hmacKeys)); + assert( + !hmacKeys.some((hmacKey) => hmacKey.id === accessId), + 'deleted HMAC key is found from getHmacKeys result'); + }); + + describe('second service account', () => { + before(async function () { + if (!SECOND_SERVICE_ACCOUNT) { + this.skip(); + return; + } + await deleteHmacKeys(SECOND_SERVICE_ACCOUNT, HMAC_PROJECT!); + }); + + it('should create key for a second service account', async () => { + const _ = await storage.createHmacKey(SECOND_SERVICE_ACCOUNT!, {projectId: HMAC_PROJECT}); + }); + + it('get HMAC keys for both service accounts', async () => { + // Create a key for the first service account + const _ = await storage.createHmacKey(SERVICE_ACCOUNT!, {projectId: HMAC_PROJECT}); + + const [hmacKeys] = await storage.getHmacKeys({projectId: HMAC_PROJECT}); + assert(hmacKeys.some((hmacKey) => + hmacKey.metadata!.serviceAccountEmail === SERVICE_ACCOUNT), + `Expected at least 1 key for service account: ${SERVICE_ACCOUNT}` + ); + assert(hmacKeys.some((hmacKey) => + hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT), + `Expected at least 1 key for service account: ${SECOND_SERVICE_ACCOUNT}` + ); + }); + + it('filter by service account email', async () => { + const [hmacKeys] = await storage.getHmacKeys({serviceAccountEmail: SECOND_SERVICE_ACCOUNT, projectId: HMAC_PROJECT}); + assert(hmacKeys.every((hmacKey) => + hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT), + 'HMAC key belonging to other service accounts unexpected' + ); + }); + }); + }); + describe('list files', () => { const DIRECTORY_NAME = 'directory-name'; @@ -3235,6 +3345,17 @@ describe('storage', () => { } } + async function deleteHmacKeys(serviceAccountEmail: string, projectId: string) { + const [hmacKeys] = await storage.getHmacKeys({ serviceAccountEmail, projectId }); + const limit = pLimit(10); + await Promise.all(hmacKeys.map((hmacKey) => limit(async () => { + if (hmacKey.metadata!.state === 'ACTIVE') { + await hmacKey.setMetadata({state:'INACTIVE' }); + } + await hmacKey.delete(); + }))); + } + // tslint:disable-next-line no-any function createFileAsync(fileObject: any) { return fileObject.file.save(fileObject.contents); From 28f5f3384b2e8e79b3d01aa4b1c36fd9ac95ecdd Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 14 Aug 2019 15:29:48 -0700 Subject: [PATCH 41/44] feat(test): hmac key - service account pool and fix tests (#810) * update HMAC samples to not require creds, and add projectId * setup second system test service account * fix sample tests * fix sample metadata for hmac keys * reorder hmackey argument * run synthtool * put projectId at the end so its optional * synthtool * fix cleanup after test * log service account used * fixy * fix(tests): create needs projectId * npm run fix * use gimmeproj instead of gimme-acc * fix hmacKeyGet * fix system-test * fix check active * fix HMAC_PROJECT in system-test * properly unlease all service accounts * fix unleasing --- .kokoro/pre-system-test.sh | 5 ++ .kokoro/setup-vars.sh | 23 ++++-- README.md | 6 ++ samples/README.md | 120 ++++++++++++++++++++++++++++ samples/hmacKeyActivate.js | 10 ++- samples/hmacKeyCreate.js | 17 ++-- samples/hmacKeyDeactivate.js | 10 ++- samples/hmacKeyDelete.js | 11 ++- samples/hmacKeyGet.js | 12 ++- samples/hmacKeysList.js | 18 ++--- samples/system-test/hmacKey.test.js | 67 ++++++++-------- synth.metadata | 2 +- system-test/storage.ts | 2 +- 13 files changed, 231 insertions(+), 72 deletions(-) diff --git a/.kokoro/pre-system-test.sh b/.kokoro/pre-system-test.sh index a1ffa0cea..abc85f9d4 100755 --- a/.kokoro/pre-system-test.sh +++ b/.kokoro/pre-system-test.sh @@ -15,3 +15,8 @@ # limitations under the License. . .kokoro/setup-vars.sh + +# Lease a second service account for testing listing with multiple service accounts +export HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT=$(./gimmeproj -project=$HMAC_PROJECT lease 15m) +# Add to the list of leased service account for clean up after tests +export LEASED_SERVICE_ACCOUNTS="$LEASED_SERVICE_ACCOUNTS $HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT" diff --git a/.kokoro/setup-vars.sh b/.kokoro/setup-vars.sh index cc2b91992..881b62611 100644 --- a/.kokoro/setup-vars.sh +++ b/.kokoro/setup-vars.sh @@ -23,9 +23,20 @@ export GCN_STORAGE_2ND_PROJECT_KEY=${KOKORO_GFILE_DIR}/no-whitelist-key.json export GOOGLE_CLOUD_KMS_KEY_ASIA="projects/long-door-651/locations/asia/keyRings/test-key-asia/cryptoKeys/test-key-asia" export GOOGLE_CLOUD_KMS_KEY_US="projects/long-door-651/locations/us/keyRings/test-key-us/cryptoKeys/test-key-us" -# Second service account for testing HMAC feature -export HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT="gcs-hmac-system-test-alt@long-door-651.iam.gserviceaccount.com" -# TODO: Switch with service-account pool -export POOL_SAMPLES_PROJECT_ID=long-door-651 -export POOL_SAMPLES_PROJECT_CREDENTIALS="${KOKORO_GFILE_DIR}/storage-hmac-samples-key.json" -export SAMPLES_HMAC_SERVICE_ACCOUNT="storage-key-hmac-samples@long-door-651.iam.gserviceaccount.com" +# For testing SA HMAC +export HMAC_PROJECT=gimme-acc +curl https://storage.googleapis.com/gimme-proj/linux_amd64/gimmeproj > gimmeproj +chmod +x gimmeproj +./gimmeproj version + +export HMAC_KEY_TEST_SERVICE_ACCOUNT=$(./gimmeproj -project=$HMAC_PROJECT lease 15m) +echo Leased service account: $HMAC_KEY_TEST_SERVICE_ACCOUNT +export LEASED_SERVICE_ACCOUNTS=$HMAC_KEY_TEST_SERVICE_ACCOUNT + +cleanup_service_accounts () { + for i in $LEASED_SERVICE_ACCOUNTS; do + ./gimmeproj -project=$HMAC_PROJECT "done" $i + done +} + +trap cleanup_service_accounts EXIT diff --git a/README.md b/README.md index adcc18c92..c6464afad 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,12 @@ has instructions for running the samples. | Buckets | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/buckets.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/buckets.js,samples/README.md) | | Encryption | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/encryption.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/encryption.js,samples/README.md) | | Files | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/files.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/files.js,samples/README.md) | +| Activate HMAC SA Key. | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyActivate.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyActivate.js,samples/README.md) | +| Create HMAC SA Key. | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyCreate.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyCreate.js,samples/README.md) | +| Deactivate HMAC SA Key. | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyDeactivate.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyDeactivate.js,samples/README.md) | +| Delete HMAC SA Key. | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyDelete.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyDelete.js,samples/README.md) | +| Get HMAC SA Key Metadata. | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyGet.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyGet.js,samples/README.md) | +| List HMAC SA Keys Metadata. | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeysList.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeysList.js,samples/README.md) | | Iam | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/iam.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/iam.js,samples/README.md) | | Notifications | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/notifications.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/notifications.js,samples/README.md) | | Quickstart | [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/quickstart.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) | diff --git a/samples/README.md b/samples/README.md index 6eec5ccd7..78ea557f5 100644 --- a/samples/README.md +++ b/samples/README.md @@ -24,6 +24,12 @@ objects to users via direct download. * [Buckets](#buckets) * [Encryption](#encryption) * [Files](#files) + * [Activate HMAC SA Key.](#activate-hmac-sa-key.) + * [Create HMAC SA Key.](#create-hmac-sa-key.) + * [Deactivate HMAC SA Key.](#deactivate-hmac-sa-key.) + * [Delete HMAC SA Key.](#delete-hmac-sa-key.) + * [Get HMAC SA Key Metadata.](#get-hmac-sa-key-metadata.) + * [List HMAC SA Keys Metadata.](#list-hmac-sa-keys-metadata.) * [Iam](#iam) * [Notifications](#notifications) * [Quickstart](#quickstart) @@ -142,6 +148,120 @@ __Usage:__ +### Activate HMAC SA Key. + +Activate HMAC SA Key. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyActivate.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyActivate.js,samples/README.md) + +__Usage:__ + + +`node hmacKeyActivate.js [projectId]` + + +----- + + + + +### Create HMAC SA Key. + +Create HMAC SA Key. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyCreate.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyCreate.js,samples/README.md) + +__Usage:__ + + +`node hmacKeyCreate.js [projectId]` + + +----- + + + + +### Deactivate HMAC SA Key. + +Deactivate HMAC SA Key. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyDeactivate.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyDeactivate.js,samples/README.md) + +__Usage:__ + + +`node hmacKeyDeactivate.js [projectId]` + + +----- + + + + +### Delete HMAC SA Key. + +Delete HMAC SA Key. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyDelete.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyDelete.js,samples/README.md) + +__Usage:__ + + +`node hmacKeyDelete.js [projectId]` + + +----- + + + + +### Get HMAC SA Key Metadata. + +Get HMAC SA Key Metadata. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeyGet.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeyGet.js,samples/README.md) + +__Usage:__ + + +`node hmacKeyGet.js [projectId]` + + +----- + + + + +### List HMAC SA Keys Metadata. + +List HMAC SA Keys Metadata. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/hmacKeysList.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/hmacKeysList.js,samples/README.md) + +__Usage:__ + + +`node hmacKeyList.js [projectId]` + + +----- + + + + ### Iam View the [source code](https://github.com/googleapis/nodejs-storage/blob/master/samples/iam.js). diff --git a/samples/hmacKeyActivate.js b/samples/hmacKeyActivate.js index b7e71d864..09a2fe4d4 100644 --- a/samples/hmacKeyActivate.js +++ b/samples/hmacKeyActivate.js @@ -19,9 +19,12 @@ // sample-metadata: // title: Activate HMAC SA Key. // description: Activate HMAC SA Key. -// usage: node hmacKeyActivate.js +// usage: node hmacKeyActivate.js [projectId] -function main(hmacKeyAccessId = 'GOOG0234230X00') { +function main( + hmacKeyAccessId = 'GOOG0234230X00', + projectId = 'serviceAccountProjectId' +) { // [START storage_activate_hmac_key] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); @@ -35,8 +38,9 @@ function main(hmacKeyAccessId = 'GOOG0234230X00') { * TODO(developer): Uncomment the following line before running the sample. */ // const hmacKeyAccessId = 'HMAC Access Key Id to update, e.g. GOOG0234230X00'; + // const projectId = 'The project Id this service account belongs to, e.g. serviceAccountProjectId'; - const hmacKey = storage.hmacKey(hmacKeyAccessId); + const hmacKey = storage.hmacKey(hmacKeyAccessId, {projectId}); const [hmacKeyMetadata] = await hmacKey.setMetadata({state: 'ACTIVE'}); console.log(`The HMAC key is now active.`); diff --git a/samples/hmacKeyCreate.js b/samples/hmacKeyCreate.js index b60e44a49..3ae1ac79e 100644 --- a/samples/hmacKeyCreate.js +++ b/samples/hmacKeyCreate.js @@ -19,22 +19,18 @@ // sample-metadata: // title: Create HMAC SA Key. // description: Create HMAC SA Key. -// usage: node hmacKeyCreate.js +// usage: node hmacKeyCreate.js [projectId] function main( - projectId = 'serviceAccountProjectId', - credentialsFile = 'serviceAccountCredentials', - serviceAccountEmail = 'service-account@example.com' + serviceAccountEmail = 'service-account@example.com', + projectId = 'serviceAccountProjectId' ) { // [START storage_create_hmac_key] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); // Creates a client - const storage = new Storage({ - projectId: projectId, // ProjectId of where to create a new HMAC SA Keys. - keyFilename: credentialsFile, // Credentials to ProjectId - }); + const storage = new Storage(); // Create HMAC SA Key async function createHmacKey() { @@ -42,8 +38,11 @@ function main( * TODO(developer): Uncomment the following line before running the sample. */ // const serviceAccountEmail = 'Service Account Email to associate HMAC Key'; + // const projectId = 'The project Id this service account to be created in, e.g. serviceAccountProjectId'; - const [hmacKey, secret] = await storage.createHmacKey(serviceAccountEmail); + const [hmacKey, secret] = await storage.createHmacKey(serviceAccountEmail, { + projectId, + }); console.log(`The base64 encoded secret is: ${secret}`); console.log(`Do not miss that secret, there is no API to recover it.`); diff --git a/samples/hmacKeyDeactivate.js b/samples/hmacKeyDeactivate.js index e649466fd..4c05ff59e 100644 --- a/samples/hmacKeyDeactivate.js +++ b/samples/hmacKeyDeactivate.js @@ -19,9 +19,12 @@ // sample-metadata: // title: Deactivate HMAC SA Key. // description: Deactivate HMAC SA Key. -// usage: node hmacKeyDeactivate.js +// usage: node hmacKeyDeactivate.js [projectId] -function main(hmacKeyAccessId = 'GOOG0234230X00') { +function main( + hmacKeyAccessId = 'GOOG0234230X00', + projectId = 'serviceAccountProjectId' +) { // [START storage_deactivate_hmac_key] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); @@ -35,8 +38,9 @@ function main(hmacKeyAccessId = 'GOOG0234230X00') { * TODO(developer): Uncomment the following line before running the sample. */ // const hmacKeyAccessId = 'HMAC Access Key Id to update, e.g. GOOG0234230X00'; + // const projectId = 'The project Id this service account belongs to, e.g. serviceAccountProjectId'; - const hmacKey = storage.hmacKey(hmacKeyAccessId); + const hmacKey = storage.hmacKey(hmacKeyAccessId, {projectId}); const [hmacKeyMetadata] = await hmacKey.setMetadata({state: 'INACTIVE'}); console.log(`The HMAC key is now inactive.`); diff --git a/samples/hmacKeyDelete.js b/samples/hmacKeyDelete.js index 8e6380bd3..bb1942825 100644 --- a/samples/hmacKeyDelete.js +++ b/samples/hmacKeyDelete.js @@ -19,9 +19,12 @@ // sample-metadata: // title: Delete HMAC SA Key. // description: Delete HMAC SA Key. -// usage: node hmacKeyDelete.js +// usage: node hmacKeyDelete.js [projectId] -function main(hmacKeyAccessId = 'GOOG0234230X00') { +function main( + hmacKeyAccessId = 'GOOG0234230X00', + projectId = 'serviceAccountProjectId' +) { // [START storage_delete_hmac_key] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); @@ -35,9 +38,9 @@ function main(hmacKeyAccessId = 'GOOG0234230X00') { * TODO(developer): Uncomment the following line before running the sample. */ // const hmacKeyAccessId = 'Inactive HMAC Access Key Id to delete, e.g. GOOG0234230X00'; + // const projectId = 'The project Id this service account belongs to, e.g. serviceAccountProjectId'; - const hmacKey = storage.hmacKey(hmacKeyAccessId); - + const hmacKey = storage.hmacKey(hmacKeyAccessId, {projectId}); await hmacKey.delete(); console.log( diff --git a/samples/hmacKeyGet.js b/samples/hmacKeyGet.js index 6d5f07efb..b5954e98a 100644 --- a/samples/hmacKeyGet.js +++ b/samples/hmacKeyGet.js @@ -19,9 +19,12 @@ // sample-metadata: // title: Get HMAC SA Key Metadata. // description: Get HMAC SA Key Metadata. -// usage: node hmacKeyGet.js +// usage: node hmacKeyGet.js [projectId] -function main(hmacKeyAccessId = 'GOOG0234230X00') { +function main( + hmacKeyAccessId = 'GOOG0234230X00', + projectId = 'serviceAccountProjectId' +) { // [START storage_get_hmac_key] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); @@ -35,8 +38,11 @@ function main(hmacKeyAccessId = 'GOOG0234230X00') { * TODO(developer): Uncomment the following line before running the sample. */ // const hmacKeyAccessId = 'HMAC Access Key Id to get, e.g. GOOG0234230X00'; + // const projectId = 'The project Id this service account belongs to, e.g. serviceAccountProjectId'; - const [hmacKey] = await storage.hmacKey(hmacKeyAccessId).get(); + const hmacKey = storage.hmacKey(hmacKeyAccessId, {projectId}); + // Populate the hmacKey object with metadata from server. + await hmacKey.getMetadata(); console.log(`The HMAC key metadata is:`); for (const [key, value] of Object.entries(hmacKey.metadata)) { diff --git a/samples/hmacKeysList.js b/samples/hmacKeysList.js index 0cca622ca..0770eaea8 100644 --- a/samples/hmacKeysList.js +++ b/samples/hmacKeysList.js @@ -19,25 +19,23 @@ // sample-metadata: // title: List HMAC SA Keys Metadata. // description: List HMAC SA Keys Metadata. -// usage: node hmacKeyList.js +// usage: node hmacKeyList.js [projectId] -function main( - projectId = 'serviceAccountProjectId', - credentialsFile = 'serviceAccountCredentials' -) { +function main(projectId = 'serviceAccountProjectId') { // [START storage_list_hmac_keys] // Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); // Creates a client - const storage = new Storage({ - projectId: projectId, // ProjectId from where to list HMAC SA Keys - keyFilename: credentialsFile, // Credentials to ProjectId - }); + const storage = new Storage(); // List HMAC SA Keys' Metadata async function listHmacKeys() { - const [hmacKeys] = await storage.getHmacKeys(); + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'The project Id this service account belongs to, e.g. serviceAccountProjectId'; + const [hmacKeys] = await storage.getHmacKeys({projectId}); // hmacKeys is an array of HmacKey objects. for (const hmacKey of hmacKeys) { diff --git a/samples/system-test/hmacKey.test.js b/samples/system-test/hmacKey.test.js index a3b35fcc8..79cb0dc9a 100644 --- a/samples/system-test/hmacKey.test.js +++ b/samples/system-test/hmacKey.test.js @@ -20,77 +20,65 @@ const {assert} = require('chai'); const cp = require('child_process'); const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); -const poolProjectId = process.env.POOL_SAMPLES_PROJECT_ID; -const poolProjectCredentials = process.env.POOL_SAMPLES_PROJECT_CREDENTIALS; -const storage = new Storage({ - projectId: poolProjectId, - keyFilename: poolProjectCredentials, -}); -const leasedServiceAccount = process.env.SAMPLES_HMAC_SERVICE_ACCOUNT; +const storage = new Storage(); +const SERVICE_ACCOUNT_EMAIL = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT; +const SERVICE_ACCOUNT_PROJECT = process.env.HMAC_PROJECT; describe('HMAC SA Key samples', () => { let hmacKey; before(async () => { - await cleanUpHmacKeys(leasedServiceAccount); - [hmacKey] = await storage.createHmacKey(leasedServiceAccount); - }); - - async function cleanUpHmacKeys(serviceAccountEmail) { - // list all HMAC keys for the given service account. - const [hmacKeys] = await storage.getHmacKeys({ - serviceAccountEmail: serviceAccountEmail, + await cleanUpHmacKeys(SERVICE_ACCOUNT_EMAIL, SERVICE_ACCOUNT_PROJECT); + [hmacKey] = await storage.createHmacKey(SERVICE_ACCOUNT_EMAIL, { + projectId: SERVICE_ACCOUNT_PROJECT, }); - // deactivate and delete the key - for (const hmacKey of hmacKeys) { - await hmacKey.setMetadata({state: 'INACTIVE'}); - await hmacKey.delete(); - } - } + }); after(async () => { - await cleanUpHmacKeys(leasedServiceAccount); + await cleanUpHmacKeys(SERVICE_ACCOUNT_EMAIL, SERVICE_ACCOUNT_PROJECT); }); it('should create an HMAC Key', async () => { const output = execSync( - `node hmacKeyCreate.js ${poolProjectId} ${poolProjectCredentials} ${leasedServiceAccount}` + `node hmacKeyCreate.js ${SERVICE_ACCOUNT_EMAIL} ${SERVICE_ACCOUNT_PROJECT}` ); assert.include(output, 'The base64 encoded secret is:'); }); it('should list HMAC Keys', async () => { - const output = execSync( - `node hmacKeysList.js ${poolProjectId} ${poolProjectCredentials}` - ); - assert.include(output, `Service Account Email: ${leasedServiceAccount}`); + const output = execSync(`node hmacKeysList.js ${SERVICE_ACCOUNT_PROJECT}`); + assert.include(output, `Service Account Email: ${SERVICE_ACCOUNT_EMAIL}`); }); it('should get HMAC Key', async () => { - const output = execSync(`node hmacKeyGet.js ${hmacKey.metadata.accessId}`); + const output = execSync( + `node hmacKeyGet.js ${hmacKey.metadata.accessId} ${SERVICE_ACCOUNT_PROJECT}` + ); assert.include(output, 'The HMAC key metadata is:'); }); it('should deactivate HMAC Key', async () => { const output = execSync( - `node hmacKeyDeactivate.js ${hmacKey.metadata.accessId}` + `node hmacKeyDeactivate.js ${hmacKey.metadata.accessId} ${SERVICE_ACCOUNT_PROJECT}` ); assert.include(output, 'The HMAC key is now inactive.'); }); it('should activate HMAC Key', async () => { const output = execSync( - `node hmacKeyActivate.js ${hmacKey.metadata.accessId}` + `node hmacKeyActivate.js ${hmacKey.metadata.accessId} ${SERVICE_ACCOUNT_PROJECT}` ); assert.include(output, 'The HMAC key is now active.'); }); it(`should delete HMAC key`, async () => { // Deactivate then delete - execSync(`node hmacKeyDeactivate.js ${hmacKey.metadata.accessId}`); + execSync( + `node hmacKeyDeactivate.js ${hmacKey.metadata.accessId} ${SERVICE_ACCOUNT_PROJECT}` + ); const output = execSync( - `node hmacKeyDelete.js ${hmacKey.metadata.accessId}` + `node hmacKeyDelete.js ${hmacKey.metadata.accessId} ${SERVICE_ACCOUNT_PROJECT}` ); assert.include( output, @@ -98,3 +86,18 @@ describe('HMAC SA Key samples', () => { ); }); }); + +async function cleanUpHmacKeys(serviceAccountEmail, projectId) { + // list all HMAC keys for the given service account. + const [hmacKeys] = await storage.getHmacKeys({ + projectId, + serviceAccountEmail: serviceAccountEmail, + }); + // deactivate and delete the key + for (const hmacKey of hmacKeys) { + if (hmacKey.metadata.state === 'ACTIVE') { + await hmacKey.setMetadata({state: 'INACTIVE'}); + } + await hmacKey.delete(); + } +} diff --git a/synth.metadata b/synth.metadata index 3abf3e198..bf13af7bb 100644 --- a/synth.metadata +++ b/synth.metadata @@ -1,5 +1,5 @@ { - "updateTime": "2019-08-02T11:26:24.848730Z", + "updateTime": "2019-08-14T00:53:10.619189Z", "sources": [ { "template": { diff --git a/system-test/storage.ts b/system-test/storage.ts index 55bec11b2..8e99c8603 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -2691,7 +2691,7 @@ describe('storage', () => { // This is generally a valid service account for a project. const ALTERNATE_SERVICE_ACCOUNT = `${process.env.PROJECT_ID}@appspot.gserviceaccount.com`; const SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT || ALTERNATE_SERVICE_ACCOUNT; - const HMAC_PROJECT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT ? process.env.HMAC_KEY_TEST_PROJECT : process.env.PROJECT_ID; + const HMAC_PROJECT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT ? process.env.HMAC_PROJECT : process.env.PROJECT_ID; // Second service account to test listing HMAC keys from different accounts. const SECOND_SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT; From 05e37becd543669347cf46b84e8e740eb16f81f4 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 14 Aug 2019 15:50:17 -0700 Subject: [PATCH 42/44] npm run fix --- system-test/storage.ts | 90 +++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/system-test/storage.ts b/system-test/storage.ts index 8e99c8603..5df502897 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -2690,10 +2690,14 @@ describe('storage', () => { describe('HMAC keys', () => { // This is generally a valid service account for a project. const ALTERNATE_SERVICE_ACCOUNT = `${process.env.PROJECT_ID}@appspot.gserviceaccount.com`; - const SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT || ALTERNATE_SERVICE_ACCOUNT; - const HMAC_PROJECT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT ? process.env.HMAC_PROJECT : process.env.PROJECT_ID; + const SERVICE_ACCOUNT = + process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT || ALTERNATE_SERVICE_ACCOUNT; + const HMAC_PROJECT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT + ? process.env.HMAC_PROJECT + : process.env.PROJECT_ID; // Second service account to test listing HMAC keys from different accounts. - const SECOND_SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT; + const SECOND_SERVICE_ACCOUNT = + process.env.HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT; let accessId: string; @@ -2706,7 +2710,9 @@ describe('storage', () => { }); it('should create an HMAC key for a service account', async () => { - const [hmacKey, secret] = await storage.createHmacKey(SERVICE_ACCOUNT, {projectId: HMAC_PROJECT}); + const [hmacKey, secret] = await storage.createHmacKey(SERVICE_ACCOUNT, { + projectId: HMAC_PROJECT, + }); // We should always get a 40 character secret, which is valid base64. assert.strictEqual(secret.length, 40); accessId = hmacKey.id!; @@ -2730,8 +2736,9 @@ describe('storage', () => { const [hmacKeys] = await storage.getHmacKeys({projectId: HMAC_PROJECT}); assert(hmacKeys.length > 0); assert( - hmacKeys.some((hmacKey) => hmacKey.id === accessId), - 'created HMAC key not found from getHmacKeys result'); + hmacKeys.some(hmacKey => hmacKey.id === accessId), + 'created HMAC key not found from getHmacKeys result' + ); }); it('should make the key INACTIVE', async () => { @@ -2752,15 +2759,19 @@ describe('storage', () => { }); it('deleted key should not show up from getHmacKeys() by default', async () => { - const [hmacKeys] = await storage.getHmacKeys({serviceAccountEmail: SERVICE_ACCOUNT, projectId: HMAC_PROJECT}); + const [hmacKeys] = await storage.getHmacKeys({ + serviceAccountEmail: SERVICE_ACCOUNT, + projectId: HMAC_PROJECT, + }); assert(Array.isArray(hmacKeys)); assert( - !hmacKeys.some((hmacKey) => hmacKey.id === accessId), - 'deleted HMAC key is found from getHmacKeys result'); + !hmacKeys.some(hmacKey => hmacKey.id === accessId), + 'deleted HMAC key is found from getHmacKeys result' + ); }); describe('second service account', () => { - before(async function () { + before(async function() { if (!SECOND_SERVICE_ACCOUNT) { this.skip(); return; @@ -2769,28 +2780,43 @@ describe('storage', () => { }); it('should create key for a second service account', async () => { - const _ = await storage.createHmacKey(SECOND_SERVICE_ACCOUNT!, {projectId: HMAC_PROJECT}); + const _ = await storage.createHmacKey(SECOND_SERVICE_ACCOUNT!, { + projectId: HMAC_PROJECT, + }); }); it('get HMAC keys for both service accounts', async () => { // Create a key for the first service account - const _ = await storage.createHmacKey(SERVICE_ACCOUNT!, {projectId: HMAC_PROJECT}); + const _ = await storage.createHmacKey(SERVICE_ACCOUNT!, { + projectId: HMAC_PROJECT, + }); const [hmacKeys] = await storage.getHmacKeys({projectId: HMAC_PROJECT}); - assert(hmacKeys.some((hmacKey) => - hmacKey.metadata!.serviceAccountEmail === SERVICE_ACCOUNT), + assert( + hmacKeys.some( + hmacKey => hmacKey.metadata!.serviceAccountEmail === SERVICE_ACCOUNT + ), `Expected at least 1 key for service account: ${SERVICE_ACCOUNT}` ); - assert(hmacKeys.some((hmacKey) => - hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT), + assert( + hmacKeys.some( + hmacKey => + hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT + ), `Expected at least 1 key for service account: ${SECOND_SERVICE_ACCOUNT}` ); }); it('filter by service account email', async () => { - const [hmacKeys] = await storage.getHmacKeys({serviceAccountEmail: SECOND_SERVICE_ACCOUNT, projectId: HMAC_PROJECT}); - assert(hmacKeys.every((hmacKey) => - hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT), + const [hmacKeys] = await storage.getHmacKeys({ + serviceAccountEmail: SECOND_SERVICE_ACCOUNT, + projectId: HMAC_PROJECT, + }); + assert( + hmacKeys.every( + hmacKey => + hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT + ), 'HMAC key belonging to other service accounts unexpected' ); }); @@ -3345,15 +3371,25 @@ describe('storage', () => { } } - async function deleteHmacKeys(serviceAccountEmail: string, projectId: string) { - const [hmacKeys] = await storage.getHmacKeys({ serviceAccountEmail, projectId }); + async function deleteHmacKeys( + serviceAccountEmail: string, + projectId: string + ) { + const [hmacKeys] = await storage.getHmacKeys({ + serviceAccountEmail, + projectId, + }); const limit = pLimit(10); - await Promise.all(hmacKeys.map((hmacKey) => limit(async () => { - if (hmacKey.metadata!.state === 'ACTIVE') { - await hmacKey.setMetadata({state:'INACTIVE' }); - } - await hmacKey.delete(); - }))); + await Promise.all( + hmacKeys.map(hmacKey => + limit(async () => { + if (hmacKey.metadata!.state === 'ACTIVE') { + await hmacKey.setMetadata({state: 'INACTIVE'}); + } + await hmacKey.delete(); + }) + ) + ); } // tslint:disable-next-line no-any From 19be674dc33aed2da6d190e8334436f9e2f490ca Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Mon, 19 Aug 2019 10:47:49 -0700 Subject: [PATCH 43/44] fix copyright header --- samples/system-test/hmacKey.test.js | 2 +- src/hmacKey.ts | 2 +- src/index.ts | 2 +- src/storage.ts | 2 +- test/hmacKey.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/system-test/hmacKey.test.js b/samples/system-test/hmacKey.test.js index 79cb0dc9a..4c2070564 100644 --- a/samples/system-test/hmacKey.test.js +++ b/samples/system-test/hmacKey.test.js @@ -1,5 +1,5 @@ /** - * Copyright 2017, Google, Inc. + * Copyright 2019 Google LLC * 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 diff --git a/src/hmacKey.ts b/src/hmacKey.ts index e931a971b..3cfa2d1f9 100644 --- a/src/hmacKey.ts +++ b/src/hmacKey.ts @@ -1,5 +1,5 @@ /** - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/index.ts b/src/index.ts index 57a77ea0b..0b8a3a36c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ /** - * Copyright 2014-2017 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/storage.ts b/src/storage.ts index 67e583f08..b938d40dd 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,5 +1,5 @@ /** - * Copyright 2014-2017 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/hmacKey.ts b/test/hmacKey.ts index b36424ea8..b073a8291 100644 --- a/test/hmacKey.ts +++ b/test/hmacKey.ts @@ -1,5 +1,5 @@ /** - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 1b80e24189d46f3089dd6d5610230b2ca8a7054b Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Wed, 21 Aug 2019 16:30:37 -0700 Subject: [PATCH 44/44] fix header --- system-test/storage.ts | 2 +- test/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system-test/storage.ts b/system-test/storage.ts index 5df502897..ca6d738ad 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -1,5 +1,5 @@ /** - * Copyright 2014 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/index.ts b/test/index.ts index 54a3f11f1..1c80d0171 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1,5 +1,5 @@ /** - * Copyright 2014 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.