Do not hard code API host of push notification service (#86)


diff --git a/README.md b/README.md
index 43a4dc6..b96f1c3 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@
 
 | Entity | Type | Parameters | Description |
 | --- | --- | --- | --- |
-| `/whisk.system/pushnotifications` | package | appId, appSecret  | Work with the Push Service |
-| `/whisk.system/pushnotifications/sendMessage` | action | text, url, deviceIds, platforms, userIds, tagNames, gcmCollapseKey, gcmCategory, gcmIcon, gcmDelayWhileIdle, gcmSync, gcmVisibility, gcmPayload, gcmPriority, gcmSound, gcmTimeToLive, gcmStyleType, gcmStyleTitle, gcmStyleUrl, gcmStyleText, gcmStyleLines, gcmLightsLedArgb, gcmLightsLedOnMs, gcmLightsLedOffMs, apnsBadge, apnsCategory, apnsIosActionKey, apnsPayload, apnsType, apnsSound, apnsTitleLocKey, apnsLocKey, apnsLaunchImage, apnsTitleLocArgs, apnsLocArgs, apnstitle, apnsSubtitle, apnsAttachmentUrl, fireFoxTitle, fireFoxIconUrl, fireFoxTimeToLive, fireFoxPayload, safariTitle, safariUrlArgs, safariAction, chromeTitle, chromeIconUrl, chromeTimeToLive, chromePayload, chromeAppExtTitle, chromeAppExtCollapseKey, chromeAppExtDelayWhileIdle, chromeAppExtIconUrl, chromeAppExtTimeToLive, chromeAppExtPayload | Send push notification to one or more specified devices |
+| `/whisk.system/pushnotifications` | package | appId, appSecret, admin_url  | Work with the Push Service |
+| `/whisk.system/pushnotifications/sendMessage` | action | text, url, apiHost, deviceIds, platforms, userIds, tagNames, gcmCollapseKey, gcmCategory, gcmIcon, gcmDelayWhileIdle, gcmSync, gcmVisibility, gcmPayload, gcmPriority, gcmSound, gcmTimeToLive, gcmStyleType, gcmStyleTitle, gcmStyleUrl, gcmStyleText, gcmStyleLines, gcmLightsLedArgb, gcmLightsLedOnMs, gcmLightsLedOffMs, apnsBadge, apnsCategory, apnsIosActionKey, apnsPayload, apnsType, apnsSound, apnsTitleLocKey, apnsLocKey, apnsLaunchImage, apnsTitleLocArgs, apnsLocArgs, apnstitle, apnsSubtitle, apnsAttachmentUrl, fireFoxTitle, fireFoxIconUrl, fireFoxTimeToLive, fireFoxPayload, safariTitle, safariUrlArgs, safariAction, chromeTitle, chromeIconUrl, chromeTimeToLive, chromePayload, chromeAppExtTitle, chromeAppExtCollapseKey, chromeAppExtDelayWhileIdle, chromeAppExtIconUrl, chromeAppExtTimeToLive, chromeAppExtPayload | Send push notification to one or more specified devices |
 | `/whisk.system/pushnotifications/webhook` | feed | events | Fire trigger events on device activities (device registration, unregistration, subscription, or unsubscription) on the Push service |
 Creating a package binding with the `appId` and `appSecret` values is suggested. This way, you don't need to specify these credentials every time you invoke the actions in the package.
 
@@ -54,6 +54,7 @@
 
 - `text`: The notification message to be shown to the user. For example: `-p text "Hi ,OpenWhisk send a notification"`.
 - `url`: An optional URL that can be sent along with the alert. For example: `-p url "https:\\www.w3.ibm.com"`.
+- `apiHost`: An optional string that specifies the API host.  The default is 'mobile.ng.bluemix.net'.  For example: `-p apiHost "mobile.eu-gb.bluemix.net"`.
 - `deviceIds` The list of specified devices. For example: `-p deviceIds "[\"deviceID1\"]"`.
 - `platforms` Send notification to the devices of the specified platforms. 'A' for apple (iOS) devices and 'G' for google (Android) devices. For example `-p platforms ["A"]`.
 - `userIds` - Send notification to the devices of the specified users. For example: `-p userIds "[\"testUser\"]"`
diff --git a/packages/actions/sendMessage.js b/packages/actions/sendMessage.js
index c3c39ab..4ee1584 100644
--- a/packages/actions/sendMessage.js
+++ b/packages/actions/sendMessage.js
@@ -21,6 +21,7 @@
 *  @param {string} appGuid - appGuid to create webhook
 *  @param {string} appSecret - appSecret of the application
 *  @param {string} url - An optional URL that can be sent along with the alert. Eg : -p url "https:\\www.mycompany.com".
+*  @param {string} apiHost - An optional string that specifies the API host.  The default is 'mobile.ng.bluemix.net'.  Eg : -p apiHost "mobile.eu-gb.bluemix.net".
 *  @param {object} text - The notification message to be shown to the user. Eg: -p text "Hi ,OpenWhisk send a notification"
 *  @param {string} deviceIds - Send notification to the list of specified devices. Eg: -p deviceIds "["deviceID1"]"
 *  @param {object} platforms - Send notification to the devices of the specified platforms. 'A' for apple (iOS) devices and 'G' for google (Android) devices. Eg: -p platforms ["A"]
@@ -85,6 +86,7 @@
 */
 module.paths.push('/usr/lib/node_modules');
 var https = require('https');
+var url = require('url');
 
 function main(params) {
 
@@ -426,10 +428,22 @@
 
   var bodyData = JSON.stringify(sendMessage);
   var request = require('request');
+  var apiHost;
+  if (params.apiHost) {
+    apiHost = params.apiHost;
+  }
+  else if (params.admin_url) {
+    var adminURL = url.parse(params.admin_url).protocol === null ? `https:${params.admin_url}` : params.admin_url;
+    apiHost = url.parse(adminURL).host;
+  }
+  else {
+    apiHost = 'mobile.ng.bluemix.net';
+  }
+
   var promise = new Promise(function (resolve, reject) {
     request({
       method: 'post',
-      uri: 'https://mobile.ng.bluemix.net/imfpush/v1/apps/' + appId + '/messages',
+      uri: `https://${apiHost}/imfpush/v1/apps/${appId}/messages`,
       headers: {
         'appSecret': appSecret,
         'Accept': 'application/json',
diff --git a/packages/installCatalog.sh b/packages/installCatalog.sh
index 06ce2fe..0dffbff 100755
--- a/packages/installCatalog.sh
+++ b/packages/installCatalog.sh
@@ -36,11 +36,11 @@
 
 $WSK_CLI -i --apihost "$APIHOST"  package update --auth "$AUTH"  --shared yes "pushnotifications" \
 -a description "This package supports sending push notifications to your mobile device, using the IBM Bluemix Push Notifications service." \
--a parameters '[ {"name":"appGuid", "required":true, "bindTime":true, "description":"Bluemix application GUID"}, {"name":"appSecret", "required":true, "bindTime":true, "type":"password", "description":"Bluemix Push Service Secret"}]' \
+-a parameters '[ {"name":"appGuid", "required":true, "bindTime":true, "description":"Bluemix application GUID"}, {"name":"appSecret", "required":true, "bindTime":true, "type":"password", "description":"Bluemix Push Service Secret"}, {"name":"admin_url", "required":false, "bindTime":true}]' \
 -a prettyName "Push Notifications" \
 -p bluemixServiceName 'imfpush'
 
-$WSK_CLI -i --apihost "$APIHOST" action update --auth "$AUTH" "pushnotifications/webhook" "$PACKAGE_HOME/feeds/webhook.js" \
+$WSK_CLI -i --apihost "$APIHOST" action update --kind nodejs:6 --auth "$AUTH" "pushnotifications/webhook" "$PACKAGE_HOME/feeds/webhook.js" \
 -a feed true \
 -a description 'pushnotifications feed' \
 -a parameters '[ {"name":"appGuid", "required":true, "bindTime":true, "description":"Bluemix application GUID"}, {"name":"appSecret", "required":true, "bindTime":true, "type":"password", "description":"Bluemix Push Service Secret"},{"name":"events", "required":true, "description":"Name of the event user want to subscribe"} ]' \
@@ -48,8 +48,8 @@
 -a sampleOutput '{"tagName": "tagName","eventType": "onDeviceRegister","applicationId": "xxx-xxx-xx"}'
 
 
-$WSK_CLI -i --apihost "$APIHOST" action update --auth "$AUTH" "pushnotifications/sendMessage" "$PACKAGE_HOME/actions/sendMessage.js" \
+$WSK_CLI -i --apihost "$APIHOST" action update --kind nodejs:6 --auth "$AUTH" "pushnotifications/sendMessage" "$PACKAGE_HOME/actions/sendMessage.js" \
 -a description 'Send push notification to all application users or to a specific set of devices' \
--a parameters '[ {"name":"appGuid", "required":true, "bindTime":true, "description":"Bluemix application GUID"}, {"name":"appSecret", "required":true, "bindTime":true, "type":"password", "description":"Bluemix Push Service Secret"}, {"name":"text", "required":true, "description":"The notification message to be shown to the user"}, {"name":"url", "required":false, "description":"An optional URL that can be sent along with the alert"}, {"name":"deviceIds", "required":false, "description":"Array of device IDs"}, {"name":"platforms", "required":false, "description":"Array of device platform"},{"name":"userIds", "required":false, "description":"Array of UserIds"},{"name":"tagNames", "required":false, "description":"Array of tag names"},{"name":"gcmCollapseKey", "required":false, "description":"This parameter identifies a group of messages"},{"name":"gcmCategory", "required":false, "description":"The category identifier to be used for the interactive push notifications"},{"name":"gcmIcon", "required":false, "description":"Specify the name of the icon to be displayed for the notification"},{"name":"gcmDelayWhileIdle", "required":false, "description":"Send message when device is active"}, {"name":"gcmSync", "required":false, "description":"Device group messaging"}, {"name":"gcmVisibility", "required":false, "description":"private/public - Visibility of notification"},{"name":"gcmPayload", "required":false, "description":"Additional payload"}, {"name":"gcmPriority", "required":false, "description":"Sets the priority of the message"},{"name":"gcmSound", "required":false, "description":"Sound file name"}, {"name":"gcmTimeToLive", "required":false, "description":"Time limit for message to be delievered"}, {"name":"gcmStyleType", "required":false, "description":"Specifies the type of expandable notifications"}, {"name":"gcmStyleTitle", "required":false, "description":"Specifies the title of the notification"}, {"name":"gcmStyleUrl", "required":false, "description":"An URL from which the picture has to be obtained for the notification"}, {"name":"gcmStyleText", "required":false, "description":"The big text in bigtext_notification"}, {"name":"gcmStyleLines", "required":false, "description":"An array of strings for inbox_notification"},{"name":"gcmLightsLedArgb", "required":false, "description":"The color of the led. The hardware will do its best approximation"},{"name":"gcmLightsLedOnMs", "required":false, "description":"The number of milliseconds for the LED to be on while it is flashing. The hardware will do its best approximation"},{"name":"gcmLightsLedOffMs", "required":false, "description":"The number of milliseconds for the LED to be off while it iss flashing. The hardware will do its best approximation"},{"name":"apnsBadge", "required":false, "description":"Value for Badge"}, {"name":"apnsCategory", "required":false, "description":"The category name"}, {"name":"apnsIosActionKey", "required":false, "description":"Title for the push notification action Key"},{"name":"apnsPayload", "required":false, "description":"Additional payload"},{"name":"apnsType", "required":false, "description":"Push notification type name"},{"name":"apnsSound", "required":false, "description":"APNS sound name"}, {"name":"apnsTitleLocKey", "required":false, "description":"The key to a title string in the Localizable.strings file for the current localization"},{"name":"apnsLocKey", "required":false, "description":"A key to an alert-message string in a Localizable.strings file for the current localization"},{"name":"apnsLaunchImage", "required":false, "description":"The filename of an image file in the app bundle, with or without the filename extension"},{"name":"apnsTitleLocArgs", "required":false, "description":"Variable string values to appear in place of the format specifiers in title-loc-key"},{"name":"apnsLocArgs", "required":false, "description":"Variable string values to appear in place of the format specifiers in locKey"},{"name":"apnstitle", "required":false, "description":"The title of Rich Push notifications"},{"name":"apnsSubtitle", "required":false, "description":"The subtitle of the Rich Notifications"},{"name":"apnsAttachmentUrl", "required":false, "description":"The link to the iOS notifications media"},{"name":"fireFoxTitle", "required":false, "description":"Specifies the title to be set for the WebPush Notification"}, {"name":"fireFoxIconUrl", "required":false, "description":"The URL of the icon to be set for the WebPush Notification."}, {"name":"fireFoxTimeToLive", "required":false, "description":"This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline."}, {"name":"fireFoxPayload", "required":false, "description":"Custom JSON payload"}, {"name":"chromeTitle", "required":false, "description":"Specifies the title to be set for the WebPush Notification"}, {"name":"chromeIconUrl", "required":false, "description":"The URL of the icon to be set for the WebPush Notification"}, {"name":"chromeTimeToLive", "required":false, "description":"This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline."}, {"name":"chromePayload", "required":false, "description":"Custom JSON payload"},{"name":"safariTitle", "required":false, "description":"Specifies the title to be set for the Safari Push Notifications"},{"name":"safariUrlArgs", "required":false, "description":"The URL arguments that need to be used with this notification. This has to provided in the form of a JSON Array"},{"name":"safariAction", "required":false, "description":"The label of the action button"},{"name":"chromeAppExtTitle", "required":false, "description":"Specifies the title to be set for the WebPush Notification"}, {"name":"chromeAppExtCollapseKey", "required":false, "description":"This parameter identifies a group of messages"},{"name":"chromeAppExtDelayWhileIdle", "required":false, "description":"When this parameter is set to true, it indicates that the message should not be sent until the device becomes active"}, {"name":"chromeAppExtIconUrl", "required":false, "description":"The URL of the icon to be set for the WebPush Notification"}, {"name":"chromeAppExtTimeToLive", "required":false, "description":"This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline"}, {"name":"chromeAppExtPayload", "required":false, "description":"Custom JSON payload"}]' \
+-a parameters '[ {"name":"appGuid", "required":true, "bindTime":true, "description":"Bluemix application GUID"}, {"name":"appSecret", "required":true, "bindTime":true, "type":"password", "description":"Bluemix Push Service Secret"}, {"name":"text", "required":true, "description":"The notification message to be shown to the user"}, {"name":"url", "required":false, "description":"An optional URL that can be sent along with the alert"}, {"name":"apiHost", "required":false, "description":"API host"}, {"name":"deviceIds", "required":false, "description":"Array of device IDs"}, {"name":"platforms", "required":false, "description":"Array of device platform"},{"name":"userIds", "required":false, "description":"Array of UserIds"},{"name":"tagNames", "required":false, "description":"Array of tag names"},{"name":"gcmCollapseKey", "required":false, "description":"This parameter identifies a group of messages"},{"name":"gcmCategory", "required":false, "description":"The category identifier to be used for the interactive push notifications"},{"name":"gcmIcon", "required":false, "description":"Specify the name of the icon to be displayed for the notification"},{"name":"gcmDelayWhileIdle", "required":false, "description":"Send message when device is active"}, {"name":"gcmSync", "required":false, "description":"Device group messaging"}, {"name":"gcmVisibility", "required":false, "description":"private/public - Visibility of notification"},{"name":"gcmPayload", "required":false, "description":"Additional payload"}, {"name":"gcmPriority", "required":false, "description":"Sets the priority of the message"},{"name":"gcmSound", "required":false, "description":"Sound file name"}, {"name":"gcmTimeToLive", "required":false, "description":"Time limit for message to be delivered"}, {"name":"gcmStyleType", "required":false, "description":"Specifies the type of expandable notifications"}, {"name":"gcmStyleTitle", "required":false, "description":"Specifies the title of the notification"}, {"name":"gcmStyleUrl", "required":false, "description":"An URL from which the picture has to be obtained for the notification"}, {"name":"gcmStyleText", "required":false, "description":"The big text in bigtext_notification"}, {"name":"gcmStyleLines", "required":false, "description":"An array of strings for inbox_notification"},{"name":"gcmLightsLedArgb", "required":false, "description":"The color of the led. The hardware will do its best approximation"},{"name":"gcmLightsLedOnMs", "required":false, "description":"The number of milliseconds for the LED to be on while it is flashing. The hardware will do its best approximation"},{"name":"gcmLightsLedOffMs", "required":false, "description":"The number of milliseconds for the LED to be off while it iss flashing. The hardware will do its best approximation"},{"name":"apnsBadge", "required":false, "description":"Value for Badge"}, {"name":"apnsCategory", "required":false, "description":"The category name"}, {"name":"apnsIosActionKey", "required":false, "description":"Title for the push notification action Key"},{"name":"apnsPayload", "required":false, "description":"Additional payload"},{"name":"apnsType", "required":false, "description":"Push notification type name"},{"name":"apnsSound", "required":false, "description":"APNS sound name"}, {"name":"apnsTitleLocKey", "required":false, "description":"The key to a title string in the Localizable.strings file for the current localization"},{"name":"apnsLocKey", "required":false, "description":"A key to an alert-message string in a Localizable.strings file for the current localization"},{"name":"apnsLaunchImage", "required":false, "description":"The filename of an image file in the app bundle, with or without the filename extension"},{"name":"apnsTitleLocArgs", "required":false, "description":"Variable string values to appear in place of the format specifiers in title-loc-key"},{"name":"apnsLocArgs", "required":false, "description":"Variable string values to appear in place of the format specifiers in locKey"},{"name":"apnstitle", "required":false, "description":"The title of Rich Push notifications"},{"name":"apnsSubtitle", "required":false, "description":"The subtitle of the Rich Notifications"},{"name":"apnsAttachmentUrl", "required":false, "description":"The link to the iOS notifications media"},{"name":"fireFoxTitle", "required":false, "description":"Specifies the title to be set for the WebPush Notification"}, {"name":"fireFoxIconUrl", "required":false, "description":"The URL of the icon to be set for the WebPush Notification."}, {"name":"fireFoxTimeToLive", "required":false, "description":"This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline."}, {"name":"fireFoxPayload", "required":false, "description":"Custom JSON payload"}, {"name":"chromeTitle", "required":false, "description":"Specifies the title to be set for the WebPush Notification"}, {"name":"chromeIconUrl", "required":false, "description":"The URL of the icon to be set for the WebPush Notification"}, {"name":"chromeTimeToLive", "required":false, "description":"This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline."}, {"name":"chromePayload", "required":false, "description":"Custom JSON payload"},{"name":"safariTitle", "required":false, "description":"Specifies the title to be set for the Safari Push Notifications"},{"name":"safariUrlArgs", "required":false, "description":"The URL arguments that need to be used with this notification. This has to provided in the form of a JSON Array"},{"name":"safariAction", "required":false, "description":"The label of the action button"},{"name":"chromeAppExtTitle", "required":false, "description":"Specifies the title to be set for the WebPush Notification"}, {"name":"chromeAppExtCollapseKey", "required":false, "description":"This parameter identifies a group of messages"},{"name":"chromeAppExtDelayWhileIdle", "required":false, "description":"When this parameter is set to true, it indicates that the message should not be sent until the device becomes active"}, {"name":"chromeAppExtIconUrl", "required":false, "description":"The URL of the icon to be set for the WebPush Notification"}, {"name":"chromeAppExtTimeToLive", "required":false, "description":"This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline"}, {"name":"chromeAppExtPayload", "required":false, "description":"Custom JSON payload"}]' \
 -a sampleInput '{"appGuid":"xxx-xxx-xx", "appSecret":"yyy-yyy-yyy", "text":"hi there"}' \
 -a sampleOutput '{"pushResponse": {"messageId":"11111s","message":{"message":{"alert":"register for tag"}}}}'
diff --git a/tests/src/test/scala/packages/PushNotificationsTests.scala b/tests/src/test/scala/packages/PushNotificationsTests.scala
index cb6afc5..8da096e 100644
--- a/tests/src/test/scala/packages/PushNotificationsTests.scala
+++ b/tests/src/test/scala/packages/PushNotificationsTests.scala
@@ -31,6 +31,8 @@
   val credentials = TestUtils.getVCAPcredentials("imfpush")
   val appSecret = credentials.get("appSecret").toJson;
   val credentialsUrl = credentials.get("url");
+  val adminURL = credentials.get("admin_url");
+  val apiHost = adminURL.split("/")(2);
   val appGuid = credentialsUrl.split("/").last.toJson;
   val url = "www.google.com".toJson;
 
@@ -67,4 +69,32 @@
                 _.response.result.get.toString should include ("message")
              }
            }
+
+    it should "Send Notification action using admin_url" in {
+        val name = "/whisk.system/pushnotifications/sendMessage"
+        withActivation(wsk.activation,wsk.action.invoke(name, Map("appSecret" -> appSecret, "appGuid" -> appGuid, "text" -> messageText, "admin_url"-> adminURL.toJson))){
+            _.response.result.get.toString should include ("message")
+        }
+    }
+
+    it should "Send Notification action using bad admin_url" in {
+        val name = "/whisk.system/pushnotifications/sendMessage"
+        withActivation(wsk.activation,wsk.action.invoke(name, Map("appSecret" -> appSecret, "appGuid" -> appGuid, "text" -> messageText, "admin_url"-> "//mobile.bad.host/pathname".toJson))){
+            _.response.success shouldBe false
+        }
+    }
+
+    it should "Send Notification action using apiHost" in {
+        val name = "/whisk.system/pushnotifications/sendMessage"
+        withActivation(wsk.activation,wsk.action.invoke(name, Map("appSecret" -> appSecret, "appGuid" -> appGuid, "text" -> messageText, "apiHost"-> apiHost.toJson))){
+            _.response.result.get.toString should include ("message")
+        }
+    }
+
+    it should "Send Notification action using bad apiHost" in {
+        val name = "/whisk.system/pushnotifications/sendMessage"
+        withActivation(wsk.activation,wsk.action.invoke(name, Map("appSecret" -> appSecret, "appGuid" -> appGuid, "text" -> messageText, "apiHost"-> "mobile.bad.host".toJson))){
+            _.response.success shouldBe false
+        }
+    }
 }