Chapter 4. Using OpenWhisk Actions

So far, every example demonstrated has made use of the CLI to work with actions. We’ve covered creating, updating, and running actions via the CLI. But what about actually using OpenWhisk actions in production? There are three ways of doing that, each with their own benefits and recommended use cases.

Authenticated REST API

The first method is the authenticated REST API. Actually, this is what the CLI was doing on your behalf. The REST API allows for full integration with OpenWhisk, doing everything the CLI does and more. However, as you can probably guess by the title of this section, it does require authentication credentials.

When making requests to the REST API, your username and password must be passed. However, this is not the same as your IBM Bluemix login. In Chapter 2, you copied a command to set up your authentication information for the CLI. You can retieve that information using the CLI as well. Running wsk property get will return a set of values related to your current setup, including authentication information in the whisk auth value:

whisk auth             lotsofrandomnumbers:evenmorerandomnumber
whisk API host         openwhisk.ng.bluemix.net
whisk API version      v1
whisk namespace        _
whisk CLI version      2017-07-12T20:09:28+00:00
whisk API build         2017-07-31T14:35:32Z
whisk API build number  whisk-build-5338

While the values in the above sample were modified, note the colon that appears inside the auth value. When using the authenticated API, your username would be on the left of this and your password would be on the right. With these values, you could then make calls to the API to perform whatever actions make sense. So why would you use this?

While serverless is a very exciting and powerful new way to develop, there are a huge amount of app servers out there that will not be disappearing overnight. (And of course, serverless absolutely does not make sense in every situation!) It is entirely possible that your organization could have an existing app server using Node, PHP, or even ColdFusion and wants to make use of functionality deployed on OpenWhisk. By making simple authenticated HTTP calls to the REST API, you can then make use of those functions.

For developers wanting to use Node with OpenWhisk, take a look at the OpenWhisk npm package. This makes using the REST API far easier and supports a nice Promise-based method of working with every aspect of the platform. Here is a simple example of calling an action.

const openwhisk = require('openwhisk');
const options = {
    apihost:'openwhisk.ng.bluemix.net',
    api_key:'super_secret_key_no_one_will_guess'
}
const ow = openwhisk(options);

ow.actions.invoke({name:'getcats',blocking:true,result:true})
.then((cats) => {
  console.log('Here are the cats:', cats);  
});

In this case, the api_key value would simply be the entire string of the auth value from the earlier CLI example. For more information about the npm package, see the docs on GitHub.

In general, the REST API will only be used for internal projects or projects involving existing app servers where authentication information can be safely embedded and used securely. The REST API should not be used in client-side applications.

Web Actions

Web Actions broadly provide the following features:

  • A public, anonymous URL for invoking the action.
  • A way to return more than just plain JSON data. Web Actions can return headers, headers and data, and non-JSON data, such as HTML and binary data. The ability to return headers will be crucial for client-side web applications.
  • Actions invoked as web actions also get access to request information about how they were invoked. This includes request headers, CGI query string and path information, and the raw body of the request.
  • A way to modify their behavior depending on how they are called. For example, by manipulating the URL, a user can ask for a portion of the data returned instead of the entire set.
  • Finally, web actions with default parameters are considered protected, which means that they are still considered a parameter but can’t be overridden by the user calling the action. Essentially, they are “read-only” parameters that can’t be changed.

Enabling web action support for an existing action is simple. Let’s begin by looking at a simple action.

const cats = ["Luna","Cracker","Robin","Pig","Simba"];

function main(args) {

    return {
        cats:cats
    };
    
}

This action returns a hard-coded list of cats. There are no parameters or any other activity; it simply returns the list. Set this up as an action called cats. (You can find this code in ch4/cats.js.)

wsk action create cats cats.js

Then ensure it works correctly:

wsk action invoke cats

Now that the action exists, let’s discuss how to enable it as a web action and how to call it.

Enabling Web Actions

Enabling an action to be a web action involves adding the web annotation to it. Here’s how to enable it:

wsk action update cats --web true

To confirm, use the CLI to retrieve the action:

wsk action get cats

The result should look like so:

{
    "namespace": "ray@camdenfamily.com_dev",
    "name": "cats",
    "version": "0.0.2",
    "exec": {
        "kind": "nodejs:6",
        "code": "\r\nconst cats = [\"Luna\",\"Cracker\",\
                "Robin\",\"Pig\",\"Simba\"];
                \r\n\r\nfunction main(args)
                {\r\n\r\n    return {\r\n        cats:cats\r\n
    };\r\n    \r\n}"
    },
    "annotations": [
        {
            "key": "web-export",
            "value": true
        },
        {
            "key": "raw-http",
            "value": false
        },
        {
            "key": "final",
            "value": true
        },
        {
            "key": "exec",
            "value": "nodejs:6"
        }
    ],
    "limits": {
        "timeout": 60000,
        "memory": 256,
        "logs": 10
    },
    "publish": false
}

You’ll notice multiple bits of information about the action, including the source code itself. The important part, though, is "web-export" under the "annotations" key. If, for some reason, you want to disable web action support, you can simply run this CLI call:

wsk action update cats --web false

Accessing Web Actions

Once an action has been web enabled, how do you access it? The general form of a web action URL is:

https://{APIHOST}/api/v1/web/{QUALIFIED ACTION NAME}.{EXT}

Let’s break this down bit by bit. The APIHOST value will be openwhisk.ng.bluemix.net if you are using Bluemix to host your OpenWhisk code. QUALIFIED ACTION NAME is a bit more complex. This is a combination of three different values.

The first value is your Bluemix space. That was set up earlier, but you may have forgotten it. A quick way to see it again is to simply list your actions with wsk action list, as shown in Figure 4-1.

Figure 4-1. Getting the action list

In Figure 4-1, notice how each action is prefixed with /ray@camdenfamily.com_dev/. This value will be different for you but is the space value you’ll need to get your web action URL.

The next part of the qualified name is the package. Packages will be discussed in the next chapter, but for now, just know that actions exist in a default package.

Finally, the last part of the qualified name is just the action itself. That seems like a lot, but keep in mind that only the end of the URL typically changes as you work with different web actions.

Even better, the CLI supports a quick way to get the base URL for a web-enabled action. Simply run wsk action get nameOfAction --url. The --url flag at the end simply asks for the URL used for the action. You won’t get the rest of the action metadata.

Here’s the URL for the cats action:

http://openwhisk.ng.bluemix.net/api/v1/web/ray@camdenfamily.com_dev/default/cats.json

Note the extension at the end tells the web action that it should return it’s data in JSON. If you open this in your browser (although you should change it to the URL for your space), you should see a result similar to Figure 4-2.

Figure 4-2. Testing the web action in a browser

In theory, this code is done. It has an anonymous accessible URL and could be used by client-side applications. To illustrate this, consider the following simple client-side application. (You can find this code in ch4/cats.html.)

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width">
  </head>

  <body>

  <h2>Cats</h2>

  <div id="result"></div>

  <script src="https://code.jquery.com/jquery-3.1.1.min.js"
  integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDY...8="
  crossorigin="anonymous"></script>
  <script>
  // Change this to YOUR url...
  let apiUrl = 'http://openwhisk.ng.bluemix.net/api/v1/web'+
  '/ray@camdenfamily.com_dev/default/cats.json';
  $(document).ready(function() {
    console.log('Load cats');
    $.get(apiUrl).then(function(res) {
      console.log(res);
      let list = '<ul>';
      res.cats.forEach((cat) => {
        list += `<li>${cat}</li>`;
      });
      list += '</ul>';
      $('#result').html(list);
    });
  });
  </script>
  </body>
</html>    

This one page application (which is built this way just for simplicity’s sake!) makes use of some simple jQuery calls to fetch the URL that points to the action defined earlier. Again, be sure to change the URL to match your own. After the JSON data is loaded, it’s simply rendered out as an unordered list (see Figure 4-3).

Figure 4-3. Client-side application

