Before you Begin

This 45-minute tutorial shows how to create a custom component that integrates currency conversion functionality into a skill conversation. You'll learn how to do the following tasks:

  • Start a new custom component service project using the Oracle Bots Node.js SDK.
  • Define the custom component's name, input parameters, and returned actions.
  • Implement logging.
  • Invoke a remote REST service.
  • Deploy custom component services to a Node container that's embedded in your Oracle Digital Assistant instance.

Background

Custom components are reusable units of custom code that you can call from your skill's dialog flow. From a skill-design perspective, there's no difference between a custom component that you build and the system components provided by Oracle Digital Assistant.

The most common use case for building custom components is to integrate a skill conversation with a remote backend system. You develop custom components using JavaScript and Node.js.

What Do You Need?

This tutorial is for developers who are familiar with JavaScript and Node.js development. To complete this tutorial you need the following:

  • Access to an Oracle Digital Assistant instance.
  • A JavaScript IDE or text editor. For example, this tutorial uses Microsoft Visual Studio Code in its screenshots. If you feel comfortable using a text editor, then that's fine too.
  • Node.js and Node Package Manager (NPM) installed for global access. If you used a Node installer (Windows, Mac), then both the node and npm executables should be available to you.

    To test if you have Node.js and NPM installed, open a terminal window and try the following commands separately:

    $ node -v

    $ npm -v


Install the Oracle Bots Node.js SDK

The Oracle Bots Node.js SDK is a Node module for building and deploying custom component services for Oracle Digital Assistant.

Follow these steps to install the Oracle Node.js Bots SDK to your local machine.

  1. Open a terminal window.
  2. If your Internet access is through a proxy, you might need to do the following to be able to install the Oracle Bots Node.js SDK from the Internet:

    • To get the external IP address of your proxy, open a browser and go to http://www.whatismyproxy.com/.
    • In the terminal window, enter these commands:

      npm config set proxy http://<external_ip>:80
      npm config set https-proxy http://<external_ip>:80
  3. To install Oracle Bots Node.js SDK for global access on your laptop, enter this command:

    npm install -g @oracle/bots-node-sdk

    On a Mac, you use the sudo command:

    sudo npm install -g @oracle/bots-node-sdk
  4. To verify the success of your installation, enter this command:

    bots-node-sdk -v

    The command should print the Oracle Bots Node.js SDK version.

    $ bots-node-sdk -v
    Version: 2.5.4

Request a fixer.io Currency Converter API Key

The Fixer website at fixer.io offers current and historic exchange rates for up to 170 currencies. To complete this tutorial, you need a free subscription that allows you to get current conversion rates.

  1. Go to https://fixer.io.
  2. Click GET FREE API KEY in the upper right corner.

    GET FREE API KEY button

  3. In the Pricing section, click GET FREE API KEY.

    GET FREE API KEYFREE pricing plan icon

  4. Complete the registration form.
  5. Copy and save the free API key that's displayed in the confirmation page.

    Fixer IO Confirmation page

The free API key has restrictions. For example, the conversion rates are updated less frequently and you only can make requests using HTTP (not HTTPS). In addition, the base currency is Euro (EUR). You can handle the base currency restriction in your custom component code and enable users to specify different base currencies.


Create the Custom Component Service

To create a custom component service project, use the Oracle Bots Node.js SDK command line interface (CLI) that was installed when you installed the Oracle Bots Node.js SDK. The Oracle Bots Node.js SDK provides all the infrastructure and tools that you need to test, debug, and deploy custom component services.

Create a Custom Component Package

