csantanapr closed pull request #113: Add interval support
URL: https://github.com/apache/incubator-openwhisk-package-alarms/pull/113
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/README.md b/README.md
index 33f383c..2b0d0d5 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,10 @@ The package includes the following feeds.
 | `/whisk.system/alarms` | package | - | Alarms and periodic utility |
 | `/whisk.system/alarms/alarm` | feed | cron, trigger_payload, maxTriggers, 
startDate, stopDate | Fire trigger event periodically |
 | `/whisk.system/alarms/once` | feed | date, trigger_payload | Fire trigger 
event once on a specific date |
+| `/whisk.system/alarms/interval` | feed | minutes, trigger_payload, 
startDate, stopDate | Fire trigger event on an interval based schedule |
 
 
-## Firing a trigger event periodically
+## Firing a trigger event periodically on a time based schedule
 
 The `/whisk.system/alarms/alarm` feed configures the Alarm service to fire a 
trigger event at a specified frequency. The parameters are as follows:
 
@@ -57,6 +58,35 @@ January 1, 2019, 00:00:00 UTC and will stop firing January 
31, 2019, 23:59:00 UT
 
 Each generated event will include as parameters the properties specified in 
the `trigger_payload` value. In this case, each trigger event will have 
parameters `name=Odin` and `place=Asgard`.
 
+
+## Firing a trigger event periodically on an interval based schedule
+
+The `/whisk.system/alarms/interval` feed configures the Alarm service to fire 
a trigger event on an interval based schedule. The parameters are as follows:
+
+- `minutes`: An integer representing the length of the interval (in minutes) 
between trigger fires.
+
+- `trigger_payload`: The value of this parameter becomes the content of the 
trigger every time the trigger is fired.
+
+- `startDate`: The date when the first trigger will be fired.  Subsequent 
fires will occur based on the interval length specified by the `minutes` 
parameter.   
+
+- `stopDate`: The date when the trigger will stop running.  Triggers will no 
longer be fired once this date has been reached.
+
+  **Note**: The `startDate` and `stopDate` parameters support an integer or 
string value.  The integer value represents the number of milliseconds 
+  since 1 January 1970 00:00:00 UTC and the string value should be in the ISO 
8601 format (http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15).
+
+
+The following is an example of creating a trigger that will be fired once 
every 90 minutes.  The trigger will not start firing until
+January 1, 2019, 00:00:00 UTC and will stop firing January 31, 2019, 23:59:00 
UTC.
+
+  ```
+  wsk trigger create interval \
+    --feed /whisk.system/alarms/interval \
+    --param minutes 90 \
+    --param trigger_payload "{\"name\":\"Odin\",\"place\":\"Asgard\"}" \
+    --param startDate "2019-01-01T00:00:00.000Z" \
+    --param stopDate "2019-01-31T23:59:00.000Z"
+  ```
+
 ## Firing a trigger event once  
 
 The `/whisk.system/alarms/once` feed configures the Alarm service to fire a 
