Before You Begin

This 60-minute tutorial shows you how to develop and deploy an entity event handler component for a composite bag entity using an external IDE.

Note:

This advanced tutorial requires familiarity with entity event handlers. If you haven't already worked with entity event handlers, you should complete this tutorial, which teaches you how to create an entity event handler using Digital Assistant's embedded JavaScript editor before starting this one.

Background

Composite bag entities group related entities as well as string, attachment, and location items into a single object that represents a real world entity -- a booking, an incident report, a person, etc. Composite bag entities automatically prompt users to provide information for each of its bag items. User input is validated against the underlying entity or, using expressions, against custom validation rules.

With entity event handlers, you can execute custom component code to validate user input, execute business logic and access backend services in the context of changing data values for a single bag item, or when the entity as a whole resolves.

Note:

This tutorial focuses on entity event handlers, not on the System.CommonResponse or the System.ResolveEntities components that reference the composite bag entities in the dialog flow definition. If you are new to composite bag entities, then now is a good time to pause and complete the Enable Real-World Entity Extraction with Composite Bag Entities and Create Different Response Types with the System.CommonResponse Component tutorials before returning here.

About The Starter Skill

This tutorial explores entity event handler functionality using a pizza ordering skill with a composite bag entity whose bag items include the PizzaToppings, PizzaDough, and DeliveryTime entities. The entity event handler that you are that you are going to create provides the following functions for these bag items:

  1. Displays a disambiguation prompt for the PizzaToppings bag item.
  2. Adds an error message to the prompt when a user fails to provide a valid value for the pizza size.
  3. Sets regular as the default value for the PizzaDough bag item.
  4. Validates the DeliveryTime bag item value as follows:
    • No new orders can be taken after 8.30 p.m.
    • Delivery time must be a time in the future.
    • No delivery in the morning (a.m.)
    • Delivery time must be at least 60 minutes later than current time.
    • Ensures that the delivery time cannot be scheduled after business hours, which conclude at 9.30 p.m.

What Do You Need?

This tutorial is for developers who are familiar with composite bag entities, JavaScript and Node.js development. To complete this tutorial, you need:

  • Access to an Oracle Digital Assistant instance (Release 21.02 or higher).
  • Version 2.5.2 (or higher) of the Oracle Bots Node SDK
  • You must have 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 verify that you have installed Node.js and NPM, open a terminal window and enter the following commands separately:
     $ node -v
     $ npm -v
  • The starter skill (CompositeBagEntityEventHandlerStarter(21.03_1).zip). Click here to download the Starter_Skill.zip file that contains this skill. Then extract Starter_Skill.zip to your local system.
  • A JavaScript IDE. For example, this tutorial uses Microsoft Visual Studio Code for its screens captures. If you feel comfortable using a text editor, then that's fine too.

If, for whatever reason, you can't finish this tutorial, you can download the completed entity event handler source file (oda.PizzaEEH.js) here and the component project that includes this source file here. If you download the ZIP of the project, you must:

  1. Unzip the project.
  2. Run npm install from the PizzaEEH folder.
  3. Run bots-node-sdk pack to create a deployment package (a .tgz file). You need Version 2.5.2 or later of the Bots Node SDK.

Get Started

Before you can use the starter skill, you need to import it into your Oracle Digital Assistant instance.

  1. Open a browser and use the following URL to access your Oracle Digital Assistant instance: <your oracle digital assistant instance>/botsui/home.
  2. Click menu icon to open the side menu.
  3. Expand Development, then select Skills.
  4. Click menu icon to close the side menu.
  5. In the Skills dashboard, click Import Skill.
  6. Navigate to the Starter_Skill file, then select the CompositeBagEntityEventHandlerStarter(21.03_1).zip file. Then click Open. Wait for the import to complete. If you can't see the imported skill, select Created Descending as the Sort by option.
  7. Select the tile for starter skill to open it for editing.
  8. If the Train option in the upper right corner shows an exclamation mark (!), it means that you need to train the model before you can use the skill.
    Description of illustration follows
    Description of the illustration.
    Click Train, accept all the default settings, then click Submit.

Test the Starter Skill