In this section, you use the bots-node-sdk init command to create a package structure with the necessary files, including a custom component JavaScript file for the the currency converter component. This command also installs all the dependencies.

  1. Open a terminal window and navigate to the folder that will contain your component package. Ensure that the folder's path doesn't contain spaces. For example, on Windows, don't navigate to a folder under Program Files.
  2. Enter this command:

    bots-node-sdk init converterccs --name converterccs --component-name CurrencyConverter

    This command might take several minutes to complete.

    As shown in this screenshot, the command creates a subfolder named converterccs, which contains your main.js and package.json files, along with the installed "dev" dependencies in node_modules. It also creates a components folder that contains CurrencyConverter.js, which is the file that you'll modify.

    project structure

  3. (Optional) Open the package.js file and notice that the name matches the value that you provided for the --name argument in your init command.

Provide the Custom Component Framework

The custom component service that you just created is the equivalent to the hello world example. In the next steps, you'll modify the code to become the starter for the currency-converter custom component.

  1. Open your JavaScript editor of choice.
  2. In the JavaScript editor, open the converterccs/components/CurrencyConverter.js file.

    Alternatively, if your editor supports projects, you can open the converterccs folder as a project, expand the components node and open CurrencyConverter.js.

    JS editor with opened component file
  3. Line 10 defines the component's name. To avoid possible naming conflicts when a skill uses more than one component service, add a prefix to the component name.

    Change:

        name: 'CurrencyConverter',

    to:

        name: 'complete.training.CurrencyConverter',
  4. Now you'll define the input properties that the skill can pass to the custom component. Replace line 12, which defines the human property, with the following code:

          variable: {required: true,type: 'string'},
          baseCurrency: {required: true,type: 'string'},
          targetCurrencies: {required: true,type: 'string'},
          amount: {required: true,type: 'int'}

    The skill can use the properties in these ways:

    • variable: Names the variable that the custom component can use to return the result object.

    • baseCurrency: Defines the currency of the amount that's to be converted

    • targetCurrencies: Lists the currencies that the amount must be converted to.

    • amount: Defines the amount to convert.

  5. Change this code line:

        supportedActions: ['weekday', 'weekend']

    to:

        supportedActions: ['success', 'failure']

    The custom component will use these actions to tell the skill whether the currency conversion succeeded.

    With these changes, the metadata() function should look like this, with the function starting on line 9 and ending on line 18:

     9  metadata: () => ({
    10    name: 'complete.training.CurrencyConverter',
    11    properties: {
    12      variable: {required: true,type: 'string'},
    13      baseCurrency: {required: true,type: 'string'},
    14      targetCurrencies: {required: true,type: 'string'},
    15      amount: {required: true,type: 'int'}
    16    },
    17    supportedActions: ['success', 'failure']
    18  }),
  6. Change the invoke: (context, done) function to the following code:

      invoke: (context, done) => {
    
        const { variable } = context.properties()
        const { baseCurrency } = context.properties()
        const { targetCurrencies } = context.properties()
        const { amount } = context.properties()
    
        done();
      }

    Your code should look like this, with the invoke() function starting on line 19 and ending on line 27:

    19  invoke: (context, done) => {
    20
    21    const {variable} = context.properties()
    22    const {baseCurrency} = context.properties()
    23    const {targetCurrencies} = context.properties()
    24    const {amount} = context.properties()
    25
    26    done();
    27  }
    

    Each of the lines that begin with const reads the value for a required component input property. Because the input parameters are flagged as required in the metadata() definition, the code doesn't have to check for missing values (the skill will invoke a runtime exception if a required property is missing). If a property is optional, then your code should check for null values.

    The done() function call invokes a callback that passes the component response back to the Oracle Digital Assistant skill that invoked the custom component.

Add Your First Logging Statement

The Oracle Bots Node SDK exposes a configurable logger. By default, logging is set up to use the console logger. You'll use the logger to track the custom component processing.

  1. Locate the the blank line above the call to done(), which should be line 25.
  2. Replace the blank line with the following code in a single line:

        context.logger().info("Input parameter values: variable: "+variable+", baseCurrency: "+baseCurrency+", targetCurrencies: "+targetCurrencies+", amount: "+amount)

    This log statement prints the names and values of the component's input parameters.

Note:

