Umbraco

Accio! Umbraco Heartcore meets Harry Potter

Poornima Nayar works her magic with this innovative setup using Umbraco Heartcore

Written by: Poornima Nayar

This is for those who didn't get their Hogwarts letter - now is your time to make magic! MVP Poornima Nayar cleverly shows how to set up Umbraco Heartcore to summon content for a Google conversational action. Wand at the ready...✨

Time to work some magic...

Now I know what you're thinking...what on earth is “Accio”? In the novel and movie series Harry Potter, 'Accio' is a Summoning Charm that summons an object towards the caster. It can summon objects in direct line of sight of the caster, as well as things out of view, by calling the object aloud after the incantation. See where I'm going with this?

Yep - I am going to try to summon some of the characters from the Harry Potter series to me! To cast and fulfil the spell, I can use Actions on Google (more on that later) but first I'll need somewhere to store and manage the content. For that, I'm going to use Umbraco Heartcore.

Accio Umbraco Heartcore!

Umbraco Heartcore is the headless CMS version of the friendly CMS, Umbraco, with all the same content editing goodness. It is a headless CMS which means that the view engine does not exist. While traditional CMS-es focus on being a single solution for managing content and providing a front-end, headless CMS focuses entirely on managing and delivering your content. The CMS acts as a store for your content and you can use the APIs to provide a front-end of your choice. 

Omnichannel content delivery animation gif

For developers, headless CMS means the freedom to choose whichever front-end they want or need. When you look from a marketeer’s perspective, what you have is a very powerful tool which can help brands build omni-channel experience for users; from websites to knowledge bases to IoT devices – the same content is used to build smart, cohesive, seamless experience to who consumes it.

Earlier this year, I spoke at NDC London about integrating a Blazor Single Page Application using the GraphQL Endpoint in Umbraco Heartcore. I used a catalogue of content, themed around Harry Potter – the seven books, some of the most famous/infamous characters and the houses of Hogwarts.

Let's take a look at how I set up that catalogue...

 

Setting up in Umbraco Heartcore

As Umbraco Heartcore is set up as a SaaS offering, all I need to worry about is setting up document types. Here's what my Character document type looks like:

Umbraco Headless backoffice solution screenshot

Here's an example of how content based on the above document type looks:

Umbraco Headless backoffice editing solution screenshot

Now I have a catalogue of content about various different characters. I can ask about a character, and then I expect to get some information about that character. And the information I receive is the value of the property IOT Description on the content node corresponding to the character in my Heartcore instance.

 

A 'portkey' to some extra content

So I have the content - but how do I summon it? This is the part where I created a conversation action with Google, to use with Google Assistant. If you'd like to see how I set that up, you can follow along here.

 

Azure Function Set Up and Code

I want my Azure Function to run when it receives an HTTP POST request from the Dialogflow Agent, so my Azure function is set up to respond to an HTTP Trigger. You can specify which type of trigger to use for your Azure Function when you create the project in Visual Studio, so the scaffolding will be adjusted appropriately for you.

My Azure Function Setup needs two Nuget packages – Umbraco.Headless.Client, to query my Heartcore instance and Google.Cloud.Dialogflow.V2 to work with a strongly-typed WebhookRequest and WebhookResponse objects. They are representations of the JSON request body sent to the webhook and the JSON response which the Assistant expects. Google uses Protocol Buffers to serialize the request and response objects and using libraries like Json.Net won’t help here. The Nuget package ships with a Json parser to parse the incoming Json to the strongly typed WebhookRequest object.

In order to query the character details in my Umbraco Heartcore instance, I am using the Content Filter endpoint. This endpoint can be used to filter content based on the value of specific properties. A search term or a value can be passed in and the endpoint will filter all content based on whether a match has been found for the search term in the property values. The match type specifies the match criteria, and can be “Like” or “Contains”. “Like” helps to filter content with similar property values - i.e. “page” and “pages” will be matched for a search term “page” among the property values.  “Contains” helps to filter content with property values that contain the search term.