In this section, you'll use the Skill Tester to explore the pizza order conversation. In the context of guiding you through the order conversation, this part of the tutorial will call out the behavior that you will be changing by adding the entity event handler.

  1. Click Skill tester icon in the top right to open the Skill Tester.
  2. Description of illustration follows
    Description of the illustration.
  3. Enter hello or hi to see the welcome message with important customer information.
  4. Next, enter I like to order a pizza.
  5. When prompted for the pizza size, enter mega.
  6. Because mega is not a valid option for the pizza size, the same dialog is displayed once more. Following the instructions provided by this tutorial, you will implement an error prompt that extends this prompt with an error message.
    Description of illustration follows
    Description of the illustration.
    Select Large for the pizza size.
  7. When prompted for a  pizza topping, enter I like a meaty and veggie pizza.
    Description of illustration follows
    Description of the illustration.

    Note: The PizzaToppings composite bag item has been configured to allow users to select a single value. Therefore, a disambiguation dialog displays for selecting either Meaty or Veggie as the pizza topping. Because no disambiguation prompt has been configured for the PizzaToppings bag item, an auto-generated one (Please select one value for PizzaTopping) is presented instead. Following the instructions in this tutorial, you will return a custom disambiguation prompt from the entity event handler you will be building.

  8. Select Meaty from the list.
  9. When prompted for the pizza dough, select regular.
    Description of illustration follows
    Description of the illustration.

    Note: For the pizza dough option, you'll implement the following use case for this scenario using the entity event handler:
    When analyzing the conversation logs, the pizza skill owners noticed that only a few customers chose the gluten-free option. To shorten the conversation, the owners want you to set regular for the pizza dough unless gluten-free is explicitly mentioned in the initial message.

  10. Enter 11pm into the Message field when prompted for the delivery time.

    Note: Here's another use case that you'll implement with the entity event handler: You're going to limit the ordering to the store's operating hours. In this case, the pizza store is only open for business until 9.30 p.m. Deliveries outside of operating hours are not possible. Because it takes 60 minutes to make a pizza, the cutoff for the last pizza order must be 8.30 p.m. The pizza skill owners want their skill to prevent users from choosing delivery times outside of operating hours.

  11. When prompted for the delivery address, enter home into the message field.
  12. Click Reset to clear the conversation. Then click Close The Close icon.
  13. The Reset button
  14. Next, explore the Composite Bag Entity that you're going to build the entity event handler for. Select Entities Entities menu item in the left navbar.
  15. Select the PizzaBag entity.
  16. Take a quick look at the PizzaBag entities bag items: PizzaSize, PizzaTopping, PizzaDough, DeliveryTime, DeliveryAddress, and NamedLocation.

Install the Oracle Bots Node SDK

