Build a Yelp Assistant using Einstein Bots

What if you had a “bot-like” Yelp Assistant who could help you find a restaurant given a Price Range, Choice of Food and your City ? Well, the answer is – YES. And the best part is – you can build it using an Einstein Bot with just a few lines of code. Let’s get started started!

Setting up Yelp

If you do not have a Yelp Account then you need to sign up for one – https://www.yelp.com/signup. After you successfully complete the sign-up process click on this link – https://www.yelp.com/developers/v3/manage_app to create an App in Yelp. If you scroll a bit down, you should be able to see a button named Join the developer beta. Ensure you click the same so as to avail some cool features such as the GraphQL. Make a note of the API Key as we will need the same in order to make API calls from Salesforce to Yelp.

GraphQL

Yelp uses the GraphQL for it’s search API which makes it super easy to construct the search requests and obtain ONLY the data you are expecting unlike a predefined output. Here is the link to the guides to learn more about Yelp GraphQL – https://www.yelp.com/developers/graphql/guides/intro.

You can also play around with GraphQL using an interactive console – https://www.yelp.com/developers/graphiql.

The query (GraphQL) that we would be using to obtain restaurants filtered by Location, Category and Price would look like as below –

 {
  search(categories: "Pizza", location: "San Francisco", price: "1,2", limit: 3) {
    business {
      name
      rating
      price
      location {
        formatted_address
      }
    }
  }
}

This would yield a response like as shown –

Yelp GraphQL

Let’s Write Some Apex

Let’s implement the Apex Class that would expose a routine to perform a GraphQL search against the Yelp database.

Yelp.cls
/**
 * The Apex Class that implements the
 * Yelp API.
 * 
 * @author      Shruti Sridharan
 * @since       07 Nov 2018
 * @revision    N/A
 */
public class Yelp {
    /**
     * Credentials and Endpoints.
     */
    private static final String GRAPH_API    = 'https://api.yelp.com/v3/graphql'
                                ,API_KEY     = '[INSERT-YOUR-API-KEY]'
                                ,GRAPH_QUERY = '{search(categories:"CAT",location:"LOC",price:"$",limit:5){business{name,rating,price,location{formatted_address}}}}';
    
    /**
     * Invoke the Yelp GraphQL API with
     * the Query Parameters.
     * 
     * @param       q                   Parameters for the GraphQL such as
     *                                  the Category, Location and Price.
     * 
     * @return      {GraphQLResponse}   API Response that has the list of
     *                                  Businesses found matching the specified
     *                                  query.
     */
    public static GraphQLResponse find( Query q ) {
        HttpRequest req = new HttpRequest();
        
        req.setMethod( 'POST' );
        req.setHeader( 'Authorization', 'Bearer ' + API_KEY );
        
        /**
         * Build GraphQL
         */
        req.setEndpoint(
            GRAPH_API + '?query=' + 
            GRAPH_QUERY
                .replace( 'CAT', EncodingUtil.urlEncode( q.categories, 'UTF-8' ) )
                .replace( 'LOC', EncodingUtil.urlEncode( q.location,   'UTF-8' ) )
                .replace( '$',   q.price )
        );
        
        HttpResponse httpRes = new Http().send( req );
        
        if( httpRes.getStatusCode() == 200 ) {
            //Parse the Response
            return ( GraphQLResponse ) JSON.deserialize( httpRes.getBody(), GraphQLResponse.class );
        }
        
        return NULL;
    }
    
    /**
     * Representation of a GraphQL
     * Query
     */
    public class Query {
        @InvocableVariable(required = TRUE)
        public String categories;
        
        @InvocableVariable(required = TRUE)
        public String location;
        
        @InvocableVariable(required = TRUE)
        public String price;
        
        public Query() {}
        
        public Query( String categories, String location, String price ) {
            this.categories = categories;
            this.location   = location;
            this.price      = price;
        }
    }
    
    /**
     * Classes Representing the JSON
     * Output
     */
    public class Location {
        public String formatted_address;
    }
    
    public class Business {
        public String name;
        public String rating;
        public String price;
        public Location location;
    }
    
    public class Search {
        public List<Business> business;
    }
    
    public class Data {
        public Search search;
    }
    
    public class GraphQLResponse {
        public Data data;
    }
}

You can notice that firing a GraphQL isn’t any different. After all, it is a simple POST request with the API Key in the header and the query (GraphQL) as a URL Parameter.