If you open Dev Tools in your browser, you’ll notice that a CORS header was added automatically to the API. CORS stands for cross-origin resource sharing and is used to help define what resources are allowed to use an API. You can learn more about CORS at the MDN but for now, just know that by default OpenWhisk is creating an API that can be used by JavaScript code on any host. This is useful, but you can disable this via annotations if you would like more precise control.

API Management

The most powerful way to expose your actions is with the API Management feature. The full name of this feature, Bluemix Native API Management, refers to the ability to not only say that a particular action should be addressable via a HTTP method, but also that you need to exert control over the API. For example, you may require a key to use the API. This also allows for things like rate limiting to ensure developers don’t abuse your API (and drive up your costs). Finally, with API management you get a basic analytics system that tells you how many times your API has been called.

Enabling API Management

In the beginning of this book, I mentioned that OpenWhisk, when being used with IBM Bluemix, had a UI that mirrored what could be done with the CLI. I’ve focused mainly on the CLI aspects as that is most likely going to be more familiar for developers. In this section, I’m going to turn our focus to the UI as it makes working with the feature easier. While some of what I’m going to show can be done with the CLI too, it makes more sense for this section to focus on the visual tools for the API manager.

If you didn’t bookmark it, you can find the Bluemix OpenWhisk console here: https://console.bluemix.net/openwhisk/ (see Figure 4-4).

Figure 4-4. Bluemix OpenWhisk console

On the lefthand side, click the “APIs” link. Until you start actually creating APIs via this tool, you’ll be presented with a basic page (see Figure 4-5) welcoming you to the feature.

Figure 4-5. API management welcome page

To begin trying the feature, simply click the Create an OpenWhisk API button. The next page (Figure 4-6) is divided into two sections, “API Info” and “Security and Rate Limiting.” Don’t worry about the security stuff yet, I will come back to this later in the chapter and modify settings there. For now, focus on the basic API information.

Figure 4-6. Setting up API information

The first item simply lets you import API information in OpenAPI format. The first thing you’ll want to specify is the API name. An API can be comprised of multiple end points, so for example, a Cat API may have the ability to create cats, delete cats, search, and so forth. We built a simple Cat action earlier in the book that returned a list of cats. If we pretend that eventually we’ll have many more such actions, we could imagine having a proper “Cat” API. So for now, we’ll use “The Awesome Cat API.”

The “Base Path” setting serves as a bucket for all the different parts of your API. You can use anything here, but it makes sense to give this a name that reflects the purpose of the actions you will expose under it. For our demo, we’ll use “/cat”.

So far, we’ve named the API and given it a base path, but it doesn’t actually have any actions you can use with it. The UI refers to this as operations. This would be the "list cats,” "delete cats,” and so on, described earlier. To add the first operation, simply click the Create operation button.

The popup launched by the UI gives you a few options (see Figure 4-7):

Path

This is the final part of the URL for your API and should reflect what you’re doing with this particular operation. Since we know we have an action that returns a list of cats, you can use “/list” here.

Verb

This drop down lets you specify which HTTP verb should be used. There are rules about when you should use what. For example, deleting a cat should require the DELETE HTTP operation. You are not required to follow these rules though. (Although you should strongly consider following the norm.) For our case, keep it at the default of GET.

Package containing action

This drop down lets you select a package. Leave that alone for now as it will be covered in Chapter 6.

Action

This drop down lets you select what action to associate with the operation. The cats action is what will return a list of cats.

Response content type

Finally, you can specify a particular content type for the response. The default is fine for this.

Figure 4-7. Details for the new operation

After hitting the Save button, you’ll be brought back to the API info screen (Figure 4-8). Now your operation should be visible in the list.

Figure 4-8. The completed information for the API

At this point, you are almost done. Scroll down past the security stuff and ensure you click the Save button. You’ll now be taken to the summary page (Figure 4-9). This page includes the base route for your API, basic analytics, and even a response log of your most recent calls.

Figure 4-9. Your new API!

Testing APIs

The first thing to note on the API summary screen is the Route URL. It will look something like this:

https://service.us.apiconnect.ibmcloud.com/gws/apigateway/api/very long random string/cat