The Oracle Bots Node SDK is a node package that provides a command line interface (CLI) that you can use to generate a starter project for your entity event handler component. You can find the Oracle Bots Node SDK sources and documentation on GitHub (https://github.com/oracle/bots-node-sdk).

In this part of the tutorial, you're going to install the Oracle Bots Node SDK on your local computer. You can skill this part if you have previously installed Version 2.5.2 or higher of the Oracle Bots Node SDK. You can check the version by entering bots-node-sdk -v on the command line.

To install the Oracle Node.js Bots SDK on 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 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 (for example, 2.5.2).

Note: If you are on your company's network, access to the npm registry may be blocked. If so, contact your network administrator for information on installing the node packages required to run the bots-node-sdk command.

Create the Entity Event Handler Project

The entity event handler component is a Node application that's initialized using the command line interface (CLI) of the Oracle Bots Node SDK.

  1. Depending on the operating system that you use on your local machine, open a terminal window or command line.
  2. Create a new folder called PizzaEEH using the following command:
    $ mkdir PizzaEEH
  3. Navigate to that folder:
    $ cd PizzaEEH
  4. Initialize the node project:
    $ npm init -y

    Note: Using the initialization command of the node package manager ensures that the name of the parent folder, PizzaEEH, also becomes the name of the component project. The name is defined in the package.json file that's created by this command. This name later becomes the name of the component deployment file.

  5. Initialize and create the entity event handler component project using the following command:
    $ bots-node-sdk init -t entityEventHandler -c oda.PizzaEEH
    Running this command returns a message confirming the creation of the project.
    Description of this image follows
    Description of the illustration.

    Note: To learn more about the bots-node-sdk command that you used to create the project and other options, refer to the Oracle Bots Node SDK documentation on GitHub.

  6. To confirm that the component was created, issue these commands:
    $ cd components
    Mac:
    $ ls
    Windows:
    $ dir

Now that you've created the entity event handler component package, you're now ready to implement the entity event handler's functions. Throughout this tutorial, you'll be editing the oda.PizzaEEH.js file that's in the components file of the generated project.

Implement a Disambiguation Prompt for the PizzaToppings Entity

When you asked for two different pizza toppings, the skill responded with the default, auto-generated disambiguation prompt, Please select one value for PizzaTopping. In this part of the tutorial, you're going to create a custom disambiguation prompt for the PizzaTopping bag item. This message displays a list of values to select from, provides keyboard shortcuts for these list items, and mentions the number of selected toppings in its prompts.

  1. Open  the PizzaEEH entity event handler project in your JavaScript IDE. If you're using Microsoft Visual Studio Code, choose File > Open Folder. Navigate to the PizzaEEH folder, then click Select Folder.
  2. Select the oda.PizzaEEH.js file in the components folder.
    Description of this image follows
    Description of the illustration.
  3. In Line 33, change SomeBagItemName in to PizzaTopping.
  4. Delete the following comments on Lines 33 and 34:

    //  TODO change to a valid bag item name

       // add item level event handlers here
  5. Click Return to add a blank line at Line 34.
    Description of this image follows
    Description of the illustration.

Customize the PizzaTopping Disambiguation Message

To return a custom disambiguation message for the PizzaTopping bag item, you need to create an item-level function. In this case, you will create a publishDisambiguateMessage function, one of the entity event handler callbacks that can be used to interact with a composite bag entity while it's being resolved by a System.CommonResponse component or a System.ResolveEntities component.

  1. Add the following code to Lines 34-36 of the event handler component. 
    publishDisambiguateMessage: async (event, context) => {            
                                 
    }
                                   
    After adding the code, your entity event handler should look like this.
    Description of this image follows
    Description of the illustration.
    While the code implements the callback handler function that publishes the disambiguation dialog, it does not change the default behavior yet. To change the default disambiguation dialog, you'll need to add the actual code logic (which is what you're going to do in the next step).
  2. Copy the following code into Line 35. You can copy and paste the code from here (without the line numbers).
    35. context.getLogger().info("publishDisambiguateMessage");
    36.                        
    37. let messageModel = context.getMessageModel();
    38.            
    39. //define a list of values to select from
    40. var postbacks = [];
                
    41. //iterate the values in the disambiguation list and create a list item for each
    42. for(let i=0; i< event.disambiguationValues.length; i++){                              
    43.              
    44.   //add a character like "a", "b", "c" and the label itself as a keyword
    45.   let chr = String.fromCharCode(97 + i);              
    46.   let keywords = [event.disambiguationValues[i],chr];
    47.              
    48.   // set label as e.g. a. <label>, b. <label>
    49.   let label = chr+". "+event.disambiguationValues[i];
    50.
    51.   //define a JSON message that will be sent to the bot when a user selects a list item.
    52.   //The message invokes the setPizzaTopping custom event
    53.   let postbackPayload = {"event" : {"name":"setPizzaTopping","properties":{"PizzaTopping": event.disambiguationValues[i]}}};
    54.              
    55.   //add postback action object to list of values
    56.   postbacks.push(messageModel.postbackActionObject(label, null, postbackPayload,keywords));
    57. }            
    58. //Define the disambiguation dialog prompt
    59. let prompt = "You selected "+event.disambiguationValues.length+ " toppings. Unfortunately you can only have one. Please choose from the list.";
    60.             
    61. let textMessage = messageModel.textConversationMessage(prompt, postbacks);
    62. context.addMessage(textMessage); 
    
    When you're done, the entity event handler code should look like this:
    Description of this illustration follows
    Description of the illustration.
    The following table describes code that you've just added and also gives you information about rendering custom user interfaces with the entity event handler and postback actions. 
    Code Line(s) Comments
    37 The context object is passed to every callback function and provides access to entity and SDK functions like the MessageModel. The MessageModel enables you to create rich responses from the skill like lists and carousels. It's the preferred option to create custom component and entity event handler skill responses in Oracle Digital Assistant.
    40 Postback actions render buttons and select items when added to a skill response. In this sample, you are building a text response that displays the user-selected pizza toppings as select items. To add postback actions to a text response, you need to add them to an array.
    42 The event object gets passed to all callback functions and provides information about the current event. For the disambiguation prompt, the event object contains an array of strings with the values of the user-selected toppings. The for-loop iterates over the toppings and creates postback actions for each of them.
    45-46 In this implementation, you are adding a character shortcut to each of the select items. Users can then tap the a or b keys on their devices to select a topping instead of entering the complete label string. On a keyboard, the ASCII characters for lower case letters start with Index 97. As you are iterating the area of toppings to disambiguate, you add the index number (zero-based) to 97 to get the corresponding character. Line 56 then sets the keywords in an array of strings.
    49 To visually indicate the shortcut to users, add the character to the list item display label.
    53 The payload for a button or select item is a JSON object. This sample creates the complete disambiguation dialog and uses a custom event function that is called for each selected item. For this tutorial, this custom event is the setPizzaTopping custom event that you'll create in the next section of this tutorial. The what? gets a JSON object passed as an argument. This JSON object contains a key-value pair wherein the value is one of the user-selected pizza toppings displayed in the disambiguation dialog.
    56 This line of code creates the postback action and saves it in the array that you will later associate with the text message.
    59 You create a text message as the disambiguation prompt that displays above the select items.
    60-62 To display the disambiguation prompt, you create a textConversationObject from the MessageModel object. The text object receives the prompts and the array of postback actions as an argument. Finally, in Line 72, the message is returned for display.

