This is an automated email from the ASF dual-hosted git repository.

csantanapr pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/incubator-openwhisk-package-cloudant.git


The following commit(s) were added to refs/heads/master by this push:
     new c8c99f9  Add support for IAM based Cloudant DB instances (#184)
c8c99f9 is described below

commit c8c99f9a61bbdadc4410c4b14f377aa998b51a5f
Author: Jason Peterson <[email protected]>
AuthorDate: Tue Nov 20 10:40:54 2018 -0500

    Add support for IAM based Cloudant DB instances (#184)
---
 .gitignore                                         |  1 +
 Dockerfile                                         | 17 +----
 actions/event-actions/changesWebAction.js          | 78 +++++++++++++---------
 actions/event-actions/changesWeb_package.json      |  5 +-
 actions/event-actions/lib/Database.js              |  4 +-
 installCatalog.sh                                  | 11 ++-
 package.json                                       |  6 +-
 provider/app.js                                    | 16 ++---
 provider/lib/utils.js                              | 27 +++++---
 .../scala/system/packages/CloudantFeedTests.scala  |  4 +-
 .../system/packages/CloudantFeedWebTests.scala     |  4 +-
 11 files changed, 94 insertions(+), 79 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4891796..009fe45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ actions/event-actions/*.zip
 actions/event-actions/package.json
 .idea
 *.iml
+package-lock.json
 
 # Eclipse
 bin/
diff --git a/Dockerfile b/Dockerfile
index 2df3aac..e5e8a21 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,17 +1,4 @@
-FROM ubuntu:14.04
-
-ENV DEBIAN_FRONTEND noninteractive
-
-# Initial update and some basics.
-# This odd double update seems necessary to get curl to download without 404 
errors.
-RUN apt-get update --fix-missing && \
-  apt-get install -y wget && \
-  apt-get update && \
-  apt-get install -y curl && \
-  apt-get update && \
-  apt-get remove -y nodejs && \
-  curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
-  apt-get install -y nodejs
+FROM node:8.12.0
 
 # only package.json
 ADD package.json /
@@ -23,4 +10,4 @@ ADD provider/. /cloudantTrigger/
 EXPOSE 8080
 
 # Run the app
-CMD ["/bin/bash", "-c", "node /cloudantTrigger/app.js >> 
/logs/cloudantTrigger_logs.log 2>&1"]
+CMD ["/bin/bash", "-c", "node /cloudantTrigger/app.js"]
diff --git a/actions/event-actions/changesWebAction.js 
b/actions/event-actions/changesWebAction.js
index af2b0e8..1e171ea 100644
--- a/actions/event-actions/changesWebAction.js
+++ b/actions/event-actions/changesWebAction.js
@@ -29,11 +29,10 @@ function main(params) {
         if (!params.host) {
             return common.sendError(400, 'cloudant trigger feed: missing host 
parameter');
         }
-        if (!params.username) {
-            return common.sendError(400, 'cloudant trigger feed: missing 
username parameter');
-        }
-        if (!params.password) {
-            return common.sendError(400, 'cloudant trigger feed: missing 
password parameter');
+        if (!params.iamApiKey) {
+            if (!params.username || !params.password) {
+                return common.sendError(400, 'cloudant trigger feed: Must 
specify parameter/s of iamApiKey or username/password');
+            }
         }
 
         var query_params;
@@ -56,30 +55,34 @@ function main(params) {
             return common.sendError(400, 'The query_params parameter is only 
allowed if the filter parameter is defined');
         }
 
-        var newTrigger = {
-            id: triggerID,
-            host: params.host,
-            port: params.port,
-            protocol: params.protocol || 'https',
-            dbname: params.dbname,
-            user: params.username,
-            pass: params.password,
-            apikey: triggerData.apikey,
-            since: params.since,
-            maxTriggers: params.maxTriggers || -1,
-            filter: params.filter,
-            query_params: query_params,
-            status: {
-                'active': true,
-                'dateChanged': Date.now()
-            },
-            additionalData: triggerData.additionalData
-        };
-
         return new Promise(function (resolve, reject) {
+            var newTrigger;
+
             common.verifyTriggerAuth(triggerData, false)
             .then(() => {
                 db = new Database(params.DB_URL, params.DB_NAME);
+
+                newTrigger = {
+                    id: triggerID,
+                    host: params.host,
+                    port: params.port,
+                    protocol: params.protocol || 'https',
+                    dbname: params.dbname,
+                    user: params.username,
+                    pass: params.password,
+                    apikey: triggerData.apikey,
+                    since: params.since,
+                    maxTriggers: params.maxTriggers || -1,
+                    filter: params.filter,
+                    query_params: query_params,
+                    status: {
+                        'active': true,
+                        'dateChanged': Date.now()
+                    },
+                    additionalData: triggerData.additionalData,
+                    iamApiKey: params.iamApiKey,
+                    iamUrl: params.iamUrl || 
'https://iam.bluemix.net/identity/token'
+                };
                 return verifyUserDB(newTrigger);
             })
             .then(() => {
@@ -124,6 +127,8 @@ function main(params) {
                         since: doc.since,
                         filter: doc.filter,
                         query_params: doc.query_params,
+                        iamApiKey: doc.iamApiKey,
+                        iamUrl: doc.iamUrl
                     },
                     status: {
                         active: doc.status.active,
@@ -242,17 +247,28 @@ function main(params) {
 }
 
 function verifyUserDB(triggerObj) {
-    var dbURL = 
`${triggerObj.protocol}://${triggerObj.user}:${triggerObj.pass}@${triggerObj.host}`;
 
-    // add port if specified
-    if (triggerObj.port) {
-        dbURL += ':' + triggerObj.port;
+    var Cloudant = require('@cloudant/cloudant');
+    var cloudant;
+
+    if (triggerObj.iamApiKey) {
+        var dbURL = `${triggerObj.protocol}://${triggerObj.host}`;
+        if (triggerObj.port) {
+            dbURL += ':' + triggerObj.port;
+        }
+        cloudant = new Cloudant({ url: dbURL, plugins: { iamauth: { iamApiKey: 
triggerObj.iamApiKey, iamTokenUrl: triggerObj.iamUrl } } });
+    }
+    else {
+        var url = 
`${triggerObj.protocol}://${triggerObj.user}:${triggerObj.pass}@${triggerObj.host}`;
+        if (triggerObj.port) {
+            url += ':' + triggerObj.port;
+        }
+        cloudant = Cloudant(url);
     }
 
     return new Promise(function(resolve, reject) {
         try {
-            var nanoConnection = require('nano')(dbURL);
-            var userDB = nanoConnection.use(triggerObj.dbname);
+            var userDB = cloudant.use(triggerObj.dbname);
             userDB.info(function(err, body) {
                 if (!err) {
                     resolve();
diff --git a/actions/event-actions/changesWeb_package.json 
b/actions/event-actions/changesWeb_package.json
index f2b3fc6..5c46d8d 100644
--- a/actions/event-actions/changesWeb_package.json
+++ b/actions/event-actions/changesWeb_package.json
@@ -1,5 +1,8 @@
 {
   "name": "changesWebAction",
   "version": "1.0.0",
-  "main": "changesWebAction.js"
+  "main": "changesWebAction.js",
+  "dependencies" : {
+    "@cloudant/cloudant": "3.0.0"
+  }
 }
diff --git a/actions/event-actions/lib/Database.js 
b/actions/event-actions/lib/Database.js
index 962b2df..9bc180d 100644
--- a/actions/event-actions/lib/Database.js
+++ b/actions/event-actions/lib/Database.js
@@ -2,8 +2,8 @@ const common = require('./common');
 
 // constructor for DB object - a thin, promise-loving wrapper around nano
 module.exports = function(dbURL, dbName) {
-    var nano = require('nano')(dbURL);
-    this.db = nano.db.use(dbName);
+    var cloudant = require('@cloudant/cloudant')(dbURL);
+    this.db = cloudant.db.use(dbName);
     var utilsDB = this;
 
     this.getWorkerID = function(availabeWorkers) {
diff --git a/installCatalog.sh b/installCatalog.sh
index 7ce6d5a..7787ec7 100755
--- a/installCatalog.sh
+++ b/installCatalog.sh
@@ -51,12 +51,8 @@ echo Installing Cloudant package.
 
 $WSK_CLI -i --apihost "$EDGEHOST" package update --auth "$AUTH" --shared yes 
cloudant \
     -a description "Cloudant database service" \
-    -a parameters '[ {"name":"bluemixServiceName", "required":false, 
"bindTime":true}, {"name":"username", "required":true, "bindTime":true, 
"description": "Your Cloudant username"}, {"name":"password", "required":true, 
"type":"password", "bindTime":true, "description": "Your Cloudant password"}, 
{"name":"host", "required":true, "bindTime":true, "description": "This is 
usually your username.cloudant.com"}, {"name":"dbname", "required":false, 
"description": "The name of your Cloudant data [...]
+    -a parameters '[  {"name":"bluemixServiceName", "required":false, 
"bindTime":true}, {"name":"username", "required":false, "bindTime":true, 
"description": "Your Cloudant username"}, {"name":"password", "required":false, 
"type":"password", "bindTime":true, "description": "Your Cloudant password"}, 
{"name":"host", "required":true, "bindTime":true, "description": "This is 
usually your username.cloudant.com"}, {"name":"iamApiKey", "required":false}, 
{"name":"iamUrl", "required":false}, {" [...]
     -p bluemixServiceName 'cloudantNoSQLDB' \
-    -p host '' \
-    -p username '' \
-    -p password '' \
-    -p dbname '' \
     -p apihost "$APIHOST"
 
 # make changesFeed.zip
@@ -73,7 +69,7 @@ $WSK_CLI -i --apihost "$EDGEHOST" action update --kind 
"$ACTION_RUNTIME_VERSION"
     -t 90000 \
     -a feed true \
     -a description 'Database change feed' \
-    -a parameters '[ {"name":"dbname", "required":true, "updatable":false}, 
{"name": "filter", "required":false, "updatable":true, "type": "string", 
"description": "The name of your Cloudant database filter"}, {"name": 
"query_params", "required":false, "updatable":true, "description": "JSON Object 
containing query parameters that are passed to the filter"} ]' \
+    -a parameters '[ {"name":"dbname", "required":true, "updatable":false}, 
{"name":"iamApiKey", "required":false, "updatable":false}, {"name":"iamUrl", 
"required":false, "updatable":false}, {"name": "filter", "required":false, 
"updatable":true, "type": "string", "description": "The name of your Cloudant 
database filter"}, {"name": "query_params", "required":false, "updatable":true, 
"description": "JSON Object containing query parameters that are passed to the 
filter"} ]' \
     -a sampleInput '{ "dbname": "mydb", "filter": "mailbox/by_status", 
"query_params": {"status": "new"} }'
 
 COMMAND=" -i --apihost $EDGEHOST package update --auth $AUTH --shared no 
cloudantWeb \
@@ -89,12 +85,13 @@ $WSK_CLI $COMMAND
 
 # make changesWebAction.zip
 cp -f changesWeb_package.json package.json
+npm install
 
 if [ -e changesWebAction.zip ]; then
     rm -rf changesWebAction.zip
 fi
 
-zip -r changesWebAction.zip lib package.json changesWebAction.js
+zip -r changesWebAction.zip lib package.json changesWebAction.js node_modules
 
 $WSK_CLI -i --apihost "$EDGEHOST" action update --kind 
"$ACTION_RUNTIME_VERSION" --auth "$AUTH" cloudantWeb/changesWebAction 
"$PACKAGE_HOME/actions/event-actions/changesWebAction.zip" \
     -a description 'Create/Delete a trigger in cloudant provider Database' \
diff --git a/package.json b/package.json
index 2b8ecb0..6214f12 100644
--- a/package.json
+++ b/package.json
@@ -12,12 +12,12 @@
     "moment": "^2.11.1",
     "lodash": "^3.10.1",
     "request": "^2.83.0",
-    "cloudant-nano": "6.7.0",
+    "@cloudant/cloudant": "3.0.0",
     "json-stringify-safe": "^5.0.1",
     "http-status-codes": "^1.0.5",
     "request-promise": "^1.0.2",
-    "redis":"^2.7.1",
+    "redis": "^2.7.1",
     "bluebird": "^3.5.0",
     "systeminformation": "^3.19.0"
   }
-}
\ No newline at end of file
+}
diff --git a/provider/app.js b/provider/app.js
index fc9149d..2488931 100644
--- a/provider/app.js
+++ b/provider/app.js
@@ -49,11 +49,11 @@ function createDatabase() {
     var method = 'createDatabase';
     logger.info(method, 'creating the trigger database');
 
-    var nano = require('cloudant-nano')(dbProtocol + '://' + dbUsername + ':' 
+ dbPassword + '@' + dbHost);
+    var cloudant = require('@cloudant/cloudant')(dbProtocol + '://' + 
dbUsername + ':' + dbPassword + '@' + dbHost);
 
-    if (nano !== null) {
+    if (cloudant !== null) {
         return new Promise(function (resolve, reject) {
-            nano.db.create(databaseName, function (err, body) {
+            cloudant.db.create(databaseName, function (err, body) {
                 if (!err) {
                     logger.info(method, 'created trigger database:', 
databaseName);
                 }
@@ -74,7 +74,7 @@ function createDatabase() {
                     }
                 };
 
-                createDesignDoc(nano.db.use(databaseName), viewDDName, viewDD)
+                createDesignDoc(cloudant.db.use(databaseName), viewDDName, 
viewDD)
                 .then(db => {
                     var filterDD = {
                         filters: {
@@ -114,7 +114,7 @@ function createDatabase() {
         });
     }
     else {
-        Promise.reject('nano provider did not get created.  check db URL: ' + 
dbHost);
+        Promise.reject('cloudant provider did not get created.  check db URL: 
' + dbHost);
     }
 }
 
@@ -181,7 +181,7 @@ function createRedisClient() {
 // Initialize the Provider Server
 function init(server) {
     var method = 'init';
-    var nanoDb;
+    var cloudantDb;
     var providerUtils;
 
     if (server !== null) {
@@ -194,11 +194,11 @@ function init(server) {
 
     createDatabase()
     .then(db => {
-        nanoDb = db;
+        cloudantDb = db;
         return createRedisClient();
     })
     .then(client => {
-        providerUtils = new ProviderUtils(logger, nanoDb, client);
+        providerUtils = new ProviderUtils(logger, cloudantDb, client);
         return providerUtils.initRedis();
     })
     .then(() => {
diff --git a/provider/lib/utils.js b/provider/lib/utils.js
index 4f08530..aa12c6d 100644
--- a/provider/lib/utils.js
+++ b/provider/lib/utils.js
@@ -30,17 +30,26 @@ module.exports = function(logger, triggerDB, redisClient) {
     this.createTrigger = function(triggerData) {
         var method = 'createTrigger';
 
-        // both couch and cloudant should have their URLs in the 
username:password@host format
-        var dbURL = 
`${triggerData.protocol}://${triggerData.user}:${triggerData.pass}@${triggerData.host}`;
+        var Cloudant = require('@cloudant/cloudant');
+        var cloudantConnection;
 
-        // add port if specified
-        if (triggerData.port) {
-            dbURL += ':' + triggerData.port;
+        if (triggerData.iamApiKey) {
+            var dbURL = `${triggerData.protocol}://${triggerData.host}`;
+            if (triggerData.port) {
+                dbURL += ':' + triggerData.port;
+            }
+            cloudantConnection = new Cloudant({ url: dbURL, plugins: { 
iamauth: { iamApiKey: triggerData.iamApiKey, iamTokenUrl: triggerData.iamUrl } 
} });
+        }
+        else {
+            var url = 
`${triggerData.protocol}://${triggerData.user}:${triggerData.pass}@${triggerData.host}`;
+            if (triggerData.port) {
+                url += ':' + triggerData.port;
+            }
+            cloudantConnection = Cloudant(url);
         }
 
         try {
-            var nanoConnection = require('cloudant-nano')(dbURL);
-            var triggeredDB = nanoConnection.use(triggerData.dbname);
+            var triggeredDB = cloudantConnection.use(triggerData.dbname);
 
             // Listen for changes on this database.
             var feed = triggeredDB.follow({since: triggerData.since, 
include_docs: false});
@@ -107,7 +116,9 @@ module.exports = function(logger, triggerDB, redisClient) {
             triggersLeft: maxTriggers,
             filter: newTrigger.filter,
             query_params: newTrigger.query_params,
-            additionalData: newTrigger.additionalData
+            additionalData: newTrigger.additionalData,
+            iamApiKey: newTrigger.iamApiKey,
+            iamUrl: newTrigger.iamUrl
         };
 
         return trigger;
diff --git a/tests/src/test/scala/system/packages/CloudantFeedTests.scala 
b/tests/src/test/scala/system/packages/CloudantFeedTests.scala
index 00c370a..7e70d8c 100644
--- a/tests/src/test/scala/system/packages/CloudantFeedTests.scala
+++ b/tests/src/test/scala/system/packages/CloudantFeedTests.scala
@@ -132,7 +132,7 @@ class CloudantFeedTests
                         "host" -> myCloudantCreds.host().toJson),
                         expectedExitCode = 246)
             }
-            feedCreationResult.stderr should include("cloudant trigger feed: 
missing password parameter")
+            feedCreationResult.stderr should include("cloudant trigger feed: 
Must specify parameter/s of iamApiKey or username/password")
 
     }
 
@@ -163,7 +163,7 @@ class CloudantFeedTests
                         "host" -> myCloudantCreds.host().toJson),
                         expectedExitCode = 246)
             }
-            feedCreationResult.stderr should include("cloudant trigger feed: 
missing username parameter")
+            feedCreationResult.stderr should include("cloudant trigger feed: 
Must specify parameter/s of iamApiKey or username/password")
 
     }
 
diff --git a/tests/src/test/scala/system/packages/CloudantFeedWebTests.scala 
b/tests/src/test/scala/system/packages/CloudantFeedWebTests.scala
index da9d518..499e61a 100644
--- a/tests/src/test/scala/system/packages/CloudantFeedWebTests.scala
+++ b/tests/src/test/scala/system/packages/CloudantFeedWebTests.scala
@@ -70,13 +70,13 @@ class CloudantFeedWebTests
     it should "reject post of a trigger due to missing username argument" in {
         val params = JsObject(requiredParams.fields - "username")
 
-        makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("cloudant trigger feed: missing username parameter")), 400)
+        makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("cloudant trigger feed: Must specify parameter/s of iamApiKey or 
username/password")), 400)
     }
 
     it should "reject post of a trigger due to missing password argument" in {
         val params = JsObject(requiredParams.fields - "password")
 
-        makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("cloudant trigger feed: missing password parameter")), 400)
+        makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("cloudant trigger feed: Must specify parameter/s of iamApiKey or 
username/password")), 400)
     }
 
     it should "reject post of a trigger due to missing dbname argument" in {

Reply via email to