### New Features
diff --git a/bin/templates/components/dataqueryeventhandler/template.js b/bin/templates/components/dataqueryeventhandler/template.js
deleted file mode 100644
index 3e3eb94..0000000
--- a/bin/templates/components/dataqueryeventhandler/template.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* eslint-disable no-unused-vars */
-
-'use strict';
-
-// You can use your favorite http client package to make REST calls, however, the node fetch API is pre-installed with the bots-node-sdk.
-// Documentation can be found at https://www.npmjs.com/package/node-fetch
-// Un-comment the next line if you want to make REST calls using node-fetch.
-// const fetch = require("node-fetch");
-
-module.exports = {
- metadata: {
- name: '{{name}}',
- eventHandlerType: '{{eventHandlerType}}'
- },
- handlers: {
- entity: {
- /**
- * Default message handler that includes acknowledgements when a bag item is updated
- * or a bag item value is provided while the user was prompted for another item
- * @param {ChangeUISettingsEvent} event
- * @param {DataQueryResolutionContext} context
- */
- changeUISettings: async (event, context) => {
- return event.settings;
- },
-
- changeResponseData: async (event, context) => {
- return context.getQueryResult();
- },
-
- changeBotMessages: async (event, context) => {
- return event.messages;
- }
- },
- attributes: {
- SomeAttributemName: { // TODO change to a valid attribute name
- // add attribute level event handlers here
- }
- // add more attributes and their handlers here
- },
- custom: {
- // add custom event handlers here
- }
- }
-};
-
-/* eslint-enable no-unused-vars */
-
-
diff --git a/bin/templates/components/dataqueryeventhandler/template.ts b/bin/templates/components/dataqueryeventhandler/template.ts
deleted file mode 100644
index 8ded1f0..0000000
--- a/bin/templates/components/dataqueryeventhandler/template.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { EntityResolutionContext
- , EntityEventHandler
- , EntityEventHandlers
- , EntityEventHandlerMetadata
- , EntityBaseEvent
- , EntityPublishMessageEvent
-} from '@oracle/bots-node-sdk/lib';
-
-// You can use your favorite http client package to make REST calls, however, the node fetch API is pre-installed with the bots-node-sdk.
-// Documentation can be found at https://www.npmjs.com/package/node-fetch
-// Un-comment the next line if you want to make REST calls using node-fetch.
-// import fetch from 'node-fetch';
-
-export class {{className}} implements EntityEventHandler {
-
- public metadata(): EntityEventHandlerMetadata {
- return {
- name: '{{name}}',
- eventHandlerType: '{{eventHandlerType}}',
- supportedActions: [] // string array of transition actions that might be set by the event handler
- };
- }
-
- public handlers(): EntityEventHandlers {
- return {
-
- entity: {
- /**
- * Default message handler that includes acknowledgements when a bag item is updated
- * or a bag item value is provided while the user was prompted for another item
- */
- publishMessage: async (event: EntityPublishMessageEvent, context: EntityResolutionContext) => {
- updatedItemsMessage(context);
- outOfOrderItemsMessage(context);
- context.addCandidateMessages();
- },
-
- /**
- * This handler is called when the composite bag entity is resolved
- */
- resolved: async (event: EntityBaseEvent, context: EntityResolutionContext) => { // eslint-disable-line no-unused-vars
- // add your back-end REST API call here
- }
- // add more entity level event handlers here
- },
-
- items: {
- SomeBagItemName: { // TODO change to a valid bag item name
- // add item level event handlers here
- }
- // add more bag items and their handlers here
- },
-
- custom: {
- // add custom event handlers here
- }
-
- };
- }
-
-}
-
-/**
- * Helper function to show acknowledgement message when a bag item value is updated.
- */
-function updatedItemsMessage(context: EntityResolutionContext) {
- if (context.getItemsUpdated().length > 0) {
- let message = "I have updated" + context.getItemsUpdated().map((item, i) => (i !== 0 ? " and the " : " the ") + item.toLowerCase() + " to " + context.getDisplayValue(item));
- context.addMessage(message);
- }
-}
-
-/**
- * Helper function to show acknowledgement message when a bag item value is provided when user was prompted for anther bag item.
- */
-function outOfOrderItemsMessage(context: EntityResolutionContext) {
- if (context.getItemsMatchedOutOfOrder().length > 0) {
- let message = "I got" + context.getItemsMatchedOutOfOrder().map((item, i) => (i !== 0 ? " and the " : " the ") + item.toLowerCase() + " " + context.getDisplayValue(item));
- context.addMessage(message);
- }
-}
diff --git a/lib/dataquery/dataQueryContext.js b/lib/dataquery/dataQueryContext.js
index 7e4c2b5..9f2d40e 100644
--- a/lib/dataquery/dataQueryContext.js
+++ b/lib/dataquery/dataQueryContext.js
@@ -176,6 +176,44 @@ class DataQueryContext extends BaseContext {
return this.getVariable('system.isFollowupResponse');
}
+ /**
+ * The end flow action that is set that can be used to transition to a different flow by defining a mapping for this action in the main flow.
+ * @param {string} action - the end flow action that can be used to transition in the main flow
+ */
+ setEndFlowAction(action) {
+ this.setVariable('system.sqlDialog.endFlowAction', action);
+ this.getResponse().transitionAction = 'system.sqlDialog.endFlow';
+ }
+
+ /**
+ * Creates a postback action that ends the flow with the specified end flow action.
+ * @param {string} the label of the postback button
+ * @param {string} action - the end flow action that can be used to transition in the main flow
+ */
+ createEndFlowPostbackAction(label, action) {
+ const mf = this.getMessageFactory();
+ return mf.createPostbackAction(label, {'action': 'system.sqlDialog.endFlow', 'variables': {'system.sqlDialog.endFlowAction': action}});
+ }
+
+ /**
+ * Invoke another flow
+ * @param {string} flowName - name of the flow to invoke
+ */
+ invokeFlow(flowName) {
+ this.setVariable('system.sqlDialog.invokeFlowName', flowName);
+ this.getResponse().transitionAction = 'system.sqlDialog.invokeFlow';
+ }
+
+ /**
+ * Creates a postback action that invokes the specified flow.
+ * @param {string} the label of the postback button
+ * @param {string} flowName - name of the flow to invoke
+ */
+ createInvokeFlowPostbackAction(label, flowName) {
+ const mf = this.getMessageFactory();
+ return mf.createPostbackAction(label, {'action': 'system.sqlDialog.invokeFlow', 'variables': {'system.sqlDialog.invokeFlowName': flowName}});
+ }
+
}
module.exports = { DataQueryContext }
\ No newline at end of file
diff --git a/lib/restservice/restServiceContext.js b/lib/restservice/restServiceContext.js
index f9c124e..ff12bd3 100644
--- a/lib/restservice/restServiceContext.js
+++ b/lib/restservice/restServiceContext.js
@@ -79,6 +79,36 @@ class RestServiceContext extends BaseContext {
this.getResponse().responsePayload = payload;
}
+ /**
+ * Adds a message to the bot response sent to the user.
+ * NOTE: This method can only be used in the validateResponsePayload handler
+ * @param {object} payload - can take a string message, or a message created using the MessageFactory
+ */
+ addMessage(payload) {
+ this.getResponse().messages = this.getResponse().messages || [];
+ this.getResponse().messages.push(super.constructMessagePayload(payload));
+ }
+
+ /**
+ * Set a transition action. When you use this function, the dialog engine will transition to the state defined for this transition action.
+ *
+ * NOTE: This method can only be used in the validateResponsePayload handler
+ * @param {string} action - name of the transition action
+ */
+ setTransitionAction(action) {
+ this.getResponse().transitionAction = action;
+ }
+
+ /**
+ * Sets an LLM prompt that will be sent to the LLM
+ *
+ * NOTE: This method can only be used in the validateResponsePayload handler
+ * @param {string} prompt - the text of the prompt
+ */
+ setLLMPrompt(prompt) {
+ this.getResponse().llmPrompt = prompt;
+ }
+
}
module.exports = { RestServiceContext }
\ No newline at end of file
diff --git a/lib/restservice/utils.js b/lib/restservice/utils.js
index 1bf2adc..fff15b5 100644
--- a/lib/restservice/utils.js
+++ b/lib/restservice/utils.js
@@ -16,22 +16,27 @@ async function invokeRestServiceEventHandlers(component, context) {
// event handlers can be async (returning a promise), but we dont want to enforce
// every event handler is async, hence Promise.resolve wrapping of invocation
if (eventName === `transformRequestPayload`) {
- let payload = context.getRequestPayload();
- event.properties = {'payload': payload};
logger.debug(`Invoking event handler ${eventName}`);
let returnValue = await Promise.resolve(handler(event.properties, context));
if (returnValue) {
context.setRequestPayload(returnValue);
}
} else if (eventName === `transformResponsePayload` || eventName === `transformErrorResponsePayload`) {
- let payload = context.getResponsePayload();
- event.properties = {'payload': payload};
logger.debug(`Invoking event handler ${eventName}`);
let returnValue = await Promise.resolve(handler(event.properties, context));
if (returnValue) {
context.setResponsePayload(returnValue);
}
+ } else if (eventName === `validateResponsePayload`) {
+ logger.debug(`Invoking event handler ${eventName}`);
+ let returnValue = await Promise.resolve(handler(event.properties, context));
+ // make sure return value is a boolean
+ let retValue = returnValue === undefined ? true : (returnValue + '' === 'true')
+ logger.debug(`${eventName} returned ${retValue}`);
+ context.getResponse().valid = retValue;
}
+
+
} else {
logger.debug(`No handler found for event: ${eventName}`);
}
diff --git a/package-lock.json b/package-lock.json
index 47b18b6..dc1d850 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@oracle/bots-node-sdk",
- "version": "2.6.7",
+ "version": "2.6.8",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 2108351..349258f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@oracle/bots-node-sdk",
- "version": "2.6.7",
+ "version": "2.6.8",
"description": "Oracle Digital Assistant SDK for custom component development and webhook integrations",
"main": "index.js",
"browser": "index-browser.js",
diff --git a/ts/lib/component/baseContext.ts b/ts/lib/component/baseContext.ts
index d4c848c..6725e5a 100644
--- a/ts/lib/component/baseContext.ts
+++ b/ts/lib/component/baseContext.ts
@@ -360,10 +360,10 @@ export abstract class BaseContext {
*/
translate(rbKey: string, ...rbArgs: string[]) {
// create freemarker expression that will be resolved in runtime after event handler or custom component response is received
- let exp = '${rb(\'' + rbKey + '\'';
+ let exp = '${rb("' + rbKey + '"';
for (let arg of rbArgs) {
// MIECS-38051: only string args should be enclosed in quotes
- typeof arg === 'string' ? exp += ',\'' + arg + '\'' : exp += ',' + arg;
+ typeof arg === 'string' ? exp += ',"' + arg + '"' : exp += ',' + arg;
}
exp += ')}';
return exp;
diff --git a/ts/lib/dataquery/dataQueryContext.ts b/ts/lib/dataquery/dataQueryContext.ts
index 34ec625..5946ef4 100644
--- a/ts/lib/dataquery/dataQueryContext.ts
+++ b/ts/lib/dataquery/dataQueryContext.ts
@@ -179,4 +179,43 @@ export class DataQueryContext extends BaseContext {
return this.getVariable('system.isFollowupResponse');
}
+ /**
+ * The end flow action that is set that can be used to transition to a different flow by defining a mapping for this action in the
+ * main flow.
+ * @param {string} action - the end flow action that can be used to transition in the main flow
+ */
+ setEndFlowAction(action: string): void {
+ this.setVariable('system.sqlDialog.endFlowAction', action);
+ this.getResponse().transitionAction = 'system.sqlDialog.endFlow';
+ }
+
+ /**
+ * Creates a postback action that ends the flow with the specified end flow action.
+ * @param {string} the label of the postback button
+ * @param {string} action - the end flow action that can be used to transition in the main flow
+ */
+ createEndFlowPostbackAction(label: string, action: string): PostbackAction {
+ const mf = this.getMessageFactory();
+ return mf.createPostbackAction(label, {'action': 'system.sqlDialog.endFlow', 'variables': {'system.sqlDialog.endFlowAction': action}});
+ }
+
+ /**
+ * Invoke another flow
+ * @param {string} flowName - name of the flow to invoke
+ */
+ invokeFlow(flowName: string): void {
+ this.setVariable('system.sqlDialog.invokeFlowName', flowName);
+ this.getResponse().transitionAction = 'system.sqlDialog.invokeFlow';
+ }
+
+ /**
+ * Creates a postback action that invokes the specified flow.
+ * @param {string} the label of the postback button
+ * @param {string} flowName - name of the flow to invoke
+ */
+ createInvokeFlowPostbackAction(label, flowName): PostbackAction {
+ const mf = this.getMessageFactory();
+ return mf.createPostbackAction(label, {'action': 'system.sqlDialog.invokeFlow',
+ 'variables': {'system.sqlDialog.invokeFlowName': flowName}});
+ }
}
diff --git a/ts/lib/restservice/restServiceContext.ts b/ts/lib/restservice/restServiceContext.ts
index b9a8bfc..751bab9 100644
--- a/ts/lib/restservice/restServiceContext.ts
+++ b/ts/lib/restservice/restServiceContext.ts
@@ -1,4 +1,5 @@
import { BaseContext } from '../component/baseContext';
+import { MessagePayload } from '../message';
// Response template
const RESPONSE = {
@@ -74,8 +75,38 @@ export class RestServiceContext extends BaseContext {
/**
* Set the response payload
*/
- setResponsePayload(payload: any) {
+ setResponsePayload(payload: any): void {
this.getResponse().responsePayload = payload;
}
+ /**
+ * Adds a message to the bot response sent to the user.
+ * NOTE: This method can only be used in the validateResponsePayload handler
+ * @param {object} payload - can take a string message, or a message created using the MessageFactory
+ */
+ addMessage(payload: string | MessagePayload): void {
+ this.getResponse().messages = this.getResponse().messages || [];
+ this.getResponse().messages.push(super.constructMessagePayload(payload));
+ }
+
+ /**
+ * Set a transition action. When you use this function, the dialog engine will transition to the state defined for this transition action.
+ *
+ * NOTE: This method can only be used in the validateResponsePayload handler
+ * @param {string} action - name of the transition action
+ */
+ setTransitionAction(action: string): void {
+ this.getResponse().transitionAction = action;
+ }
+
+ /**
+ * Sets an LLM prompt that will be sent to the LLM
+ *
+ * NOTE: This method can only be used in the validateResponsePayload handler
+ * @param {string} prompt - the text of the prompt
+ */
+ setLLMPrompt(prompt: string): void {
+ this.getResponse().llmPrompt = prompt;
+ }
+
}
diff --git a/ts/lib/restservice/restServiceTypes.ts b/ts/lib/restservice/restServiceTypes.ts
index 674e1f4..c8a8aad 100644
--- a/ts/lib/restservice/restServiceTypes.ts
+++ b/ts/lib/restservice/restServiceTypes.ts
@@ -20,4 +20,14 @@ export interface TransformPayloadEvent {
payload: any
}
+export interface ChatEntry {
+ role: 'user' | 'system' | 'assistant';
+ content: string;
+}
+
+export interface ValidateResponseEvent {
+ payload: string;
+ chatHistory: ChatEntry[];
+ entityMatches?: Map
+}
diff --git a/ts/lib/restservice/utils.ts b/ts/lib/restservice/utils.ts
index 0f90d42..284b985 100644
--- a/ts/lib/restservice/utils.ts
+++ b/ts/lib/restservice/utils.ts
@@ -18,21 +18,24 @@ export async function invokeRestServiceEventHandlers(component: RestServiceEvent
// event handlers can be async (returning a promise), but we dont want to enforce
// every event handler is async, hence Promise.resolve wrapping of invocation
if (eventName === `transformRequestPayload`) {
- let payload = context.getRequestPayload();
- event.properties = {'payload': payload};
logger.debug(`Invoking event handler ${eventName}`);
let returnValue = await Promise.resolve(handler(event.properties, context));
if (returnValue) {
context.setRequestPayload(returnValue);
}
} else if (eventName === `transformResponsePayload` || eventName === `transformErrorResponsePayload`) {
- let payload = context.getResponsePayload();
- event.properties = {'payload': payload};
logger.debug(`Invoking event handler ${eventName}`);
let returnValue = await Promise.resolve(handler(event.properties, context));
if (returnValue) {
context.setResponsePayload(returnValue);
}
+ } else if (eventName === `validateResponsePayload`) {
+ logger.debug(`Invoking event handler ${eventName}`);
+ let returnValue = await Promise.resolve(handler(event.properties, context));
+ // make sure return value is a boolean
+ let retValue = returnValue === undefined ? true : (returnValue + '' === 'true')
+ logger.debug(`${eventName} returned ${retValue}`);
+ context.getResponse().valid = retValue;
}
} else {
logger.debug(`No handler found for event: ${eventName}`);