Create a Custom Event

Custom components defined in an entity event handler component can be called from postback actions (buttons or list items) that are rendered by the event handler. In this tutorial, a custom event, setPizzaTopping, is called from the disambiguation dialog items to set the selected pizza value as the user's choice.

  1. To create a custom event, remove the //add custom event handlers here comment in Line 69.
    Description of this image follows
    Description of the illustration.
  2. In Line 69, add the following code. You can copy this code from here (without the line numbers).
    69. setPizzaTopping: async (event, context) => {          
    70.   context.clearItemValue("PizzaTopping");             
    71.   context.setItemValue("PizzaTopping", event.PizzaTopping);
    72.   //set a confirmation message. The boolean value true at its end sets the "keepTurn"
    73.   //behavior. True means that the bot continues with the next bag item, not waiting 
    74.   //for user input
    75.  context.addMessage("Great. Now that we've got this sorted, let us continue.", true);          
    76. }                            
                               
    The following table highlights the code lines for handling the payload sent by the postback actions. 
    Code Line(s) Comments
    70 No topping has been set for the composite bag entity. However, to use this custom event elsewhere -- not just from the disambiguation dialog -- the existing value must first be cleared.
    71 The disambiguation dialog that you created earlier sends a JSON payload that has a key-value property defined. The key name in this payload is PizzaTopping. The key names don't need to match the bag items that they're created for; you can name these keys anything. The fact that the key name matches the bag item in this tutorial is just coincidence.
    75 After updating the pizza toppings, this optional message displays to acknowledge the user selection and to guide the user through the next step.

Congratulations! You've completed the custom disambiguation prompt for the PizzaToppings entity. This means that you've completed the first part of this tutorial. Next, you'll package the event entity  handler and test out your work.

Deploy the Custom Entity Event Handler Component

In this step, you're going to package the entity event handler in to a TGZ file and then test it with the Skill Tester.

Package the Entity Event Handler

  1. Ensure you saved the latest changes of the entity event handler in your JavaScript editor.
  2. Open a terminal window or command line and navigate to the PizzaEEH folder.
  3. Run the following command:
    $ bots-node-sdk pack
    A confirmation statement for the creation of the PizzaEEH-1.0.0.tgz deployment file displays after the packaging process completes.
    Description of this image follows
    Description of the illustration.
  4. In Oracle Digital Assistant, navigate to the Composite Bag entity event handler starter skill. If you closed the skill, re-open it.
  5. Click Components components menu icon the side menu to open the custom components panel.
  6. Click + Service.
  7. Set the value of the Name field to PizzaEventHandler. Optionally, enter a value for the Description field. The PizzaEventHandler name will later display in the select dialog that you use to register the event handler with the composite bag entity.
  8. Ensure that Embedded Container is selected.
  9. Click Upload a component package file (.tgz file created by npm pack).
  10. Navigate to the PizzaEEH folder on your local machine.
  11. Select the PizzaEEH-1.0.0.tgz file, then click Open.
  12. Switch on the Enable Component Logging toggle button.
  13. Click Create.
    The deployment of the entity event handler will take a few seconds to complete. Oracle Digital Assistant displays the Ready status The Ready Status indicator icon after a successful deployment and populates the left side of the page with the oda.PizzaEEH component entry under the PizzaEventHandler header.
    Description of this image follows
    Description of the illustration.
  14. Select Entities Entities menu item in the left navbar.
  15. Select the PizzaBag entity.
  16. Expand the drop-down list in the Event Handler section and then choose oda.PizzaEEH.
    Description of this image follows
    Description of the illustration.

Test the Disambiguation Dialog

In this step, you're going to use the Skill Tester to test the disambiguation prompt.

  1. Click Conversation tester icon to open the Skill Tester.
  2. If a previous conversation exists, click Reset.
  3. Enter I like to order a pizza.
  4. Select Large as a pizza size.
  5. When prompted for a pizza topping, enter I like a meaty and veggie pizza. The PizzaTopping entity's custom disambiguation prompt displays along with the customized list of values.
    Description of this image follows
    Description of the illustration.
  6. You can now either select Meaty as a choice, or enter a. Both options will set Meaty as the pizza topping and display the confirmation prompt defined by the custom event function.
    Description of this image follows
    Description of the illustration.
  7. Click Reset.

