Skip to Content
Technical Articles
Author's profile photo Moe Alhennawi

Integrating SAP Conversational AI (CAI) with a third-party system using Cloud Platform Integration (CPI – PI): Movie Bot Example

Motive

Having had several conversations with customers, colleagues, and partners about SAP CAI, one thing always stands out: integration. Chat bots offer a new user experience that in many ways is more efficient and effective than a human interaction and there are many statistics that support that statement. However, it’s challenging for a chat bot to be helpful on its own, it needs to be able to retrieve information from other systems and present it to the user.

With many developers and customers not having the resources (skills or licenses) to build a middleware application (Node.js for example) from scratch for their integration purposes, I have decided to demonstrate an integration example using SAP CPI – read more about CPI here

This example is inspired by the tutorial originally developed by the SAP CAI (formerly Recast.AI) team

 

Disclaimer

This guide is intended for knowledge sharing purposes only. The steps taken including the code’s accuracy and cleanliness, security considerations, and overall steps are not to be considered best practices.

 

Overview

Here is an over-simplified diagram of how the integration process looks like. The information here will eventually come together to a full circle when you get to the end.

 

Prerequisites

  • You must have a CPI-PI account
  • Basic knowledge of CPI
  • You have opened a free SAP CAI account
  • You have read and understood the part 1 & 2 of the movie bot tutorial

 

Steps

1.0 Forking and configuring the bot

 

Forking the bot

  • Sign into your account
  • Go to the original bot’s page here
  • Click the Fork button as shown below

Configuring the bot

  • Once the forking process is complete, go to settings by clicking the cog icon

  • Select the “Bot webhook base URL” box and type in your CPI runtime node. Your runtime node should look something like this: https://<Account Short Name>-iflmap.<SSL HOST>.<Landscape Host>/http. For example, https://l0001-iflmap.hcisbp.eu1.hana.ondemand.com/http
  • Make sure to enter /http at the end of the URL because we will be using an http adapter later

  • On the same screen click the “build” tab at the top then select the “discover” skill > actions tab > scroll down to the “_memory.yes” action
  • Click the pencil icon to edit the action block

  • Make sure “/discover-movies” is added to the URL and POST method is selected
  • Under Authentication: select basic authentication and enter your CPI tenant’s username and password
  • Under Headers: add the field Content-Type application/json
  • Under Body: use the default

 

2.0 Get your MovieDB API URL and API Key

 

Go to link below and follow the steps:

  • Create a MovieDB account
  • Make sure you are logged into your The Movie DB account
  • Follow this link The Movie DB API and follow the steps to get your API key

It should look like this:

 

3.0 Building your integration flow

Go to your CPI tenant:

  • Create an integration package (or use a pre-existing one of your liking)
  • Create a new integration flow of type “Process Integration”

Building your integration flow

  • Start by selecting an HTTPS adapter and dragging the line from “Sender” to “Start”
  • Under the connection tab, make sure that you enter/do as shown in the screenshot
  • Leave everything else for the adapter untouched

Next, we want to extract the parameters from the incoming message from CAI and save them as properties so that we use them in the MovieDB query

  • Open the menu on the left, go to the transformation button, open the “script” dropdown menu and select JavaScript
  • Add the code below and save


importClass(com.sap.gateway.ip.core.customdev.util.Message);
importClass(java.util.HashMap);


function processData(message) {
    var body = message.getBody(java.lang.String); //retrieve payload
    var myObj = JSON.parse(body); //parse payload
    //create the needed variables for the query
    var kind = myObj.conversation.memory.recording.type; 
    var genreId = myObj.conversation.memory.genre.id;
    var language = myObj.conversation.language;
    var conversationId = myObj.conversation.id;

    //assign variables to properties
    message.setProperty("kind", kind);
    message.setProperty("genreId", genreId);
    message.setProperty("language", language);
    message.setProperty("conversationId", conversationId);

    message.setHeader("Accept","application/json");
    message.setHeader("Content-Type","application/json");

	return message;
}

Next, we will query the MovieDB

  • Add a request reply box, a receiver box outside the integration bus, and an http adapter connecting the two (as shown)
  • Configure the http adapter as shown in the screenshot – replace the “api_key” field with your API key

Now that we will receive the response in the payload (body of the message), we will use the data and store them in properties again so that we use them in our final response back to the bot

  • Insert a script box of type JavaScript (look at how the integration flow looks so far)
  • Insert the following code in it (very similar to the one above except that we are using an array)


importClass(com.sap.gateway.ip.core.customdev.util.Message);
importClass(java.util.HashMap);