i.e. With “hello world” as the search term, the content returned would have property values which can be “hello” and/or “world”. An optional content type can also be specified with the filter endpoint.

With the Umbraco Headless Client Nuget package I can query this endpoint using the Filter() method which accepts a ContentFilter object as parameter and returns a PagedContent object as the result. I want to filter content only of the type “character”, so I create the ContentFilter object passing in the content type alias “character” and an array of ContentFilterProperties

For the ContentFilterProperties object I create and build a list of ContentFilterProperties type and add my filter property to the list.

My character content type has a “characterName” property. I want to filter all content where the characterName property value contains the search term. The search term in this instance is the name of the character which Dialogflow deciphers as the $person parameter value. So if the user asks for Hermione or Hermione Granger, it returns the details for Hermione Granger.

//create a HeartCore ContentDeliveryService object passing in the alias of the Heartcore instance
var cdnService = new ContentDeliveryService("heartcore-instance-name");

var filterProperties = new List<ContentFilterProperties>();

//create and add filter properties
filterProperties.Add(new ContentFilterProperties("characterName", searchTerm , ContentFilterMatch.Contains));

//filter out only the character content type
var filter = new ContentFilter("character", filterProperties.ToArray());

//call the Filter endpoint passing in the filter
var items = await cdnService.Content.Filter(filter);

To get the name of the character from the JSON request body the following code can be used. It is important to use the JsonParser which ships with the Google client library to deserialize the JSON request.

//Ignore unknown fields to futureproof the webhook against new fields added to the Webhook object
            var jsonParser = new
JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true));

            string requestBody;

            //Read the request body
            using (var reader = new StreamReader(req.Body))
            {
                requestBody = await reader.ReadToEndAsync();
            }

            //Parse the request body into a strongly-typed WebhookRequest object
            WebhookRequest request = jsonParser.Parse<WebhookRequest>(requestBody);

            //Get the name of the character from the parameter field "person"
            var searchTerm = request.QueryResult.Parameters.Fields["person"].StructValue.Fields["name"].StringValue;

To send the response back to the user, a WebhookResponse object must be sent back. I create a WebhookResponse object and set the FulfillmentText property to the value of the property “iotDescription” on the content item returned.

var fulfillmentText = "Sorry, I could not find anything about " + searchTerm;

if(items != null && items.Content!=null &&  items.Content.Items !=null && items.Content.Items.Any())
{
   fulfillmentText = items.Content.Items.FirstOrDefault().Properties["iotDescription"].ToString();
}

//form the webhook response object passing in the fulfillmenttext
WebhookResponse response = new WebhookResponse
{
    FulfillmentText = fulfillmentText
};

//return the result with the response which will served to the user
return new OkObjectResult(response);

The user gets to hear the value of the FulfillmentText property of the WebhookResponse object. On devices with a screen, the text is also displayed to the user. 

You can send a rich response which contains a title, description and image as well using the FulfillmentMessage object which lets you build various kinds of rich responses.

 

Putting it all together, this is a gist of my Azure Function.

To test my webhook fulfilment I'll use nGrok which lets me test my Azure Function in development. Once I have tested my intent fulfilment, I can integrate my Dialogflow Agent to my Actions Project, but more on that in my follow-up article.

Mischief, managed!

And that is it! I can ask for “Accio Harry” and it should summon some details about Harry to the caster of the charm, i.e. me! Here is a sample below!

Umbraco Headless CMS - test simulator

Be sure to check out my article on setting up the conversational action on Google Dialogflow!

I have left out details on how to publish the action to a production environment owing to the length of the article. But here are some useful resources about it. 

Pre-launch checklist 

✨ Versioning 

Submit your project

 

I hope you enjoyed reading the article! Ta and Hi-Five You Rock!

______________________________________________________________________________________

 

And H5YR to you too, Poornima! 10 points to Hufflepuff 😉

 

Poornima Nayar is a 2x Umbraco MVP, Microsoft MVP and Umbraco Certified Master. Be sure to say hello on Twitter @poornimanayar! 🙌