Repository: cloudant/nodejs-cloudant (js)

Issue:
"promises" plug-in not functioning when using vcapServices option to initialize Cloudant
Please [read these guidelines](http://ibm.biz/cdt-issue-guide) before opening an issue.

<!-- Issues will be CLOSED IMMEDIATELY if the following template is not completed. -->

## Bug Description
When initializing Cloudant using the vcapServices option, the "promises" plug-in does not seem to function.  When initializing cloudant using url:<Username:Password.bluemix.cloudant.com>, the plugin DOES work.  In both cases, the Cloudant database successfully connects.  
### 1. Steps to reproduce and the simplest code sample possible to demonstrate the issue
The following code DOES work:
```javascript
let cloud_DB = new Cloudant({url:cloudand_url, plugins: 'promises' }, (err, cloudant, reply) => {
appEnv.services }, (err, cloudant, reply) => {
  if (err) {
    throw (err)
    console.log("Error creating cloudant " + err);
  }
    console.log("made DB");
});

cloud_DB.db.use('database_name').info().then((body) => {
  console.log(`got info <from a promise>: ${body}`);
})
```
the following code DOES NOT work:
```javascript
let cloud_DB = new Cloudant({vcapInstanceName:'Cloudant-d2', vcapServices: appEnv.services, plugins: 'promises' }, (err, cloudant, reply) => {
  if (err) {
    throw (err)
    console.log("Error creating cloudant " + err);
  }
    console.log("made DB");
});

cloud_DB.db.use('database_name').info().then((body) => {
  console.log(`got info <from a promise>: ${body}`);
})
```
It is fair to point out that I am passing **appEvn.services** to vcapServices.  This appEvn uses cfenv to get the vcap services or a local copy, if the code is local. 
### 2. What you expected to happen
In both cases, I would expect to see in the console:
```
made DB
got info <from a promise>: [object object]
```
### 3. What actually happened
In the first example, I see what I expect.

In the second example, I see ``made DB`` but then receive a
```TypeError: cloud_DB.db.use(...).info(...).then is not a function ```

**note** that if I make the same call without using promises, it works in both cases.  Example:
```Javascript
cloud_DB.db.use('database_name').info((err, body) => {
    if(err) console.log("DB info Error " + err);
    if(body){
      console.log("DB info " + body);
    }
});
```
## Environment details

- @cloudant/cloudant version
    > 2.4.0
- Node.js version
    > 9.8.0



Pull request:
Fix plugin configuration when instantiating a new client using VCAP
<!--
Thanks for your hard work, please ensure all items are complete before opening.
-->
## Checklist

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](../blob/master/DCO1.1.txt)
- [x] Added tests for code changes _or_ test/build only changes
- [x] Updated the change log file (`CHANGES.md`|`CHANGELOG.md`) _or_ test/build only changes
- [x] Completed the PR template below:

## Description

<!--
Provide a short description; saving the detail for the `Approach` section

Also EITHER:
Link to issue this PR is resolving, use the Fixes #nnn form so that the
issue closes automatically when the PR merges e.g.:

Fixes #23

OR

For PRs without an associated issue and/or test/build issues

### 1. Steps to reproduce and the simplest code sample possible to demonstrate the issue
### 2. What you expected to happen
### 3. What actually happened
-->
Fix plugin configuration when instantiating a new client using VCAP.

Fixes #340.

## Approach

<!--
Be brief: which component(s) of the code base does the fix focus on.

A place to note whether the part of the code base that is being worked is
particularly sensitive.
-->
Always look at the `plugins` array for user specified plugins.

## Schema & API Changes
No change.
<!--
EITHER:

- "No change"

OR

For public API (as opposed to internal) changes

- "Fixing bug in API, will change x in such-and-such way"
-->

## Security and Privacy
No change.
<!--
EITHER:

- "No change"

OR

"Making changes in e.g. auth|https|encryption|io
need to be careful about..."

-->

## Testing
Added additional unit test.
<!--
EITHER:

- Added new tests:
    - test x
    - test y
    - test z

OR

- Modified existing tests because ...

OR