Add an Error Message to the Pizza Size Prompt

You've probably noticed when testing the skill that the prompt displayed for selecting a pizza size always remains the same. This might be confusing to users who may not be paying close enough attention to their input to notice that they've mistakenly entered an invalid value. To help them out, you're going to add a error prompt message that alerts them the first time that they've entered an invalid value for the pizza size.

To add this error message:

  1. Navigate to the end of the code you've written for the PizzaTopping bag item. This is located at around Line 65.
  2. Add a comma after the closing bracket (},}.
  3. Description of this illustration follows
    Description of the illustration.
  4. In Line 66, delete the //add more bag items and their handlers here comment to create an empty line.
  5. Add the following code for customizing the prompts for the PizzaSize bag item at Line 66. You can copy this code from here (without the line numbers).
    66. PizzaSize: {                            
    67.   publishPromptMessage: async (event, context) => {
    68.     context.getLogger().info(context.getCurrentItem() + " : publishPromptMessage");
    69.     let promptCount = event.promptCount;
    70.     if (promptCount > 0) {
    71.       //add error message
    72.       let candidateMessages = context.getCandidateMessages();
    73.       let errorMessage = "Your provided message '" + context.getUserInput() +
    74.                          "' is not a valid pizza size. "
    75.        candidateMessages[0].text = errorMessage + candidateMessages[0].text;
    76.        context.addCandidateMessages(candidateMessages);
    77.     }
    78.    else {
    79.      context.addCandidateMessages(context.getCandidateMessages());
    80.    }
    81.   }
    82. }
    

    After you've added this code, the Event Handler Component should look like this:
    Description of this image follows
    Description of the illustration.
    The following table highlights the code lines for detecting a previously failed attempt by a user to input a valid value and for adding an error message as a prefix to the bag item prompt.
    Code Line(s) Comments
    67 The publishPromptMessage function allows you to display a custom prompt. If this function has not been set by the entity event handler, then the prompt defined for the composite bag entity item is used instead.
    69 The event object that gets passed to the publishPromptMessage function contains the promptCount attribute that keeps track of how often the prompt displays for a bag item. If the value is set to 0, then it's the first time that the prompt has been displayed for the entity to resolve. A value of 1 or higher indicates a previously failed attempt by the user to provide a valid value for the bag item. Because of this, all that your code needs to do to display an error message is to check whether the promptCount is greater than zero.
    72 - 76 The validation error message should be displayed before the prompt defined for the bag item. To enable this, the default prompt is accessed through the context.getCandidateMessages() function. The context object, also provides access to the user input, which is incorporated into the error message. To display the custom prompt, call context.addCandidateMessages() and pass the changed message as an argument.
    79 If you add the publishPromptMessage function for a bag item, then you also must return a prompt. Therefore, if the promptCount value is 0, meaning that there were no previously failed input attempts for this bag item, then you can access the candidateMessages generated by the system and return them.
  6. Save your work.

Deploy the Event Entity Handler Project and Test the publishPromptMessage Function

  1. Open a terminal window or command line and then navigate into the PizzaEEH folder.
  2. Run the following command:
    $ bots-node-sdk pack

    A confirmation statement for the creation of the PizzaEEH-1.0.0.tgz deployment file displays after the packaging process completes.
    Description of this image follows
    Description of the illustration.
  3. In Oracle Digital Assistant, navigate to the Composite Bag entity event handler starter skill. If you closed the skill, re-open it.
  4. Click Components components menu icon in the left navbar.
  5. Select the PizzaEventHandler component service.
    Description of this image follows
    Description of the illustration.
  6. Click the Change link that opens the dialog for importing the updated deployment file.
    Description of this image follows
    Description of the illustration.
  7. In the dialog, navigate into the PizzaEEH folder and select the PizzaEEH-1.0.0.tgz file.
  8. Click Open. Wait for the file to be uploaded and processed.
  9. Click Skill tester icon to open the Skill Tester.
  10. Enter I like to order a pizza.
  11. When prompted for the pizza size, enter mega.
  12. Just as before, because mega is not a valid option for the pizza size, the same dialog is displayed again. This time however, the prompt contains a validation error message that alerts the user that mega is not a valid pizza size.
    Description of this image follows
    Description of the illustration.
  13. Click Reset to clear the conversation.

Add the Code that Validates the Delivery Time

