[
  {
    "path": ".eslintignore",
    "content": "coverage\nnode_modules\ntmp\ntmpdirs-serverless\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  \"extends\": \"airbnb\",\n  \"plugins\": [],\n  \"rules\": {\n    \"func-names\": \"off\",\n\n    // doesn't work in node v4 :(\n    \"strict\": \"off\",\n    \"prefer-rest-params\": \"off\",\n    \"react/require-extension\" : \"off\",\n    \"import/no-extraneous-dependencies\" : \"off\"\n  },\n  \"env\": {\n       \"mocha\": true\n   }\n};\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\n*.log\nnpm-debug.log\n\n# Runtime data\npids\n*.pid\n*.seed\ndist\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git\nnode_modules\npackage-lock.json\n\n# IDE stuff\n**/.idea\n.vscode/\n\n# OS stuff\n.DS_Store\n.tmp\n\n# Serverless stuff\nadmin.env\n.env\ntmp\n.coveralls.yml\ntmpdirs-serverless\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"11\"\n  - \"10\"\n  - \"8\"\n\nsudo: required\n\nservices:\n - docker\n\nbefore_install:\n  - cd $TRAVIS_BUILD_DIR\n\ninstall:\n  - ./tools/travis/setup.sh\n  - cd $TRAVIS_BUILD_DIR\n\nscript:\n  - ./tools/travis/build.sh\n  - cd $TRAVIS_BUILD_DIR\n  - npm run report\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2018 Serverless, Inc. http://www.serverless.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "**📦 Archived - This repository is archived and preserved for reference only. No updates, issues, or pull requests will be accepted. If you have questions, please reach out to our support team.**\n\n---\n\n\n# Serverless Apache OpenWhisk Plugin\n[![Build Status](https://travis-ci.org/serverless/serverless-openwhisk.svg?branch=master)](https://travis-ci.org/serverless/serverless-openwhisk)\n[![codecov](https://codecov.io/gh/serverless/serverless-openwhisk/branch/master/graph/badge.svg)](https://codecov.io/gh/serverless/serverless-openwhisk)\n\nThis plugin enables support for the [Apache OpenWhisk platform](https://openwhisk.apache.org/) within the Serverless Framework.\n\n## Getting Started\n\n### Register account with Apache OpenWhisk\n\nBefore you can deploy your service to Apache OpenWhisk, you need to have an account registered with the platform.\n\n- *Want to run the platform locally?* Please read the project's [*Quick Start*](https://github.com/openwhisk/openwhisk#quick-start) guide for deploying it locally.\n- *Want to use a hosted provider?* Please [sign up](https://cloud.ibm.com/registration) for a free account with [IBM Cloud](https://cloud.ibm.com/) and then follow the instructions for getting access to [IBM Cloud Functions (Apache OpenWhisk)](https://cloud.ibm.com/openwhisk).\n\n### Set up account credentials\n\nAccount credentials for OpenWhisk can be provided through a configuration file or environment variables. This plugin requires the API endpoint, namespace and authentication credentials.\n\n**Do you want to use a configuration file for storing these values?** Please [follow the instructions](https://console.ng.bluemix.net/openwhisk/cli) for setting up the OpenWhisk command-line utility. This tool stores account credentials in the `.wskprops` file in the user's home directory. The plugin automatically extracts credentials from this file at runtime.  No further configuration is needed.\n\n**Do you want to use environment variables for credentials?** Use the following environment variables to be pass in account credentials. These values override anything extracted from the configuration file.\n\n- *OW_APIHOST* - Platform endpoint, e.g. `openwhisk.ng.bluemix.net`\n- *OW_AUTH* - Authentication key, e.g. `xxxxxx:yyyyy`\n- *OW_NAMESPACE* - Namespace, defaults to user-provided credentials\n- *OW_APIGW_ACCESS_TOKEN* - API gateway access token (optional)\n- *OW_IAM_NAMESPACE_API_KEY* - IBM Cloud IAM API key (optional & overrides `auth`).\n\n### Install Serverless Framework\n\n```shell\n$ npm install --global serverless\n```\n\n**_This framework plugin requires Node.js runtime version 6.0 or above._**\n\n### Create Service From Template\n\nUsing the `create` command, you can create an example service from the [following template](https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/openwhisk-nodejs).\n\n```shell\nserverless create --template openwhisk-nodejs --path my_service\ncd my_service\nnpm install\n```\n\nMore service examples are available in the [`serverless-examples`](https://github.com/serverless/examples) repository.\n\n**Using a self-hosted version of the platform?**\n\nEnsure you set the `ignore_certs` option in the serverless.yaml prior to deployment.\n\n```\nprovider:\n  name: openwhisk\n  ignore_certs: true\n```\n\n### Deploy Service\n\nThe sample service from the template can be deployed without modification.\n\n```shell\nserverless deploy\n```\n\nIf the deployment succeeds, the following messages will be printed to the console.\n\n```sh\n$ serverless deploy\nServerless: Packaging service...\nServerless: Compiling Functions...\nServerless: Compiling API Gateway definitions...\nServerless: Compiling Rules...\nServerless: Compiling Triggers & Feeds...\nServerless: Deploying Functions...\nServerless: Deployment successful!\n\nService Information\nplatform:\topenwhisk.ng.bluemix.net\nnamespace:\t_\nservice:\tmy_service\n\nactions:\nmy_service-dev-hello\n\ntriggers:\n**no triggers deployed***\n\nrules:\n**no rules deployed**\n\nendpoints:\n**no routes deployed**\n\nweb-actions:\n**no web actions deployed**\n```\n\n### Test Service\n\nUse the `invoke` command to test your newly deployed service.\n\n```shell\n$ serverless invoke --function hello\n{\n    \"payload\": \"Hello, World!\"\n}\n$ serverless invoke --function hello --data '{\"name\": \"OpenWhisk\"}'\n{\n    \"payload\": \"Hello, OpenWhisk!\"\n}\n```\n\n*Add the `-v` or `--verbose` flag to show more [invocation details](https://github.com/apache/incubator-openwhisk/blob/master/docs/annotations.md#annotations-specific-to-activations), e.g. activation id and duration details.*\n\n```shell\n$ serverless invoke --function hello -v\n=> action (<ACTION_NAME>) activation (<ID>) duration: 96ms (init: 83ms, wait: 35ms)\n{\n    \"payload\": \"Hello, OpenWhisk!\"\n}\n```\n\n## Writing Functions - Node.js\n\nHere's an `index.js` file containing an example handler function.\n\n```javascript\nfunction main(params) {\n  const name = params.name || 'World';\n  return {payload:  'Hello, ' + name + '!'};\n};\n\nexports.main = main;\n```\n\nModules [should return the function handler](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#packaging-an-action-as-a-nodejs-module) as a custom property on the global `exports` object.\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the source file and module property containing the serverless function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n```\n\n### Request Properties\n\nOpenWhisk executes the handler function for each request. This function is called with a single argument, an object [containing the request properties](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#passing-parameters-to-an-action).\n\n```javascript\nfunction main(params) {\n  const parameter = params.parameter_name;\n  ...\n};\n```\n\n### Function Return Values\n\nThe handler must return an object from the function call. Returning `undefined` or `null` will result in an error. If the handler is carrying out an [asynchronous task](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#creating-asynchronous-actions), it can return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n\n```javascript\n// synchronous return\nfunction main () {\n  return { payload: \"...\" }\n}\n\n// asychronous return\nfunction main(args) {\n  return new Promise(function(resolve, reject) {\n    setTimeout(function() {\n      resolve({ done: true });\n     }, 2000);\n  })\n}\n```\n\nIf you want to return an error message, return an object with an `error` property with the message. Promise values that are rejected will be interpreted as runtime errors.\n\n```javascript\n// synchronous return\nfunction main () {\n  return { error: \"...\" }\n}\n\n// asychronous return\nfunction main(args) {\n  return new Promise(function(resolve, reject) {\n    setTimeout(function() {\n      reject(\"error message\");\n     }, 2000);\n  })\n}\n```\n\n### Using NPM Modules\n\nNPM modules must be [installed locally](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#packaging-an-action-as-a-nodejs-module) in the `node_modules` directory before deployment. This directory will be packaged up in the deployment artefact. Any dependencies included in `node_modules` will be available through `require()` in the runtime environment.\n\nOpenWhisk provides a number of popular NPM modules in the runtime environment. Using these modules doesn't require them to be included in the deployment package. See [this list](https://github.com/openwhisk/openwhisk/blob/master/docs/reference.md#javascript-runtime-environments) for full details of which modules are available.\n\n```javascript\nconst leftPad = require(\"left-pad\")\n\nfunction pad_lines(args) {\n    const lines = args.lines || [];\n    return { padded: lines.map(l => leftPad(l, 30, \".\")) }\n};\n\nexports.handler = pad_lines;\n```\n\n## Writing Functions - PHP\n\nHere's an `index.php` file containing an example handler function.\n\n```php\n<?php\nfunction main(array $args) : array\n{\n    $name = $args[\"name\"] ?? \"stranger\";\n    $greeting = \"Hello $name!\";\n    echo $greeting;\n    return [\"greeting\" => $greeting];\n}\n```\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the source file and function name of the serverless function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    runtime: php\n```\n\n### Request Properties\n\nOpenWhisk executes the handler function for each request. This function is called with a single argument, an associative array [containing the request properties](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#passing-parameters-to-an-action).\n\n```php\nfunction main(array $args) : array\n{\n    $name = $args[\"name\"] ?? \"stranger\";\n    ...\n}\n```\n\n### Function Return Values\n\nThe handler must return  an associative array from the function call.\n\n```php\nfunc main(args: [String:Any]) -> [String:Any] {\n\t...\n    return [\"foo\" => $bar];\n}\n```\n\nIf you want to return an error message, return an object with an `error` property with the message.\n\n## Writing Functions - Python\n\nHere's an `index.py` file containing an example handler function.\n\n```python\ndef endpoint(params):\n    name = params.get(\"name\", \"stranger\")\n    greeting = \"Hello \" + name + \"!\"\n    print(greeting)\n    return {\"greeting\": greeting}\n```\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the source file and module property containing the serverless function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.endpoint\n    runtime: python:3\n```\n\n### Request Properties\n\nOpenWhisk executes the handler function for each request. This function is called with a single argument, a dictionary [containing the request properties](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#passing-parameters-to-an-action).\n\n```python\ndef endpoint(params):\n    name = params.get(\"name\", \"stranger\")\n    ...\n```\n\n### Function Return Values\n\nThe handler must return a dictionary from the function call.\n\n```python\ndef endpoint(params):\n    ...\n    return {\"foo\": \"bar\"}\n```\n\nIf you want to return an error message, return an object with an `error` property with the message.\n\n## Writing Functions - Ruby\n\nHere's an `hello.rb` file containing an example handler function.\n\n```ruby\ndef main(args)\n  name = args[\"name\"] || \"stranger\"\n  greeting = \"Hello #{name}!\"\n  puts greeting\n  { \"greeting\" => greeting }\nend\n```\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the source file and function name of the serverless function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: hello.main\n    runtime: ruby\n```\n\n### Request Properties\n\nOpenWhisk executes the handler function for each request. This function is called with a single argument, which is a hash [containing the request properties](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#passing-parameters-to-an-action).\n\n```ruby\ndef main(args)\n  name = args[\"name\"] || \"stranger\"\n  ...\n```\n\n### Function Return Values\n\nThe handler must return a hash from the function call.\n\n```ruby\ndef main(args)\n  ...\n  { \"greeting\" => greeting }\nend\n```\n\nIf you want to return an error message, return an `error` property string in the return hash.\n\n## Writing Functions - Swift\n\nHere's an `index.swift` file containing an example handler function.\n\n```swift\nfunc main(args: [String:Any]) -> [String:Any] {\n    if let name = args[\"name\"] as? String {\n      return [ \"greeting\" : \"Hello \\(name)!\" ]\n    } else {\n      return [ \"greeting\" : \"Hello stranger!\" ]\n    }\n}\n```\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the source file and module property containing the serverless function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    runtime: swift\n```\n\n### Request Properties\n\nOpenWhisk executes the handler function for each request. This function is called with a single argument, a dictionary [containing the request properties](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#passing-parameters-to-an-action).\n\n```swift\nfunc main(args: [String:Any]) -> [String:Any] {\n    let prop = args[\"prop\"] as? String\n}\n```\n\n### Function Return Values\n\nThe handler must return a dictionary from the function call.\n\n```swift\nfunc main(args: [String:Any]) -> [String:Any] {\n\t...\n    return [\"foo\": \"bar\"]\n}\n```\n\nIf you want to return an error message, return an object with an `error` property with the message.\n\n### Codable Support\n\nSwift 4 runtimes support [Codable types](https://developer.apple.com/documentation/swift/codable) to handle the converting between JSON input parameters and response types to native Swift types.\n\n```swift\nstruct Employee: Codable {\n  let id: Int?\n  let name: String?\n}\n// codable main function\nfunc main(input: Employee, respondWith: (Employee?, Error?) -> Void) -> Void {\n    // For simplicity, just passing same Employee instance forward\n    respondWith(input, nil)\n}\n```\n\n### Pre-Compiled Swift Binaries\n\nOpenWhisk supports creating Swift actions from a pre-compiled binary. This reduces startup time for Swift actions by removing the need for a dynamic compilation step.\n\nIn the `serverless.yaml` file, the `handler` property can refer to the zip file containing a binary file produced by the build.\n\n```yaml\nfunctions:\n  hello:\n    handler: action.zip\n```\n\nCompiling a single Swift file to a binary can be handled using this Docker command with the OpenWhisk Swift runtime image. `main.swift` is the file containing the swift code and `action.zip` is the zip archive produced.\n\n```\ndocker run -i openwhisk/action-swift-v4.2 -compile main < main.swift > action.zip\n```\n\nSwift packages containing multiple source files with a package descriptor (`Package.swift` ) can be built using the following command.\n\n```\nzip - -r * | docker run -i openwhisk/action-swift-v4.2 -compile main > action.zip\n```\n\n## Writing Functions - Java\n\nHere's an `src/main/java/HelloWorld.java` file containing an example handler function.\n\n```java\nimport com.google.gson.JsonObject;\n\npublic class HelloWorld {\n\n  public static JsonObject main(JsonObject args) throws Exception {\n\n    final String name = args.getAsJsonPrimitive(\"name\").getAsString();\n\n    final JsonObject response = new JsonObject();\n    response.addProperty(\"greeting\", \"Hello \" + name + \"!\");\n\n    return response;\n  }\n}\n```\n\nHere is a simple `pom.xml` file that will allow you to use Maven to build it. You will notice that `gson` is excluded from the uberjar. That is because OpenWhisk already provides this dependency.\n\n```xml\n<project>\n <modelVersion>4.0.0</modelVersion>\n <groupId>hello</groupId>\n <artifactId>hello-world</artifactId>\n <version>1.0</version>\n\n <dependencies>\n  <dependency>\n    <groupId>com.google.code.gson</groupId>\n    <artifactId>gson</artifactId>\n    <version>2.8.2</version>\n  </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-shade-plugin</artifactId>\n        <version>3.1.0</version>\n        <executions>\n          <execution>\n            <phase>package</phase>\n            <goals>\n              <goal>shade</goal>\n            </goals>\n            <configuration>\n              <minimizeJar>true</minimizeJar>\n              <artifactSet>\n                <excludes>\n                  <exclude>com.google.code.gson:gson</exclude>\n                </excludes>\n              </artifactSet>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n </project>\n```\n\nIn the `serverless.yaml` file (see below), the `handler` property is the uberjar produced by calling `mvn clean package`, a colon, and then the fully qualified class name of the class with the main function. If you do not provide a class name after the jar, it will look for a class in the default package called `Main`.\n\n```yaml\nservice: my-java-service\nprovider:\n  name: openwhisk\n  runtime: java\nfunctions:\n  hello:\n    handler: target/hello-world-1.0.jar:HelloWorld\nplugins:\n  - serverless-openwhisk\n```\n\n### Request Properties\n\nOpenWhisk executes the handler function for each request. This function is called with a single argument, a `com.google.gson.JsonObject` [containing the request properties](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#passing-parameters-to-an-action).\n\n```java\nimport com.google.gson.JsonObject;\n\npublic class MyActionClass {\n  public static JsonObject main(JsonObject args) throws Exception\n  {\n    final String name = args.getAsJsonPrimitive(\"name\").getAsString();\n    ...\n  }\n}\n```\n\n### Function Return Values\n\nThe handler must return an `com.google.gson.JsonObject` from the function call.\n\n```java\nimport com.google.gson.JsonObject;\n\npublic class MyActionClass {\n  public static JsonObject main(JsonObject args) throws Exception\n  {\n    ...\n    final JsonObject response = new JsonObject();\n    response.addProperty(\"greeting\", \"Hello \" + name + \"!\");\n\n    return response;\n  }\n}\n```\n\nIf you want to return an error message, throw an exception.\n\n## Writing Functions - Binary\n\nOpenWhisk supports executing a compiled binary for the function handler. Using a Python wrapper, the file will be invoked within the `openwhisk/dockerskeleton` Docker container.\n\nThe binary must be compiled for the correct platform architecture and only link to shared libraries installed in the `openwhisk/dockerskeleton` runtime.\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the binary file to upload.\n\n```yaml\nfunctions:\n  my_function:\n    handler: bin_file\n    runtime: binary\n```\n\n### Request Properties\n\nOpenWhisk executes the binary file for each request. Event parameters are streamed to `stdio` as a JSON object string.\n\n### Function Return Values\n\nThe handler must write a JSON object string with the response parameters to `stdout` before exiting.\n\nIf you want to return an error message, return an object with an `error` property with the message.\n\n## Custom Runtime Images\n\nOpenWhisk actions can use [custom Docker images as the runtime environment](https://medium.com/openwhisk/large-applications-on-openwhisk-bcf15bff94ec). This allows extra packages, libraries or tools to be pre-installed in the runtime environment. Using a custom runtime image, with extra libraries and dependencies built-in, is useful for overcoming the [maximum deployment size](https://github.com/apache/incubator-openwhisk/blob/master/docs/reference.md#system-limits) on actions.\n\n*Images must implement the [API used by the platform](http://jamesthom.as/blog/2017/01/16/openwhisk-docker-actions/) to interact with runtime environments. Images must also be available on Docker Hub. OpenWhisk does not support private Docker registries.*\n\nOpenWhisk publishes the [existing runtime images on Docker Hub](https://hub.docker.com/r/openwhisk/). Using these images in the `FROM` directive in the `Dockerfile` is an easy way to [create new images](https://docs.docker.com/engine/reference/commandline/build/) compatible with the platform.\n\nIn the `serverless.yaml` file, the `image` property is used to denote the custom runtime image.\n\n```yaml\nfunctions:\n  my_function:\n    handler: source.js\n    runtime: nodejs\n    image: dockerhub_user/image_name\n```\n\n*Node.js, Swift, Python and Binary runtimes support using a custom image property.*\n\n## Writing Functions - Docker\n\nOpenWhisk supports creating actions from public images on Docker Hub without handler files. These images are expected to support the platform API used to instantiate and invoke serverless functions.\n\nAll necessary files for execution must be provided within the image. Local source files will not be uploaded to the runtime environment.\n\nIn the `serverless.yaml` file, the `handler` property is used to denote the image label.\n\n```yaml\nfunctions:\n  my_function:\n    handler: repo/image_name\n    runtime: docker\n```\n\n## Working With Packages\n\nOpenWhisk provides a concept called \"packages\" to manage related actions. Packages can contain multiple actions under a common identifier in a namespace. Configuration values needed by all actions in a package can be set as default properties on the package, rather than individually on each action.\n\n*Packages are identified using the following format:* `/namespaceName/packageName/actionName`.\n\n***Rules and triggers can not be created within packages.***\n\n### Implicit Packages\n\nActions can be assigned to packages by setting the function `name` with a package reference.\n\n```yaml\nfunctions:\n  foo:\n    handler: handler.foo\n    name: \"myPackage/foo\"\n  bar:\n    handler: handler.bar\n    name: \"myPackage/bar\"\n```\n\nIn this example, two new actions (`foo` & `bar`) will be created using the `myPackage` package.\n\nPackages which do not exist will be automatically created during deployments. When using the `remove` command, any packages referenced in the `serverless.yml` will be deleted.\n\n### Explicit Packages\n\nPackages can also be defined explicitly to set shared configuration parameters. Default package parameters are merged into event parameters for each invocation.\n\n```yaml\nfunctions:\n  foo:\n    handler: handler.foo\n    name: \"myPackage/foo\"\n\nresources:\n  packages:\n    myPackage:\n      name: optionalCustomName\n      parameters:\n        hello: world\n```\n\n*Explicit packages support the following properties: `name`, `parameters`, `annotations`, `services` and `shared`.*\n\n### Binding Packages\n\nOpenWhisk also supports \"binding\" external packages into your workspace. Bound packages can have default parameters set for shared actions.\n\nFor example, binding the `/whisk.system/cloudant` package into a new package allows you to set default values for the `username`, `password` and `dbname` properties. Actions from this package can then be invoked with having to pass these parameters in.\n\nDefine packages explicitly with a `binding` parameter to use this behaviour.\n\n```yaml\nresources:\n  packages:\n    mySamples:\n      binding: /whisk.system/cloudant\n      parameters:\n        username: bernie\n        password: sanders\n        dbname: vermont\n```\n\nFor more details on package binding, please see the documentation [here](https://github.com/apache/incubator-openwhisk/blob/master/docs/packages.md#creating-and-using-package-bindings).\n\n## Binding Services (IBM Cloud Functions)\n\n***This feature requires the [IBM Cloud CLI](https://console.bluemix.net/docs/cli/reference/bluemix_cli/download_cli.html#download_install) and [IBM Cloud Functions plugin](https://console.bluemix.net/openwhisk/learn/cli) to be installed.***\n\nIBM Cloud Functions supports [automatic binding of service credentials](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) to actions using the CLI.\n\nBound service credentials will be passed as the `__bx_creds` parameter in the invocation parameters.\n\nThis feature is also available through the `serverless.yaml` file using the `bind` property for each function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: file_name.handler    \n    bind:\n      - service:\n          name: cloud-object-storage\n          instance: my-cos-storage\n```\n\nThe `service` configuration supports the following properties.\n\n- `name`: identifier for the cloud service\n- `instance`: instance name for service (*optional*)\n- `key`: key name for instance and service (*optional*)\n\n*If the `instance` or `key` properties are missing, the first available instance and key found will be used.*\n\nBinding services removes the need to manually create default parameters for service keys from platform services.\n\nMore details on binding service credentials to actions can be found in the [official documentation](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) and [this blog post](http://jamesthom.as/blog/2018/06/05/binding-iam-services-to-ibm-cloud-functions/).\n\nPackages defined in the `resources` section can bind services using the same configuration properties.\n\n```yaml\nresources:\n  packages:\n    myPackage:\n      bind:\n        - service:\n            name: cloud-object-storage\n            instance: my-cos-storage\n```\n\n## Runtime Configuration Properties\n\nThe following OpenWhisk configuration properties are supported for functions defined in\nthe `serverless.yaml` file.\n\n```yaml\nfunctions:\n  my_function:\n    handler: file_name.handler_func\n    name: \"custom_function_name\"\n    runtime: 'runtime_label' // defaults to nodejs:default\n    namespace: \"...\" // defaults to user-provided credentials\n    memory: 256 // 128 to 512 (MB).\n    timeout: 60 // 0.1 to 600 (seconds)\n    concurrency: 1 // 1 to 500, default is 1\n    parameters:\n      foo: bar // default parameters\n    annotations:\n      foo: bar // action annotations\n    bind:\n      - service:\n          name: cloud-object-storage\n          instance: my-cos-storage\n```\n\n## Writing Sequences\n\nOpenWhisk supports a special type of serverless function called [sequences](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md#creating-action-sequences).\n\nThese functions are defined from a list of other serverless functions. Upon invocation, the platform executes each function in series. Request parameters are passed into the first function in the list. Each subsequent function call is passed the output from the previous step as input parameters. The last function's return value is returned as the response result.\n\nHere's an example of the configuration to define a sequence function, composed of three other functions.\n\n```yaml\nfunctions:\n  my_function:\n    sequence:\n      - parse_input\n      - do_some_algorithm\n      - construct_output\n```\n\n*Sequence functions do not have a handler file defined. If you want to refer to functions not defined in the serverless project, use the fully qualified identifier e.g. /namespace/package/action_name*\n\n## Connecting HTTP Endpoints\n\nFunctions can be bound to public URL endpoints using the [API Gateway service](https://github.com/openwhisk/openwhisk/blob/master/docs/apigateway.md). HTTP requests to configured endpoints will invoke functions on-demand. Requests parameters are passed as function arguments. Function return values are serialised as the JSON response body.\n\nHTTP endpoints for functions can be configured through the `serverless.yaml` file.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    events:\n      - http: GET /api/greeting\n```\n\nHTTP event configuration also supports using explicit parameters.\n\n- `method` - HTTP method (mandatory).\n- `path` - URI path for API gateway (mandatory).\n- `resp` - controls [web action content type](https://github.com/apache/incubator-openwhisk/blob/master/docs/webactions.md#additional-features), values include: `json`, `html`, `http`, `svg`or `text` (optional, defaults to `json`).\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    events:\n      - http:\n          method: GET\n          path: /api/greeting\n          resp: http\n```\n\nAPI Gateway hosts serving the API endpoints will be shown during deployment.\n\n```shell\n$ serverless deploy\n...\nendpoints:\nGET https://xxx-gws.api-gw.mybluemix.net/service_name/api/greeting --> service_name-dev-my_function\n```\n\nCalling the configured API endpoints will execute the deployed functions.\n\n````shell\n$ http get https://xxx-gws.api-gw.mybluemix.net/api/greeting?user=\"James Thomas\"\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\nDate: Mon, 19 Dec 2016 15:47:53 GMT\n\n{\n    \"message\": \"Hello James Thomas!\"\n}\n````\n\nFunctions exposed through the API Gateway service are automatically converted into Web Actions during deployment. The framework [secures Web Actions for HTTP endpoints](https://github.com/apache/incubator-openwhisk/blob/master/docs/webactions.md#securing-web-actions) using the `require-whisk-auth` annotation. If the `require-whisk-auth` annotation is manually configured, the existing annotation value is used, otherwise a random token is automatically generated.\n\n### URL Path Parameters\n\nThe API Gateway service [supports path parameters]() in user-defined HTTP paths. This allows functions to handle URL paths which include templated values, like resource identifiers.\n\nPath parameters are identified using the `{param_name}` format in the URL path. The API Gateway sends the full matched path value in the `__ow_path` field of the event parameters.\n\n```yaml\nfunctions:\n  retrieve_users:\n    handler: users.get\n    events:\n      - http:\n          method: GET\n          path: /users/{id}\n          resp: http\n```\n\nThis feature comes with the following restrictions:\n\n- *Path parameters are only supported when `resp` is configured as`http`.*\n- *Individual path parameter values are not included as separate event parameters. Users have to manually parse values from the full `__ow_path` value.*\n\n### CORS Support\n\nAPI Gateway endpoints automatically include CORS headers for all endpoints under the service base path. This property can be disabled by manually configuring the `resources.apigw.cors` property.\n\n```yaml\nresources:\n    apigw:\n        cors: false\n```\n\n### Application Authentication\n\nAPI endpoints can be protected by API keys with a secret or API keys alone.\n\nSetting the HTTP headers used to pass keys and secrets automatically enables API Gateway authentication.\n\nThis parameter configures the HTTP header containing the API key. Without the additional secret header, authentication uses an API key alone.\n\n```yaml\nresources:\n    apigw:\n        auth:\n            key: API-Key-Header\n```\n\nAdding the secret header parameter enables authentication using keys with secrets.\n\n```yaml\nresources:\n    apigw:\n        auth:\n            key: API-Key-Header\n            secret: API-Key-Secret-Header\n```\n\n*See the API Gateway [configuration panel](https://cloud.ibm.com/openwhisk/apimanagement) to manage API keys and secrets after authentication is enabled.* \n\n### Application Authentication with OAuth\n\nAPI endpoints can also be protected by an external OAuth providers. \n\nOAuth tokens must be included as the Authorization header of each API request. Token will be validated with the specified token provider. If the token is invalid, requests are rejected with response code 401.\n\nThe following OAuth providers are supported: *[IBM Cloud App ID](https://cloud.ibm.com/catalog/services/app-id), Google, Facebook and Github.*\n\n```yaml\nresources:\n  apigw:\n    oauth:\n      provider: app-id || google || facebook || github\n```\n\nIf the `app-id` provider is selected, the tenant identifier must be provided as an additional configuration token. This can be retrieved from the `tenantId` property of provisioned service credentials for the instance\n\n```yaml\nresources:\n  apigw:\n    oauth:\n      provider: app-id\n      tenant: uuid\n```\n\n*Application Authentication with keys (and secrets) and OAuth support are mutually exclusive configuration options.*\n\n### Rate Limiting\n\nAPI Gateways endpoints support rate limiting to reject excess traffic. When rate limiting is enabled,  API calls falling outside of the limit will be rejected and response code 429 will be returned.\n\n**Rate limiting is on a per-key basis and application authentication (without oauth) must be enabled.**\n\nThe  leaky bucket algorithm is used to prevent sudden bursts of invocations of APIs. If the limit is set as 10 calls per minute, users will be restricted to 1 call every 6 seconds (60/10 = 6).\n\n```yaml\nresources:\n  apigw:\n    rate_limit:\n      rate: 100\n      unit: minute || second || hour || day\n```\n\n- `rate`: number of API calls per unit of time.\n- `unit`: unit of time (*minute, second, hour, day*) used to threshold API calls with rate.\n\n### Base Path\n\nAll API Gateway endpoints defined as HTTP events in the `serverless.yml` are deployed under the default base path (`/`). This basepath can be configured explicitly using the following parameter.\n\n```yaml\nresources:\n  apigw:\n    basepath: /api\n```\n\n### API Name\n\nThe service name is used as the API identifier in the API Gateway swagger files. This can be configured explicitly using the following parameter.\n\n```yaml\nresources:\n  apigw:\n    name: my-api-name\n```\n\n## Exporting Web Actions\n\nFunctions can be turned into \"*web actions*\" which return HTTP content without use of an API Gateway. This feature is enabled by setting an annotation (`web-export`) in the configuration file.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    annotations:\n      web-export: true\n```\n\nFunctions with this annotation can be invoked through a URL template with the following parameters.\n\n```\nhttps://{APIHOST}/api/v1/web/{USER_NAMESPACE}/{PACKAGE}/{ACTION_NAME}.{TYPE}\n```\n\n- *APIHOST* - platform endpoint e.g. *openwhisk.ng.bluemix.net.*\n- *USER_NAMESPACE* - this must be an explicit namespace and cannot use the default namespace (_).\n- *PACKAGE* - action package or `default`.\n- *ACTION_NAME* - default form `${servicename}-${space}-${name}`.\n- *TYPE* - `.json`, `.html`, `.text` or `.http`.\n\nReturn values from the function are used to construct the HTTP response. The following parameters are supported.\n\n1. `headers`: a JSON object where the keys are header-names and the values are string values for those headers (default is no headers).\n2. `code`: a valid HTTP status code (default is 200 OK).\n3. `body`: a string which is either plain text or a base64 encoded string (for binary data).\n\nHere is an example of returning HTML content:\n\n```\nfunction main(args) {\n    var msg = \"you didn&#39;t tell me who you are.\"\n    if (args.name) {\n        msg = `hello ${args.name}!`\n    }\n    return {body:\n       `<html><body><h3><center>${msg}</center></h3></body></html>`}\n}\n```\n\nHere is an example of returning binary data:\n\n```\nfunction main() {\n   let png = <base 64 encoded string>\n   return {\n      headers: { \"Content-Type\": \"image/png\" },\n      body: png };\n}\n```\n\nFunctions can access request parameters using the following environment variables.\n\n1. `__ow_method` - HTTP method of the request.\n2. `__ow_headers` - HTTP request headers.\n3. `__ow_path` - Unmatched URL path of the request.\n4. `__ow_body` - Body entity from request.\n5. `__ow_query` - Query parameters from the request.\n\n**Full details on this feature are available in this [here](https://github.com/apache/incubator-openwhisk/blob/master/docs/webactions.md).**\n\n## Scheduled Invocations\n\nFunctions can be set up to fire automatically using the [alarm package](https://github.com/openwhisk/openwhisk/blob/master/docs/catalog.md#using-the-alarm-package). This allows you to invoke functions with preset parameters at specific times (*12:00 each day*) or according to a schedule (*every ten minutes*).\n\nScheduled invocation for functions can be configured through the `serverless.yaml` file.\n\nThe `schedule` event configuration is controlled by a string, based on the UNIX crontab syntax, in the format `cron(X X X X X)`. This can either be passed in as a native string or through the `rate` parameter.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    events:\n      - schedule: cron(* * * * *) // fires each minute.\n```\n\nThis above example generates a new trigger (`${service}_crawl_schedule_trigger`) and rule (`${service}_crawl_schedule_rule`) during deployment.\n\nOther `schedule` event parameters can be manually configured, e.g trigger or rule names.\n\n```yaml\nfunctions:\n  aggregate:\n    handler: statistics.handler\n    events:\n      - schedule:\n          rate: cron(0 * * * *) // call once an hour\n          trigger: triggerName\n          rule: ruleName\n          max: 10000 // max invocations, default: 1000, max: 10000\n          params: // event params for invocation\n            hello: world\n```\n\n## IBM Message Hub Events\n\nIBM Bluemix provides an \"Apache Kafka\"-as-a-Service called IBM Message Hub. Functions can be connected to fire when messages arrive on Kafka topics.\n\nIBM Message Hub instances can be provisioned through the IBM Bluemix platform. OpenWhisk on Bluemix will export Message Hub service credentials bound to a package with the following name:\n\n```\n/${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n```\n\nRather than having to manually define all the properties needed by the Message Hub trigger feed, you can reference a package to use instead. Credentials from the referenced package will be used when executing the trigger feed.\n\nDevelopers only need to add the topic to listen to for each trigger.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - message_hub:\n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                topic: my_kafka_topic\n\n```\n\nThe plugin will create a trigger called `${serviceName}_${fnName}_messagehub_${topic}` and a rule called `${serviceName}_${fnName}_messagehub_${topic}_rule` to bind the function to the message hub events.\n\nThe trigger and rule names created can be set explicitly using the `trigger` and`rule` parameters.\n\nOther functions can bind to the same trigger using the inline `trigger` event referencing this trigger name.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - message_hub:\n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                topic: my_kafka_topic\n                trigger: log_events\n                rule: connect_index_to_kafka\n     another:\n        handler: users.another\n        events:\n            - trigger: log_events\n```\n\n### Using Manual Parameters\n\nParameters for the Message Hub event source can be defined explicitly, rather than using pulling credentials from a package.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - message_hub:\n                topic: my_kafka_topic\n                brokers: afka01-prod01.messagehub.services.us-south.bluemix.net:9093\n                user: USERNAME\n                password: PASSWORD\n                admin_url:  https://kafka-admin-prod01.messagehub.services.us-south.bluemix.net:443\n                json: true\n                binary_key: true\n                binary_value: true\n```\n\n`topic`, `brokers`, `user`, `password` and `admin_url` are mandatory parameters.\n\n## Cloudant DB Events\n\nIBM Cloudant provides a hosted NoSQL database, based upon CouchDB, running on IBM Bluemix. Functions can be connected to events fired when the database is updated. These events use the [CouchDB changes feed](http://guide.couchdb.org/draft/notifications.html) to follow database modifications.\n\nIBM Cloudant instances can be provisioned through the IBM Bluemix platform. OpenWhisk on Bluemix will export Cloudant service credentials bound to a package with the following name:\n\n```\n/${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n```\n\nRather than having to manually define all the properties needed by the [Cloudant trigger feed](https://github.com/openwhisk/openwhisk-package-cloudant#using-the-cloudant-package), you can reference a package to use instead. Credentials from the referenced package will be used when executing the trigger feed.\n\nDevelopers only need to add the database name to follow for modifications.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant:\n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                db: my_db_name\n\n```\n\nThe plugin will create a trigger called `${serviceName}_${fnName}_cloudant_${topic}` and a rule called `${serviceName}_${fnName}_cloudant_${topic}_rule` to bind the function to the Cloudant update events.\n\nThe trigger and rule names created can be set explicitly using the `trigger` and`rule` parameters.\n\nOther functions can bind to the same trigger using the inline `trigger` event referencing this trigger name.\n\n### Using Manual Parameters\n\nParameters for the Cloudant event source can be defined explicitly, rather than using pulling credentials from a package.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant: // basic auth example\n                host: xxx-yyy-zzz-bluemix.cloudant.com\n                username: USERNAME\n                password: PASSWORD\n                db: db_name\n\t\t\t- cloudant: // iam auth example\n                host: xxx-yyy-zzz-bluemix.cloudant.com\n                iam_api_key: IAM_API_KEY\n                db: db_name\n```\n\n `username` and `password` or `iam_api_key` parameters can be used for authentication.\n\n### Adding Optional Parameters\n\nThe following optional feed parameters are also supported:\n\n* `max` - Maximum number of triggers to fire. Defaults to infinite.\n* `filter` - Filter function defined on a design document.\n* `query` - Optional query parameters for the filter function.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant:\n                ...\n                max: 10000\n                query:\n                   status: new\n                filter: mailbox/by_status\n```\n\n## Custom Event Triggers\n\nFunctions are connected to event sources in OpenWhisk [using triggers and rules](https://github.com/openwhisk/openwhisk/blob/master/docs/triggers_rules.md). Triggers create a named event stream within the system. Triggers can be fired manually or connected to external data sources, like databases or message queues.\n\nRules set up a binding between triggers and serverless functions. With an active rule, each time a trigger is fired, the function will be executed with the trigger payload.\n\nEvent binding for functions can be configured through the `serverless.yaml` file.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    events:\n      - trigger: my_trigger\n```\nThis configuration will create a trigger called `servicename-my_trigger` with an active rule binding `my_function` to this event stream.\n\n### Customising Rules\n\nRule names default to the following format `servicename-trigger-to-action`. These names be explicitly set through configuration.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    events:\n      - trigger:\n        name: \"my_trigger\"\n        rule: \"rule_name\"\n```\n\n### Customing Triggers\n\nTriggers can be defined as separate resources in the `serverless.yaml` file. This allows you to set up trigger properties like default parameters.\n\n```yaml\nfunctions:\n  my_function:\n    handler: index.main\n    events:\n      - trigger: my_trigger\n\nresources:\n  triggers:\n    my_trigger:\n      parameters:\n        hello: world            \n```\n\n### Trigger Feeds\n\nTriggers can be bound to external event sources using the `feed` property. OpenWhisk [provides a catalogue](https://github.com/openwhisk/openwhisk/blob/master/docs/catalog.md) of third-party event sources bundled as [packages](https://github.com/openwhisk/openwhisk/blob/master/docs/packages.md#creating-and-using-trigger-feeds).\n\nThis example demonstrates setting up a trigger which uses the `/whisk.system/alarms/alarm` feed. The `alarm` feed will fire a trigger according to a user-supplied cron schedule.\n\n```yaml\nresources:\n  triggers:\n    alarm_trigger:\n      parameters:\n        hello: world\n      feed: /whisk.system/alarms/alarm\n      feed_parameters:\n        cron: '*/8 * * * * *'\n```\n\n## Commands\n\nThe following serverless commands are currently implemented for the OpenWhisk provider.\n\n- `deploy` - [Deploy functions, triggers and rules for service](https://serverless.com/framework/docs/providers/openwhisk/cli-reference/deploy/).\n- `invoke`- [Invoke deployed serverless function and show result](https://serverless.com/framework/docs/providers/openwhisk/cli-reference/invoke/).\n- `invokeLocal`- [Invoke serverless functions locally and show result](https://serverless.com/framework/docs/providers/openwhisk/cli-reference/invoke#invoke-local).\n- `remove` - [Remove functions, triggers and rules for service](https://serverless.com/framework/docs/providers/openwhisk/cli-reference/remove/).\n- `logs` - [Display activation logs for deployed function](https://serverless.com/framework/docs/providers/openwhisk/cli-reference/logs/).\n- `info` - [Display details on deployed functions, triggers and rules](https://serverless.com/framework/docs/providers/openwhisk/cli-reference/info/).\n"
  },
  {
    "path": "compile/apigw/README.md",
    "content": "# Compile API Gateway Endpoints\n\nThis plugins compiles the HTTP events bound to functions in `serverless.yaml` to\ncorresponding [OpenWhisk API Gateway endpoint](https://github.com/openwhisk/openwhisk/blob/master/docs/apigateway.md)\ndefinitions.\n\n## How it works\n\n`Compile HTTP` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all functions which are defined in `serverless.yaml` looking for\nthe defined events. For each `http` event defined for the function, the\ncorresponding API gateway endpoint definition will be created.\n\n## Examples\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.handler\n        events:\n            - http: GET /api/greeting\n```\n\nThis definition will create a new endpoint, which binds the configured Action\n(index) to the URL path (/api/greeting) and HTTP method (GET).\n\nHTTP operation and path parameters can also be passed object properties on the\nevent object.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.handler\n        events:\n            - http: \n                method: GET \n                basepath: /mypath\n                path: /api/greeting\n                resp: json\n```\n\nDuring deployment the endpoint configuration file will be uploaded to OpenWhisk.\nEach user has a unique hostname which provides access to the configured API\nendpoints. Invoking the endpoints on the gateway host will execute functions\non-demand.\n\nAPI Gateway hosts serving the API endpoints will be shown during deployment.\n\n```shell\n$ serverless deploy\n...\nendpoints:\nGET https://xxx-gws.api-gw.mybluemix.net/api/greeting --> index\n```\n\nCalling the configured API endpoints will execute the deployed functions.\n\n````shell\n$ http get https://xxx-gws.api-gw.mybluemix.net/api/greeting?user=\"James Thomas\"\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\nDate: Mon, 19 Dec 2016 15:47:53 GMT\n\n{\n    \"message\": \"Hello James Thomas!\"\n}\n````\n"
  },
  {
    "path": "compile/apigw/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst crypto = require('crypto');\nconst { formatApiHost } = require('../../utils');\n\nclass OpenWhiskCompileHttpEvents {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'before:package:compileEvents': this.setup.bind(this),\n      'before:package:compileFunctions': this.addWebAnnotations.bind(this),\n      'package:compileEvents': this.compileHttpEvents.bind(this)\n    };\n  }\n\n  setup() {\n    // This object will be used to store the endpoint definitions, passed directly to\n    // the OpenWhisk SDK during the deploy process.\n    this.serverless.service.apigw = {};\n\n    // Actions and Triggers referenced by Rules must used fully qualified identifiers (including namespace).\n    if (!this.serverless.service.provider.namespace) {\n      return this.provider.props().then(props => {\n        this.serverless.service.provider.namespace = props.namespace;\n        this.serverless.service.provider.host = props.apihost;\n      });\n    }\n  }\n\n  generateAuthString() {\n    return crypto.randomBytes(64).toString('hex')\n  }\n\n  // HTTP events need Web Actions enabled for those functions. Add\n  // annotation 'web-export' if it is not already present.\n  addWebAnnotations() {\n    const names = Object.keys(this.serverless.service.functions)\n    names.forEach(fnName => {\n      const f = this.serverless.service.functions[fnName]\n      const httpEvents = (f.events || []).filter(e => e.http)\n      if (httpEvents.length) {\n        if (!f.annotations) f.annotations = {}\n        f.annotations['web-export'] = true\n\n        if (!f.annotations.hasOwnProperty('require-whisk-auth')) {\n          f.annotations['require-whisk-auth'] = this.generateAuthString()\n        }\n      }\n    })\n\n    return BbPromise.resolve();\n  }\n\n  calculateFunctionName(functionName, functionObject) {\n    return functionObject.name || `${this.serverless.service.service}_${functionName}`;\n  }\n\n  calculateFunctionNameSpace(functionObject) {\n    return functionObject.namespace\n      || this.serverless.service.provider.namespace\n      || '_';\n  }\n\n  retrieveAuthKey(functionObject) {\n    const annotations = functionObject.annotations || {}\n    return annotations['require-whisk-auth']\n  }\n\n  //\n  // This method takes the rule definitions, parsed from the user's YAML file,\n  // and turns it into the OpenWhisk Rule resource object.\n  //\n  // These resource objects are passed to the OpenWhisk SDK to create the associated Rules\n  // during the deployment process.\n  //\n  // Parameter values will be parsed from the user's YAML definition, either as a value from\n  // the rule definition or the service provider defaults.\n  compileHttpEvent(funcName, funcObj, http) {\n    const options = this.parseHttpEvent(http);\n    options.namespace = this.calculateFunctionNameSpace(funcName, funcObj);\n\n    const name = this.calculateFunctionName(funcName, funcObj).split('/');\n    options.action = name.pop();\n    options.pkge = name.pop() || \"default\";\n\n    const secure_key = this.retrieveAuthKey(funcObj)\n    if (secure_key) {\n      options.secure_key = secure_key;\n    }\n\n    return options;\n  }\n\n  parseHttpEvent(httpEvent) {\n    if (httpEvent.path && httpEvent.method) {\n      return { relpath: httpEvent.path, operation: httpEvent.method, responsetype: httpEvent.resp || 'json' };\n    } else if (typeof httpEvent === 'string') {\n      const method_and_path = httpEvent.trim().split(' ');\n      if (method_and_path.length !== 2) {\n        throw new this.serverless.classes.Error(\n          `Incorrect HTTP event parameter value (${httpEvent}), must be string in form: HTTP_METHOD API_PATH e.g. GET /api/foo`);\n      }\n      return { operation: method_and_path[0], relpath: method_and_path[1], responsetype: httpEvent.resp || 'json'  }\n    } \n\n    throw new this.serverless.classes.Error(\n      `Incorrect HTTP event parameter value (${httpEvent}), must be string (\"GET /api/foo\") or object ({method: \"GET\", path: \"/api/foo\"})`);\n  }\n\n  addAuthToSwagger(swagger, auth) {\n    if (!auth.key && auth.secret) {\n      throw new this.serverless.classes.Error(\n        \"Missing mandatory resources.apigw.auth.key parameter. Must be defined to enable authentication.\"\n      )\n    }\n    swagger.security = [{ client_id: [] }]\n    const client_id = {\n      in: \"header\", name: auth.key,\n      type: \"apiKey\", \"x-key-type\": \"clientId\"\n    }\n    swagger.securityDefinitions = { client_id }\n\n    if (auth.secret) {\n      swagger.security[0].client_secret = []\n      swagger.securityDefinitions.client_secret = { \n        in: \"header\", name: auth.secret,\n        type: \"apiKey\", \"x-key-type\": \"clientSecret\"\n      }\n    }\n  }\n\n  addOAuthToSwagger(swagger, oauth) {\n    if (!oauth.provider) return\n\n    const providers_urls = {\n      google: \"https://www.googleapis.com/oauth2/v3/tokeninfo\",\n      facebook: \"https://graph.facebook.com/debug_token\",\n      github: \"https://api.github.com/user\",\n    }\n\n    if (oauth.provider !== 'app-id' && oauth.provider !== 'google'\n      && oauth.provider !== 'facebook' && oauth.provider !== 'github') {\n      throw new this.serverless.classes.Error(\n        `OAuth defined with invalid provider (${oauth.provider}), must be: app-id, google, facebook, github.`\n      )\n    }\n\n    const security = {}\n    security[oauth.provider] = []\n    swagger.security = [ security ]\n\n    const definition = {\n      flow: \"application\", tokenUrl: \"\", type: \"oauth2\",\n      \"x-provider\": { name: oauth.provider },\n      \"x-tokenintrospect\": { url: null }\n    }\n\n    if (oauth.provider === 'app-id') {\n      if (!oauth.tenant) {\n        throw new this.serverless.classes.Error(\n          `OAuth provider app-id defined without tenant parameter`\n        )\n      }\n      definition['x-provider'].params = { tenantId: oauth.tenant }\n    } else {\n      definition['x-tokenintrospect'].url = providers_urls[oauth.provider]\n    }\n\n    const securityDefinitions = {}\n    securityDefinitions[oauth.provider] = definition\n    swagger.securityDefinitions = securityDefinitions\n  }\n\n  addRateLimitToSwagger(swagger, rate_limit) {\n    const rate = rate_limit.rate\n    const unit = rate_limit.unit\n\n    if (!rate) {\n      throw new this.serverless.classes.Error(\n        \"Missing rate limit parameter: rate.\"\n      )\n    }\n    if (!unit) {\n      throw new this.serverless.classes.Error(\n        \"Missing rate limit parameter: unit.\"\n      )\n    }\n\n    if (unit !== \"minute\" && unit !== \"second\" && unit !== \"hour\" && unit !== \"day\") {\n      throw new this.serverless.classes.Error(\n        \"Invalid rate limit parameter: unit.\"\n      )\n    }\n    swagger[\"x-ibm-rate-limit\"] = [{\n      rate: rate_limit.rate,\n      unit: rate_limit.unit,\n      units: 1\n    }]\n  }\n\n  generateSwagger(service, host, options, httpEvents) {\n    const paths = httpEvents.reduce((paths, httpEvent) => {\n      const path = paths[httpEvent.relpath] || {}\n      const operation = httpEvent.operation.toLowerCase()\n      \n      path[operation] = this.compileSwaggerPath(httpEvent, host)\n      paths[httpEvent.relpath] = path\n\n      return paths\n    }, {})\n\n    const cases = httpEvents.map(httpEvent => this.compileSwaggerCaseSwitch(httpEvent, host))\n    const execute_body = { \"operation-switch\": { case: cases } }\n    const enabled = options.hasOwnProperty('cors') ? options.cors : true\n\n    const x_ibm_configuration = { \n      cors: { enabled },\n      assembly: { execute: [ execute_body ] }\n    }\n\n    const swagger = {\n      swagger: \"2.0\",\n      basePath: options.basepath || \"/\",\n      info: {\n        title: options.name || service,\n        version: \"1.0\"\n      },\n      paths,\n      \"x-ibm-configuration\": x_ibm_configuration\n    }\n\n    if (options.auth) {\n      this.addAuthToSwagger(swagger, options.auth)\n    }\n\n    if (options.oauth) {\n      this.addOAuthToSwagger(swagger, options.oauth)\n    }\n\n    if (options.rate_limit) {\n      this.addRateLimitToSwagger(swagger, options.rate_limit)\n    }\n\n    return swagger\n  }\n\n  compileSwaggerPath(httpEvent, host) {\n    const operationId = this.operationId(httpEvent)\n    const pathParameters = this.parsePathParameters(httpEvent.relpath)\n    const responses = { \"200\": { description: \"A successful invocation response\" } }\n    const webaction_url = this.webActionUrl(httpEvent, host)\n\n    const x_ow = {\n      action: httpEvent.action, namespace: httpEvent.namespace,\n      package: httpEvent.pkge, url: webaction_url\n    }\n    \n    const swaggerPath = { operationId, responses, \"x-openwhisk\": x_ow }\n\n    if (pathParameters.length) {\n      swaggerPath.parameters = pathParameters.map(this.createPathParameter)\n    }\n\n    return swaggerPath\n  }\n\n  parsePathParameters (path) {\n    const regex = /{([^}]+)\\}/g\n    const findAllParams = p => {\n      const ids = []\n      let id = regex.exec(p)\n      while (id) {\n        ids.push(id[1])\n        id = regex.exec(p)\n      }\n      return ids\n    }\n\n    return path.split('/')\n      .map(findAllParams)\n      .reduce((sum, el) => sum.concat(el), [])\n  }\n\n  createPathParameter (name) {\n    return {\n      name: name, in: 'path',\n      description: `Default description for '${name}'`,\n      required: true, type: 'string'\n    }\n  }\n\n  compileSwaggerCaseSwitch(httpEvent, host) {\n    const pathParameters = this.parsePathParameters(httpEvent.relpath)\n    const webaction_url = this.webActionUrl(httpEvent, host, !!pathParameters.length)\n    const operationId = this.operationId(httpEvent)\n\n    const header = {\n      set: \"message.headers.X-Require-Whisk-Auth\",\n      value: httpEvent.secure_key\n    }\n\n    const execute = [\n      { \"set-variable\": { actions: [ header ] } },\n      { invoke: { \"target-url\": webaction_url, verb: \"keep\" } }\n    ]\n    const operations = [ operationId ]\n\n    const swaggerCaseSwitch = { execute, operations }\n    return swaggerCaseSwitch\n  }\n\n  webActionUrl(httpEvent, host, has_path_params) {\n    const url = `${formatApiHost(host)}/api/v1/web/${httpEvent.namespace}/${httpEvent.pkge}/${httpEvent.action}.${httpEvent.responsetype}${has_path_params ? '$(request.path)': ''}`\n\n    return url\n  }\n\n  operationId(httpEvent) {\n    return `${httpEvent.operation}-${httpEvent.relpath}`.toLowerCase()\n  }\n\n  compileFunctionHttpEvents(functionName, functionObject) {\n    if (!functionObject.events) return []\n\n    const events = functionObject.events\n      .filter(e => e.http)\n      .map(e => this.compileHttpEvent(functionName, functionObject, e.http))\n\n    if (events.length && this.options.verbose) {\n      this.serverless.cli.log(`Compiled API Gateway definition (${functionName}): ${JSON.stringify(events)}`);\n    }\n\n    return events\n  }\n\n  compileHttpEvents () {\n    this.serverless.cli.log('Compiling API Gateway definitions...');\n\n    const allFunctions = this.serverless.service.getAllFunctions()\n\n    const httpEvents = allFunctions.map(\n      functionName => this.compileFunctionHttpEvents(functionName, this.serverless.service.getFunction(functionName))\n    ).reduce((a, b) => a.concat(b), [])\n\n    if (httpEvents.length) {\n      const service = this.serverless.service.service\n      const host = this.serverless.service.provider.host\n      const resources = this.serverless.service.resources || {}\n      const options = resources.apigw || {}\n      this.serverless.service.apigw.swagger = this.generateSwagger(service, host, options, httpEvents)\n    }\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskCompileHttpEvents;\n"
  },
  {
    "path": "compile/apigw/tests/index.js",
    "content": "'use strict';\n\nconst crypto = require('crypto');\nconst BbPromise = require('bluebird');\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileHttpEvents = require('../index');\n\ndescribe('OpenWhiskCompileHttpEvents', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileHttpEvents;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileHttpEvents = new OpenWhiskCompileHttpEvents(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskCompileHttpEvents.setup();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n\n  describe('#addWebAnnotations()', () => {\n    it('should add annotations when http event present', () => {\n      openwhiskCompileHttpEvents.serverless.service.functions = {\n        a: { events: [ { http: true } ], annotations: {} },\n        b: { events: [ { http: true } ], annotations: { foo: 'bar' } },\n        c: { events: [ { http: true } ], annotations: { 'web-export': false } },\n        d: { events: [ { http: true } ] }\n      }\n\n      const auth_string = crypto.randomBytes(64).toString('hex');\n      sandbox.stub(openwhiskCompileHttpEvents, 'generateAuthString', () => auth_string);\n\n      return openwhiskCompileHttpEvents.addWebAnnotations().then(() => {\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.a.annotations).to.deep.equal({ 'web-export': true, 'require-whisk-auth': auth_string })\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.b.annotations).to.deep.equal({ foo: 'bar', 'web-export': true, 'require-whisk-auth': auth_string })\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.c.annotations).to.deep.equal({ 'web-export': true, 'require-whisk-auth': auth_string })\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.d.annotations).to.deep.equal({ 'web-export': true, 'require-whisk-auth': auth_string })\n      })\n    });\n\n    it('should not add auth annotation when annotation already present', () => {\n      openwhiskCompileHttpEvents.serverless.service.functions = {\n        a: { events: [ { http: true } ], annotations: { 'require-whisk-auth': false } },\n        b: { events: [ { http: true } ], annotations: { 'require-whisk-auth': true } },\n        c: { events: [ { http: true } ], annotations: { 'require-whisk-auth': 'some string' } }\n      }\n\n      return openwhiskCompileHttpEvents.addWebAnnotations().then(() => {\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.a.annotations).to.deep.equal({ 'web-export': true, 'require-whisk-auth': false })\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.b.annotations).to.deep.equal({ 'web-export': true, 'require-whisk-auth': true })\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.c.annotations).to.deep.equal({ 'web-export': true, 'require-whisk-auth': 'some string' })\n      })\n    });\n\n    it('should ignore annotations when http event not present', () => {\n      openwhiskCompileHttpEvents.serverless.service.functions = {\n        a: { },\n        b: { events: [] },\n        c: { events: [], annotations: { hello: 'world', 'web-export': true } }\n      }\n      return openwhiskCompileHttpEvents.addWebAnnotations().then(() => {\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.a.annotations).to.be.equal(undefined)\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.b.annotations).to.be.equal(undefined)\n        expect(openwhiskCompileHttpEvents.serverless.service.functions.c.annotations).to.deep.equal({ hello: 'world', 'web-export': true })\n      })\n    });\n  })\n\n  describe('#compileHttpEvents()', () => {\n    it('should return empty swagger if functions has no http events', () =>\n      expect(openwhiskCompileHttpEvents.compileHttpEvents().then(() => {\n        expect(openwhiskCompileHttpEvents.serverless.service.apigw).to.deep.equal({});\n      })).to.eventually.be.fulfilled\n    );\n\n    it('should call compileFunctionEvents for each function with events', () => {\n      const stub = sinon.stub(openwhiskCompileHttpEvents, 'compileFunctionHttpEvents').returns([{foo: 'bar'}]);\n\n      sandbox.stub(openwhiskCompileHttpEvents.serverless.service, 'getAllFunctions', () => [\"first\", \"second\", \"third\"]);\n\n      sandbox.stub(openwhiskCompileHttpEvents, 'generateSwagger', () => ({\"swagger\": {}}));\n      const handler = name => ({events: {}})\n      openwhiskCompileHttpEvents.serverless.service.getFunction = handler;\n\n      return expect(openwhiskCompileHttpEvents.compileHttpEvents().then(() => {\n        expect(openwhiskCompileHttpEvents.serverless.service.apigw.swagger).to.deep.equal(\n          {swagger: {}}\n        );\n        expect(stub.calledThrice).to.be.equal(true);\n      })).to.eventually.be.fulfilled;\n    });\n  });\n\n  describe('#compileFunctionHttpEvents()', () => {\n    it('should not call compileHttpEvents when events parameter is missing', () => {\n      const stub = sinon.stub(openwhiskCompileHttpEvents, 'compileHttpEvent')\n      const events = openwhiskCompileHttpEvents.compileFunctionHttpEvents('name', {})\n      expect(events).to.deep.equal([]);\n      expect(stub.called).to.be.equal(false);\n    })\n    \n    it('should not call compileHttpEvents when events list contains no events', () => {\n      const stub = sinon.stub(openwhiskCompileHttpEvents, 'compileHttpEvent')\n      const events = openwhiskCompileHttpEvents.compileFunctionHttpEvents('name', { events: [{\"trigger\": {}}] })\n      expect(events).to.deep.equal([]);\n      expect(stub.called).to.be.equal(false);\n    })\n\n    it('should call compileHttpEvents when events list contains triggers', () => {\n      const stub = sinon.stub(openwhiskCompileHttpEvents, 'compileHttpEvent').returns({})\n      const events = openwhiskCompileHttpEvents.compileFunctionHttpEvents('name', { events: [\n        {\"http\": true},\n        {\"http\": true},\n        {\"http\": true}\n      ] })\n      expect(events).to.deep.equal([{}, {}, {}]);\n      expect(stub.calledThrice).to.be.equal(true);\n    })\n\n    it('should log event when verbose flag is used', () => {\n      openwhiskCompileHttpEvents.options.verbose = true\n      const log = sandbox.stub(openwhiskCompileHttpEvents.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskCompileHttpEvents.serverless.cli, 'consoleLog')\n      const stub = sinon.stub(openwhiskCompileHttpEvents, 'compileHttpEvent').returns({ foo: 'bar' })\n      openwhiskCompileHttpEvents.compileFunctionHttpEvents('name', { events: [\n        {\"http\": true},\n        {\"http\": true},\n        {\"http\": true}\n      ] })\n\n      expect(log.calledOnce).to.be.equal(true);\n      const result = JSON.stringify([{foo: \"bar\"}, {foo: \"bar\"}, {foo: \"bar\"}])\n      expect(log.args[0][0]).to.be.equal(`Compiled API Gateway definition (name): ${result}`);\n    })\n  });\n\n  describe('#compileHttpEvent()', () => {\n    it('should define http events from string property', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n      const http =  \"GET /api/foo/bar\"\n      const result = openwhiskCompileHttpEvents.compileHttpEvent('action-name', {}, http);\n      return expect(result).to.deep.equal({\n        relpath: '/api/foo/bar',\n        operation: 'GET',\n        pkge: 'default',\n        namespace: 'sample_ns',\n        action: 'my-service_action-name',\n        responsetype: 'json'\n      });\n    });\n\n    it('should define http events from string property with explicit package', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n      const http =  \"GET /api/foo/bar\"\n      const fnObj = { name: 'somePackage/actionName' }\n      const result = openwhiskCompileHttpEvents.compileHttpEvent('action-name', fnObj, http);\n      return expect(result).to.deep.equal({\n        relpath: '/api/foo/bar',\n        operation: 'GET',\n        pkge: 'somePackage',\n        namespace: 'sample_ns',\n        action: 'actionName',\n        responsetype: 'json'\n      });\n    });\n\n    it('should define http events from object property', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n      const http =  {path: \"/api/foo/bar\", method: \"GET\"}\n      const result = openwhiskCompileHttpEvents.compileHttpEvent('action-name', {}, http);\n      return expect(result).to.deep.equal({relpath: '/api/foo/bar', operation: 'GET', action: 'my-service_action-name', namespace: 'sample_ns', pkge: 'default', responsetype: 'json'});\n    });\n\n    it('should add secure auth key if present', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n      const http =  {path: \"/api/foo/bar\", method: \"GET\"}\n      const result = openwhiskCompileHttpEvents.compileHttpEvent('action-name', {\n        annotations: { 'require-whisk-auth': 'auth-token' }\n      }, http);\n      return expect(result).to.deep.equal({relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token', action: 'my-service_action-name', namespace: 'sample_ns', pkge: 'default', responsetype: 'json'});\n    });\n\n    it('should define http events with explicit response type', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n      const http =  {path: \"/api/foo/bar\", method: \"GET\", resp: 'http'}\n      const result = openwhiskCompileHttpEvents.compileHttpEvent('action-name', {}, http);\n      return expect(result).to.deep.equal({relpath: '/api/foo/bar', operation: 'GET', action: 'my-service_action-name', namespace: 'sample_ns', pkge: 'default', responsetype: 'http'});\n    });\n\n    it('should throw if http event value invalid', () => {\n      expect(() => openwhiskCompileHttpEvents.compileHttpEvent('', {}, 'OPERATION'))\n        .to.throw(Error, /Incorrect HTTP event/);\n      expect(() => openwhiskCompileHttpEvents.compileHttpEvent('', {}, {}))\n        .to.throw(Error, /Incorrect HTTP event/);\n      expect(() => openwhiskCompileHttpEvents.compileHttpEvent('', {}, {method: true}))\n        .to.throw(Error, /Incorrect HTTP event/);\n      expect(() => openwhiskCompileHttpEvents.compileHttpEvent('', {}, {path: true}))\n        .to.throw(Error, /Incorrect HTTP event/);\n    });\n  });\n\n  describe('#compileSwaggerPath()', () => {\n    it('should define swagger definition from http events', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const result = openwhiskCompileHttpEvents.compileSwaggerPath(httpEvent, host);\n\n      const expectedResult = {\n        operationId: \"get-/api/foo/bar\",\n        responses: {\n          \"200\": { description: \"A successful invocation response\" }\n        },\n        \"x-openwhisk\": {\n          action: \"action-name\",\n          namespace: \"user@host.com_space\",\n          package: \"default\",\n          url: \"https://openwhisk.somewhere.com/api/v1/web/user@host.com_space/default/action-name.json\"\n        }\n      }\n\n      return expect(result).to.deep.equal(expectedResult)\n    });\n\n    it('should define swagger definition from http events and respect specified protocol on api host', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'http://openwhisk.somewhere.com'\n      const result = openwhiskCompileHttpEvents.compileSwaggerPath(httpEvent, host);\n\n      const expectedResult = {\n        operationId: \"get-/api/foo/bar\",\n        responses: {\n          \"200\": { description: \"A successful invocation response\" }\n        },\n        \"x-openwhisk\": {\n          action: \"action-name\",\n          namespace: \"user@host.com_space\",\n          package: \"default\",\n          url: \"http://openwhisk.somewhere.com/api/v1/web/user@host.com_space/default/action-name.json\"\n        }\n      }\n\n      return expect(result).to.deep.equal(expectedResult)\n    });\n\n    it('should define swagger definition with path parameters', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/{id}', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'http'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const result = openwhiskCompileHttpEvents.compileSwaggerPath(httpEvent, host);\n\n      const expectedResult = {\n        operationId: \"get-/api/foo/{id}\",\n        parameters: [{\n          name: \"id\", in: \"path\",\n          description: \"Default description for 'id'\",\n          required: true, type: \"string\"\n        }],\n        responses: {\n          \"200\": { description: \"A successful invocation response\" }\n        },\n        \"x-openwhisk\": {\n          action: \"action-name\",\n          namespace: \"user@host.com_space\",\n          package: \"default\",\n          url: \"https://openwhisk.somewhere.com/api/v1/web/user@host.com_space/default/action-name.http\"\n        }\n      }\n\n      return expect(result).to.deep.equal(expectedResult)\n    });\n \n  });\n\n  describe('#compileSwaggerCaseSwitch()', () => {\n    it('should define swagger case statement from http events', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const result = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(httpEvent, host);\n\n      const expectedResult = {\n        execute: [{\n            \"set-variable\": {\n              actions: [{\n                  set: \"message.headers.X-Require-Whisk-Auth\",\n                  value: \"auth-token\"\n              }]\n            }\n          },\n          {\n            invoke: {\n              \"target-url\": \"https://openwhisk.somewhere.com/api/v1/web/user@host.com_space/default/action-name.json\",\n              \"verb\": \"keep\"\n            }\n          }\n        ],\n        operations: [ \"get-/api/foo/bar\" ]\n      }\n\n      return expect(result).to.deep.equal(expectedResult)\n    });\n\n    it('should define swagger case statement from http events with path parameters', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/{id}', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'http'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const result = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(httpEvent, host);\n\n      const expectedResult = {\n        execute: [{\n            \"set-variable\": {\n              actions: [{\n                  set: \"message.headers.X-Require-Whisk-Auth\",\n                  value: \"auth-token\"\n              }]\n            }\n          },\n          {\n            invoke: {\n              \"target-url\": \"https://openwhisk.somewhere.com/api/v1/web/user@host.com_space/default/action-name.http$(request.path)\",\n              \"verb\": \"keep\"\n            }\n          }\n        ],\n        operations: [ \"get-/api/foo/{id}\" ]\n      }\n\n      return expect(result).to.deep.equal(expectedResult)\n    });\n  });\n\n  describe('#generateSwagger()', () => {\n    it('should generate APIGW swagger with paths and case statements from http event', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = \"false\"\n      const swaggerPath = openwhiskCompileHttpEvents.compileSwaggerPath(httpEvent, host)\n      const swaggerCase = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(httpEvent, host)\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n      const swaggerCases = result[\"x-ibm-configuration\"]\n        .assembly.execute[0][\"operation-switch\"].case \n\n      expect(result.swagger).to.equal(\"2.0\")\n      expect(result.basePath).to.equal(\"/\")\n      expect(result.info.title).to.equal(service)\n      expect(result.info.version).to.equal(\"1.0\")\n      expect(result.paths[\"/api/foo/bar\"].get).to.deep.equal(swaggerPath)\n      expect(swaggerCases[0]).to.deep.equal(swaggerCase)\n    });\n\n    it('should generate APIGW swagger with multiple http events on same path', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const gethttpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const posthttpEvent = {\n        relpath: '/api/foo/bar', operation: 'POST', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = \"false\"\n\n      const getswaggerPath = openwhiskCompileHttpEvents.compileSwaggerPath(gethttpEvent, host)\n      const getswaggerCase = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(gethttpEvent, host)\n      const postswaggerPath = openwhiskCompileHttpEvents.compileSwaggerPath(posthttpEvent, host)\n      const postswaggerCase = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(posthttpEvent, host)\n\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ gethttpEvent, posthttpEvent ] );\n      const swaggerCases = result[\"x-ibm-configuration\"]\n        .assembly.execute[0][\"operation-switch\"].case \n\n      expect(result.paths[\"/api/foo/bar\"].get).to.deep.equal(getswaggerPath)\n      expect(result.paths[\"/api/foo/bar\"].post).to.deep.equal(postswaggerPath)\n      expect(swaggerCases.length).to.equal(2)\n      expect(swaggerCases[0]).to.deep.equal(getswaggerCase)\n      expect(swaggerCases[1]).to.deep.equal(postswaggerCase)\n    });\n    \n    it('should generate APIGW swagger with multiple http events on different paths', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const gethttpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const posthttpEvent = {\n        relpath: '/api/foo/ccc', operation: 'POST', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = \"false\"\n\n      const getswaggerPath = openwhiskCompileHttpEvents.compileSwaggerPath(gethttpEvent, host)\n      const getswaggerCase = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(gethttpEvent, host)\n      const postswaggerPath = openwhiskCompileHttpEvents.compileSwaggerPath(posthttpEvent, host)\n      const postswaggerCase = openwhiskCompileHttpEvents.compileSwaggerCaseSwitch(posthttpEvent, host)\n\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ gethttpEvent, posthttpEvent ] );\n      const swaggerCases = result[\"x-ibm-configuration\"]\n        .assembly.execute[0][\"operation-switch\"].case \n\n      expect(result.paths[\"/api/foo/bar\"].get).to.deep.equal(getswaggerPath)\n      expect(result.paths[\"/api/foo/ccc\"].post).to.deep.equal(postswaggerPath)\n      expect(swaggerCases.length).to.equal(2)\n      expect(swaggerCases[0]).to.deep.equal(getswaggerCase)\n      expect(swaggerCases[1]).to.deep.equal(postswaggerCase)\n    });\n\n    it('should generate APIGW swagger with default API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = {}\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result[\"x-ibm-configuration\"].cors.enabled).to.equal(true)\n    });\n\n    it('should generate APIGW swagger with custom CORS API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { cors: false }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result[\"x-ibm-configuration\"].cors.enabled).to.equal(false)\n    });\n\n    it('should generate APIGW swagger with custom basepath API gateway option', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { basepath: \"/api\" }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.basePath).to.equal(options.basepath)\n    });\n\n    it('should generate APIGW swagger with custom API name option', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { name: \"my-api\" }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.info.title).to.equal(options.name)\n    });\n\n\n    it('should generate APIGW swagger with custom auth key API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { auth: { key: \"some-header-key\" } }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.security).to.deep.equal([{\"client_id\": []}])\n      expect(result.securityDefinitions.client_id).to.deep.equal({\"in\": \"header\", type: \"apiKey\", \"x-key-type\": \"clientId\", name: \"some-header-key\" })\n    });\n\n    it('should generate APIGW swagger with custom auth key and secret API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { auth: { key: \"some-header-key\", secret: \"some-header-secret\" } }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.security).to.deep.equal([{client_id: [], client_secret: []}])\n      expect(result.securityDefinitions.client_id).to.deep.equal({\"in\": \"header\", type: \"apiKey\", \"x-key-type\": \"clientId\", name: \"some-header-key\" })\n      expect(result.securityDefinitions.client_secret).to.deep.equal({\"in\": \"header\", type: \"apiKey\", \"x-key-type\": \"clientSecret\", name: \"some-header-secret\" })\n    });\n    \n    it('should generate APIGW swagger with AppID OAuth provider API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { oauth: { provider: \"app-id\", tenant: \"some-id\" } }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.security).to.deep.equal([{\"app-id\": []}])\n      expect(result.securityDefinitions[\"app-id\"]).to.deep.equal({\n        flow: \"application\", tokenUrl: \"\", type: \"oauth2\",\n        \"x-provider\": {\n          name: \"app-id\", params: { tenantId: options.oauth.tenant }\n        },\n        \"x-tokenintrospect\": { \"url\": null }\n      })\n    });\n\n    it('should generate APIGW swagger with Google OAuth provider API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { oauth: { provider: \"google\" } }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.security).to.deep.equal([{\"google\": []}])\n      expect(result.securityDefinitions[\"google\"]).to.deep.equal({\n        flow: \"application\", tokenUrl: \"\", type: \"oauth2\",\n        \"x-provider\": { name: \"google\" },\n        \"x-tokenintrospect\": { url: \"https://www.googleapis.com/oauth2/v3/tokeninfo\"}\n      })\n    });\n\n    it('should generate APIGW swagger with Facebook OAuth provider API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { oauth: { provider: \"facebook\" } }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.security).to.deep.equal([{\"facebook\": []}])\n      expect(result.securityDefinitions[\"facebook\"]).to.deep.equal({\n        flow: \"application\", tokenUrl: \"\", type: \"oauth2\",\n        \"x-provider\": { name: \"facebook\" },\n        \"x-tokenintrospect\": { url: \"https://graph.facebook.com/debug_token\"}\n      })\n    });\n\n    it('should generate APIGW swagger with Github OAuth provider API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { oauth: { provider: \"github\" } }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result.security).to.deep.equal([{\"github\": []}])\n      expect(result.securityDefinitions[\"github\"]).to.deep.equal({\n        flow: \"application\", tokenUrl: \"\", type: \"oauth2\",\n        \"x-provider\": { name: \"github\" },\n        \"x-tokenintrospect\": { url: \"https://api.github.com/user\"}\n      })\n    });\n\n    it('should generate APIGW swagger with rate limiting API gateway options', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      const options = { rate_limit: { rate: 1000, unit: \"minute\"} }\n      const result = openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ] );\n\n      expect(result[\"x-ibm-rate-limit\"]).to.deep.equal([{rate: 1000, unit: \"minute\", units: 1}])\n    });\n\n    it('should throw if API GW auth options are invalid', () => {\n      openwhiskCompileHttpEvents.serverless.service.service = 'my-service'\n      openwhiskCompileHttpEvents.serverless.service.provider = {namespace: \"sample_ns\"};\n\n      const httpEvent = {\n        relpath: '/api/foo/bar', operation: 'GET', secure_key: 'auth-token',\n        action: 'action-name', namespace: 'user@host.com_space', pkge: 'default', responsetype: 'json'\n      }\n\n      const host = 'openwhisk.somewhere.com'\n      const service = \"my-service\"\n      let options = { auth: { secret: \"something\" } }\n      \n      expect(() => openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ]))\n        .to.throw(Error, /Missing mandatory resources.apigw.auth.key/);\n\n      options = { rate_limit: { } }\n      expect(() => openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ]))\n        .to.throw(Error, /Missing rate limit parameter: rate/);\n      options = { rate_limit: { rate: 1000 } }\n      expect(() => openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ]))\n        .to.throw(Error, /Missing rate limit parameter: unit/);\n      options = { rate_limit: { rate: 1000, unit: 'blah' } }\n      expect(() => openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ]))\n        .to.throw(Error, /Invalid rate limit parameter: unit/);\n\n      options = { oauth: { provider: 'blah' } }\n      expect(() => openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ]))\n        .to.throw(Error, /OAuth defined with invalid provider/);\n\n      options = { oauth: { provider: 'app-id' } }\n      expect(() => openwhiskCompileHttpEvents.generateSwagger(service, host, options, [ httpEvent ]))\n        .to.throw(Error, /OAuth provider app-id defined without tenant parameter/);\n    });\n  });\n});\n"
  },
  {
    "path": "compile/cloudant/README.md",
    "content": "# Cloudant Events\n\nThis plugins compiles the `cloudant` events in `serverless.yaml` to corresponding [OpenWhisk Cloudant Trigger Feeds](https://github.com/openwhisk/openwhisk-package-cloudant) definitions.\n\n## How it works\n\n`Compile Cloudant` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all schedule event which are defined in `serverless.yaml`.\n\n### Using Package Parameters \n\nIBM Cloudant instances can be provisioned through the IBM Bluemix platform. OpenWhisk on Bluemix will export Cloudant service credentials bound to a package with the following name:\n\n```\n/${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n```\n\nRather than having to manually define all the properties needed by the Cloudant trigger feed, you can reference a package to use instead. Credentials from the referenced package will be used when executing the trigger feed.\n\nDevelopers only need to add the database to listen to for each trigger.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant: \n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                db: db_name\n \n```\n\nThe plugin will create a trigger called `${serviceName}_${fnName}_cloudant_${db}` and a rule called `${serviceName}_${fnName}_cloudant_${db}_rule` to bind the function to the database update events.\n\nThe trigger and rule names created can be set explicitly using the `trigger` and `rule` parameters.\n\nOther functions can bind to the same trigger using the inline `trigger` event referencing this trigger name.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant: \n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                db: my_db\n                trigger: db_events\n                rule: connect_index_to_db \n     another:\n        handler: users.another\n        events:\n            - trigger: db_events \n```\n\n### Using Manual Parameters\n\nTrigger feed parameters for the Cloudant event source can be defined explicitly, rather than using pulling credentials from a package.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant: \n                host: xxx-yyy-zzz-bluemix.cloudant.com\n                username: USERNAME\n                password: PASSWORD\n                db: db_name               \n```\n\n### Adding Optional Parameters\n\nThe following optional feed parameters are also supported:\n\n* `max` - Maximum number of triggers to fire. Defaults to infinite.\n* `filter` - Filter function defined on a design document.\n* `query` - Optional query parameters for the filter function. \n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - cloudant: \n                ...\n                max: 10000 \n                query: \n                   status: new\n                filter: mailbox/by_status\n```\n\n"
  },
  {
    "path": "compile/cloudant/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nclass OpenWhiskCompileCloudant {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n    this.default_package = '/whisk.system/cloudant'\n\n    this.hooks = {\n      'before:package:compileEvents': () => BbPromise.bind(this)\n        .then(this.setup)\n        .then(this.processCloudantEvents)\n    };\n  }\n\n  setup() {\n    if (!this.serverless.service.resources) {\n      this.serverless.service.resources = {};\n    }\n\n    if (!this.serverless.service.resources.triggers) {\n      this.serverless.service.resources.triggers = {};\n    }\n\n    if (!this.serverless.service.resources.rules) {\n      this.serverless.service.resources.rules = {};\n    }\n }\n\n  validateConfig (fnName, config) {\n    if (!config.db) {\n      throw new this.serverless.classes.Error(\n        `Cloudant event property (db) missing on function: ${fnName}`\n      )\n    }\n\n    if (!config.package) {\n      const config_properties = ['db', 'password', 'username', 'host']\n\n      if (!config.db) {\n        throw new this.serverless.classes.Error(\n          `Cloudant event property (db) missing on function: ${fnName}`\n        )\n      }\n\n      if (!config.host) {\n        throw new this.serverless.classes.Error(\n          `Cloudant event property (host) missing on function: ${fnName}`\n        )\n      }\n\n      const has_manual_auth = !!config.username && !!config.password\n      const has_iam_auth = !!config.iam_api_key\n      if (!has_manual_auth && !has_iam_auth) {\n        throw new this.serverless.classes.Error(\n          `Cloudant event authentication property (username & password or iam_api_key) missing on function: ${fnName}`\n        )\n      }\n\n    }\n  }\n\n  compileCloudantTrigger (fnName, config) {\n    this.validateConfig(fnName, config)\n    const name = config.trigger || this.defaultCloudantName(fnName, config.db)\n    const feed = `${config.package || this.default_package}/changes`\n\n    const feed_parameters = { \n      dbname: config.db\n    }\n\n    if (!config.package) {\n      if (config.iam_api_key) {\n        feed_parameters.iamApiKey = config.iam_api_key\n      } else {\n        feed_parameters.username = config.username\n        feed_parameters.password = config.password\n      }\n      feed_parameters.host = config.host\n    }\n\n    if (config.max) {\n      feed_parameters.maxTriggers = config.max\n    }\n\n    if (config.query) {\n      feed_parameters.query_params = config.query\n    }\n\n    if (config.filter) {\n      feed_parameters.filter = config.filter\n    }\n\n    return { name, content: { feed, feed_parameters } }\n  }\n\n  defaultCloudantName (fnName, db) {\n    return `${this.serverless.service.service}_${fnName}_cloudant_${db}`\n  }\n\n  processCloudantEvent (fnName, config) {\n    const fnObj = this.serverless.service.getFunction(fnName)\n    const trigger = this.compileCloudantTrigger(fnName, config)\n    const rule = config.rule || `${this.defaultCloudantName(fnName, config.db)}_rule`\n\n    fnObj.events.push({ trigger: { name: trigger.name, rule } })\n    this.serverless.service.resources.triggers[trigger.name] = trigger.content\n  }\n\n  processCloudantEvents () {\n    this.serverless.service.getAllFunctions().forEach(name => {\n      const fn = this.serverless.service.getFunction(name)\n      const events = (fn.events || []).filter(e => e.cloudant)\n      events.forEach(e => this.processCloudantEvent(name, e.cloudant))\n    })\n  }\n}\n\nmodule.exports = OpenWhiskCompileCloudant\n"
  },
  {
    "path": "compile/cloudant/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileCloudant = require('../index');\n\ndescribe('OpenWhiskCompileCloudant', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileCloudant;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileCloudant = new OpenWhiskCompileCloudant(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { log: () => {} };\n    openwhiskCompileCloudant.setup()\n\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#processCloudantEvents()', () => {\n    it('should call processCloudantEvent for each cloudant event.', () => {\n      const service = openwhiskCompileCloudant.serverless.service;\n      const fns = {\n        first: {\n          events: [{}, {cloudant: {package: 'testing_package', db: 'some_db'}}, {trigger: true}]\n        },\n        second: {\n          events: [{cloudant: {package: 'another_package', db: 'some_db'}}]\n        },\n        third: {}\n      }\n\n      service.getAllFunctions = () => Object.keys(fns)\n      service.getFunction = name => fns[name];\n\n      const spy = openwhiskCompileCloudant.processCloudantEvent = sinon.spy()\n      openwhiskCompileCloudant.processCloudantEvents()\n      expect(spy.calledTwice).to.be.equal(true)\n      expect(spy.withArgs(\"first\", {package: 'testing_package', db: 'some_db'}).calledOnce).to.be.equal(true)\n      expect(spy.withArgs(\"second\", {package: 'another_package', db: 'some_db'}).calledOnce).to.be.equal(true)\n    })\n  })\n\n  describe('#processCloudantEvents()', () => {\n    it('should create trigger & rules and update manifest resources.', () => {\n      const cloudant = { package: 'some_package', db: 'testing' }\n      const fnObj = { events: [{cloudant}] }\n      serverless.service.getFunction = () => fnObj\n      openwhiskCompileCloudant.compileCloudantTrigger = () => ({name: 'serviceName_fnName_cloudant_testing', content: { a: 1 }})\n      openwhiskCompileCloudant.processCloudantEvent(\"fnName\", fnObj.events[0].cloudant)\n      expect(fnObj.events[1]).to.be.deep.equal({\n        trigger: { name: 'serviceName_fnName_cloudant_testing', rule: 'serviceName_fnName_cloudant_testing_rule' }\n      })\n\n      expect(serverless.service.resources.triggers).to.be.deep.equal({serviceName_fnName_cloudant_testing: {a: 1}})\n    })\n  })\n\n  describe('#compileCloudantTrigger()', () => {\n    it('should throw errors for missing db parameter.', () => {\n      expect(() => openwhiskCompileCloudant.compileCloudantTrigger('testing', {}))\n        .to.throw(Error, 'Cloudant event property (db) missing on function: testing');\n    })\n\n    it('should throw errors for missing host parameters without package', () => {\n      const config = { db: 'dbname', username: 'user', password: 'password' }\n\n      expect(() => openwhiskCompileCloudant.compileCloudantTrigger('testing', config))\n        .to.throw(Error, `Cloudant event property (host) missing on function: testing`);\n    })\n\n    it('should throw errors for missing username parameter without package', () => {\n      const config = { db: 'dbname', host: 'host.com', password: 'password' }\n\n      expect(() => openwhiskCompileCloudant.compileCloudantTrigger('testing', config))\n        .to.throw(Error, `Cloudant event authentication property (username & password or iam_api_key) missing on function: testing`);\n    })\n\n    it('should throw errors for missing password parameter without package', () => {\n      const config = { db: 'dbname', host: 'host.com', username: 'username' }\n\n      expect(() => openwhiskCompileCloudant.compileCloudantTrigger('testing', config))\n        .to.throw(Error, `Cloudant event authentication property (username & password or iam_api_key) missing on function: testing`);\n    })\n\n    it('should throw errors for missing authentication parameters without package', () => {\n      const config = { db: 'dbname', host: 'host.com' }\n\n      expect(() => openwhiskCompileCloudant.compileCloudantTrigger('testing', config))\n        .to.throw(Error, `Cloudant event authentication property (username & password or iam_api_key) missing on function: testing`);\n    })\n\n    it('should return trigger for cloudant provider using package.', () => {\n      const db = 'my_db', pkge = '/bluemixOrg_bluemixSpace/packageId'\n      const trigger = openwhiskCompileCloudant.compileCloudantTrigger('testing', { db, 'package': pkge }) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_cloudant_${db}`,\n        content: {\n          feed: `${pkge}/changes`,\n          feed_parameters: {\n            dbname: `${db}`\n          }\n        }\n      })\n    })\n\n    it('should return trigger for cloudant provider with manual username & password configuration.', () => {\n      const config = { db: 'dbname', username: 'user', password: 'password', host: 'hostname' }\n      const trigger = openwhiskCompileCloudant.compileCloudantTrigger('testing', config) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_cloudant_${config.db}`,\n        content: {\n          feed: `/whisk.system/cloudant/changes`,\n          feed_parameters: {\n            username: config.username,\n            password: config.password,\n            host: config.host,\n            dbname: config.db\n          }\n        }\n      })\n    })\n\n    it('should return trigger for cloudant provider with manual iam api key configuration.', () => {\n      const config = { db: 'dbname', iam_api_key: 'api_key', host: 'hostname' }\n      const trigger = openwhiskCompileCloudant.compileCloudantTrigger('testing', config) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_cloudant_${config.db}`,\n        content: {\n          feed: `/whisk.system/cloudant/changes`,\n          feed_parameters: {\n            iamApiKey: config.iam_api_key,\n            host: config.host,\n            dbname: config.db\n          }\n        }\n      })\n    })\n\n    it('should return trigger with optional configuration parameters.', () => {\n      const config = { db: 'dbname', username: 'user', password: 'password', host: 'hostname', max: 10000, query: { key: 'value' }, filter: 'some/view' }\n      const trigger = openwhiskCompileCloudant.compileCloudantTrigger('testing', config) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_cloudant_${config.db}`,\n        content: {\n          feed: `/whisk.system/cloudant/changes`,\n          feed_parameters: {\n            username: config.username,\n            password: config.password,\n            host: config.host,\n            dbname: config.db,\n            maxTriggers: 10000,\n            filter: 'some/view',\n            query_params: {\n              key: 'value'\n            }\n          }\n        }\n      })\n    })\n\n\n  })\n});\n"
  },
  {
    "path": "compile/functions/README.md",
    "content": "# Compile Functions\n\nThis plugins compiles the functions in `serverless.yaml` to corresponding [OpenWhisk Action](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md)\ndefinitions.\n\n## How it works\n\n`Compile Functions` hooks into the [`package:compileFunctions`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all functions which are defined in `serverless.yaml`.\n\nInside the function loop it creates corresponding OpenWhisk Action definition based on the settings\n(e.g. function `name` property or service `defaults`) which are provided in the `serverless.yaml` file.\n\nThe function will be called `<serviceName>_<functionName>` by default but you\ncan specify an alternative name with the help of the functions `name` property.\nThe Action namespace defaults to the service provider namespace but can be set\nmanually, using the `namespace` parameter.\n\nThe functions `MemorySize` is set to `256`, `Timeout to `60` and `Runtime` to `nodejs`. You can overwrite those defaults by setting\ncorresponding entries in the function definition or server provider properties.\n\nAt the end all OpenWhisk Action definitions are merged inside the `serverless.service.actions` section.\n\n### Action Rules\n\nAction [Rules](https://github.com/openwhisk/openwhisk/blob/master/docs/triggers_rules.md), binding Actions to [Triggers](https://github.com/openwhisk/openwhisk/blob/master/docs/triggers_rules.md), can be defined using the `events` property.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - triggers: \n                trigger: \"myTriggerName\"\n```\n\nThis definition will create a new Rule, which binds the configured Action to the\nTrigger.\n\nMore documentation on the Rules configuration can be found in the [`compileRules` plugin](../rules). \n"
  },
  {
    "path": "compile/functions/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst Runtimes = require('./runtimes/index.js')\n\nclass OpenWhiskCompileFunctions {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n    this.runtimes = new Runtimes(serverless)\n\n    this.hooks = {\n      'before:package:createDeploymentArtifacts':  () => BbPromise.bind(this)\n        .then(this.excludes)\n        .then(this.disableSeqPackaging),\n      'before:package:compileFunctions': this.setup.bind(this),\n      'package:compileFunctions': this.compileFunctions.bind(this),\n    };\n  }\n\n  // Ensure we don't bundle provider plugin with service artifact.\n  excludes() {\n    const exclude = this.serverless.service.package.exclude || [];\n    exclude.push(\"node_modules/serverless-openwhisk/**\");\n    this.serverless.service.package.exclude = exclude;\n  }\n\n  disableSeqPackaging() {\n    this.serverless.service.getAllFunctions().forEach(functionName => {\n      const functionObject = this.serverless.service.getFunction(functionName);\n\n      if (functionObject.sequence) {\n        Object.assign(functionObject, { package: { disable: true } })\n      }\n    })\n  }\n\n  setup() {\n    // This object will be used to store the Action resources, passed directly to\n    // the OpenWhisk SDK during the deploy process.\n    this.serverless.service.actions = {};\n   }\n\n  calculateFunctionName(functionName, functionObject) {\n    return functionObject.name || `${this.serverless.service.service}_${functionName}`;\n  }\n\n  calculateFunctionNameSpace(functionName, functionObject) {\n    return functionObject.namespace || this.serverless.service.provider.namespace;\n  }\n\n  calculateMemorySize(functionObject) {\n    return functionObject.memory || this.serverless.service.provider.memory;\n  }\n\n  calculateConcurrency(functionObject) {\n    return functionObject.concurrency || this.serverless.service.provider.concurrency;\n  }\n\n  calculateTimeout(functionObject) {\n    return functionObject.timeout || this.serverless.service.provider.timeout;\n  }\n\n  calculateOverwrite(functionObject) {\n    let Overwrite = true;\n\n    if (functionObject.hasOwnProperty('overwrite')) {\n      Overwrite = functionObject.overwrite;\n    } else if (this.serverless.service.provider.hasOwnProperty('overwrite')) {\n      Overwrite = this.serverless.service.provider.overwrite;\n    }\n\n    return Overwrite;\n  }\n\n  compileFunctionAction(params) {\n    return {\n      actionName: params.FunctionName,\n      namespace: params.NameSpace,\n      overwrite: params.Overwrite,\n      action: {\n        exec: params.Exec,\n        limits: {\n          timeout: params.Timeout ? (params.Timeout * 1000) : undefined,\n          memory: params.MemorySize,\n          concurrency: params.Concurrency,\n        },\n        parameters: params.Parameters,\n        annotations: params.Annotations\n      },\n    };\n  }\n\n  // This method takes the function handler definition, parsed from the user's YAML file,\n  // and turns it into the OpenWhisk Action resource object.\n  //\n  // These resource objects are passed to the OpenWhisk SDK to create the associated Actions\n  // during the deployment process.\n  //\n  // Parameter values will be parsed from the user's YAML definition, either as a value from\n  // the function handler definition or the service provider defaults.\n  compileFunction(functionName, functionObject) {\n    return this.runtimes.exec(functionObject).then(Exec => {\n      const FunctionName = this.calculateFunctionName(functionName, functionObject);\n      const NameSpace = this.calculateFunctionNameSpace(functionName, functionObject);\n      const MemorySize = this.calculateMemorySize(functionObject);\n      const Timeout = this.calculateTimeout(functionObject);\n      const Overwrite = this.calculateOverwrite(functionObject);\n      const Concurrency = this.calculateConcurrency(functionObject);\n\n      // optional action parameters\n      const Parameters = Object.keys(functionObject.parameters || {})\n        .map(key => ({ key, value: functionObject.parameters[key] }));\n      \n      // optional action annotations \n      const Annotations = this.constructAnnotations(functionObject.annotations);\n\n      return this.compileFunctionAction(\n        { FunctionName, NameSpace, Overwrite, Exec, Timeout, MemorySize, Concurrency, Parameters, Annotations }\n      );\n    });\n  }\n\n  constructAnnotations (annotations) {\n    if (!annotations) return []\n\n    // finalise action parameters when exposing as external HTTP endpoint.\n    // mirrors behaviour from OpenWhisk CLI.\n    if (annotations['web-export']) {\n      annotations['final'] = true\n    }\n\n    const converted = Object.keys(annotations)\n      .map(key => ({ key, value: annotations[key] }));\n\n    return converted\n  }\n\n  logCompiledFunction (name, fn) {\n    const clone = JSON.parse(JSON.stringify(fn))\n    if (clone.action.exec.code) {\n      clone.action.exec.code = '<hidden>'\n    }\n    this.serverless.cli.log(`Compiled Function (${name}): ${JSON.stringify(clone)}`);\n  }\n\n  compileFunctions() {\n    this.serverless.cli.log('Compiling Functions...');\n\n    if (!this.serverless.service.actions) {\n      throw new this.serverless.classes.Error(\n        'Missing Resources section from OpenWhisk Resource Manager template');\n    }\n\n    const functionPromises = this.serverless.service.getAllFunctions().map((functionName) => {\n      const functionObject = this.serverless.service.getFunction(functionName);\n\n      if (!functionObject.handler && !functionObject.sequence) {\n        throw new this.serverless.classes\n          .Error(`Missing \"handler\" or \"sequence\" property in function ${functionName}`);\n      }\n\n      if (functionObject.handler && functionObject.sequence) {\n        throw new this.serverless.classes\n          .Error(`Found both \"handler\" and \"sequence\" properties in function ${functionName}, please choose one.`);\n      }\n\n      const functions = this.serverless.service.actions;\n      const err = () => {\n        throw new this.serverless.classes\n          .Error(`Unable to read handler file in function ${functionName}`);\n      };\n\n      let compileFn = this.compileFunction(functionName, functionObject)\n        .then(newFunction => (functions[functionName] = newFunction))\n\n      if (this.options.verbose) {\n        compileFn = compileFn.then(fn => this.logCompiledFunction(functionName, fn))\n      }\n\n      return compileFn.catch(err);\n    });\n\n    return BbPromise.all(functionPromises);\n  }\n}\n\nmodule.exports = OpenWhiskCompileFunctions;\n"
  },
  {
    "path": "compile/functions/runtimes/base.js",
    "content": "'use strict';\n\nconst fs = require('fs-extra')\nconst JSZip = require('jszip')\nconst BbPromise = require('bluebird')\n\nclass BaseRuntime {\n  constructor(serverless) {\n    this.serverless = serverless\n  }\n\n  match (functionObject) {\n    if (!functionObject.hasOwnProperty('handler')) return false\n    const runtime = this.calculateRuntime(functionObject)\n    return !!(runtime && runtime.startsWith(this.kind))\n  }\n\n  exec (functionObject) {\n    const main = this.calculateFunctionMain(functionObject);\n    const kind = this.calculateKind(functionObject);\n    const exec = { main, kind }\n\n    if (functionObject.hasOwnProperty('image')) {\n      exec.image = functionObject.image\n    }\n\n    return this.generateActionPackage(functionObject).then(code => Object.assign(exec, { code }))\n  }\n\n  calculateFunctionMain(functionObject) {\n    const splitted = functionObject.handler.split('.');\n    if (splitted.length < 2) {\n      return functionObject;\n    }\n    return splitted[splitted.length - 1];\n  }\n\n  calculateRuntime(functionObject) {\n    return functionObject.runtime || this.serverless.service.provider.runtime\n  }\n\n  calculateDefaultRuntime(functionObject) {\n    const runtime = this.calculateRuntime(functionObject)\n    return runtime.includes(':') ? runtime : `${runtime}:default`\n  }\n\n  calculateKind(functionObject) {\n    if (functionObject.hasOwnProperty('image')) return 'blackbox'\n\n    return this.calculateDefaultRuntime(functionObject)\n  }\n\n  isValidFile (handlerFile) {\n    return fs.existsSync(handlerFile)\n  }\n\n  convertHandlerToPath(functionHandler) {\n    const lastDot = functionHandler.lastIndexOf('.');\n    if (lastDot === -1) {\n      return functionHandler;\n    }\n    return functionHandler.substring(0, lastDot) + this.extension;\n  }\n\n  convertHandlerToPathInZip(functionHandler) {\n    let path = this.convertHandlerToPath(functionHandler);\n    while (path.startsWith('../')) {\n      path = path.substring(3);\n    }\n    return path;\n  }\n\n  generateActionPackage(functionObject) {\n    // Check that handler file exists\n    const handlerFile = this.convertHandlerToPath(functionObject.handler);\n    if (!this.isValidFile(handlerFile)) {\n      throw new this.serverless.classes.Error(`Function handler (${handlerFile}) does not exist.`)\n    }\n\n    // Generate action package\n    const handlerFileZipPath = this.convertHandlerToPathInZip(functionObject.handler);\n\n    return this.getArtifactZip(functionObject)\n      .then(zip => this.processActionPackage(handlerFileZipPath, zip))\n      .then(zip => zip.generateAsync(\n        { type: 'nodebuffer', compression: 'DEFLATE', compressionOptions: { level: 9 } }\n      ))\n      .then(buf => buf.toString('base64'));\n  }\n\n  getArtifactZip(functionObject) {\n    const artifactPath = this.getArtifactPath(functionObject)\n    const readFile = BbPromise.promisify(fs.readFile);\n    return readFile(artifactPath).then(zipBuffer => JSZip.loadAsync(zipBuffer))\n  }\n\n  getArtifactPath(functionObject) {\n    return this.serverless.service.package.individually ? \n      functionObject.package.artifact : this.serverless.service.package.artifact;\n  }\n}\n\nmodule.exports = BaseRuntime\n"
  },
  {
    "path": "compile/functions/runtimes/binary.js",
    "content": "'use strict';\n\nconst BaseRuntime = require('./base')\n\nclass Binary extends BaseRuntime {\n  constructor (serverless) {\n    super(serverless)\n    this.kind = 'binary'\n  }\n\n  exec (functionObject) {\n    const image = 'openwhisk/dockerskeleton'\n    const kind = 'blackbox'\n    return this.generateActionPackage(functionObject).then(code => ({ image, kind, code }))\n  }\n\n  processActionPackage (handlerFile, zip) {\n    return zip.file(handlerFile).async('nodebuffer').then(data => {\n      zip.remove(handlerFile)\n      return zip.file('exec', data)\n    })\n  }\n\n  calculateKind (functionObject) {\n    return `blackbox`\n  }\n\n  convertHandlerToPath (functionHandler) {\n    return functionHandler\n  }\n}\n\nmodule.exports = Binary\n"
  },
  {
    "path": "compile/functions/runtimes/docker.js",
    "content": "'use strict';\n\nclass Docker {\n  constructor(serverless) {\n    this.serverless = serverless\n  }\n\n  match (functionObject) {\n    if (!functionObject.hasOwnProperty('handler')) return false\n    return this.calculateRuntime(functionObject) === 'docker'\n  }\n\n  exec (functionObject) {\n    return { kind: 'blackbox', image: functionObject.handler }\n  }\n\n  calculateRuntime(functionObject) {\n    return functionObject.runtime || this.serverless.service.provider.runtime\n  }\n}\n\n\nmodule.exports = Docker\n"
  },
  {
    "path": "compile/functions/runtimes/index.js",
    "content": "'use strict';\n\nconst Binary = require('./binary')\nconst Docker = require('./docker')\nconst Node = require('./node')\nconst Python = require('./python')\nconst Swift = require('./swift')\nconst Php = require('./php')\nconst Sequence = require('./sequence')\nconst Java = require('./java')\nconst Ruby = require('./ruby')\n\nclass Runtimes {\n  constructor(serverless) {\n    this.serverless = serverless;\n    this.runtimes = [\n      new Binary(serverless),\n      new Docker(serverless),\n      new Node(serverless),\n      new Python(serverless),\n      new Swift(serverless),\n      new Php(serverless),\n      new Sequence(serverless),\n      new Java(serverless),\n      new Ruby(serverless)\n    ];\n  }\n \n  exec (functionObj) {\n    const matched = this.runtimes.find(runtime => runtime.match(functionObj))\n    if (matched) return Promise.resolve(matched.exec(functionObj))\n\n    throw new this.serverless.classes.Error(\n      'This runtime is not currently supported by the OpenWhisk provider plugin.');\n  }\n}\n\nmodule.exports = Runtimes\n"
  },
  {
    "path": "compile/functions/runtimes/java.js",
    "content": "'use strict';\n\nconst fs = require('fs-extra');\nconst BbPromise = require('bluebird');\nconst BaseRuntime = require('./base');\nconst JSZip = require('jszip');\n\nclass Java extends BaseRuntime {\n  constructor(serverless) {\n    super(serverless);\n    this.kind = 'java';\n    this.extension = '.jar';\n  }\n\n  convertHandlerToPath(functionHandler) {\n    const lastColon = functionHandler.lastIndexOf(':');\n    if (lastColon === -1) {\n      return functionHandler;\n    }\n    return functionHandler.substring(0, lastColon);\n  }\n\n  // Main class has to be defined as an annotation, otherwise it will assume the main class is called 'Main'.\n  calculateFunctionMain(functionObject) {\n    if (functionObject.handler) {\n      const splitted = functionObject.handler.split(':');\n      if (splitted.length > 1 && splitted[splitted.length - 1]) {\n        return splitted[splitted.length - 1];\n      }\n    }\n    return 'Main';\n  }\n\n  // Ensure zip package used to deploy action has the correct artifacts for the runtime by only\n  // including the deployable JAR file.\n  processActionPackage(handlerFile, zip) {\n    return zip;\n  }\n}\n\nmodule.exports = Java;\n"
  },
  {
    "path": "compile/functions/runtimes/node.js",
    "content": "'use strict';\n\nconst BaseRuntime = require('./base')\n\nclass Node extends BaseRuntime {\n  constructor (serverless) {\n    super(serverless)\n    this.kind = 'nodejs'\n    this.extension = '.js'\n  }\n\n  calculateRuntime(functionObject) {\n    return super.calculateRuntime(functionObject) || 'nodejs:default'\n  }\n\n  processActionPackage (handlerFile, zip) {\n    zip.file(\"package.json\", JSON.stringify({main: handlerFile}))\n    return zip\n  }\n\n  //We're handling a special case here which is that if TypeScript is being used\n  //we won't actually have a \".js\" file (this.extension), instead we'll have a \"ts\"\n  //which should still be considered safe enough, or at least shouldn't\n  //completely stop the deployment process\n  isValidFile(handlerFile) {\n    return super.isValidFile(handlerFile) || this.isValidTypeScriptFile(handlerFile);\n  }\n\n  //Check for TypeScript version of handler file\n  isValidTypeScriptFile(handlerFile) {\n    //replaces the last occurance of `.js` with `.ts`, case insensitive\n    const typescriptHandlerFile = handlerFile.replace(/\\.js$/gi, \".ts\");\n    return super.isValidFile(typescriptHandlerFile);\n  }\n}\n\nmodule.exports = Node\n"
  },
  {
    "path": "compile/functions/runtimes/php.js",
    "content": "'use strict';\n\nconst BaseRuntime = require('./base')\n\nclass Php extends BaseRuntime {\n  constructor (serverless) {\n    super(serverless)\n    this.kind = 'php'\n    this.extension = '.php'\n  }\n\n  processActionPackage (handlerFile, zip) {\n    return zip.file(handlerFile).async('nodebuffer').then(data => {\n      zip.remove(handlerFile)\n      return zip.file('index.php', data)\n    })\n  }\n}\n\nmodule.exports = Php\n"
  },
  {
    "path": "compile/functions/runtimes/python.js",
    "content": "'use strict';\n\nconst BaseRuntime = require('./base')\n\nclass Python extends BaseRuntime {\n  constructor (serverless) {\n    super(serverless)\n    this.kind = 'python'\n    this.extension = '.py'\n  }\n\n  processActionPackage (handlerFile, zip) {\n    return zip.file(handlerFile).async('nodebuffer').then(data => {\n      zip.remove(handlerFile)\n      return zip.file('__main__.py', data)\n    })\n  }\n\n  calculateDefaultRuntime (functionObject) {\n    return this.calculateRuntime(functionObject)\n  }\n}\n\nmodule.exports = Python\n"
  },
  {
    "path": "compile/functions/runtimes/ruby.js",
    "content": "'use strict';\n\nconst BaseRuntime = require('./base')\n\nclass Ruby extends BaseRuntime {\n  constructor (serverless) {\n    super(serverless)\n    this.kind = 'ruby'\n    this.extension = '.rb'\n  }\n\n  processActionPackage (handlerFile, zip) {\n    return zip.file(handlerFile).async('nodebuffer').then(data => {\n      zip.remove(handlerFile)\n      return zip.file('main.rb', data)\n    })\n  }\n}\n\nmodule.exports = Ruby \n"
  },
  {
    "path": "compile/functions/runtimes/sequence.js",
    "content": "'use strict';\n\nclass Sequence {\n  constructor(serverless) {\n    this.serverless = serverless\n  }\n\n  match (functionObject) {\n    return functionObject.hasOwnProperty('sequence')\n  }\n\n  exec (functionObject) {\n    // sequence action names must be fully qualified.\n    // use default namespace if this is missing.\n    const components = functionObject.sequence.map(name => {\n      if (name.startsWith('/')) {\n        return name\n      }\n      const func = this.serverless.service.getFunction(name)\n      return `/_/${func.name}`\n    })\n\n    return { kind: 'sequence', components }\n  }\n}\n\n\nmodule.exports = Sequence\n"
  },
  {
    "path": "compile/functions/runtimes/swift.js",
    "content": "'use strict';\n\nconst fs = require('fs-extra')\nconst BaseRuntime = require('./base')\nconst JSZip = require(\"jszip\")\n\nclass Swift extends BaseRuntime {\n  constructor (serverless) {\n    super(serverless)\n    this.kind = 'swift'\n    this.extension = '.swift'\n  }\n\n  convertHandlerToPath (functionHandler) {\n    if (this.isZipFile(functionHandler)) {\n      return functionHandler\n    }\n\n    return super.convertHandlerToPath(functionHandler)\n  }\n\n  calculateFunctionMain(functionObject) {\n    if (this.isZipFile(functionObject.handler)) {\n      return 'main'\n    }\n\n    return super.calculateFunctionMain(functionObject)\n  }\n\n  isZipFile (path) {\n    return path.endsWith('.zip')\n  }\n\n  readHandlerFile (path) {\n    const contents = fs.readFileSync(path)\n    const encoding = this.isZipFile(path) ? 'base64' : 'utf8'\n    return contents.toString(encoding)\n  }\n\n  exec (functionObject) {\n    const main = this.calculateFunctionMain(functionObject);\n    const kind = this.calculateKind(functionObject);\n    const handlerPath = this.convertHandlerToPath(functionObject.handler)\n\n    if (!this.isValidFile(handlerPath)) {\n      throw new this.serverless.classes.Error(`Function handler (${handlerPath}) does not exist.`)\n    }\n\n    const code = this.readHandlerFile(handlerPath)\n    const binary = this.isZipFile(handlerPath)\n    const exec = { main, kind, code, binary }\n\n    if (functionObject.hasOwnProperty('image')) {\n      exec.image = functionObject.image\n    }\n\n    return Promise.resolve(exec)\n  }\n}\n\nmodule.exports = Swift\n"
  },
  {
    "path": "compile/functions/runtimes/tests/all.js",
    "content": "'use strict';\n\nrequire('./index');\nrequire('./base');\nrequire('./node');\nrequire('./docker');\nrequire('./python');\nrequire('./swift');\nrequire('./php');\nrequire('./binary');\nrequire('./sequence');\nrequire('./java');\nrequire('./ruby');\n"
  },
  {
    "path": "compile/functions/runtimes/tests/base.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\n\nconst BaseRuntime = require('../base');\n\ndescribe('Base', () => {\n  const base = new BaseRuntime();\n  base.extension = '.js';\n\n  describe('#calculateFunctionMain()', () => {\n    it('should extract the main function for a given file handler', () => {\n      const functionObject = { handler: 'index.main' };\n      const result = base.calculateFunctionMain(functionObject);\n      expect(result).to.equal('main');\n    });\n    it('should return the input for a given file handler without exported function', () => {\n      const functionObject = { handler: 'index' };\n      const result = base.calculateFunctionMain(functionObject);\n      expect(result).to.equal(functionObject);\n    });\n    it('should extract the main function for a given path handler', () => {\n      const functionObject = { handler: 'myFunction@0.1.0/index.main' };\n      const result = base.calculateFunctionMain(functionObject);\n      expect(result).to.equal('main');\n    });\n    it('should extract the main function for a given relative path handler', () => {\n      const functionObject = { handler: '../myFunction/index.main' };\n      const result = base.calculateFunctionMain(functionObject);\n      expect(result).to.equal('main');\n    });\n  });\n\n  describe('#convertHandlerToPathInZip()', () => {\n    it('should extract the path in zip for a given file handler', () => {\n      const result = base.convertHandlerToPathInZip('index.main');\n      expect(result).to.equal('index.js');\n    });\n\n    it('should return the input for a given file handler without exported function', () => {\n      const result = base.convertHandlerToPathInZip('index');\n      expect(result).to.equal('index');\n    });\n\n    it('should extract the path in zip for a given path handler', () => {\n      const result = base.convertHandlerToPathInZip('myFunction@0.1.0/index.main');\n      expect(result).to.equal('myFunction@0.1.0/index.js');\n    });\n\n    it('should extract the path in zip for a given relative path handler', () => {\n      const result = base.convertHandlerToPathInZip('../myFunction@0.1.0/index.main');\n      expect(result).to.equal('myFunction@0.1.0/index.js');\n    });\n  });\n\n  describe('#convertHandlerToPath()', () => {\n    it('should extract the path for a given file handler', () => {\n      const result = base.convertHandlerToPath('index.main');\n      expect(result).to.equal('index.js');\n    });\n\n    it('should return the input for a given file handler without exported function', () => {\n      const result = base.convertHandlerToPath('index');\n      expect(result).to.equal('index');\n    });\n\n    it('should extract the path for a given path handler', () => {\n      const result = base.convertHandlerToPath('myFunction@0.1.0/index.main');\n      expect(result).to.equal('myFunction@0.1.0/index.js');\n    });\n\n    it('should extract the path for a given relative path handler', () => {\n      const result = base.convertHandlerToPath('../myFunction@0.1.0/index.main');\n      expect(result).to.equal('../myFunction@0.1.0/index.js');\n    });\n  });\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/binary.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Binary = require('../binary');\nconst JSZip = require(\"jszip\");\nconst fs = require('fs-extra');\n\ndescribe('Binary', () => {\n  let serverless;\n  let node;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    node = new Binary(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({runtime: 'binary', handler: 'bin_file'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'binary';\n      expect(node.match({handler: 'bin_file'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(node.match({runtime: 'nodejs', handler: 'bin_file'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({handler: 'bin_file'})).to.equal(false)\n    });\n\n    it('should not match default runtime', () => {\n      expect(node.match({handler: 'bin_file'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(node.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return binary exec definition', () => {\n      const fileContents = 'zip file contents';\n      const handler = 'bin_file';\n\n      const exec = { image: 'openwhisk/dockerskeleton', kind: 'blackbox', code: new Buffer(fileContents) };\n      sandbox.stub(node, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(node.exec({ handler, runtime: 'binary'}))\n        .to.eventually.deep.equal(exec);\n    })\n  });\n\n  describe('#generateActionPackage()', () => {\n    it('should throw error for missing handler file', () => {\n      expect(() => node.generateActionPackage({handler: 'does_not_exist'}))\n        .to.throw(Error, 'Function handler (does_not_exist) does not exist.');\n    })\n\n    it('should read service artifact and add package.json for handler', () => {\n      node.serverless.service.package = {artifact: '/path/to/zip_file.zip'};\n      node.isValidFile = () => true\n      const zip = new JSZip();\n      zip.file(\"handler\", \"blah blah blah\");\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage({handler: 'handler'}).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler\")).to.be.equal(null)\n            return zip.file(\"exec\").async(\"string\").then(code => {\n              expect(code).to.be.equal('blah blah blah')\n            })\n          })\n        })\n      });\n    })\n\n    it('should handle service artifact for individual function handler', () => {\n      const functionObj = {handler: 'handler', package: { artifact: '/path/to/zip_file.zip'}}\n      node.serverless.service.package = {individually: true};\n      node.isValidFile = () => true\n\n      const zip = new JSZip();\n      zip.file(\"handler\", \"blah blah blah\");\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage(functionObj).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler\")).to.be.equal(null)\n            return zip.file(\"exec\").async(\"string\").then(code => {\n              expect(code).to.be.equal('blah blah blah')\n            })\n          })\n        })\n      });\n    })\n  })\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/docker.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Docker = require('../docker');\n\ndescribe('Docker', () => {\n  let serverless;\n  let docker;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    docker = new Docker(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(docker.match({runtime: 'docker', handler: 'repo/image'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'docker';\n      expect(docker.match({handler: 'repo/image'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(docker.match({runtime: 'nodejs', handler: 'repo/image'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(docker.match({handler: 'repo/image'})).to.equal(false)\n    });\n\n    it('should not match default runtime', () => {\n      expect(docker.match({handler: 'repo/image'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(docker.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return docker definition for docker image handler', () => {\n      const handler = 'repo/image'\n      const exec = { kind: 'blackbox', image: 'repo/image' };\n\n      expect(docker.exec({ runtime: 'docker', handler })).to.deep.equal(exec);\n    });\n  });\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Runtimes = require('../index');\n\ndescribe('Runtimes', () => {\n  let serverless;\n  let runtimes;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    runtimes = new Runtimes(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#exec()', () => {\n    it('should throw error for unknown runtime', () => {\n      expect(() => runtimes.exec({}))\n        .to.throw(Error, /This runtime is not currently supported/);\n    });\n\n    it('should execute and return thenable exec for thenable matching runtime', () => {\n      const result = { foo: 'bar' }\n      const match = sinon.stub().returns(true)\n      const exec = sinon.stub().returns(Promise.resolve(result))\n      runtimes.runtimes = [{ match, exec }]\n\n      return runtimes.exec({}).then(resp => {\n        expect(resp).to.deep.equal(result)\n        expect(match.called).to.equal(true)\n        expect(exec.called).to.equal(true)\n      })\n    });\n\n    it('should execute and return thenable exec for non-thenable matching runtime', () => {\n      const result = { foo: 'bar' }\n      const match = sinon.stub().returns(true)\n      const exec = sinon.stub().returns(result)\n      runtimes.runtimes = [{ match, exec }]\n\n      return runtimes.exec({}).then(resp => {\n        expect(resp).to.deep.equal(result)\n        expect(match.called).to.equal(true)\n        expect(exec.called).to.equal(true)\n      })\n    });\n  });\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/java.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Java = require('../java');\nconst JSZip = require('jszip');\nconst fs = require('fs-extra');\n\ndescribe('Java', () => {\n  let serverless;\n  let java;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = { classes: { Error }, service: {}, getProvider: sandbox.spy() };\n    serverless.service.provider = { name: 'openwhisk' };\n    java = new Java(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(java.match({ runtime: 'java', handler: 'file:func' })).to.equal(true);\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'java';\n      expect(java.match({ handler: 'file:func' })).to.equal(true);\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(java.match({ runtime: 'python', handler: 'file:func' })).to.equal(false);\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(java.match({ handler: 'file:func' })).to.equal(false);\n    });\n\n    it('should not match when missing handler', () => {\n      expect(java.match({})).to.equal(false);\n    });\n  });\n\n  describe('#calculateFunctionMain()', () => {\n    it('should return Main when no main class is defined', () => {\n      expect(java.calculateFunctionMain({})).to.equal('Main');\n    });\n    it('should return Main when no main class is defined, but the file is', () => {\n      expect(java.calculateFunctionMain({ handler: 'target/my-jar.jar' })).to.equal('Main');\n    });\n    it('should return Main when no main class is defined, but there is a colon', () => {\n      expect(java.calculateFunctionMain({ handler: 'target/my-jar.jar:' })).to.equal('Main');\n    });\n    it('should return the provided class when a main class is defined', () => {\n      expect(\n        java.calculateFunctionMain({ handler: 'target/my-jar.jar:my.main.class.Name' })\n      ).to.equal('my.main.class.Name');\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return java exec definition', () => {\n      const fileContents = 'some file contents';\n      const handler = 'target/my-jar.jar:my.main.class.Name';\n\n      const exec = {\n        main: 'my.main.class.Name',\n        kind: 'java:default',\n        code: new Buffer(fileContents),\n      };\n      sandbox.stub(java, 'generateActionPackage', functionObj => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(java.exec({ handler, runtime: 'java' })).to.eventually.deep.equal(exec);\n    });\n\n    it('should return java exec definition with custom image', () => {\n      const fileContents = 'some file contents';\n      const handler = 'target/my-jar.jar:my.main.class.Name';\n\n      const exec = {\n        main: 'my.main.class.Name',\n        kind: 'blackbox',\n        image: 'foo',\n        code: new Buffer(fileContents),\n      };\n      sandbox.stub(java, 'generateActionPackage', functionObj => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(java.exec({ handler, runtime: 'java', image: 'foo' })).to.eventually.deep.equal(\n        exec\n      );\n    });\n  });\n\n  describe('#convertHandlerToPath()', () => {\n    it('should return file path passed', () => {\n      expect(java.convertHandlerToPath('target/my-jar.jar')).to.be.equal('target/my-jar.jar');\n    });\n    it('should return file path passed, excluding the colon', () => {\n      expect(java.convertHandlerToPath('target/my-jar.jar:')).to.be.equal('target/my-jar.jar');\n    });\n    it('should return file path passed without the class', () => {\n      expect(java.convertHandlerToPath('target/my-jar.jar:my.main.class.Name')).to.be.equal(\n        'target/my-jar.jar'\n      );\n    });\n  });\n\n  describe('#generateActionPackage()', () => {\n    it('should throw error for missing handler file', () => {\n      expect(() =>\n        java.generateActionPackage({ handler: './does_not_exist/my-jar.jar:my.main.class.Name' })\n      ).to.throw(Error, 'Function handler (./does_not_exist/my-jar.jar) does not exist.');\n    });\n\n    it('should create zip file with the Java jar file for the action', () => {\n      java.serverless.service.package = { artifact: '/path/to/zip_file.zip' };\n      java.isValidFile = () => true;\n      const zip = new JSZip();\n      const source = 'binary file contents';\n      zip.file('target/my-jar.jar', source);\n      return zip.generateAsync({ type: 'nodebuffer' }).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          cb(null, zipped);\n        });\n        return java\n          .generateActionPackage({ handler: 'target/my-jar.jar:my.main.class.Name' })\n          .then(data =>\n            JSZip.loadAsync(new Buffer(data, 'base64')).then(zip =>\n              zip\n                .file('target/my-jar.jar')\n                .async('string')\n                .then(contents => {\n                  expect(contents).to.be.equal(source);\n                })\n            )\n          );\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/node.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Node = require('../node');\nconst BaseRuntime = require('../base');\nconst JSZip = require(\"jszip\");\nconst fs = require('fs-extra');\n\ndescribe('Node', () => {\n  let serverless;\n  let node;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    node = new Node(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(node.match({runtime: 'nodejs', handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should match with default runtime', () => {\n      expect(node.match({handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(node.match({runtime: 'python', handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(node.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(node.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return default nodejs exec definition', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', kind: 'nodejs:default', code: new Buffer(fileContents) };\n      sandbox.stub(node, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(node.exec({ handler }))\n        .to.eventually.deep.equal(exec);\n    })\n\n    it('should return custom nodejs exec definition', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', kind: 'nodejs:6', code: new Buffer(fileContents) };\n      sandbox.stub(node, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(node.exec({ handler, runtime: 'nodejs:6' }))\n        .to.eventually.deep.equal(exec);\n    })\n\n    it('should support using custom image', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', image: 'blah', kind: 'blackbox', code: new Buffer(fileContents) };\n      sandbox.stub(node, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(node.exec({ handler, image: 'blah', runtime: 'nodejs:6' }))\n        .to.eventually.deep.equal(exec);\n    })\n  });\n\n  describe('#isValidTypeScriptFile()', () => {\n    it('should report valid file path when a js path is passed that has a ts file instead', () => {\n      //We need to mock the node's `super` call, which is why we're using BaseRuntime\n      sandbox.stub(BaseRuntime.prototype, 'isValidFile', (path) => {\n        expect(path).to.equal('valid_typescript_handler_wrong_extension.ts');\n        return true;\n      });\n      expect(node.isValidTypeScriptFile('valid_typescript_handler_wrong_extension.js')).to.equal(true)\n    });\n  });\n\n  describe('#isValidFile()', () => {\n    it('should still allow a js file to be used for handler', () => {\n      //We need to mock the node's `super` call, which is why we're using BaseRuntime\n      sandbox.stub(BaseRuntime.prototype, 'isValidFile', (path) => {\n        expect(path).to.equal('valid_js_handler.js');\n        return true;\n      });\n      expect(node.isValidFile('valid_js_handler.js')).to.equal(true)\n    });\n  });\n\n  describe('#generateActionPackage()', () => {\n    it('should throw error for missing handler file', () => {\n      expect(() => node.generateActionPackage({handler: 'does_not_exist.main'}))\n        .to.throw(Error, 'Function handler (does_not_exist.js) does not exist.');\n    })\n\n    it('should read service artifact and add package.json for handler', () => {\n      node.serverless.service.package = {artifact: '/path/to/zip_file.zip'};\n      node.isValidFile = () => true\n      const zip = new JSZip();\n      zip.file(\"handler.js\", \"function main() { return {}; }\");\n      zip.file(\"package.json\", '{\"main\": \"index.js\"}')\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage({handler: 'handler.main'}).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            return zip.file(\"package.json\").async(\"string\").then(package_json => {\n              expect(package_json).to.be.equal('{\"main\":\"handler.js\"}')\n            })\n          })\n        })\n      });\n    })\n\n    it('should read service artifact and add package.json for relative path handler', () => {\n      node.serverless.service.package = { artifact: '/path/to/zip_file.zip' };\n      node.isValidFile = () => true;\n      const zip = new JSZip();\n      zip.file('folder/handler.js', 'function main() { return {}; }');\n      zip.file('folder/package.json', '{\"main\": \"index.js\"}');\n      return zip.generateAsync({ type: 'nodebuffer' }).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage({ handler: '../folder/handler.main' }).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(actionPackage => {\n            return actionPackage.file('package.json').async('string').then(packageJson => {\n              expect(packageJson).to.be.equal('{\"main\":\"folder/handler.js\"}');\n            });\n          });\n        });\n      });\n    });\n\n    it('should handle service artifact for individual function handler', () => {\n      const functionObj = {handler: 'handler.main', package: { artifact: '/path/to/zip_file.zip'}}\n      node.serverless.service.package = {individually: true};\n      node.isValidFile = () => true\n\n      const zip = new JSZip();\n      zip.file(\"handler.js\", \"function main() { return {}; }\");\n      zip.file(\"package.json\", '{\"main\": \"index.js\"}')\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage(functionObj).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            return zip.file(\"package.json\").async(\"string\").then(package_json => {\n              expect(package_json).to.be.equal('{\"main\":\"handler.js\"}')\n            })\n          })\n        })\n      });\n    })\n  })\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/php.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Php = require('../php');\nconst JSZip = require(\"jszip\");\nconst fs = require('fs-extra');\n\ndescribe('Php', () => {\n  let serverless;\n  let php;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    php = new Php(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(php.match({runtime: 'php', handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'php';\n      expect(php.match({handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(php.match({runtime: 'python', handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(php.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(php.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return php exec definition', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', kind: 'php:default', code: new Buffer(fileContents) };\n      sandbox.stub(php, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(php.exec({ handler, runtime: 'php'}))\n        .to.eventually.deep.equal(exec);\n    })\n\n    it('should support using custom image', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', image: 'blah', kind: 'blackbox', code: new Buffer(fileContents) };\n      sandbox.stub(php, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(php.exec({ handler, image: 'blah', runtime: 'php:7.1' }))\n        .to.eventually.deep.equal(exec);\n    })\n  });\n\n  describe('#generateActionPackage()', () => {\n    it('should throw error for missing handler file', () => {\n      expect(() => php.generateActionPackage({handler: 'does_not_exist.main'}))\n        .to.throw(Error, 'Function handler (does_not_exist.php) does not exist.');\n    })\n\n    it('should read service artifact and add index.php for handler', () => {\n      php.serverless.service.package = {artifact: '/path/to/zip_file.zip'};\n      php.isValidFile = () => true\n      const zip = new JSZip();\n      const source = '<?php\\nfunction main(array $args) : array\\n{\\nreturn [];\\n}'\n      zip.file(\"handler.php\", source);\n\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return php.generateActionPackage({handler: 'handler.main'}).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler.php\")).to.be.equal(null)\n            return zip.file(\"index.php\").async(\"string\").then(main => {\n              expect(main).to.be.equal(source)\n            })\n          })\n        })\n      });\n    })\n\n    it('should handle service artifact for individual function handler', () => {\n      const functionObj = {handler: 'handler.main', package: { artifact: '/path/to/zip_file.zip'}}\n      php.serverless.service.package = {individually: true};\n      php.isValidFile = () => true\n\n      const zip = new JSZip();\n      const source = '<?php\\nfunction main(array $args) : array\\n{\\nreturn [];\\n}'\n      zip.file(\"handler.php\", source);\n\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return php.generateActionPackage(functionObj).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler.php\")).to.be.equal(null)\n            return zip.file(\"index.php\").async(\"string\").then(main => {\n              expect(main).to.be.equal(source)\n            })\n          })\n        })\n      });\n    });\n  })\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/python.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Python = require('../python');\nconst JSZip = require(\"jszip\");\nconst fs = require('fs-extra');\n\ndescribe('Python', () => {\n  let serverless;\n  let node;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    node = new Python(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({runtime: 'python', handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(node.match({handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(node.match({runtime: 'nodejs', handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match default runtime', () => {\n      expect(node.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(node.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return python exec definition', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', kind: 'python', code: new Buffer(fileContents) };\n      sandbox.stub(node, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(node.exec({ handler, runtime: 'python'}))\n        .to.eventually.deep.equal(exec);\n    })\n\n    it('should support using custom image', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', image: 'blah', kind: 'blackbox', code: new Buffer(fileContents) };\n      sandbox.stub(node, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(node.exec({ handler, image: 'blah', runtime: 'python' }))\n        .to.eventually.deep.equal(exec);\n    })\n  });\n\n  describe('#generateActionPackage()', () => {\n    it('should throw error for missing handler file', () => {\n      expect(() => node.generateActionPackage({handler: 'does_not_exist.main'}))\n        .to.throw(Error, 'Function handler (does_not_exist.py) does not exist.');\n    })\n\n    it('should read service artifact and add __main__.py for handler', () => {\n      node.serverless.service.package = {artifact: '/path/to/zip_file.zip'};\n      node.isValidFile = () => true\n      const zip = new JSZip();\n      zip.file(\"handler.py\", \"def main(dict):\\n\\treturn {}\");\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage({handler: 'handler.main'}).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler.py\")).to.be.equal(null)\n            return zip.file(\"__main__.py\").async(\"string\").then(package_json => {\n              expect(package_json).to.be.equal('def main(dict):\\n\\treturn {}')\n            })\n          })\n        })\n      });\n    })\n\n    it('should handle service artifact for individual function handler', () => {\n      const functionObj = {handler: 'handler.main', package: { artifact: '/path/to/zip_file.zip'}}\n      node.serverless.service.package = {individually: true};\n      node.isValidFile = () => true\n\n      const zip = new JSZip();\n      zip.file(\"handler.py\", \"def main(dict):\\n\\treturn {}\");\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return node.generateActionPackage(functionObj).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler.py\")).to.be.equal(null)\n            return zip.file(\"__main__.py\").async(\"string\").then(package_json => {\n              expect(package_json).to.be.equal('def main(dict):\\n\\treturn {}')\n            })\n          })\n        })\n      });\n    })\n  })\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/ruby.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Ruby = require('../ruby');\nconst JSZip = require('jszip');\nconst fs = require('fs-extra');\n\ndescribe('Ruby', () => {\n  let serverless;\n  let ruby;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    ruby = new Ruby(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(ruby.match({runtime: 'ruby', handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'ruby';\n      expect(ruby.match({handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(ruby.match({runtime: 'python', handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'python';\n      expect(ruby.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(ruby.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return ruby exec definition', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', kind: 'ruby:default', code: new Buffer(fileContents) };\n      sandbox.stub(ruby, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(ruby.exec({ handler, runtime: 'ruby'}))\n        .to.eventually.deep.equal(exec);\n    })\n\n    it('should support using custom image', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n\n      const exec = { main: 'some_func', image: 'blah', kind: 'blackbox', code: new Buffer(fileContents) };\n      sandbox.stub(ruby, 'generateActionPackage', (functionObj) => {\n        expect(functionObj.handler).to.equal(handler);\n        return Promise.resolve(new Buffer(fileContents));\n      });\n      return expect(ruby.exec({ handler, image: 'blah', runtime: 'ruby:7.1' }))\n        .to.eventually.deep.equal(exec);\n    })\n  });\n\n  describe('#generateActionPackage()', () => {\n    it('should throw error for missing handler file', () => {\n      expect(() => ruby.generateActionPackage({handler: 'does_not_exist.main'}))\n        .to.throw(Error, 'Function handler (does_not_exist.rb) does not exist.');\n    })\n\n    it('should read service artifact and add main.rb for handler', () => {\n      ruby.serverless.service.package = {artifact: '/path/to/zip_file.zip'};\n      ruby.isValidFile = () => true\n      const zip = new JSZip();\n      const source = 'def main(args)\\nname = args[\"name\"] || \"stranger\"\\ngreeting = \"Hello #{name}!\"\\n{ \"greeting\" => greeting }\\nend'\n      zip.file(\"handler.rb\", source);\n\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return ruby.generateActionPackage({handler: 'handler.main'}).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler.rb\")).to.be.equal(null)\n            return zip.file(\"main.rb\").async(\"string\").then(main => {\n              expect(main).to.be.equal(source)\n            })\n          })\n        })\n      });\n    })\n\n    it('should handle service artifact for individual function handler', () => {\n      const functionObj = {handler: 'handler.main', package: { artifact: '/path/to/zip_file.zip'}}\n      ruby.serverless.service.package = {individually: true};\n      ruby.isValidFile = () => true\n\n      const zip = new JSZip();\n      const source = 'def main(args)\\nname = args[\"name\"] || \"stranger\"\\ngreeting = \"Hello #{name}!\"\\n{ \"greeting\" => greeting }\\nend'\n      zip.file(\"handler.rb\", source);\n\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFile', (path, cb) => {\n          expect(path).to.equal('/path/to/zip_file.zip');\n          cb(null, zipped);\n        });\n        return ruby.generateActionPackage(functionObj).then(data => {\n          return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {\n            expect(zip.file(\"handler.rb\")).to.be.equal(null)\n            return zip.file(\"main.rb\").async(\"string\").then(main => {\n              expect(main).to.be.equal(source)\n            })\n          })\n        })\n      });\n    });\n  })\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/sequence.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Sequence = require('../sequence');\n\ndescribe('Sequence', () => {\n  let serverless;\n  let sequence;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    sequence = new Sequence(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match function object with sequence property', () => {\n      expect(sequence.match({sequence: true})).to.equal(true)\n    });\n    it('should ignore function object without sequence property', () => {\n      expect(sequence.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return sequence definition for sequence function', () => {\n      const exec = { kind: 'sequence', components: [\"/_/one\", \"/a/two\", \"/a/b/three\"] };\n\n      sequence.serverless.service.getFunction = () => ({name: 'one'});\n      sequence.serverless.service.provider.namespace = 'namespace';\n      expect(sequence.exec({\n        sequence: [\"one\", \"/a/two\", \"/a/b/three\"]\n      })).to.deep.equal(exec);\n    });\n  });\n});\n"
  },
  {
    "path": "compile/functions/runtimes/tests/swift.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst Swift = require('../swift');\nconst JSZip = require(\"jszip\");\nconst fs = require('fs-extra');\n\ndescribe('Swift', () => {\n  let serverless;\n  let node;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    serverless.service.provider = { name: 'openwhisk' };\n    node = new Swift(serverless);\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#match()', () => {\n    it('should match with explicit runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({runtime: 'swift', handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should match with provider runtime', () => {\n      serverless.service.provider.runtime = 'swift';\n      expect(node.match({handler: 'file.func'})).to.equal(true)\n    });\n\n    it('should not match when wrong explicit runtime', () => {\n      expect(node.match({runtime: 'nodejs', handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when wrong provider runtime', () => {\n      serverless.service.provider.runtime = 'nodejs';\n      expect(node.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match default runtime', () => {\n      expect(node.match({handler: 'file.func'})).to.equal(false)\n    });\n\n    it('should not match when missing handler', () => {\n      expect(node.match({})).to.equal(false)\n    });\n  });\n\n  describe('#exec()', () => {\n    it('should return swift exec with source file handler', () => {\n      const fileContents = 'some file contents';\n      const handler = 'handler.some_func';\n      node.isValidFile = () => true\n      sandbox.stub(fs, 'readFileSync', (path) => {\n        expect(path).to.equal('handler.swift');\n        return Buffer.from(fileContents)\n      });\n\n      const exec = { main: 'some_func', binary: false, kind: 'swift:default', code: fileContents };\n      return expect(node.exec({ handler, runtime: 'swift'}))\n        .to.eventually.deep.equal(exec);\n    })\n\n    it('should return swift exec with zip file handler', () => {\n      const handler = 'my_file.zip';\n      node.isValidFile = () => true\n\n      const zip = new JSZip();\n      const source = 'binary file contents' \n      zip.file(\"exec\", source);\n\n      return zip.generateAsync({type:\"nodebuffer\"}).then(zipped => {\n        sandbox.stub(fs, 'readFileSync', (path) => {\n          expect(path).to.equal(handler);\n          return zipped\n        });\n\n      const b64 = zipped.toString('base64')\n      const exec = { main: 'main', binary: true, kind: 'swift:default', code: b64 };\n      return expect(node.exec({ handler, runtime: 'swift'}))\n        .to.eventually.deep.equal(exec);\n      })\n    })\n  });\n\n  describe('#convertHandlerToPath()', () => {\n    it('should return file path for swift function handlers', () => {\n      expect(node.convertHandlerToPath('file.func')).to.be.equal('file.swift')\n    })\n\n    it('should return file path for zip files', () => {\n      expect(node.convertHandlerToPath('my_file.zip')).to.be.equal('my_file.zip')\n    })\n  })\n});\n"
  },
  {
    "path": "compile/functions/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileFunctions = require('../index');\n\ndescribe('OpenWhiskCompileFunctions', () => {\n  let serverless;\n  let openwhiskCompileFunctions;\n  let sandbox;\n\n  const openwhiskResourcesMockObject = {\n    first: {\n      actionName: 'first',\n      namespace: '',\n      action: {\n        exec: { kind: 'nodejs:default', code: 'function main() {};' },\n      },\n    },\n    second: {\n      actionName: 'second',\n      namespace: '',\n      action: {\n        exec: { kind: 'nodejs:default', code: 'function main() {};' },\n      },\n    },\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    serverless = {classes: {Error}, service: {}, getProvider: sandbox.spy()};\n    openwhiskCompileFunctions = new OpenWhiskCompileFunctions(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: '',\n      apihost: '',\n      auth: '',\n    };\n    serverless.service.provider = { name: 'openwhisk' };\n\n    serverless.cli = { consoleLog: () => {}, log: () => {} };\n\n    openwhiskCompileFunctions.setup();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#disableSeqPackaging()', () => {\n    it('should add disable flag to sequences', () => {\n      const fns = {\n        first: { handler: 'foo.js' },\n        second: { handler: 'foo.js' },\n        seq: { sequence: [ 'first', 'second' ] }\n      }\n\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => Object.keys(fns)\n      openwhiskCompileFunctions.serverless.service.getFunction = name => fns[name];\n\n      openwhiskCompileFunctions.disableSeqPackaging()\n      expect(fns.seq.package.disable).to.be.true\n    });\n  });\n\n  describe('#constructAnnotations()', () => {\n    it('should handle missing annotations', () => {\n      expect(openwhiskCompileFunctions.constructAnnotations())\n        .to.deep.equal([]);\n    })\n    it('should handle empty annotations', () => {\n      expect(openwhiskCompileFunctions.constructAnnotations({}))\n        .to.deep.equal([]);\n    })\n    it('should handle annotations present', () => {\n      expect(openwhiskCompileFunctions.constructAnnotations({\n        hello: 'world', foo: 'bar'\n      })).to.deep.equal([\n        { key: 'hello', value: 'world' },\n        { key: 'foo', value: 'bar' }\n      ]);\n    })\n    it('should add final annotations if web-export is present', () => {\n      expect(openwhiskCompileFunctions.constructAnnotations({\n        hello: 'world', foo: 'bar', \"web-export\": true\n      })).to.deep.equal([\n        { key: 'hello', value: 'world' },\n        { key: 'foo', value: 'bar' },\n        { key: 'web-export', value: true },\n        { key: 'final', value: true }\n      ]);\n    })\n  })\n\n  describe('#calculateFunctionNameSpace()', () => {\n    it('should return namespace from function object', () => {\n      expect(openwhiskCompileFunctions\n        .calculateFunctionNameSpace('testing', { namespace: 'testing' })\n      ).to.equal('testing');\n    });\n\n    it('should return namespace from service provider', () => {\n      openwhiskCompileFunctions.serverless.service.provider = { namespace: 'testing' };\n      expect(openwhiskCompileFunctions.calculateFunctionNameSpace('testing', {}))\n        .to.equal('testing');\n    });\n  });\n\n  describe('#logCompiledFunction()', () => {\n    it('should log function contents with code to console.', () => {\n      const log = sandbox.stub(openwhiskCompileFunctions.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskCompileFunctions.serverless.cli, 'consoleLog')\n\n      openwhiskCompileFunctions.logCompiledFunction('first', openwhiskResourcesMockObject.first)\n      expect(log.calledOnce).to.be.equal(true);\n      const clone = JSON.parse(JSON.stringify(openwhiskResourcesMockObject.first))\n      clone.action.exec.code = '<hidden>'\n      expect(log.args[0][0]).to.be.equal(`Compiled Function (first): ${JSON.stringify(clone)}`) \n    });\n  \n    it('should log function contents without code to console.', () => {\n      const log = sandbox.stub(openwhiskCompileFunctions.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskCompileFunctions.serverless.cli, 'consoleLog')\n\n      const clone = JSON.parse(JSON.stringify(openwhiskResourcesMockObject.first))\n      delete clone.action.exec.code\n      openwhiskCompileFunctions.logCompiledFunction('first', clone)\n      expect(log.calledOnce).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal(`Compiled Function (first): ${JSON.stringify(clone)}`) \n    });\n  });\n\n  describe('#compileFunctions()', () => {\n    it('should create action function with parsed parameters', () => {\n      let functionObject = {\n        handler: \"foo.js\",\n        name: \"name\",\n        namespace: \"namespace\",\n        overwrite: \"overwrite\",\n        memory: 123,\n        concurrency: 456,\n        timeout: 789,\n        parameters: {\n          hello: \"world\",\n          foo: \"bar\"\n        },\n        annotations: {\n          hello: \"world\",\n          foo: \"bar\"\n        }\n      };\n\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => ['service_name'];\n      openwhiskCompileFunctions.serverless.service.getFunction = () => functionObject;\n      sandbox.stub(openwhiskCompileFunctions, 'runtimes', {\n        exec: () => Promise.resolve()\n      });\n\n      return openwhiskCompileFunctions.compileFunctions().then(functionActions => {\n        let functionAction = functionActions[0];\n        expect(functionAction.actionName).to.be.equal(functionObject.name);\n        expect(functionAction.namespace).to.be.equal(functionObject.namespace);\n        expect(functionAction.overwrite).to.be.equal(functionObject.overwrite);\n        expect(functionAction.action.limits.memory).to.be.equal(functionObject.memory);\n        expect(functionAction.action.limits.concurrency).to.be.equal(functionObject.concurrency);\n        expect(functionAction.action.limits.timeout).to.be.equal(functionObject.timeout * 1000);\n\n        let paramsAndAnnotations = [\n          { key: 'hello', value: 'world' },\n          { key: 'foo', value: 'bar' }\n        ];\n        expect(functionAction.action.parameters).to.deep.equal(paramsAndAnnotations);\n        expect(functionAction.action.annotations).to.deep.equal(paramsAndAnnotations);\n      });\n    });\n\n    it('should not add implicit limits parameters', () => {\n      let functionObject = {\n        handler: \"foo.js\",\n        name: \"name\"\n      };\n\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => ['service_name'];\n      openwhiskCompileFunctions.serverless.service.getFunction = () => functionObject;\n      sandbox.stub(openwhiskCompileFunctions, 'runtimes', {\n        exec: () => Promise.resolve()\n      });\n\n      return openwhiskCompileFunctions.compileFunctions().then(functionActions => {\n        let functionAction = functionActions[0];\n        expect(functionAction.actionName).to.be.equal(functionObject.name);\n        expect(functionAction.action.limits.memory).to.be.undefined;\n        expect(functionAction.action.limits.concurrency).to.be.undefined;\n        expect(functionAction.action.limits.timeout).to.be.undefined;\n      });\n    });\n\n    it('should throw an error if the resource section is not available', () => {\n      openwhiskCompileFunctions.serverless.service.actions = null;\n      expect(() => openwhiskCompileFunctions.compileFunctions())\n        .to.throw(Error, /Missing Resources section/);\n    });\n\n    it('should throw an error if function definition has handler and sequence', () => {\n      const f = { sequence: true, handler: true };\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => ['service_name'];\n\n      openwhiskCompileFunctions.serverless.service.getFunction = () => f;\n\n      expect(() => openwhiskCompileFunctions.compileFunctions())\n        .to.throw(Error, /both \"handler\" and \"sequence\" properties/);\n    });\n\n    it('should throw an error if function definition is missing a handler or sequence', () => {\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => ['service_name'];\n\n      openwhiskCompileFunctions.serverless.service.getFunction = () => ({});\n\n      expect(() => openwhiskCompileFunctions.compileFunctions())\n        .to.throw(Error, /Missing \"handler\" or \"sequence\"/);\n    });\n\n    it('should throw an error if unable to read function handler file', () => {\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => ['service_name'];\n\n      const missing = { handler: 'missing.handler' };\n\n      openwhiskCompileFunctions.serverless.service.getFunction = () => missing;\n\n      sandbox.stub(openwhiskCompileFunctions, 'compileFunction', () => Promise.reject());\n      return expect(openwhiskCompileFunctions.compileFunctions()).to.be.rejected;\n    });\n\n    it('should create corresponding function resources', () => {\n      const keys = Object.keys(openwhiskResourcesMockObject);\n      const handler = function (name) {\n        return { handler: `${name}.handler` };\n      };\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => keys;\n      openwhiskCompileFunctions.serverless.service.getFunction = name => handler(name);\n      const log = sandbox.stub(openwhiskCompileFunctions, 'logCompiledFunction')\n\n      const mock = openwhiskResourcesMockObject;\n      sandbox.stub(\n        openwhiskCompileFunctions, 'compileFunction', name => Promise.resolve(mock[name]));\n      const f = openwhiskCompileFunctions.serverless.service.actions;\n\n      return openwhiskCompileFunctions.compileFunctions().then(() => { \n        expect(f).to.deep.equal(openwhiskResourcesMockObject) \n        expect(log.called).to.be.equal(false);\n      });\n    });\n\n    it('should log compiled functions with verbose flag', () => {\n      const keys = Object.keys(openwhiskResourcesMockObject);\n      const handler = function (name) {\n        return { handler: `${name}.handler` };\n      };\n      openwhiskCompileFunctions.options.verbose = true;\n      openwhiskCompileFunctions.serverless.service.getAllFunctions = () => keys;\n      openwhiskCompileFunctions.serverless.service.getFunction = name => handler(name);\n      const log = sandbox.stub(openwhiskCompileFunctions, 'logCompiledFunction')\n\n      const mock = openwhiskResourcesMockObject;\n      sandbox.stub(\n        openwhiskCompileFunctions, 'compileFunction', name => Promise.resolve(mock[name]));\n      const f = openwhiskCompileFunctions.serverless.service.actions;\n\n      return openwhiskCompileFunctions.compileFunctions().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "compile/message_hub/README.md",
    "content": "# Compile Triggers\n\nThis plugins compiles the `message_hub` events in `serverless.yaml` to corresponding [OpenWhisk Message Hub Trigger Feeds](https://github.com/openwhisk/openwhisk-package-kafka) definitions.\n\n## How it works\n\n`Compile Message Hub` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all schedule event which are defined in `serverless.yaml`.\n\n### Using Package Parameters \n\nIBM Message Hub instances can be provisioned through the IBM Bluemix platform.\nOpenWhisk on Bluemix will export Message Hub service credentials bound to a\npackage with the following name:\n```\n/${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n```\n\nRather than having to manually define all the properties needed by the Message\nHub trigger feed, you can reference a package to use instead. Credentials from\nthe referenced package will be used when executing the trigger feed.\n\nDevelopers only need to add the topic to listen to for each trigger.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - message_hub: \n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                topic: my_kafka_topic\n \n```\n\nThe plugin will create a trigger called `${serviceName}_${fnName}_messagehub_${topic}`\nand a rule called `${serviceName}_${fnName}_messagehub_${topic}_rule` to bind the function to\nthe message hub events.\n\nThe trigger and rule names created can be set explicitly using the `trigger` and\n`rule` parameters.\n\nOther functions can bind to the same trigger using the inline `trigger` event\nreferncing this trigger name.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - message_hub: \n                package: /${BLUEMIX_ORG}_${BLUEMIX_SPACE}/Bluemix_${SERVICE_NAME}_Credentials-1\n                topic: my_kafka_topic\n                trigger: log_events\n                rule: connect_index_to_kafka \n     another:\n        handler: users.another\n        events:\n            - trigger: log_events \n```\n\n### Using Manual Parameters\n\nTrigger feed parameters for the Message Hub event source can be defined\nexplicitly, rather than using pulling credentials from a package.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - message_hub: \n                topic: my_kafka_topic\n                brokers: afka01-prod01.messagehub.services.us-south.bluemix.net:9093\n                user: USERNAME\n                password: PASSWORD\n                admin_url:  https://kafka-admin-prod01.messagehub.services.us-south.bluemix.net:443\n                json: true\n                binary_key: true\n                binary_value: true\n```\n\n`topic`, `brokers`, `user`, `password` and `admin_url` are mandatory parameters.\n"
  },
  {
    "path": "compile/message_hub/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nconst config_properties = ['user', 'password', 'brokers', 'topic', 'admin_url']\n\nclass OpenWhiskCompileMessageHub {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n    this.default_package = '/whisk.system/messaging'\n\n    this.hooks = {\n      'before:package:compileEvents': () => BbPromise.bind(this)\n        .then(this.setup)\n        .then(this.processMessageHubEvents)\n    };\n  }\n\n  setup() {\n    if (!this.serverless.service.resources) {\n      this.serverless.service.resources = {};\n    }\n\n    if (!this.serverless.service.resources.triggers) {\n      this.serverless.service.resources.triggers = {};\n    }\n\n    if (!this.serverless.service.resources.rules) {\n      this.serverless.service.resources.rules = {};\n    }\n }\n\n  validateConfig (fnName, config) {\n    if (!config.topic) {\n      throw new this.serverless.classes.Error(\n        `Message Hub event property (topic) missing on function: ${fnName}`\n      )\n    }\n\n    if (!config.package) {\n      config_properties.forEach(prop => {\n        if (!config[prop]) {\n          throw new this.serverless.classes.Error(\n            `Message Hub event property (${prop}) missing on function: ${fnName}`\n          )\n        }\n      })\n    }\n  }\n\n  compileMessageHubTrigger (fnName, config) {\n    this.validateConfig(fnName, config)\n    const name = config.trigger || this.defaultMessageHubName(fnName, config.topic)\n    const feed = `${config.package || this.default_package}/messageHubFeed`\n\n    const feed_parameters = { \n      topic: config.topic,\n      isJSONData: config.json || false,\n      isBinaryKey: config.binary_key || false,\n      isBinaryValue: config.binary_value || false\n    }\n\n    if (!config.package) {\n      feed_parameters.user = config.user\n      feed_parameters.password = config.password\n      feed_parameters.kafka_brokers_sasl = Array.isArray(config.brokers) ? config.brokers.join(',') : config.brokers\n      feed_parameters.kafka_admin_url = config.admin_url\n    }\n\n    return { name, content: { feed, feed_parameters } }\n  }\n\n  defaultMessageHubName (fnName, topic) {\n    return `${this.serverless.service.service}_${fnName}_messagehub_${topic}`\n  }\n\n  processMessageHubEvent (fnName, config) {\n    const fnObj = this.serverless.service.getFunction(fnName)\n    const trigger = this.compileMessageHubTrigger(fnName, config)\n    const rule = config.rule || `${this.defaultMessageHubName(fnName, config.topic)}_rule`\n\n    fnObj.events.push({ trigger: { name: trigger.name, rule } })\n    this.serverless.service.resources.triggers[trigger.name] = trigger.content\n  }\n\n  processMessageHubEvents () {\n    this.serverless.service.getAllFunctions().forEach(name => {\n      const fn = this.serverless.service.getFunction(name)\n      const events = (fn.events || []).filter(e => e.message_hub)\n      events.forEach(e => this.processMessageHubEvent(name, e.message_hub))\n    })\n  }\n}\n\nmodule.exports = OpenWhiskCompileMessageHub\n"
  },
  {
    "path": "compile/message_hub/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileMessageHub = require('../index');\n\ndescribe('OpenWhiskCompileMessageHub', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileMessageHub;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileMessageHub = new OpenWhiskCompileMessageHub(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { log: () => {} };\n    openwhiskCompileMessageHub.setup()\n\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#processMessageHubEvents()', () => {\n    it('should call processMessageHubEvent for each message hub event.', () => {\n      const service = openwhiskCompileMessageHub.serverless.service;\n      const fns = {\n        first: {\n          events: [{}, {message_hub: {package: 'testing_package', topic: 'some_topic'}}, {trigger: true}]\n        },\n        second: {\n          events: [{message_hub: {package: 'another_package', topic: 'some_topic'}}]\n        },\n        third: {}\n      }\n\n      service.getAllFunctions = () => Object.keys(fns)\n      service.getFunction = name => fns[name];\n\n      const spy = openwhiskCompileMessageHub.processMessageHubEvent = sinon.spy()\n      openwhiskCompileMessageHub.processMessageHubEvents()\n      expect(spy.calledTwice).to.be.equal(true)\n      expect(spy.withArgs(\"first\", {package: 'testing_package', topic: 'some_topic'}).calledOnce).to.be.equal(true)\n      expect(spy.withArgs(\"second\", {package: 'another_package', topic: 'some_topic'}).calledOnce).to.be.equal(true)\n    })\n  })\n\n  describe('#processMessageHubEvents()', () => {\n    it('should create trigger & rules and update manifest resources.', () => {\n      const message_hub = { package: 'some_package', topic: 'testing' }\n      const fnObj = { events: [{message_hub}] }\n      serverless.service.getFunction = () => fnObj\n      openwhiskCompileMessageHub.compileMessageHubTrigger = () => ({name: 'serviceName_fnName_messagehub_testing', content: { a: 1 }})\n      openwhiskCompileMessageHub.processMessageHubEvent(\"fnName\", fnObj.events[0].message_hub)\n      expect(fnObj.events[1]).to.be.deep.equal({\n        trigger: { name: 'serviceName_fnName_messagehub_testing', rule: 'serviceName_fnName_messagehub_testing_rule' }\n      })\n\n      expect(serverless.service.resources.triggers).to.be.deep.equal({serviceName_fnName_messagehub_testing: {a: 1}})\n    })\n  })\n\n  describe('#compileMessageHubTrigger()', () => {\n    it('should throw errors for missing topic parameter.', () => {\n      expect(() => openwhiskCompileMessageHub.compileMessageHubTrigger('testing', {}))\n        .to.throw(Error, 'Message Hub event property (topic) missing on function: testing');\n    })\n\n    it('should throw errors for missing mandatory parameters without package', () => {\n      const config = { topic: 'topic', user: 'user', password: 'password', admin_url: 'url', brokers: 'brokers' }\n\n      Object.keys(config).forEach(key => {\n        const cloned = Object.assign({}, config)\n        cloned[key] = ''\n        expect(() => openwhiskCompileMessageHub.compileMessageHubTrigger('testing', cloned))\n          .to.throw(Error, `Message Hub event property (${key}) missing on function: testing`);\n      })\n    })\n\n    it('should return trigger for message hub provider using package.', () => {\n      const topic = 'my_topic', pkge = '/bluemixOrg_bluemixSpace/packageId'\n      const trigger = openwhiskCompileMessageHub.compileMessageHubTrigger('testing', { topic, 'package': pkge }) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_messagehub_${topic}`,\n        content: {\n          feed: `${pkge}/messageHubFeed`,\n          feed_parameters: {\n            topic: `${topic}`,\n            isJSONData: false,\n            isBinaryKey: false,\n            isBinaryValue: false\n          }\n        }\n      })\n    })\n\n    it('should return trigger for message hub provider using package with options.', () => {\n      const topic = 'my_topic', pkge = '/bluemixOrg_bluemixSpace/packageId'\n      const trigger = openwhiskCompileMessageHub.compileMessageHubTrigger('testing', { json: true, binary_value: true, binary_key: true, topic, 'package': pkge }) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_messagehub_${topic}`,\n        content: {\n          feed: `${pkge}/messageHubFeed`,\n          feed_parameters: {\n            topic: `${topic}`,\n            isJSONData: true,\n            isBinaryKey: true,\n            isBinaryValue: true \n          }\n        }\n      })\n    })\n\n    it('should return trigger with minimum message hub config properties.', () => {\n      const config = { topic: 'topic', user: 'user', password: 'password', admin_url: 'url', brokers: 'brokers' }\n      const trigger = openwhiskCompileMessageHub.compileMessageHubTrigger('testing', config) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_messagehub_${config.topic}`,\n        content: {\n          feed: `/whisk.system/messaging/messageHubFeed`,\n          feed_parameters: {\n            kafka_brokers_sasl: config.brokers,\n            user: config.user,\n            password: config.password,\n            topic: config.topic,\n            kafka_admin_url: config.admin_url,\n            isJSONData: false,\n            isBinaryKey: false,\n            isBinaryValue: false \n          }\n        }\n      })\n    })\n\n    it('should return trigger with optional message hub config properties.', () => {\n      const config = { json: true, binary_key: true, binary_value: true, topic: 'topic', user: 'user', password: 'password', admin_url: 'url', brokers: ['a', 'b', 'c'] }\n      const trigger = openwhiskCompileMessageHub.compileMessageHubTrigger('testing', config) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_testing_messagehub_${config.topic}`,\n        content: {\n          feed: `/whisk.system/messaging/messageHubFeed`,\n          feed_parameters: {\n            kafka_brokers_sasl: config.brokers.join(','),\n            user: config.user,\n            password: config.password,\n            topic: config.topic,\n            kafka_admin_url: config.admin_url,\n            isJSONData: true,\n            isBinaryKey: true,\n            isBinaryValue: true\n          }\n        }\n      })\n    })\n  })\n});\n"
  },
  {
    "path": "compile/packages/README.md",
    "content": "# Compile Packages\n\nThis plugins compiles the packages in `serverless.yaml` to corresponding [OpenWhisk Packages](https://github.com/openwhisk/openwhisk/blob/master/docs/packages.md)\ndefinitions.\n\n## How it works\n\n`Compile Packages` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all packages which are defined in `serverless.yaml`.\n\n### Implicit Packages\n\nActions can be assigned to packages by setting the function `name` with a package reference.\n\n```yaml\nfunctions:\n  foo:\n    handler: handler.foo\n    name: \"myPackage/foo\"\n  bar:\n    handler: handler.bar\n    name: \"myPackage/bar\"\n```\n\nIn this example, two new actions (`foo` & `bar`) will be created using the `myPackage` package.\n\nPackages which do not exist will be automatically created during deployments. When using the `remove` command, any packages referenced in the `serverless.yml` will be deleted.\n\n### Explicit Packages\n\nPackages can also be defined explicitly to set shared configuration parameters. Default package parameters are merged into event parameters for each invocation.\n\n```yaml\nfunctions:\n  foo:\n    handler: handler.foo\n    name: \"myPackage/foo\"\n    \nresources:\n  packages:\n    myPackage:\n      parameters:\n        hello: world \n```\n\n### Binding Packages\n\nOpenWhisk also supports \"binding\" external packages into your workspace. Bound packages can have default parameters set for shared actions.\n\nFor example, binding the `/whisk.system/cloudant` package into a new package allows you to set default values for the `username`, `password` and `dbname` properties. Actions from this package can then be invoked with having to pass these parameters in.\n\nDefine packages explicitly with a `binding` parameter to use this behaviour.\n\n```yaml\nresources:\n  packages:\n    mySamples:\n      binding: /whisk.system/cloudant\n      parameters:\n        username: bernie\n        password: sanders\n        dbname: vermont\n```\n\nFor more details on package binding, please see the documentation [here](https://github.com/apache/incubator-openwhisk/blob/master/docs/packages.md#creating-and-using-package-bindings)."
  },
  {
    "path": "compile/packages/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nclass OpenWhiskCompilePackages {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'before:package:compileEvents': () => BbPromise.bind(this)\n        .then(this.setup)\n        .then(this.renameManifestPackages)\n        .then(this.mergeActionPackages),\n      'package:compileEvents': this.compilePackages.bind(this),\n    };\n  }\n\n  setup() {\n    // This object will be used to store the Packages resource, passed directly to\n    // the OpenWhisk SDK during the deploy process.\n    this.serverless.service.packages = {};\n  }\n\n  renameManifestPackages() {\n    const resources = this.serverless.service.resources\n    if (!resources || !resources.packages) return;\n\n    const manifestPackages = resources.packages;\n\n    Object.keys(manifestPackages).forEach(packageKey => {\n      const pack = manifestPackages[packageKey];\n\n      if (pack.name && pack.name !== packageKey) {\n        // move the package under the new name\n        manifestPackages[pack.name] = pack;\n        delete manifestPackages[packageKey];\n      }\n    })\n  }\n\n  mergeActionPackages() {\n    const actionPackages = this.getActionPackages();\n    if (!actionPackages.length) return;\n\n    if (!this.serverless.service.resources) {\n      this.serverless.service.resources = {};\n    }\n\n    if (!this.serverless.service.resources.packages) {\n      this.serverless.service.resources.packages = {};\n    }\n\n    const manifestPackages = this.serverless.service.resources.packages;\n\n    actionPackages.forEach(pkge => {\n      manifestPackages[pkge] = manifestPackages[pkge] || {}\n    })\n  }\n\n  getActionPackages() {\n    const actionPackages = new Set();\n\n    this.serverless.service.getAllFunctions()\n      .map(name => this.serverless.service.getFunction(name))\n      .filter(func => func.name)\n      .forEach(func => {\n        const id = func.name.match(/^(.+)\\/.+$/)\n        if (id) actionPackages.add(id[1])\n      });\n\n    return [...actionPackages];\n  }\n\n  calculatePackageName(packageName, packageObject) {\n    return packageObject.name || packageName;\n  }\n\n  compilePackage(name, params) {\n    const effectiveName = this.calculatePackageName(name, params);\n    const pkge = { name: effectiveName, overwrite: true, package: {} };\n\n    pkge.namespace = params.namespace\n      || this.serverless.service.provider.namespace;\n\n    if (params.hasOwnProperty('overwrite')) {\n      pkge.overwrite = params.overwrite;\n    } else if (this.serverless.service.provider.hasOwnProperty('overwrite')) {\n      pkge.overwrite = params.overwrite;\n    }\n\n    if (params.hasOwnProperty('shared')) {\n      pkge.package.publish = params.shared;\n    }\n\n    if (params.parameters) {\n      pkge.package.parameters = Object.keys(params.parameters).map(\n        key => ({ key, value: params.parameters[key] })\n      );\n    }\n\n    if (params.annotations) {\n      pkge.package.annotations = Object.keys(params.annotations).map(\n        key => ({ key, value: params.annotations[key] })\n      );\n    }\n\n    if (params.binding) {\n      // package identifier must be in format: /namespace/package\n      const to_bind = params.binding.match(/^\\/(.+)\\/(.+)$/)\n      if (!to_bind) {\n        throw new this.serverless.classes.Error(`Invalid Package Binding (${params.binding}). Must be in form: /namespace/package`);\n      }\n      pkge.package.binding = { name: to_bind[2], namespace: to_bind[1] }\n    }\n\n    if (this.options.verbose) {\n      this.serverless.cli.log(`Compiled Package (${name}): ${JSON.stringify(pkge)}`);\n    }\n\n    return pkge;\n  }\n\n  compilePackages() {\n    this.serverless.cli.log('Compiling Packages...');\n\n    const manifestResources = this.serverless.service.resources;\n    const owPackages = this.serverless.service.packages;\n\n    if (!owPackages) {\n      throw new this.serverless.classes.Error(\n        'Missing Packages section from OpenWhisk Resource Manager template');\n    }\n\n    if (manifestResources && manifestResources.packages) {\n      Object.keys(manifestResources.packages).forEach(pkge => {\n        owPackages[pkge] = this.compilePackage(pkge, manifestResources.packages[pkge]);\n      });\n    }\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskCompilePackages;\n"
  },
  {
    "path": "compile/packages/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompilePackages = require('../index');\n\ndescribe('OpenWhiskCompilePackages', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompilePackages;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompilePackages = new OpenWhiskCompilePackages(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskCompilePackages.setup();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#renameManifestPackages()', () => {\n    it('should handle config without resource packages', () => {\n      openwhiskCompilePackages.serverless.service.resources = null\n      openwhiskCompilePackages.renameManifestPackages();\n      openwhiskCompilePackages.serverless.service.resources = {}\n      openwhiskCompilePackages.renameManifestPackages();\n      openwhiskCompilePackages.serverless.service.resources.packages = {}\n      openwhiskCompilePackages.renameManifestPackages();\n    })\n\n    it('should rename packages with explicit names', () => {\n      openwhiskCompilePackages.serverless.service.resources.packages = {\n        'first' : { name: 'firstchanged', parameters: { hello: 'world first' } },\n        'second' : { parameters: { hello: 'world second' } }\n      };\n\n      const expected = {\n        'firstchanged' : { name: 'firstchanged', parameters: { hello: 'world first' } },\n        'second' : { parameters: { hello: 'world second' } }\n      };\n\n      openwhiskCompilePackages.renameManifestPackages();\n      expect(openwhiskCompilePackages.serverless.service.resources.packages)\n        .to.deep.equal(expected);\n\n    })\n  })\n\n  describe('#getActionPackages()', () => {\n    it('should return no package names for functions without name property', () => {\n      const service = openwhiskCompilePackages.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({})\n      service.getFunction = handler;\n\n      expect(openwhiskCompilePackages.getActionPackages()).to.deep.equal([])\n    })\n\n    it('should return no package names for functions with name missing package', () => {\n      const service = openwhiskCompilePackages.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({name: \"foo\"})\n      service.getFunction = handler;\n\n      expect(openwhiskCompilePackages.getActionPackages()).to.deep.equal([])\n    })\n\n    it('should return package names for functions with name including package', () => {\n      const service = openwhiskCompilePackages.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({name: `${name}/${name}`})\n      service.getFunction = handler;\n\n      expect(openwhiskCompilePackages.getActionPackages()).to.deep.equal([\"first\", \"second\", \"third\"])\n\n      service.getAllFunctions = () => [\"first\", \"first\", \"first\"];\n      expect(openwhiskCompilePackages.getActionPackages()).to.deep.equal([\"first\"])\n    })\n  })\n\n  describe('#calculatePackageName()', () => {\n    it('should return package name from object key', () => {\n      expect(openwhiskCompilePackages.calculatePackageName('a', { parameters: 'p' })).to.equal('a');\n    })\n\n    it('should return package name from name property', () => {\n      expect(openwhiskCompilePackages.calculatePackageName('a', { name: 'b' })).to.equal('b');\n    })\n  })\n\n  describe('#mergeActionPackages()', () => {\n    it('should set up packages from action names', () => {\n      openwhiskCompilePackages.serverless.service.resources = {};\n\n      const output = {first: {}, second: {}, third: {}};\n      sandbox.stub(openwhiskCompilePackages, 'getActionPackages', () => [\"first\", \"second\", \"third\"]);\n      openwhiskCompilePackages.mergeActionPackages();\n      expect(openwhiskCompilePackages.serverless.service.resources.packages).to.deep.equal(output)\n    });\n\n    it('should ignore packages already defined in resources', () => {\n      const packages = {first: 1, second: 2, third: 3};\n      openwhiskCompilePackages.serverless.service.resources = { packages };\n\n      sandbox.stub(openwhiskCompilePackages, 'getActionPackages', () => [\"first\", \"second\", \"third\"]);\n      openwhiskCompilePackages.mergeActionPackages();\n      expect(openwhiskCompilePackages.serverless.service.resources.packages).to.deep.equal(packages)\n    })\n  })\n\n  describe('#compilePackages()', () => {\n    it('should throw an error if the resource section is not available', () => {\n      openwhiskCompilePackages.serverless.service.packages = null;\n      expect(() => openwhiskCompilePackages.compilePackages())\n        .to.throw(Error, /Missing Packages section/);\n    });\n\n    it('should return empty packages if manifest has no packages', () =>\n      expect(openwhiskCompilePackages.compilePackages()).to.eventually.fulfilled\n    );\n\n    it('should call compilePackage for each package definition', () => {\n      const packages = { a: {}, b: {}, c: {} };\n      const stub = sinon.stub(openwhiskCompilePackages, 'compilePackage');\n      openwhiskCompilePackages.serverless.service.resources.packages = packages;\n      openwhiskCompilePackages.serverless.service.packages = {};\n      return expect(openwhiskCompilePackages.compilePackages().then(() => {\n        expect(stub.calledThrice).to.be.equal(true);\n        Object.keys(packages).forEach(\n          key => expect(stub.calledWith(key, packages[key])).to.be.equal(true)\n        );\n      })).to.eventually.be.fulfilled;\n    });\n\n    it('should update package definitions from manifest values', () => {\n      const pkge = { shared: true, overwrite: false, namespace: 'another_ns', annotations: { foo: 'bar' }, parameters: { hello: 'world' } };\n      const expected = {\n        name: 'sample',\n        overwrite: false,\n        namespace: 'another_ns',\n        package: {\n          publish: true,\n          parameters: [{ key: 'hello', value: 'world' }],\n          annotations: [{ key: 'foo', value: 'bar' }]\n        },\n      };\n      openwhiskCompilePackages.serverless.service.resources.packages = { sample: pkge };\n      return expect(openwhiskCompilePackages.compilePackages().then(() =>\n        expect(openwhiskCompilePackages.serverless.service.packages)\n          .to.deep.equal({ sample: expected })\n      )).to.eventually.be.fulfilled;\n    });\n\n    it('should merge packages with explicit names', () => {\n      openwhiskCompilePackages.serverless.service.resources.packages = {\n        'first' : { name: 'firstchanged', parameters: { hello: 'world first' } },\n        'second' : { parameters: { hello: 'world second' } }\n      };\n\n      sandbox.stub(openwhiskCompilePackages, 'getActionPackages', () => ['firstchanged', 'second', 'third']);\n\n      const expected = {\n        firstchanged: {\n          name: 'firstchanged',\n          overwrite: true,\n          package: { parameters: [{ key: 'hello', value: 'world first' }] },\n          namespace: 'testing'\n        },\n        second: {\n          name: 'second',\n          overwrite: true,\n          package: { parameters: [{ key: 'hello', value: 'world second' }] },\n          namespace: 'testing'\n        },\n        third: {\n          name: 'third',\n          overwrite: true,\n          package: {},\n          namespace: 'testing'\n        }\n      };\n\n      // Simulate hooks\n      openwhiskCompilePackages.setup();\n      openwhiskCompilePackages.renameManifestPackages();\n      openwhiskCompilePackages.mergeActionPackages();\n\n      return expect(openwhiskCompilePackages.compilePackages().then(() => {\n        expect(openwhiskCompilePackages.serverless.service.packages)\n          .to.deep.equal(expected)\n      })).to.eventually.be.fulfilled;\n    });\n  });\n  describe('#compilePackage()', () => {\n    it('should define packages without a body', () => {\n      const testing = { name: 'testing', namespace: 'testing', overwrite: true, package: {} };\n      const result = openwhiskCompilePackages.compilePackage('testing', {});\n      return expect(result).to.deep.equal(testing);\n    });\n\n    it('should define packages with manifest params', () => {\n      const params = { shared: true, overwrite: false, namespace: 'another_ns', annotations: { foo: 'bar' }, parameters: { hello: 'world' }, binding: '/whisk.system/utils' };\n      const expected = {\n        name: 'testing',\n        overwrite: false,\n        namespace: 'another_ns',\n        package: { \n          publish: true,\n          annotations: [{ key: 'foo', value: 'bar' }],\n          parameters: [{ key: 'hello', value: 'world' }],\n          binding: { namespace: 'whisk.system', name: 'utils' }\n        },\n      };\n      const result = openwhiskCompilePackages.compilePackage('testing', params);\n      return expect(result).to.deep.equal(expected);\n    });\n\n    it('should throw an error for invalid binding identifier', () => {\n      const params = { binding: 'external_ns/external_package' };\n      expect(() => openwhiskCompilePackages.compilePackage('testing', params))\n        .to.throw(Error, /Invalid Package Binding/);\n      params.binding = 'incorrect';\n      expect(() => openwhiskCompilePackages.compilePackage('testing', params))\n        .to.throw(Error, /Invalid Package Binding/);\n    });\n\n    it('should log packages to console when verbose flag is set', () => {\n      openwhiskCompilePackages.options.verbose = true;\n      const log = sandbox.stub(openwhiskCompilePackages.serverless.cli, 'log');\n      sandbox.stub(openwhiskCompilePackages.serverless.cli, 'consoleLog');\n      const params = {\n        name: 'myPackage',\n        overwrite: true,\n        namespace: 'testing'\n      };\n      const result = openwhiskCompilePackages.compilePackage('myPackage', params);\n      expect(log.calledOnce).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal(`Compiled Package (myPackage): ${JSON.stringify(result)}`);\n    });\n\n    it('should rename packages when name parameter is present', () => {\n      const params = { name: 'customname', shared: true, overwrite: false, namespace: 'another_ns', parameters: { hello: 'world' } };\n      const expected = {\n        name: 'customname',\n        overwrite: false,\n        namespace: 'another_ns',\n        package: {\n          publish: true,\n          parameters: [{ key: 'hello', value: 'world' }]\n        },\n      };\n      const result = openwhiskCompilePackages.compilePackage('testing', params);\n      return expect(result).to.deep.equal(expected);\n    });\n  });\n});\n"
  },
  {
    "path": "compile/rules/README.md",
    "content": "# Compile Rules\n\nThis plugins compiles the triggers bound to functions in `serverless.yaml` to\ncorresponding [OpenWhisk Rules](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md)\ndefinitions.\n\n## How it works\n\n`Compile Rules` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all functions which are defined in `serverless.yaml` looking for\nthe defined events. For each `trigger` event defined for the function, the\ncorresponding `rule` will be created.\n\n## Examples\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.handler\n        events:\n            - trigger: \"my_trigger\"\n```\n\nThis definition will create a new Rule, called `my-service_my_trigger_to_index`,\nwhich binds the configured Action (index) to the Trigger (my_trigger).\n\nTriggers can be defined within the `serverless.yaml` file, see the documentation\nin the [`compileTriggers` plugin](../triggers). Triggers that aren't explicitly\ndefined will be automatically created.\n\nIf the event trigger value is a string, containing the trigger name, the rule\nname will be automatically generated in the form: `servicename-trigger-to-action`.\n\nIf the trigger event value is an object, you can set the rule name explicitly.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.handler\n        events:\n          - trigger:\n              name: \"my_trigger\"\n              rule: \"rule_name\"\n```\n\nAt the end all OpenWhisk Rule definitions are merged inside the `serverless.service.rules` section.\n"
  },
  {
    "path": "compile/rules/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nclass OpenWhiskCompileRules {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'before:package:compileEvents': this.setup.bind(this),\n      'package:compileEvents': this.compileRules.bind(this),\n    };\n  }\n\n  setup() {\n    // This object will be used to store the Rule resources, passed directly to\n    // the OpenWhisk SDK during the deploy process.\n    this.serverless.service.rules = {};\n\n    if (this.serverless.service.provider.namespace) {\n      return Promise.resolve();\n    }\n    // Actions and Triggers referenced by Rules must used fully qualified identifiers (including namespace).\n    return this.provider.props().then(props => {\n      this.serverless.service.provider.namespace = props.namespace || '_';\n    });\n  }\n\n  calculateFunctionName(functionName, functionObject) {\n    const namespace = this.calculateFunctionNameSpace(functionObject);\n    const name = functionObject.name || `${this.serverless.service.service}_${functionName}`;\n    return `/${namespace}/${name}`\n  }\n\n  calculateFunctionNameSpace(functionObject) {\n    return functionObject.namespace || this.serverless.service.provider.namespace\n  }\n\n  calculateTriggerName(triggerName) {\n    let namespace = this.serverless.service.provider.namespace;\n\n    const resources = this.serverless.service.resources;\n    if (resources.triggers && resources.triggers[triggerName]) {\n      namespace = resources.triggers[triggerName].namespace || namespace;\n    }\n\n    return `/${namespace}/${triggerName}`;\n }\n\n  generateDefaultRuleName(functionName, triggerName) {\n      return `${this.serverless.service.service}_${triggerName}_to_${functionName}`\n  }\n\n  //\n  // This method takes the rule definitions, parsed from the user's YAML file,\n  // and turns it into the OpenWhisk Rule resource object.\n  //\n  // These resource objects are passed to the OpenWhisk SDK to create the associated Rules\n  // during the deployment process.\n  //\n  // Parameter values will be parsed from the user's YAML definition, either as a value from\n  // the rule definition or the service provider defaults.\n  compileRule(funcName, funcObj, trigger) {\n    const namespace = this.calculateFunctionNameSpace(funcObj);\n    const action = this.calculateFunctionName(funcName, funcObj);\n    const defaultRuleName = this.generateDefaultRuleName(funcName, trigger);\n\n    if (typeof trigger === 'string') {\n      return { ruleName: defaultRuleName, overwrite: true, trigger: this.calculateTriggerName(trigger), action, namespace };\n    }\n    \n    if (!trigger.hasOwnProperty('rule')) {\n      throw new this.serverless.classes.Error(\n        `Missing mandatory rule property from Event Trigger definition for Function: ${funcName}`);\n    }\n    \n    if (!trigger.hasOwnProperty('name')) {\n      throw new this.serverless.classes.Error(\n        `Missing mandatory name property from Event Trigger definition for Function: ${funcName}`);\n    }\n\n    const ruleName = trigger.rule || defaultRuleName;\n\n    let overwrite = true\n    if(trigger.hasOwnProperty('overwrite')) {\n      overwrite = trigger.overwrite;\n    }\n\n    return { ruleName, overwrite, trigger: this.calculateTriggerName(trigger.name), action, namespace };\n  }\n\n  compileFunctionRules(functionName, functionObject) {\n    if (!functionObject.events) return []\n\n    const events = functionObject.events\n      .filter(e => e.trigger)\n      .map(e => this.compileRule(functionName, functionObject, e.trigger))\n\n    if (events.length && this.options.verbose) {\n      this.serverless.cli.log(`Compiled Rule (${functionName}): ${JSON.stringify(events)}`);\n    }\n\n    return events\n  }\n\n  compileRules() {\n    this.serverless.cli.log('Compiling Rules...');\n\n    const manifestResources = this.serverless.service.resources;\n    const owRules = this.serverless.service.rules;\n\n    if (!owRules) {\n      throw new this.serverless.classes.Error(\n        'Missing Rules section from OpenWhisk Resource Manager template');\n    }\n\n    const allFunctions = this.serverless.service.getAllFunctions()\n\n    const functionRules = allFunctions.map(\n      functionName => this.compileFunctionRules(functionName, this.serverless.service.getFunction(functionName))\n    ).reduce((a, b) => a.concat(b), [])\n\n    functionRules.forEach(rule => {\n      owRules[rule.ruleName] = rule;\n    })\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskCompileRules;\n"
  },
  {
    "path": "compile/rules/tests/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileRules = require('../index');\n\ndescribe('OpenWhiskCompileRules', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileRules;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileRules = new OpenWhiskCompileRules(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { consoleLog: () => {}, log: () => {} };\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#setup()', () => {\n    it('should not call provider props if namespace in defaults', () => {\n      openwhiskCompileRules.serverless.getProvider = () => ({props: sinon.assert.fail});\n      openwhiskCompileRules.setup();\n    })\n\n    it('should use provider props if namespace available', () => {\n      openwhiskCompileRules.serverless.service.provider.namespace = null;\n      const props = () => BbPromise.resolve({namespace: 'sample_ns'})\n      openwhiskCompileRules.provider = { props };\n      return openwhiskCompileRules.setup().then(() => {\n        expect(openwhiskCompileRules.serverless.service.provider.namespace).to.equal(\"sample_ns\")\n      });\n    })\n\n    it('should use default namespace if provider namespace missing', () => {\n      openwhiskCompileRules.serverless.service.provider.namespace = null;\n      const props = () => BbPromise.resolve({})\n      openwhiskCompileRules.provider = { props };\n      return openwhiskCompileRules.setup().then(() => {\n        expect(openwhiskCompileRules.serverless.service.provider.namespace).to.equal(\"_\")\n      });\n    })\n  });\n\n  describe('#compileRules()', () => {\n    beforeEach(() => {\n      openwhiskCompileRules.setup();\n    });\n\n    it('should throw an error if the resource section is not available', () => {\n      openwhiskCompileRules.serverless.service.rules = null;\n      expect(() => openwhiskCompileRules.compileRules())\n        .to.throw(Error, /Missing Rules section/);\n    });\n\n    it('should return empty rules if functions has no triggers', () =>\n      expect(openwhiskCompileRules.compileRules().then(() => {\n        expect(openwhiskCompileRules.serverless.service.rules).to.deep.equal({});\n      })).to.eventually.be.fulfilled\n    );\n\n    it('should call compileFunctionRule and update rules for each function with events', () => {\n      const stub = sinon.stub(openwhiskCompileRules, 'compileFunctionRules').returns([{ruleName: 'ruleName'}]);\n      openwhiskCompileRules.serverless.service.rules = {};\n\n      sandbox.stub(openwhiskCompileRules.serverless.service, 'getAllFunctions', () => [\"first\", \"second\", \"third\"]);\n\n      const handler = name => ({events: {}})\n      openwhiskCompileRules.serverless.service.getFunction = handler;\n\n      return expect(openwhiskCompileRules.compileRules().then(() => {\n        expect(openwhiskCompileRules.serverless.service.rules).to.deep.equal({\n          'ruleName': {ruleName: 'ruleName'}\n        });\n        expect(stub.calledThrice).to.be.equal(true);\n      })).to.eventually.be.fulfilled;\n    });\n  });\n\n  describe('#compileFunctionRules()', () => {\n    beforeEach(() => {\n      openwhiskCompileRules.setup();\n    });\n\n    it('should not call compileRule when events parameter is missing', () => {\n      const stub = sinon.stub(openwhiskCompileRules, 'compileRule')\n      const rules = openwhiskCompileRules.compileFunctionRules('name', {})\n      expect(rules).to.deep.equal([]);\n      expect(stub.called).to.be.equal(false);\n    })\n    \n    it('should not call compileRule when events list contains no triggers', () => {\n      const stub = sinon.stub(openwhiskCompileRules, 'compileRule')\n      const rules = openwhiskCompileRules.compileFunctionRules('name', { events: [{\"api\": {}}] })\n      expect(rules).to.deep.equal([]);\n      expect(stub.called).to.be.equal(false);\n    })\n\n    it('should call compileRule when events list contains triggers', () => {\n      const stub = sinon.stub(openwhiskCompileRules, 'compileRule').returns({})\n      const rules = openwhiskCompileRules.compileFunctionRules('name', { events: [\n        {\"trigger\": {}},\n        {\"trigger\": {}},\n        {\"trigger\": {}},\n      ] })\n      expect(rules).to.deep.equal([{}, {}, {}]);\n      expect(stub.calledThrice).to.be.equal(true);\n    })\n\n    it('should log rules when verbose flag is used', () => {\n      openwhiskCompileRules.options.verbose = true\n      const log = sandbox.stub(openwhiskCompileRules.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskCompileRules.serverless.cli, 'consoleLog')\n      const stub = sinon.stub(openwhiskCompileRules, 'compileRule').returns({ foo: 'bar' })\n      openwhiskCompileRules.compileFunctionRules('name', { events: [\n        {\"trigger\": true},\n        {\"trigger\": true},\n        {\"trigger\": true}\n      ] })\n\n      expect(log.calledOnce).to.be.equal(true);\n      const result = JSON.stringify([{foo: 'bar'}, {foo: 'bar'}, {foo: 'bar'}]);\n      expect(log.args[0][0]).to.be.equal(`Compiled Rule (name): ${result}`);\n    })\n\n  });\n\n  describe('#compileRule()', () => {\n    beforeEach(() => {\n      openwhiskCompileRules.setup();\n    });\n\n    it('should define rules from trigger string', () => {\n      openwhiskCompileRules.serverless.service.service = 'my-service' \n      openwhiskCompileRules.serverless.service.provider = {namespace: \"sample_ns\"};\n      const funcObj = {}\n      const trigger = \"some-trigger\"\n      const testing = {\n        ruleName: 'my-service_some-trigger_to_action-name', \n        action: '/sample_ns/my-service_action-name',\n        trigger: '/sample_ns/some-trigger',\n        namespace: 'sample_ns', \n        overwrite: true\n      };\n      const result = openwhiskCompileRules.compileRule('action-name', {}, trigger);\n      return expect(result).to.deep.equal(testing);\n    });\n\n    it('should define rules from trigger object', () => {\n      openwhiskCompileRules.serverless.service.service = 'my-service' \n      openwhiskCompileRules.serverless.service.provider = {namespace: \"sample_ns\"};\n      const funcObj = { namespace: 'custom_ns' }\n      const trigger = {name: \"custom_trigger_name\", rule: \"custom_rule_name\", overwrite: false}\n      const testing = {\n        ruleName: 'custom_rule_name', \n        action: '/custom_ns/my-service_action-name',\n        trigger: '/sample_ns/custom_trigger_name',\n        namespace: 'custom_ns',\n        overwrite: false \n      };\n      const result = openwhiskCompileRules.compileRule('action-name', funcObj, trigger);\n      return expect(result).to.deep.equal(testing);\n    });\n\n    it('should throw if trigger missing rule', () => {\n      expect(() => openwhiskCompileRules.compileRule('', {}, {name: ''}))\n        .to.throw(Error, /Missing mandatory rule property from Event Trigger/);\n    });\n\n    it('should throw if trigger missing name', () => {\n      expect(() => openwhiskCompileRules.compileRule('', {}, {rule: ''}))\n        .to.throw(Error, /Missing mandatory name property from Event Trigger/);\n    });\n\n\n    /*\n    it('should define rules with manifest params', () => {\n      const params = { overwrite: true, namespace: 'another_ns', parameters: { hello: 'world' } };\n      const expected = {\n        ruleName: 'testing',\n        overwrite: true,\n        namespace: 'another_ns',\n        parameters: [{ key: 'hello', value: 'world' }],\n      };\n      const result = openwhiskCompileRules.compileRule('testing', params);\n      return expect(result).to.deep.equal(expected);\n    });\n\n    it('should define rules with feed manifest params', () => {\n      const feedName = '/ns/package/feed';\n      const params = { feed: feedName, feed_parameters: { hello: 'world' } };\n      const expected = {\n        ruleName: 'myRule',\n        overwrite: true,\n        namespace: 'testing',\n        feed: {\n          feedName: 'package/feed',\n          namespace: 'ns',\n          rule: '/testing/myRule',\n          params: params.feed_parameters,\n        },\n      };\n      const result = openwhiskCompileRules.compileRule('myRule', params);\n      return expect(result).to.deep.equal(expected);\n    });\n    */\n  });\n});\n"
  },
  {
    "path": "compile/schedule/README.md",
    "content": "# Compile Triggers\n\nThis plugins compiles the schedule events in `serverless.yaml` to corresponding [OpenWhisk Alarm Trigger Feeds](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md)\ndefinitions.\n\n## How it works\n\n`Compile Schedule` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all schedule event which are defined in `serverless.yaml`.\n\n### Implicit Schedule Definition\n\nAlarm triggers referenced in the function event configuration don't have to\nexplicitly defined the triggers and rules in the `resources` section. The plugin\nwill set up these resources for creation without any further configuration.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - schedule: cron(* * * * *) // fires every minute \n```\n\nThe plugin will create a trigger called `${serviceName}_${fnName}_alarmTrigger`\nand a rule called `${serviceName}_${fnName}_alarmRule` to bind the function to\nthe cron events.\n\n### Explicit Schedule Definition\n\nAdding extra properties for the alarm event can be handled by defining an object\nas the `schedule` value rather than the cron string.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - schedule:\n              rate: cron(* * * * *)\n              trigger: custom_trigger_name\n              rule: custom_rule_name\n              max: 10000 // maximum event fires, defaults to 1000\n              params: \n                hello: world\n\n```\n\n`rate` is the only mandatory property. \n\nAll OpenWhisk Schedule events are merged inside the `serverless.service.triggers` and `serverless.service.rules` section.\n"
  },
  {
    "path": "compile/schedule/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nclass OpenWhiskCompileSchedules {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n    this.feed = '/whisk.system/alarms/alarm'\n\n    this.hooks = {\n      'before:package:compileEvents': () => BbPromise.bind(this)\n        .then(this.setup)\n        .then(this.processScheduleEvents)\n    };\n  }\n\n  setup() {\n    if (!this.serverless.service.resources) {\n      this.serverless.service.resources = {};\n    }\n\n    if (!this.serverless.service.resources.triggers) {\n      this.serverless.service.resources.triggers = {};\n    }\n\n    if (!this.serverless.service.resources.rules) {\n      this.serverless.service.resources.rules = {};\n    }\n }\n\n  // correct rate syntax is: cron(* * * * *)\n  parseScheduleRate (rate) {\n    const cron = rate.match(/cron\\((.*)\\)/)\n    if (!cron || !cron[1] || cron[1].split(' ').length !== 5) {\n      throw new this.serverless.classes.Error(\n        [`Schedule event rate property value is invalid: ${rate}`, \n         'The correct syntax should be \"cron(_ _ _ _ _)\"'].join('\\n')\n      )\n    }\n\n    return cron[1]\n  }\n\n  compileScheduleTrigger (fnName, schedule) {\n    const name = schedule.trigger || `${this.serverless.service.service}_${fnName}_schedule_trigger`\n    const cron = this.parseScheduleRate(schedule.rate || schedule)\n    const trigger_payload = JSON.stringify(schedule.params || {})\n\n    const feed_parameters = { cron, trigger_payload }\n\n    if (schedule.max) {\n      feed_parameters.maxTriggers = schedule.max\n    }\n\n    return { name, content: { feed: this.feed, feed_parameters } }\n  }\n\n  defaultScheduleRuleName (triggerName, fnName) {\n    return `${this.serverless.service.service}_${fnName}_schedule_rule`\n  }\n\n  processScheduleEvent (fnName, schedule) {\n    const fnObj = this.serverless.service.getFunction(fnName)\n    const trigger = this.compileScheduleTrigger(fnName, schedule)\n    const rule = schedule.rule || this.defaultScheduleRuleName(trigger.name, fnName)\n\n    fnObj.events.push({ trigger: { name: trigger.name, rule } })\n    this.serverless.service.resources.triggers[trigger.name] = trigger.content\n  }\n\n  processScheduleEvents () {\n    this.serverless.service.getAllFunctions().forEach(name => {\n      const fn = this.serverless.service.getFunction(name)\n      const events = (fn.events || []).filter(e => e.schedule)\n      events.forEach(e => this.processScheduleEvent(name, e.schedule))\n    })\n  }\n}\n\nmodule.exports = OpenWhiskCompileSchedules;\n"
  },
  {
    "path": "compile/schedule/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileSchedules = require('../index');\n\ndescribe('OpenWhiskCompileSchedules', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileSchedules;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileSchedules = new OpenWhiskCompileSchedules(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { log: () => {} };\n    openwhiskCompileSchedules.setup()\n\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#processScheduleEvents()', () => {\n    it('should update create schedule trigger and update manifest resources.', () => {\n      const fnObj = { events: [{schedule: \"cron(* * * * *)\"}] }\n      serverless.service.getFunction = () => fnObj\n      openwhiskCompileSchedules.compileScheduleTrigger = () => ({name: 'serviceName_fnName_schedule_trigger', content: { a: 1 }})\n      openwhiskCompileSchedules.processScheduleEvent(\"fnName\", fnObj.events[0].schedule)\n      expect(fnObj.events[1]).to.be.deep.equal({\n        trigger: { name: 'serviceName_fnName_schedule_trigger', rule: 'serviceName_fnName_schedule_rule' }\n      })\n\n      expect(serverless.service.resources.triggers).to.be.deep.equal({serviceName_fnName_schedule_trigger: {a: 1}})\n    })\n  })\n\n  describe('#processScheduleEvents()', () => {\n    it('should call processEventSchedule for each schedule event.', () => {\n      const service = openwhiskCompileSchedules.serverless.service;\n      const fns = {\n        first: {\n          events: [{}, {schedule: \"cron(* * * * *)\"}, {trigger: true}]\n        },\n        second: {\n          events: [{schedule: \"cron(* * * * *)\"}]\n        },\n        third: {}\n      }\n\n      service.getAllFunctions = () => Object.keys(fns)\n      service.getFunction = name => fns[name];\n\n      const spy = openwhiskCompileSchedules.processScheduleEvent = sinon.spy()\n      openwhiskCompileSchedules.processScheduleEvents()\n      expect(spy.calledTwice).to.be.equal(true)\n      expect(spy.withArgs(\"first\", \"cron(* * * * *)\").calledOnce).to.be.equal(true)\n      expect(spy.withArgs(\"second\", \"cron(* * * * *)\").calledOnce).to.be.equal(true)\n    })\n  })\n\n\n  describe('#compileScheduleTrigger()', () => {\n    it('should throw errors for incorrect rate definition.', () => {\n      let name = 'my_fn'\n      expect(() => openwhiskCompileSchedules.compileScheduleTrigger(name, 'ron(* * * * *)'))\n        .to.throw(Error, /Schedule event rate property value is invalid/);\n      expect(() => openwhiskCompileSchedules.compileScheduleTrigger(name, '* * * * *'))\n        .to.throw(Error, /Schedule event rate property value is invalid/);\n      expect(() => openwhiskCompileSchedules.compileScheduleTrigger(name, 'cron(* * * *)'))\n        .to.throw(Error, /Schedule event rate property value is invalid/);\n      expect(() => openwhiskCompileSchedules.compileScheduleTrigger(name, 'cron(* * * * * *)'))\n        .to.throw(Error, /Schedule event rate property value is invalid/);\n    })\n\n    it('should return default trigger for simple schedule event.', () => {\n      let name = 'my_fn', rate = 'cron(* * * * *)'\n      const trigger = openwhiskCompileSchedules.compileScheduleTrigger(name, rate) \n      expect(trigger).to.be.deep.equal({\n        name: `${serverless.service.service}_${name}_schedule_trigger`,\n        content: {\n          feed: '/whisk.system/alarms/alarm',\n          feed_parameters: {\n            cron: '* * * * *',\n            trigger_payload: \"{}\"\n          }\n        }\n      })\n    })\n\n    it('should return trigger for object schedule event.', () => {\n      const name = 'my_fn'\n      const rate = {\n        rate: 'cron(* * * * *)',\n        trigger: 'trigger_name',\n        max: 500,\n        params: { hello: 'world' }\n      }\n      const trigger = openwhiskCompileSchedules.compileScheduleTrigger(name, rate) \n      expect(trigger).to.be.deep.equal({\n        name: `trigger_name`,\n        content: {\n          feed: '/whisk.system/alarms/alarm',\n          feed_parameters: {\n            cron: '* * * * *',\n            trigger_payload: JSON.stringify(rate.params),\n            maxTriggers: 500\n          }\n        }\n      })\n    })\n  })\n\n    /*\n\n  // should throw for incorrect format...\n  describe('#getEventSchedules()', () => {\n    it('should return all names for simple triggers registered on functions', () => {\n      const service = openwhiskCompileSchedules.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({events: [{schedule: \"cron(* * * * *)\"}, {schedule: \"cron(* * * * *)\"}]})\n      service.getFunction = handler;\n\n      expect(openwhiskCompileSchedules.getEventSchedules()).to.deep.equal([\"blah\", \"foo\"])\n    })\n\n    /*\n    it('should return all names for complex triggers registered on functions', () => {\n      const service = openwhiskCompileSchedules.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({events: [{trigger: {name: \"blah\"}}, {trigger: {name: \"foo\"}}]})\n      service.getFunction = handler;\n\n      expect(openwhiskCompileSchedules.getEventSchedules()).to.deep.equal([\"blah\", \"foo\"])\n    })\n  })\n\n    */\n  /*\n  describe('#mergeEventSchedules()', () => {\n    it('should set up non-existant triggers', () => {\n      openwhiskCompileSchedules.serverless.service.resources = {};\n\n      const output = {first: {}, second: {}, third: {}};\n      sandbox.stub(openwhiskCompileSchedules, 'getEventSchedules', () => [\"first\", \"second\", \"third\"]);\n      openwhiskCompileSchedules.mergeEventSchedules();\n      expect(openwhiskCompileSchedules.serverless.service.resources.triggers).to.deep.equal(output)\n    });\n\n    it('should ignore existing triggers', () => {\n      const triggers = {first: 1, second: 2, third: 3};\n      openwhiskCompileSchedules.serverless.service.resources = { triggers };\n\n      sandbox.stub(openwhiskCompileSchedules, 'getEventSchedules', () => [\"first\", \"second\", \"third\"]);\n      openwhiskCompileSchedules.mergeEventSchedules();\n      expect(openwhiskCompileSchedules.serverless.service.resources.triggers).to.deep.equal(triggers)\n    })\n\n  })\n  */\n\n});\n"
  },
  {
    "path": "compile/servicebindings/README.md",
    "content": "# Service Bindings\n\nThis plugin binds IBM Cloud platform service credentials to actions and packages in `serverless.yaml`.\n\n## How it works\n\n`Compile Service Bindings` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\n***This feature requires the [IBM Cloud CLI](https://console.bluemix.net/docs/cli/reference/bluemix_cli/download_cli.html#download_install) and [IBM Cloud Functions plugin](https://console.bluemix.net/openwhisk/learn/cli) to be installed.***\n\nIBM Cloud Functions supports [automatic binding of service credentials](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) to actions using the CLI.\n\nBound service credentials will be passed as the `__bx_creds` parameter in the invocation parameters.\n\nThis feature is also available through the `serverless.yaml` file using the `bind` property for each function.\n\n```yaml\nfunctions:\n  my_function:\n    handler: file_name.handler    \n    bind:\n      - service:\n          name: cloud-object-storage\n          instance: my-cos-storage\n```\n\nThe `service` configuration supports the following properties.\n\n- `name`: identifier for the cloud service\n- `instance`: instance name for service (*optional*) \n- `key`: key name for instance and service (*optional*) \n\n*If the `instance` or `key` properties are missing, the first available instance and key found will be used.*\n\nBinding services removes the need to manually create default parameters for service keys from platform services.\n\nMore details on binding service credentials to actions can be found in the [official documentation](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) and [this blog post](http://jamesthom.as/blog/2018/06/05/binding-iam-services-to-ibm-cloud-functions/).\n\nPackages defined in the `resources` section can bind services using the same configuration properties.\n\n```yaml\nresources:\n  packages:\n    myPackage:\n      bind:\n        - service:\n            name: cloud-object-storage\n            instance: my-cos-storage\n```"
  },
  {
    "path": "compile/servicebindings/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nclass OpenWhiskCompileServiceBindings {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'package:compileEvents': this.compileServiceBindings.bind(this)\n    };\n  }\n\n  calculateFunctionName(name, props) {\n    return props.name || `${this.serverless.service.service}_${name}`;\n  }\n\n  parseServiceBindings(name, properties) {\n    const bindings = properties.bind || []\n    const servicebindings = bindings.filter(b => b.service)\n      .map(b => Object.assign(b.service, { action: name } ))\n\n    const serviceNames = new Set()\n\n    for (let sb of servicebindings) {\n      if (!sb.hasOwnProperty('name')) {\n        throw new Error(`service binding missing name parameter: ${JSON.stringify(sb)}`)\n      }\n\n      if (serviceNames.has(sb.name)) {\n        throw new Error(`multiple bindings for same service not supported: ${sb.name}`)\n      }\n\n      serviceNames.add(sb.name)\n    }\n\n    return servicebindings\n  }\n\n  compileFnServiceBindings() {\n    return this.serverless.service.getAllFunctions()\n      .map(name => {\n        const fnObj = this.serverless.service.getFunction(name)\n        const fnName = this.calculateFunctionName(name, fnObj)\n        return this.parseServiceBindings(fnName, fnObj)\n      })\n      .filter(sbs => sbs.length > 0)\n  }\n\n  compilePkgServiceBindings() {\n    const manifestResources = this.serverless.service.resources || {}\n    const packages = manifestResources.packages || {}\n\n    return Object.keys(packages)\n      .map(name => this.parseServiceBindings(name, packages[name]))\n      .filter(sbs => sbs.length > 0)\n  }\n\n  compileServiceBindings() {\n    this.serverless.cli.log('Compiling Service Bindings...');\n\n    const fnServiceBindings = this.compileFnServiceBindings()\n    const pkgServiceBindings = this.compilePkgServiceBindings()\n\n    this.serverless.service.bindings = {\n      fns: fnServiceBindings,\n      packages: pkgServiceBindings\n    }\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskCompileServiceBindings;\n"
  },
  {
    "path": "compile/servicebindings/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileServiceBindings = require('../index');\n\ndescribe('OpenWhiskCompileServiceBindings', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileServiceBindings;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileServiceBindings = new OpenWhiskCompileServiceBindings(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { consoleLog: () => {}, log: () => {} };\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#parseServiceBindings()', () => {\n    it('should return empty array when missing service bindings', () => {\n      const action = 'fnName'\n      expect(openwhiskCompileServiceBindings.parseServiceBindings(action, {})).to.deep.equal([])\n      expect(openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: []})).to.deep.equal([])\n      expect(openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: [{}]})).to.deep.equal([])\n      expect(openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: [{blah: {}}]})).to.deep.equal([])\n    })\n\n    it('should return array with single service binding property', () => {\n      const action = 'fnName'\n      const service = { name: 'my-service', instance: 'my-instance', key: 'mykey' }\n      const response = { action: `fnName`, name: 'my-service', instance: 'my-instance', key: 'mykey' }\n      const result = openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: [{ service }]})\n      expect(result).to.deep.equal([response])\n    })\n\n    it('should return array with multiple service binding properties', () => {\n      const action = 'fnName'\n      const service_a = { action: `serviceName_fnName`, name: 'my-service-a', instance: 'my-instance-a', key: 'mykey' }\n      const service_b = { action: `serviceName_fnName`, name: 'my-service-b', instance: 'my-instance-b', key: 'mykey' }\n      const service_c = { action: `serviceName_fnName`, name: 'my-service-c', instance: 'my-instance-c', key: 'mykey' }\n      const services = [{ service: service_a }, { service: service_b }, { service: service_c } ]\n      const result = openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: services})\n      expect(result).to.deep.equal([service_a, service_b, service_c])\n    })\n   \n    it('should throw an error if service binding is missing name', () => {\n      const service = { instance: 'my-instance-a', key: 'mykey' }\n      const action = 'fnName'\n      const services = [{ service }]\n      expect(() => openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: services}))\n        .to.throw(Error, /service binding missing name parameter/);\n    });\n \n    it('should throw an error if multiple bindings for same service name', () => {\n      const action = 'fnName'\n      const service = { name: 'my-service', instance: 'my-instance-a', key: 'mykey' }\n      const services = [{ service }, { service }]\n      expect(() => openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: services}))\n        .to.throw(Error, /multiple bindings for same service not supported/);\n    });\n  })\n\n  describe('#compileServiceBindings()', () => {\n    it('should return service bindings for simple functions', () => {\n      const fns = {\n        a: { bind: [{ service: { name: 'service-name-a' } }] },\n        b: { bind: [{ service: { name: 'service-name-b', instance: 'instance-name' } }] },\n        c: { bind: [{ service: { name: 'service-name-a' } }, { service: { name: 'service-name-b' } }] },\n        d: { },\n      }\n\n      const service = openwhiskCompileServiceBindings.serverless.service\n      service.getAllFunctions = () => Object.keys(fns)\n      service.getFunction = name => fns[name]\n\n      const services = [\n        [{ action: 'serviceName_a', name: 'service-name-a' }],\n        [{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' }],\n        [{ action: 'serviceName_c', name: 'service-name-a' }, { action: 'serviceName_c', name: 'service-name-b' }]\n      ]\n      return openwhiskCompileServiceBindings.compileServiceBindings().then(result => {\n        expect(service.bindings.fns).to.deep.equal(services)\n        expect(service.bindings.packages).to.deep.equal([])\n      })\n    })\n\n    it('should return service bindings for functions with explicit name', () => {\n      const fns = {\n        a: { name: 'some_name', bind: [{ service: { name: 'service-name-a' } }] }\n      }\n\n      const service = openwhiskCompileServiceBindings.serverless.service\n      service.getAllFunctions = () => Object.keys(fns)\n      service.getFunction = name => fns[name]\n\n      const services = [ [{ action: 'some_name', name: 'service-name-a' }] ]\n      return openwhiskCompileServiceBindings.compileServiceBindings().then(result => {\n        expect(service.bindings.fns).to.deep.equal(services)\n        expect(service.bindings.packages).to.deep.equal([])\n      })\n    })\n\n\n    it('should return service bindings for packages', () => {\n      const service = openwhiskCompileServiceBindings.serverless.service\n      service.resources.packages = {\n        a: { bind: [{ service: { name: 'service-name-a' } }] },\n        b: { bind: [{ service: { name: 'service-name-b', instance: 'instance-name' } }] },\n        c: { bind: [{ service: { name: 'service-name-a' } }, { service: { name: 'service-name-b' } }] },\n        d: { },\n      }\n\n      const services = [\n        [{ action: 'a', name: 'service-name-a' }],\n        [{ action: 'b', name: 'service-name-b', instance: 'instance-name' }],\n        [{ action: 'c', name: 'service-name-a' }, { action: 'c', name: 'service-name-b' }]\n      ]\n\n      return openwhiskCompileServiceBindings.compileServiceBindings().then(() => {\n        expect(service.bindings.packages).to.deep.equal(services);\n        expect(service.bindings.fns).to.deep.equal([]);\n      });\n    });\n\n    it('should return service bindings for functions & packages', () => {\n      const service = openwhiskCompileServiceBindings.serverless.service;\n      service.resources.packages = {\n        a: { bind: [{ service: { name: 'service-name-a' } }] }\n      };\n\n      const fns = {\n        b: { bind: [{ service: { name: 'service-name-b', instance: 'instance-name' } }] },\n      }\n\n      service.getAllFunctions = () => Object.keys(fns)\n      service.getFunction = name => fns[name]\n\n      const services = {\n        packages: [[{ action: 'a', name: 'service-name-a' }]],\n        fns: [[{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' }]]\n      }\n\n      return openwhiskCompileServiceBindings.compileServiceBindings().then(() => {\n        expect(service.bindings).to.deep.equal(services);\n      });\n\n    })\n  })\n});\n"
  },
  {
    "path": "compile/triggers/README.md",
    "content": "# Compile Triggers\n\nThis plugins compiles the triggers in `serverless.yaml` to corresponding [OpenWhisk Triggers](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md)\ndefinitions.\n\n## How it works\n\n`Compile Triggers` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.\n\nIt loops over all triggers which are defined in `serverless.yaml`.\n\n### Implicit Trigger Definition\n\nTriggers referenced from function event configuration don't have to be\nexplicitly defined in the `resources` section. The plugin will set up these\nresources for creation without any further configuration.\n\n```yaml\n# serverless.yaml\nfunctions:\n    index:\n        handler: users.main\n        events:\n            - triggers: \n                trigger: \"myTriggerName\"\n```\n\nThis function handler includes a reference to a trigger that will be created\nduring deployment.\n\n### Explicit Trigger Definition\n\nSpecifying triggers in the `resources` section allows users to override default\nparameters used when creating triggers implicitly.\n\nThe trigger will be identified by the `triggers` property identifier, using the\nnamespace from service provider defaults (unless set manually using the\n`namespace` property).\n\nDefault parameters for each trigger can be set using the `parameters` property.\n\nConnecting triggers to event feeds is supported through the `feed` and\n`feed_parameters` properties, as shown in the example below.\n\n```yaml\n# serverless.yaml\nresources:\n    triggers:\n        myTrigger:\n            parameters: \n                hello: world\n            feed: /whisk.system/alarms/alarm\n            feed_parameters: \n                cron: '*/8 * * * * *'\n```\n\nAt the end all OpenWhisk Trigger definitions are merged inside the `serverless.service.triggers` section.\n"
  },
  {
    "path": "compile/triggers/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nclass OpenWhiskCompileTriggers {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'before:package:compileEvents': () => BbPromise.bind(this)\n        .then(this.setup)\n        .then(this.mergeEventTriggers),\n      'package:compileEvents': this.compileTriggers.bind(this),\n    };\n  }\n\n  setup() {\n    // This object will be used to store the Trigger resources, passed directly to\n    // the OpenWhisk SDK during the deploy process.\n    this.serverless.service.triggers = {};\n  }\n\n  mergeEventTriggers() {\n    const triggers = this.getEventTriggers();\n    if (!triggers.length) return;\n\n    if (!this.serverless.service.resources) {\n      this.serverless.service.resources = {};\n    }\n\n    if (!this.serverless.service.resources.triggers) {\n      this.serverless.service.resources.triggers = {};\n    }\n\n    const manifestTriggers = this.serverless.service.resources.triggers || {};\n\n    triggers.forEach(trigger => {\n      manifestTriggers[trigger] = manifestTriggers[trigger] || {}\n    })\n  }\n\n  getEventTriggers() {\n    const eventTriggers = new Set();\n\n    this.serverless.service.getAllFunctions()\n      .map(name => this.serverless.service.getFunction(name))\n      .filter(func => func.events)\n      .forEach(func => func.events.forEach(event => {\n        if (event.trigger) {\n          eventTriggers.add(event.trigger.name || event.trigger)\n        }\n      }));\n\n    return [...eventTriggers];\n  }\n\n  // Trigger identifiers are composed of a namespace and a name.\n  // The name may optionally include a package identifier.\n  //\n  // Valid examples shown here:\n  //\n  // /james.thomas@uk.ibm.com/myPackage/myTrigger\n  // /james.thomas@uk.ibm.com/myTrigger\n  compileTriggerFeed(trigger, feed, params) {\n    const feedPathParts = feed.split('/').filter(i => i);\n    const namespace = feedPathParts.splice(0, 1).join();\n    const feedName = feedPathParts.join('/');\n\n    return { trigger, feedName, namespace, params };\n  }\n\n  //\n  // This method takes the trigger definitions, parsed from the user's YAML file,\n  // and turns it into the OpenWhisk Trigger resource object.\n  //\n  // These resource objects are passed to the OpenWhisk SDK to create the associated Triggers\n  // during the deployment process.\n  //\n  // Parameter values will be parsed from the user's YAML definition, either as a value from\n  // the trigger definition or the service provider defaults.\n  compileTrigger(name, params) {\n    const trigger = { triggerName: name, overwrite: true };\n\n    trigger.namespace = params.namespace\n      || this.serverless.service.provider.namespace;\n\n    if (params.hasOwnProperty('overwrite')) {\n      trigger.overwrite = params.overwrite;\n    } else if (this.serverless.service.provider.hasOwnProperty('overwrite')) {\n      trigger.overwrite = params.overwrite;\n    }\n\n    if (params.parameters) {\n      trigger.parameters = Object.keys(params.parameters).map(\n        key => ({ key, value: params.parameters[key] })\n      );\n    }\n\n    // binding triggers to event feeds is sent as a separate API request\n    // once triggers have been created.\n    if (params.feed) {\n      trigger.feed = this.compileTriggerFeed(\n        `/${trigger.namespace}/${trigger.triggerName}`, params.feed, params.feed_parameters\n      );\n    }\n\n    if (this.options.verbose) {\n      this.serverless.cli.log(`Compiled Trigger (${name}): ${JSON.stringify(trigger)}`);\n    }\n\n    return trigger;\n  }\n\n  compileTriggers() {\n    this.serverless.cli.log('Compiling Triggers & Feeds...');\n\n    const manifestResources = this.serverless.service.resources;\n    const owTriggers = this.serverless.service.triggers;\n\n    if (!owTriggers) {\n      throw new this.serverless.classes.Error(\n        'Missing Triggers section from OpenWhisk Resource Manager template');\n    }\n\n    if (manifestResources && manifestResources.triggers) {\n      Object.keys(manifestResources.triggers).forEach(trigger => {\n        owTriggers[trigger] = this.compileTrigger(trigger, manifestResources.triggers[trigger]);\n      });\n    }\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskCompileTriggers;\n"
  },
  {
    "path": "compile/triggers/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\nconst sinon = require('sinon');\nconst OpenWhiskCompileTriggers = require('../index');\n\ndescribe('OpenWhiskCompileTriggers', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskCompileTriggers;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    serverless = {classes: {Error}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskCompileTriggers = new OpenWhiskCompileTriggers(serverless, options);\n    serverless.service.service = 'serviceName';\n    serverless.service.provider = {\n      namespace: 'testing',\n      apihost: '',\n      auth: '',\n    };\n\n    serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskCompileTriggers.setup();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#getEventTriggers()', () => {\n    it('should return all names for simple triggers registered on functions', () => {\n      const service = openwhiskCompileTriggers.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({events: [{trigger: \"blah\"}, {trigger: \"foo\"}]})\n      service.getFunction = handler;\n\n      expect(openwhiskCompileTriggers.getEventTriggers()).to.deep.equal([\"blah\", \"foo\"])\n    })\n\n    it('should return all names for complex triggers registered on functions', () => {\n      const service = openwhiskCompileTriggers.serverless.service;\n      service.getAllFunctions = () => [\"first\", \"second\", \"third\"];\n      const handler = name => ({events: [{trigger: {name: \"blah\"}}, {trigger: {name: \"foo\"}}]})\n      service.getFunction = handler;\n\n      expect(openwhiskCompileTriggers.getEventTriggers()).to.deep.equal([\"blah\", \"foo\"])\n    })\n  })\n\n  describe('#mergeEventTriggers()', () => {\n    it('should set up non-existant triggers', () => {\n      openwhiskCompileTriggers.serverless.service.resources = {};\n\n      const output = {first: {}, second: {}, third: {}};\n      sandbox.stub(openwhiskCompileTriggers, 'getEventTriggers', () => [\"first\", \"second\", \"third\"]);\n      openwhiskCompileTriggers.mergeEventTriggers();\n      expect(openwhiskCompileTriggers.serverless.service.resources.triggers).to.deep.equal(output)\n    });\n\n    it('should ignore existing triggers', () => {\n      const triggers = {first: 1, second: 2, third: 3};\n      openwhiskCompileTriggers.serverless.service.resources = { triggers };\n\n      sandbox.stub(openwhiskCompileTriggers, 'getEventTriggers', () => [\"first\", \"second\", \"third\"]);\n      openwhiskCompileTriggers.mergeEventTriggers();\n      expect(openwhiskCompileTriggers.serverless.service.resources.triggers).to.deep.equal(triggers)\n    })\n\n  })\n\n  describe('#compileTriggers()', () => {\n    it('should throw an error if the resource section is not available', () => {\n      openwhiskCompileTriggers.serverless.service.triggers = null;\n      expect(() => openwhiskCompileTriggers.compileTriggers())\n        .to.throw(Error, /Missing Triggers section/);\n    });\n\n    it('should return empty triggers if manifest has no triggers', () =>\n      expect(openwhiskCompileTriggers.compileTriggers()).to.eventually.fulfilled\n    );\n\n    it('should call compileTrigger for each trigger definition', () => {\n      const triggers = { a: {}, b: {}, c: {} };\n      const stub = sinon.stub(openwhiskCompileTriggers, 'compileTrigger');\n      openwhiskCompileTriggers.serverless.service.resources.triggers = triggers;\n      openwhiskCompileTriggers.serverless.service.triggers = {};\n      return expect(openwhiskCompileTriggers.compileTriggers().then(() => {\n        expect(stub.calledThrice).to.be.equal(true);\n        Object.keys(triggers).forEach(\n          key => expect(stub.calledWith(key, triggers[key])).to.be.equal(true)\n        );\n      })).to.eventually.be.fulfilled;\n    });\n\n    it('should update trigger definitions from manifest values', () => {\n      const trigger = { overwrite: true, namespace: 'another_ns', parameters: { hello: 'world' } };\n      const expected = {\n        triggerName: 'sample',\n        overwrite: true,\n        namespace: 'another_ns',\n        parameters: [{ key: 'hello', value: 'world' }],\n      };\n      openwhiskCompileTriggers.serverless.service.resources.triggers = { sample: trigger };\n      return expect(openwhiskCompileTriggers.compileTriggers().then(() =>\n        expect(openwhiskCompileTriggers.serverless.service.triggers)\n          .to.deep.equal({ sample: expected })\n      )).to.eventually.be.fulfilled;\n    });\n  });\n\n  describe('#compileTriggerFeed()', () => {\n    it('should define trigger feed without parameters', () => {\n      const expected = {\n        trigger: '/ns/testing',\n        feedName: 'package/action',\n        namespace: 'ns',\n        params: {},\n      };\n      const result\n        = openwhiskCompileTriggers.compileTriggerFeed('/ns/testing', '/ns/package/action', {});\n      return expect(result).to.deep.equal(expected);\n    });\n    it('should define trigger feed with parameters', () => {\n      const expected = {\n        trigger: '/ns/testing',\n        feedName: 'package/action',\n        namespace: 'ns',\n        params: {\n          hello: 'world',\n        },\n      };\n      const result = openwhiskCompileTriggers\n        .compileTriggerFeed('/ns/testing', '/ns/package/action', { hello: 'world' });\n      return expect(result).to.deep.equal(expected);\n    });\n  });\n\n  describe('#compileTrigger()', () => {\n    it('should define triggers without a body', () => {\n      const testing = { triggerName: 'testing', namespace: 'testing', overwrite: false };\n      const result = openwhiskCompileTriggers.compileTrigger('testing', testing);\n      return expect(result).to.deep.equal(testing);\n    });\n\n    it('should define triggers with manifest params', () => {\n      const params = { overwrite: true, namespace: 'another_ns', parameters: { hello: 'world' } };\n      const expected = {\n        triggerName: 'testing',\n        overwrite: true,\n        namespace: 'another_ns',\n        parameters: [{ key: 'hello', value: 'world' }],\n      };\n      const result = openwhiskCompileTriggers.compileTrigger('testing', params);\n      return expect(result).to.deep.equal(expected);\n    });\n\n    it('should define triggers with feed manifest params', () => {\n      const feedName = '/ns/package/feed';\n      const params = { feed: feedName, feed_parameters: { hello: 'world' } };\n      const expected = {\n        triggerName: 'myTrigger',\n        overwrite: true,\n        namespace: 'testing',\n        feed: {\n          feedName: 'package/feed',\n          namespace: 'ns',\n          trigger: '/testing/myTrigger',\n          params: params.feed_parameters,\n        },\n      };\n      const result = openwhiskCompileTriggers.compileTrigger('myTrigger', params);\n      return expect(result).to.deep.equal(expected);\n    });\n\n    it('should log triggers to console when verbose flag is set', () => {\n      openwhiskCompileTriggers.options.verbose = true\n      const log = sandbox.stub(openwhiskCompileTriggers.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskCompileTriggers.serverless.cli, 'consoleLog')\n      const feedName = '/ns/package/feed';\n      const params = { feed: feedName, feed_parameters: { hello: 'world' } };\n      const expected = {\n        triggerName: 'myTrigger',\n        overwrite: true,\n        namespace: 'testing',\n        feed: {\n          feedName: 'package/feed',\n          namespace: 'ns',\n          trigger: '/testing/myTrigger',\n          params: params.feed_parameters,\n        },\n      };\n      const result = openwhiskCompileTriggers.compileTrigger('myTrigger', params);\n      expect(log.calledOnce).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal(`Compiled Trigger (myTrigger): ${JSON.stringify(result)}`)\n    });\n  });\n});\n"
  },
  {
    "path": "configCredentials/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst path = require('path');\nconst fse = require('fs-extra');\n\nclass OpenWhiskConfigCredentials {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    // Note: we're not setting the provider here as this plugin should also be\n    // run when the CWD is not an AWS service\n\n    // this will be merged with the core config commands\n    this.commands = {\n      config: {\n        commands: {\n          credentials: {\n            lifecycleEvents: [\n              'config',\n            ],\n            options: {\n              apihost: {\n                usage: 'OpenWhisk platform API hostname.',\n                shortcut: 'h',\n                required: true,\n              },\n              auth: {\n                usage: 'User authentication credentials for the provider',\n                shortcut: 'a',\n                required: true,\n              }\n            },\n          },\n        },\n      },\n    };\n\n    this.hooks = {\n      'config:credentials:config': () => BbPromise.bind(this)\n        .then(this.configureCredentials),\n    };\n  }\n\n  configureCredentials() {\n    // sanitize\n    this.options.provider = this.options.provider.toLowerCase();\n    this.options.profile = this.options.profile ? this.options.profile : 'default';\n\n    // resolve if provider option is not 'aws'\n    if (this.options.provider !== 'openwhisk') {\n      return BbPromise.resolve();\n    }\n\n    // validate\n    if (!this.options.apihost || !this.options.auth) {\n      throw new this.serverless.classes.Error('Please include --auth and --apihost options for Apache OpenwWhisk.');\n    }\n\n    this.serverless.cli.log('Setting up Apache OpenWhisk...');\n    this.serverless.cli.log('Saving your credentials in \"~/.wskprops\"...');\n\n    // locate home directory on user's machine\n    const env = process.env;\n    const home = env.HOME ||\n      env.USERPROFILE ||\n      (env.HOMEPATH ? ((env.HOMEDRIVE || 'C:/') + env.HOMEPATH) : null);\n\n    if (!home) {\n      throw new this.serverless.classes\n        .Error('Can\\'t find home directory on your local file system.');\n    }\n\n    // check if ~/.wskprops exists\n    const credsPath = path.join(home, '.wskprops');\n\n    if (this.serverless.utils.fileExistsSync(credsPath)) {\n      // check if credentials files contains anything\n      const credsFile = this.serverless.utils.readFileSync(credsPath);\n\n      // if credentials file exists w/ auth, exit\n      if (credsFile.length && credsFile.indexOf(`AUTH`) > -1) {\n        this.serverless.cli.log(\n          `Failed! ~/.wskprops exists and already has credentials.`);\n        return BbPromise.resolve();\n      }\n    }\n\n    // write credentials file with 'default' profile\n    this.serverless.utils.writeFile(\n      credsPath,\n      `APIHOST=${this.options.apihost}\nAUTH=${this.options.auth}`); \n\n    this.serverless.cli.log(\n      'Success! Your Apache OpenWhisk credentials were stored in the configuration file (~/.wskprops).'\n    );\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskConfigCredentials;\n"
  },
  {
    "path": "configCredentials/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst BbPromise = require('bluebird');\nconst fs = require('fs');\nconst fse = require('fs-extra');\nconst os = require('os');\nconst path = require('path');\nconst crypto = require('crypto');\nconst OpenWhiskConfigCredentials = require('../index');\nconst OpenWhiskProvider = require('../../provider/openwhiskProvider');\n\nconst getTmpDirPath = () => path.join(os.tmpdir(),\n  'tmpdirs-serverless', 'serverless', crypto.randomBytes(8).toString('hex'));\n\nconst getTmpFilePath = (fileName) => path.join(getTmpDirPath(), fileName);\n\ndescribe('OpenWhiskConfigCredentials', () => {\n  let openwhiskConfigCredentials;\n\n  const serverless = {\n    cli: { log: () => {} },\n    utils: {}\n  };\n \n  beforeEach(() => {\n    const options = {\n      provider: 'openwhisk',\n      apihost: 'openwhisk.ng.bluemix.net',\n      auth: 'user:pass',\n    };\n    openwhiskConfigCredentials = new OpenWhiskConfigCredentials(serverless, options);\n  });\n\n  describe('#constructor()', () => {\n    it('should have the command \"config\"', () => {\n      expect(openwhiskConfigCredentials.commands.config).to.not.equal(undefined);\n    });\n\n    it('should have the sub-command \"credentials\"', () => {\n      expect(openwhiskConfigCredentials.commands.config.commands.credentials).to.not.equal(undefined);\n    });\n\n    it('should have no lifecycle event', () => {\n      expect(openwhiskConfigCredentials.commands.config.lifecycleEvents).to.equal(undefined);\n    });\n\n    it('should have the lifecycle event \"config\" for the \"credentials\" sub-command', () => {\n      expect(openwhiskConfigCredentials.commands.config.commands.credentials.lifecycleEvents)\n        .to.deep.equal(['config']);\n    });\n\n    it('should have the req. options \"apihost\" and \"auth\" for the \"credentials\" sub-command', () => {\n      // eslint-disable-next-line no-unused-expressions\n      expect(openwhiskConfigCredentials.commands.config.commands.credentials.options.apihost.required)\n        .to.be.true;\n      // eslint-disable-next-line no-unused-expressions\n      expect(openwhiskConfigCredentials.commands.config.commands.credentials.options.auth.required)\n        .to.be.true;\n    });\n\n    it('should have a \"config:credentials:config\" hook', () => {\n      expect(openwhiskConfigCredentials.hooks['config:credentials:config']).to.not.equal(undefined);\n    });\n\n    it('should run promise chain in order for \"config:credentials:config\" hook', () => {\n      const openwhiskConfigCredentialsStub = sinon\n        .stub(openwhiskConfigCredentials, 'configureCredentials').returns(BbPromise.resolve());\n\n      return openwhiskConfigCredentials.hooks['config:credentials:config']().then(() => {\n        expect(openwhiskConfigCredentialsStub.calledOnce).to.equal(true);\n\n        openwhiskConfigCredentials.configureCredentials.restore();\n      });\n    });\n  });\n\n  describe('#configureCredentials()', () => {\n    let homeDir;\n    let tmpDirPath;\n    let credentialsFilePath;\n\n    beforeEach(() => {\n      // create a new tmpDir for the homeDir path\n      tmpDirPath = getTmpDirPath();\n      fse.mkdirsSync(tmpDirPath);\n\n      // create the .openwhisk/credetials directory and file\n      credentialsFilePath = path.join(tmpDirPath, '.wskprops');\n      fse.ensureFileSync(credentialsFilePath);\n\n      // save the homeDir so that we can reset this later on\n      homeDir = os.homedir();\n      process.env.HOME = tmpDirPath;\n      process.env.HOMEPATH = tmpDirPath;\n      process.env.USERPROFILE = tmpDirPath;\n    });\n\n    it('should lowercase the provider option', () => {\n      openwhiskConfigCredentials.options.provider = 'SOMEPROVIDER';\n\n      return openwhiskConfigCredentials.configureCredentials().then(() => {\n        expect(openwhiskConfigCredentials.options.provider).to.equal('someprovider');\n      });\n    });\n\n    it('should resolve if the provider option is not \"openwhisk\"', (done) => {\n      openwhiskConfigCredentials.options.provider = 'invalid-provider';\n\n      openwhiskConfigCredentials.configureCredentials().then(() => done());\n    });\n\n    it('should throw an error if the \"apihost\" and \"auth\" options are not given', () => {\n      openwhiskConfigCredentials.options.apihost = false;\n      openwhiskConfigCredentials.options.auth = false;\n\n      expect(() => openwhiskConfigCredentials.configureCredentials()).to.throw(Error);\n    });\n\n    it('should reject if credentials file exists and has credentials', () => {\n      serverless.utils.fileExistsSync = () => true;\n      serverless.utils.readFileSync = () => \"AUTH\";\n\n      const lines = []\n      serverless.cli.log = log => lines.push(log)\n      return openwhiskConfigCredentials.configureCredentials().then(() => {\n        expect(lines[2]).to.equal('Failed! ~/.wskprops exists and already has credentials.');\n      });\n    });\n\n    it('should write to empty credentials file', () => {\n      openwhiskConfigCredentials.options.apihost = 'my-apihost';\n      openwhiskConfigCredentials.options.auth = 'my-auth';\n\n      let args \n      serverless.utils.fileExistsSync = () => true;\n      serverless.utils.readFileSync = () => \"\";\n      serverless.utils.writeFile = (path, content) => args = {path, content};\n\n      return openwhiskConfigCredentials.configureCredentials().then(() => {\n        expect(args.content).to.equal('APIHOST=my-apihost\\nAUTH=my-auth');\n      });\n    });\n\n    afterEach(() => {\n      // recover the homeDir\n      process.env.HOME = homeDir;\n      process.env.HOMEPATH = homeDir;\n      process.env.USERPROFILE = homeDir;\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/README.md",
    "content": "# Deploy\n\nThis plugin (re)deploys the service to OpenWhisk.\n\n## How it works\n\n`Deploy` starts by hooking into the\n[`deploy:initializeResources`](/lib/plugins/deploy) lifecycle.  It fetches the\nuser credentials for the OpenWhisk service being used, storing them under\n`serverless.service.defaults`. \n\n### User Credentials \n\nThe plugin attempts to parse configuration settings from the `.wskprops` file in the user's home directory,\nor from the file path specified in the `WSK_CONFIG_FILE` environment variable. These  settings can be set\nmanually using the following environment parameters.\n\n- **OW_AUTH** - Authentication key for OpenWhisk provider.\n- **OW_APIHOST** - API endpoint for OpenWhisk provider.\n- **OW_NAMESPACE** - User namespace for OpenWhisk resources.\n\nIf both the properties file and environment parameters are missing one of these\nvalues, an error will be thrown. \n\n**Note:** Other plugins (e.g. the `Compile Functions` plugin) use these\n`defaults` property when compiling resource definitions and using the OpenWhisk\nAPIs.\n\n### Deploying Functions, Triggers, Rules and Feeds\n\nNext up it hooks into the [`deploy:deploy`](/lib/plugins/deploy) lifecycle and deploys the\npreviously created resource definitions for Actions, Triggers, Feeds and Rules\nusing the OpenWhisk APIs.\n\nResources are deployed in the following order:\n\n- **Actions**\n- **Triggers**\n- **Feeds**\n- **Rules**\n\nFailure to deploy a single resource at any stage will cause the entire\ndeployment to halt with the error message from the failed deployment.\n"
  },
  {
    "path": "deploy/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst initializeResources = require('./lib/initializeResources');\nconst deployPackages = require('./lib/deployPackages');\nconst deployFunctions = require('./lib/deployFunctions');\nconst deployRules = require('./lib/deployRules');\nconst deployTriggers = require('./lib/deployTriggers');\nconst deployFeeds = require('./lib/deployFeeds');\nconst deployApiGw = require('./lib/deployApiGw');\nconst deployServiceBindings = require('./lib/deployServiceBindings');\n\nclass OpenWhiskDeploy {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    Object.assign(\n      this,\n      initializeResources,\n      deployPackages,\n      deployFunctions,\n      deployApiGw,\n      deployRules,\n      deployTriggers,\n      deployFeeds,\n      deployServiceBindings\n    );\n\n    this.hooks = {\n      'deploy:initializeResources': () => BbPromise.bind(this).then(this.initializeResources),\n\n      'deploy:deploy': () => BbPromise.bind(this)\n        .then(this.deployPackages)\n        .then(this.deployFunctions)\n        .then(this.deploySequences)\n        .then(this.deployRoutes)\n        .then(this.deployTriggers)\n        .then(this.deployFeeds)\n        .then(this.deployRules)\n        .then(this.configureServiceBindings)\n        .then(() => this.serverless.cli.log('Deployment successful!')),\n    };\n  }\n}\n\nmodule.exports = OpenWhiskDeploy;\n"
  },
  {
    "path": "deploy/lib/deployApiGw.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  deployRouteSwagger(route) {\n    return this.provider.client().then(ow => {\n      if (this.options.verbose) {\n        this.serverless.cli.log(`Deploying API Gateway Route: ${JSON.stringify(route)}`);\n      }\n      return ow.routes.create(route)\n        .then(() => {\n          if (this.options.verbose) {\n            this.serverless.cli.log(`Deployed API Gateway Route: ${JSON.stringify(route)}`);\n          }\n        }).catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to deploy API Gateway route due to error: ${err.message}`\n        );\n      })\n    });\n  },\n\n\n  replaceDefaultNamespace(swagger) {\n    return this.provider.client()\n      .then(ow => ow.actions.list())\n      .then(allActions => {\n\n        for(let path in swagger.paths) {\n          for(let verb in swagger.paths[path]) {\n            const operation = swagger.paths[path][verb] \n            if (operation['x-openwhisk'].namespace === '_') {\n              const swaggerAction = operation['x-openwhisk']\n\n              const action = allActions.find(item => item.name === swaggerAction.action)\n              swaggerAction.namespace = action.namespace\n              swaggerAction.url = swaggerAction.url.replace(/web\\/_/, `web/${action.namespace}`)\n\n              const id = operation.operationId\n              const stmts = swagger[\"x-ibm-configuration\"].assembly.execute[0]['operation-switch'].case\n              const stmt = stmts.find(stmt => stmt.operations[0] === id)\n              const invoke = stmt.execute[stmt.execute.length -1].invoke\n              invoke['target-url'] = invoke['target-url'].replace(/web\\/_/, `web/${action.namespace}`)\n            }\n          }\n        }\n        return swagger\n      })\n  },\n\n  deployRoutes() {\n    const apigw = this.serverless.service.apigw;\n\n    if (!apigw.swagger) {\n      return BbPromise.resolve();\n    }\n\n    this.serverless.cli.log('Deploying API Gateway definitions...');\n    return this.replaceDefaultNamespace(apigw.swagger)\n      .then(swagger => this.deployRouteSwagger({ swagger }))\n  }\n};\n"
  },
  {
    "path": "deploy/lib/deployFeeds.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n\n  deleteFeed(feed) {\n    return new Promise((resolve, reject) => {\n      this.provider.client().then(ow =>\n          ow.feeds.delete(feed).then(() => resolve(feed)).catch(() => resolve(feed))\n      );\n    })\n  },\n\n  deployFeed(feed) {\n    return this.provider.client().then(ow => {\n      if (this.options.verbose) {\n        this.serverless.cli.log(`Deploying Feed: ${feed.feedName}`);\n      }\n      return ow.feeds.create(feed)\n        .then(() => {\n          if (this.options.verbose) {\n            this.serverless.cli.log(`Deployed Feed: ${feed.feedName}`);\n          }\n        }).catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to deploy feed (${feed.feedName}) due to error: ${err.message}`\n        );\n      })\n    });\n  },\n\n  deployFeeds() {\n    const feeds = this.getFeeds()\n\n    if (feeds.length) {\n      this.serverless.cli.log('Binding Feeds To Triggers...');\n    }\n\n    const deleteAndDeployFeeds = feeds.map(feed => {\n      return this.deleteFeed(feed).then(() => this.deployFeed(feed))\n    })\n    return BbPromise.all(deleteAndDeployFeeds)\n  },\n\n  getFeeds() {\n    const triggers = this.serverless.service.triggers;\n    return Object.keys(triggers).map(t => triggers[t].feed).filter(f => f);\n  }\n};\n"
  },
  {
    "path": "deploy/lib/deployFunctions.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  deployFunctionHandler(functionHandler) {\n    return this.provider.client().then(ow => {\n      if (this.options.verbose) {\n        this.serverless.cli.log(`Deploying Function: ${functionHandler.actionName}`);\n      }\n      return ow.actions.create(functionHandler)\n        .then(() => {\n          if (this.options.verbose) {\n            this.serverless.cli.log(`Deployed Function: ${functionHandler.actionName}`);\n          }\n        })\n        .catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to deploy function (${functionHandler.actionName}) due to error: ${err.message}`\n        );\n      })}\n    );\n  },\n\n  deploySequences() {\n    const sequences = this.filterActions(true)\n\n    if (sequences.length) {\n      this.serverless.cli.log('Deploying Sequences...');\n    }\n\n    return this.deployActions(sequences);\n  },\n\n  deployFunctions() {\n    this.serverless.cli.log('Deploying Functions...');\n    return this.deployActions(this.filterActions())\n  },\n\n  deployActions(names) {\n    const actions = this.serverless.service.actions;\n    return BbPromise.all(\n      names.map(a => this.deployFunctionHandler(actions[a]))\n    );\n  },\n\n  filterActions(sequence) {\n    const actions = this.serverless.service.actions;\n    const kind = action => action.action.exec.kind\n    const match = action => (kind(action) === 'sequence') === !!sequence\n    return Object.keys(actions).filter(a => match(actions[a]))\n  },\n};\n"
  },
  {
    "path": "deploy/lib/deployPackages.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  deployPackage(pkge) {\n    return this.provider.client().then(ow => {\n      if (this.options.verbose) {\n        this.serverless.cli.log(`Deploying Package: ${pkge.name}`);\n      }\n      return ow.packages.create(pkge)\n       .then(() => {\n         if (this.options.verbose) {\n           this.serverless.cli.log(`Deployed Package: ${pkge.name}`);\n         }\n       }).catch(err => {\n         throw new this.serverless.classes.Error(\n          `Failed to deploy package (${pkge.name}) due to error: ${err.message}`\n        );\n       });\n    });\n  },\n\n  deployPackages() {\n    const pkges = this.getPackages();\n\n    if (pkges.length) {\n      this.serverless.cli.log('Deploying Packages...');\n    }\n\n    return BbPromise.all(\n      pkges.map(p => this.deployPackage(p))\n    );\n  },\n\n  getPackages() {\n    const pkges = this.serverless.service.packages;\n    return Object.keys(this.serverless.service.packages).map(p => pkges[p]);\n  }\n};\n"
  },
  {
    "path": "deploy/lib/deployRules.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  deployRule(rule) {\n    return this.provider.client().then(ow => {\n      if (this.options.verbose) {\n        this.serverless.cli.log(`Deploying Rule: ${rule.ruleName}`);\n      }\n      return ow.rules.create(rule)\n       .then(() => {\n          if (this.options.verbose) {\n            this.serverless.cli.log(`Deployed Rule: ${rule.ruleName}`);\n          }\n        }).catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to deploy rule (${rule.ruleName}) due to error: ${err.message}`\n        );\n      })\n    });\n  },\n\n  enableRule(rule) {\n    return this.provider.client().then(ow =>\n      ow.rules.enable(rule).catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to enable rule (${rule.ruleName}) due to error: ${err.message}`\n        );\n      })\n    );\n  },\n\n  deployRules() {\n    const rules = this.getRules();\n    if (rules.length) {\n      this.serverless.cli.log('Deploying Rules...');\n    }\n    return BbPromise.all(rules.map(r => this.deployRule(r).then(() => this.enableRule(r))));\n  },\n\n  getRules() {\n    const rules = this.serverless.service.rules;\n    return Object.keys(this.serverless.service.rules).map(r => rules[r]);\n  }\n};\n"
  },
  {
    "path": "deploy/lib/deployServiceBindings.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst { spawn } = require('child_process');\n\nmodule.exports = {\n  configureServiceBinding(binding) {\n    if (this.options.verbose) {\n      this.serverless.cli.log(`Configuring Service Binding: ${JSON.stringify(binding)}`);\n    }\n\n    return new Promise((resolve, reject) => {\n      const args = ['wsk', 'service', 'bind', binding.name, binding.action]\n\n      if (binding.instance) {\n        args.push(\"--instance\", binding.instance)\n      }\n\n      if (binding.key) {\n        args.push(\"--keyname\", binding.key)\n      }\n\n      const bx = spawn('bx', args);\n\n      const stdout = []\n      const stderr = []\n\n      bx.stdout.on('data', data => {\n        stdout.push(data.toString())\n      });\n\n      bx.stderr.on('data', (data) => {\n        stderr.push(data.toString())\n      });\n\n      bx.on('error', (err) => {\n        if (err.code === 'ENOENT') {\n          const err = new this.serverless.classes.Error(\n            'Unable to execute `bx wsk service bind` command. Is IBM Cloud CLI installed?'\n          )\n          return reject(err)\n        }\n        reject(err.message)\n      });\n\n      bx.on('close', (code) => {\n        if (code === 2) {\n          const err = new this.serverless.classes.Error(\n            'Unable to execute `bx wsk service bind` command. Is IBM Cloud Functions CLI plugin installed?'\n          )\n          return reject(err)\n        }\n        if (code > 0) {\n          const errmsg = (stderr[0] || '').split('\\n')[0]\n          const err = new this.serverless.classes.Error(`Failed to configure service binding (${JSON.stringify(binding)})\\n  ${errmsg}`);\n          return reject(err)\n        }\n        if (this.options.verbose) {\n          this.serverless.cli.log(`Configured Service Binding: ${JSON.stringify(binding)}`);\n        }\n        resolve()\n      });\n    });\n  },\n\n  configureServiceBindings() {\n    const bindings = this.getServiceBindings();\n\n    if (bindings.fns.length || bindings.packages.length) {\n      this.serverless.cli.log('Configuring Service Bindings...');\n    }\n\n    return BbPromise.all(\n      bindings.packages.map(sbs => BbPromise.mapSeries(sbs, sb => this.configureServiceBinding(sb)))\n    ).then(() => BbPromise.all(\n      bindings.fns.map(sbs => BbPromise.mapSeries(sbs, sb => this.configureServiceBinding(sb)))\n    ));\n  },\n\n  getServiceBindings() {\n    return this.serverless.service.bindings || { fns: [], packages: [] } ;\n  }\n};\n"
  },
  {
    "path": "deploy/lib/deployTriggers.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  deployTrigger(trigger) {\n    return this.provider.client().then(ow => {\n      if (this.options.verbose) {\n        this.serverless.cli.log(`Deploying Trigger: ${trigger.triggerName}`);\n      }\n\n      return ow.triggers.create(trigger)\n        .then(() => {\n          if (this.options.verbose) {\n            this.serverless.cli.log(`Deployed Trigger: ${trigger.triggerName}`);\n          }\n        }).catch(err => {\n          throw new this.serverless.classes.Error(\n            `Failed to deploy trigger (${trigger.triggerName}) due to error: ${err.message}`\n          );\n        });\n    });\n  },\n\n  deployTriggers() {\n    const triggers = this.getTriggers(this.serverless.service.triggers);\n\n    if(triggers.length) {\n      this.serverless.cli.log('Deploying Triggers...');\n    }\n\n    return BbPromise.all(\n      triggers.map(t => this.deployTrigger(t))\n    );\n  },\n\n  getTriggers(triggers) {\n    const feedMask = { feed: undefined };\n    return Object.keys(triggers)\n      .map(t => {\n        const trigger = triggers[t];\n        if (trigger.feed) {\n          Object.assign(trigger, {\n            trigger: {\n              annotations: [{\n                key: 'feed',\n                value: `/${trigger.feed.namespace}/${trigger.feed.feedName}`,\n              }],\n            },\n          });\n        }\n        return Object.assign(trigger, feedMask);\n      });\n  },\n};\n"
  },
  {
    "path": "deploy/lib/initializeResources.js",
    "content": "'use strict';\n\n// This class ensures that all the required authentication credentials\n// are available, either from the user's .wskprops file or environment\n// parameters.\n\nmodule.exports = {\n  initializeResources() {\n    this.serverless.cli.log('Initialising Resources...');\n    const ParamNames = ['auth', 'apihost', 'namespace'];\n    const Defaults = this.serverless.service.provider;\n\n    return this.provider.props()\n      .then(props => {\n        Object.assign(Defaults, props);\n        ParamNames.forEach(key => {\n          if (!Defaults[key]) {\n            const envName = `OW_${key.toUpperCase()}`;\n            throw new this.serverless.classes.Error(\n              `OpenWhisk required configuration parameter ${envName} missing or blank. ` +\n              'Must be present in .wskprops as environment variable.'\n            );\n          }\n        });\n      });\n  },\n};\n"
  },
  {
    "path": "deploy/lib/validate.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  validate () {\n    if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service');\n    }\n\n    this.options.stage = this.options.stage\n      || (this.serverless.service.provider && this.serverless.service.provider.stage)\n      || 'dev';\n    this.options.region = this.options.region\n      || (this.serverless.service.provider && this.serverless.service.provider.region)\n      || 'us-east-1';\n\n    return BbPromise.resolve();\n  }\n};\n"
  },
  {
    "path": "deploy/tests/all.js",
    "content": "'use strict';\n\nrequire('./validate');\nrequire('./initializeResources');\nrequire('./deployFunctions');\nrequire('./deployRules');\nrequire('./deployPackages');\nrequire('./deployTriggers');\nrequire('./deployFeeds');\nrequire('./deployApiGw');\nrequire('./deployServiceBindings');\nrequire('./index');\n"
  },
  {
    "path": "deploy/tests/deployApiGw.js",
    "content": "'use strict';\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\nconst fs = require('fs');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployHttpEvents', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.service = 'my-service'\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} }\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#replaceDefaultNamespace()', async () => {\n    it('should return same swagger doc without default namespace', async () => {\n      const source = fs.readFileSync('./deploy/tests/resources/swagger.json', 'utf-8')\n      const swagger = JSON.parse(source)\n\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const list = params => {\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ actions: { list } });\n      });\n\n      let result = await openwhiskDeploy.replaceDefaultNamespace(swagger)\n      expect(result).to.be.deep.equal(swagger)\n    })\n\n    it('should replace default namespace in swagger doc', async () => {\n      const without_default_ns = fs.readFileSync('./deploy/tests/resources/swagger.json', 'utf-8')\n      const with_default_ns = fs.readFileSync('./deploy/tests/resources/swagger_default_ns.json', 'utf-8')\n      const source = JSON.parse(with_default_ns)\n      const converted = JSON.parse(without_default_ns)\n\n      const actions = [{\"name\":\"hello\",\"namespace\":\"user@host.com_dev\"}]\n\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const list = params => {\n          return Promise.resolve(actions);\n        };\n\n        return Promise.resolve({ actions: { list } });\n      });\n\n      let result = await openwhiskDeploy.replaceDefaultNamespace(source)\n      expect(result).to.be.deep.equal(converted)\n    })\n\n    it('should return same swagger doc including path params', async () => {\n      const without_default_ns = fs.readFileSync('./deploy/tests/resources/swagger_ns_paths.json', 'utf-8')\n      const with_default_ns = fs.readFileSync('./deploy/tests/resources/swagger_paths.json', 'utf-8')\n      const source = JSON.parse(with_default_ns)\n      const converted = JSON.parse(without_default_ns)\n\n      const actions = [{\"name\":\"hello\",\"namespace\":\"user@host.com_dev\"}]\n\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const list = params => {\n          return Promise.resolve(actions);\n        };\n\n        return Promise.resolve({ actions: { list } });\n      });\n\n      let result = await openwhiskDeploy.replaceDefaultNamespace(source)\n      expect(result).to.be.deep.equal(converted)\n\n  })\n  })\n\n  /**\n  describe('#configRouteSwagger()', () => {\n    it('should update swagger with CORS config parameter', () => {\n      const source = fs.readFileSync('./deploy/tests/resources/swagger.json', 'utf-8')\n      const swagger = JSON.parse(source)\n      const options = { cors: false }\n      let result = openwhiskDeploy.configRouteSwagger(swagger, options)\n      expect(result['x-ibm-configuration'].cors.enabled).to.be.equal(false)\n\n      swagger['x-ibm-configuration'].cors.enabled = false\n      options.cors = true\n      result = openwhiskDeploy.configRouteSwagger(swagger, options)\n      expect(result['x-ibm-configuration'].cors.enabled).to.be.equal(true)\n    })\n\n    it('should maintain existing swagger config parameters', () => {\n      const source = fs.readFileSync('./deploy/tests/resources/swagger.json', 'utf-8')\n      const swagger = JSON.parse(source)\n      swagger['x-ibm-configuration'].test = 'value'\n      const options = { cors: false }\n      let result = openwhiskDeploy.configRouteSwagger(swagger, options)\n      expect(result['x-ibm-configuration'].test).to.be.equal('value')\n    })\n\n    it('should leave swagger the same without config parameters', () => {\n      const source = fs.readFileSync('./deploy/tests/resources/swagger.json', 'utf-8')\n      const swagger = JSON.parse(source)\n      const options = { }\n      const result = openwhiskDeploy.configRouteSwagger(swagger, options)\n      expect(result).to.be.deep.equal(swagger)\n    })\n  })\n  */\n\n/**\n  describe('#updateRouteConfig()', () => {\n    it('should retrieve and deploy updated api gw route swagger to openwhisk', () => {\n      const source = fs.readFileSync('./deploy/tests/resources/swagger.json', 'utf-8')\n      const swagger = JSON.parse(source)\n\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const get = params => {\n          expect(params).to.be.deep.equal({basepath: '/my-service'});\n          return Promise.resolve({ apis: [{value: {apidoc:swagger}}]});\n        };\n\n        const create = params => {\n          expect(params.swagger).to.be.deep.equal(swagger);\n          return Promise.resolve({});\n        };\n\n        return Promise.resolve({ routes: { get: get, create: create } });\n      });\n      return expect(openwhiskDeploy.updateRoutesConfig('/my-service', {random: false}))\n        .to.eventually.be.fulfilled;\n    });\n  });\n  */\n \n  describe('#deployRouteSwagger()', () => {\n    it('should deploy api gw route handler to openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal({foo: 'bar'});\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ routes: { create } });\n      });\n      return expect(openwhiskDeploy.deployRouteSwagger({foo: 'bar'}))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ routes: { create } });\n      });\n      return expect(openwhiskDeploy.deployRouteSwagger({relpath: '/foo/bar'}))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${err.message}`)\n        );\n    });\n\n    it('should log function deploy information with verbose flag', () => {\n      openwhiskDeploy.options.verbose = true\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskDeploy.serverless.cli, 'consoleLog')\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ routes: { create } });\n      });\n\n      return openwhiskDeploy.deployRouteSwagger({foo: 'bar'}).then(() => {\n      expect(log.calledTwice).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal('Deploying API Gateway Route: ' + JSON.stringify({foo: 'bar'}))\n      expect(log.args[1][0]).to.be.equal('Deployed API Gateway Route: ' + JSON.stringify({foo: 'bar'}))\n      })\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/tests/deployFeeds.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployFeeds', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  const mockFeedObject = {\n    feeds: {\n      myFeed: {\n        feedName: 'myFeed',\n      },\n    },\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} }\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#deployFeeds()', () => {\n    it('should call deleteFeed & deployFeed for each registered trigger feed', () => {\n      const deployStub = sandbox.stub(openwhiskDeploy, 'deployFeed', () => Promise.resolve());\n      const deleteStub = sandbox.stub(openwhiskDeploy, 'deleteFeed', () => Promise.resolve());\n\n      const triggerWithFeed = { feed: { feedName: 'blah' } };\n      openwhiskDeploy.serverless.service.triggers\n        = { myTrigger: triggerWithFeed, anotherTrigger: {}, finalTrigger: triggerWithFeed };\n\n      return openwhiskDeploy.deployFeeds().then(() => {\n        expect(deployStub.calledTwice).to.be.equal(true);\n        expect(deployStub.calledWith(triggerWithFeed.feed)).to.be.equal(true);\n        expect(deleteStub.calledTwice).to.be.equal(true);\n        expect(deleteStub.calledWith(triggerWithFeed.feed)).to.be.equal(true);\n      });\n    });\n    it('should not log anything for empty feeds', () => {\n      openwhiskDeploy.serverless.service.triggers = {};\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log');\n      return openwhiskDeploy.deployFeeds().then(() => {\n        console.log(log.called)\n        expect(log.called).to.be.equal(false);\n      });\n    })\n  });\n\n  describe('#deployFeed()', () => {\n    it('should deploy feed to openwhisk', () => {\n      const stub = sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal(mockFeedObject.feeds.myFeed);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ feeds: { create } });\n      });\n      return openwhiskDeploy.deployFeed(mockFeedObject.feeds.myFeed).then(() => {\n        expect(stub.called).to.be.true\n      })\n    });\n\n    it('should log function deploy information with verbose flag', () => {\n      openwhiskDeploy.options.verbose = true\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskDeploy.serverless.cli, 'consoleLog')\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ feeds: { create } });\n      });\n\n      return openwhiskDeploy.deployFeed(mockFeedObject.feeds.myFeed).then(() => {\n      expect(log.calledTwice).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal('Deploying Feed: myFeed')\n      expect(log.args[1][0]).to.be.equal('Deployed Feed: myFeed')\n      })\n    });\n\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ feeds: { create } });\n      });\n      return expect(openwhiskDeploy.deployFeed(mockFeedObject.feeds.myFeed))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${mockFeedObject.feeds.myFeed.feedName}.*${err.message}`)\n        );\n    });\n  });\n\n  describe('#deleteFeed()', () => {\n    it('should delete feed from openwhisk', () => {\n      const stub = sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const _delete = params => {\n          expect(params).to.be.deep.equal(mockFeedObject.feeds.myFeed);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ feeds: { \"delete\": _delete } });\n      });\n\n      return openwhiskDeploy.deleteFeed(mockFeedObject.feeds.myFeed).then(() => {\n        expect(stub.called).to.be.true\n      })\n    });\n\n    it('should handle errors from non-existant feeds', () => {\n      const err = { message: 'some reason' };\n      const stub = sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const _delete = () => Promise.reject(err);\n        return Promise.resolve({ feeds: { \"delete\": _delete } });\n      });\n\n      return openwhiskDeploy.deleteFeed(mockFeedObject.feeds.myFeed).then(() => {\n        expect(stub.called).to.be.true\n      })\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/tests/deployFunctions.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployFunctions', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  const fileContents =\n    `function main() {\n      return {payload: 'Hello world'};\n    }`;\n\n  const mockFunctionObject = {\n    actionName: 'serviceName_functionName',\n    namespace: 'namespace',\n    action: {\n      exec: { kind: 'nodejs:default', code: fileContents },\n      limits: { timeout: 60 * 1000, memory: 256 },\n      parameters: [{ key: 'foo', value: 'bar' }],\n    },\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} }\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#deployFunctionHandler()', () => {\n    it('should deploy function handler to openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal({\n            actionName: mockFunctionObject.actionName,\n            namespace: mockFunctionObject.namespace,\n            action: mockFunctionObject.action,\n          });\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ actions: { create } });\n      });\n      return expect(openwhiskDeploy.deployFunctionHandler(mockFunctionObject))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should log function deploy information with verbose flag', () => {\n      openwhiskDeploy.options.verbose = true\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskDeploy.serverless.cli, 'consoleLog')\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ actions: { create } });\n      });\n\n      return openwhiskDeploy.deployFunctionHandler(mockFunctionObject).then(() => {\n      expect(log.calledTwice).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal('Deploying Function: serviceName_functionName')\n      expect(log.args[1][0]).to.be.equal('Deployed Function: serviceName_functionName')\n      })\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ actions: { create } });\n      });\n      return expect(openwhiskDeploy.deployFunctionHandler(mockFunctionObject))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${mockFunctionObject.actionName}.*${err.message}`)\n        );\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/tests/deployPackages.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployPackages', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  const mockPackageObject = {\n    packages: {\n      myPackage: {\n        name: 'myPackage',\n        namespace: 'myNamespace'\n      },\n    },\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} }\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#deployPackage()', () => {\n    it('should deploy package to openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal(mockPackageObject.packages.myPackage);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ packages: { create } });\n      });\n      return expect(openwhiskDeploy.deployPackage(mockPackageObject.packages.myPackage))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ packages: { create } });\n      });\n      return expect(openwhiskDeploy.deployPackage(mockPackageObject.packages.myPackage))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${mockPackageObject.packages.myPackage.name}.*${err.message}`)\n        );\n    });\n\n    it('should log package deploy information with verbose flag', () => {\n      openwhiskDeploy.options.verbose = true\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskDeploy.serverless.cli, 'consoleLog')\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ packages: { create } });\n      });\n\n      return openwhiskDeploy.deployPackage(mockPackageObject.packages.myPackage).then(() => {\n      expect(log.calledTwice).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal('Deploying Package: myPackage')\n      expect(log.args[1][0]).to.be.equal('Deployed Package: myPackage')\n      })\n    })\n  });\n});\n"
  },
  {
    "path": "deploy/tests/deployRules.js",
    "content": "'use strict';\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployRules', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  const mockRuleObject = {\n    rules: {\n      myRule: {\n        ruleName: 'myRule',\n        namepspace: 'myNamespace',\n        action: 'myAction',\n        trigger: 'myTrigger',\n      },\n    },\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} }\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#deployRule()', () => {\n    it('should deploy function handler to openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal(mockRuleObject.rules.myRule);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ rules: { create } });\n      });\n      return expect(openwhiskDeploy.deployRule(mockRuleObject.rules.myRule))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ rules: { create } });\n      });\n      return expect(openwhiskDeploy.deployRule(mockRuleObject.rules.myRule))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${mockRuleObject.rules.myRule.ruleName}.*${err.message}`)\n        );\n    });\n\n    it('should log function deploy information with verbose flag', () => {\n      openwhiskDeploy.options.verbose = true\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log')\n      const clog = sandbox.stub(openwhiskDeploy.serverless.cli, 'consoleLog')\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ rules: { create } });\n      });\n\n      return openwhiskDeploy.deployRule(mockRuleObject.rules.myRule).then(() => {\n      expect(log.calledTwice).to.be.equal(true);\n      expect(log.args[0][0]).to.be.equal('Deploying Rule: myRule')\n      expect(log.args[1][0]).to.be.equal('Deployed Rule: myRule')\n      })\n    });\n  });\n\n  describe('#enableRule()', () => {\n    it('should call enable rule on openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const enable = params => {\n          expect(params).to.be.deep.equal(mockRuleObject.rules.myRule);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ rules: { enable } });\n      });\n      return expect(openwhiskDeploy.enableRule(mockRuleObject.rules.myRule))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should reject when enable rule fails with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const enable = () => Promise.reject(err);\n\n        return Promise.resolve({ rules: { enable } });\n      });\n      return expect(openwhiskDeploy.enableRule(mockRuleObject.rules.myRule))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${mockRuleObject.rules.myRule.ruleName}.*${err.message}`)\n        );\n    });\n\n  });\n\n  describe('#deployRules()', () => {\n    it('should call deployRule & enableRule for each rule', () => {\n      const deployStub = sandbox.stub(openwhiskDeploy, 'deployRule', () => Promise.resolve());\n      const enableStub = sandbox.stub(openwhiskDeploy, 'enableRule', () => Promise.resolve());\n\n      openwhiskDeploy.serverless.service.rules\n        = { hello: {}, foo: {} };\n\n      return openwhiskDeploy.deployRules().then(() => {\n        expect(deployStub.calledTwice).to.be.equal(true);\n        expect(deployStub.calledWith({})).to.be.equal(true);\n        expect(enableStub.calledTwice).to.be.equal(true);\n        expect(enableStub.calledWith({})).to.be.equal(true);\n      });\n    });\n    it('should not log anything for empty feeds', () => {\n      openwhiskDeploy.serverless.service.rules = {};\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log');\n      return openwhiskDeploy.deployRules().then(() => {\n        console.log(log.called)\n        expect(log.called).to.be.equal(false);\n      });\n    })\n  });\n});\n"
  },
  {
    "path": "deploy/tests/deployServiceBindings.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployServiceBindings', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  const mockPackageObject = {\n    packages: {\n      myPackage: {\n        name: 'myPackage',\n        namespace: 'myNamespace'\n      },\n    },\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} }\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#configureServiceBindings()', () => {\n    it('should call binding command for each binding and return when all finish', () => {\n      const bindings = [[{name: 'a'}, {name: 'a'}, {name: 'a'}]]\n      openwhiskDeploy.serverless.service.bindings = { fns: bindings, packages: bindings }\n      sandbox.stub(openwhiskDeploy, 'configureServiceBinding', () => {\n        return Promise.resolve();\n      });\n      return openwhiskDeploy.configureServiceBindings().then(results => {\n        expect(results.length).to.equal(bindings.length)\n      })\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const bindings = [[{name: 'a'}, {name: 'a'}, {name: 'a'}]]\n      openwhiskDeploy.serverless.service.bindings = { fns: bindings, packages: bindings }\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy, 'configureServiceBinding', () => {\n        return Promise.reject(err);\n      });\n\n      return expect(openwhiskDeploy.configureServiceBindings())\n        .to.eventually.be.rejectedWith(new RegExp(`${err.message}`));\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/tests/deployTriggers.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('deployTriggers', () => {\n  let serverless;\n  let openwhiskDeploy;\n  let sandbox;\n\n  const mockTriggerObject = {\n    triggers: {\n      myTrigger: {\n        triggerName: 'myTrigger',\n        namespace: 'myNamespace',\n      },\n      feedTrigger: {\n        triggerName: 'feedTrigger',\n        namespace: 'feedNamespace',\n        trigger: {\n          annotations: [\n            {\n              key: 'feed',\n              value: '/whisk.system/alarms/alarm'\n            }\n          ]\n        },\n      },\n    },\n    serviceTriggers: {\n      myTrigger: {\n        triggerName: 'myTrigger',\n        namespace: 'myNamespace',\n      },\n      feedTrigger: {\n        triggerName: 'feedTrigger',\n        namespace: 'feedNamespace',\n        feed: {\n          trigger : '/feedNamespace/feedTrigger',\n          feedName: 'alarms/alarm',\n          namespace: 'whisk.system',\n          params: {\n            cron: '* * * * *',\n            trigger_payload: {}\n          }\n        }\n      },\n    },\n    owTriggers: [\n      {\n        triggerName: 'myTrigger',\n        namespace: 'myNamespace',\n        feed: undefined\n      },\n      {\n        triggerName: 'feedTrigger',\n        namespace: 'feedNamespace',\n        trigger: {\n          annotations: [\n            {\n              key: 'feed',\n              value: '/whisk.system/alarms/alarm'\n            }\n          ],\n        },\n        feed: undefined\n      },\n    ],\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {}; };\n    serverless = { classes: { Error, CLI }, service: { provider: {}, resources: {}, getAllFunctions: () => [] }, getProvider: sandbox.spy() };\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.serverless.cli = { consoleLog: () => {}, log: () => {} };\n    openwhiskDeploy.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeploy.provider = { client: () => {} };\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#deployTrigger()', () => {\n    it('should deploy trigger to openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal(mockTriggerObject.triggers.myTrigger);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ triggers: { create } });\n      });\n      return expect(openwhiskDeploy.deployTrigger(mockTriggerObject.triggers.myTrigger))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ triggers: { create } });\n      });\n      return expect(openwhiskDeploy.deployTrigger(mockTriggerObject.triggers.myTrigger))\n        .to.eventually.be.rejectedWith(\n          new RegExp(`${mockTriggerObject.triggers.myTrigger.triggerName}.*${err.message}`)\n        );\n    });\n\n    it('should log function deploy information with verbose flag', () => {\n      openwhiskDeploy.options.verbose = true;\n      const log = sandbox.stub(openwhiskDeploy.serverless.cli, 'log');\n      const clog = sandbox.stub(openwhiskDeploy.serverless.cli, 'consoleLog');\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => Promise.resolve();\n\n        return Promise.resolve({ triggers: { create } });\n      });\n\n      return openwhiskDeploy.deployTrigger(mockTriggerObject.triggers.myTrigger).then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0]).to.be.equal('Deploying Trigger: myTrigger');\n        expect(log.args[1][0]).to.be.equal('Deployed Trigger: myTrigger');\n      });\n    });\n\n    it('should deploy trigger with feed annotation to openwhisk', () => {\n      sandbox.stub(openwhiskDeploy.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal(mockTriggerObject.triggers.feedTrigger);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ triggers: { create } });\n      });\n      return expect(openwhiskDeploy.deployTrigger(mockTriggerObject.triggers.feedTrigger))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should change the trigger format to match the ow.', () => {\n      expect(openwhiskDeploy.getTriggers(mockTriggerObject.serviceTriggers))\n        .to.be.deep.equal(mockTriggerObject.owTriggers)\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/tests/index.js",
    "content": "'use strict';\n\nconst OpenWhiskDeploy = require('../index');\nconst expect = require('chai').expect;\nconst BbPromise = require('bluebird');\nconst sinon = require('sinon');\n\ndescribe('OpenWhiskDeploy', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n  };\n  const openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n  openwhiskDeploy.serverless.cli = new serverless.classes.CLI();\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskDeploy.hooks).to.be.not.empty);\n\n    it('should run \"deploy:initializeResources\" hook promise chain in order', () => {\n      const initializeResourcesStub = sinon\n        .stub(openwhiskDeploy, 'initializeResources').returns(BbPromise.resolve());\n\n      return openwhiskDeploy.hooks['deploy:initializeResources']().then(() => {\n        expect(initializeResourcesStub.calledOnce).to.be.equal(true);\n        openwhiskDeploy.initializeResources.restore();\n      });\n    });\n\n    it('should run \"deploy:deploy\" promise chain in order', () => {\n      const deployPackagesStub = sinon\n        .stub(openwhiskDeploy, 'deployPackages').returns(BbPromise.resolve());\n      const deployFunctionsStub = sinon\n        .stub(openwhiskDeploy, 'deployFunctions').returns(BbPromise.resolve());\n      const deploySequencesStub = sinon\n        .stub(openwhiskDeploy, 'deploySequences').returns(BbPromise.resolve());\n      const deployRulesStub = sinon\n        .stub(openwhiskDeploy, 'deployRules').returns(BbPromise.resolve());\n      const deployTriggersStub = sinon\n        .stub(openwhiskDeploy, 'deployTriggers').returns(BbPromise.resolve());\n      const deployFeedsStub = sinon\n        .stub(openwhiskDeploy, 'deployFeeds').returns(BbPromise.resolve());\n      const deployRoutesStub = sinon\n        .stub(openwhiskDeploy, 'deployRoutes').returns(BbPromise.resolve());\n\n      return openwhiskDeploy.hooks['deploy:deploy']().then(() => {\n        expect(deployPackagesStub.calledOnce).to.be.equal(true);\n        expect(deployFunctionsStub.calledOnce).to.be.equal(true);\n        expect(deploySequencesStub.calledOnce).to.be.equal(true);\n        expect(deployRoutesStub.calledOnce).to.be.equal(true);\n        expect(deployRulesStub.calledOnce).to.be.equal(true);\n        expect(deployTriggersStub.calledOnce).to.be.equal(true);\n        expect(deployFeedsStub.calledOnce).to.be.equal(true);\n\n        openwhiskDeploy.deployFunctions.restore();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "deploy/tests/initializeResources.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst chaiAsPromised = require('chai-as-promised');\nconst OpenWhiskDeploy = require('../index');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('#initializeResources()', () => {\n  let serverless;\n  let sandbox;\n  let openwhiskDeploy;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeploy = new OpenWhiskDeploy(serverless, options);\n    openwhiskDeploy.provider = {props: () => {}};\n\n    serverless.cli = { log: () => {} };\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  it('should instantiate openwhisk resources from openwhisk authentication properties', () => {\n    const mockObject = {\n      apihost: 'blah.blah.com', auth: 'another_user:another_pass', namespace: 'user@user.com',\n    };\n\n    sandbox.stub(openwhiskDeploy.provider, 'props', () => Promise.resolve(mockObject));\n    return openwhiskDeploy.initializeResources().then(() => {\n      expect(openwhiskDeploy.serverless.service.provider).to.deep.equal(mockObject);\n    });\n  });\n\n  it('should throw error when parameter (OW_AUTH) is missing', () => {\n    const mockObject = {\n      apihost: 'blah.blah.com', namespace: 'user@user.com',\n    };\n\n    sandbox.stub(openwhiskDeploy.provider, 'props', () => Promise.resolve(mockObject));\n    return expect(openwhiskDeploy.initializeResources()).to.be.rejectedWith(/OW_AUTH/);\n  });\n\n  it('should throw error when parameter (OW_APIHOST) is missing', () => {\n    const mockObject = {\n      auth: 'user:pass', namespace: 'user@user.com',\n    };\n\n    sandbox.stub(openwhiskDeploy.provider, 'props', () => Promise.resolve(mockObject));\n    return expect(openwhiskDeploy.initializeResources()).to.be.rejectedWith(/OW_APIHOST/);\n  });\n\n  it('should throw error when parameter (OW_NAMESPACE) is missing', () => {\n    const mockObject = {\n      auth: 'user:pass', apihost: 'blah.blah.com',\n    };\n\n    sandbox.stub(openwhiskDeploy.provider, 'props', () => Promise.resolve(mockObject));\n    return expect(openwhiskDeploy.initializeResources()).to.be.rejectedWith(/OW_NAMESPACE/);\n  });\n});\n"
  },
  {
    "path": "deploy/tests/resources/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"basePath\": \"/testing\",\n    \"info\": {\n        \"title\": \"testing\",\n        \"version\": \"1.0\"\n    },\n    \"paths\": {\n        \"/testing\": {\n            \"get\": {\n                \"operationId\": \"getTesting\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"A successful invocation response\"\n                    }\n                },\n                \"x-openwhisk\": {\n                    \"action\": \"hello\",\n                    \"namespace\": \"user@host.com_dev\",\n                    \"package\": \"default\",\n                    \"url\": \"https://openwhisk/api/v1/web/user@host.com_dev/default/hello.json\"\n                }\n            }\n        }\n    },\n    \"x-ibm-configuration\": {\n        \"assembly\": {\n            \"execute\": [\n                {\n                    \"operation-switch\": {\n                        \"case\": [\n                            {\n                                \"execute\": [\n                                    {\n                                        \"set-variable\": {\n                                            \"actions\": [\n                                                {\n                                                    \"set\": \"message.headers.X-Require-Whisk-Auth\",\n                                                    \"value\": \"3b08f67e-c7fa-4998-9096-ffa355932a3d\"\n                                                }\n                                            ]\n                                        }\n                                    },\n                                    {\n                                        \"invoke\": {\n                                            \"target-url\": \"https://openwhisk/api/v1/web/user@host.com_dev/default/hello.json\",\n                                            \"verb\": \"keep\"\n                                        }\n                                    }\n                                ],\n                                \"operations\": [\n                                    \"getTesting\"\n                                ]\n                            }\n                        ],\n                        \"otherwise\": [],\n                        \"title\": \"whisk-invoke\"\n                    }\n                }\n            ]\n        },\n        \"cors\": {\n            \"enabled\": true\n        }\n    }\n}\n"
  },
  {
    "path": "deploy/tests/resources/swagger_default_ns.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"basePath\": \"/testing\",\n    \"info\": {\n        \"title\": \"testing\",\n        \"version\": \"1.0\"\n    },\n    \"paths\": {\n        \"/testing\": {\n            \"get\": {\n                \"operationId\": \"getTesting\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"A successful invocation response\"\n                    }\n                },\n                \"x-openwhisk\": {\n                    \"action\": \"hello\",\n                    \"namespace\": \"_\",\n                    \"package\": \"default\",\n                    \"url\": \"https://openwhisk/api/v1/web/_/default/hello.json\"\n                }\n            }\n        }\n    },\n    \"x-ibm-configuration\": {\n        \"assembly\": {\n            \"execute\": [\n                {\n                    \"operation-switch\": {\n                        \"case\": [\n                            {\n                                \"execute\": [\n                                    {\n                                        \"set-variable\": {\n                                            \"actions\": [\n                                                {\n                                                    \"set\": \"message.headers.X-Require-Whisk-Auth\",\n                                                    \"value\": \"3b08f67e-c7fa-4998-9096-ffa355932a3d\"\n                                                }\n                                            ]\n                                        }\n                                    },\n                                    {\n                                        \"invoke\": {\n                                            \"target-url\": \"https://openwhisk/api/v1/web/_/default/hello.json\",\n                                            \"verb\": \"keep\"\n                                        }\n                                    }\n                                ],\n                                \"operations\": [\n                                    \"getTesting\"\n                                ]\n                            }\n                        ],\n                        \"otherwise\": [],\n                        \"title\": \"whisk-invoke\"\n                    }\n                }\n            ]\n        },\n        \"cors\": {\n            \"enabled\": true\n        }\n    }\n}\n"
  },
  {
    "path": "deploy/tests/resources/swagger_ns_paths.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"basePath\": \"/testing\",\n    \"info\": {\n        \"title\": \"testing\",\n        \"version\": \"1.0\"\n    },\n    \"paths\": {\n        \"/testing\": {\n            \"get\": {\n                \"operationId\": \"getTesting\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"A successful invocation response\"\n                    }\n                },\n                \"x-openwhisk\": {\n                    \"action\": \"hello\",\n                    \"namespace\": \"user@host.com_dev\",\n                    \"package\": \"default\",\n                    \"url\": \"https://openwhisk/api/v1/web/user@host.com_dev/default/hello.http\"\n                }\n            }\n        }\n    },\n    \"x-ibm-configuration\": {\n        \"assembly\": {\n            \"execute\": [\n                {\n                    \"operation-switch\": {\n                        \"case\": [\n                            {\n                                \"execute\": [\n                                    {\n                                        \"set-variable\": {\n                                            \"actions\": [\n                                                {\n                                                    \"set\": \"message.headers.X-Require-Whisk-Auth\",\n                                                    \"value\": \"3b08f67e-c7fa-4998-9096-ffa355932a3d\"\n                                                }\n                                            ]\n                                        }\n                                    },\n                                    {\n                                        \"invoke\": {\n                                            \"target-url\": \"https://openwhisk/api/v1/web/user@host.com_dev/default/hello.http$(request.path)\",\n                                            \"verb\": \"keep\"\n                                        }\n                                    }\n                                ],\n                                \"operations\": [\n                                    \"getTesting\"\n                                ]\n                            }\n                        ],\n                        \"otherwise\": [],\n                        \"title\": \"whisk-invoke\"\n                    }\n                }\n            ]\n        },\n        \"cors\": {\n            \"enabled\": true\n        }\n    }\n}\n"
  },
  {
    "path": "deploy/tests/resources/swagger_paths.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"basePath\": \"/testing\",\n    \"info\": {\n        \"title\": \"testing\",\n        \"version\": \"1.0\"\n    },\n    \"paths\": {\n        \"/testing\": {\n            \"get\": {\n                \"operationId\": \"getTesting\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"A successful invocation response\"\n                    }\n                },\n                \"x-openwhisk\": {\n                    \"action\": \"hello\",\n                    \"namespace\": \"_\",\n                    \"package\": \"default\",\n                    \"url\": \"https://openwhisk/api/v1/web/_/default/hello.http\"\n                }\n            }\n        }\n    },\n    \"x-ibm-configuration\": {\n        \"assembly\": {\n            \"execute\": [\n                {\n                    \"operation-switch\": {\n                        \"case\": [\n                            {\n                                \"execute\": [\n                                    {\n                                        \"set-variable\": {\n                                            \"actions\": [\n                                                {\n                                                    \"set\": \"message.headers.X-Require-Whisk-Auth\",\n                                                    \"value\": \"3b08f67e-c7fa-4998-9096-ffa355932a3d\"\n                                                }\n                                            ]\n                                        }\n                                    },\n                                    {\n                                        \"invoke\": {\n                                            \"target-url\": \"https://openwhisk/api/v1/web/_/default/hello.http$(request.path)\",\n                                            \"verb\": \"keep\"\n                                        }\n                                    }\n                                ],\n                                \"operations\": [\n                                    \"getTesting\"\n                                ]\n                            }\n                        ],\n                        \"otherwise\": [],\n                        \"title\": \"whisk-invoke\"\n                    }\n                }\n            ]\n        },\n        \"cors\": {\n            \"enabled\": true\n        }\n    }\n}\n"
  },
  {
    "path": "deploy/tests/validate.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst OpenWhiskDeploy = require('../index');\n\ndescribe('#validate()', () => {\n  let serverless;\n  let openwhiskDeploy;\n\n  beforeEach(() => {\n    const CLI = function () { this.log = function () {};};\n    serverless = {classes: {Error, CLI}, service: {provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: () => {}};\n    openwhiskDeploy = new OpenWhiskDeploy(serverless);\n\n    openwhiskDeploy.serverless.config = { servicePath: true };\n\n    openwhiskDeploy.serverless.service.environment = {\n      vars: {},\n      stages: {\n        dev: {\n          vars: {},\n          regions: {\n            'us-east-1': {\n              vars: {},\n            },\n          },\n        },\n      },\n    };\n\n    openwhiskDeploy.serverless.service.functions = {\n      first: {\n        handler: true,\n      },\n    };\n\n    openwhiskDeploy.options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n  });\n\n  it('should throw error if stage does not exist in service', () => {\n    openwhiskDeploy.options.stage = 'prod';\n    expect(() => openwhiskDeploy.validate()).to.throw(Error);\n  });\n\n  it('should throw error if region does not exist in service', () => {\n    openwhiskDeploy.options.region = 'us-west-2';\n    expect(() => openwhiskDeploy.validate()).to.throw(Error);\n  });\n\n  it('should throw error if not inside service (servicePath not defined)', () => {\n    openwhiskDeploy.serverless.config.servicePath = false;\n    expect(() => openwhiskDeploy.validate()).to.throw(Error);\n  });\n\n  it('should throw error if region vars object does not exist', () => {\n    openwhiskDeploy.serverless.service.environment.stages.dev.regions['us-east-1'] = {};\n    expect(() => openwhiskDeploy.validate()).to.throw(Error);\n  });\n});\n\n"
  },
  {
    "path": "deployFunction/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst fs = require('fs-extra');\nconst path = require('path')\nconst CompileFunctions = require('../compile/functions/')\n\nclass OpenWhiskDeployFunction {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options || {};\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    // Temporary hack until we have a better way to access existing plugins.\n    const is_package_plugin = plugin => plugin.hasOwnProperty('packageFunction')\n    this.pkg = serverless.pluginManager.getPlugins().find(is_package_plugin)\n\n    this.compileFunctions = new CompileFunctions(serverless, options)\n\n    this.hooks = {\n      'deploy:function:initialize': () => BbPromise.bind(this)\n        .then(this.validate),\n      'deploy:function:packageFunction': () => BbPromise.bind(this)\n        .then(this.packageFunction)\n        .then(this.compileFunction),\n      'deploy:function:deploy': () => BbPromise.bind(this)\n        .then(this.deployFunction)\n        .then(this.cleanup)\n    };\n  }\n\n  validate () {\n    if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service');\n    }\n\n    this.options.stage = this.options.stage\n      || (this.serverless.service.provider && this.serverless.service.provider.stage)\n      || 'dev';\n    this.options.region = this.options.region\n      || (this.serverless.service.provider && this.serverless.service.provider.region)\n      || 'us-east-1';\n\n    return BbPromise.resolve();\n  }\n\n  compileFunction () {\n    const functionObject = this.serverless.service.getFunction(this.options.function);\n    return this.compileFunctions.compileFunction(this.options.function, functionObject).then(action => this.action = action)\n  }\n\n  packageFunction () {\n    this.serverless.cli.log(`Packaging function: ${this.options.function}...`);\n    const functionObject = this.serverless.service.getFunction(this.options.function);\n    // sequences do not need packaging, no files to deploy\n    if (functionObject.sequence) {\n      return BbPromise.resolve();\n    }\n\n    this.serverless.service.package.individually = true\n    return this.pkg.packageFunction(this.options.function);\n  }\n\n  deployFunction (data) {\n    this.serverless.cli.log(`Deploying function: ${this.options.function}...`);\n    return this.provider.client().then(ow =>\n      ow.actions.create(this.action).then(() => {\n        this.serverless.cli.log(`Successfully deployed function: ${this.options.function}`);\n      }).catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to deploy function (${this.options.function}) due to error: ${err.message}`\n        );\n      })\n    );\n  }\n\n  cleanup () {\n    if (this.serverless.config.servicePath) {\n      const serverlessTmpDirPath = path.join(this.serverless.config.servicePath, '.serverless');\n\n      if (this.serverless.utils.dirExistsSync(serverlessTmpDirPath)) {\n        fs.removeSync(serverlessTmpDirPath);\n      }\n    }\n\n    return BbPromise.resolve();\n  }\n}\n\nmodule.exports = OpenWhiskDeployFunction;\n"
  },
  {
    "path": "deployFunction/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\nconst sinon = require('sinon');\nconst path = require('path');\nconst fs = require('fs');\nconst OpenWhiskDeployFunction = require('../index');\nconst BbPromise = require('bluebird');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskDeployFunction', () => {\n  let serverless;\n  let openwhiskDeployFunction;\n  let sandbox\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    serverless = {pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => {}, provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sandbox.spy()};\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskDeployFunction = new OpenWhiskDeployFunction(serverless, options);\n    openwhiskDeployFunction.serverless.cli = new serverless.classes.CLI();\n    openwhiskDeployFunction.serverless.service.provider = {\n      namespace: 'testing',\n      apihost: 'openwhisk.org',\n      auth: 'user:pass',\n    };\n    openwhiskDeployFunction.provider = {client: () => {}}\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskDeployFunction.hooks).to.be.not.empty);\n  });\n\n  describe('hooks', () => {\n    it('should run \"deploy:function:initialize\" promise chain in order', () => {\n      const validateStub = sinon\n        .stub(openwhiskDeployFunction, 'validate').returns(BbPromise.resolve());\n\n      return openwhiskDeployFunction.hooks['deploy:function:initialize']().then(() => {\n        expect(validateStub.calledOnce).to.equal(true);\n        openwhiskDeployFunction.validate.restore()\n      });\n    });\n\n    it('should run \"deploy:function:packageFunction\" promise chain in order', () => {\n      const packageFunctionStub = sinon\n        .stub(openwhiskDeployFunction, 'packageFunction').returns(BbPromise.resolve());\n      const compileFunctionStub = sinon\n        .stub(openwhiskDeployFunction, 'compileFunction').returns(BbPromise.resolve());\n\n      return openwhiskDeployFunction.hooks['deploy:function:packageFunction']().then(() => {\n        expect(packageFunctionStub.calledOnce).to.equal(true);\n        expect(compileFunctionStub.calledOnce).to.equal(true);\n        expect(compileFunctionStub.calledAfter(packageFunctionStub))\n          .to.equal(true);\n\n        openwhiskDeployFunction.packageFunction.restore();\n        openwhiskDeployFunction.compileFunction.restore();\n      });\n    });\n\n    it('should run \"deploy:function:deploy\" promise chain in order', () => {\n      const deployFunctionStub = sinon\n        .stub(openwhiskDeployFunction, 'deployFunction').returns(BbPromise.resolve());\n      const cleanupStub = sinon\n        .stub(openwhiskDeployFunction, 'cleanup').returns(BbPromise.resolve());\n\n      return openwhiskDeployFunction.hooks['deploy:function:deploy']().then(() => {\n        expect(deployFunctionStub.calledOnce).to.equal(true);\n        expect(cleanupStub.calledAfter(deployFunctionStub))\n          .to.equal(true);\n\n        openwhiskDeployFunction.deployFunction.restore();\n        openwhiskDeployFunction.cleanup.restore();\n      });\n    });\n  });\n\n  describe('#packageFunction()', () => {\n    it('should not package sequence actions', () => {\n      const fObj = {sequence: ['a', 'b', 'c']}\n      const spy = sinon.spy()\n      openwhiskDeployFunction.pkg = { packageFunction: spy }\n\n      const getFunctionStub = sinon.stub(openwhiskDeployFunction.serverless.service, \"getFunction\").returns(fObj)\n\n      return openwhiskDeployFunction.packageFunction().then(() => {\n        expect(spy.called).to.be.false\n      })\n    })\n  })\n\n  describe('#compileFunction()', () => {\n    it('should store compiled function on instance', () => {\n      const fObj = {handler: \"file.main\"}\n      const action = {name: \"action\"}\n      const getFunctionStub = sinon.stub(openwhiskDeployFunction.serverless.service, \"getFunction\").returns(fObj)\n      const compileFunctionStub = sinon.stub(openwhiskDeployFunction.compileFunctions, \"compileFunction\").returns(BbPromise.resolve(action))\n\n      return openwhiskDeployFunction.compileFunction().then(() => {\n        expect(openwhiskDeployFunction.action).to.be.equal(action);\n        getFunctionStub.restore();\n        compileFunctionStub.restore();\n      })\n    })\n  })\n\n  describe('#deployFunction()', () => {\n    it('should deploy function to openwhisk', () => {\n      openwhiskDeployFunction.action = {actionName: \"sample\"}\n      sandbox.stub(openwhiskDeployFunction.provider, 'client', () => {\n        const create = params => {\n          expect(params).to.be.deep.equal(openwhiskDeployFunction.action);\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ actions: { create } });\n      });\n      return expect(openwhiskDeployFunction.deployFunction())\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should reject when function handler fails to deploy with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskDeployFunction.provider, 'client', () => {\n        const create = () => Promise.reject(err);\n\n        return Promise.resolve({ actions: { create } });\n      });\n      return expect(openwhiskDeployFunction.deployFunction())\n        .to.eventually.be.rejected;\n    });\n  });\n});\n"
  },
  {
    "path": "index.js",
    "content": "'use strict';\n\n/*\nNOTE: this plugin is used to add all the differnet provider related plugins at once.\nThis way only one plugin needs to be added to the service in order to get access to the\nwhole provider implementation.\n*/\n\nconst CompileFunctions = require('./compile/functions/index.js');\nconst CompileTriggers = require('./compile/triggers/index.js');\nconst CompileRules = require('./compile/rules/index.js');\nconst CompilePackages = require('./compile/packages/index.js');\nconst CompileHttpEvents = require('./compile/apigw/index.js');\nconst CompileSchedule = require('./compile/schedule/index.js');\nconst CompileMessageHub = require('./compile/message_hub/index.js');\nconst CompileCloudant = require('./compile/cloudant/index.js');\nconst CompileServiceBindings = require('./compile/servicebindings/index.js');\nconst Deploy = require('./deploy/index.js');\nconst Invoke = require('./invoke/index.js');\nconst InvokeLocal = require('./invokeLocal/index.js');\nconst Remove = require('./remove/index.js');\nconst Logs = require('./logs/index.js');\nconst Info = require('./info/index.js');\nconst DeployFunction = require('./deployFunction/index.js');\nconst OpenwhiskProvider = require('./provider/openwhiskProvider.js');\nconst ConfigCredentials = require('./configCredentials/index.js')\n\nclass Index {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options;\n\n    this.serverless.pluginManager.addPlugin(OpenwhiskProvider);\n    this.serverless.pluginManager.addPlugin(CompilePackages);\n    this.serverless.pluginManager.addPlugin(CompileFunctions);\n    this.serverless.pluginManager.addPlugin(CompileHttpEvents);\n    this.serverless.pluginManager.addPlugin(CompileRules);\n    this.serverless.pluginManager.addPlugin(CompileTriggers);\n    this.serverless.pluginManager.addPlugin(CompileSchedule);\n    this.serverless.pluginManager.addPlugin(CompileMessageHub);\n    this.serverless.pluginManager.addPlugin(CompileCloudant);\n    this.serverless.pluginManager.addPlugin(CompileServiceBindings);\n    this.serverless.pluginManager.addPlugin(Remove);\n    this.serverless.pluginManager.addPlugin(Invoke);\n    this.serverless.pluginManager.addPlugin(InvokeLocal);\n    this.serverless.pluginManager.addPlugin(Deploy);\n    this.serverless.pluginManager.addPlugin(Logs);\n    this.serverless.pluginManager.addPlugin(Info);\n    this.serverless.pluginManager.addPlugin(DeployFunction);\n    this.serverless.pluginManager.addPlugin(ConfigCredentials);\n  }\n}\n\nmodule.exports = Index;\n"
  },
  {
    "path": "info/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst chalk = require('chalk');\nconst { formatApiHost } = require('../utils');\n\nclass OpenWhiskInfo {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options || {};\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'info:info': () => BbPromise.bind(this)\n        .then(this.validate)\n        .then(this.info),\n      'after:deploy:deploy': () => BbPromise.bind(this)\n        .then(() => {\n          if (this.options.noDeploy) {\n            return BbPromise.resolve();\n          }\n          this.consoleLog('');\n          this.failsafe = true;\n          return BbPromise.bind(this)\n            .then(this.validate)\n            .then(this.info)\n        })\n    };\n  }\n\n  validate() {\n    if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service.');\n    }\n\n    return this.provider.props().then(props => {\n      this.props = props;\n      this.props.apihost = formatApiHost(props.apihost);\n      return this.provider.client();\n    }).then(client => {\n      this.client = client;\n    })\n  }\n\n  info () {\n    this.consoleLog(`${chalk.yellow.underline('Service Information')}`);\n\n    return BbPromise.bind(this)\n      .then(this.showServiceInfo)\n      .then(this.showPackagesInfo)\n      .then(this.showActionsInfo)\n      .then(this.showTriggersInfo)\n      .then(this.showRulesInfo)\n      .then(this.showRoutesInfo)\n      .then(this.showWebActionsInfo);\n  }\n\n  showServiceInfo () {\n    this.consoleLog(`platform:\\t${this.props.apihost}`);\n    this.consoleLog(`namespace:\\t${this.props.namespace || '_'}`);\n    this.consoleLog(`service:\\t${this.serverless.service.service}\\n`);\n  }\n\n  showActionsInfo () {\n    this.consoleLog(`${chalk.yellow('actions:')}`);\n    return this.client.actions.list().then(actions => {\n      this._actions = actions;\n      if (!actions.length) return console.log('**no actions deployed**\\n');\n      const names = actions.map(action => {\n        const pkge = action.namespace.match(/\\/(.+)/)\n        if (!pkge) return action.name\n        return `${pkge[1]}/${action.name}`\n      }).join('    ');\n      this.consoleLog(names + '\\n')\n    })\n  }\n\n  showWebActionsInfo () {\n    this.consoleLog(`${chalk.yellow('endpoints (web actions):')}`);\n    const web_actions = this._actions.filter(action => {\n      const annotations = action.annotations || []\n      return annotations.some(a => a.key === 'web-export' && a.value === true)\n    })\n    if (!web_actions.length) {\n      this.consoleLog('**no web actions deployed**\\n');\n      return Promise.resolve();\n    }\n\n    const extractNsAndPkge = id => {\n      let [ns, pkge] = id.split('/')\n      pkge = pkge || 'default'\n      return  { ns, pkge }\n    }\n\n    return this.provider.props().then(props => {\n      web_actions.forEach(action => {\n        const nsPkge = extractNsAndPkge(action.namespace)\n        this.consoleLog(`${formatApiHost(props.apihost)}/api/v1/web/${nsPkge.ns}/${nsPkge.pkge}/${action.name}`)\n      })\n    })\n  }\n\n  showResourcesInfo (resource) {\n    this.consoleLog(`${chalk.yellow(`${resource}:`)}`);\n    return this.client[resource].list().then(resources => {\n      if (!resources.length) return console.log(`**no ${resource} deployed**\\n`);\n      const names = resources.map(r => r.name).join('    ');\n      this.consoleLog(names + '\\n')\n    })\n  }\n\n  showPackagesInfo () {\n    return this.showResourcesInfo('packages')\n  }\n\n  showTriggersInfo () {\n    return this.showResourcesInfo('triggers')\n  }\n\n  showRulesInfo () {\n    return this.showResourcesInfo('rules')\n  }\n\n  showRoutesInfo () {\n    this.consoleLog(`${chalk.yellow('endpoints (api-gw):')}`)\n    let operation = this.client.routes.list().then(routes => {\n      if (!routes.apis.length) return console.log('**no routes deployed**\\n')\n      routes.apis.forEach(api => this.logApiEndPoints(api.value))\n      this.consoleLog('')\n    })\n      \n    operation = operation.catch(err => {\n      this.consoleLog(`${chalk.red('**failed to fetch routes**')}`)\n      if(err.message.match(/status code 400/) && err.message.match(/expired/)) {\n        this.consoleLog(`${chalk.red('**api gateway key is wrong or has expired! if it has expired, please refresh with wsk bluemix login**\\n')}`)\n      }\n\n      if (!this.failsafe) throw err\n    })\n\n    return operation\n  }\n\n  logEndPoint (baseUrl, path, method, actionName) {\n    if (!path.startsWith('/')) {\n      path = `/${path}`\n    }\n\n    if (baseUrl.endsWith('/')) {\n      baseUrl = baseUrl.slice(0, baseUrl.length - 1)\n    }\n\n    this.consoleLog(`${method.toUpperCase()} ${baseUrl}${path} --> ${actionName}`)\n  }\n\n  logApiEndPoints (api) {\n    const paths = api.apidoc.paths\n    Object.keys(paths).forEach(path => {\n      const methods = Object.keys(paths[path])\n      methods.forEach(method => {\n        const operation = paths[path][method]\n        let actionName = 'unknown'\n        if (operation.hasOwnProperty('x-openwhisk')) {\n          actionName = operation['x-openwhisk'].action;\n        } else if (operation.hasOwnProperty('x-ibm-op-ext')) {\n          actionName = operation['x-ibm-op-ext'].actionName;\n        }\n        this.logEndPoint(api.gwApiUrl, path, method, actionName)\n      })\n    })\n  }\n\n  consoleLog (message) {\n    console.log(message)\n  }\n}\n\nmodule.exports = OpenWhiskInfo;\n"
  },
  {
    "path": "info/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\nconst sinon = require('sinon');\nconst path = require('path');\nconst os = require('os');\nconst OpenWhiskInfo = require('../');\nconst BbPromise = require('bluebird');\nconst chalk = require('chalk');\nconst moment = require('moment');\nconst Credentials = require('../../provider/credentials')\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskInfo', () => {\n  let sandbox;\n\n  const CLI = function () { this.log = function () {};};\n  const serverless = {pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => {}, provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n    function: 'first'\n  };\n  const openwhiskInfo = new OpenWhiskInfo(serverless, options);\n  openwhiskInfo.client = { routes: { list: () => {} }, rules: { list: () => {} }, triggers: { list: () => {} }, packages: { list: () => {} }, actions: { list: () => {} } };\n  serverless.service.service = \"my_service\";\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskInfo.hooks).to.be.not.empty);\n\n    it('should run promise chain in order', () => {\n      const validateStub = sinon\n        .stub(openwhiskInfo, 'validate').returns(BbPromise.resolve());\n      const infoStub = sinon\n        .stub(openwhiskInfo, 'info').returns(BbPromise.resolve());\n\n      return openwhiskInfo.hooks['info:info']().then(() => {\n        expect(validateStub.calledOnce).to.be.equal(true);\n        expect(infoStub.calledAfter(validateStub)).to.be.equal(true);\n        openwhiskInfo.validate.restore();\n        openwhiskInfo.info.restore();\n      });\n    });\n  });\n\n\n  describe('#info()', () => {\n    it('should show title and call display functions', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      const service = sandbox.stub(openwhiskInfo, 'showServiceInfo')\n      const action = sandbox.stub(openwhiskInfo, 'showActionsInfo')\n      const packages = sandbox.stub(openwhiskInfo, 'showPackagesInfo')\n      const triggers = sandbox.stub(openwhiskInfo, 'showTriggersInfo')\n      const rules = sandbox.stub(openwhiskInfo, 'showRulesInfo')\n      const routes = sandbox.stub(openwhiskInfo, 'showRoutesInfo')\n      const web_actions = sandbox.stub(openwhiskInfo, 'showWebActionsInfo')\n\n      return openwhiskInfo.info().then(() => {\n        expect(service.calledOnce).to.be.equal(true);\n        expect(packages.calledOnce).to.be.equal(true);\n        expect(action.calledOnce).to.be.equal(true);\n        expect(triggers.calledOnce).to.be.equal(true);\n        expect(rules.calledOnce).to.be.equal(true);\n        expect(routes.calledOnce).to.be.equal(true);\n        expect(web_actions.calledOnce).to.be.equal(true);\n        expect(log.args[0][0].match(/Service Information/)).to.be.ok;\n      });\n    });\n  });\n\n  describe('#showServiceInfo()', () => {\n    it('should show service, platform and call display functions', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      openwhiskInfo.props = {\n        apihost: 'some_end_point',\n        namespace: 'custom_ns'\n      };\n\n      openwhiskInfo.showServiceInfo()\n      expect(log.calledThrice).to.be.equal(true);\n      expect(log.args[0][0].match(/platform:\\tsome_end_point/)).to.be.ok;\n      expect(log.args[1][0].match(/namespace:\\tcustom_ns/)).to.be.ok;\n      expect(log.args[2][0].match(/service:\\tmy_service/)).to.be.ok;\n    });\n  });\n\n  describe('#showPackagesInfo()', () => {\n    it('should show package names returned', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.packages, 'list').returns(BbPromise.resolve([\n        {name: 'first'}, {name: 'second'}, {name: 'third'}\n      ]));\n\n      return expect(openwhiskInfo.showPackagesInfo().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0].match(/packages:/)).to.be.ok;\n        expect(log.args[1][0].match(/first    second    third/)).to.be.ok;\n      }));\n    })\n  })\n\n  describe('#showActionsInfo()', () => {\n    it('should show action names returned', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.actions, 'list').returns(BbPromise.resolve([\n        {name: 'first', namespace: 't'}, {name: 'second', namespace: \"t\"}, {name: 'third', namespace: \"t\"}\n      ]));\n\n      return expect(openwhiskInfo.showActionsInfo().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0].match(/actions:/)).to.be.ok;\n        expect(log.args[1][0].match(/first    second    third/)).to.be.ok;\n      }));\n    })\n\n    it('should show package action names returned', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.actions, 'list').returns(BbPromise.resolve([\n        {name: 'first', namespace: \"testing\"}, {name: 'second', namespace: 'user@host.com/somePackage'}, {name: 'third', namespace: \"testing\"}\n      ]));\n\n      return expect(openwhiskInfo.showActionsInfo().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0].match(/actions:/)).to.be.ok;\n        expect(log.args[1][0].match(/first    somePackage\\/second    third/)).to.be.ok;\n      }));\n    })\n\n  })\n\n  describe('#showTriggersInfo()', () => {\n    it('should show trigger names returned', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.triggers, 'list').returns(BbPromise.resolve([\n        {name: 'first'}, {name: 'second'}, {name: 'third'}\n      ]));\n\n      return expect(openwhiskInfo.showTriggersInfo().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0].match(/triggers:/)).to.be.ok;\n        expect(log.args[1][0].match(/first    second    third/)).to.be.ok;\n      }));\n    })\n  })\n\n  describe('#showRulesInfo()', () => {\n    it('should show rules names returned', () => {\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.rules, 'list').returns(BbPromise.resolve([\n        {name: 'first'}, {name: 'second'}, {name: 'third'}\n      ]));\n\n      return expect(openwhiskInfo.showRulesInfo().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0].match(/rules:/)).to.be.ok;\n        expect(log.args[1][0].match(/first    second    third/)).to.be.ok;\n      }));\n    })\n  })\n\n  describe('#showRoutesInfo()', () => {\n    it('should show routes returned', () => {\n      const endpoint = {\n        \"x-ibm-op-ext\": {\n          \"actionName\": \"my_service-dev-hello\"\n        }\n      }\n      const apis = [{\n        value: {\n          gwApiUrl: 'https://api-gateway.com/service_name',\n          apidoc: {\n            paths: {\n              \"/api/hello\": { get: endpoint },\n              \"/api/foobar\": { post: endpoint }\n            }\n          }\n        }\n      }, {\n        value: {\n          gwApiUrl: 'https://api-gateway.com/service_name',\n          apidoc: {\n            paths: {\n              \"/api/foo/1\": { get: endpoint },\n              \"/api/bar/2\": { post: endpoint }\n            }\n          }\n        }\n      }] \n\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.routes, 'list').returns(BbPromise.resolve({ apis }));\n\n      return expect(openwhiskInfo.showRoutesInfo().then(() => {\n        expect(log.args[0][0].match(/endpoints \\(api-gw\\):/)).to.be.ok;\n        expect(log.args[1][0].match(/GET https:\\/\\/api-gateway.com\\/service_name\\/api\\/hello/)).to.be.ok;\n        expect(log.args[2][0].match(/POST https:\\/\\/api-gateway.com\\/service_name\\/api\\/foobar/)).to.be.ok;\n        expect(log.args[3][0].match(/GET https:\\/\\/api-gateway.com\\/service_name\\/api\\/foo\\/1/)).to.be.ok;\n        expect(log.args[4][0].match(/POST https:\\/\\/api-gateway.com\\/service_name\\/api\\/bar\\/2/)).to.be.ok;\n      }));\n    })\n\n    it('should show api v2 routes returned', () => {\n      const endpoint = {\n        \"x-openwhisk\": {\n          \"action\": \"my_service-dev-hello\"\n        }\n      }\n      const apis = [{\n        value: {\n          gwApiUrl: 'https://api-gateway.com/service_name/api',\n          apidoc: {\n            paths: {\n              \"hello\": { get: endpoint },\n              \"foobar\": { post: endpoint }\n            }\n          }\n        }\n      }, {\n        value: {\n          gwApiUrl: 'https://api-gateway.com/service_name/api',\n          apidoc: {\n            paths: {\n              \"foo/1\": { get: endpoint },\n              \"bar/2\": { post: endpoint }\n            }\n          }\n        }\n      }] \n\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.routes, 'list').returns(BbPromise.resolve({ apis }));\n\n      return expect(openwhiskInfo.showRoutesInfo().then(() => {\n        expect(log.args[0][0].match(/endpoints \\(api-gw\\):/)).to.be.ok;\n        expect(log.args[1][0].match(/GET https:\\/\\/api-gateway.com\\/service_name\\/api\\/hello/)).to.be.ok;\n        expect(log.args[2][0].match(/POST https:\\/\\/api-gateway.com\\/service_name\\/api\\/foobar/)).to.be.ok;\n        expect(log.args[3][0].match(/GET https:\\/\\/api-gateway.com\\/service_name\\/api\\/foo\\/1/)).to.be.ok;\n        expect(log.args[4][0].match(/POST https:\\/\\/api-gateway.com\\/service_name\\/api\\/bar\\/2/)).to.be.ok;\n      }));\n    })\n\n    it('should display error message in failsafe mode', () => {\n      openwhiskInfo.failsafe = true;\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.routes, 'list').returns(BbPromise.resolve(false));\n\n      return expect(openwhiskInfo.showRoutesInfo().then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        expect(log.args[0][0].match(/endpoints \\(api-gw\\):/)).to.be.ok;\n        expect(log.args[1][0].match(/failed to fetch routes/)).to.be.ok;\n      }));\n    })\n\n    it('should display error message about expired key in failsafe mode', () => {\n      openwhiskInfo.failsafe = true;\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.routes, 'list').returns(BbPromise.reject(new Error('status code 400 blah blah expired')));\n\n      return expect(openwhiskInfo.showRoutesInfo().then(() => {\n        expect(log.calledThrice).to.be.equal(true);\n        expect(log.args[0][0].match(/endpoints \\(api-gw\\):/)).to.be.ok;\n        expect(log.args[1][0].match(/failed to fetch routes/)).to.be.ok;\n        expect(log.args[2][0].match(/expired/)).to.be.ok;\n      }));\n    })\n\n    it('should return error about expired key without failsafe mode', () => {\n      openwhiskInfo.failsafe = false;\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      sandbox.stub(openwhiskInfo.client.routes, 'list').returns(BbPromise.reject(new Error('status code 400 blah blah expired')));\n\n      return expect(openwhiskInfo.showRoutesInfo())\n        .to.eventually.be.rejected;\n    })\n  })\n\n  describe('#showWebActionsInfo()', () => {\n    it('should show web action routes returned', () => {\n      const apihost = 'openwhisk.ng.bluemix.net'\n      openwhiskInfo.provider = { props: () => Promise.resolve({ apihost }) }\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      openwhiskInfo._actions = [\n        {name: 'first', namespace: 'user_name', annotations: [{key: 'web-export', value: true}, {key: 'a', value: 'b'}]}, \n        {name: 'second', namespace: 'user_name', annotations: [{key: 'web-export', value: false}]}, \n        {name: 'third'},\n        {name: 'fourth', namespace: 'user_name', annotations: [{key: 'web-export', value: true}]}, \n        {name: 'fifth', annotations: []},\n        {name: 'sixth', namespace: 'user_name/custom_package', annotations: [{key: 'web-export', value: true}]},\n      ];\n\n      return expect(openwhiskInfo.showWebActionsInfo().then(() => {\n        expect(log.callCount).to.be.equal(4);\n        expect(log.args[0][0].match(/endpoints \\(web actions\\):/)).to.be.ok;\n        expect(log.args[1][0].match(/https:\\/\\/openwhisk.ng.bluemix.net\\/api\\/v1\\/web\\/user_name\\/default\\/first/)).to.be.ok;\n        expect(log.args[2][0].match(/https:\\/\\/openwhisk.ng.bluemix.net\\/api\\/v1\\/web\\/user_name\\/default\\/fourth/)).to.be.ok;\n        expect(log.args[3][0].match(/https:\\/\\/openwhisk.ng.bluemix.net\\/api\\/v1\\/web\\/user_name\\/custom_package\\/sixth/)).to.be.ok;\n      }));\n    })\n\n    it('should show web action routes returned with apihost that include a protocol', () => {\n      const apihost = 'http://localhost'\n      openwhiskInfo.provider = { props: () => Promise.resolve({ apihost }) }\n      const log = sandbox.stub(openwhiskInfo, 'consoleLog')\n      openwhiskInfo._actions = [\n        {name: 'first', namespace: 'user_name', annotations: [{key: 'web-export', value: true}, {key: 'a', value: 'b'}]},\n        {name: 'second', namespace: 'user_name', annotations: [{key: 'web-export', value: false}]},\n        {name: 'third'},\n        {name: 'fourth', namespace: 'user_name', annotations: [{key: 'web-export', value: true}]},\n        {name: 'fifth', annotations: []},\n        {name: 'sixth', namespace: 'user_name/custom_package', annotations: [{key: 'web-export', value: true}]},\n      ];\n\n      return expect(openwhiskInfo.showWebActionsInfo().then(() => {\n        expect(log.callCount).to.be.equal(4);\n        expect(log.args[0][0].match(/endpoints \\(web actions\\):/)).to.be.ok;\n        expect(log.args[1][0].match(/http:\\/\\/localhost\\/api\\/v1\\/web\\/user_name\\/default\\/first/)).to.be.ok;\n        expect(log.args[2][0].match(/http:\\/\\/localhost\\/api\\/v1\\/web\\/user_name\\/default\\/fourth/)).to.be.ok;\n        expect(log.args[3][0].match(/http:\\/\\/localhost\\/api\\/v1\\/web\\/user_name\\/custom_package\\/sixth/)).to.be.ok;\n      }));\n    })\n  })\n});\n"
  },
  {
    "path": "invoke/README.md",
    "content": "# Invoke\n\nThis plugin invokes an OpenWhisk Action.\n\n## How it works\n\n`Invoke` hooks into the [`invoke:invoke`](/lib/plugins/invoke) lifecycle. It\nwill send the HTTP POST request to the Action endpoint to trigger the function\nactivation.\n\nThe output of the function is fetched and will be prompted on the console.\n"
  },
  {
    "path": "invoke/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst chalk = require('chalk');\nconst path = require('path');\nconst stdin = require('get-stdin');\nconst fse = require('fs-extra');\n\nconst CmdLineParamsOptions = {\n  type: ['blocking', 'nonblocking'],\n  log: ['result', 'response'],\n};\n\nclass OpenWhiskInvoke {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options || {};\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'invoke:invoke': () => BbPromise.bind(this)\n        .then(this.validate)\n        .then(this.invoke)\n        .then(this.log),\n    };\n  }\n\n  validate() {\n    if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service.');\n    }\n\n    this.serverless.service.getFunction(this.options.function);\n\n    return new Promise((resolve, reject) => {\n      if (this.options.data) {\n        resolve();\n      } else if (this.options.path) {\n        const absolutePath = path.isAbsolute(this.options.path) ?\n          this.options.path :\n          path.join(this.serverless.config.servicePath, this.options.path);\n        if (!this.serverless.utils.fileExistsSync(absolutePath)) {\n          throw new this.serverless.classes.Error('The file you provided does not exist.');\n        }\n        this.options.data = this.readFileSync(absolutePath);\n        console.log(typeof this.options.data)\n\n        if (this.options.data == null) {\n          throw new this.serverless.classes.Error(\n            'The file path provided must point to a JSON file with a top-level JSON object definition.'\n          );\n        }\n        resolve();\n      } else {\n        return this.getStdin().then(input => {\n          this.options.data = input || '{}';\n          resolve();\n        });\n      }\n    }).then(() => {\n      try {\n        this.options.data = JSON.parse(this.options.data);\n        if (this.options.data == null || typeof this.options.data !== 'object') throw new this.serverless.classes.Error('Data parameter must be a JSON object')\n        \n          } catch (exception) {\n        throw new this.serverless.classes.Error(\n          `Error parsing data parameter as JSON: ${exception}`\n        );\n      }\n    }).then(() => {\n      this.validateParamOptions();\n\n      return this.provider.client().then(client => {\n        this.client = client;\n      });\n    });\n  }\n\n  readFileSync(path) {\n    return fse.readFileSync(path);\n  }\n\n  getStdin() {\n    return stdin()\n  }\n  // ensure command-line parameter values is a valid option.\n  validateParamOptions() {\n    Object.keys(CmdLineParamsOptions).forEach(key => {\n      if (!this.options[key]) {\n        this.options[key] = CmdLineParamsOptions[key][0];\n      } else if (!CmdLineParamsOptions[key].find(i => i === this.options[key])) {\n        const options = CmdLineParamsOptions[key].join(' or ');\n        throw new this.serverless.classes.Error(\n          `Invalid ${key} parameter value, must be either ${options}.`\n        );\n      }\n    });\n  }\n\n  invoke() {\n    const functionObject = this.serverless.service.getFunction(this.options.function);\n\n    const options = {\n      blocking: this.isBlocking(),\n      actionName: functionObject.name\n        || `${this.serverless.service.service}_${this.options.function}`,\n    };\n\n    if (functionObject.namespace) {\n      options.namespace = functionObject.namespace;\n    }\n\n    if (this.options.data) {\n      options.params = this.options.data;\n    }\n\n    return this.client.actions.invoke(options)\n      .catch(err => this.formatErrMsg(err));\n  }\n\n  formatErrMsg (err) {\n    let err_msg = `Failed to invoke function service (${this.options.function}) due to error:`\n    const base_err = err.error\n    if (base_err.response && base_err.response.result && typeof base_err.response.result.error === 'string') {\n      err.message = base_err.response.result.error\n      err_msg = `Failed to invoke function service (${this.options.function}) due to application error:`\n      const logs_msg = ` Check logs for activation: ${base_err.activationId}`\n      throw new this.serverless.classes.Error(`${err_msg}\\n\\n     ${err.message}\\n\\n    ${logs_msg}`)\n    }\n\n    throw new this.serverless.classes.Error(`${err_msg}\\n\\n     ${err.message}`)\n  }\n\n  isBlocking() {\n    return this.options.type === 'blocking';\n  }\n\n  isLogResult() {\n    return this.options.log === 'result';\n  }\n\n  log(invocationReply) {\n    if (this.options.verbose || this.options.v) {\n      this.logDetails(invocationReply)\n    }\n\n    let color = 'white';\n\n    // error response indicated in-blocking call boolean parameter, success.\n    if (this.isBlocking() && !invocationReply.response.success) {\n      color = 'red';\n    }\n\n    let result = invocationReply;\n\n    // blocking invocation result available as 'response.result' parameter\n    if (this.isBlocking() && this.isLogResult()) {\n      result = invocationReply.response.result;\n    }\n\n    this.consoleLog(chalk[color](JSON.stringify(result, null, 4)));\n    return BbPromise.resolve();\n  }\n\n  logDetails(invocationReply) {\n    const id = `/${invocationReply.namespace}/${invocationReply.name}`\n    const actv = invocationReply.activationId\n\n    const find_time = (annotations, key) => (annotations.find(el => el.key === key) || {value: 0}).value\n    const annotations = invocationReply.annotations || []\n    const waitTime = find_time(annotations, 'waitTime')\n    const initTime = find_time(annotations, 'initTime')\n\n    const field = (name, label) => `${chalk.blue(name)} (${chalk.yellow(label)})`\n    const time = (name, value, color = 'blue') => `${chalk[color](name)}: ${chalk.green(value + 'ms')}`\n\n    const duration = (duration, init = 0, wait) => `${time('duration', duration)} (${time('init', init, 'magenta')}, ${time('wait', wait, 'magenta')})`\n\n    this.consoleLog(`${chalk.green('=>')} ${field('action', id)} ${field('activation', actv)} ${duration(invocationReply.duration, initTime, waitTime)}`)\n  }\n\n  consoleLog(msg) {\n    console.log(msg); // eslint-disable-line no-console\n  }\n}\n\nmodule.exports = OpenWhiskInvoke;\n"
  },
  {
    "path": "invoke/tests/index.js",
    "content": "'use strict';\n\nconst chalk = require('chalk');\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\nconst sinon = require('sinon');\nconst path = require('path');\nconst os = require('os');\nconst OpenWhiskInvoke = require('../');\nconst BbPromise = require('bluebird');\nconst fs = require('fs-extra');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskInvoke', () => {\n  let sandbox;\n\n  const CLI = function () { this.log = function () {};};\n  const serverless = {config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => {}, provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n    function: 'first',\n  };\n  const openwhiskInvoke = new OpenWhiskInvoke(serverless, options);\n\n  beforeEach(() => {\n    openwhiskInvoke.provider = {client: () => Promise.resolve({})}\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskInvoke.hooks).to.be.not.empty);\n\n    it('should run promise chain in order', () => {\n      const validateStub = sinon\n        .stub(openwhiskInvoke, 'validate').returns(BbPromise.resolve());\n      const invokeStub = sinon\n        .stub(openwhiskInvoke, 'invoke').returns(BbPromise.resolve());\n      const logStub = sinon\n        .stub(openwhiskInvoke, 'log').returns(BbPromise.resolve());\n\n      return openwhiskInvoke.hooks['invoke:invoke']().then(() => {\n        expect(validateStub.calledOnce).to.be.equal(true);\n        expect(invokeStub.calledAfter(validateStub)).to.be.equal(true);\n        expect(logStub.calledAfter(invokeStub)).to.be.equal(true);\n\n        openwhiskInvoke.validate.restore();\n        openwhiskInvoke.invoke.restore();\n        openwhiskInvoke.log.restore();\n      });\n    });\n  });\n\n  describe('#validate()', () => {\n    beforeEach(() => {\n      serverless.config.servicePath = true;\n      serverless.service.environment = {\n        vars: {},\n        stages: {\n          dev: {\n            vars: {},\n            regions: {\n              'us-east-1': {\n                vars: {},\n              },\n            },\n          },\n        },\n      };\n      serverless.service.functions = {\n        first: {\n          handler: true,\n        },\n      };\n      serverless.service.getFunction = name => serverless.service.functions[name];\n    });\n\n    it('it should parse data parameter as JSON if provided', () => {\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      const data = {\n        testProp: 'testValue',\n      };\n      openwhiskInvoke.options.data = '{\"hello\": \"world\"}';\n\n      return openwhiskInvoke.validate().then(() => {\n        expect(openwhiskInvoke.options.data).to.deep.equal({hello: \"world\"});\n        openwhiskInvoke.options.data = null;\n      });\n    });\n\n    it('it should parse stdin as JSON data without explicit options', () => {\n      const data = '{\"hello\": \"world\"}';\n      sinon.stub(openwhiskInvoke, 'getStdin').returns(BbPromise.resolve(data));\n\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      return openwhiskInvoke.validate().then(() => {\n        expect(openwhiskInvoke.options.data).to.deep.equal({hello: \"world\"});\n        openwhiskInvoke.options.data = null;\n      });\n    });\n\n    it('it should throw if file is not parsed as JSON object (invalid)', () => {\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      openwhiskInvoke.options.data = '{\"hello\": \"world\"';\n      return expect(openwhiskInvoke.validate()).to.eventually.be.rejectedWith('Error parsing')\n    });\n\n    it('it should throw if file is not parsed as JSON object (number)', () => {\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      openwhiskInvoke.options.data = '1';\n      return expect(openwhiskInvoke.validate()).to.eventually.be.rejectedWith('Error parsing')\n    });\n\n    it('it should parse file if file path is provided', () => {\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      const data = {\n        testProp: 'testValue',\n      };\n      openwhiskInvoke.serverless.utils = {fileExistsSync: () => true};\n      openwhiskInvoke.readFileSync = () => JSON.stringify(data);\n      openwhiskInvoke.options.path = 'data.json';\n      openwhiskInvoke.options.data = null;\n\n      return openwhiskInvoke.validate().then(() => {\n        expect(openwhiskInvoke.options.data).to.deep.equal(data);\n        openwhiskInvoke.options.path = false;\n        serverless.config.servicePath = true;\n      });\n    });\n\n    it('it should throw if file is not parsed as JSON object', () => {\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      openwhiskInvoke.serverless.utils = {fileExistsSync: () => true};\n      openwhiskInvoke.options.path = 'data.txt';\n      openwhiskInvoke.readFileSync = () => 'testing';\n\n      return expect(openwhiskInvoke.validate()).to.eventually.be.rejectedWith('Error parsing')\n    });\n\n    it('it should throw if type parameter is not valid value', () => {\n      openwhiskInvoke.options.type = 'random';\n      openwhiskInvoke.options.path = null;\n      openwhiskInvoke.options.data = null;\n      return expect(openwhiskInvoke.validate()).to.eventually.be.rejectedWith('blocking or nonblocking')\n    });\n\n    it('it should throw if log parameter is not valid value', () => {\n      openwhiskInvoke.options.type = 'blocking';\n      openwhiskInvoke.options.log = 'random';\n      openwhiskInvoke.options.path = null;\n      openwhiskInvoke.options.data = '{}';\n      return expect(openwhiskInvoke.validate()).to.eventually.be.rejectedWith('result or response')\n    });\n\n    it('it should throw error if service path is not set', () => {\n      serverless.config.servicePath = false;\n      expect(() => openwhiskInvoke.validate()).to.throw(Error);\n    });\n\n    it('it should throw error if file path does not exist', () => {\n      serverless.config.servicePath = path.join(os.tmpdir(), (new Date).getTime().toString());\n      openwhiskInvoke.serverless.utils = {fileExistsSync: () => false};\n      openwhiskInvoke.options.path = 'some/path';\n      openwhiskInvoke.options.data = null;\n\n      return expect(openwhiskInvoke.validate()).to.eventually.be.rejectedWith('does not exist')\n    });\n  });\n\n  describe('#invoke()', () => {\n    let invokeStub;\n    beforeEach(() => {\n      openwhiskInvoke.serverless.service.functions = {\n        first: {\n          namespace: 'sample',\n          handler: true,\n        },\n      };\n\n      openwhiskInvoke.serverless.service.service = 'new-service';\n      openwhiskInvoke.options = {\n        stage: 'dev',\n        function: 'first',\n        data: { a: 1 },\n      };\n\n      openwhiskInvoke.client = { actions: { invoke: () => {} } };\n    });\n\n    afterEach(() => {\n      invokeStub.restore();\n    });\n\n    it('should invoke with correct params', () => {\n      invokeStub = sinon.stub(openwhiskInvoke.client.actions, 'invoke')\n        .returns(BbPromise.resolve());\n      return openwhiskInvoke.invoke().then(() => {\n        expect(invokeStub.calledOnce).to.be.equal(true);\n        expect(invokeStub.args[0][0]).to.be.deep.equal({\n          actionName: 'new-service_first',\n          blocking: false,\n          namespace: 'sample',\n          params: { a: 1 },\n        });\n      });\n    }\n    );\n\n\n    it('should reject when sdk client fails', () => {\n      invokeStub = sinon.stub(openwhiskInvoke.client.actions, 'invoke').returns(BbPromise.reject());\n      return expect(openwhiskInvoke.invoke()).to.be.eventually.rejected;\n    });\n  });\n\n  describe('#log()', () => {\n    it('should log activation response result', () => {\n      const log = sandbox.stub(openwhiskInvoke, 'consoleLog');\n      openwhiskInvoke.options.log = 'result'\n      openwhiskInvoke.options.type = 'blocking'\n\n      const result = {success: true, result: { hello: \"world\"} };\n      return openwhiskInvoke.log({response: result}).then(() => {\n        expect(log.calledOnce).to.be.equal(true);\n        const msg = chalk.white(JSON.stringify(result.result, null, 4));\n        console.log(msg)\n        expect(log.args[0][0]).to.be.equal(msg);\n      });\n    });\n\n    it('should log verbose activation response result', () => {\n      const log = sandbox.stub(openwhiskInvoke, 'consoleLog');\n      openwhiskInvoke.options.log = 'result'\n      openwhiskInvoke.options.type = 'blocking'\n      openwhiskInvoke.options.v = true\n\n      const input = {\n        activationId: 12345,\n        name: 'blah',\n        namespace: 'workspace',\n        duration: 100,\n        annotations: [ { key: \"waitTime\", value: 33 } ],\n        response: { success: true, result: { hello: \"world\"} }\n      };\n      return openwhiskInvoke.log(input).then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        const msg = chalk.white(JSON.stringify(input.response.result, null, 4));\n\n        const field = (name, label) => `${chalk.blue(name)} (${chalk.yellow(label)})`\n        const time = (name, value, color = 'blue') => `${chalk[color](name)}: ${chalk.green(value + 'ms')}`\n      const duration = (duration, init = 0, wait) => `${time('duration', duration)} (${time('init', init, 'magenta')}, ${time('wait', wait, 'magenta')})`\n\n        const output = `${chalk.green('=>')} ${field('action', '/workspace/blah')} ${field('activation', 12345)} ${duration(100, undefined, 33)}`\n\n        expect(log.args[0][0]).to.be.equal(output);\n        expect(log.args[1][0]).to.be.equal(msg);\n      });\n    });\n\n    it('should log verbose activation coldstart response result', () => {\n      const log = sandbox.stub(openwhiskInvoke, 'consoleLog');\n      openwhiskInvoke.options.log = 'result'\n      openwhiskInvoke.options.type = 'blocking'\n      openwhiskInvoke.options.v = true\n\n      const input = {\n        activationId: 12345,\n        name: 'blah',\n        namespace: 'workspace',\n        duration: 100,\n        annotations: [ \n          { key: \"waitTime\", value: 33 },\n          { key: \"initTime\", value: 63 }\n        ],\n        response: { success: true, result: { hello: \"world\"} }\n      };\n      return openwhiskInvoke.log(input).then(() => {\n        expect(log.calledTwice).to.be.equal(true);\n        const msg = chalk.white(JSON.stringify(input.response.result, null, 4));\n\n        const field = (name, label) => `${chalk.blue(name)} (${chalk.yellow(label)})`\n        const time = (name, value, color = 'blue') => `${chalk[color](name)}: ${chalk.green(value + 'ms')}`\n      const duration = (duration, init = 0, wait) => `${time('duration', duration)} (${time('init', init, 'magenta')}, ${time('wait', wait, 'magenta')})`\n\n        const output = `${chalk.green('=>')} ${field('action', '/workspace/blah')} ${field('activation', 12345)} ${duration(100, 63, 33)}`\n\n        expect(log.args[0][0]).to.be.equal(output);\n        expect(log.args[1][0]).to.be.equal(msg);\n      });\n    });\n\n\n\n  });\n});\n"
  },
  {
    "path": "invokeLocal/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst _ = require('lodash');\nconst path = require('path');\nconst chalk = require('chalk');\nconst stdin = require('get-stdin');\nconst spawn = require('child_process').spawn;\n\nclass OpenWhiskInvokeLocal {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options || {};\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    this.hooks = {\n      'invoke:local:invoke': () => BbPromise.bind(this)\n        .then(this.validate)\n        .then(this.mergePackageParams)\n        .then(this.loadEnvVars)\n        .then(this.invokeLocal),\n    };\n  }\n\n  validate() {\n    if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service.');\n    }\n\n    this.options.functionObj = this.serverless.service.getFunction(this.options.function);\n\n    return new BbPromise(resolve => {\n      if (this.options.data) {\n        resolve();\n      } else if (this.options.path) {\n        const absolutePath = path.isAbsolute(this.options.path) ?\n          this.options.path :\n          path.join(this.serverless.config.servicePath, this.options.path);\n        if (!this.serverless.utils.fileExistsSync(absolutePath)) {\n          throw new this.serverless.classes.Error('The file you provided does not exist.');\n        }\n        this.options.data = this.serverless.utils.readFileSync(absolutePath);\n        resolve();\n      } else {\n        stdin().then(input => {\n          this.options.data = input;\n          resolve();\n        });\n      }\n    }).then(() => {\n      const params = this.options.functionObj.parameters || {};\n      let data = {};\n      try {\n        if(typeof this.options.data === 'object') {\n          data = this.options.data;\n        } else {\n          data = JSON.parse(this.options.data);\n        }\n      } catch (exception) {\n        // do nothing if it's a simple string or object already\n      }\n\n      this.options.data = Object.assign(params, data);\n    });\n  }\n\n  mergePackageParams() {\n    const functionObj = this.serverless.service.getFunction(this.options.function) || {}\n    const name = functionObj.name || ''\n    const id = name.match(/^(.+)\\/.+$/)\n    if (id) {\n      const pgke = id[1]\n      const manifestPackages = this.serverless.service.resources.packages || {};\n      const packageDetails = manifestPackages[pgke] || {}\n      const packageParams = packageDetails.parameters || {}\n      this.options.data = Object.assign(packageParams, this.options.data);\n    }\n\n    return BbPromise.resolve();\n  }\n\n  loadEnvVars() {\n    return this.provider.props().then(props => {\n      const envVars = {\n        __OW_API_KEY: props.auth,\n        __OW_API_HOST: props.apihost,\n        __OW_ACTION_NAME: this.calculateFunctionName(this.options.function, this.options.functionObj),\n        __OW_NAMESPACE: this.calculateFunctionNameSpace(this.options.functionObj)\n      };\n\n      _.merge(process.env, envVars);\n\n      return BbPromise.resolve();\n    })\n  }\n\n  calculateFunctionName(functionName, functionObject) {\n    const namespace = this.calculateFunctionNameSpace(functionObject);\n    const name = functionObject.name || `${this.serverless.service.service}_${functionName}`;\n    return `/${namespace}/${name}`\n  }\n\n  calculateFunctionNameSpace(functionObject) {\n    return functionObject.namespace\n      || this.serverless.service.provider.namespace\n      || '_';\n  }\n\n  invokeLocal() {\n    const runtime = this.options.functionObj.runtime\n      || this.serverless.service.provider.runtime\n      || 'nodejs:default';\n    const handler = this.options.functionObj.handler;\n    const handlerPath = handler.split('.')[0];\n    const handlerName = handler.split('.')[1];\n\n    if (runtime.startsWith('nodejs')) {\n      return this.invokeLocalNodeJs(\n        handlerPath,\n        handlerName,\n        this.options.data);\n    } else if (runtime.startsWith('python')) {\n      return this.invokeLocalPython(\n        handlerPath,\n        handlerName,\n        this.options.data);\n    }\n\n    throw new this.serverless.classes\n      .Error('You can only invoke Node.js or Python functions locally.');\n  }\n\n  invokeLocalNodeJs(handlerPath, handlerName, params) {\n    let action, result;\n\n    try {\n      /*\n       * we need require() here to load the handler from the file system\n       * which the user has to supply by passing the function name\n       */\n      action = require(path // eslint-disable-line global-require\n        .join(this.serverless.config.servicePath, handlerPath))[handlerName];\n    } catch (error) {\n      this.serverless.cli.consoleLog(error);\n      process.exit(0);\n    }\n\n    try {\n      let result = action(params)\n      return Promise.resolve(result).then(result => {\n        this.serverless.cli.consoleLog(JSON.stringify(result, null, 4));\n      }).catch(err => {\n        const errorResult = {\n          errorMessage: err.message || err,\n          errorType: err.constructor.name,\n        };\n        this.serverless.cli.consoleLog(chalk.red(JSON.stringify(errorResult, null, 4)));\n        process.exitCode = 1;\n      })\n    } catch (err) {\n      const errorResult = {\n        errorMessage: err.message,\n        errorType: err.constructor.name,\n      };\n      this.serverless.cli.consoleLog(chalk.red(JSON.stringify(errorResult, null, 4)));\n      process.exitCode = 1;\n    }\n  }\n\n  invokeLocalPython(handlerPath, handlerName, params) {\n    if (process.env.VIRTUAL_ENV) {\n      process.env.PATH = `${process.env.VIRTUAL_ENV}/bin:${process.env.PATH}`;\n    }\n    return new BbPromise(resolve => {\n      const python = spawn(\n        path.join(__dirname, 'invoke.py'), [handlerPath, handlerName], { env: process.env });\n      python.stdout.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString()));\n      python.stderr.on('data', (buf) => this.serverless.cli.consoleLog(buf.toString()));\n      python.stdin.write(JSON.stringify(params || {}));\n      python.stdin.end();\n      python.on('close', () => resolve());\n    });\n  }\n}\n\nmodule.exports = OpenWhiskInvokeLocal;\n"
  },
  {
    "path": "invokeLocal/invoke.py",
    "content": "#!/usr/bin/env python\n\nimport argparse\nimport json\nimport sys\nfrom time import time\nfrom importlib import import_module\n\nparser = argparse.ArgumentParser(\n    prog='invoke',\n    description='Runs a Lambda entry point (handler) with an optional event',\n)\n\nparser.add_argument('handler_path',\n                    help=('Path to the module containing the handler function,'\n                          ' omitting \".py\". IE: \"path/to/module\"'))\n\nparser.add_argument('handler_name', help='Name of the handler function')\n\nif __name__ == '__main__':\n    args = parser.parse_args()\n\n    # this is needed because you need to import from where you've executed sls\n    sys.path.append('.')\n\n    module = import_module(args.handler_path.replace('/', '.'))\n    handler = getattr(module, args.handler_name)\n\n    event = json.load(sys.stdin)\n    result = handler(event)\n    sys.stdout.write(json.dumps(result, indent=4))\n"
  },
  {
    "path": "invokeLocal/tests/fixture/handlerWithError.js",
    "content": "'use strict';\n\nmodule.exports.withObj = () => {\n  return { message: 'hello' }\n};\n\nmodule.exports.withError = () => {\n  throw new Error('failed')\n};\n\nmodule.exports.withPromise = () => {\n  return Promise.resolve({ message: 'hello' })\n};\n\nmodule.exports.withRejectedPromise = () => {\n  return Promise.reject(new Error('failed'))\n};\n"
  },
  {
    "path": "invokeLocal/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst path = require('path');\nconst OpenWhiskInvokeLocal = require('../index');\nconst OpenWhiskProvider = require('../../provider/openwhiskProvider');\nconst BbPromise = require('bluebird');\nconst os = require('os');\nconst crypto = require('crypto');\n\nconst getTmpDirPath = () => path.join(os.tmpdir(),\n  'tmpdirs-serverless', 'serverless', crypto.randomBytes(8).toString('hex'));\n\nconst getTmpFilePath = (fileName) => path.join(getTmpDirPath(), fileName);\n\ndescribe('OpenWhiskInvokeLocal', () => {\n  const CLI = function () { this.consoleLog = function () {};};\n  const serverless = {\n    config: () => {},\n    utils: {},\n    pluginManager: { getPlugins: () => []},\n    classes: {Error, CLI},\n    service: {\n      environment: {},\n      getFunction: () => {},\n      provider: {},\n      resources: {},\n      getAllFunctions: () => []\n    }\n  };\n  serverless.setProvider = () => {}\n  const provider = new OpenWhiskProvider(serverless)\n  serverless.getProvider = () => provider;\n\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n    function: 'first',\n  };\n  const openwhiskInvokeLocal = new OpenWhiskInvokeLocal(serverless, options);\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskInvokeLocal.hooks).to.be.not.empty);\n\n    it('should set the provider variable to an instance of OpenWhiskProvider', () =>\n      expect(openwhiskInvokeLocal.provider).to.be.instanceof(OpenWhiskProvider));\n\n    it('should run promise chain in order', () => {\n      const validateStub = sinon\n        .stub(openwhiskInvokeLocal, 'validate').returns(BbPromise.resolve());\n      const loadEnvVarsStub = sinon\n        .stub(openwhiskInvokeLocal, 'loadEnvVars').returns(BbPromise.resolve());\n      const invokeLocalStub = sinon\n        .stub(openwhiskInvokeLocal, 'invokeLocal').returns(BbPromise.resolve());\n\n\n      return openwhiskInvokeLocal.hooks['invoke:local:invoke']().then(() => {\n        expect(validateStub.calledOnce).to.be.equal(true);\n        expect(loadEnvVarsStub.calledAfter(validateStub)).to.be.equal(true);\n        expect(invokeLocalStub.calledAfter(loadEnvVarsStub)).to.be.equal(true);\n\n        openwhiskInvokeLocal.validate.restore();\n        openwhiskInvokeLocal.loadEnvVars.restore();\n        openwhiskInvokeLocal.invokeLocal.restore();\n      });\n    });\n\n    it('should set an empty options object if no options are given', () => {\n      const openwhiskInvokeWithEmptyOptions = new OpenWhiskInvokeLocal(serverless);\n\n      expect(openwhiskInvokeWithEmptyOptions.options).to.deep.equal({});\n    });\n  });\n\n  describe('#validate()', () => {\n    beforeEach(() => {\n      serverless.config.servicePath = true;\n      serverless.service.environment = {\n        vars: {},\n        stages: {\n          dev: {\n            vars: {},\n            regions: {\n              'us-east-1': {\n                vars: {},\n              },\n            },\n          },\n        },\n      };\n      serverless.service.functions = {\n        first: {\n          handler: true,\n        },\n      };\n      openwhiskInvokeLocal.options.data = null;\n      openwhiskInvokeLocal.options.path = false;\n      serverless.service.getFunction = () => serverless.service.functions.first\n    });\n\n    it('should ignore data if it is a simple string', () => {\n      openwhiskInvokeLocal.options.data = 'simple-string';\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal({});\n      });\n    });\n\n    it('should ignore data if it is an array', () => {\n      openwhiskInvokeLocal.options.data = '[]';\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal({});\n      });\n    });\n\n    it('should parse data if it is a json string', () => {\n      openwhiskInvokeLocal.options.data = '{\"key\": \"value\"}';\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal({ key: 'value' });\n      });\n    });\n\n    it('it should parse file if relative file path is provided', () => {\n      serverless.config.servicePath = getTmpDirPath();\n      const data = {\n        testProp: 'testValue',\n      };\n      serverless.utils.fileExistsSync = () => true;\n      serverless.utils.readFileSync = path => JSON.stringify(data);\n      openwhiskInvokeLocal.options.path = 'data.json';\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal(data);\n      });\n    });\n\n    it('it should parse file if absolute file path is provided', () => {\n      serverless.config.servicePath = getTmpDirPath();\n      const data = {\n        event: {\n          testProp: 'testValue',\n        },\n      };\n      serverless.utils.fileExistsSync = () => true;\n      serverless.utils.readFileSync = path => JSON.stringify(data);\n      const dataFile = path.join(serverless.config.servicePath, 'data.json');\n      openwhiskInvokeLocal.options.path = dataFile;\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal(data);\n      });\n    });\n\n    it('it should accept file path containing javascript object', () => {\n      serverless.config.servicePath = getTmpDirPath();\n      const data = {\n        event: {\n          testProp: 'testValue',\n        },\n      };\n      serverless.utils.fileExistsSync = () => true;\n      serverless.utils.readFileSync = path => data;\n      const dataFile = path.join(serverless.config.servicePath, 'data.json');\n      openwhiskInvokeLocal.options.path = dataFile;\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal(data);\n      });\n    });\n\n    it('it should throw error if service path is not set', () => {\n      serverless.config.servicePath = false;\n      expect(() => openwhiskInvokeLocal.validate()).to.throw(Error);\n    });\n\n    it('it should reject error if file path does not exist', () => {\n      serverless.config.servicePath = getTmpDirPath();\n      openwhiskInvokeLocal.options.path = 'some/path';\n\n      return openwhiskInvokeLocal.validate().catch((err) => {\n        expect(err).to.be.instanceOf(Error);\n      });\n    });\n\n    it('should resolve if path is not given', () => {\n      openwhiskInvokeLocal.options.path = false;\n\n      return openwhiskInvokeLocal.validate()\n    });\n\n    it('should use parameters from function object', () => {\n      serverless.service.functions.first.parameters = {\n        foo: 'bar', nums: 1, arr: ['foo', 'bar']\n      }\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal(serverless.service.functions.first.parameters);\n      });\n    });\n\n    it('should merge parameters from function object and data parameters', () => {\n      serverless.service.functions.first.parameters = {\n        foo: 'bar', nums: 1, arr: ['foo', 'bar']\n      }\n      openwhiskInvokeLocal.options.data = JSON.stringify({ foo: 'foo', bar: 'foo' })\n\n      return openwhiskInvokeLocal.validate().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal({\n          foo: 'foo',\n          bar: 'foo',\n          nums: 1,\n          arr: ['foo', 'bar']\n        });\n      });\n    });\n  });\n\n  describe('#loadEnvVars()', () => {\n    beforeEach(() => {\n      serverless.config.servicePath = true;\n      serverless.service.provider = {\n        namespace: 'testing_ns',\n        environment: {\n          providerVar: 'providerValue',\n        },\n      };\n\n      openwhiskInvokeLocal.options = {\n        region: 'us-east-1',\n        functionObj: {\n          name: 'serviceName-dev-hello',\n          environment: {\n            functionVar: 'functionValue',\n          },\n        },\n      };\n      serverless.getProvider()._props = {apihost: 'endpoint', auth: 'user:pass'}\n    });\n\n    it('it should expected env vars', () => openwhiskInvokeLocal\n      .loadEnvVars().then(() => {\n        expect(process.env.__OW_API_KEY).to.equal('user:pass');\n        expect(process.env.__OW_API_HOST).to.equal('endpoint');\n        expect(process.env.__OW_ACTION_NAME).to.equal('/testing_ns/serviceName-dev-hello');\n        expect(process.env.__OW_NAMESPACE).to.equal('testing_ns');\n      })\n    );\n  });\n\n  describe('#mergePackageParams()', () => {\n    beforeEach(() => {\n      serverless.config.servicePath = true;\n      serverless.service.provider = {\n        namespace: 'testing_ns',\n        environment: {\n          providerVar: 'providerValue',\n        },\n      };\n\n      serverless.service.functions.first.name = 'mypackage/first'\n      serverless.service.functions.first.parameters = {\n        foo: 'bar', nums: 1, arr: ['foo', 'bar']\n      }\n      openwhiskInvokeLocal.options = { data: { foo: 'bar', nums: 1, arr: ['foo', 'bar'] }};\n    });\n\n\n    it('it should ignore implicit packages without parameters', () => openwhiskInvokeLocal\n      .mergePackageParams().then(() => {\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal(serverless.service.functions.first.parameters);\n      })\n    );\n\n    it('it should merge implicit packages with parameters', () => { \n      serverless.service.resources.packages = {\n        mypackage: { parameters: { hello: 'world', foo: 'baz' } }\n      }\n      return openwhiskInvokeLocal.mergePackageParams().then(() => {\n        const merged = Object.assign(serverless.service.resources.packages.mypackage.parameters, openwhiskInvokeLocal.options.data)\n        expect(openwhiskInvokeLocal.options.data).to.deep.equal(merged);\n      })\n    });\n  });\n\n  describe('#invokeLocal()', () => {\n    let invokeLocalNodeJsStub, invokeLocalPythonStub;\n\n    beforeEach(() => {\n      invokeLocalNodeJsStub =\n        sinon.stub(openwhiskInvokeLocal, 'invokeLocalNodeJs').returns(BbPromise.resolve());\n      invokeLocalPythonStub =\n        sinon.stub(openwhiskInvokeLocal, 'invokeLocalPython').returns(BbPromise.resolve());\n\n      openwhiskInvokeLocal.serverless.service.service = 'new-service';\n      openwhiskInvokeLocal.options = {\n        stage: 'dev',\n        function: 'first',\n        functionObj: {\n          handler: 'handler.hello',\n          name: 'hello',\n        },\n        data: {},\n      };\n    });\n\n    afterEach(() => {\n      invokeLocalNodeJsStub.restore();\n      invokeLocalPythonStub.restore();\n    });\n\n    it('should call invokeLocalNodeJs when no runtime is set', () => openwhiskInvokeLocal.invokeLocal()\n      .then(() => {\n        expect(invokeLocalNodeJsStub.calledOnce).to.be.equal(true);\n        expect(invokeLocalNodeJsStub.calledWithExactly(\n          'handler',\n          'hello',\n          {}\n        )).to.be.equal(true);\n        openwhiskInvokeLocal.invokeLocalNodeJs.restore();\n      })\n    );\n\n    it('should call invokeLocalNodeJs when nodejs runtime is set', () => {\n      openwhiskInvokeLocal.options.functionObj.runtime = 'nodejs:6';\n      openwhiskInvokeLocal.invokeLocal()\n      .then(() => {\n        expect(invokeLocalNodeJsStub.calledOnce).to.be.equal(true);\n        expect(invokeLocalNodeJsStub.calledWithExactly(\n          'handler',\n          'hello',\n          {}\n        )).to.be.equal(true);\n        openwhiskInvokeLocal.invokeLocalNodeJs.restore();\n      })\n    });\n\n    it('should call invokeLocalPython when python runtime is set', () => {\n      openwhiskInvokeLocal.options.functionObj.runtime = 'python';\n      openwhiskInvokeLocal.invokeLocal()\n      .then(() => {\n        expect(invokeLocalPythonStub.calledOnce).to.be.equal(true);\n        expect(invokeLocalPythonStub.calledWithExactly(\n          'handler',\n          'hello',\n          {}\n        )).to.be.equal(true);\n        openwhiskInvokeLocal.invokeLocalPython.restore();\n      })\n    });\n\n    it('throw error when using invalid runtime', () => {\n      openwhiskInvokeLocal.options.functionObj.runtime = 'wrong';\n      expect(() => openwhiskInvokeLocal.invokeLocal()).to.throw(Error);\n      delete openwhiskInvokeLocal.options.functionObj.runtime;\n    });\n  });\n\n  describe('#invokeLocalNodeJs', () => {\n    beforeEach(() => {\n      openwhiskInvokeLocal.options = {\n        functionObj: {\n          name: '',\n        },\n      };\n\n      serverless.cli = new CLI(serverless);\n      sinon.stub(serverless.cli, 'consoleLog');\n    });\n\n    afterEach(() => {\n      serverless.cli.consoleLog.restore();\n    });\n\n    it('should print message for non-promise return', () => {\n      openwhiskInvokeLocal.serverless.config.servicePath = __dirname;\n      return openwhiskInvokeLocal.invokeLocalNodeJs('fixture/handlerWithError', 'withObj').then(() => {\n        expect(serverless.cli.consoleLog.lastCall.args[0]).to.contain('\"message\": \"hello\"');\n      })\n    });\n\n    it('should print message for promise return', () => {\n      openwhiskInvokeLocal.serverless.config.servicePath = __dirname;\n      return openwhiskInvokeLocal.invokeLocalNodeJs('fixture/handlerWithError', 'withPromise').then(() => {\n        expect(serverless.cli.consoleLog.lastCall.args[0]).to.contain('\"message\": \"hello\"');\n      })\n    });\n\n\n\n    it('should exit with error exit code for thrown errors', () => {\n      process.exitCode = -1\n      openwhiskInvokeLocal.serverless.config.servicePath = __dirname;\n      openwhiskInvokeLocal.invokeLocalNodeJs('fixture/handlerWithError', 'withError');\n\n      expect(process.exitCode).to.be.equal(1);\n      expect(serverless.cli.consoleLog.lastCall.args[0]).to.contain('\"errorMessage\": \"failed\"');\n    });\n\n    it('should exit with error exit code for rejected promises', () => {\n      process.exitCode = -1\n      openwhiskInvokeLocal.serverless.config.servicePath = __dirname;\n      return openwhiskInvokeLocal.invokeLocalNodeJs('fixture/handlerWithError', 'withRejectedPromise').then(() => {\n        expect(process.exitCode).to.be.equal(1);\n        expect(serverless.cli.consoleLog.lastCall.args[0]).to.contain('errorMessage');\n      })\n    });\n  });\n});\n"
  },
  {
    "path": "logs/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst chalk = require('chalk');\nconst moment = require('moment');\nconst path = require('path');\n\nclass OpenWhiskLogs {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options || {};\n    this.provider = this.serverless.getProvider('openwhisk');\n    this.previous_activations = new Set()\n\n    this.hooks = {\n      'logs:logs': () => BbPromise.bind(this)\n        .then(this.validate)\n        .then(this.functionLogs)\n    };\n  }\n\n  validate () {\n  if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service.');\n    }\n\n    this.serverless.service.getFunction(this.options.function);\n\n    this.options.stage = this.options.stage\n      || (this.serverless.service.provider && this.serverless.service.provider.stage)\n      || 'dev';\n    this.options.region = this.options.region\n      || (this.serverless.service.provider && this.serverless.service.provider.region)\n      || 'us-east-1';\n\n    this.options.interval = this.options.interval || 1000;\n\n    if (this.options.filter) {\n      this.options.filter = new RegExp(this.options.filter, 'i');\n    }\n\n    if (this.options.startTime) {\n      this.options.startTime = moment(this.options.startTime)\n    }\n\n    return this.provider.client().then(client => {\n      this.client = client;\n    });\n  }\n\n  functionLogs () {\n    return BbPromise.bind(this)\n        .then(this.retrieveInvocationLogs)\n        .then(this.filterFunctionLogs)\n        .then(this.showFunctionLogs)\n        .then(() => {\n          if (this.options.tail) {\n            this.timeout = setTimeout(() => {\n              this.functionLogs()\n            }, this.options.interval)\n          }\n        })\n  }\n\n  retrieveInvocationLogs () {\n    const functionObject = this.serverless.service.getFunction(this.options.function);\n\n    const options = {\n      docs: true,\n      limit: 100,\n      namespace: '_'\n    };\n\n    return this.client.activations.list(options)\n      .catch(err => {\n        throw new this.serverless.classes.Error(\n          `Failed to retrieve activation logs due to error:`\n          + ` ${err.message}`\n        );\n      });\n  }\n\n  // activation log annotations have a { key: 'path, value: 'namespace/actioname' } member.\n  hasPathAnnotationWithName (annotations, name) {\n    return annotations.filter(an => an.key === 'path')\n      .map(an => an.value.split('/').slice(1).join('/'))\n      .some(value => value === name)\n  }\n\n  filterFunctionLogs (logs) {\n    const functionObject = this.serverless.service.getFunction(this.options.function);\n    const actionName = functionObject.name || `${this.serverless.service.service}_${this.options.function}`\n\n    // skip activations for other actions or that we have seen before \n    const filtered = logs.filter(log => this.hasPathAnnotationWithName((log.annotations || []), actionName)\n      && !this.previous_activations.has(log.activationId))\n\n    // allow regexp filtering of log messages\n    if (this.options.filter) {\n      filtered.forEach(log => {\n        log.logs = log.logs.filter(logLine => logLine.match(this.options.filter))\n      })\n    }\n    \n    // filter those logs based upon start time\n    if (this.options.startTime) {\n      filtered.forEach(log => {\n        log.logs = log.logs.filter(logLine => {\n          const timestamp = logLine.split(\" \")[0]\n          return this.options.startTime.isBefore(moment(timestamp))\n        })\n      })\n    }\n      \n    return BbPromise.resolve(filtered);\n  }\n\n  showFunctionLogs (logs) {\n    if (!this.options.tail && !logs.length) {\n      this.consoleLog(`There's no log data for function \"${this.options.function}\" available right now…`)\n      return BbPromise.resolve();\n    }\n\n    logs.filter(log => log.logs.length)\n      .reverse()\n      .map((log, idx, arr) => {\n        if (this.timeout && idx === 0) console.log('')\n\n        this.previous_activations.add(log.activationId)\n        this.consoleLog(this.formatActivationLine(log))\n        log.logs.map(this.formatLogLine).forEach(this.consoleLog)\n\n        if (idx != (arr.length - 1)) {\n          this.consoleLog('')\n        }\n      })\n    return BbPromise.resolve();\n  }\n\n  formatActivationLine (activation) {\n    return `${chalk.blue('activation')} (${chalk.yellow(activation.activationId)}):`\n  }\n\n  formatLogLine (logLine) {\n    const items = logLine.split(' ').filter(item => item !== '')\n    const format = 'YYYY-MM-DD HH:mm:ss.SSS'\n    const timestamp = chalk.green(moment(items[0]).utc().format(format))\n\n    let contents = items.slice(2).join(' ')\n    if (items[1] === 'stderr:') {\n      contents = chalk.red(contents)\n    }\n\n    return `${timestamp} ${contents}`\n  }\n\n  consoleLog(msg) {\n    console.log(msg); // eslint-disable-line no-console\n  }\n}\n\nmodule.exports = OpenWhiskLogs;\n"
  },
  {
    "path": "logs/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst chaiAsPromised = require('chai-as-promised');\nconst sinon = require('sinon');\nconst path = require('path');\nconst os = require('os');\nconst OpenWhiskLogs = require('../');\nconst BbPromise = require('bluebird');\nconst chalk = require('chalk');\nconst moment = require('moment');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskLogs', () => {\n  let sandbox;\n\n     const CLI = function () { this.log = function () {};};\n  const serverless = {config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: name => (serverless.service.functions[name]), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n    function: 'first',\n  };\n  const openwhiskLogs = new OpenWhiskLogs(serverless, options);\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskLogs.hooks).to.be.not.empty);\n\n    it('should run promise chain in order', () => {\n      const validateStub = sinon\n        .stub(openwhiskLogs, 'validate').returns(BbPromise.resolve());\n      const retrieveInvocationLogsStub = sinon\n        .stub(openwhiskLogs, 'retrieveInvocationLogs').returns(BbPromise.resolve());\n      const filterFunctionLogsStub = sinon\n        .stub(openwhiskLogs, 'filterFunctionLogs').returns(BbPromise.resolve());\n      const showFunctionLogsStub = sinon\n        .stub(openwhiskLogs, 'showFunctionLogs').returns(BbPromise.resolve());\n\n      return openwhiskLogs.hooks['logs:logs']().then(() => {\n        expect(validateStub.calledOnce).to.be.equal(true);\n        expect(retrieveInvocationLogsStub.calledAfter(validateStub)).to.be.equal(true);\n        expect(filterFunctionLogsStub.calledAfter(retrieveInvocationLogsStub)).to.be.equal(true);\n        expect(showFunctionLogsStub.calledAfter(filterFunctionLogsStub)).to.be.equal(true);\n\n        openwhiskLogs.validate.restore();\n        openwhiskLogs.retrieveInvocationLogs.restore();\n        openwhiskLogs.filterFunctionLogs.restore();\n        openwhiskLogs.showFunctionLogs.restore();\n      });\n    });\n  });\n\n  describe('#functionLogs', () => {\n    let clock;\n    beforeEach(() => {\n      openwhiskLogs.serverless.service.functions = {\n        first: {\n          handler: true,\n        },\n      };\n\n      openwhiskLogs.serverless.service.service = 'new-service';\n      openwhiskLogs.options = {\n        function: 'first'\n      };\n\n      clock = sinon.useFakeTimers();\n    });\n\n    afterEach(() => {\n      clock.restore();\n    });\n\n    it('should not tail logs unless option is set', () => {\n      const retrieveInvocationLogsStub = sinon\n        .stub(openwhiskLogs, 'retrieveInvocationLogs').returns(BbPromise.resolve([]));\n\n      return openwhiskLogs.functionLogs().then(() => {\n        expect(clock.timers).to.be.equal(undefined)\n        openwhiskLogs.retrieveInvocationLogs.restore();\n      });\n    })\n\n    it('should support tailing logs', () => {\n      const retrieveInvocationLogsStub = sinon\n        .stub(openwhiskLogs, 'retrieveInvocationLogs').returns(BbPromise.resolve([]));\n      openwhiskLogs.options.tail = true\n      openwhiskLogs.options.interval = 100\n\n      return openwhiskLogs.functionLogs().then(() => {\n        expect(clock.timers['1'].createdAt).to.be.equal(0)\n        expect(clock.timers['1'].delay).to.be.equal(100)\n        openwhiskLogs.retrieveInvocationLogs.restore();\n      });\n    })\n  })\n\n  describe('#retrieveLogs()', () => {\n    let activationsStub, clock;\n    beforeEach(() => {\n      openwhiskLogs.serverless.service.functions = {\n        first: {\n          handler: true,\n        },\n      };\n\n      openwhiskLogs.serverless.service.service = 'new-service';\n      openwhiskLogs.options = {\n        function: 'first'\n      };\n\n      openwhiskLogs.client = { activations: { list: () => {} } };\n      clock = sinon.useFakeTimers();\n    });\n\n    afterEach(() => {\n      activationsStub.restore();\n      clock.restore();\n    });\n\n    it('should invoke with correct params', () => {\n      activationsStub = sinon.stub(openwhiskLogs.client.activations, 'list')\n        .returns(BbPromise.resolve());\n      return openwhiskLogs.retrieveInvocationLogs().then(() => {\n        expect(activationsStub.calledOnce).to.be.equal(true);\n        expect(activationsStub.args[0][0]).to.be.deep.equal({\n          docs: true,\n          limit: 100,\n          namespace: '_'\n        });\n      });\n    }\n    );\n\n    it('should reject when sdk client fails', () => {\n      activationsStub = sinon.stub(openwhiskLogs.client.activations, 'list').returns(BbPromise.reject());\n      return expect(openwhiskLogs.retrieveInvocationLogs()).to.be.eventually.rejected;\n    });\n  });\n\n  describe('#filterFunctionLogs()', () => {\n    beforeEach(() => {\n      openwhiskLogs.serverless.service.functions = {\n        first: {\n          handler: true,\n        },\n      };\n\n      openwhiskLogs.serverless.service.service = 'new-service';\n      openwhiskLogs.options = {\n        function: 'first'\n      };\n    });\n\n    it('should filter out different function logs', () => {\n      const logs = [\n        { name: \"new-service_first\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first'} ] },\n        { name: \"new-service_first\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first'} ] },\n        { name: \"new-service_second\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_second'} ] },\n        { name: \"new-service_third\", annotations: [ {key: \"path\", value: \"user@host.com_dev/new-second_third\"} ] },\n        { name: \"new-service_first\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first'} ] }\n    ]\n      return openwhiskLogs.filterFunctionLogs(logs).then(logs => {\n        expect(logs.length).to.be.equal(3)\n        logs.forEach(log => expect(log.name).to.be.equal('new-service_first'))\n      })\n    });\n\n    it('should filter out different function logs with package function', () => {\n      openwhiskLogs.serverless.service.functions.first.name = 'packagename/funcname'\n      const logs = [\n        { name: \"funcname\", annotations:[{ key:\"path\", value:\"user@host.com_dev/packagename/funcname\"} ]},\n        { name: \"funcname\", annotations:[{ key:\"path\", value:\"user@host.com_dev/packagename/funcname\"} ]},\n        { name: \"new-service_second\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_second'} ] },\n        { name: \"new-service_third\", annotations: [ {key: \"path\", value: \"user@host.com_dev/new-second_third\"} ] },\n        { name: \"funcname\", annotations:[{ key:\"path\", value:\"user@host.com_dev/packagename/funcname\"} ]}\n      ]\n\n      return openwhiskLogs.filterFunctionLogs(logs).then(logs => {\n        expect(logs.length).to.be.equal(3)\n        logs.forEach(log => expect(log.name).to.be.equal('funcname'))\n      })\n    });\n\n\n    it('should filter out logs lines based upon contents', () => {\n      openwhiskLogs.options.startTime = moment('2001-01-01')\n      const logs = [{name: \"new-service_first\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first' }], logs: [\"2001-01-02 matching line\", \"2001-01-01 another matching line\", \"2000-12-31 should not match\"]}]\n      return openwhiskLogs.filterFunctionLogs(logs).then(logs => {\n        expect(logs.length).to.be.equal(1)\n        expect(logs[0].logs).to.be.deep.equal([\"2001-01-02 matching line\"])\n        delete openwhiskLogs.options.startTime\n      })\n    });\n\n    it('should filter out logs lines based upon contents', () => {\n      openwhiskLogs.options.filter = new RegExp('matching', 'i')\n      const logs = [\n        {name: \"new-service_first\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first' }], logs: [\"matching line\", \"another matching line\", \"should not match\"]},\n        {name: \"new-service_first\", annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first' }], logs: [\"does not match\"]}\n      ]\n\n      return openwhiskLogs.filterFunctionLogs(logs).then(logs => {\n        expect(logs.length).to.be.equal(2)\n        expect(logs[0].logs).to.be.deep.equal([\"matching line\", \"another matching line\"])\n        expect(logs[1].logs).to.be.deep.equal([])\n        delete openwhiskLogs.options.filter\n      })\n    });\n\n    it('should filter already seen log messages', () => {\n      openwhiskLogs.previous_activations = new Set([1, 2, 3, 4, 5])\n      const logs = [\n        {activationId: 1, annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first' }], name: \"new-service_first\"},\n        {activationId: 5, annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first' }],  name: \"new-service_first\"},\n        {activationId: 6, annotations: [ {key: \"path\", value: 'user@host.com_dev/new-service_first' }],  name: \"new-service_first\"}\n      ]\n\n      return openwhiskLogs.filterFunctionLogs(logs).then(logs => {\n        expect(logs.length).to.be.equal(1)\n        expect(logs[0].name).to.be.equal('new-service_first')\n        expect(logs[0].activationId).to.be.equal(6)\n      })\n    });\n  });\n\n  describe('#showFunctionLogs()', () => {\n    let logStub\n    beforeEach(() => {\n      openwhiskLogs.serverless.service.functions = {\n        first: {\n          handler: true,\n        },\n      };\n\n      openwhiskLogs.serverless.service.service = 'new-service';\n      openwhiskLogs.options = {\n        function: 'first'\n      };\n    });\n\n    afterEach(() => {\n      logStub.restore();\n    })\n\n    it('should return no logs message for zero activations', () => {\n      logStub = sinon.stub(openwhiskLogs, 'consoleLog')\n\n      return openwhiskLogs.showFunctionLogs([]).then(() => {\n        expect(logStub.calledOnce).to.be.equal(true);\n        expect(logStub.args[0][0]).to.be.deep.equal(`There's no log data for function \"first\" available right now…`);\n      });\n    });\n\n    it('should return log messages for activations', () => {\n      logStub = sinon.stub(openwhiskLogs, 'consoleLog')\n      openwhiskLogs.previous_activations = new Set()\n      const activation = { activationId: 12345, logs: [\n        \"2016-11-21T11:08:05.980285407Z stdout: this is the message\",\n        \"2016-11-21T11:08:05.980285407Z stderr: this is an error\"\n      ]}\n\n      return openwhiskLogs.showFunctionLogs([activation]).then(() => {\n        expect(logStub.calledThrice).to.be.equal(true);\n        expect(logStub.args[0][0]).to.be.deep.equal(`${chalk.blue('activation')} (${chalk.yellow(12345)}):`);\n        expect(logStub.args[1][0]).to.be.deep.equal(`${chalk.green('2016-11-21 11:08:05.980')} this is the message`);\n        expect(logStub.args[2][0]).to.be.deep.equal(`${chalk.green('2016-11-21 11:08:05.980')} ${chalk.red('this is an error')}`);\n        expect(openwhiskLogs.previous_activations.size).to.be.equal(1)\n        expect(openwhiskLogs.previous_activations.has(12345)).to.be.equal(true)\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"serverless-openwhisk\",\n  \"version\": \"0.18.4\",\n  \"description\": \"OpenWhisk support for the Serverless Framework\",\n  \"main\": \"index.js\",\n  \"directories\": {\n    \"test\": \"tests\"\n  },\n  \"scripts\": {\n    \"test\": \"istanbul cover -x '**/*.test.js' node_modules/mocha/bin/_mocha tests/all -- -R spec --recursive\",\n    \"report\": \"istanbul report lcovonly && codecov\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/serverless/serverless-openwhisk.git\"\n  },\n  \"keywords\": [\n    \"serverless\",\n    \"framework\",\n    \"openwhisk\"\n  ],\n  \"author\": \"James Thomas <james@jamesthom.as>\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/serverless/serverless-openwhisk/issues\"\n  },\n  \"engines\": {\n    \"node\": \">=6.0\"\n  },\n  \"homepage\": \"https://github.com/serverless/serverless-openwhisk#readme\",\n  \"dependencies\": {\n    \"@ibm-functions/iam-token-manager\": \"^1.0.3\",\n    \"bluebird\": \"^3.4.6\",\n    \"chalk\": \"^1.1.3\",\n    \"fs-extra\": \"^1.0.0\",\n    \"get-stdin\": \"^5.0.1\",\n    \"jszip\": \"^3.1.3\",\n    \"jws\": \"^3.2.2\",\n    \"lodash\": \"^4.17.11\",\n    \"moment\": \"^2.16.0\",\n    \"openwhisk\": \"^3.19.0\"\n  },\n  \"devDependencies\": {\n    \"chai\": \"^3.5.0\",\n    \"chai-as-promised\": \"^6.0.0\",\n    \"coveralls\": \"^3.0.3\",\n    \"istanbul\": \"^0.4.4\",\n    \"mocha\": \"^6.1.2\",\n    \"mocha-lcov-reporter\": \"^1.2.0\",\n    \"mock-require\": \"^1.3.0\",\n    \"proxyquire\": \"^1.7.10\",\n    \"sinon\": \"^1.17.5\"\n  }\n}\n"
  },
  {
    "path": "provider/cliTokenManager.js",
    "content": "'use strict';\n\n\"use strict\";\n\nconst jws = require('jws');\nconst { exec } = require('child_process');\nconst { readFileSync } = require('fs');\nconst path = require('path');\n\n// Configuration file location for IBM Cloud CLI.\n// This will contain the current IAM tokens for the user.\nconst DEFAULT_CONFIG_LOCATION = `.bluemix/config.json`\n\n// This class handles retrieving authentication tokens for IAM namespaces on IBM Cloud Functions.\n// Tokens are parsed from the configuration file used by the IBM Cloud CLI.\n// If tokens have expired, the CLI command `ibmcloud iam oauth-tokens` is executed.\n// This will automatically refresh the tokens in the configuration.\nmodule.exports = class CliTokenManager {\n  constructor(_exec = exec, _readFile = readFileSync) {\n    this.exec = _exec\n    this.readFile = _readFile\n    this.refresh_command = 'ibmcloud iam oauth-tokens'\n  }\n\n  getAuthHeader () {\n    const to_header = token => `Bearer ${token}`\n    const token = this.readTokenFromConfig()\n    if (this.isTokenExpired(token)) {\n      return this.refreshToken().then(to_header)\n    }\n\n    return Promise.resolve(to_header(token))\n  }\n\n  refreshToken () {\n    return new Promise((resolve, reject) => {\n      this.exec(this.refresh_command, error => {\n        if (error) {\n          const err_message = `IAM token from IBM Cloud CLI configuration file (.bluemix/config.json) has expired. `\n            + `Refresh failed using CLI command (ibmcloud iam oauth-tokens). Check error message for details: ${error}`\n          return reject(new Error(err_message))\n        }\n        resolve(this.readTokenFromConfig())\n      });\n    })\n  }\n\n  // IAM Tokens stored under the IAMToken field in configuration.\n  readTokenFromConfig (configPath = CliTokenManager.configFilePath()) {\n    const contents = this.readFile(configPath, 'utf-8')\n    const config = JSON.parse(contents)\n    const [prefix, token] = config.IAMToken.split(' ')\n    return token\n  }\n\n  isTokenExpired (token) {\n    const decoded = jws.decode(token, { json: true })\n    const expiry_time = decoded.payload.exp\n    const now = Math.floor(Date.now() / 1000)\n\n    return expiry_time <= now\n  }\n\n  // Support both platforms for configuration files.\n  static configFilePath (config_file = DEFAULT_CONFIG_LOCATION) {\n    const home_dir = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];\n    const config_path = path.format({ dir: home_dir, base: config_file });\n    return config_path\n  }\n}\n"
  },
  {
    "path": "provider/credentials.js",
    "content": "'use strict';\n\nconst path = require('path');\nconst fs = require('fs-extra');\n\nconst ENV_PARAMS = ['OW_APIHOST', 'OW_AUTH', 'OW_NAMESPACE', 'OW_APIGW_ACCESS_TOKEN', 'OW_IAM_NAMESPACE_API_KEY'];\n\nfunction getWskPropsFile() {\n  const Home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];\n  return process.env.WSK_CONFIG_FILE || path.format({ dir: Home, base: '.wskprops' });\n}\n\nfunction readWskPropsFile() {\n  const wskFilePath = getWskPropsFile();\n\n  return new Promise(resolve => {\n    fs.readFile(wskFilePath, 'utf8', (err, data) => {\n      resolve(err ? '' : data);\n    });\n  });\n}\n\nfunction getWskProps() {\n  return readWskPropsFile().then(data => {\n    if (!data) return {};\n\n    const wskProps = data.trim().split('\\n')\n    .map(line => line.split('='))\n    .reduce((params, keyValue) => {\n      params[keyValue[0].toLowerCase()] = keyValue[1]; // eslint-disable-line no-param-reassign\n      return params;\n    }, {});\n\n    return wskProps;\n  });\n}\n\nfunction getWskEnvProps() {\n  const envProps = {};\n  ENV_PARAMS.forEach((envName) => {\n    if (process.env[envName]) envProps[envName.slice(3).toLowerCase()] = process.env[envName];\n  });\n  return envProps;\n}\n\nmodule.exports = {\n  getWskProps() {\n    return getWskProps()\n      .then(props => Object.assign(props, getWskEnvProps()));\n  },\n  ENV_PARAMS,\n};\n"
  },
  {
    "path": "provider/openwhiskProvider.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst openwhisk = require('openwhisk')\nconst IamTokenManager = require('@ibm-functions/iam-token-manager')\nconst CliTokenManager = require('./cliTokenManager')\nconst Credentials = require('./credentials');\n\nconst constants = {\n  providerName: 'openwhisk',\n};\n\nconst credentials = ['apihost', 'auth'];\n\nclass OpenwhiskProvider {\n  static getProviderName() {\n    return constants.providerName;\n  }\n\n  constructor(serverless) {\n    this.serverless = serverless;\n    this.provider = this;\n    this.serverless.setProvider(constants.providerName, this);\n    this.sdk = openwhisk\n  }\n\n  // Returns OpenWhisk SDK client configured with authentication credentials.\n  // Auto-detects use of IAM namespaces when using IBM Cloud Functions and adds\n  // external auth handler to client.\n  client() {\n    if (this._client) return BbPromise.resolve(this._client)\n\n    const ignore_certs = this.serverless.service.provider.ignore_certs || false\n    return this.props().then(props => {\n      if (props.hasOwnProperty('iam_namespace_api_key')) {\n        const auth_handler = new IamTokenManager({ iamApikey: props.iam_namespace_api_key });\n        this._client = openwhisk({ apihost: props.apihost, auth_handler, namespace: props.namespace });\n      } else if (this.isIBMCloudIAMProps(props)) {\n        const auth_handler = new CliTokenManager()\n        this._client = openwhisk({ apihost: props.apihost, auth_handler, namespace: props.namespace });\n      } else {\n        this.hasValidCreds(props)\n        this._client = openwhisk({ apihost: props.apihost, api_key: props.auth, namespace: props.namespace, ignore_certs, apigw_token: props.apigw_access_token });\n      }\n\n      return this._client\n    })\n  }\n\n  props() {\n    if (this._props) return BbPromise.resolve(this._props)\n\n    return Credentials.getWskProps().then(wskProps => {\n      this._props = wskProps;\n      return this._props;\n    })\n  }\n\n  hasValidCreds(creds) {\n    credentials.forEach(prop => {\n      if (!creds[prop]) {\n        throw new Error(`Missing mandatory openwhisk configuration property: OW_${prop.toUpperCase()}.` +\n          ' Check .wskprops file or set environment variable?');\n      }\n    });\n    return creds;\n  }\n\n  // Auto-detect whether ~/.wskprops uses IBM Cloud IAM namespace (and therefore requires IAM auth handler).\n  // Namespace will be IAM NS ID rather than default namespace. Api host will end with ibm.com hostname.\n  isIBMCloudIAMProps (props) {\n    return props.namespace !== '_' && props.apihost.endsWith('cloud.ibm.com')\n  }\n}\n\nmodule.exports = OpenwhiskProvider;\n"
  },
  {
    "path": "provider/tests/cliTokenManager.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst fs = require('fs-extra');\nconst chaiAsPromised = require('chai-as-promised');\nconst CliTokenManager = require('../cliTokenManager.js');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('CliTokenManager', () => {\n  describe('#getAuthHeader()', () => {\n    it('should return bearer token from configuration', () => {\n      const cliTokenManager = new CliTokenManager()\n      const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM'\n      cliTokenManager.readTokenFromConfig = () => token\n      cliTokenManager.isTokenExpired = () => false\n      const header = `Bearer ${token}`\n      return cliTokenManager.getAuthHeader().then(result => {\n        expect(result).to.equal(header);\n      })\n    });\n\n    it('should return refreshed bearer token when token is expired', () => {\n      const cliTokenManager = new CliTokenManager()\n      const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM'\n      cliTokenManager.readTokenFromConfig = () => null\n      cliTokenManager.isTokenExpired = () => true \n      cliTokenManager.refreshToken = () => Promise.resolve(token)\n      const header = `Bearer ${token}`\n      return cliTokenManager.getAuthHeader().then(result => {\n        expect(result).to.equal(header);\n      })\n    });\n  })\n\n  describe('#readTokenFromConfig()', () => {\n    it('should return bearer token from default configuration file', () => {\n      const readFile = (path, format) => {\n        expect(path).to.equal(config_path)\n        expect(format).to.equal('utf-8')\n        return JSON.stringify({ IAMToken: `Bearer ${config_token}`}) \n      }\n\n      const cliTokenManager = new CliTokenManager(null, readFile)\n      const config_token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM'\n      const config_path = `~/.bluemix/config.json`\n      const token = cliTokenManager.readTokenFromConfig(config_path)\n      expect(token).to.equal(config_token)\n    });\n  });\n\n  describe('#isTokenExpired()', () => {\n    it('should return true for expired JWT tokens', () => {\n      const cliTokenManager = new CliTokenManager()\n      // created from http://jwtbuilder.jamiekurtz.com/\n      // JWT expired in 2000.\n      const expired_token = 'eyJraWQiOiIyMDE5MDIwNCIsImFsZyI6IlJTMjU2In0.eyJpYW1faWQiOiJJQk1pZC0yNzAwMDJQUzIxIiwiaWQiOiJJQk1pZC0yNzAwMDJQUzIxIiwicmVhbG1pZCI6IklCTWlkIiwiaWRlbnRpZmllciI6IjI3MDAwMlBTMjEiLCJnaXZlbl9uYW1lIjoiSmFtZXMiLCJmYW1pbHlfbmFtZSI6IlRob21hcyIsIm5hbWUiOiJKYW1lcyBUaG9tYXMiLCJlbWFpbCI6ImphbWVzLnRob21hc0B1ay5pYm0uY29tIiwic3ViIjoiamFtZXMudGhvbWFzQHVrLmlibS5jb20iLCJhY2NvdW50Ijp7InZhbGlkIjp0cnVlLCJic3MiOiI4ZDYzZmIxY2M1ZTk5ZTg2ZGQ3MjI5ZGRkZmExNjY0OSJ9LCJpYXQiOjE1NjM0NDAyMzEsImV4cCI6MTU2MzQ0MzgzMSwiaXNzIjoiaHR0cHM6Ly9pYW0uY2xvdWQuaWJtLmNvbS9pZGVudGl0eSIsImdyYW50X3R5cGUiOiJwYXNzd29yZCIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4IiwiYWNyIjoxLCJhbXIiOlsicHdkIl19.DhgBTV_dxtSirpSoe-H_xXfxBKYIrxFqiu4eVluTq78Sqp9FCCQoMSuJBD0ysHsD-0sIp5yHq03-0DnAdldnD2YkFRwrDXY-9uG5cJGB1vH3l6X6BaWprGG-AcswqeTklnjCrRqIiUr5EU9odZAfwbDPYdoE21gudS2kMZoVgezJsUtYz2tJH-I-1JfbBPuTLLuhWVr4ZPP2GzOvI7xpWBVwMYmUviLrxD_-Gq2vJyly1rNBYA4VZKf1G46yT790EqRz9N3o18bmKUxDCP6ur2oVHwGNQy15fn8LsiylHf4s9p9yPuLtgExN6FcdMfPU8hUT1UWfaWssjpetk3crjA'\n      const expired = cliTokenManager.isTokenExpired(expired_token)\n      expect(expired).to.equal(true)\n    });\n    \n    it('should return false for non-expired JWT tokens', () => {\n      const cliTokenManager = new CliTokenManager()\n      // created from http://jwtbuilder.jamiekurtz.com/ - example JWT expires in 2100.\n      // I won't be around when this unit test starts failing...\n      const expired_token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NjM0NTM2OTYsImV4cCI6NDExOTUxMTI5NiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.WNqaMqKIqkKXT731uGV8jnJmNj74qYUSiZeLLYl6ME0'\n      const expired = cliTokenManager.isTokenExpired(expired_token)\n      expect(expired).to.equal(false)\n    });\n  });\n\n  describe('#configFilePath()', () => {\n    it('should return default config location', () => {\n      const default_path = `${process.env['HOME']}/.bluemix/config.json`\n      expect(CliTokenManager.configFilePath()).to.equal(default_path)\n    });\n  });\n\n  describe('#refreshToken()', () => {\n    it('should return current token once command has executed', () => {\n      const cliTokenManager = new CliTokenManager()\n      const token = 'eyj0exaioijkv1qilcjhbgcioijiuzi1nij9.eyj1c2vyswqioijimdhmodzhzi0znwrhltq4zjitogzhyi1jzwyzota0njywymqifq.-xn_h82phvtcma9vdohrczxh-x5mb11y1537t3rgzcm'\n      cliTokenManager.readTokenFromConfig = () => token\n      cliTokenManager.exec = (cmd, cb) => {\n        expect(cmd).to.equal(cliTokenManager.refresh_command)\n        setTimeout(() => cb(), 0)\n      }\n     \n      return cliTokenManager.refreshToken().then(_token => {\n        expect(_token).to.equal(token)\n      })\n    });\n\n    it('should throw error when refresh token command fails', () => {\n      const cliTokenManager = new CliTokenManager()\n      cliTokenManager.exec = (_, cb) => {\n        setTimeout(() => cb(new Error(\"cmd failed\")), 0)\n      }\n     \n      return expect(cliTokenManager.refreshToken()).to.eventually.be.rejectedWith(/^IAM token from IBM Cloud CLI/);\n    });\n  });\n});\n"
  },
  {
    "path": "provider/tests/credentials.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst fs = require('fs-extra');\nconst chaiAsPromised = require('chai-as-promised');\nconst Credentials = require('../credentials');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('#getWskProps()', () => {\n  let sandbox;\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    ['WSK_CONFIG_FILE', ...Credentials.ENV_PARAMS].forEach(param => process.env[param] = '');\n    sandbox.restore();\n  });\n\n  describe('should instantiate openwhisk resources from the properties file', () => {\n    const mockObject = {\n      apihost: 'openwhisk.ng.bluemix.net',\n      auth: 'user:pass',\n      namespace: 'blah@provider.com_dev',\n      apigw_access_token: 'blahblahblahkey1234',\n      iam_namespace_api_key: 'some-api-key-value',\n    };\n\n    const wskProps =\n      'APIHOST=openwhisk.ng.bluemix.net\\nNAMESPACE=blah@provider.com_dev\\n'  +\n      'AUTH=user:pass\\nAPIGW_ACCESS_TOKEN=blahblahblahkey1234\\nIAM_NAMESPACE_API_KEY=some-api-key-value';\n\n    it('when the default is used', () => {\n      const home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];\n\n      sandbox.stub(fs, 'readFile', (path, encoding, cb) => {\n        expect(path.match(home).length).to.equal(1);\n        expect(path.match('.wskprops').length).to.equal(1);\n        cb(null, wskProps);\n      });\n\n      return expect(Credentials.getWskProps()).to.eventually.deep.equal(mockObject);\n    });\n\n    it('when a path is specified', () => {\n      const propsFilePath = '/different/place/file_name';\n      process.env.WSK_CONFIG_FILE = propsFilePath;\n\n      sandbox.stub(fs, 'readFile', (path, encoding, cb) => {\n        expect(path).to.equal(propsFilePath);\n        cb(null, wskProps);\n      });\n\n      return expect(Credentials.getWskProps()).to.eventually.deep.equal(mockObject);\n    });\n  });\n\n  it('should instantiate openwhisk resources from environment variables', () => {\n    const mockObject = {\n      apihost: 'blah.blah.com',\n      auth: 'another_user:another_pass',\n      namespace: 'user@user.com',\n      apigw_access_token: 'some_access_token',\n      iam_namespace_api_key: 'some_api_key'\n    };\n\n    sandbox.stub(fs, 'readFile', (path, encoding, cb) => {\n      cb(true);\n    });\n\n    process.env.OW_APIHOST = 'blah.blah.com';\n    process.env.OW_AUTH = 'another_user:another_pass';\n    process.env.OW_NAMESPACE = 'user@user.com';\n    process.env.OW_APIGW_ACCESS_TOKEN = 'some_access_token';\n    process.env.OW_IAM_NAMESPACE_API_KEY = 'some_api_key';\n\n    return expect(Credentials.getWskProps()).to.eventually.deep.equal(mockObject);\n  });\n\n  it('should overwrite properties files resource variables with environment variables', () => {\n    const mockObject = {\n      apihost: 'blah.blah.com',\n      auth: 'another_user:another_pass',\n      namespace: 'user@user.com',\n      apigw_access_token: 'some_access_token',\n      iam_namespace_api_key: 'some_api_key'\n    };\n\n    const wskProps =\n      'APIHOST=openwhisk.ng.bluemix.net\\nNAMESPACE=blah@provider.com_dev\\nAUTH=user:pass\\nAPIGW_ACCESS_TOKEN=hello\\nIAM_NAMESPACE_API_KEY=old_key';\n\n    sandbox.stub(fs, 'readFile', (path, encoding, cb) => {\n      cb(null, wskProps);\n    });\n\n    process.env.OW_APIHOST = 'blah.blah.com';\n    process.env.OW_AUTH = 'another_user:another_pass';\n    process.env.OW_NAMESPACE = 'user@user.com';\n    process.env.OW_APIGW_ACCESS_TOKEN = 'some_access_token';\n    process.env.OW_IAM_NAMESPACE_API_KEY = 'some_api_key';\n\n    return expect(Credentials.getWskProps()).to.eventually.deep.equal(mockObject);\n  });\n});\n"
  },
  {
    "path": "provider/tests/index.js",
    "content": "'use strict';\n\nrequire('./openwhiskProvider');\nrequire('./credentials');\nrequire('./cliTokenManager');\n"
  },
  {
    "path": "provider/tests/openwhiskProvider.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst openwhisk = require('openwhisk');\nconst chaiAsPromised = require('chai-as-promised');\nconst BbPromise = require('bluebird');\n\nrequire('chai').use(chaiAsPromised);\n\nconst OpenwhiskProvider = require('../openwhiskProvider');\nconst Credentials = require('../credentials');\nconst CliTokenManager = require('../cliTokenManager.js');\n\ndescribe('OpenwhiskProvider', () => {\n  let openwhiskProvider;\n  let serverless;\n  let sandbox;\n\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n  };\n\n  beforeEach(() => {\n    sandbox = sinon.sandbox.create();\n    const CLI = function () { this.log = function () {};};\n    const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n    openwhiskProvider = new OpenwhiskProvider(serverless, options);\n    openwhiskProvider.serverless.cli = new serverless.classes.CLI();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#getProviderName()', () => {\n    it('should return the provider name', () => {\n      expect(OpenwhiskProvider.getProviderName()).to.equal('openwhisk');\n    });\n  });\n\n  describe('#constructor()', () => {\n    it('should set Serverless instance', () => {\n      expect(typeof openwhiskProvider.serverless).to.not.equal('undefined');\n    });\n\n    it('should set OpenWhisk instance', () => {\n      expect(typeof openwhiskProvider.sdk).to.not.equal('undefined');\n    });\n\n    it('should set the provider property', () => {\n      expect(openwhiskProvider.provider).to.equal(openwhiskProvider);\n    });\n  });\n\n  describe('#client()', () => {\n    it('should return pre-configured openwhisk client', () => {\n      openwhiskProvider._client = null\n      const creds = {apihost: 'some_api', auth: 'user:pass'}\n      sandbox.stub(openwhiskProvider, \"props\").returns(BbPromise.resolve(creds))\n      return openwhiskProvider.client().then(client => {\n        expect(client.actions.client.options).to.be.deep.equal({apigwToken: undefined, apigwSpaceGuid: undefined, namespace: undefined, apiKey: creds.auth, ignoreCerts: false, apiVersion: 'v1', cert: undefined, key: undefined, api: `https://${creds.apihost}/api/v1/`, authHandler: undefined, noUserAgent: undefined})\n        expect(typeof openwhiskProvider._client).to.not.equal('undefined');\n      })\n    })\n\n    it('should allow ignore_certs options for openwhisk client', () => {\n      openwhiskProvider._client = null\n      const creds = {apihost: 'some_api', auth: 'user:pass'}\n      sandbox.stub(openwhiskProvider, \"props\").returns(BbPromise.resolve(creds))\n      openwhiskProvider.serverless.service.provider.ignore_certs = true\n      return openwhiskProvider.client().then(client => {\n        expect(client.actions.client.options).to.be.deep.equal({apigwToken: undefined, apigwSpaceGuid: undefined, namespace: undefined, apiKey: creds.auth, ignoreCerts: true, apiVersion: 'v1', cert: undefined, key: undefined, api: `https://${creds.apihost}/api/v1/`, authHandler: undefined, noUserAgent: undefined})\n        expect(typeof openwhiskProvider._client).to.not.equal('undefined');\n      })\n    })\n\n    it('should allow apigw_access_token option for openwhisk client', () => {\n      openwhiskProvider._client = null\n      const creds = {apihost: 'some_api', auth: 'user:pass', apigw_access_token: 'token'}\n      sandbox.stub(openwhiskProvider, \"props\").returns(BbPromise.resolve(creds))\n      return openwhiskProvider.client().then(client => {\n        expect(client.actions.client.options).to.be.deep.equal({apigwToken: 'token', apigwSpaceGuid: 'user', namespace: undefined, apiKey: creds.auth, ignoreCerts: false, apiVersion: 'v1', cert: undefined, key: undefined, api: `https://${creds.apihost}/api/v1/`, authHandler: undefined, noUserAgent: undefined})\n        expect(typeof openwhiskProvider._client).to.not.equal('undefined');\n      })\n    })\n\n    it('should cache client instance', () => {\n      openwhiskProvider._client = {}\n      return openwhiskProvider.client().then(client => {\n        expect(client).to.be.equal(openwhiskProvider._client)\n      })\n    })\n\n    it('should support client auth using IBM Cloud IAM API key', () => {\n      openwhiskProvider._client = null\n      const API_KEY = 'some-key-value';\n      const creds = {iam_namespace_api_key: API_KEY, apihost: 'some_api', namespace: 'a34dd39e-e3de-4160-bbab-59ac345678ed'}\n      sandbox.stub(openwhiskProvider, \"props\").returns(BbPromise.resolve(creds))\n\n      return openwhiskProvider.client().then(client => {\n        expect(client.actions.client.options.namespace).to.be.deep.equal(creds.namespace)\n        expect(client.actions.client.options.api).to.be.deep.equal(`https://${creds.apihost}/api/v1/`)\n        expect(typeof client.actions.client.options.authHandler).to.not.equal('undefined')\n        expect(client.actions.client.options.authHandler.iamApikey).to.be.deep.equal(API_KEY)\n      })\n    })\n\n    it('should support client auth using IBM Cloud CLI configuration file', () => {\n      openwhiskProvider._client = null\n      const API_KEY = 'some-key-value';\n      const creds = {apihost: 'region.functions.cloud.ibm.com', namespace: 'a34dd39e-e3de-4160-bbab-59ac345678ed'}\n      sandbox.stub(openwhiskProvider, \"props\").returns(BbPromise.resolve(creds))\n\n      return openwhiskProvider.client().then(client => {\n        expect(client.actions.client.options.namespace).to.be.deep.equal(creds.namespace)\n        expect(client.actions.client.options.api).to.be.deep.equal(`https://${creds.apihost}/api/v1/`)\n        expect(client.actions.client.options.authHandler instanceof CliTokenManager).to.be.equal(true)\n      })\n    })\n  })\n\n  describe('#props()', () => {\n    it('should return promise that resolves with provider credentials', () => {\n      openwhiskProvider._props = null\n      const creds = {apihost: 'some_api', auth: 'user:pass', namespace: 'namespace'}\n      sandbox.stub(Credentials, \"getWskProps\").returns(BbPromise.resolve(creds))\n      return openwhiskProvider.props().then(props => {\n        expect(props).to.be.deep.equal({auth: creds.auth, namespace: creds.namespace, apihost: creds.apihost})\n        expect(typeof openwhiskProvider._props).to.not.equal('undefined');\n      })\n    });\n\n    it('should return cached provider credentials', () => {\n      openwhiskProvider._props = {}\n      const stub = sandbox.stub(Credentials, \"getWskProps\")\n      return openwhiskProvider.props().then(props => {\n        expect(props).to.be.equal(openwhiskProvider._props)\n        expect(stub.called).to.be.equal(false)\n      })\n    });\n\n    it('should reject promise when getWskProps rejects', () => {\n      sandbox.stub(Credentials, \"getWskProps\").returns(BbPromise.reject())\n      return expect(openwhiskProvider.props()).to.eventually.be.rejected;\n    });\n  });\n\n  describe('#hasValidCreds()', () => {\n    it('should throw error when parameter (AUTH) is missing', () => {\n      const mockObject = {\n        apihost: 'blah.blah.com', namespace: 'user@user.com',\n      };\n\n      return expect(() => openwhiskProvider.hasValidCreds(mockObject)).to.throw(/OW_AUTH/);\n    });\n\n    it('should throw error when parameter (APIHOST) is missing', () => {\n      const mockObject = {\n        auth: 'user:pass', namespace: 'user@user.com',\n      };\n\n      return expect(() => openwhiskProvider.hasValidCreds(mockObject)).to.throw(/OW_APIHOST/);\n    });\n  })\n})\n"
  },
  {
    "path": "remove/README.md",
    "content": "# Remove\n\nThis plugin removes the Action from OpenWhisk.\n\n## How it works\n\n`Remove` hooks into the [`remove:remove`](/lib/plugins/remove) lifecycle. It\nwill send the HTTP DELETE request to the Action endpoint to trigger the function\nremoval.\n"
  },
  {
    "path": "remove/index.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst validate = require('./lib/validate');\nconst removePackages = require('./lib/removePackages');\nconst removeFunctions = require('./lib/removeFunctions');\nconst removeTriggers = require('./lib/removeTriggers');\nconst removeRules = require('./lib/removeRules');\nconst removeFeeds = require('./lib/removeFeeds');\nconst removeRoutes = require('./lib/removeRoutes');\nconst setupResources = require('./lib/setupResources');\nconst util = require('./lib/util');\n\nclass OpenWhiskRemove {\n  constructor(serverless, options) {\n    this.serverless = serverless;\n    this.options = options || {};\n    this.provider = this.serverless.getProvider('openwhisk');\n\n    Object.assign(this, validate, setupResources, removePackages, removeFunctions,\n      removeTriggers, removeRules, removeFeeds, removeRoutes, util);\n\n    this.hooks = {\n      'remove:remove': () => BbPromise.bind(this)\n          .then(this.validate)\n          .then(this.setupResources)\n          .then(this.removeRoutes)\n          .then(this.removeRules)\n          .then(this.removeFunctions)\n          .then(this.removePackages)\n          .then(this.removeTriggers)\n          .then(this.removeFeeds)\n          .then(() => this.serverless.cli.log('Resource removal successful!')),\n    };\n  }\n}\n\nmodule.exports = OpenWhiskRemove;\n"
  },
  {
    "path": "remove/lib/removeFeeds.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  removeFeed (feed) {\n    const onProvider = ow => ow.feeds.delete(feed);\n    const errMsgTemplate =\n      `Failed to remove feed (${feed.feedName}) due to error:`\n    return this.handleOperationFailure(onProvider, errMsgTemplate);\n  },\n\n  removeTriggerFeed(triggerName, params) {\n    return this.provider.props().then(props => {\n      const triggerNamespace = params.namespace\n        || `${props.namespace}`;\n\n      const trigger = `/${triggerNamespace}/${triggerName}`;\n\n      // split feed identifier into namespace & name\n      const feedPathParts = params.feed.split('/').filter(i => i);\n      const namespace = feedPathParts.splice(0, 1).join();\n      const feedName = feedPathParts.join('/');\n      return this.removeFeed({ trigger, feedName, namespace });\n    });\n  },\n\n  removeFeeds() {\n    const resources = this.serverless.service.resources;\n\n    if (!resources || !resources.triggers) {\n      return BbPromise.resolve();\n    }\n\n    const triggers = resources.triggers;\n    const feeds = Object.keys(triggers).filter(t => triggers[t].feed)\n\n    if (feeds.length) {\n      this.serverless.cli.log('Removing Feeds...');\n    }\n\n    return BbPromise.all(\n      feeds.map(t => this.removeTriggerFeed(t, triggers[t]))\n    );\n  },\n};\n"
  },
  {
    "path": "remove/lib/removeFunctions.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  removeFunctionHandler(functionHandler) {\n    const onProvider = ow => ow.actions.delete(functionHandler);\n    const errMsgTemplate =\n      `Failed to delete function service (${functionHandler.actionName}) due to error:`;\n    return this.handleOperationFailure(onProvider, errMsgTemplate);\n  },\n\n  removeFunction(functionName) {\n    const functionObject = this.serverless.service.getFunction(functionName);\n    const FunctionHandler = {};\n\n    FunctionHandler.actionName = functionObject.name\n      || `${this.serverless.service.service}_${functionName}`;\n\n    if (functionObject.namespace) {\n      FunctionHandler.namespace = functionObject.namespace;\n    }\n\n    return this.removeFunctionHandler(FunctionHandler);\n  },\n\n  removeFunctions() {\n    this.serverless.cli.log('Removing Functions...');\n\n    return BbPromise.all(\n      this.serverless.service.getAllFunctions().map(f => this.removeFunction(f))\n    );\n  },\n};\n"
  },
  {
    "path": "remove/lib/removePackages.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  removePackageHandler(pkge) {\n    const onProvider = ow => ow.packages.delete(pkge);\n    const errMsgTemplate =\n      `Failed to delete package (${pkge.name}) due to error:`;\n    return this.handleOperationFailure(onProvider, errMsgTemplate);\n  },\n\n  removePackage(name) {\n    const packageObject = this.serverless.service.resources.packages[name];\n    const pkge = { name };\n\n    if (packageObject.namespace) {\n      pkge.namespace = packageObject.namespace;\n    }\n\n    return this.removePackageHandler(pkge);\n  },\n\n  removePackages() {\n    const resources = this.serverless.service.resources;\n\n    if (!resources || !resources.packages) {\n      return BbPromise.resolve();\n    }\n\n    const packages = Object.keys(resources.packages)\n\n    if (packages.length) {\n      this.serverless.cli.log('Removing Packages...');\n    }\n\n    return BbPromise.all(\n      packages.map(p => this.removePackage(p))\n    );\n  }\n};\n"
  },
  {
    "path": "remove/lib/removeRoutes.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  hasRoutes() {\n    return this.serverless.service.getAllFunctions()\n      .map(f => this.serverless.service.getFunction(f))\n      .some(fnObj => {\n        const events = fnObj.events || []\n        return events.some(event => event.http)\n      })\n  },\n\n  basePath() {\n    const resources = this.serverless.service.resources || {}\n    const options = resources.apigw || {}\n    return options.basepath || \"/\"\n  },\n\n  removeRoutes() {\n    if (!this.hasRoutes()) {\n      return Promise.resolve();\n    }\n\n    this.serverless.cli.log('Removing API Gateway definitions...');\n\n    const basepath = this.basePath()\n    const onProvider = ow => ow.routes.delete({ basepath });\n    const errMsgTemplate = `Failed to unbind API Gateway routes (${basepath}) due to error:`;\n\n    return this.handleOperationFailure(onProvider, errMsgTemplate);\n  }\n};\n"
  },
  {
    "path": "remove/lib/removeRules.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\nconst Util = require('./util.js');\n\nmodule.exports = {\n  removeRule (ruleName) {\n    const onProvider = ow => ow.rules.delete({ ruleName });\n    const errMsgTemplate = `Failed to delete rule (${ruleName}) due to error:`;\n    return this.handleOperationFailure(onProvider, errMsgTemplate);\n  }, \n\n  removeRules() {\n    if (this.serverless.service.rules.length) {\n      this.serverless.cli.log('Removing Rules...');\n    }\n\n    return BbPromise.all(\n      this.serverless.service.rules.map(r => this.removeRule(r))\n    );\n  }\n};\n"
  },
  {
    "path": "remove/lib/removeTriggers.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  removeTriggerHandler(Trigger) {\n    const onProvider = ow => ow.triggers.delete(Trigger);\n    const errMsgTemplate = \n      `Failed to delete event trigger (${Trigger.triggerName}) due to error:`;\n    return this.handleOperationFailure(onProvider, errMsgTemplate);\n  },\n\n  removeTrigger(triggerName) {\n    const triggerObject = this.serverless.service.resources.triggers[triggerName];\n    const Trigger = { triggerName };\n\n    if (triggerObject.namespace) {\n      Trigger.namespace = triggerObject.namespace;\n    }\n\n    return this.removeTriggerHandler(Trigger);\n  },\n\n  removeTriggers() {\n    const resources = this.serverless.service.resources;\n\n    if (!resources || !resources.triggers) {\n      return BbPromise.resolve();\n    }\n\n    const triggers = Object.keys(resources.triggers)\n\n    if (triggers.length) {\n      this.serverless.cli.log('Removing Triggers...');\n    }\n\n    return BbPromise.all(\n      triggers.map(t => this.removeTrigger(t))\n    );\n  }\n};\n"
  },
  {
    "path": "remove/lib/setupResources.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  initializeTriggers () {\n    if (!this.serverless.service.resources) {\n      this.serverless.service.resources = {};\n    }\n\n    if (!this.serverless.service.resources.triggers) {\n      this.serverless.service.resources.triggers = {};\n    }\n\n    const triggers = this.getEventTriggers()\n    const manifestTriggers = this.serverless.service.resources.triggers || {};\n\n    triggers.forEach(trigger => {\n      manifestTriggers[trigger] = manifestTriggers[trigger] || {}\n    })\n  },\n\n  getEventTriggers() {\n    const triggers = new Set();\n\n    this.serverless.service.getAllFunctions()\n      .forEach(name => {\n        const func = this.serverless.service.getFunction(name);\n        const events = func.events || []\n        events.forEach(event => {\n          if (event.schedule) {\n            triggers.add(event.schedule.name || \n              `${this.serverless.service.service}_${name}_schedule_trigger`)\n          } else if (event.message_hub) {\n            triggers.add(event.message_hub.name || \n              `${this.serverless.service.service}_${name}_messagehub_${event.message_hub.topic}`)\n          } else if (event.cloudant) {\n            triggers.add(event.cloudant.name || \n              `${this.serverless.service.service}_${name}_cloudant_${event.cloudant.db}`)\n          } else if (event.trigger) {\n            triggers.add(event.trigger.name || event.trigger)\n          }\n        })\n      })\n\n    return [...triggers];\n  },\n  \n  initializeRules() {\n    const allFunctions = this.serverless.service.getAllFunctions()\n\n    const rules = allFunctions.map(\n      functionName => this.getRuleNames(functionName, this.serverless.service.getFunction(functionName))\n    ).reduce((a, b) => a.concat(b), [])\n\n    this.serverless.service.rules = rules;\n  },\n\n  getRuleName(funcName, funcObj, trigger) {\n    const defaultRuleName = this.generateDefaultRuleName(funcName, trigger);\n\n    if (typeof trigger === 'string') {\n      return defaultRuleName\n    }\n    \n    return trigger.rule || defaultRuleName;\n  },\n\n  getScheduleRuleName(funcName, funcObj, schedule) {\n    return schedule.rule || `${this.serverless.service.service}_${funcName}_schedule_rule`\n  },\n\n  getMessageHubRuleName(funcName, funcObj, config) {\n    return config.rule || `${this.serverless.service.service}_${funcName}_messagehub_${config.topic}_rule`\n  },\n\n  getCloudantRuleName(funcName, funcObj, config) {\n    return config.rule || `${this.serverless.service.service}_${funcName}_cloudant_${config.db}_rule`\n  },\n\n  getRuleNames(functionName, functionObject) {\n    if (!functionObject.events) return []\n\n    const triggerRules = functionObject.events\n      .filter(e => e.trigger)\n      .map(e => this.getRuleName(functionName, functionObject, e.trigger))\n\n    const scheduleRules = functionObject.events\n      .filter(e => e.schedule)\n      .map(e => this.getScheduleRuleName(functionName, functionObject, e.schedule))\n\n    const messageHubRules = functionObject.events\n      .filter(e => e.message_hub)\n      .map(e => this.getMessageHubRuleName(functionName, functionObject, e.message_hub))\n\n    const cloudantRules = functionObject.events\n      .filter(e => e.cloudant)\n      .map(e => this.getCloudantRuleName(functionName, functionObject, e.cloudant))\n\n    return triggerRules.concat(scheduleRules, messageHubRules, cloudantRules)\n  },\n\n  generateDefaultRuleName(functionName, triggerName) {\n      return `${this.serverless.service.service}_${triggerName}_to_${functionName}`\n  },\n\n  setupResources () {\n    this.serverless.cli.log('Setting up resources...');\n    this.initializeRules();\n    this.initializeTriggers();\n    return BbPromise.resolve();\n  }\n};\n"
  },
  {
    "path": "remove/lib/util.js",
    "content": "'use strict';\n\nmodule.exports = {\n   handleOperationFailure (onProvider, errMsgTemplate) {\n    return new Promise((resolve, reject) => {\n      this.provider.client().then(onProvider).then(resolve).catch(err => {\n        this.serverless.cli.log(`${errMsgTemplate}: ${err.message}`);\n        resolve();\n      });\n    })\n  }\n};\n"
  },
  {
    "path": "remove/lib/validate.js",
    "content": "'use strict';\n\nconst BbPromise = require('bluebird');\n\nmodule.exports = {\n  validate() {\n    if (!this.serverless.config.servicePath) {\n      throw new this.serverless.classes.Error('This command can only be run inside a service.');\n    }\n\n    this.options.stage = this.options.stage\n      || (this.serverless.service.provider && this.serverless.service.provider.stage)\n      || 'dev';\n    this.options.region = this.options.region\n      || (this.serverless.service.provider && this.serverless.service.provider.region)\n      || 'us-east-1';\n\n    return BbPromise.resolve();\n  },\n};\n"
  },
  {
    "path": "remove/tests/all.js",
    "content": "'use strict';\n\nrequire('./index');\nrequire('./removePackages');\nrequire('./removeFunctions');\nrequire('./removeTriggers');\nrequire('./removeRules');\nrequire('./removeRoutes');\nrequire('./removeFeeds');\nrequire('./setupResources');\n"
  },
  {
    "path": "remove/tests/index.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst BbPromise = require('bluebird');\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../');\n\ndescribe('OpenWhiskRemove', () => {\n  const options = {\n    stage: 'dev',\n    region: 'us-east-1',\n  };\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n  const openwhiskRemove = new OpenWhiskRemove(serverless, options);\n  openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n\n  describe('#constructor()', () => {\n    it('should have hooks', () => expect(openwhiskRemove.hooks).to.be.not.empty);\n\n    it('should have access to the serverless instance', () => {\n      expect(openwhiskRemove.serverless).to.deep.equal(serverless);\n    });\n\n    it('should run promise chain in order', () => {\n      const validateStub = sinon\n        .stub(openwhiskRemove, 'validate').returns(BbPromise.resolve());\n      sinon.stub(openwhiskRemove, 'removePackages').returns(BbPromise.resolve());\n      sinon.stub(openwhiskRemove, 'removeFunctions').returns(BbPromise.resolve());\n      sinon.stub(openwhiskRemove, 'removeTriggers').returns(BbPromise.resolve());\n      sinon.stub(openwhiskRemove, 'removeRules').returns(BbPromise.resolve());\n      sinon.stub(openwhiskRemove, 'removeRoutes').returns(BbPromise.resolve());\n\n      return openwhiskRemove.hooks['remove:remove']()\n        .then(() => {\n          expect(validateStub.calledOnce).to.be.equal(true);\n\n          openwhiskRemove.validate.restore();\n          openwhiskRemove.removePackages.restore();\n          openwhiskRemove.removeFunctions.restore();\n          openwhiskRemove.removeRoutes.restore();\n          openwhiskRemove.removeTriggers.restore();\n          openwhiskRemove.removeRules.restore();\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/removeFeeds.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n  let openwhiskRemove;\n  let sandbox;\n\n  const mockFeedObject = {\n    feedName: 'someFeed',\n  };\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {props: () => {}, client: () => {}};\n    process.env.OW_NAMESPACE = 'default';\n\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n    delete process.env.OW_NAMESPACE;\n  });\n\n  describe('#removeFeeds()', () => {\n    it('should call removeFeed for each trigger feed', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removeTriggerFeed', () => Promise.resolve());\n      const triggers = {\n        first: { feed: mockFeedObject },\n        second: { feed: mockFeedObject },\n        third: {},\n      };\n\n      openwhiskRemove.serverless.service.resources = { triggers };\n\n      return openwhiskRemove.removeFeeds().then(() => {\n        expect(stub.calledTwice).to.be.equal(true);\n        expect(stub.calledWith('first', triggers.first)).to.be.equal(true);\n        expect(stub.calledWith('second', triggers.second)).to.be.equal(true);\n      });\n    });\n  });\n\n  describe('#removeTriggerFeed()', () => {\n    it('should call removeFeed with correct feed parameters', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removeFeed', () => Promise.resolve());\n      sandbox.stub(openwhiskRemove.provider, 'props', () => Promise.resolve({namespace: 'default'}));\n\n      const trigger = { feed: '/whisk.system/alarms/alarm', feed_parameters: { a: 1 } };\n      const feed\n        = { feedName: 'alarms/alarm', namespace: 'whisk.system', trigger: '/default/myTrigger' };\n\n      return openwhiskRemove.removeTriggerFeed('myTrigger', trigger).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith(feed)).to.be.equal(true);\n      });\n    });\n\n    it('should call removeFeed with custom trigger namespace', () => {\n      sandbox.stub(openwhiskRemove.provider, 'props', () => Promise.resolve({}));\n      const stub = sandbox.stub(openwhiskRemove, 'removeFeed', () => Promise.resolve());\n\n      const trigger\n        = { namespace: 'custom', feed: '/whisk.system/alarms/alarm', feed_parameters: { a: 1 } };\n      const feed\n        = { feedName: 'alarms/alarm', namespace: 'whisk.system', trigger: '/custom/myTrigger' };\n\n      return openwhiskRemove.removeTriggerFeed('myTrigger', trigger).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith(feed)).to.be.equal(true);\n      });\n    });\n  });\n\n  describe('#removeFeed()', () => {\n    it('should remove feed from openwhisk', () => {\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        const stub = params => {\n          expect(params).to.be.deep.equal({\n            feedName: 'some_feed',\n            namespace: 'test',\n            trigger: 'myTrigger',\n          });\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ feeds: { delete: stub } });\n      });\n      return expect(openwhiskRemove.removeFeed(\n        { feedName: 'some_feed', namespace: 'test', trigger: 'myTrigger' }\n      )).to.eventually.be.fulfilled;\n    });\n\n    it('should reject when feed removal fails to be removed with error message', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskRemove.provider, 'client', () => Promise.resolve(\n        { feeds: { delete: () => Promise.reject(err) } }\n      ));\n\n      const log = sandbox.stub(openwhiskRemove.serverless.cli, \"log\");\n      const result = openwhiskRemove.removeFeed({feedName: 'test'}).then(() => {\n        expect(log.called).to.be.equal(true);\n        expect(log.args[0][0].match(/Failed to remove feed \\(test\\)/)).to.be.ok;\n      })\n      return expect(result).to.eventually.be.fulfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/removeFunctions.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  let openwhiskRemove;\n  let sandbox;\n\n  const mockFunctionObject = {\n    actionName: 'serviceName_functionName',\n    namespace: 'namespace',\n  };\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {client: () => {}};\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#removeFunction()', () => {\n    it('should call removeFunctionHandler with default params', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removeFunctionHandler', () => Promise.resolve());\n      const retValue = { name: 'name', namespace: 'namespace' };\n      sandbox.stub(openwhiskRemove.serverless.service, 'getFunction', () => retValue);\n      const functionName = 'testing';\n\n      return openwhiskRemove.removeFunction(functionName).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith({ actionName: 'name', namespace: 'namespace' })).to.be.equal(true);\n      });\n    });\n\n    it('should call removeFunctionHandler without functionObject name or namespace', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removeFunctionHandler', () => Promise.resolve());\n      const nothing = {};\n      sandbox.stub(openwhiskRemove.serverless.service, 'getFunction', () => nothing);\n      const functionName = 'testing';\n\n      return openwhiskRemove.removeFunction(functionName).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith({ actionName: 'helloworld_testing' })).to.be.equal(true);\n      });\n    });\n  });\n\n  describe('#removeFunctionHandler()', () => {\n    it('should remove function handler from openwhisk', () => {\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        const stub = params => {\n          expect(params).to.be.deep.equal({\n            actionName: mockFunctionObject.actionName,\n            namespace: mockFunctionObject.namespace,\n          });\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ actions: { delete: stub } });\n      });\n      return expect(openwhiskRemove.removeFunctionHandler(mockFunctionObject))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should still resolve when function handler fails to be removed', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskRemove.provider, 'client', () => Promise.resolve(\n        { actions: { delete: () => Promise.reject(err) } }\n      ));\n\n      const log = sandbox.stub(openwhiskRemove.serverless.cli, \"log\");\n      const result = openwhiskRemove.removeFunctionHandler(mockFunctionObject).then(() => {\n        expect(log.called).to.be.equal(true);\n        expect(log.args[0][0].match(/Failed to delete function service \\(serviceName_functionName\\)/)).to.be.ok;\n      })\n      return expect(result).to.eventually.be.fulfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/removePackages.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  let openwhiskRemove;\n  let sandbox;\n\n  const mockPackageObject = {\n    name: 'somePackage',\n    namespace: 'namespace',\n  };\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {client: () => {}};\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#removePackage()', () => {\n    it('should call removePackageHandler with default params', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removePackageHandler', () => Promise.resolve());\n      const packages = { myPackage: {} };\n      openwhiskRemove.serverless.service.resources = { packages };\n      const name = 'myPackage';\n\n      return openwhiskRemove.removePackage(name).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith({ name: 'myPackage' })).to.be.equal(true);\n      });\n    });\n\n    it('should call removePackageHandler with custom namespace', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removePackageHandler', () => Promise.resolve());\n      const packages = { myPackage: { namespace: 'myNamespace' } };\n      openwhiskRemove.serverless.service.resources = { packages };\n      const name = 'myPackage';\n\n      return openwhiskRemove.removePackage(name).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith({ name: 'myPackage', namespace: 'myNamespace' }))\n          .to.be.equal(true);\n      });\n    });\n  });\n\n  describe('#removeFunctionHandler()', () => {\n    it('should remove function handler from openwhisk', () => {\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        const stub = params => {\n          expect(params).to.be.deep.equal({\n            name: mockPackageObject.name,\n            namespace: mockPackageObject.namespace,\n          });\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ packages: { delete: stub } });\n      });\n      return expect(openwhiskRemove.removePackageHandler(mockPackageObject))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should resolve even when function handler fails to be removed', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskRemove.provider, 'client', () => Promise.resolve(\n        { packages: { delete: () => Promise.reject(err) } }\n      ));\n      const log = sandbox.stub(openwhiskRemove.serverless.cli, \"log\");\n      const result = openwhiskRemove.removePackageHandler(mockPackageObject).then(() => {\n        expect(log.called).to.be.equal(true);\n        expect(log.args[0][0].match(/Failed to delete package \\(somePackage\\)/)).to.be.ok;\n      })\n      return expect(result).to.eventually.be.fulfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/removeRoutes.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  let openwhiskRemove;\n  let sandbox;\n\n  const mockFunctionObject = {\n    actionName: 'serviceName_functionName',\n    namespace: 'namespace',\n  };\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {client: () => {}};\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#removeRoutes()', () => {\n    it('should not remove routes when http events not defined.', () => {\n      const fnDefs = {\n        none: {\n        },\n        has_event: {\n          events: [{trigger: true}, {schedule: true}]\n        }\n      } \n      openwhiskRemove.serverless.service.getAllFunctions = () => Object.keys(fnDefs)\n      openwhiskRemove.serverless.service.getFunction = (name) => fnDefs[name]\n \n      const stub = sinon.stub().returns(Promise.resolve())\n\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        return Promise.resolve({ routes: { delete: stub } });\n      });\n\n      return openwhiskRemove.removeRoutes().then(() => {\n        expect(stub.called).to.be.equal(false);\n      })\n    });\n\n    it('should remove service api gw routes from openwhisk', () => {\n      const fnDefs = {\n        has_event: {\n          events: [{http: true}]\n        }\n      } \n      openwhiskRemove.serverless.service.getAllFunctions = () => Object.keys(fnDefs)\n      openwhiskRemove.serverless.service.getFunction = (name) => fnDefs[name]\n \n      const stub = sinon.stub().returns(Promise.resolve())\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        return Promise.resolve({ routes: { delete: stub } });\n      });\n\n      const result = openwhiskRemove.removeRoutes().then(() => {\n        expect(stub.called).to.be.equal(true);\n        expect(stub.args[0][0]).to.be.deep.equal({\n          basepath: '/'\n        });\n      })\n      return expect(result).to.eventually.be.fulfilled;\n    });\n\n    it('should still resolve when api gw routes fail to be removed', () => {\n      const fnDefs = {\n        has_event: {\n          events: [{http: true}]\n        }\n      } \n      openwhiskRemove.serverless.service.getAllFunctions = () => Object.keys(fnDefs)\n      openwhiskRemove.serverless.service.getFunction = (name) => fnDefs[name]\n\n\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskRemove.provider, 'client', () => Promise.resolve(\n        { routes: { delete: () => Promise.reject(err) } }\n      ));\n\n      const log = sandbox.stub(openwhiskRemove.serverless.cli, \"log\");\n      const result = openwhiskRemove.removeRoutes().then(() => {\n        expect(log.called).to.be.equal(true);\n        expect(log.args[1][0].match(/Failed to unbind API Gateway routes \\(\\/\\)/)).to.be.ok;\n      })\n      return expect(result).to.eventually.be.fulfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/removeRules.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  let openwhiskRemove;\n  let sandbox;\n\n  const mockRuleObject = {\n    myRule: 'myTrigger',\n  };\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {client: () => {}};\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#removeRules()', () => {\n    it('should call removeRule for each rule', () => {\n      openwhiskRemove.serverless.service.rules = [\"first\", \"second\"]\n      const stub = sandbox.stub(openwhiskRemove, 'removeRule', () => Promise.resolve());\n\n      return openwhiskRemove.removeRules().then(() => {\n        expect(stub.calledTwice).to.be.equal(true);\n        expect(stub.calledWith('first')).to.be.equal(true);\n        expect(stub.calledWith('second')).to.be.equal(true);\n      });\n    });\n  });\n\n  describe('#removeRule()', () => {\n    it('should remove rule handler from openwhisk', () => {\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        const stub = params => {\n          expect(params).to.be.deep.equal({\n            ruleName: 'myRule',\n          });\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ rules: { delete: stub } });\n      });\n      return expect(openwhiskRemove.removeRule('myRule'))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should resolve even if function handler fails to be removed', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskRemove.provider, 'client', () => Promise.resolve(\n        { rules: { delete: () => Promise.reject(err) } }\n      ));\n      const log = sandbox.stub(openwhiskRemove.serverless.cli, \"log\");\n      const result = openwhiskRemove.removeRule('myRule').then(() => {\n        expect(log.called).to.be.equal(true);\n        expect(log.args[0][0].match(/Failed to delete rule \\(myRule\\)/)).to.be.ok;\n      })\n      return expect(result)\n        .to.eventually.be.fulfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/removeTriggers.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n\n  let openwhiskRemove;\n  let sandbox;\n\n  const mockTriggerObject = {\n    triggerName: 'someTrigger',\n    namespace: 'namespace',\n  };\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {client: () => {}};\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#removeTrigger()', () => {\n    it('should call removeTriggerHandler with default params', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removeTriggerHandler', () => Promise.resolve());\n      const triggers = { myTrigger: {} };\n      openwhiskRemove.serverless.service.resources = { triggers };\n      const triggerName = 'myTrigger';\n\n      return openwhiskRemove.removeTrigger(triggerName).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith({ triggerName: 'myTrigger' })).to.be.equal(true);\n      });\n    });\n\n    it('should call removeTriggerHandler with custom namespace', () => {\n      const stub = sandbox.stub(openwhiskRemove, 'removeTriggerHandler', () => Promise.resolve());\n      const triggers = { myTrigger: { namespace: 'myNamespace' } };\n      openwhiskRemove.serverless.service.resources = { triggers };\n      const triggerName = 'myTrigger';\n\n      return openwhiskRemove.removeTrigger(triggerName).then(() => {\n        expect(stub.calledOnce).to.be.equal(true);\n        expect(stub.calledWith({ triggerName: 'myTrigger', namespace: 'myNamespace' }))\n          .to.be.equal(true);\n      });\n    });\n  });\n\n  describe('#removeFunctionHandler()', () => {\n    it('should remove function handler from openwhisk', () => {\n      sandbox.stub(openwhiskRemove.provider, 'client', () => {\n        const stub = params => {\n          expect(params).to.be.deep.equal({\n            triggerName: mockTriggerObject.triggerName,\n            namespace: mockTriggerObject.namespace,\n          });\n          return Promise.resolve();\n        };\n\n        return Promise.resolve({ triggers: { delete: stub } });\n      });\n      return expect(openwhiskRemove.removeTriggerHandler(mockTriggerObject))\n        .to.eventually.be.fulfilled;\n    });\n\n    it('should resolve even when function handler fails to be removed', () => {\n      const err = { message: 'some reason' };\n      sandbox.stub(openwhiskRemove.provider, 'client', () => Promise.resolve(\n        { triggers: { delete: () => Promise.reject(err) } }\n      ));\n      const log = sandbox.stub(openwhiskRemove.serverless.cli, \"log\");\n      const result = openwhiskRemove.removeTriggerHandler(mockTriggerObject).then(() => {\n        expect(log.called).to.be.equal(true);\n        expect(log.args[0][0].match(/Failed to delete event trigger \\(someTrigger\\)/)).to.be.ok;\n      })\n      return expect(result).to.eventually.be.fulfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "remove/tests/setupResources.js",
    "content": "'use strict';\n\nconst expect = require('chai').expect;\nconst sinon = require('sinon');\nconst OpenWhiskRemove = require('../index');\nconst chaiAsPromised = require('chai-as-promised');\n\nrequire('chai').use(chaiAsPromised);\n\ndescribe('OpenWhiskRemove', () => {\n  const CLI = function () { this.log = function () {};};\n  const serverless = {setProvider: () => {}, config: () => {}, pluginManager: { getPlugins: () => []}, classes: {Error, CLI}, service: {getFunction: () => ({}), provider: {}, resources: {}, getAllFunctions: () => []}, getProvider: sinon.spy()};\n  let openwhiskRemove;\n  let sandbox;\n\n  beforeEach(() => {\n    const options = {\n      stage: 'dev',\n      region: 'us-east-1',\n    };\n    openwhiskRemove = new OpenWhiskRemove(serverless, options);\n    openwhiskRemove.serverless.cli = new serverless.classes.CLI();\n    openwhiskRemove.serverless.service.service = 'helloworld';\n    openwhiskRemove.provider = {client: () => {}};\n    sandbox = sinon.sandbox.create();\n  });\n\n  afterEach(() => {\n    sandbox.restore();\n  });\n\n  describe('#initializeRules()', () => {\n    it('should set up empty rules when configuration missing', () => {\n      openwhiskRemove.serverless.service.rules = null;\n      const stub = sandbox.stub(openwhiskRemove.serverless.service, \"getAllFunctions\").returns([])\n      openwhiskRemove.initializeRules();\n      expect(stub.called).to.be.equal(true);\n      expect(openwhiskRemove.serverless.service.rules).to.deep.equal([]);\n    });\n\n    it('should set up configured rules', () => {\n      const rules = {\n        \"first\": {events: [{trigger: 'trigger'}]},\n        \"second\": {events: [{trigger: { name: 'trigger', rule: 'rule_name'}}]}\n      }\n      openwhiskRemove.serverless.service.rules = null;\n      sandbox.stub(openwhiskRemove.serverless.service, \"getAllFunctions\").returns([\"first\", \"second\"])\n      sandbox.stub(openwhiskRemove.serverless.service, \"getFunction\", id => rules[id])\n      openwhiskRemove.initializeRules();\n      expect(openwhiskRemove.serverless.service.rules).to.deep.equal([\"helloworld_trigger_to_first\", \"rule_name\"]);\n    });\n  });\n\n  describe('#initializeTriggers()', () => {\n    it('should set up triggers without configured rules', () => {\n      openwhiskRemove.serverless.service.resources.triggers =  null\n      sandbox.stub(openwhiskRemove, \"getEventTriggers\").returns([])\n      openwhiskRemove.initializeTriggers();\n      expect(openwhiskRemove.serverless.service.resources.triggers).to.deep.equal({});\n    });\n    it('should set up triggers', () => {\n      openwhiskRemove.serverless.service.resources.triggers = {existing: {}};\n      sandbox.stub(openwhiskRemove, \"getEventTriggers\").returns([\"first\", \"second\"])\n      openwhiskRemove.initializeTriggers();\n      expect(openwhiskRemove.serverless.service.resources.triggers).to.deep.equal({existing: {}, first: {}, second: {}});\n    });\n  });\n});\n"
  },
  {
    "path": "tests/all.js",
    "content": "// OpenWhisk Plugins Tests\nrequire('../compile/servicebindings/tests');\nrequire('../compile/packages/tests');\nrequire('../compile/triggers/tests');\nrequire('../compile/rules/tests');\nrequire('../compile/apigw/tests');\nrequire('../compile/functions/tests');\nrequire('../compile/functions/runtimes/tests/all.js');\nrequire('../compile/schedule/tests');\nrequire('../compile/message_hub/tests');\nrequire('../compile/cloudant/tests');\nrequire('../configCredentials/tests');\nrequire('../deploy/tests');\nrequire('../deploy/tests/all');\nrequire('../deployFunction/tests');\nrequire('../invoke/tests');\nrequire('../invokeLocal/tests');\nrequire('../provider/tests');\nrequire('../remove/tests');\nrequire('../remove/tests/all');\nrequire('../logs/tests');\nrequire('../info/tests');\n"
  },
  {
    "path": "tools/travis/build.sh",
    "content": "#!/bin/bash\nSCRIPTDIR=$(cd $(dirname \"$0\") && pwd)\nHOMEDIR=\"$SCRIPTDIR/../../../\"\n\n# Start Serverless related runs -------------\ncd $HOMEDIR;\ntouch $HOMEDIR./serverless-openwhisk/serverless.yml;\necho $'provider: \\n  name: openwhisk \\n  ignore_certs: true \\n' > $HOMEDIR./serverless-openwhisk/serverless.yml;\ncat $HOMEDIR./serverless-openwhisk/serverless.yml;\n\nnpm install --global serverless $HOMEDIR./serverless-openwhisk\n\ncd $HOMEDIR./serverless-openwhisk\nnpm run test\nexitstatus=$?\n\nrm $HOMEDIR/./serverless-openwhisk/serverless.yml\nexit $exitstatus\n"
  },
  {
    "path": "tools/travis/setup.sh",
    "content": "#!/bin/bash\nSCRIPTDIR=$(cd $(dirname \"$0\") && pwd)\nHOMEDIR=\"$SCRIPTDIR/../../../\"\n\n# install node and npm\nsudo apt-get -y install nodejs npm\nnpm install -g codecov\nnpm install\n"
  },
  {
    "path": "utils/index.js",
    "content": "'use strict';\n\nfunction formatApiHost(apihost) {\n  if (apihost && !(apihost.startsWith('http://') || apihost.startsWith('https://'))) {\n    // assume https unless explicitly declared\n    return `https://${apihost}`;\n  } else {\n    return apihost;\n  }\n}\n\nmodule.exports = { formatApiHost };\n"
  }
]