- N/A build or packaging only changes

OR

In exceptional circumstances there may be a good reason we can't add automated
tests, for example if a specific device is required to reproduce a problem.

- No new tests because...
-->

## Monitoring and Logging
No change.
<!--
EITHER:

- "No change"

OR

- "Added new log line X..."
-->


Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 3a70894..2a963c6 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,6 @@
+# UNRELEASED
+- [FIXED] Don't override `plugins` array when instantiating a new client using VCAP.
+
 # 2.4.0 (2018-09-19)
 - [FIXED] Case where `username` and `password` options were not used if a `url` was supplied.
 - [FIXED] Case where `vcapServices` was supplied with a basic-auth `url`.
diff --git a/lib/client.js b/lib/client.js
index 67b34fd..cb725e8 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -45,14 +45,19 @@ class CloudantClient {
     self._usePromises = false;
     self.useLegacyPlugin = false;
 
-    // build plugin array
+    // Build plugin array.
     var plugins = [];
-    // If IAM key found in VCAP, add IAM plugin
+
     if (self._cfg.creds && self._cfg.creds.iamApiKey) {
-      plugins.push({ iamauth: { iamApiKey: self._cfg.creds.iamApiKey } });
-    } else if (typeof this._cfg.plugin === 'undefined' && typeof this._cfg.plugins === 'undefined') {
-      plugins = [ 'cookieauth' ]; // default
-    } else {
+      // => Found IAM API key in VCAP - Add 'iamauth' plugin.
+      plugins = [ { iamauth: { iamApiKey: self._cfg.creds.iamApiKey } } ];
+    } else if (typeof this._cfg.plugins === 'undefined') {
+      // => No plugins specified - Add 'cookieauth' plugin.
+      plugins = [ 'cookieauth' ];
+    }
+
+    // Add user specified plugins.
+    if (typeof this._cfg.plugins !== 'undefined') {
       [].concat(self._cfg.plugins).forEach(function(plugin) {
         if (typeof plugin !== 'function' || plugin.pluginVersion >= 2) {
           plugins.push(plugin);


Test patch:
diff --git a/test/plugins/iamauth.js b/test/plugins/iamauth.js
index 4a0415c..7ff1d71 100644
--- a/test/plugins/iamauth.js
+++ b/test/plugins/iamauth.js
@@ -17,6 +17,7 @@
 
 const assert = require('assert');
 const Client = require('../../lib/client.js');
+const Cloudant = require('../../cloudant.js');
 const nock = require('../nock.js');
 const uuidv4 = require('uuid/v4'); // random
 
@@ -477,4 +478,39 @@ describe('#db IAMAuth Plugin', function() {
       'did not throw with expected message'
     );
   });
+
+  it('supports using vcap with the promise plugin', function(done) {
+    // NOTE: Use NOCK_OFF=true to test using a real CouchDB instance.
+    var iamMocks = nock(TOKEN_SERVER)
+      .post('/identity/token', {
+        'grant_type': 'urn:ibm:params:oauth:grant-type:apikey',
+        'response_type': 'cloud_iam',
+        'apikey': IAM_API_KEY
+      })
+      .reply(200, MOCK_IAM_TOKEN_RESPONSE);
+
+    var cloudantMocks = nock(SERVER)
+      .post('/_iam_session', {access_token: MOCK_ACCESS_TOKEN})
+      .reply(200, {ok: true}, MOCK_SET_IAM_SESSION_HEADER)
+      .get(DBNAME)
+      .reply(200, {doc_count: 0});
+
+    var cloudant = new Cloudant({
+      vcapServices: {
+        cloudantNoSQLDB: [
+          { credentials: { apikey: IAM_API_KEY, host: `${ME}.cloudant.com` } }
+        ]
+      },
+      plugins: 'promises'
+    });
+
+    cloudant.use(DBNAME.substring(1)).info().then((data) => {
+      assert.equal(data.doc_count, 0);
+      iamMocks.done();
+      cloudantMocks.done();
+      done();
+    }).catch(function(err) {
+      assert.fail(`Unexpected reject: ${err}`);
+    });
+  });
 });