Because this entry-level tutorial doesn't cover custom component debugging, you are using the logger to track the processing. To learn how to debug custom components, see Debug Custom Components Locally.

Build the fixer.io Request URL

  1. Create a new line under context.logger().
  2. Replace the blank line with the following code, including the blank line before the first var statement and the blank line after the context.logger() statement.

                                                                   
        var tmpTargetCurrencies = targetCurrencies+","+baseCurrency
        var fixerIoAPIKey = "<replace with your api key>"
        var reqUrl = "http://data.fixer.io/api/latest?access_key="+fixerIoAPIKey + "&base=EUR&symbols=" + tmpTargetCurrencies
        // hide API key from logs
        context.logger().info("fixer.io request URL:"+reqUrl.replace(fixerIoAPIKey,"*********"))
           

    The custom component's invoke function should now look like this, with the invoke function starting on line 19 and ending on line 34:

    19  invoke: (context, done) => {
    20
    21    const {variable} = context.properties()
    22    const {baseCurrency} = context.properties()
    23    const {targetCurrencies} = context.properties()
    24    const {amount} = context.properties()
    25    context.logger().info("Input parameter values: variable: " + variable + ", baseCurrency: " + baseCurrency + ", targetCurrencies: " + targetCurrencies + ", amount: " + amount)
    26
    27    var tmpTargetCurrencies = targetCurrencies + "," + baseCurrency
    28    var fixerIoAPIKey = "<replace with your api key>"
    29    var reqUrl = "http://data.fixer.io/api/latest?access_key="+fixerIoAPIKey + "&base=EUR&symbols=" + tmpTargetCurrencies
    30    //hide  API key from logs
    31    context.logger().info("fixer.io request URL:" + reqUrl.replace(fixerIoAPIKey, "*********"))
    32
    33    done()
    34  }
  3. In line 28, replace <replace with your api key> with your fixer.io API key. For example:

    28    var fixerIoAPIKey = "1234567abcde890123b45678c1"

    To keep this tutorial as simple as possible, we are having you put the key in the code. However, in production, you would include the key as a component property. You would also use a custom parameter to set the key value, and then pass the custom parameter value to the component as described in Custom Parameters in Using Oracle Digital Assistant, .

The free fixer.io subscription key only supports EUR as the base currency. To be able to use any currency as the base currency, the fixer.io request URL in line 25 needs to contain the skill-provided base currency as an additional value for the target currency.

The replace(...) function in the logging statement in line 31 ensures that your fixer.io API key does not appear in the log message.

Add the Currency Conversion Code