The response of the API is deserialized into a class of type – GraphQLResponse.

Note: It is important to keep the credentials and endpoints hard-coded within the class instead of a Custom Setting or Custom Metadata. The Bot fails to fire the Apex failing to do the same.

YelpBot.cls

The whole idea is to allow the Einstein Bot to interact with the Yelp API to display the restaurants for a given search query. For an Einstein Bot to invoke a piece of Apex Code, there are few prerequisites that needs to be established.

  1. The Apex Class should expose a method that has the annotation – @InvocableMethod
  2. The parameter of the method will always be of type – List<T> where T should be an Apex Class with public members annotated with @InvocableVariable. The Apex Class should also have an empty constructor if you plan to implement a parameterized constructor.
  3. The return type of the method should also be of type – List<T> where T should be an Apex Class with public members annotated with @InvocableVariable.

Thus, we need to write another Apex Class that in-turn calls the Yelp.find() at the same time meeting all the prerequisites.

/**
 * The Apex Class that will be invoked
 * by the Einstein Bot.
 * 
 * @author      Shruti Sridharan
 * @since       08 Nov 2018
 * @revision    N/A
 */
public class YelpBot {
    /**
     * Method invoked by the Bot in
     * order to find Restaurants given
     * a City, Food and Price Range(using
     * the Yelp API).
     * 
     * @param       query                   City, Food and Price Range
     * 
     * @return      {List<YelpBotResponse>  Restaurants Found
     */
    @InvocableMethod
    public static List<YelpBotResponse> findRestaurant( List<Yelp.Query> query ) {
        Yelp.GraphQLResponse gqlResp = Yelp.find( query[0] );
        
        if( gqlResp != NULL ) {
            String retaurantsFound = '\n';
            Integer index = 1;
            for( Yelp.Business restaurant : gqlResp.data.search.business ) {
                retaurantsFound += 
                    index++                                 + ') '+
                    restaurant.name                         + '|' + 
                    restaurant.rating                       + '|' + 
                    restaurant.price                        + '|' + 
                    restaurant.location.formatted_address   + '\n\n';
                
            }
            
            return new List<YelpBotResponse>{ new YelpBotResponse( retaurantsFound ) };
        }
        
        return new List<YelpBotResponse>{ new YelpBotResponse( 'Oops! We did not find any 🙁' ) };
    }
    
    //Bot Response Class
    public class YelpBotResponse {
        @InvocableVariable
        public String data;
        
        public YelpBotResponse( String data ) {
            this.data = data;
        }
    }
}

You can notice that in the above code, we are concatenating all the restaurants found and returning the same as a chunk back to the Bot.

Permission Set – sfdc.chatbot.service.permset

Ensure that both the above Apex Classes have been added to the Permission Set –

sfdc.chatbot.service.permset

Ouch! That was a bit of Apex. Now, let’s start building the bot.

Einstein Bot Prep

Before we start building the bot, there are a couple of things that needs to be completed.

Live Agent Setup
  1. Enable Live Agent
  2. Create a Skill for the System Administrator Profile and assign it to yourself
  3. Create a Chat Button (You can use the Skill created in Step 2)
  4. Create a Live Agent Configuration (You can use the Skill created in Step 2)
  5. Create a New Live Agent Deployment

You can use the Deployment Code received in Step 5 and the Chat Button Snippet obtained in Step 3 to create a Visualforce Page and add the same to a Force.com Site.

Snap-ins Setup

Ensure that you have also created a Snap-in Deployment.

If you find all of these overwhelming, then I would highly recommend you to take a look at the Trailhead Project – https://trailhead.salesforce.com/en/content/learn/projects/build-an-einstein-bot to get yourselves acquainted with the Einstein Bot setup.

Building the Bot

After that lengthy prep, let’s finally start building it. Here is a quick video that demos the entire building process.

Bot in Action

Let’s take a peek at the bot in Action –

Troubleshooting Your Bot

Here are some tips to make a note of:

  1. If you observe that the Bot is failing to fire the Apex Action, then you probably have forgotten to add the Apex Classes to the Permission Set – sfdc.chatbot.service.permset
  2. You can always use the Event Logs from Performance tab to identify any errors like as shown below – Performance Logs.pngBot Error.png
Advertisements

One thought on “Build a Yelp Assistant using Einstein Bots

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s