Building a server-side app - Part 4: Accessing framework APIs
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:
- Planning the update
- Retrieving and storing the app parameters
- Appending the parameters to link URLs
- Accessing the framework
This tutorial is the fourth part of a series on building a server-side Zendesk app:
- Part 1: Core concepts
- Part 2: Displaying server-side content in a Zendesk app
- Part 3: Accessing external APIs
- Part 4: Accessing framework APIs - YOU ARE HERE
- Part 5: Securing the app
- Part 6: Deploying the app
Planning the update
The error messages displayed by the app are unsatisfactory — they're just plain text on a blank page.
In this section, you'll use the ZAF client's notify action to display success or error messages with in-product notifications.
Retrieving and storing the app parameters
The ZAF SDK needs two parameters to create a client object that can interact with ZAF: the URL of the Zendesk Support instance (its origin
) and the Zendesk app's unique id (its app_guid
).
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%example.zendesk.com
To retrieve the Zendesk app parameters from the query string and store them:
-
In app.py, modify the sidebar route as follows (highlighted):
@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 agent's browser for other pages.
- The first bolded line uses the Bottle
-
Because you introduced the
request
andresponse
Bottle objects, make sure to import them at the top of the file:
...
from bottle import route, run, template, request, response
...
Appending the parameters to link URLs
To make the app parameters available to the page that will interact with the Apps 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 ZAF API to display an in-product notification that the request was successful. The start.tpl template includes a link to that page:
<a class="btn btn-default btn-block" href="list" role="button"
>List tech note ideas</a
>
To append the app parameters to the link:
-
In app.py, modify the sidebar route as follows (highlighted):
@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.
-
In the start.tpl template, append the query string to the URL of the list page:
... href="list?{{qs}}" ...
When an agent 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 ZAF.
Accessing the framework
The plan is to use the ZAF client's notify
action to display in-product notifications of the request's success or failure to the agent.
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 ZAF.
Displaying a success message
If the request to Asana is successful, display a success notification with the list page.
-
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>
-
Immediately following it, add another script tag and use it to create the ZAF client and invoke the client's
notify
action:<script>
const client = ZAFClient.init()
client.invoke("notify", "Request successful!")
</script>
Note that creating the client would fail here without the Zendesk app parameters in the link that the agent clicked on the start page to get this page.
Displaying 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.
-
In the
show_tasks()
function in app.py, make the following changes (highlighted) to the error-handlingelse
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 web 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. -
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.
-
Replace the
\\ JS here
placeholder in the conditional block with the followingscript
tags:<script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
<script>
const msg = "{{error_msg}}"
const 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
action to display the message.
To test the changes, introduce a typo in the Asana endpoint URL in app.py and restart the Bottle development server. 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.
-
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>
const 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.
-
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>
const action = "notifyFailure"
const msg = "{{error_msg}}"
</script>
<script src="js/main.js"></script>
-
In the app_remote folder, create a folder named static/js.
-
In the js folder, create a file named main.js.
The folder structure should look as follows:
/app_remote
/views
...
/static
/js
main.js
-
In main.js, add the following JavaScript:
function init() {
const 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 and displays different notifications based on the value of the action variable. -
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')
...
-
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, response, static_file
...
To test the updated web app in Zendesk Support:
-
Restart the Bottle development server.
-
In the Zendesk Support instance running the local ZCLI server, refresh the app and click List tech note ideas.
A notification appears:
You've updated the web app to access ZAF APIs to display in-product notifications in Zendesk Support. In the next tutorial, you'll update the web app so that it only responds to verified requests from your Zendesk app. See Part 5: Securing the app.