This tutorial uses the node-fetch module, which is is pre-installed with Bots Node SDK.

  1. To create a reference to the request module, un-comment line 6 so that it looks like this:

    const fetch = require("node-fetch")
  2. Near the bottom of the file, add a new line above the done() function call.

    You should now have 2 blank lines above the done() function call.

  3. Replace the blank line that's just above the done() function call with the following code, which calls the fixer.io remote REST service:

        fetch(reqUrl)
          .then((response) => {
            // Replace this line with the contents of converterimpl.txt        
          })
          .catch((err) => {
            done(err)
          }
        );  

    Your code should look like this:

    29     var reqUrl = "http://data.fixer.io/api/latest?access_key="+fixerIoAPIKey + "&base=EUR&symbols=" + tmpTargetCurrencies;
    30     // hide API key from logs
    31     context.logger().info("fixer.io request URL:"+reqUrl.replace(fixerIoAPIKey,"*********")) 
    32     
    33     fetch(reqUrl)
    34       .then((response) => {
    35         // Replace this line with the contents of converterimpl.txt
    36       })
    37       .catch((err) => {
    38         done(err)
    39       }
    40     );    
    41     done()
    42   }
    43  };
  4. Remove the second done() code line (line 41 in the above code snippet). You'll add this function call to other locations in the final currency conversion implementation.

    Note:

    Typically, because the done() function call is so important, you shouldn't remove the done() call, but instead wait until you've built the code that you can move it to so that it serves as a reminder. In this case, you'll re-add the funtion in the next section, so it's OK to delete it now.
  5. Now that you've defined the basic outline for the complete.training.CurrencyConverter component, you can implement the code to convert the base currency amount into one or more target currencies.

    Open converterimpl.txt and copy its contents.

  6. In line 35, replace this line with the code that you copied to the clipboard.

            // Replace this line with the contents of converterimpl.txt
  7. Save your work.
  8. Compare your custom component with the sample code in completecode.txt, which is also shown in the following screenshot.

    completed request function

    Let's pause for a few minutes to understand the code that you just added.

    • Line 33: The fetch() function performs the REST call to the fixer.io website. It then calls an anonymous function to which it passes the response object and the error object (err) if it exists.
    • Line 35: A 200 response status code means that the REST service call succeeded.
    • Line 45: Even if the REST call succeeded, the response may contain an error, such as an invalid API key. This line checks the response body to see if the conversion was successful.
    • Line 47: If the request returned conversion rates, then this code block saves the rates in an array. The response body is a JSON object, so the rates are retrieved from data.rates.
    • Line 48: To work around the free API key limitation that only EUR can be used as a base currency in the request, the target currency is set to the skill-provided base currency. In this line of code, the base currency is removed from the conversion rates so that only target currencies get reported back to the skill.
    • Line 49: This line computes the converted amount for the currently handled target currency. To get the conversion rate of the target currency relative to the skill-provided base currency (and not EUR), it divides the target currency rate by the base currency rate, which is the skill-provided currency rate in relation to EUR. The code then saves the currency symbol (for example, USD), the conversion rate, and the converted amount to an object, and then adds the object to an array.
    • Lines 59-64: These lines build the response object that will be returned to the skill. The result object contains a base property, which holds information about the base currency, and a conversion property, which contains all the target currencies and their converted amounts.
    • Line 66: This code saves the result object to a dialog-flow context variable. The name of the context variable was provided by the skill designer through the custom component's variable property.
    • Line 67: In this line, the custom component triggers navigation to the success transition action.
    • Lines 37 and 69: These code sections handle the cases where 1) the response status code wasn't 200, and 2) the conversion wasn't successful, such as when the API key is missing.

Note: We intentionally haven't explained the structure of the returned object. Later, when you wear the skill designer's hat, you'll learn how to get the structure of custom component response data.

Package and Deploy the Custom Component Service

You can deploy custom components to either a skill's embedded container, Oracle Functions, a remote Node server, or Oracle Mobile Hub. In this tutorial, you'll deploy your custom component service to a skill's embedded container. For this, you first need to package the custom component service into a deployable file.

Package the Component Project for Deployment

  1. Save your work in your JavaScript IDE.
  2. Open a terminal window and navigate into the converterccs folder.
  3. In the convertercss folder, enter this command:

    bots-node-sdk pack

    Because of the prepack script in the package.json file, the command first executes this command to verify the package. The command then packages the Node project and its dependencies into a deployable tarball, converterccs-1.0.0.tgz as shown here.

    $ bots-node-sdk pack
    ---------------------------------------------------------------------
    Preparing artifact from: converterccs...
    ---------------------------------------------------------------------
    
    > converterccs@1.0.0 prepack /converterccs
    > npm run bots-node-sdk -- pack --dry-run
    
    
    > converterccs@1.0.0 bots-node-sdk /converterccs
    > bots-node-sdk "pack" "--dry-run"
    
    ---------------------------------------------------------------------
    Component Package 'converterccs' is valid!
    ---------------------------------------------------------------------
    npm notice
    npm notice package: converterccs@1.0.0
    npm notice === Tarball Contents ===
    npm notice 576B   package.json
    npm notice 30B    .dockerignore
    npm notice 197B   docker-compose.yml
    npm notice 86B    Dockerfile
    npm notice 60B    main.js
    npm notice 2.6kB  README.md
    npm notice 3.4kB  components/CurrencyConverter.js
    npm notice === Tarball Details ===
    npm notice name:          converterccs
    npm notice version:       1.0.0
    npm notice filename:      converterccs-1.0.0.tgz
    npm notice package size:  8.8 kB
    npm notice unpacked size: 107.4 kB
    npm notice shasum:        99c850e2711cbf48e3dcea83cb043f92bc0ad69c
    npm notice integrity:     sha512-urml2rznGTvf7[...]czHsWQB4VXZPg==
    npm notice total files:   7
    npm notice
    converterccs-1.0.0.tgz
    converterccs $

    Note:

    This package doesn't have any dependencies. When you package custom components that have dependencies, your tarball must include all dependencies except for devDependencies.

