diff --git a/lib/IAMClient.js b/lib/IAMClient.js index 2c731ecd..7a6d9784 100644 --- a/lib/IAMClient.js +++ b/lib/IAMClient.js @@ -456,6 +456,15 @@ class VaultClient { /** * Get accounts using account ids, canonical ids or email addresses * + * There are 2 signatures: + * - `getAccounts(accountIds, emailAddresses, canonicalIds, options, callback)` + * - `getAccounts( + * filter: { accountIds?, emailAddresses?, canonicalIds?, accountNames? }, + * options, callback)` + * + * The last signature allows to add a new filter `accountNames` + * without messing with previous signature. + * * @param {array|undefined} accountIds - Account ids, exclusive with * emailAddresses and canonicalIds * @param {array|undefined} emailAddresses - Email addresses, exclusive @@ -465,22 +474,42 @@ class VaultClient { * @param {object} options - Options * @param {string} [options.reqUid] - Request uid * @param {function} callback - Callback(err, result) + * + * @param {Object} filter - Contains the previous signature's first 3 params + * with additional `accountNames` field + * + * Note: do not use `accountNames` with vault2 + * * @return {undefined} */ getAccounts(accountIds, emailAddresses, canonicalIds, options, callback) { + let accountNames; + if (arguments.length === 3) { + // check and convert args from signature getAccounts(filter, options, callback) + const filter = accountIds; + /* eslint-disable-next-line no-param-reassign */ + options = emailAddresses; + /* eslint-disable-next-line no-param-reassign */ + callback = canonicalIds; + assert(filter && typeof filter === 'object' && !Array.isArray(filter), + 'first arg (filter) must be an object containing one filter field'); + assert(options && typeof options === 'object' && !Array.isArray(options), + 'second arg (options) must be an object'); + assert(callback && typeof callback === 'function', + 'third arg (callback) must be a function'); + ({ + /* eslint-disable-next-line no-param-reassign */ + accountIds, emailAddresses, canonicalIds, accountNames + } = filter); + } assert((accountIds && Array.isArray(accountIds)) || !accountIds, 'accountIds should be an array'); assert((emailAddresses && Array.isArray(emailAddresses)) || !emailAddresses, 'emailAddresses should be an array'); assert((canonicalIds && Array.isArray(canonicalIds)) || !canonicalIds, 'canonicalIds should be an array'); - if ( - (accountIds && (emailAddresses || canonicalIds)) - || (emailAddresses && (accountIds || canonicalIds)) - || (canonicalIds && (accountIds || emailAddresses))) { - assert(false, 'accountIds, emailAddresses and canonicalIds ' - + 'ids are exclusive'); - } + assert((accountNames && Array.isArray(accountNames)) || !accountNames, + 'accountNames should be an array'); const data = { Action: 'GetAccounts', Version: '2010-05-08', @@ -494,7 +523,13 @@ class VaultClient { if (emailAddresses) { data.emailAddresses = emailAddresses; } - + if (accountNames) { + data.accountNames = accountNames; + } + if (Object.values(data).length > 3) { + assert(false, 'accountIds, emailAddresses, canonicalIds ' + + 'and accountNames ids are exclusive'); + } const verb = this.useAuthenticatedAdminRoutes ? 'POST' : 'GET'; this.request(verb, '/', this.useAuthenticatedAdminRoutes, callback, data, options.reqUid, null); diff --git a/tests/unit/getAccounts.js b/tests/unit/getAccounts.js new file mode 100644 index 00000000..b9640747 --- /dev/null +++ b/tests/unit/getAccounts.js @@ -0,0 +1,100 @@ +/* eslint-disable operator-linebreak */ +'use strict'; // eslint-disable-line + +const assert = require('assert'); +const IAMClient = require('../../lib/IAMClient.js'); + +const accountIds = ['accountId1', 'accountId2']; +const canonicalIds = ['canId1', 'canId2']; +const emailAddresses = ['email1', 'email2']; +const accountNames = ['name1', 'name2']; +const opt = { reqUid: 'test.getAccounts.reqUid' }; +const mockCB = () => {}; + +const expectedData = { + Action: 'GetAccounts', + Version: '2010-05-08', +}; + +describe('getAccounts', () => { + let client; + let spyArg = null; + + beforeEach('spy on request', done => { + client = new IAMClient('127.0.0.1', 8500); + client.request = function spy(...args) { + spyArg = args; + }; + done(); + }); + + afterEach('reset spyArg', () => { spyArg = null; }); + + describe('should send request with regular function signature (5 args)', () => { + [ + { name: 'accountIds', args: [accountIds, null, null, opt, mockCB] }, + { name: 'emailAddresses', args: [null, emailAddresses, null, opt, mockCB] }, + { name: 'canonicalIds', args: [null, null, canonicalIds, opt, mockCB] }, + ].forEach(({ name, args }) => it(`for ${name}`, () => { + client.getAccounts(...args); + const [method, path, auth, cb, data, reqUid, contentType] = spyArg; + assert.strictEqual(method, 'GET'); + assert.strictEqual(path, '/'); + assert.strictEqual(auth, false); + assert.strictEqual(cb, mockCB); + assert.strictEqual(reqUid, opt.reqUid); + assert.strictEqual(contentType, null); + assert.deepStrictEqual( + data, + Object.assign({}, expectedData, { [name]: args[0] || args[1] || args[2] }), + ); + })); + }); + + describe('should send request with new function signature (3args)', () => { + [ + { name: 'accountIds', args: [{ accountIds }, opt, mockCB] }, + { name: 'emailAddresses', args: [{ emailAddresses }, opt, mockCB] }, + { name: 'canonicalIds', args: [{ canonicalIds }, opt, mockCB] }, + { name: 'accountNames', args: [{ accountNames }, opt, mockCB] }, + ].forEach(({ name, args }) => it(`for ${name}`, () => { + client.getAccounts(...args); + const [method, path, auth, cb, data, reqUid, contentType] = spyArg; + assert.strictEqual(method, 'GET'); + assert.strictEqual(path, '/'); + assert.strictEqual(auth, false); + assert.strictEqual(cb, mockCB); + assert.strictEqual(reqUid, opt.reqUid); + assert.strictEqual(contentType, null); + assert.deepStrictEqual( + data, + Object.assign({}, expectedData, { [name]: Object.values(args[0])[0] }), + ); + })); + }); + + describe('should throw with regular function signature (5args)', () => { + it('for all values set', () => { + assert.throws(() => client.getAccounts(accountIds, emailAddresses, canonicalIds, opt, mockCB), + assert.AssertionError); + }); + }); + + describe('should throw with new function signature (3args)', () => { + it('for all values set', () => { + assert.throws(() => client.getAccounts( + { + accountIds, emailAddresses, canonicalIds, accountNames, + }, + opt, + mockCB, + ), + assert.AssertionError); + }); + + it('for wrong argument type', () => { + assert.throws(() => client.getAccounts(accountIds, opt, mockCB), + assert.AssertionError); + }); + }); +});