trigger event on a specified date. The parameters are as follows:
diff --git a/action/alarmWebAction.js b/action/alarmWebAction.js
index 9f8789c..7c620c6 100644
--- a/action/alarmWebAction.js
+++ b/action/alarmWebAction.js
@@ -49,16 +49,33 @@ function main(params) {
             newTrigger.date = date;
         }
         else {
-            if (!params.cron) {
-                return common.sendError(400, 'alarms trigger feed is missing 
the cron parameter');
+            var cronHandle;
+
+            if (params.isInterval) {
+                if (!params.minutes) {
+                    return common.sendError(400, 'interval trigger feed is 
missing the minutes parameter');
+                }
+                if (+params.minutes !== parseInt(params.minutes)) {
+                    return common.sendError(400, 'the minutes parameter must 
be an integer');
+                }
+                var minutesParam = parseInt(params.minutes);
+
+                if (minutesParam <= 0) {
+                    return common.sendError(400, 'the minutes parameter must 
be an integer greater than zero');
+                }
+                newTrigger.minutes = minutesParam;
             }
+            else {
+                if (!params.cron) {
+                    return common.sendError(400, 'alarms trigger feed is 
missing the cron parameter');
+                }
 
-            var cronHandle;
-            try {
-                cronHandle = new CronJob(params.cron, function() {});
-                newTrigger.cron = params.cron;
-            } catch(ex) {
-                return common.sendError(400, `cron pattern '${params.cron}' is 
not valid`);
+                try {
+                    cronHandle = new CronJob(params.cron, function() {});
+                    newTrigger.cron = params.cron;
+                } catch(ex) {
+                    return common.sendError(400, `cron pattern 
'${params.cron}' is not valid`);
+                }
             }
 
             if (params.startDate) {
@@ -68,23 +85,31 @@ function main(params) {
                 }
                 newTrigger.startDate = startDate;
             }
+            else if (params.isInterval) {
+                //if startDate was not given we will start it 30 seconds
+                //from now since startDate must be in the future
+                newTrigger.startDate = Date.now() + (1000 * 30);
+            }
 
-            if (params.stopDate) {
-                if (params.maxTriggers) {
+            if (params.maxTriggers && params.stopDate) {
+                if (params.isInterval) {
+                    return common.sendError(400, 'maxTriggers is not supported 
for the interval trigger feed');
+                }
+                else {
                     return common.sendError(400, 'maxTriggers is not allowed 
when the stopDate parameter is specified');
                 }
-
-                var stopDate = validateDate(params.stopDate, 'stopDate', 
params.startDate);
+            }
+            else if (params.stopDate) {
+                var stopDate = validateDate(params.stopDate, 'stopDate', 
newTrigger.startDate);
                 if (stopDate !== params.stopDate) {
                     return common.sendError(400, stopDate);
                 }
-                //verify that the next scheduled trigger fire will occur 
before the stop date
-                var triggerDate = cronHandle.nextDate();
-                if (triggerDate.isAfter(new Date(params.stopDate))) {
-                    return common.sendError(400, 'the next scheduled trigger 
fire is not until after the stop date');
-                }
-
                 newTrigger.stopDate = stopDate;
+
+                //verify that the first scheduled trigger fire will occur 
before the stop date
+                if (cronHandle && cronHandle.nextDate().isAfter(new 
Date(params.stopDate))) {
+                    return common.sendError(400, 'the first scheduled trigger 
fire is not until after the stop date');
+                }
             }
         }
 
@@ -137,9 +162,14 @@ function main(params) {
                     body.config.date = doc.date;
                 }
                 else {
-                    body.config.cron = doc.cron;
                     body.config.startDate = doc.startDate;
                     body.config.stopDate = doc.stopDate;
+                    if (doc.minutes) {
+                        body.config.minutes = doc.minutes;
+                    }
+                    else {
+                        body.config.cron = doc.cron;
+                    }
                 }
                 resolve({
                     statusCode: 200,
@@ -158,7 +188,7 @@ function main(params) {
             common.verifyTriggerAuth(triggerURL, params.authKey, true)
             .then(() => {
                 db = new Database(params.DB_URL, params.DB_NAME);
-                return db.updateTrigger(triggerID, 0);
+                return db.disableTrigger(triggerID, 0);
             })
             .then(id => {
                 return db.deleteTrigger(id, 0);
diff --git a/action/lib/Database.js b/action/lib/Database.js
index e1fdb27..a878b85 100644
--- a/action/lib/Database.js
+++ b/action/lib/Database.js
@@ -85,7 +85,7 @@ module.exports = function(dbURL, dbName) {
         });
     };
 
-    this.updateTrigger = function(triggerID, retryCount) {
+    this.disableTrigger = function(triggerID, retryCount) {
 
         return new Promise(function(resolve, reject) {
 
@@ -98,7 +98,7 @@ module.exports = function(dbURL, dbName) {
                         if (err) {
                             if (err.statusCode === 409 && retryCount < 5) {
                                 setTimeout(function () {
-                                    utilsDB.updateTrigger(triggerID, 
(retryCount + 1))
+                                    utilsDB.disableTrigger(triggerID, 
(retryCount + 1))
                                     .then(id => {
                                         resolve(id);
                                     })
@@ -121,7 +121,7 @@ module.exports = function(dbURL, dbName) {
                     if (retryCount === 0) {
                         var parts = triggerID.split('/');
                         var id = parts[0] + '/_/' + parts[2];
-                        utilsDB.updateTrigger(id, (retryCount + 1))
+                        utilsDB.disableTrigger(id, (retryCount + 1))
                         .then(id => {
                             resolve(id);
                         })
diff --git a/installCatalog.sh b/installCatalog.sh
index 3b34e49..ddc9337 100755
--- a/installCatalog.sh
+++ b/installCatalog.sh
@@ -76,6 +76,12 @@ $WSK_CLI -i --apihost "$EDGEHOST" action update --kind 
nodejs:6 --auth "$AUTH" a
      -a feed true \
      -p fireOnce true
 
+$WSK_CLI -i --apihost "$EDGEHOST" action update --kind nodejs:6 --auth "$AUTH" 
alarms/interval "$PACKAGE_HOME/action/alarmFeed.zip" \
+     -a description 'Fire trigger at specified interval' \
+     -a parameters '[ {"name":"minutes", "required":true}, 
{"name":"startDate", "required":false}, {"name":"stopDate", "required":false} 
]' \
+     -a feed true \
+     -p isInterval true
+
 if [ -n "$WORKERS" ];
 then
     $WSK_CLI -i --apihost "$EDGEHOST" package update --auth "$AUTH" --shared 
no alarmsWeb \
diff --git a/provider/lib/cronAlarm.js b/provider/lib/cronAlarm.js
index 98b691b..f60ec62 100644
--- a/provider/lib/cronAlarm.js
+++ b/provider/lib/cronAlarm.js
@@ -10,6 +10,7 @@ module.exports = function(logger, newTrigger) {
         apikey: newTrigger.apikey,
         name: newTrigger.name,
         namespace: newTrigger.namespace,
+        payload: newTrigger.payload,
         cron: newTrigger.cron
     };
 
diff --git a/provider/lib/dateAlarm.js b/provider/lib/dateAlarm.js
index 04d2ec0..011261d 100644
--- a/provider/lib/dateAlarm.js
+++ b/provider/lib/dateAlarm.js
@@ -6,6 +6,7 @@ module.exports = function(logger, newTrigger) {
         apikey: newTrigger.apikey,
         name: newTrigger.name,
         namespace: newTrigger.namespace,
+        payload: newTrigger.payload,
         date: newTrigger.date
     };
 
diff --git a/provider/lib/intervalAlarm.js b/provider/lib/intervalAlarm.js
new file mode 100644
index 0000000..077a68b
--- /dev/null
+++ b/provider/lib/intervalAlarm.js
@@ -0,0 +1,66 @@
+var lt =  require('long-timeout');
+
+module.exports = function(logger, newTrigger) {
+
+
+    var cachedTrigger = {
+        apikey: newTrigger.apikey,
+        name: newTrigger.name,
+        namespace: newTrigger.namespace,
+        payload: newTrigger.payload,
+        minutes: newTrigger.minutes
+    };
+
+    this.scheduleAlarm = function(triggerIdentifier, callback) {
+        var method = 'scheduleIntervalAlarm';
+
+        try {
+            return new Promise(function(resolve, reject) {
+
+                var intervalInMilliSeconds = newTrigger.minutes * 1000 * 60;
+                var startDate = new Date(newTrigger.startDate).getTime();
+
+                if (newTrigger.stopDate) {
+                    cachedTrigger.stopDate = newTrigger.stopDate;
+                    //do not create trigger if the stopDate is in the past
+                    if (new Date(newTrigger.stopDate).getTime() <= Date.now()) 
{
+                        return reject('the stop date has expired');
+                    }
+                }
+
+                if (startDate > Date.now()) {
+                    //fire the trigger and start the interval on the start date
+                    logger.info(method, triggerIdentifier, 'waiting for start 
date', startDate);
+                    lt.setTimeout(function() {
+                        logger.info(method, triggerIdentifier, 'firing first 
trigger and starting interval upon reaching start date', startDate);
+                        var intervalHandle = lt.setInterval(callback, 
intervalInMilliSeconds);
+                        cachedTrigger.intervalHandle = intervalHandle;
+                        resolve(cachedTrigger);
+                    }, startDate - Date.now());
+                }
+                else {
+                    //fire the trigger and start the interval at the next 
scheduled interval
+                    //as long as the next scheduled interval is not past the 
stop date
+                    var intervalsFired = Math.floor((Date.now() - 
startDate)/intervalInMilliSeconds);
+                    var nextScheduledInterval = startDate + 
(intervalInMilliSeconds * (intervalsFired + 1));
+
+                    if (newTrigger.stopDate && nextScheduledInterval > new 
Date(newTrigger.stopDate).getTime()) {
+                        return reject('the next scheduled trigger fire is 
after the stop date');
+                    }
+
+                    logger.info(method, triggerIdentifier, 'waiting for next 
interval');
+                    lt.setTimeout(function() {
+                        logger.info(method, triggerIdentifier, 'firing trigger 
and starting interval for trigger past its start date');
+                        var intervalHandle = lt.setInterval(callback, 
intervalInMilliSeconds);
+                        cachedTrigger.intervalHandle = intervalHandle;
+                        resolve(cachedTrigger);
+                    }, nextScheduledInterval - Date.now());
+                }
+
+            });
+        } catch (err) {
+            return Promise.reject(err);
+        }
+    };
+
+};
diff --git a/provider/lib/utils.js b/provider/lib/utils.js
index 8c7040b..a8f3153 100644
--- a/provider/lib/utils.js
+++ b/provider/lib/utils.js
@@ -1,8 +1,10 @@
 var request = require('request');
 var HttpStatus = require('http-status-codes');
+var lt =  require('long-timeout');
 var constants = require('./constants.js');
 var DateAlarm = require('./dateAlarm.js');
 var CronAlarm = require('./cronAlarm.js');
+var IntervalAlarm = require('./intervalAlarm.js');
 
 module.exports = function(
   logger,
@@ -36,7 +38,7 @@ module.exports = function(
                 var triggerHandle = utils.triggers[triggerIdentifier];
                 if (triggerHandle && (!triggerHandle.maxTriggers || 
triggerHandle.maxTriggers === -1 || triggerHandle.triggersLeft > 0)) {
                     try {
-                        utils.fireTrigger(newTrigger.namespace, 
newTrigger.name, newTrigger.payload, newTrigger.apikey);
+                        utils.fireTrigger(triggerHandle);
                     } catch (e) {
                         logger.error(method, 'Exception occurred while firing 
trigger', triggerIdentifier, e);
                     }
@@ -48,6 +50,9 @@ module.exports = function(
         if (newTrigger.date) {
             alarm = new DateAlarm(logger, newTrigger);
         }
+        else if (newTrigger.minutes) {
+            alarm = new IntervalAlarm(logger, newTrigger);
+        }
         else {
             alarm = new CronAlarm(logger, newTrigger);
         }
@@ -55,17 +60,16 @@ module.exports = function(
         return alarm.scheduleAlarm(triggerIdentifier, callback);
     };
 
-    this.fireTrigger = function(namespace, name, payload, apikey) {
+    this.fireTrigger = function(dataTrigger) {
         var method = 'fireTrigger';
 
-        var triggerIdentifier = utils.getTriggerIdentifier(apikey, namespace, 
name);
+        var triggerIdentifier = utils.getTriggerIdentifier(dataTrigger.apikey, 
dataTrigger.namespace, dataTrigger.name);
         var host = 'https://' + utils.routerHost + ':443';
-        var auth = apikey.split(':');
-        var dataTrigger = utils.triggers[triggerIdentifier];
-        var uri = host + '/api/v1/namespaces/' + namespace + '/triggers/' + 
name;
+        var auth = dataTrigger.apikey.split(':');
+        var uri = host + '/api/v1/namespaces/' + dataTrigger.namespace + 
'/triggers/' + dataTrigger.name;
 
-        logger.info(method, 'Cron fired for', triggerIdentifier, 'attempting 
to fire trigger');
-        utils.postTrigger(dataTrigger, payload, uri, auth, 0)
+        logger.info(method, 'Alarm fired for', triggerIdentifier, 'attempting 
to fire trigger');
+        utils.postTrigger(dataTrigger, uri, auth, 0)
         .then(triggerId => {
             logger.info(method, 'Trigger', triggerId, 'was successfully 
fired');
             utils.disableExtinctTriggers(triggerIdentifier, dataTrigger);
@@ -76,7 +80,7 @@ module.exports = function(
         });
     };
 
-    this.postTrigger = function(dataTrigger, payload, uri, auth, retryCount) {
+    this.postTrigger = function(dataTrigger, uri, auth, retryCount) {
         var method = 'postTrigger';
 
         return new Promise(function(resolve, reject) {
@@ -93,7 +97,7 @@ module.exports = function(
                     user: auth[0],
                     pass: auth[1]
                 },
-                json: payload
+                json: dataTrigger.payload
             }, function(error, response) {
                 try {
                     var triggerIdentifier = 
utils.getTriggerIdentifier(dataTrigger.apikey, dataTrigger.namespace, 
dataTrigger.name);
@@ -115,7 +119,7 @@ module.exports = function(
                             if (retryCount < retryAttempts) {
                                 logger.info(method, 'attempting to fire 
trigger again', triggerIdentifier, 'Retry Count:', (retryCount + 1));
                                 setTimeout(function () {
-                                    utils.postTrigger(dataTrigger, payload, 
uri, auth, (retryCount + 1))
+                                    utils.postTrigger(dataTrigger, uri, auth, 
(retryCount + 1))
                                     .then(triggerId => {
                                         resolve(triggerId);
                                     })
@@ -154,8 +158,12 @@ module.exports = function(
         else if (dataTrigger.stopDate) {
             //check if the next scheduled trigger is after the stop date
             if (dataTrigger.cronHandle && 
dataTrigger.cronHandle.nextDate().isAfter(new Date(dataTrigger.stopDate))) {
-                utils.disableTrigger(triggerIdentifier, undefined, 
'Automatically disabled after firing last scheduled trigger');
-                logger.info(method, 'last scheduled trigger before stop date, 
disabled', triggerIdentifier);
+                utils.disableTrigger(triggerIdentifier, undefined, 
'Automatically disabled after firing last scheduled cron trigger');
+                logger.info(method, 'last scheduled cron trigger before stop 
date, disabled', triggerIdentifier);
+            }
+            else if (dataTrigger.minutes && (Date.now() + (dataTrigger.minutes 
* 1000 * 60) > new Date(dataTrigger.stopDate).getTime())) {
+                utils.disableTrigger(triggerIdentifier, undefined, 
'Automatically disabled after firing last scheduled interval trigger');
+                logger.info(method, 'last scheduled interval trigger before 
stop date, disabled', triggerIdentifier);
             }
         }
         else if (dataTrigger.maxTriggers && dataTrigger.triggersLeft === 0) {
@@ -204,6 +212,9 @@ module.exports = function(
             if (utils.triggers[triggerIdentifier].cronHandle) {
                 utils.triggers[triggerIdentifier].cronHandle.stop();
             }
+            else if (utils.triggers[triggerIdentifier].intervalHandle) {
+                
lt.clearInterval(utils.triggers[triggerIdentifier].intervalHandle);
+            }
             delete utils.triggers[triggerIdentifier];
             logger.info(method, 'trigger', triggerIdentifier, 'successfully 
deleted from memory');
         }
@@ -255,6 +266,13 @@ module.exports = function(
                                 .then(cachedTrigger => {
                                     utils.triggers[triggerIdentifier] = 
cachedTrigger;
                                     logger.info(method, triggerIdentifier, 
'created successfully');
+                                    if (cachedTrigger.intervalHandle && 
utils.activeHost === utils.host) {
+                                        try {
+                                            utils.fireTrigger(cachedTrigger);
+                                        } catch (e) {
+                                            logger.error(method, 'Exception 
occurred while firing trigger', triggerIdentifier, e);
+                                        }
+                                    }
                                 })
                                 .catch(err => {
                                     var message = 'Automatically disabled 
after receiving error on trigger initialization: ' + err;
@@ -300,6 +318,13 @@ module.exports = function(
                         .then(cachedTrigger => {
                             utils.triggers[triggerIdentifier] = cachedTrigger;
                             logger.info(method, triggerIdentifier, 'created 
successfully');
+                            if (cachedTrigger.intervalHandle && 
utils.activeHost === utils.host) {
+                                try {
+                                    utils.fireTrigger(cachedTrigger);
+                                } catch (e) {
+                                    logger.error(method, 'Exception occurred 
while firing trigger', triggerIdentifier, e);
+                                }
+                            }
                         })
                         .catch(err => {
                             var message = 'Automatically disabled after 
receiving error on trigger creation: ' + err;
diff --git a/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala 
b/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala
index fb7c6d8..da901e8 100644
--- a/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala
+++ b/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala
@@ -22,7 +22,7 @@ import common.{TestHelpers, Wsk, WskProps, WskTestHelpers}
 import org.junit.runner.RunWith
 import org.scalatest.{FlatSpec, Inside}
 import org.scalatest.junit.JUnitRunner
-import spray.json.DefaultJsonProtocol.{LongJsonFormat, StringJsonFormat, 
BooleanJsonFormat}
+import spray.json.DefaultJsonProtocol.{IntJsonFormat, LongJsonFormat, 
StringJsonFormat, BooleanJsonFormat}
 import spray.json.{JsObject, JsString, pimpAny}
 
 /**
@@ -230,4 +230,45 @@ class AlarmsHealthFeedTests
             println("Activation list after wait should equal with activation 
list after stopDate")
             activationsAfterWait should be(activations)
     }
+
+    it should "fire interval trigger using startDate and stopDate" in 
withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            implicit val wskprops = wp // shadow global props and make implicit
+            val triggerName = s"dummyAlarmsTrigger-${System.currentTimeMillis}"
+            val packageName = "dummyAlarmsPackage"
+
+            // the package alarms should be there
+            val packageGetResult = wsk.pkg.get("/whisk.system/alarms")
+            println("fetched package alarms")
+            packageGetResult.stdout should include("ok")
+
+            // create package binding
+            assetHelper.withCleaner(wsk.pkg, packageName) {
+                (pkg, name) => pkg.bind("/whisk.system/alarms", name)
+            }
+
+            val startDate = System.currentTimeMillis + (1000 * 20)
+            val stopDate = startDate + (1000 * 90)
+
+            println(s"Creating trigger: $triggerName")
+            // create whisk stuff
+            val feedCreationResult = assetHelper.withCleaner(wsk.trigger, 
triggerName) {
+                (trigger, name) =>
+                    trigger.create(name, feed = 
Some(s"$packageName/interval"), parameters = Map(
+                        "minutes" -> 1.toJson,
+                        "startDate" -> startDate.toJson,
+                        "stopDate" -> stopDate.toJson))
+            }
+            feedCreationResult.stdout should include("ok")
+
+            println("waiting for start date")
+            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), 
retries = 45).length
+            println(s"Found activation size (should be 1): $activations")
+            activations should be(1)
+
+            println("waiting for interval")
+            val activationsAfterInterval = wsk.activation.pollFor(N = 2, 
Some(triggerName), retries = 90).length
+            println(s"Found activation size (should be 2): 
$activationsAfterInterval")
+            activationsAfterInterval should be(2)
+    }
 }
diff --git a/tests/src/test/scala/system/packages/AlarmsFeedTests.scala 
b/tests/src/test/scala/system/packages/AlarmsFeedTests.scala
index 2ad09c7..876b070 100644
--- a/tests/src/test/scala/system/packages/AlarmsFeedTests.scala
+++ b/tests/src/test/scala/system/packages/AlarmsFeedTests.scala
@@ -290,4 +290,63 @@ class AlarmsFeedTests
             feedCreationResult.stderr should include(s"stopDate parameter 
'${stopDate}' must be greater than the startDate")
 
     }
+
+    it should "return error message when interval action does not include 
minutes parameter" in withAssetCleaner(wskprops) {
+
+        (wp, assetHelper) =>
+            implicit val wskprops = wp // shadow global props and make implicit
+            val triggerName = 
s"dummyCloudantTrigger-${System.currentTimeMillis}"
+            val packageName = "dummyCloudantPackage"
+            val feed = "interval"
+
+            // the package alarms should be there
+            val packageGetResult = wsk.pkg.get("/whisk.system/alarms")
+            println("fetched package alarms")
+            packageGetResult.stdout should include("ok")
+
+            // create package binding
+            assetHelper.withCleaner(wsk.pkg, packageName) {
+                (pkg, name) => pkg.bind("/whisk.system/alarms", name)
+            }
+
+            // create whisk stuff
+            val feedCreationResult = assetHelper.withCleaner(wsk.trigger, 
triggerName, confirmDelete = false) {
+                (trigger, name) =>
+                    trigger.create(name, feed = Some(s"$packageName/$feed"), 
parameters = Map(
+                        "trigger_payload" -> "alarmTest".toJson),
+                        expectedExitCode = 246)
+            }
+            feedCreationResult.stderr should include("interval trigger feed is 
missing the minutes parameter")
+
+    }
+
+    it should "return error message when interval action includes invalid 
minutes parameter" in withAssetCleaner(wskprops) {
+
+        (wp, assetHelper) =>
+            implicit val wskprops = wp // shadow global props and make implicit
+            val triggerName = 
s"dummyCloudantTrigger-${System.currentTimeMillis}"
+            val packageName = "dummyCloudantPackage"
+            val feed = "interval"
+
+            // the package alarms should be there
+            val packageGetResult = wsk.pkg.get("/whisk.system/alarms")
+            println("fetched package alarms")
+            packageGetResult.stdout should include("ok")
+
+            // create package binding
+            assetHelper.withCleaner(wsk.pkg, packageName) {
+                (pkg, name) => pkg.bind("/whisk.system/alarms", name)
+            }
+
+            // create whisk stuff
+            val feedCreationResult = assetHelper.withCleaner(wsk.trigger, 
triggerName, confirmDelete = false) {
+                (trigger, name) =>
+                    trigger.create(name, feed = Some(s"$packageName/$feed"), 
parameters = Map(
+                        "trigger_payload" -> "alarmTest".toJson,
+                        "minutes" -> "five".toJson),
+                        expectedExitCode = 246)
+            }
+            feedCreationResult.stderr should include("the minutes parameter 
must be an integer")
+
+    }
 }
diff --git a/tests/src/test/scala/system/packages/AlarmsFeedWebTests.scala 
b/tests/src/test/scala/system/packages/AlarmsFeedWebTests.scala
index a1f3060..cb8d41f 100644
--- a/tests/src/test/scala/system/packages/AlarmsFeedWebTests.scala
+++ b/tests/src/test/scala/system/packages/AlarmsFeedWebTests.scala
@@ -51,25 +51,25 @@ class AlarmsFeedWebTests
         wsk.action.get(webAction, FORBIDDEN)
     }
 
-    it should "reject put of a trigger due to missing triggerName argument" in 
{
+    it should "reject post of a trigger due to missing triggerName argument" 
in {
         val params = JsObject(originalParams.fields - "triggerName")
 
         makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("no trigger name parameter was provided")), 400)
     }
 
-    it should "reject put of a trigger due to missing cron argument" in {
+    it should "reject post of a trigger due to missing cron argument" in {
         val params = JsObject(originalParams.fields - "cron")
 
         makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("alarms trigger feed is missing the cron parameter")), 400)
     }
 
-    it should "reject put of a trigger due to invalid cron argument" in {
+    it should "reject post of a trigger due to invalid cron argument" in {
         val params = JsObject(originalParams.fields + ("cron" -> 
JsString("***")))
 
         makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("cron pattern '***' is not valid")), 400)
     }
 
-    it should "reject put of a trigger when authentication fails" in {
+    it should "reject post of a trigger when authentication fails" in {
         val params = JsObject(originalParams.fields + ("cron" -> JsString("* * 
* * *")))
         makePostCallWithExpectedResult(params, JsObject("error" -> 
JsString("Trigger authentication request failed.")), 401)
     }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to