25 Backend Integration
You can integrate skills with backend services, such as Oracle Human Capital Management Cloud and Oracle Financials Cloud, through custom components. For example, you might want to create a skill that lets your employees report their travel expenses to Oracle Financials Cloud.
To integrate a skill with a backend service:
-
Implement: Using JavaScript and the Oracle Digital Assistant Node.js SDK, implement a custom component that transfers data to and from the skill using the SDK's metadata and conversation objects. See Task 1: Implement Custom Components.
-
Deploy: If you are hosting the components on Oracle Mobile Hub backend, Oracle Functions, or a Node.js server, deploy the component package. See Task 2: Deploy the Component Package to a Service.
-
Add to Skill: Make the components available to a skill by adding a component service for it. See Task 3: Add Component Package to a Skill.
Task 1: Implement Custom Components
To implement custom components, you use the Oracle Digital Assistant Node.js SDK to interface with Digital Assistant's custom component service.
Here's how to implement custom components that you can deploy to the Digital Assistant embedded container, Oracle Functions, a Mobile Hub backend, or a Node.js server:
Note:
If you plan to deploy the custom component package to an embedded custom component service, each skill that you add the package to is counted as a separate service. There's a limit to how many embedded custom component services an instance can have. If you don't know the limit, ask your service administrator to get theembedded-custom-component-service-count
for you as described in View Service Limits in the Infrastructure Console. Consider packaging several components per package to minimize the number of embedded component services that you use. If you try to add a component service after you meet that limit, the service creation fails.
Step 1: Install the Software for Building Custom Components
To build a custom component package, you need Node.js, Node Package Manager, and the Oracle Digital Assistant Bots Node.js SDK.
Note:
If you plan to deploy to the embedded container, your package should be compatible with the following versions:- For Oracle Digital Assistant instances that are provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure), your package should be compatible with Node.js 8.11.5 and Bots Node SDK 2.2.2.
- For Oracle Digital Assistant instances that are provisioned on Oracle Cloud Platform (as all version 19.4.1 instances are), your package should be compatible with 8.11.4 and Bots Node SDK 2.2.0.
Step 2: Create the Custom Component Package
To start a project, you use the bots-node-sdk init
command from the SDK’s command line interface (CLI) to create the necessary files and directory structure for your component structure.
The init
command has a few options, such as whether to use JavaScript (the default) or TypeScript, and what to name the initial component's JavaScript file. These options are described in CLI Developer Tools. Here's the basic command for starting a JavaScript project:
bots-node-sdk init <top-level folder path> --name <component service name>
This command completes the following actions for a JavaScript package:
-
Creates the top-level folder.
-
Creates a
components
folder and adds a sample component JavaScript file namedhello.world.js
. This is where you'll put your component JavaScript files. -
Adds a
package.json
file, which specifiesmain.js
as the main entry point and lists@oracle/bots-node-sdk
as adevDependency
. The package file also points to somebots-node-sdk
scripts.{ "name": "myCustomComponentService", "version": "1.0.0", "description": "Oracle Bots Custom Component Package", "main": "main.js", "scripts": { "bots-node-sdk": "bots-node-sdk", "help": "npm run bots-node-sdk -- --help", "prepack": "npm run bots-node-sdk -- pack --dry-run", "start": "npm run bots-node-sdk -- service ." }, "repository": {}, "dependencies": {}, "devDependencies": { "@oracle/bots-node-sdk": "^2.2.2", "express": "^4.16.3" } }
-
Adds a
main.js
file, which exports the package settings and points to the components folder for the location of the components, to the top-level folder. -
Adds an
.npmignore
file to the top-level folder. This file is used when you export the component package. It must exclude.tgz
files from the package. For example:*.tgz
. -
For some versions of npm, creates a
package-lock.json
file. - Installs all package dependencies into the
node_modules
subfolder.
Note:
If you don't use thebots-node-sdk init
command to create the package folder, then ensure that the top-level folder contains an .npmignore
file that contains a *.tgz
entry. For example:*.tgz
spec
service-*
Otherwise, every time you pack the files into a TGZ file, you include the TGZ file that already exists in the top-level folder, and your TGZ file will continue to double in size.
If you plan to deploy to the embedded container, your package should be compatible with the following versions:
- For Oracle Digital Assistant instances that are provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure), your package should be compatible with Node.js 8.11.5 and Bots Node SDK 2.2.2.
- For Oracle Digital Assistant instances that are provisioned on Oracle Cloud Platform (as all version 19.4.1 instances are), your package should be compatible with 8.11.4 and Bots Node SDK 2.2.0.
Step 3: Create and Build a Custom Component
Here are the steps for building each custom component in your package:
Create the Component File
Use the SDK's CLI init component
command to create a JavaScript or TypeScript file with the framework for working with the Oracle Digital Assistant Node.js SDK to write a custom component. The language that you specified when you ran the init
command to create the component package determines whether a JavaScript or a TypeScript file is created.
For example, to create a file for the custom component, from a terminal window, CD to the package’s top-level folder and type the following command, replacing <component name>
with your component's name:
bots-node-sdk init component <component name> c components
For JavaScript, this command adds the <component name>.js
to the components folder
. For TypeScript, the file is added to the src/components
folder. The c
argument indicates that the file is for a custom component.
Note that the component name can't exceed 100 characters. You can only use alphanumeric characters and underscores in the name. You can't use hyphens. Nor can the name have a System.
prefix. Oracle Digital Assistant won't allow you to add a custom component service that has invalid component names.
For further details, see https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
.
Add Code to the metadata and invoke Functions
Your custom component must export two objects:
-
metada
: This provides the following component information to the skill.-
Component name
-
Supported properties
-
Supported transition actions
By default, the custom component supports the following properties. You don't need to add them to the metadata.
-
autoNumberPostbackActions
: Boolean. Not required. Whentrue
, buttons and list options are numbered automatically. The default isfalse
. See Auto-Numbering for Text-Only Channels. -
insightsEndConversation
: Boolean. Not required. Whentrue
, the session stops recording the conversation for insights reporting. The default isfalse
. See Model the Dialog Flow. -
insightsInclude
: Boolean. Not required. Whentrue
, the state is included in insights reporting. The default istrue
. See Model the Dialog Flow. -
translate
: Boolean. Not required. Whentrue
, autotranslation is enabled for this component. The default is the value of theautotranslation
context variable. See Translation Services in Skills.
-
-
invoke
: This contains the logic to execute. In this method, you can read and write skill context variables, create conversation messages, set state transitions, make REST calls, and more. Theinvoke
function takes two arguments:context
, which names the reference to theCustomComponentContext
object in the Digital Assistant Node.js SDK. This class is described in the SDK documentation at https://oracle.github.io/bots-node-sdk/. Note that in earlier versions of the SDK, the name wasconversation
. You can use either name.done
, which is a callback that the component invokes when it has finished processing
Here’s an example:
'use strict';
module.exports = {
metadata: {
name: 'helloWorld',
properties: {
human: { required: true, type: 'string' }
},
supportedActions: ['weekday', 'weekend']
},
invoke: (context, done) => {
// Retrieve the value of the 'human' component property.
const { human } = context.properties();
// determine date
const now = new Date();
const dayOfWeek = now.toLocaleDateString('en-US', { weekday: 'long' });
const isWeekend = [0, 6].indexOf(now.getDay()) > -1;
// Send two messages, and transition based on the day of the week
context.reply(`Greetings ${human}`)
.reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
.transition(isWeekend ? 'weekend' : 'weekday');
done();
}
}
Bots Node SDK version 2.5.1 introduced an alternative syntax for the invoke
function, which uses an async
keyword instead of the done
argument, as shown here.
invoke: async (context) => {
...
context.reply('hello world');
}
To learn more as well as explore code examples, see Writing Custom Components in the Bots Node SDK documentation.
Control the Flow with keepTurn and transition
You use different combinations of the Bots Node SDK keepTurn
and transition
functions, as well as the call to done()
, to define how the custom component interacts with a user and how the conversation continues after the component returns flow control to the skill.
-
keepTurn(boolean)
specifies whether the conversation should transition to another state without first prompting for user input.Note that if you want to set
keepTurn
to true, you should callkeepTurn
after you callreply
becausereply
implicitly setskeepTurn
tofalse
. -
transition(action)
causes the dialog to transition to the next state after all replies, if any, are sent. The optionalaction
argument names that action (outcome) that the component returns.If you don't call
transition()
, then, when you invokedone()
, the response is sent but the dialog stays in the state and subsequent user input comes back to this component. That is,invoke()
is called again. -
done()
signals that the custom component request has been processed and that the server response should be sent. You must always call it once perinvoke()
.
invoke: (context, done) ==> {
...
context.reply(payload);
context.keepTurn(true);
context.transition ("success");
done();
}
Here are some common use cases where you would use keepTurn
and transition
to control the dialog flow:
Use Case | Values Set for keepTurn and transition |
---|---|
A custom component that transitions to another state without first prompting the user for input. |
For example, this custom component updates a context variable with a list of values to be immediately displayed by a
System.List component, which the skill has defined for the next state in the dialog flow definition.
|
A custom component that enables the skill to wait for input after control returns to the skill and before the skill transitions to another state. |
For example:
|
A custom component that gets user input without returning flow control back to the skill. For example:
|
For example, this custom component outputs a quote and then displays
Yes and No buttons to request another quote. It transitions back to the skill when the user clicks No .
If a component doesn’t transition to another state before it calls For more complex state handling, such as giving the user the option to cancel if a data retrieval is taking too long, you can create and use a context variable. For example: Note that as long as you don't transition, all values that are passed in as component properties are available, even after you call |
The component invocation repeats without user input. For example:
|
Here's a somewhat contrived example that shows how to repeat the invocation without waiting for user input, and then how to transition when done:
|
Access the Backend
You'll find that there are several Node.js libraries that have been built to make HTTP requests easy, and the list changes frequently. You should review the pros and cons of the currently available libraries and decide which one works best for you. We recommend that you use a library that supports promises so that you can leverage the async
version of the invoke
method, which was introduced in version 2.5.1, and use the await
keyword to write your REST calls in a synchronous way.
One option is the node fetch API that's pre-installed with the Bots Node SDK. Access the Backend Using HTTP REST Calls in the Bots Node SDK documentation contains some code examples.
Use the SDK to Access Request and Response Payloads
You use CustomComponentContext
instance methods to get the context for the invocation, access and change variables, and send results back to the dialog engine.
You can find several code examples for using these methods in Writing Custom Components and Conversation Messaging in the Bots Node SDK documentation
The SDK reference documentation is at https://github.com/oracle/bots-node-sdk
.
Ensure the Component Works in Digital Assistants
In a digital assistant conversation, a user can break a conversation flow by changing the subject. For example, if a user starts a flow to make a purchase, they might interrupt that flow to ask how much credit they have on a gift card. We call this a non sequitur. To enable the digital assistant to identify and handle non sequiturs, call the context.invalidInput(payload)
method when a user utterance response is not understood in the context of the component.
In a digital conversation, the runtime determines if an invalid input is a non sequitur by searching for response matches in all skills. If it finds matches, it reroutes the flow. If not, it displays the message, if provided, prompts the user for input, and then executes the component again. The new input is passed to the component in the text
property.
In a standalone skill conversation, the runtime displays the message, if provided, prompts the user for input, and then executes the component again. The new input is passed to the component in the text
property.
This example code calls context.invalidInput(payload)
whenever the input doesn’t convert to a number.
"use strict"
module.exports = {
metadata: () => ({
"name": "AgeChecker",
"properties": {
"minAge": { "type": "integer", "required": true }
},
"supportedActions": [
"allow",
"block",
"unsupportedPayload"
]
}),
invoke: (context, done) => {
// Parse a number out of the incoming message
const text = context.text();
var age = 0;
if (text){
const matches = text.match(/\d+/);
if (matches) {
age = matches[0];
} else {
context.invalidUserInput("Age input not understood. Please try again");
done();
return;
}
} else {
context.transition('unsupportedPayload");
done();
return;
}
context.logger().info('AgeChecker: using age=' + age);
// Set action based on age check
let minAge = context.properties().minAge || 18;
context.transition( age >= minAge ? 'allow' : 'block' );
done();
}
};
Here’s an example of how a digital assistant handles invalid input at runtime. For the first age response (twentyfive
), there are no matches in any skills registered with the digital assistant so the conversation displays the specified context.invalidUserInput
message. In the second age response (send money
), the digital assistant finds a match so it asks if it should reroute to that flow.

Description of the illustration components-nonsequitur-conversation.png
You should call either context.invalidInput()
or context.transition()
. If you call both operations, ensure that the system.invalidUserInput
variable is still set if any additional message is sent. Also note that user input components such as System.CommonResponse
, System.Text
, System.List
, and System.ResolveEntities
reset system.invalidUserInput
.
Say, for example, that we modify the AgeChecker component as shown below, and call context.transition()
after context.invalidInput()
.
if (matches) { age = matches[0]; } else {
context.invalidUserInput("Age input not understood. Please try again");
context.transition("invalid");
context.keepTurn(true);
done();
return;
}
In this case, the data flow needs to transition back to askage
so that the user gets two output messages – "Age input not understood. Please try again" followed by "How old are you?".
askage:
component: "System.Output"
properties:
text: "How old are you?"
transitions: {}
checkage:
component: "AgeChecker"
properties:
minAge: 18
transitions:
actions:
allow: "crust"
block: "underage"
invalid: "askage"
Task 2: Deploy the Component Package to a Service
You can host the custom component package from a Digital Assistant embedded container, Oracle Functions, Mobile Hub, or an external Node.js server. For embedded component services, you deploy the package when you create the service. For Oracle Functions, external Node.js server, and Mobile Hub services, you must first deploy the package to the service, as described here, before you add it to a skill as a component service.
Deploy to a Node.js Server
To host a custom component package on an external Node.js server, use the bots-node-sdk pack --service express
CLI to copy your component package folders and make a few changes that are specific to Express, then install the component package and start it on your server.
-
From the custom component package's top-level folder (the one that contains the
main.js
file), type this command in a terminal window:bots-node-sdk pack --service express
The command does the following:
- Copies the files and subfolders to
service-express-<package version>
. - Adds an
index.js
service wrapper. - Creates an
api.js
file, which is an Express wrapper formain.js
. - Modifies the
package.json
file to set the main file toindex.js
and add the dependencies.
This step shows the basic CLI command. For more information, see
https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
. - Copies the files and subfolders to
-
Run these commands:
npm install npm start
Deploy to Oracle Functions
You can deploy your custom component package to Oracle Functions.
Note:
This feature is not available for Digital Assistant instances that are paired with a subscription to a Fusion-based Oracle Cloud Applications service, such as HCM Cloud or Sales Cloud.Here are the high-level steps:
Get Artifact Names and Permissions for Oracle Functions Deployment
Before you can deploy custom components to Oracle Functions, you need to obtain the names of the artifacts that are used for deployment, and you need to verify that you have permission to use them.
To set up your instance for Oracle Functions deployment, your tenancy administrator completed the steps in Setup and Policies for Oracle Functions. As part of the process, they created the following artifacts. Ask your administrator for their names, which you'll need when you complete the steps in Set Up Your User Account for Oracle Functions:
-
The names of the region and compartment to use for your functions.
-
The name of the compartment for the function application's virtual network (VCN). Typically, this is the same compartment as the one used for functions.
-
The name of the VCN to use for the function application.
Also, ask your administrator to verify that you belong to a group that has the necessary permissions for function developers, which includes access to these artifacts.
Set Up Your User Account for Oracle Functions
Before you can deploy custom component packages to Oracle Functions, you must complete these steps in the Oracle Cloud Infrastructure Console:
Note:
You'll need to know the name of the compartments and virtual network (VCN) to use and you'll need to belong to a group that allows function development as described in Get Artifact Names and Permissions for Oracle Functions Deployment.-
Sign into the Console and, in the top bar, select the region that the function-development compartment is in.
-
Click
on the top left to open the navigation menu, click Developer Services, and then, in the List Scope section, select the compartment that's been set up for function development.
-
You'll deploy to Oracle Functions through the Oracle Cloud Infrastructure Registry. If you don't already have a registry repository that you can use, click Container Registry in the Developer Services section, and then click Create Repository.
Give the repository a name, and then click Create Repository.
-
If you don't have a functions application for your custom component packages, you'll need to create one. From the Developer Services section, click Functions, and then click Create Application. Provide a name, select a VCN, select at least one subnet, and click Create.
If you don't see any VCNs to choose from, you might not be in the correct region.
There are limits to the number of applications and functions. For the default limits, see Functions Limits in Oracle Cloud Infrastructure Documentation .
-
Click the application that you use for function deployment, click Getting Started in the Resources section, and then click Local Setup.
As shown in the following screenshot, the page displays several commands that you'll need to use to set up your local computer and to deploy your custom component package. Copy and save the commands for steps 3 through 7.
You'll use these later after you've installed the required software on your local machine and are ready to deploy your custom components. Alternatively, bookmark this page so that you can return to it when you need to use the commands.
Don't run these commands now. Just copy them.
-
In your copied command that looks similar to the following, change
[OCIR-REPO]
to the name of your registry repository.fn update context registry phx.ocir.io/devdigital/[OCIR-REPO]
-
Click the Profile icon in the top-right corner, and then click User Settings to go to the User Details page.
-
In the next step you'll create a PEM file that you need to store in a
.oci
folder on your local machine. If your home folder on your local machine doesn't have this directory, create one from a terminal window.-
Linux and Mac:
cd ~ mkdir .oci
-
Windows:
cd C:\Users\<your-user-name> mkdir .oci
-
-
You need a public and private
PEM
file for secure access. If you haven't set one up for your user account yet, then, from User Details in the Console, click API Keys from the Resources section, and then click Add API Key.Save the private key file (the
PEM
file) to the.oci
directory in your home folder. - Make a note of the Fingerprint that's associated with the API key. When you have multiple API keys, you must know which fingerprint to use for each private
PEM
file. -
If you haven't already set up a
config
file for the fingerprint on your local machine, then, from the API Keys section, do these steps:-
Click
in the row for your API key's fingerprint and then click View Configuration file.
-
Copy the Configuration File Preview content.
-
In the
.oci
folder on your local machine (the same folder that you saved your private key file in), create a file namedconfig
and paste the copied contents into the file.
-
-
In the
config
file, change thekey_file
property to point to the location of your private PEM file. For example:key_file=/home/joe/.oci/my-private.pem
-
If you don't have an auth token, click Auth Tokens in the Resources menu, and then click Generate Token. Copy the auth token immediately to a secure location from where you can retrieve it later because you won't see the auth token again in the console. You use the auth token as a password when you sign in to push your custom component package to the Oracle Infrastructure registry for deployment.
Set Up Your Local Machine for Oracle Functions
You'll need to install cURL, the OCI command line interface (CLI), Fn, and Docker on your local machine to enable deployment to Oracle Functions. If your machine runs on Windows, then you'll need to either install Oracle VM VirtualBox and host the Linux operating system, or use Cloud Shell.
If you plan to deploy our custom components from Cloud Shell, then you don't need to do the steps in this section. To learn how to deploy from Cloud Shell, see Cloud Shell in Oracle Cloud Infrastructure Documentation. You can use a repository such as GitHub or Object Store to make your custom component package available in the shell.
-
(Windows only) If your machine runs on Windows, then you must use a Linux guest on Oracle VM VirtualBox to deploy your custom component package to Oracle Functions.
Here are the steps to install Linux on VirtualBox:
-
Install VirtualBox from https://www.virtualbox.org/.
-
Download a Linux ISO. For example, to get the ISO for Ubuntu-Mate, go to https://ubuntu-mate.org/download/ and click 64-bit PCs/Macs.
-
In VirtualBox, create a virtual machine from the ISO. You can find instructions for creating a Ubunto-Mate virtual machine at https://itsfoss.com/install-linux-in-virtualbox/. This will be your Linux guest.
-
Start the Linux guest.
-
From a terminal window, run this command:
sudo apt-get update
This updates the package lists for new packages and packages that need upgrading.
Tip:
To open a terminal window in Ubuntu, press Ctrl-Alt-T. -
To be able to do things like copy and paste in a terminal window, you'll need the guest additions. Download http://download.virtualbox.org/virtualbox/<release>/VBoxGuestAdditions_<release>.iso and install and configure the additions using the instructions at https://itsfoss.com/virtualbox-guest-additions-ubuntu/
Be sure to configure configured Devices > Drag and Drop to bidirectional.
-
Enter this command in a terminal window to install node.js and npm on the guest.
sudo apt install npm
-
Drag the
.oci
folder in your home directory on your local machine into the home folder on the Linux guest.Because it's a hidden file, you must press Ctrl-H or select View > Show Hidden Files in the home folder to see it.
-
From the
.oci
folder on the Linux guest, open theconfig
file and changekey_file
to point to the location of the file in your Linux guest. For example:key_file=/home/joe/.oci/my-private.pem
-
Complete the remaining steps in this topic from the Linux guest.
-
- (Mac only) If you haven't already, install Homebrew to enable you to install cURL, OCI CLI, and Fn. See https://docs.brew.sh/Installation. Alternatively, you can use the equivalent MacPorts commands.
-
If your Internet access is through a VPN, then you might need to set up proxies. For example:
export http-proxy = http://<external_ip>:80 export https-proxy = http://<external_ip>:80 export no_proxy = localhost,127.0.0.1,<list> export noproxy = localhost,127.0.0.1,<list> export no_proxy = localhost,127.0.0.1,<list> # Example for apt nano /etc/apt/apt.conf Acquire::http::Proxy "http://<external_ip>:80"; Acquire::https::Proxy "http://<external_ip>:80";
-
Run the appropriate command to bring the packages up to date.
-
Linux:
sudo apt update && sudo apt upgrade
-
Mac:
brew update && brew upgrade
-
-
(Linux only) You'll use cURL to install OCI and Fn. Enter this commeand in a terminal window. The last statement is to verify that it installed successfully.
sudo apt install curl curl --version
-
Fn uses the OCI CLI to deploy the custom components to Oracle Functions. Run the appropriate command to install the CLI, and accept all defaults.
-
Linux:
bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
-
Mac:
brew update && brew install oci-cli
-
-
In Set Up Your User Account for Oracle Functions, you created a
config
file. You now need to configure the CLI to use that file. Open a new terminal window, run this command, provide the location of yourconfig
file, and then entern
for the remaining questions (yourconfig
file already has the necessary settings).oci setup config
For example:
$ oci setup config This command provides a walkthrough of creating a valid CLI config file. ... Enter a location for your config [/home/joe/.oci/config]: Config file: /home/joe/.oci/config already exists. Do you want add a profile here? (If no, you will be prompted to overwrite the file) [Y/n]: n File: /home/joe/.oci/config already exists. Do you want to overwrite (Removes existing profiles!!!)? [y/N]: n
-
You need Docker 17.10.0-ce or later to push the custom component package to the registry.
-
For Ubuntu, the installation instructions are at https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
-
For Mac, the installation instructions are at https://docs.docker.com/docker-for-mac/install/
See https://docs.docker.com/engine/install/linux-postinstall/ if you don’t want to preface the
docker
command withsudo
. -
-
If you are using VPN, then follow the instructions at https://docs.docker.com/network/proxy/
-
Ensure that Docker is running. You can't start Fn, which you install next, if Docker isn't running.
-
You'll use Fn, which is a lightweight Docker-based serverless-functions platform, to configure the context and deploy the package. Follow the instructions for installing Fn, starting the Fn server, and testing the installation at https://fnproject.io/tutorials/install/
You don't need to configure the context or set the registry at this time. You'll do this when you complete the steps in Deploy the Custom Components to Oracle Functions.
Modify the Custom Component Package for Oracle Functions
Before you can deploy a custom component package to Oracle Functions, you'll need to add func.js
and func.yaml
files, add a developer dependency for the fnproject FDK, and install the FDK.
Note:
(Windows only) Complete these steps on your local machine and then use drag-and-drop to copy the component package to your Linux guest. Alternatively, install node.js and the Bots Node SDK, as described in Step 1: Install the Software for Building Custom Components, on your Linux guest before you do the steps.-
In the top folder for your custom component package (the folder that contains
main.js
), create a file namedfunc.js
, and then add the following code. This is the file that Oracle Functions will invoke./*** This function handles an invocation that sets the "Oracle-Bots-Fn-Path" header to determine which component to invoke or if metadata should be returned. ***/ const fdk = require('@fnproject/fdk'); const OracleBotLib = require('@oracle/bots-node-sdk/lib'); const path = require("path"); const BOTS_FN_PATH_HEADER = "Oracle-Bots-Fn-Path"; const METADATA_PATH = "metadata"; const COMPONENT_PREFIX = "components/"; let shell; let componentsRegistry; const getComponentsRegistry = function (packagePath) { let registry = require(packagePath); if (registry.components) { return OracleBotLib.ComponentRegistry.create(registry.components, path.join(process.cwd(), packagePath)); } return null; } componentsRegistry = getComponentsRegistry('.'); if (componentsRegistry && componentsRegistry.getComponents().size > 0) { shell = OracleBotLib.ComponentShell({logger: console}, componentsRegistry); if (!shell) { throw new Error("Failed to initialize Bots Node SDK"); } } else { throw new Error("Unable to process component registry because no components were found in package: " + packagePath); } const _handle = function (input, ctx) { let botsFnPath = ctx.getHeader(BOTS_FN_PATH_HEADER); if (!botsFnPath) { throw new Error("Missing required header " + BOTS_FN_PATH_HEADER); } else if (botsFnPath === METADATA_PATH) { return shell.getAllComponentMetadata(); } else if (botsFnPath.startsWith(COMPONENT_PREFIX)) { let componentName = botsFnPath.substring(COMPONENT_PREFIX.length); if (!componentName) { throw new Error("The component name is missing from the header " + BOTS_FN_PATH_HEADER + ": " + botsFnPath); } return new Promise((resolve) => { let callback = (err, data) => { if (!err) { resolve(data); } else { console.log("Component invocation failed", err.stack); throw err; } }; shell.invokeComponentByName(componentName, input, {logger: () => console}, callback); }); } }; fdk.handle(function (input, ctx) { try { return _handle(input, ctx); } catch (err) { console.log("Function failed", err.stack); throw err; } });
-
In the same folder, create a file named
func.yaml
and then add the following content:schema_version: 20180708 name: <custom component package name> version: 0.0.1 runtime: node entrypoint: node func.js
-
In the
name
property, change<custom component package name>
to the name of your custom component package, and then save your changes. The name is typically the same as the name that you specify in thepackage.json
file.The name should be no more than 255 characters and contain only letters, numbers,
_
, and-
. -
In a terminal window, change to the package's top folder and enter this command to install the FDK and to add it as a package dependency in the
package.json
file:npm install --save-dev @fnproject/fdk
-
(Optional – Linux and Mac only) Run this command to install package dependencies:
npm install
Note that if the
node_modules
folder doesn't exist, then thefn deploy
command that you do later will invokenpm install
for you. -
(Windows only) Complete these steps to copy your custom component code to your Linux guest for deployment:
-
Drag-and-drop the top level folder to the Linux guest.
-
In a terminal window, change to the top-level folder (the one that contains
main.js
) and type this command to add execute permissions for the folder.chmod 755 components
-
Delete the
node_modules
folder to ensure that you don't have any platform-dependent node modules. -
(Optional) Run this command to reinstall the node module dependencies.
npm install
Note that if the
node_modules
folder doesn't exist, then thefn deploy
command that you run later will invokenpm install
for you.
-
You are now ready to complete the steps in Deploy the Custom Components to Oracle Functions.
Deploy the Custom Components to Oracle Functions
After you create the func.js
file, add fnproject to the development dependencies, and install the dependencies as described in Modify the Custom Component Package for Oracle Functions, you are ready to deploy a Docker image of the component package to Oracle Functions.
When you completed the steps in Set Up Your User Account for Oracle Functions, you copied commands from the Local Setup on the Getting Started page for steps 3 through 7. You'll use these copied commands to deploy your custom components.
If you're using Cloud Shell, then use the similar commands shown in Cloud Shell Setup instead.
To deploy the custom components:
-
Ensure that Docker and the Fn server are running.
-
In a terminal window, change to the top directory for your custom component package and enter your equivalent copied commands to configure the context.
fn create context <context> --provider oracle fn use context <context> fn update context oracle.compartment-id <compartment-ocid> fn update context api-url https://functions.<region-identifier>.oraclecloud.com
You don't have to run these commands again until you need to change the context configurations.
-
To point the registry repository that you created in Set Up Your User Account for Oracle Functions, enter your copied command that's equivalent to the following. If you haven't already, change
[OCIR-REPO]
to the name of your repository.fn update context registry <region-key>.ocir.io/<tenancy-namespace>/[OCIR-REPO]
You don't have to run this command again until you need to change the repository configuration.
-
If you haven't signed into Docker in your current session, run the copied command that's equivalent to the one shown here.
When it prompts you for a password, enter your auth token, which is the token that you obtained while completing the steps in Set Up Your User Account for Oracle Functions.
docker login -u '<tenancy-namespace>/<user-name>' <region-key>.ocir.io
-
To deploy the custom components, run this command:
fn deploy --app <application>
If you see the following message, open the
.oci/config
file and verify thatfingerprint
shows the correct fingerprint for the specifiedkey_file
. If not, go to your user settings in the Console, click API Keys, view the configuration file for the correct fingerprint, and then replace the content of your config file with the displayed content.Fn: Service error:NotAuthenticated. The required information to complete authentication was not provided or was incorrect.. http status code: 401.
Your custom components are ready to use in a skill as described in Add Oracle Function Service.
Deploy to Mobile Hub
To host a custom component package in Mobile Hub, use the bots-node-sdk pack --service mobile-api
CLI to copy your component package folders and make a few changes that are specific to Mobile Hub, including the RAML file. Then create the custom API from the RAML file, and upload a ZIP of the component package into the custom API.
-
From the custom component package's top-level folder (the one that contains the
main.js
file), type this command in a terminal window:bots-node-sdk pack --service mobile-api
The command does the following:
- Copies the files and subfolders to
service-mobile-api-<package version>
. - Adds a
component.service.raml
file, which contains the necessary endpoints and operations. - Creates an
api.js
file, which is a Mobile Hub wrapper formain.js
. - Modifies the
package.json
file to set the main file toapi.js
, set the dependencies, and add the Mobile Hub node configuration.
This step shows the basic CLI command. For more information, see
https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
. - Copies the files and subfolders to
- Review the
package.json
file and verify that the package name conforms to the following Mobile Hub constraints. Modify the name as necessary to conform.- The name must consist only of letters (A-Za-z), numbers (0-9), and underscores (_).
- The name must begin with a letter.
- The name must be 100 characters or less.
-
From the Mobile Hub APIs page, click New API > API, and then create the custom API by uploading the
component.service.raml
file. -
From the Security tab, switch off Login Required and then click Save.
-
Zip up the
service-mobile-api-<package version>
folder, and then upload the ZIP file from the custom API’s Implementation tab. -
From the Test page, invoke the
GET
request. The response should show the component metadata.Tip:
If you get a status 500, and the error is that it can’t find a matching route definition, check your files for bad JavaScript syntax, as that is the typical cause.
Task 3: Add Component Package to a Skill
You add a component package to a skill by creating a component service. For component packages that you host on an external server, Oracle Functions, or on Oracle Mobile Hub, a component service is an active connection from the skill to host server. Alternatively, you can upload the component package and host it from your Oracle Digital Assistant instance. This is referred to as an embedded component service.
A component service has two functions:
-
It queries the component to get the package metadata, including the names of the components, their properties, and allowed actions for each one. After a service is added to the skill, you can see this information in the Components tab, which you access by clicking Components
in the left navbar. You can reference this page to get the component names, properties, and actions, which you will need to use the components in your dialog flow.
-
It allows the skill to invoke the components.
The JSON payload of the call made by the Dialog Engine to the components includes input parameters, variable values, user-level context, and the user’s message text. The component returns the results by changing the values of existing variables or adding new ones (or both). The Dialog Engine parses the returned payload and proceeds.
To add a custom component package to a skill, go to the skill's Components tab and click Add Service, which opens a dialog for configuring the service.
How you configure the service depends on where you are hosting the component package. These topics provide instructions for each type:
After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.
When you upload a package to the embedded container, Digital Assistant verifies that the package is valid, and can reject the package for these reasons:
-
There are JavaScript errors.
-
The package doesn't contain all the node module dependencies.
-
A component name has more than 100 characters, begins with
System.
, or contains other than alphanumeric characters and underscores, then the service creation fails. -
Your instance already has the maximum number of embedded component services.
-
The TGZ file is too large. This typically happens when the
.npmignore
file doesn't contain a*.tgz
entry and therefore, every time you pack the files, a nested copy of the existing TGZ is added.
See Add Embedded Component Service for more information about these verification checks.
Add Embedded Component Service
If you want to host the custom component package from your Oracle Digital Assistant instance, complete these steps:
Prepare the Package for an Embedded Container Service
If you want to host the custom component package from Oracle Digital Assistant as an embedded component service, you must first pack the custom components into a TGZ file. Then, when you create the embedded component service, you upload this file.
This TGZ file, which you package using bots-node-sdk pack
, must contain the assets and structure described in Task 1: Implement Custom Components. It also must contain all the node modules that it depends on (the bots-node-sdk pack
does that for you).
Note:
There's a limit to how many embedded custom component services an instance can have. If you don't know the limit, ask your service administrator to get theembedded-custom-component-service-count
for you as described in View Service Limits in the Infrastructure Console. If you try to add a component service after you meet that limit, then the service creation fails.
To prepare a package for uploading to the embedded container service:
-
Ensure that you have the latest version of the Bots Node.js command line tools.
The embedded container requires that the TGZ file includes all
dependencies
. Earlier versions did not bundle thedependencies
into the file. Now, the command that you'll use to create the TGZ file ensures that yourpackage.json
file contains abundledDependencies
node that lists all the dependent modules that need to be included in the TGZ file. -
In the directory that contains the
main.js
file, run the following command for each of the modules that your package depends on. You don't need to do this fordevDependencies
, such as the Bots Node SDK.This command adds the module to the
node_modules
folder and adds it as a dependency inpackage.json
.npm install <module>
If your
package.json
already names all the dependencies, then you can runnpm install
instead. -
Ensure that the top-level folder contains an
.npmignore
file that has a*.tgz
entry. For example:*.tgz spec service-*
Otherwise, when you pack the files into a TGZ file, you include the TGZ file that already exists in the top-level folder, and your TGZ file will double in size. After you pack the files a few times, the TGZ file will be to large to upload into the container.
-
Run this command:
bots-node-sdk pack
This command validates the component package, updates it to include
devDependencies
if necessary, and then creates a TGZ file, which you'll upload when you create an embedded component service from the skill’s Components tab. Note that the files you've listed asdependencies
are included asbundledDependencies
, with the exception of the Bots Node SDK and Express, which aredevDependencies
.
Your package should be compatible with the following versions:
- For Oracle Digital Assistant instances that are provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure), your package should be compatible with Node.js 8.11.5 and Bots Node SDK 2.2.2.
- For Oracle Digital Assistant instances that are provisioned on Oracle Cloud Platform (as all version 19.4.1 instances are), your package should be compatible with 8.11.4 and Bots Node SDK 2.2.0.
For more information about the pack
command, see https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
.
Upload Package to Create an Embedded Component Service
After you pack a custom component package into a TGZ file, you can upload it to create an embedded component service from the skill's Components tab.
To learn how to create the TGZ file, see Prepare the Package for an Embedded Container Service.
Note:
When you upload the package to the embedded component service, it's deployed to Oracle Functions Service. If your instance is provisioned on the Oracle Cloud Platform (as all version 19.4.1 instances are), then the service is instead deployed within the Digital Assistant instance.To create the embedded component service:
-
From the skill, click Components
.
-
Click
.
-
Select Embedded Container.
-
Click Upload a component package file and point to the TGZ file to upload, or drag the file to the Package File box.
-
(Optional) If you want to send custom component
context.logger()
statements to the service's log, then switch Enable Component Logging to On. This switch is available only in instances of Oracle Digital Assistant that were provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure).You can see the log from the Components tab by clicking Diagnostics > View Logs.
-
Click Create.
Digital Assistant uploads the TGZ file and creates the embedded component service. During the upload, Digital Assistant verifies that the package is valid, and can reject the package for the reasons that are described later in this section.
After you upload the TGZ file, the custom component service is built and its components are deployed. If the Components page displays an awaiting deployment message after you upload the TGZ file, it means that the service has been created, but is not yet available. When the service becomes available, the deployment metadata displays in place of the awaiting deployment message.
-
Ensure that Service Enabled is switched to On.
During the upload, Digital Assistant might reject the package. Here are reasons for rejection and ways to resolve the issues.
-
JavaScript contains syntax errors: If a component's JavaScript has syntax errors, then that component is not added to the container, which results in this error message:
Error Message: failed to start service built: Invalid component path:
View the component files in an editor that detects syntax errors. Also, try hosting the package on a local server that sends error messages to a console log.
Another reason for this message might be that the package doesn't contain all the node module dependencies. See the next item in this list.
-
Missing node modules: If the package doesn't contain all the node module dependencies, then you'll get the same error message as above:
Error Message: failed to start service built: Invalid component path:
To learn how to include node module dependencies, see Prepare the Package for an Embedded Container Service.
-
Component name is too long: If a component name has more than 100 characters, begins with
System.
, or contains other than alphanumeric characters and underscores, then the service creation fails.Change the name in the component's JavaScript, repackage, and upload again.
-
Exceeded component service limit: If your instance already has the maximum number of embedded custom component services, then the service creation fails. Ask your service administrator for the
embedded-custom-component-service-count
limit as described in View Service Limits in the Infrastructure Console.If you need to raise the limit, you can request an increase. See Requesting a Service Limit Increase.
-
TGZ file is too large: This typically happens when the
.npmignore
file doesn't contain a*.tgz
entry and therefore, every time you pack the files, a nested copy of the existing TGZ is added.When the top-level folder contains an
.npmignore
file with*.tgz
, the previous version of the TGZ file isn't included when you update the package.
If you want to send custom component context.logger()
statements to the service's log, then switch Enable Component Logging to On. This switch is available only in instances of Oracle Digital Assistant that were provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure).
When Enable Component Logging is switched to On, you can click the Diagnostics button for the service to access view logs and crash reports to diagnose the problem.
- Select View Logs to view messages that the custom component sends to
context.logger()
. This feature is available only in instances of Oracle Digital Assistant that were provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure). The Enable Component Logging switch must be On for the log to contain these messages. - Select View Crash Report to view details about what may have caused the container to crash.
After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.
Add Oracle Function Service
You can deploy your custom components to Oracle Functions and add them to a skill as an Oracle Function service.
To add an Oracle Function service:
-
You'll need to know the function's URL. A user with function development privileges can get the URL from the Integration Console for you.
- Sign in to the Integration Console.
-
Click
on the top left to open the navigation menu, click Developer Services > Functions, and then select the compartment that's been set up for function development.
-
Click the application.
-
In the Functions section, click the More icon for your function and click Copy Invoke Endpoint. Skill developers need this value to add the custom component package as a component service in a skill.
-
A skill developer adds the component service to a skill in Oracle Digital Assistant. Sign into Oracle Digital Assistant, open the skill and click Components
.
-
Click
.
-
Provide a name and description for the service.
-
Select Oracle Function.
-
In the URL text box, enter the invoke endpoint URL that you copied from the Developer Services > Functions page in the Infrastructure Console while completing the steps in Set Up Your User Account for Oracle Functions.
-
Click Create.
If you get a "Can't Create the Service" error, go to Developer Services > Functions in the Infrastructure Console, select the compartment, click the application, and click Logs in the Resources menu. Then enable the log for the function, retry the deployment, and check the logs (click the log name to see the log). To learn more about logs, see Storing and Viewing Function Logs in Oracle Cloud Infrastructure Documentation.
If you see an error like this, it's because the folder doesn't have the right permissions. On your local machine, use
chmod 775
to change the folder's permissions, then redeploy:"Error: EACCES: permission denied, stat '/function/components/hello.world.js.js'"
If you see an error like this then, on your local machine, delete
node_modules
, runnpm install
and redeploy."Error: Cannot find module '@fnproject/fdk'"
-
Ensure that Service Enabled is switched to On.
After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.
Add External Component Service
You can host your custom components on your own Node.js server and add them to a skill as an external component service.
Tip:
You can use the external service option during development, as described in Run the Component Service in a Development Environment.To add an external component service:
-
From the skill, click Components
.
-
Click
.
-
Select External.
-
In the Metadata URL text box, enter the The URL that points to the GET endpoint that returns the list of components.
-
Enter the service's user name and password.
-
Click Create.
-
Ensure that Service Enabled is switched to On.
After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.
Add Mobile Hub Component Service
You can host your custom components from Oracle Mobile Hub, and add them to a skill as an Oracle Mobile Cloud component service. Custom components that are hosted on Mobile Hub can integrate with remote services using connectors that are controlled by a Mobile Hub backend and they have access to the Mobile Hub platform APIs.
Because the backend that hosts the custom code handles the authentication for the custom components, you need to refer to the backend’s Settings page to get the information that you need to complete the configuration.
To add a component service for the Mobile Hub backend:
-
From the skill, click Components
.
-
Click
.
-
Select Oracle Mobile Cloud.
-
Enter the unique identifier assigned to the Mobile Hub backend in the Backend ID field. This ID is passed in the REST header of every call from the skill.
-
In the MetadataURL field, enter the
/components
endpoint from the custom code API. For example,http://<server>:<port>/mobile/custom/ccPackage/components
. -
Choose Use Anonymous Access if the service allows anonymous login. If you choose this option, enter the anonymous key, which is a unique string that allows your app to access anonymous APIs without sending an encoded user name and password. The anonymous key is passed in their place. You can find the anonymous key on the backend's Settings page in Mobile Hub. (You may need to click Show.)
If the component service requires a login (meaning no anonymous access), then enter the user name and password.
-
If the service requires specific parameters, click Add HTTP Header and then define the key-value pairs for the headers.
-
Click Create.
-
Ensure that Service Enabled is switched to On.
After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.