Webviews
Your skill can allow its customers to enter structured data using a Webview app.
Natural language conversations are, by their very nature, free-flowing. But they may not always be the best way for your skill to collect information from its users. For example, when entering credit card or passport details, users need to enter specific information (and enter it precisely). To help with these kinds of tasks, your skill can call a webview app.
These apps not only enable structured data entry through UI elements like
forms, date pickers, fields, and LOVs, but they can also validate the user input and
collect information in various ways, like uploading images, capturing user signatures,
or scanning barcodes. Webview apps also protect sensitive user data like credit card
numbers because this data doesn’t appear the chat history when it’s entered into the
app.
How Do I Integrate a Webview into a Skill?
- A Webview Service that connects the skill to the web app, which can be hosted on an external web server, or within Digital Assistant.
- A Webview Component in the dialog flow. This component acts a gateway to the web app by naming the Webview
Service, listing the dialog flow variables that get based to the web app, and storing any
values returned by the web app.
At runtime, the component renders a button that launches the web app. The Webview component launches the app as a webview within the skill, or in a separate browser tab when the skill runs on a web channel.
- The web app itself, which is hosted within Digital Assistant, or on a remote web server.
Tip:
Refer to the webhook starter sample that's described in the SDK documentation athttps://oracle.github.io/bots-node-sdk/
.
Digital Assistant-Hosted Webviews
index.html
) that launches the web app and gets updated in response
to the skill user's input. When the Webview component calls the SPA:
- The
index.html
is loaded and launches the web app as a webview or in a separate browser tab. - The Webview component then passes the parameter values collected in the dialog flow along with the callback URL. Enable the SPA to Access the Input Parameters and Callback URL describes different approaches to passing these values.
- The web app makes a POST request to the callback URL that was
generated by the Webview component. This request signals that the app has
completed its processing. If the app returns data, it's included in this request
as a JSON object that gets stored in the
variable
property. You can reference this data in your dialog flow using${variable_property_name.value.Param}
.
You can write the SPA using different frameworks, such as Oracle Visual Builder, Angular, Oracle JavaScript Extension Toolkit (JET), or React.js.
The backend for Oracle Visual Builder manages REST connections, users (through Oracle Identity Cloud Service), and runs business objects, so any Oracle Visual Builder app hosted within Digital Assistant will have the following limitations:
- It can't use business objects.
- It can't integrate with Oracle Identity Cloud Service.
- It can't access a REST service using the Oracle Visual Builder authentication proxy.
To host the app within Digital Assistant you must bundle it into a TAR archive (a TGZ file). Because this is a SPA, the
index.html
file must be at the root of this package.
Enable the SPA to Access the Input Parameters and Callback URL
window.webViewParameters
variable
(shown in the following snippet) into the <head>
element of the
index.html
file at runtime. The key-values pairs in the payload
inform the SPA of the input values passed from the
skill.window.webviewParameters = {
parameters: [
{"key": "variableA", "value": "jsonObjA"},
{"key": "variableB", "value": "jsonObjB"},
...
{"key": "webview.onDone",
"value": "https://host:port/patch"},
]
};
To
enable your app to access these objects, declare a
window.webviewParameters['parameters']
variable:let webviewParameters = window.webviewParameters !=null?window.webviewParameters['parameters']:null;
The
returned object gets stored in the Webview's Output for Service
property because of the callback.
app.js
file, the
function returns the value for a named key. If it cannot be found, it sets a default
value.
Tip:
You can use this snippet in your own code. You can usevar getParam
instead of
this.getParam
.
class App extends Component {
constructor(props) {
super(props);
let wvParams = window.webviewParameters['parameters'];
this.getParam = (arrParams, key, defaultValue) => {
if (arrParams) {
let param = arrParams.find(e => {
return e.key === key;
});
return param ? param.value : defaultValue;
}
return defaultValue;
};
Defining Placeholders in the index.html File
When you host the SPA within Digital Assistant, you don't need to define any placeholders for the variable values in the
index.html
file. As long as the index.html
file
has a <head>
element, your web app will know what values to
expect, and the callback.
Add a Single Placeholder in the <head> Element
Within the <head>
element, insert a
<script>
block with the
webview.sourceVariableList
placeholder. The web app replaces this
with a JSON-encoded string that has the input parameter data and the callback URL.
In the following example, the key is window.wvParams
. You
can use any name for this key as long as you append it with window.
You
must always define the value as "webview.sourceVariableList"
.
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
<script>
window.wvParams="webview.sourceVariableList";
</script>
</head>
In the app code example below:
- The
let
statement assignswebview.sourceVariableList
towvParams
. - The output values get parsed as a JSON object.
fullname
extracts the name of the variable defined for thesourceVariableList
property in the webview component (wherename
is the name of the variable defined).
class App extends Component {
constructor(props) {
super(props);
let wvParams = (window.wvParams === "webview.sourceVariableList" ?
[] : JSON.parse(window.wvParams)['parameters']);
this.getParam = (arrParams, key, defaultValue) => {
if (arrParams) {
let param = arrParams.find(e => {
return e.key === key;
});
return param ? param.value : defaultValue;
}
return defaultValue;
};
fullname = getParam(wvParams, 'name', null);
callbackurl = getParam(wvParams, 'webview.onDone', null);
...
Add Multiple Placehoders in the <head> Element
Add a <script>
block that has placeholders for each value
defined for the SourceVariable
component and the callback URL. The web
app returns the callback URL and the data for input parameters as a JSON-encoded string.
Because you've added placeholders, you don't have to declare a
window.webviewParameters['parameters']
variable.
- Match the input values defined for
SourceVariable
property. - Be appended by
webview.
(webview.keyword
, for example).
webview.onDone
. In the
following snippet, for example, webview.keyword
,
webview.assignee
, webview.inventor
all match
sourceVariableList: "assignee, keyword, inventor"
. The callback URL
is defined with webview.onDone
. You can name the callback key anything,
but you always need to define the value as webview.onDone
.
You can optionally set global variables by appending the keys with window.
(window.Keyword
in the following snippet, for example).
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
<script>
window.Keyword="webview.keyword";
window.Assignee="webview.assignee";
window.Inventor="webview.inventor";
window.CALLBACK_URL="webview.onDone";
</script>
</head>
Wire the Callback URL to a Done Button in the Web App
The callback URL that's generated by the Webview component is essentially a
single-use callback because of its state token
(https://...?state=<callback-state-token>
). While it has a
default lifetime of an hour, it gets cleared after Webview component handles the
callback request from the web app. Because users won’t be able to query the web app
after this point, it should transition to an End page with a Close button that’s wired
to the callback URL.
Externally Hosted Webviews
You would host an app on external server if it has security requirements, leverages server-side infrastructure, or requires data integration or central administration. Remotely hosted web apps can be SPAs, but don't have to be. You can modify an existing web app to render as a webview or create a web app specifically for a skill.
- At runtime, the Webview component sends the intermediary service a
POST request that includes the skill’s callback URL and the user's input
parameters as an array of key-value pairs. The component adds
webview.onDone
variable as the callback URL key. The keys are the names of parameters that are referenced by both the Webview's Inputs for Service property and thewebview.onDone
property.{ "parameters": [{ "value": "CDG", "key": "origin" }, { "value": "MUC", "key": "destination" }, { "value": "https://<url>:443/connectors/v2/callback?state=cb5443. ..2c" "key": "webview.onDone" }]
- The intermediary service returns a JSON object to the skill that
has a single property,
webview.url
. Its string defines the redirect URL to the web app, which is used by the subsequent GET request from the skill. This URL also contains the key-values passed from the POST request (unless the payload of the POST request is saved in a storage accessible to the web application). It might look something like:
The web app uses the callback URL in a call to pass control back to the skill, and optionally, to pass a response payload.{ "webview.url": "https://<app url>?callbackURL=https://example.com:443/connectors/v2/callback?state=cb55435552c&origin=CDG&destination=MUC }
This illustrates using the JQueryself.buttonClick = function (event) { if (event.currentTarget.id === 'Submit') { let data = {}; data.origin = self.origin(); data.destination = self.destination(); //return date in milliseconds data.departureDate = (new Date(self.departureDate())).getTime(); data.returnDate = (new Date(self.returnDate())).getTime(); data.status = "success" /* function jqueryPost(url, data) { return $.ajax({ contentType: "text/plain", url: url, data: data || {}, type: "POST", dataType: "text/plain" }); }; jqueryPost(webViewCallback, JSON.stringify(data)); */ //JQuery post call $.post(webViewCallback,JSON.stringify(data)); } else { //if user pressed "cancel" pass no data but a status informing the bot designer let data = {}; data.status = "cancel" $.post(webViewCallback, JSON.stringify(data)); }
$.post
method to return JSON object to the skill withorigin
,destination
,departureDate
, andreturnDate
key value pairs:{ "origin":"CDC" "destination":"MUC" "departureDate":"15689736000000" "returnDate":"15689736000000" }
Tip:
This snippet has a"status"
property that's set to"success"
or"cancel"
to indicate that a user has completed work in the web app or canceled it. Users may close the browser before the callback request has completed, so include a listener for the closing that issues a callback if not yet happened. The call could use a status of"cancel"
to indicate that the form wasn't completed successfully. If the user closes the window and you don't catch this, then the skill waits until it time out. - The skill launches the web app by sending a GET request to the URL
defined by the
webview.url
property. - After the webview processes the input, it sends a completion
callback, which is a POST request, to the callback URL that the Webview
component generated and provided to the intermediary service. This request not
only signals that the app has finished (and that the skill should resume control
of the session), but can return a payload of data, which gets stored in the
Webview’s Output for Service property.
Depending on your hosting strategy, there are a couple of things to keep in mind:
- If you host the web app and the intermediary service on the same web server, then the skill's request parameters can be saved into a session, thus eliminating the need for a long URL string.
- If you host the web app and the intermediary service on different servers, then all of the web app request parameters in the redirect URL that's sent to the skill must be encoded as query parameters.
Create a Webview Service
Configuring a Webview Service connects your skill to the service that hosts the webview app.
You create Webview Services from the Webview page, which is accessed by first clicking Components () in the left navbar, then clicking Webview. This page lists the various Webview Services that you can reference in the Webview's Webview Component Service property.
You don’t need to reconfigure a Digital Assistant-hosted service when you version or clone your skill. If you host the web app externally, however, you do need to reconfigure the service when you version or clone your skill.
Create a Digital Assistant-Hosted Webview Service
While you may need to provide authentication credentials as part of
configuring an externally-hosted Webview Service, you only need to package the web app
into a TAR archive (a TGZ file) and then upload it. The index.html
file
must be at the root level of this file.
tar -zcvf webapp.tgz *
In this example, the
-zcvf
command creates a file called webapp.tgz
. As
stated in Defining Placeholders in the index.html File, you can author the web app using your framework of choice as long as
the index.html
file is at the root of the TGZ file. In fact, the
index.html
can even be the only file at the root level.
- Enter the name of the services in the Name file and a description (which is optional). The name that you enter here must match the value for the Webview Component Service property of the Webview component.
- Switch on the Service Hosted option.
- Drop the TGZ into the Package File field or browse to, and select, the TGZ file.
- Click Create.
Package Oracle Visual Builder Applications
You build and optimize your Oracle Visual Builder apps for Digital Assistant using the vb-build Grunt task. You can run this task locally, or as part of a build on Oracle Developer Cloud Service (DevCS).
- Ensure that you've configured its service connection to accommodate the limitations described in How Do I Integrate a Webview into a Skill? by choosing Direct (Bypass the proxy) and Allow Anonymous Access in the Oracle Visual Builder.
- If you're using Oracle Visual Builder to optimize the binary, then select
Push to Git. Otherwise, you can skip this step.
Refer to the Oracle Visual Builder Documentation to find out more about securing the Oracle Visual Builder app and integrating it into a Git repository.
Package the Oracle Visual Builder App Locally
To optimize and package your Oracle Visual Builder app locally:
- In the Oracle Visual Builder home page, select your app and then click Export without Data.
- Unzip the app.
- Run
npm install
on the root folder (where both thepackage.json
andGruntfile.js
files are located).Running
npm install
retrieves thegrunt-vb-build npm
package that's defind in thepackage.json
file. - Enter the following
parameters:
./node_modules/.bin/grunt vb-build \ --url=${serviceURL} \ --username=${username} \ --password=${password} \ --id=${id} --ver=${ver} \ --ver=<your visual app ID>\ --git-source=<local directory for sources>
Parameter Description url
Your Visual Builder instance URL. username
Your user name for the Visual Builder instance. password
Your password for the Visual Builder instance. id
The ID of the application. The application ID may be the same as the application name, but the application ID must be unique in your identity domain. ver
The version of your application. git
Specifies the location of the sources (if they are not located in your current folder). - After the build completes, navigate to the application directory (located with
in the
WebApps
directory). For example,build/optimized/webApps/financialDispute
. - Run the GNU tar command (
tar -zcvf webapp.tgz *
, for example).tar -zcvf webapp.tgz *
Package the App Using Oracle Developer Cloud Service
- Configure a build job in Oracle Cloud for the web app:
- Associate the job with Git by adding Git as the source control (your web app also needs to be integrated with a Git repository).
- Select a build template.
- Add string parameters that get passed into the build. These
parameters include:
- The application's service URL, ID, and version (which you can obtain from your Oracle Visual Builder instance)
- Your user and password (a Password Parameter)
- The optimization, such as Uglify2.
- For the build steps, add a shell script that begins with
npm install
and passes in the default parameters to the Visual Builder Grunt Tasks, such asvb-build
.npm install ./node_modules/.bin/grunt vb-build \ --url=${URL} \ --username=${username} \ --password=${password} \ --id=${id} --ver=${ver} \ --optimize=${optimize} \ --schema=dev \
- For the After Build configuration, configure archiving, by
choosing Artifact Archiver (selected from Add
After Build Action menu) and then enter
build*zip
in the Files to Archive field.
- After the build completes, download the ZIP file and then extract
it. The
index.html
file is located within thewebapp
folder (located in thewebapps
directory). - Package the app into a TGZ file (
tar -zcvf webapp.tgz *
, for example).
Create an Externally-Hosted Webview Service
- Name—The name for remote service.
Note
The name that you enter here must match the value for the Webview Component Service property of the Webview component. - Switch off the Service Hosted toggle.
- Web App URL—The base endpoint provided by a
web server that accepts the source parameters as the payload in a HTTP POST
request. For example,
https://example.oracle.com:3001/webviewParams
. Enter the URL for the intermediary service when the web app and the intermediary service are hosted separately. - Auth Token—An authorization token that’s sent
with requests to the URL specified by the Web App Url
property. This property is the form of
Basic <token>
orBearer <token>
. This is an optional property - Query Parameters—A stringified JSON object whose key-value pairs are the query parameters that are appended to the POST request. This is an optional property.
Reference the Returned Data in the Dialog Flow
Because the values returned in the payload do not update any of the variable values, the property names in the response payload don't need to match the variable names defined in the Inputs for Service property.
You can access the returned payload using
${variable_property_name.value.Param}
.
Tip:
Web app developers should ensure that the returned payload includes descriptive property names.