If you click on this link, you’ll end up on a page with a 404 error. This is expected as the route URL is just the base of your API. Remember the operation defined earlier? Add /list to the end, and you should see your API result (Figure 4-10).

Figure 4-10. The browser’s view of the data

If you reload your API multiple times, you can then return to the dashboard, use the Refresh button (Figure 4-11) by the analytics, and see how API calls are recorded.

Figure 4-11. Analytics and logging for your API

The action used for the list operation, cats, is relatively trivial. Let’s add a quick enhancement that lets users filter the list of cats. You can find the following code in source control at ch4/cats2.js.

const catList = ["Luna","Cracker","Robin","Pig","Simba"];

function main(args) {

    let cats = catList;

    if(args.filter && args.filter !== '') {
        cats = cats.filter((cat) => {
            return (cat.indexOf(args.filter) >= 0);
        });
    }

    return {
        cats:cats
    };
    
}

The first change was to create a copy of the list of cats (now renamed to catList). This lets us optionally modify the list. The next change is a simple check for a filter argument. If it exists and isn’t blank, it is used to check against each cat, and only cats with a matching name are returned.

To update the action, simply use the CLI: wsk action update cats cats2.js. In case you’re curious, it is completely fine to create an action with one filename and then update it with another. The name of the action is important, not the file that acted as the source.

Now that the action is updated, the API created by the gateway can be used to work with this new feature. At the end of the URL, simply append: ?filter=Lu (see Figure 4-12).

Figure 4-12. API result after filtering

For both web actions and APIs created with the API management feature, simple query parameters and form values are associated with arguments.

Locking Down APIs

Now that you’ve got a managed API, how can you go about limiting access to it? Bluemix Native API Management provides a quick and simple way of doing so. Ensure you’re in the “Definition” page and toggle Require applications to authenticate via API Key, as shown in Figure 4-13.

Figure 4-13. Enabling authentication

Once enabled, you can select how authentication is enforced. You can require either just an API key or an API key and secret value. The location of these values can only be in the header, not passed in as a query string or form value. Finally, you can tweak the names of the header values required for authentication. For now though, keep everything as the default.

Next, enable rate limiting. As you can guess, this lets you limit the number of times an API can be called within a time frame. Be sure to make note of the description of the leaky bucket method used to determine how often you can run the API. Imagine you’ve specified 100 calls per hour. The leaky bucket method does not let you call the API 99 times at once and then a final time later in the hour. Rather, the 100 calls must be spread out evenly across the entire hour (demonstrated in Figure 4-14).

Figure 4-14. Enabling rate limiting

You can also enable authentication via OAuth (Figure 4-15). For now, only Google is supported, but leave that turned off for now.

Figure 4-15. Google OAuth is also an option

Finally, be sure to click the Save button so your changes are applied. Now if you rerun the API call in your browser, you’ll get an error, as shown in Figure 4-16.

Figure 4-16. Your API is now protected

To start using this API, you’ll need a key. Click on the “Sharing” link and you’ll be provided with an interface that lets you work with keys in two contexts—either for users in Bluemix or those outside. In general, you’ll probably be sharing the API with outside users, so begin by clicking the Create API Key in the second section. Figure 4-17 shows the dialog created when this is clicked.

Figure 4-17. Adding a new key

The name is simply a description for who is using the key, so feel free to enter anything here, such as, Testing Key. After adding it, you’ll see it listed back on the sharing page (Figure 4-18).

Figure 4-18. List of shared keys

Technically, at this point, you can simply open your command line and use a tool like Curl to call the API and include the proper authorization key. Another option is to use a desktop client such as Postman. However, note that next to the API key is an “API Portal Link.” Click on this and you get a custom built developer portal (Figure 4-19) just for that key.

Figure 4-19. The developer portal

Also, make note of the “Try It” link on the righthand side. Click that, paste in the key, and then hit the Call operation button. You’ll get a full report on both the request and response as well as the actual data of the result, as shown in Figure 4-20.

Figure 4-20. Testing the API

Remember that this API had only one operation. A more real world example would have multiple operations and opportunies to test in the portal.

Get Developing Serverless Applications now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.