In the previous tutorials in this series, you designed the user interface for an app named Learn More. In this tutorial, you update the app to search Wikipedia for articles on a specific subject, and then paste links to the articles in the chat window for the agent, ready for sending to the visitor.

Let's work out the app logic:

  1. The app makes a request to a MediaWiki API endpoint to search for articles about a chosen subject.
  2. If the request is successful, the app converts the results data into a message and pastes the message in the chat's message area. The app uses the Apps framework API to paste the message. The agent can simply press Enter to send the links to the visitor.
  3. If the request fails, the app should show an error message.

This tutorial covers the steps to building the app logic:

  1. Adding an event listener
  2. Making the API request
  3. Post the article links in the chat text area
  4. Code complete!

This tutorial is part of a series on building a Zendesk app:

Adding an event listener

The first step is to add an event listener for the Get article links button in your user interface. After an agent enters a subject and clicks the button, the app should perform the following tasks:

  1. Retrieve the subject string the agent entered in the form
  2. Make an API request to search for articles on the subject

To add an event listener

  • In the main.js file, insert the following code snippet (highlighted) in the self-executing function at the top of the file:

    (function () {  var client = ZAFClient.init();  client.invoke('resize', { width: '100%', height: '180px' });  showSearchForm();
      document.getElementById("get-btn").addEventListener("click", function(event) {    event.preventDefault();    var search_str = document.getElementById("subject-field").value;    getArticles(search_str, client);  });})();

    The highlighted snippet adds a "click" event listener to the element with an id of get-btn, which is the id you used for your Get article links button. When the user clicks the button, the script runs the function that's specified as an argument, which consists of 3 statements:

  • The first prevents the button from submitting the form

  • The second retrieves the value of the form input field. You used subject-field as the id of your input field.

  • The third calls a custom function named getArticles(), which you'll define in the next section. The functions takes the search_str variable and the ZAF client object as arguments. You'll need both to make the API request, described in the next section

Making the API request

You can use the client.request() method to make HTTP requests to external APIs. The request() method lets you get around the cross-domain restrictions of browser requests by routing your requests through a proxy server rather than through the browser directly.

Defining the API request

The client.request() method takes as an argument an object that defines the request settings. Example:

var settings = {  url:'https://www.example.com/api/resource.json',  data: { fname: 'Jack', lname: 'Sprat' },  dataType: 'json'};
client.request(settings).then(  // do something with the response);

What are the request settings for the Learn More app? The Search API page on the MediaWiki site lists the information. The app will search for the articles using the following API endpoint:

https://en.wikipedia.org/w/api.php

An endpoint is a URL where you can get or set information about a resource. To perform an article search, the endpoint takes the following parameters:

  • action - the operation to perform. See the API:Query action in the developer docs
  • srsearch - the search term
  • srlimit - the total number of articles to return
  • list - the data to return
  • format - the format of the data to return

You'll specify the following parameter values:

{  action: 'query',  srsearch: {search_term},  srlimit: 3,  list: 'search',  format: 'json'}

Here's how the information maps to the request settings:

  • the url is "https://en.wikipedia.org/w/api.php", the API endpoint
  • the data are the endpoint parameters defined above
  • the dataType is json (what the app expects in the response)

The MediaWiki API also requires that you specify a custom "Api-User-Agent" header in the request's headers. See Identifying your client on the MediaWiki site. You can set the header as follows in the request settings:

headers: {'Api-User-Agent': 'MyChatApp/1.0 ([email protected])'}

The header should identify your script or service and provide some means of contacting you, such as an e-mail address.

Searching for Wikipedia articles

In the previous section, you sussed out the parameters of a Wikipedia API search and well as the settings to make an API request. Plug this information into the client.request() method to enable your app to make the requests.

To search for the Wikipedia articles

  1. Insert the following function in the main.js file:

    function getArticles(search_str, client) {  var parameters = {    action: "query",    list: "search",    srsearch: search_str,    srlimit: 3,    format: "json",  };  var settings = {    url: "https://en.wikipedia.org/w/api.php",    data: parameters,    headers: { "Api-User-Agent": "MyChatApp/1.0 ([email protected])" },    dataType: "json",  };
      client.request(settings).then(function (response) {    console.log(response);  });}

    If the request is successful, the app displays the response data in the browser's console with console.log(response).

    Requests are asynchronous. In other words, the app doesn't wait for a response. It continues working normally while the request is being handled. The app gets a callback when the response is received. The .then()method is used to wait for the callback. The method returns a Promise and takes two arguments: a function to run on success and a function to run on failure. The statement basically means, "make the request, then do one of these two things after you know the outcome."

    client.get('ticket.requester.id').then(  inline_fn_on_success,  inline_fn_on_failure);

    However, one of the idiosyncrasies of the MediaWiki API is that it returns a success response (status 200) even if it encountered an error. The 200 response body contains an object named error. You can use this information to manage errors, as described in the next step.

  2. Add the following snippet (highlighted) to the function that runs on success:

    client.request(settings).then(  function(response) {    if (response.error) {      showError(response.error);    }    console.log(response);  });

    You defined the showError() function in the previous part of this tutorial. You'll update it next to display the error data.

  3. Make the following changes (highlighted) to the showError() function in main.js to handle the response error data:

    function showError(error) {  var error_data = {    code: error.code,    info: error.info,  };  ...}

    After receiving the error data, the showError() function extracts the error.code and error.info values to display them in the error template.

  4. Check the results of the request in your browser console. Save the file, reload the chat window, run a search on "Einstein", and look for the results in the browser console.

    Note: If nothing happens and you have a host-mapped domain, change the URL of the chat window to the default Zendesk domain (https://yoursubdomain.zendesk.com/...) and reload it.

    You should get an object named query. The object in turn should contain an array named search that lists 3 articles and their properties:

  5. Test the results in case of failure by introducing a typo in one of the request parameters (example, action: 'query'). Save the file, reload the chat window, and run a search. The app should display the error template with dynamic information about the error.

    Remember to fix the typo in your request parameters after you're satisfied that the error handling works as intended.

Your getArticles() function should look as follows:

function getArticles(search_str, client) {  var parameters = {    action: "query",    list: "search",    srsearch: search_str,    srlimit: 3,    format: "json",  };  var settings = {    url: "https://en.wikipedia.org/w/api.php",    data: parameters,    headers: { "Api-User-Agent": "MyChatApp/1.0 ([email protected])" },    dataType: "json",  };
  client.request(settings).then(function (response) {    if (response.error) {      showError(response.error);    }    console.log(response);  });}

Post the article links in the chat text area

The Zendesk Apps framework gives you access to several Chat actions. The framework's Chat object has a chat.postToChatTextArea action that posts a predefined message in the chat's text area. The message isn't sent until the agent presses Enter. You'll use this action to post the article links in the message area.

The first order of business is to convert the article data returned by the API into a message for the chat text area. The message should consists of 3 lines, each containing the title and URL of an article.

The data returned by the API has the following structure:

{  query: {    search: [      {        ns: 0,        size: 143771,        snippet: "<span class="searchmatch">Albert</span> <span class="searchmatch">Einstein</span>...",        timestamp: "2017-07-17T09:13:47Z",        title: "Albert Einstein",        wordcount: 15112     },      ...    ]  }}

The data includes a title, but not a URL. However, you can create the URL by appending the article title to the Wikipedia base URL. Example:

var url = 'https://en.wikipedia.org/wiki/' + 'Albert Einstein';

Note: Wikipedia URLs use underscores for spaces in titles. However, you don't have to replace spaces with underscores in your title strings. Any Wikipedia URL with spaces in the title automatically redirects to a page with underscores. Example: "https://en.wikipedia.org/wiki/Albert_Einstein".

Tip: If your visitors use another language, you can change the 'en' locale in the url to the desired locale. Example: 'https://fr.wikipedia.org/wiki/'.

To post the article links in the text area

  1. In the getArticles() function, replace the console.log(response) statement in the request callback with the 3 statements (highlighted):

    ...  client.request(settings).then(    function(response) {      if (response.error) {        showError(response.error);      }      var articles = response.query.search;      var msg = buildMessage(articles);      client.invoke('chat.postToChatTextArea', msg);    });

    Based on the known structure of the response data, you can access the array of articles with the following expression: response.query.search. You pass the array to a custom function named buildMessage() that you'll write next.

    Finally, you run the app framework's chat.postToChatTextArea action to post the returned message in the text area, ready to send to the visitor.

  2. Add the buildMessage() function definition to the main.js file:

    function buildMessage(articles) {  var message = '';  var url = 'https://en.wikipedia.org/wiki/';  for (i = 0; i < articles.length; i++) {    message += '"' + articles[i].title + '" - ' + url + encodeURI(articles[i].title) + '\n';  }  return message;}

    The function iterates through the array of articles, creating a line for each that consists of the title (articles[i].title) and the url (url + encodeURI(articles[i].title)), separated by a hyphen.

    The function returns the completed message, which is then used in the chat.postToChatTextArea action:

    var msg = buildMessage(articles);client.invoke('chat.postToChatTextArea', msg);
  3. Save the file, reload the chat window, and run a search.

    The app should post the results in the text area:

    Note: If nothing happens, make sure the chat has started by typing a few words in the chat area. The postToChatTextArea action only works if the chat area is active.

    When the agent hits Enter, the message is sent to the visitor. The links are clickable in the visitor's chat window:

Code complete!

Congratulations! You developed your first working Chat app. The final version of your main.js file should look like this:

(function () {  var client = ZAFClient.init();  client.invoke("resize", { width: "100%", height: "180px" });  showSearchForm();
  document.getElementById("get-btn").addEventListener("click", function(event) {    event.preventDefault();    var search_str = document.getElementById("subject-field").value;    getArticles(search_str, client);  });})();
function getArticles(search_str, client) {  var parameters = {    action: "query",    list: "search",    srsearch: search_str,    srlimit: 3,    format: "json",  };  var settings = {    url: "https://en.wikipedia.org/w/api.php",    data: parameters,    headers: { "Api-User-Agent": "MyChatApp/1.0 ([email protected])" },    dataType: "json",  };
  client.request(settings).then(function (response) {    if (response.error) {      showError(response.error);    }    var articles = response.query.search;    var msg = buildMessage(articles);    client.invoke('chat.postToChatTextArea', msg);  });}
function buildMessage(articles) {  var message = '';  var url = 'https://en.wikipedia.org/wiki/';  for (i = 0; i < articles.length; i++) {    message += '"' + articles[i].title + '" - ' + url + encodeURI(articles[i].title) + '\n';  }  return message;}
function showSearchForm() {  var source = document.getElementById("search-template").innerHTML;  var template = Handlebars.compile(source);  document.getElementById("content").innerHTML = template();}
function showError(error) {  var error_data = {    code: error.code,    info: error.info,  };  var source = document.getElementById("error-template").innerHTML;  var template = Handlebars.compile(source);  var html = template(error_data);  document.getElementById("content").innerHTML = html;}

Your iframe.html file should look like this:

<!DOCTYPE html><html>
<head>  <meta charset="utf-8">  <link rel="stylesheet"    href="https://cdn.jsdelivr.net/combine/npm/@zendeskgarden/[email protected],npm/@zendeskgarden/[email protected],npm/@zendeskgarden/[email protected]">  <link rel="stylesheet" href="main.css"></head>
<body>  <h2>LEARN MORE</h2>
  <div id="content"></div>
  <footer>    <a href="https://mysite.github.io/support" target="_blank">Report bugs</a>  </footer>
  <script id="search-template" type="text/x-handlebars-template">    <form id="search-form">      <p>        <label for="name">Subject</label><br>        <input          type="text"          name="subject-field"          id="subject-field"        >      </p>      <p>        <button id="get-btn" class="c-btn">Get article links</button>      </p>    </form>  </script>
  <script id="error-template" type="text/x-handlebars-template">    <div>      <p style="color: red;"><strong>Something went wrong.</strong></p>      <p><strong>Error code</strong>: {{code}}</p>      <p><strong>Info</strong>: {{info}}</p>      <p>Please report a bug at the link below or        <a id="reset-link" href="">reset</a>        the form.</p>    </div>  </script>
  <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/handlebars.min.js"></script>  <script src="main.js"></script></body>
</html>

If you like, keep tweaking your app.

The final step involves validating, packaging, and installing the app in your Zendesk Chat instance. Continue to Part 5 - Installing the app in Zendesk Chat.