Clone the Tester Skill

If you are using a shared Oracle Digital Assistant instance, then the test skill might already exist. If it does, then you can simply clone the skill. If it doesn't exist, then skip to the next section, where you import the skill.

  1. To open the Oracle Digital Assistant instance, open a browser and go to <your-instance url>/botsui.
  2. Click icon to open navigation menu on the top-left corner of the page to show the navigation menu.
  3. Expand Development and select Skills.

    Main Menu

  4. Click icon to open navigation menu again to hide the navigation menu.
  5. In the Skills dashboard, type currency in the Filter field to find the CurrencyConverterTester skill.

    If the CurrencyConverterTester skill isn’t available in your instance, then skip to the next section to import it.

  6. On the CurrencyConverterTester card, click Options Options, and then select Clone.
  7. Name the skill <your-initials>_CurrencyConverterTester, or use any prefix that creates a name that's unique to the instance. For example, FN_CurrencyConverterTester.
  8. Enter a short description.
  9. Select Open cloned skill afterwards, and then click Clone.

Import the Tester Skill

If your instance doesn't have the CurrencyConverterTester skill, then you can import the one that we provide.

  1. Download currency-converter-tester-2-0.zip to your local machine.
  2. To open the Oracle Digital Assistant instance, open a browser and go to <your-instance url>/botsui.
  3. Click icon to open navigation menu on the top-left corner of the page to show the navigation menu.
  4. Expand Development and select Skills.

    Main Menu

  5. Click icon to open navigation menu again to hide the navigation menu.
  6. Click import skill button to start the import process.
  7. Browse to the folder where you saved the dowloaded currency-converter-tester-2-0.zip file, select it, and then click Open to upload and install the starter skill.
  8. Locate your CurrencyConverterTester skill and click its tile to open it.

    If you don't see the skill, type currency in the Filter field to find it.

Deploy Your Custom Component Service to the Skill

  1. In the CurrencyConverterTester skill, click Components Components icon to open the component service page.
  2. Click + Service button to open the Create Service dialog.
  3. In the Name field, enter CurrencyConverter.
  4. In the Description field, enter fixer.io integration.
  5. Ensure that Embedded Container is selected.
  6. Locate the converterccs-1.0.0.tgz file in the converterccs folder on your file system.
  7. Drag-and-drop the converterccs-1.0.0.tgz file into the Create Service dialog's Package File field.
  8. Switch Enable Component Logging to On.
  9. Click Create.

    Component registration

  10. Wait for the Status field to show Ready, which may take a few seconds.

    Status showing deployment as ready

  11. Expand CurrencyConverter if it isn't already expanded, and select complete.training.CurrencyConverter. Notice that the component description is the information that you defined in the custom component's metadata() function:

    You'll see some properties that you didn't define in your metadata, such as insightsEndConversation and translate. Digital Assistant automatically adds these properties to all component services.

    Component description

    With the custom component service deployed to the skill's embedded container, you are ready to test the component.

Test the Custom Component

The tester skill's dialog flow is aleady set up for testing. If you closely followed the steps in this tutorial, then the component name, the input parameter names, and the action transition names match the names used in the converter: state in the dialog flow. If you deviated from the tutorial, you must first apply the changes to the converter: state in the flow.