In this section, you'll validate the user-provided value for the delivery time. The following rules need to apply to validate the DeliveryTime bag item:

  • No new orders cannot be taken after 8:30 p.m.
  • The delivery time must be a time in the future.
  • No delivery in the morning (a.m.).
  • The delivery time must be at least 60 minutes later than current time.
  • The delivery time cannot be after business hours (9:30 p.m.).

Note: This tutorial assumes that the skill and its customers are all in the same timezone. Keep in mind, that the skill's timezone is UTC (Coordinated Universal Time).

  1. In your JavaScript IDE, navigate to the end of the PizzaSize bag item definition, which is around Line 82.
  2. Add an ending comma after the end curly bracket of the PizzaSize item (},) and then click Return create a new blank line (as Line 83).
  3. Add the following code to Line 83 to create the DeliveryTime composite bag item:
    DeliveryTime: {
              
    }
    
  4. To perform custom validation, add this validate function to the DeliveryTime code block at Line 84:
    validate: async (event, context) =>{
    
      return true;
    }
    
    Your DeliveryTime event handler definition should now look like this.
    Description of this image follows
    Description of the illustration.
  5. The DeliveryTime bag item is defined as a TIME entity. This means that the user input is provided in a structure as shown in the image below.
    Description of this image follows
    Description of the illustration.
  6. On Line 85, add the following code to the validate function. You can copy this code from here (without the line numbers).
    85. const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    86.
    87.  //create order date (will be created in UTC as milliseconds)
    88.  let orderDate = new Date();
    89.  let orderDateTimeInMs = orderDate.getTime();
    90.
    91. //access delivery hours, minutes and whether it is am or pm
    92. let hrs = event.newValue.hrs;
    93. let mins = event.newValue.mins;
    94  let ampm = event.newValue.hourFormat;
    95.
    96. //get timezoneOffset from user profile (channel)
    97. let timezoneOffset = context.getVariable('profile.timezoneOffset');
    98.
    99. //determine max delivery time in milliseconds
    100. let orderDay = orderDate.getDate();
    101. let orderMonth = monthNames[orderDate.getMonth()];
    102. let orderYear = orderDate.getFullYear();
    103. let maxDeliveryTimeInMilliseconds = (new Date(orderMonth + " " + orderYear + ", " + orderDay + " 8:30 pm")).getTime() + (timezoneOffset != null? timezoneOffset:0);
    104.
    105. //compose delivery date string from order date day, month, year and 
    106. //the hours and min information passed through the TIME entity. This
    107. //avoids people ordering pizza e.g. for tomorrow or yesterday
    108. let deliveryDateString = monthNames[orderDate.getMonth()] + " " +
    109.                          orderDate.getFullYear() + ", " +
    110.                          orderDate.getDate() + " " +
    111.                          hrs + ":" + mins + " " + ampm;
    112.            
    113. //delivery date should consider timezoneOffset if available
    114. let deliveryDateInMilliseconds = (new Date(deliveryDateString)).getTime() + (timezoneOffset != null? timezoneOffset:0);
    115.
    116. //hours and minutes cannot both be 0
    117. if ((hrs == 0) && (mins == 0)) {
    118.   let message = "Please enter a time. Pizzas are delivered the same day. Orders must be placed 60 minutes before delivery, but at the latest at 8:30 p.m.";
    119.   context.addValidationError('DeliveryTime', message);
    110.   return false;
    121. }
    122.
    123. //check if shop is closed for deliveries, which is for when order time plus pizza 
    124. //creation time is later than max delivery time 
    125. if ((orderDateTimeInMs + 3600000) > maxDeliveryTimeInMilliseconds) {
    126.   let message = "Sorry, but I only accept orders until 8:30 p.m. All orders after 8.30 pm must be processed by an employee";
    127.   context.addMessage(message, true)                            
    128.   context.cancel();
    129.   return false;
    130. }
    131.            
    132. //delivery is for "pm" hours until 9:30
    133. if (ampm.toLowerCase() === 'am') {
    134.   let message = "Sorry, we only deliver from 12 pm to 9.30 pm. Orders must be received 60 minutes before delivery, that is, at 8:30 pm at the latest. Please choose another time.";
    135.   context.addValidationError('DeliveryTime', message);
    136.   return false;
    137. }
    138.            
    139. //is delivery time in future? If it is not, raise validation error.           
    140. if (deliveryDateInMilliseconds < orderDateTimeInMs) {              
    141.   let message = "The delivery time cannot be in the past. Please enter a time that is at least 60 minutes ahead of the current time.";
    142.   context.addValidationError('DeliveryTime', message);
    143.   return false;
    144. }
    145.
    146. //is requested delivery time outside delivery hours?
    147. if (deliveryDateInMilliseconds > maxDeliveryTimeInMilliseconds) {              
    148.   let message = "We deliver until 9:30 pm. Orders must be placed 60 minutes before delivery. Please try again.";
    149.   context.addValidationError('DeliveryTime', message);
    150.   return false;
    151. }
    152.
    153. //is delivery within 60 minutes? If so, ask user to change to later
    154. if (deliveryDateInMilliseconds < (orderDateTimeInMs + 3600000)) {
    155.   let message = "Quality takes time. We need at least 60 minutes to create the pizza. Please provide a new delivery time";
    156.   context.addValidationError('DeliveryTime', message);
    157.   return false;
    158. }
    159.
    160. return true;
    
    While the validate function adds a lot of code to accommodate the various date validations, an Apache FreeMarker implementation of the same validation rules would have resulted in a complicated expression that would be difficult to manage. Handling complex validations is one of the strengths of the entity event handler. The following table explains the code lines for the date validation. The highlights include using the event object argument and using validation to trigger a cancel action.
    Code Line(s) Comments
    88 - 114 To validate the delivery time for an order, the entity event handler validation function must compute the values for the order date, the requested delivery time, and the maximum delivery time, in milliseconds.
    92-94 The DeliveryTime bag item is a TIME entity type. The TIME entity passes the time entered by the user as a JSON object to the event handler. To access information about the hour, minutes (and whether it's a.m. or p.m.), the JSON object contains the hrs, mins, and hourFormat attributes. The validate function receives the time information in the event object. The content of the event object is specific to an event. For the validate function, the newValue property holds the TIME entity payload.
    97 The dates are computed as UTC, which also is the time set for Oracle Digital Assistant in the cloud. To adjust UTC to local time so that orders from Duesseldorf in Germany (+1hr) have the same closing time for deliveries as orders from Mumbai in India (+5:30hrs), the time offset must be accessed from the user profile. The time offset is set by the messenger client. The embedded conversation tester used in this tutorial sets the offset. For other messengers like the Oracle Web SDK, it's up to the developer who configures the Web SDK to access the timezone offset and pass it to the skill. If no timezone is set, it will be set to 0.
    119 The context object that gets passed to the event handler function provides a function,  addValidationError, that is used in the code to write a validation message. The validation message must provide the error description as well as information needed for the user to try to enter a valid value again. If a skill has a resource bundle defined to display messages, then this resource bundle can also be accessed from the context object.
    120, 160 The validate function needs to return the false Boolean value for a validation that has failed and true for a validation that has succeeded. If no value is returned, then false is set as the default. The validate function is called after the entity has been validated (that is, when the user provides a valid time) and after any optional custom validation defined for the bag item using Apache FreeMarker has succeeded.
    127-128 Customers who place orders that are outside of the delivery times are connected to a pizza store employee. The sample skill connects customers to pizza store employees when the cancel action transition that's configured for the OBotML component state has been triggered. To trigger the cancel action from the validation code, the event handler uses the cancel() function on the context object. Because a validation message does not display when customers are connected to an employee, the code in Line 137  uses addMessage() to prompt the user.
  7. Save your work.

Deploy the Updated Entity Event Handler and Test the Delivery Time Validation

It's time for another round of testing! That means that you need to first repackage and then redeploy the entity event handler before you have another chat with the skill.

  1. Open a terminal window or command line and navigate to the PizzaEEH folder.
  2. Run the following command:
    $ bots-node-sdk pack
  3. Reopen Oracle Digital Assistant (if needed). Click Components components menu icon in the left navbar.
  4. Select the PizzaEventHandler component service.
    Description of this image follows
    Description of the illustration.
  5. Click the Change link to open the file dialog to import the updated deployment file.
    Description of this image follows
    Description of the illustration.
  6. In the dialog, navigate to the PizzaEEH folder and then select the PizzaEEH-1.0.0.tgz file.
  7. Click Open. Wait for the confirmation message.
  8. Click Skill tester icon to open the Skill Tester.
  9. Enter I like to order a small regular veggie pizza. The command contains information for the pizza type, pizza size and the pizza dough, so that navigation goes straight to the delivery time prompt.
  10. When prompted for the delivery time, Enter 10 pm. Because delivery is only available until 8:30 p.m., you should see a validation error prompt generated by the entity event handler that you just updated.
  11. Try try a time that is less than 60 minutes from your current time. For example, if it's 3:00 p.m. for you, then enter 3:30 pm. You should see a message telling you that you need to allow at least 60 minutes for the preparation and delivery of the pizza.
  12. Enter 8 am into the Message field. This time, a message should display that tells you that pizzas are only delivered from 12 p.m. to 9.30 p.m.
    Note: The skill directs you to a human agent after a certain number of failed attempts. If this happens, clear the conversation by clicking Reset and start the testing again by entering I like to order a small regular veggie pizza.
  13. When you're done testing the delivery time, click Reset.

Set the Default Value (regular) for the the PizzaDough Bag Item

The skill offers two types of dough: regular and gluten-free. Because gluten-free dough is ordered less often than regular dough, you will make regular dough the default. Unless a user explicitly mentions gluten-free, the pizza will be on a regular base. So far, the conversation has displayed a dialog for selecting one of the pizza dough types. To suppress this dialog, you need to first change a setting for the composite bag item configuration:

  1. Select Entities Entities menu item in the left navbar.
  2. Select the PizzaBag entity.
  3. Cursor over the PizzaDough item and then click Edit The Edit icon to open the Edit Bag Item page.
    Description of this image follows
    Description of the illustration.
  4. Scroll down to the Extraction Rules section and enter false in the Prompt for Value field.
    Description of this image follows
    Description of the illustration.
    Setting this field to false ensures that the PizzaDough item is not prompted for during the user conversation. The only option for setting its value for this item is through the initial user message. If the user does not explicitly request gluten-free dough at the outset of the conversation, then the entity event handler sets the value to regular dough by default.
  5. Exit the editing page for the PizzaDough item by clicking Close (located at the upper right).
  6. Click Skill tester icon to open the Skill Tester.
  7. Enter I like to order a small veggie pizza. Notice that skill now just prompts you for the delivery time. It no longer displays the pizza dough prompt.
  8. Click Reset and the close the Skill Tester.
  9. Go back to your JavaScript IDE and if needed, open the entity event handler.
  10. Navigate to Line 27, where the resolved: async (event, context) function in the entity level event handler is located.
    Description of illustration follows
    Description of the illustration.
  11. In Line 28, remove the //add your backend REST API call here comment to create blank line.
  12. Add the following block of code at Line 28. You can copy this code from here (without the line numbers).
    28. let pizzaDoughHasValue = context.getItemValue('PizzaDough') != null? true : false;
    29. if(!pizzaDoughHasValue){
    30.   //if  no value is set for the pizza dough, set it to 'regular'
    31.   context.setItemValue('PizzaDough', 'regular');
    32. }
                                

    When you're done, the code should look like this:

    Description of illustration follows
    Description of the illustration.

    This table highlights the code.

    Code Line Comments
    27 The resolved event handler function is called when the composite bag entity has been resolved. Up to this point, values should be set for all of the bag items. If there is no value set for the PizzaDough item, then the code in Line 41 sets it to regular.
    28 The PizzaDough item is based on a custom value list entity. This means that the value accessed for the bag item is either a string or null. If it's null, then no PizzaDough value has been set by the user
  13. Save your work.

Deploy the Updated Event Entity Handler and Test the Default Value

Now that you've completed the entity event handler, it's now time for some end-to-end testing.

  1. Open a terminal window or command line and navigate into the PizzaEEH folder.
  2. Run the following command:
    $ bots-node-sdk pack
    A confirmation for the creation of the PizzaEEH-1.0.0.tgz deployment file displays after the packaging process completes.
    Description of this image follows
    Description of the illustration.
  3. If needed, reopen the starter skill.
  4. Click Components components menu icon in the left navbar.
  5. Select the PizzaEventHandler component service.
    Description of this image follows
    Description of the illustration.
  6. Click the Change link.
    Description of this image follows
    Description of the illustration.
  7. Navigate to the PizzaEEH folder and then select the PizzaEEH-1.0.0.tgz file.
  8. Click Open. Wait for the confirmation message.
  9. Click Skill tester icon to open the Skill Tester.
  10. Enter I like to order a small veggie pizza. Note that the message contains no information about the pizza dough.
  11. Because the PizzaDough bag item is configured so that it's not be prompted for, the next prompt asks for the delivery time. You already have tested the DeliveryTime bag item validation in the entity event handler, so enter 8 pm.
  12. When prompted for the delivery address, enter home. The confirmation message should show the delivery of the small veggie pizza on a regular pizza dough base. So, without having to mention the pizza dough, enter your initial  message. The skill has defaulted to regular dough.
  13. Click Reset to clear the conversation.
  14. Enter I like to order a small veggie pizza with a gluten-free dough.
  15. When prompted for the delivery time, enter 8 pm.
  16. When asked for the delivery address, enter home.
    Note the confirmation prompt for veggie pizza includes gluten-free dough. This proves that the entity event handler does not set the PizzzaDough bag item value when it already has a value. This value could also have been set to regular had the initial message included a request for regular dough.

Learn More