function processData(message) {
    var body = message.getBody(java.lang.String);
    var myObj = JSON.parse(body);
    
    var i, x = "";
    
    for (i in myObj.results) {
    x += myObj.results[i];
    }
    
    var title = [myObj.results[0].title, myObj.results[1].title, myObj.results[2].title, myObj.results[3].title, myObj.results[4].title];
    var subtitle = [myObj.results[0].overview, myObj.results[1].overview, myObj.results[2].overview, myObj.results[3].overview, myObj.results[4].overview];
    var imageUrl = [myObj.results[0].poster_path, myObj.results[1].poster_path, myObj.results[2].poster_path, myObj.results[3].poster_path, myObj.results[4].poster_path];
    var buttonValue = [myObj.results[0].id, myObj.results[1].id, myObj.results[2].id, myObj.results[3].id, myObj.results[4].id];


    message.setProperty("title", title);
    message.setProperty("subtitle", subtitle);
    message.setProperty("imageUrl", imageUrl);
    message.setProperty("buttonValue", buttonValue);

	return message;
}

 

Finally, we will add a “Content Modifier” which will contain the final payload that goes back to CAI

  • Add in a “Content Modifier”
  • In the message header, add in the authorization details from CAI, that would be your request token (find it in the settings of your bot’s page under token)
  • Add in the code below under the message body tab
  • Known issue: if the number characters in the content array exceeds 512, then the bot will show “No Reply”

Here is the JSON you will need to put in the body

{
    "replies": [
        {
            "type": "carousel",
            "content":[
            {
              "title": "${property.title[0]}",
              "subtitle": "${property.subtitle[0]}",
              "imageUrl": "https://image.tmdb.org/t/p/w600_and_h900_bestv2${property.imageUrl[0]}",
              "buttons": [
                {
                  "title": "View More",
                  "type": "web_url",
                  "value": "https://www.themoviedb.org/${property.kind}/${property.id[0]}"
                }
              ]
            },
			{
              "title": "${property.title[1]}",
              "subtitle": "${property.subtitle[1]}",
              "imageUrl": "https://image.tmdb.org/t/p/w600_and_h900_bestv2${property.imageUrl[1]}",
              "buttons": [
                {
                  "title": "View More",
                  "type": "web_url",
                  "value": "https://www.themoviedb.org/${property.kind}/${property.id[1]}"
                }
              ]
            },
            {
              "title": "${property.title[2]}",
              "subtitle": "${property.subtitle[2]}",
              "imageUrl": "https://image.tmdb.org/t/p/w600_and_h900_bestv2${property.imageUrl[2]}",
              "buttons": [
                {
                  "title": "View More",
                  "type": "web_url",
                  "value": "https://www.themoviedb.org/${property.kind}/${property.id[2]}"
                }
              ]
            },
            {
              "title": "${property.title[3]}",
              "subtitle": "${property.subtitle[3]}",
              "imageUrl": "https://image.tmdb.org/t/p/w600_and_h900_bestv2${property.imageUrl[3]}",
              "buttons": [
                {
                  "title": "View More",
                  "type": "web_url",
                  "value": "https://www.themoviedb.org/${property.kind}/${property.id[3]}"
                }
              ]
            },
            {
              "title": "${property.title[4]}",
              "subtitle": "${property.subtitle[4]}",
              "imageUrl": "https://image.tmdb.org/t/p/w600_and_h900_bestv2${property.imageUrl[4]}",
              "buttons": [
                {
                  "title": "View More",
                  "type": "web_url",
                  "value": "https://www.themoviedb.org/${property.kind}/${property.id[4]}"
                }
              ]
            }
          ]
        }
    ]
}

Optional, add the following groovy script if you want to see how your payload looks like at the end of the integration flow

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import com.sap.it.api.mapping.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;


def Message processData(Message message) {

	map = message.getProperties();
	property_ENABLE_PAYLOAD_LOGGING = "TRUE";
	if (property_ENABLE_PAYLOAD_LOGGING.toUpperCase().equals("TRUE")) {
		def header = message.getHeaders() as String;
		def body = message.getBody(java.lang.String) as String;
		
		String timeStamp = new SimpleDateFormat("HH:mm:ss.SSS").format(new Date());
		String logTitle = timeStamp + " JSstep ";
		  
		def messageLog = messageLogFactory.getMessageLog(message);
		if (messageLog != null) {
			messageLog.addAttachmentAsString(logTitle, body, "text/xml");
		}
	}
	return message;
}

Final look at the integration flow

4.0 Deploy and test your bot

You should be good to go now, however, feel free to ask for support in the comments section below. I’d love to hear from the community how this example can be enhanced through better code, instructions, or any other way.

5.0 Take Action

Part of my incentive of writing this blog is not only to support the SAP community members in their development, but also to hear their feedback and work together to continue building better solutions. With that, I’d like to ask you all to try this out and provide your feedback in the comments: authoring tips, improved code snippets, questions, or anything that you believe will help the community.

 

Mohammad Alhennawi

Technology Consultant, SAP

Assigned Tags

      4 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Homiar Kalwachwala
      Homiar Kalwachwala

      Thank you Moe for sharing. Very informative & detailed.

      Author's profile photo Moe Alhennawi
      Moe Alhennawi
      Blog Post Author

      Thank you Homiar!

      Author's profile photo Quintin Scheitlin
      Quintin Scheitlin

      Love it! Thanks for sharing your inspiration and making your results easily reproducible. 

      Author's profile photo Moe Alhennawi
      Moe Alhennawi
      Blog Post Author

      Thank you for the benevolent words, Quintin!