Note:

The tester flow uses a composite bag entity to guide users through the steps of providing a base currency, a conversion amount, and one or many target currencies. Although the currency converter that you built supports 170 currencies, the skill uses a reduced list.
  1. Click Flows Flows to open the dialog flow editor.

    Notice the getConversion: state in line 11. The System.ResolveEntities component accesses the CONVERSION composite bag entity through the conversion context variable, which is referenced from the component's variable property. The System.ResolveEntities resolves the composite bag entity, asking the user to provide a base currency, a currency amount, and one or more target currencies.

    Note: This tutorial doesn't make use of the composite bag entity's out-of-order extraction functionality. Users will have to provide one-by-one each piece information that's required to perform the currency conversion.

    The converter: state in line 19 references the custom component that you built. The custom component input properties reference the conversion variable to obtain their values. The custom component saves the conversion results in the conversionResult context variable.

  2. To test the skill, click Preview Preview icon.
  3. Start by entering hi.
  4. From the displayed currency list, select CAD for the base currency.
  5. Enter 100 for the amount to convert.
  6. When asked for the target currencies, type 4 5 9, and then press the Enter key.

    Converter testing

    As a result, you should see this skill response, which provides the currency amount and rate for each target currency:

    Conversion response

  7. Click Reset to start a new Preview session.
  8. Enter hi.
  9. Select USD as the base currency. You'll have to click Show More twice for the USD symbol to show.
  10. Enter 100.
  11. Type EUR CHF GBP, and then press the Enter key. You should get a conversion quote similar to the one before.

    Note:

    The custom component was designed to not display any target currency that matches the base currency.
  12. Click Reset, and then continue trying different conversions.
  13. Close Preview, but don't reset it. You'll need the log messages from the current session for the next section of the tutorial.

View The Component's Log Messages

The custom component prints log messages from within its code (context.logger().info(...). When logging is enabled for a service, you can view these messages from the component service page.

  1. Click Components Components icon to open the component service page.
  2. Select CurrencyConverter, click Diagnostics, and then click View Logs.

    Log Viewer

    The dialog displays log messages that are similar to those shown in the following screenshot, such as:

    [2021-05-18T17:27:27.889] [INFO] service - Successful conversion

    Logs written by converter

    Notice the following log message:

    access_key=*********&base=EUR&symbols=CHF,CNY,INR,CAD

    Instead of printing the access credential, the custom component code masks the information, so that it's not disclosed. This masking is part of the custom component code that you edited.

Use Preview to Discover the Response Data Structure

When you created the custom component, we didn't explore the custom component response data structure nor did we explore the content of the dialog flow. How do you know what is in a component response? For example, how would you know that the conversionResult variable can be used as shown in the dialog flow editor lines 37 - 43?

      text: |-
            Conversion of ${conversionResult.value.base.amount} ${conversionResult.value.base.symbol} using rate of ${conversionResult.value.date}.
  
            <#list conversionResult.value.conversion as item>
                ${item?index+1}. ${item.symbol}: ${item.amount?string["#.000"]} ${item.symbol}
                conversion rate: ${item.conversionRate}
                
            </#list>

To figure this out, you can use Preview.

To learn about the structure of a data object returned by a custom component, stop after processing the custom component, and then use the Variables section in the Preview's Conversation tab to explore the variable's content, as described in these steps.

  1. Click Preview.
  2. Click Reset.
  3. Enter hi.
  4. Select a base currency.
  5. Enter a value for the amount.
  6. Select one or more target currencies.
  7. Look at the Variables section that's displayed in the Preview's Conversation tab:

    Variables view

  8. Fully expand the conversionResult variable to see the structure of the data object as shown here.

    expanded variables

    You can see the conversionResult object's properties, array items, and subproperties.

You've now learned how to build, deploy, use, and test a custom component. See the next section to learn more about these tasks.

Learn More