In the previous tutorial, you updated the web app to get tasks from the Asana API and then display them in the Zendesk app. In this tutorial, you'll update the web app to access framework APIs.

The tutorial covers the following tasks:

This tutorial is the fourth part of a series on building a server-side Zendesk app:

Disclaimer: Zendesk provides this article for instructional purposes only. The Zendesk Support team does not provide support for the content. Please post any issue in the comments section below or in the Zendesk Apps community, or search for a solution online.

Planning the update

The error messages displayed by the app are unsatisfactory -- they're just plain text in a blank page.

In this section, you'll use the Apps framework's notify method to display success or error messages with in-product notifications.

The tutorial only scratches the surface of the framework APIs. For a comprehensive list of resources at your disposal, see the Support API docs.

Retrieve and store the app parameters

The ZAF SDK needs two parameters to create a client object that can interact with the framework: the url of the Zendesk Support instance (its origin) and the app's unique id (its app_guid). For details, see Core Concepts.

You must retrieve the parameters from the initial page request made by Zendesk Support when the app starts. This is the page specified in the manifest.json file, which is http://localhost:8080/sidebar in this case. Zendesk Support appends the parameters to the URL as query string parameters. Example:

https://localhost:8080/sidebar?app_guid=f278bc69-6098-aab88a5ec49f&origin=https%3A%2F%2Fomniwear.zendesk.com

To retrieve the app parameters from the query string and store them:

  1. In app.py, modify the sidebar route as follows (in bold):

    @route('/sidebar')
    def send_iframe_html():
        qs = request.query_string
        response.set_cookie('my_app_params', qs)
        return template('start')
    

    Here's how it works:

    • The first bolded line uses the Bottle request object to retrieve the query string and assigns it to the qs variable
    • The second line uses the Bottle response object to save the query string in a cookie on the user's browser for other pages.
  2. Because you introduced the request and response Bottle objects, make sure to import them at the top of the file:

    from bottle import route, run, template, request, response

To make the app parameters available to the page that will interact with the framework, you must append them as a query string to any link to the page. When your server sends the page to the iframe, Zendesk Support reads the URL parameters from the response's referer header.

For the tutorial, you want the page that lists tasks to access the framework API to display an in-product notification that the request was successful. A link to that page appears in the start.tpl template:

<a class="btn btn-default btn-block" href="list" role="button">List tech note ideas</a>

To append the app parameters to the link:

  1. In app.py, modify the sidebar route as follows (in bold):

    @route('/sidebar')
    def send_iframe_html():
        qs = request.query_string
        response.set_cookie('my_app_params', qs)
        return template('start', qs=qs)
    

    You pass the query string to the start.tpl template so you can add it to the URL of the list page.

  2. In the start.tpl template, append the query string to the URL of the list page:

    ... href="list?{{qs}}" ...

    When a user clicks the link in the iframe, the query string is sent to the server and then back to the iframe in the response's referer header.

    Note: You don't have to append the parameters to a link if the linked page won't interact with the framework.

Access the framework

The plan is to use the framework's notify method to display in-product notifications of the request's success or failure to the user.

You access the framework from the HTML page currently loaded in the iframe, not from the server directly. The lingua franca (the common language) is JavaScript. You use JS with the ZAF SDK, a JavaScript library, to create a client object that lets you communicate with the framework.

Display a success message

If the request to Asana is successful, display a success notification with the list page.

  1. In the list_tasks.tpl template, import the ZAF SDK by adding the following script tag before the closing body tag:

    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
  2. Immediately following it, add another script tag and use it to create the ZAF client and invoke the framework's notify function:

    <script>  var client = ZAFClient.init();  client.invoke('notify', 'Request sucessful!');</script>

    Note that creating the client would fail here without the app parameters in the link that the user clicked on the start page to get this page.

Display an error message

If the request fails (if it returns anything but a 200 status code), then switch back to the start page and display an error message.

  1. In the show_tasks() function in app.py, make the following changes (in bold) to the error-handling else clause:

        ...
    else:
        msg = 'Problem with the request: {} {}'.format(r.status_code, r.reason)
        qs = request.get_cookie('my_app_params')
        return template('start', qs=qs, error_msg=msg)

    If there's an error, the app renders the start.tpl template instead of list_tasks.tpl, and passes the error message to the template. Because the template expects a qs parameter for the list link (href="list?{{qs}}" ...), the app retrieves the query string from the cookie and passes it to the template too.

  2. In the start.tpl template, add the following template conditional block before the closing body tag:

    % if defined('error_msg'):    \ JS here% end

    This checks to see if a template parameter named error_msg exists in the template namespace. It exists only if there was a request error.

  3. Replace the \\ JS here placeholder in the conditional block with the following script tags:

    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script><script>  var msg = '{{error_msg}}';  var client = ZAFClient.init();  client.invoke('notify', msg, 'error');</script>

    The first tag imports the ZAF SDK. The second tag assigns the error message to a variable, creates a client object, and invokes the framework's notify function to display the message.

To test the changes, introduce a typo in the Asana endpoint URL. Remember to fix the URL when you're finished testing.

Separating the JavaScript

It's good practice to separate your JavaScript from your HTML in a server-side app too.

  1. In the list_tasks.tpl template, replace all the script tags with the following script tags:

    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script><script>  var action = 'notifySuccess';</script><script src="js/main.js"></script>

    The first tag still imports the ZAF SDK. The second sets an action for your JS script. The third imports your custom script, which you'll create soon.

  2. In the start.tpl template, replace the script tags in the conditional block with the following script tags:

    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script><script>  var action = 'notifyFailure';  var msg = '{{error_msg}}';</script><script src="js/main.js"></script>
  3. In the app_remote folder, create a folder named static/js.
  4. In the js folder, create a file named main.js.

    The folder structure should look as follows:

    /app_remote  /views    ...  /static    /js      main.js
  5. In main.js, add the following JavaScript:

    function init() {  var client = ZAFClient.init();  switch (action) {    case 'notifySuccess':      client.invoke('notify', 'Request successful!');      break;    case 'notifyFailure':      client.invoke('notify', msg, 'error');      break;  }}
    window.addEventListener('load', init, false);

    The event listener on the last line runs the init() function after the page finishes loading. The function creates a client object, then displays different notifications depending on value of the action variable.

  6. In the app.py file, add the following route to handle requests for any file in the js folder:

    @route('/js/<filename>')def send_js(filename):    return static_file(filename, root='static/js')
  7. Because you introduced the Bottle static_file() method, add it to the list of imported Bottle methods at the top of the file:

    from bottle import route, run, template, request, static_file

Code complete

The server-side code of your web app should look like this:

The client-side code should look like this:

In this tutorial, you updated the web app to access framework APIs to display in-product notifications in Zendesk Support. In the next and final part, you'll deploy the web app on a server and install the basic framework app in Zendesk Support. Click to go to the next tutorial now: Part 5: Deploy the app.