Scripting

As you can see from the above commands, Tigase uses pre-defined scripts for processing of all requests in HTTP API. Although the list may be small for now, this does mean with a little bit of Groovy scripting, you can create your own scripts to interpret REST commands and send them to the server!

All scripts for this purpose will be an implementation of class extending tigase.http.rest.Handler class. The URI of the scripts will be inside the scripts folder. For example, if the script uses TestHandler with a regular expression set to /test and is placed inside the scripts/rest/ the handler will be called with this URI: scripts/rest/test/.

Properties

When extending classes, you will need to set the following listed properties. regex:: Regular expression which is used to match request URI and parse parameters embedded in URI, for example:

-----
/\/([^@\/]+)@([^@\/]+)/
-----
requiredRole
Role of user required to be able to access this URI. Available values are null, user, and admin. Authentication for the script will be required if requiredRole is not null.
isAsync
If set to true, it will be possible to wait for results pending the arrival of IQ stanzas for instance.
Properties containing closures

Extended class should also set for closures for one or more of the following properties: execGet, execPut, execPost, execDelete, depending on which HTTP action is needed to support the following URI. Each closure has a dynamic arguments list generated at runtime. Below is a list of arguments passed to closure which describe how and when the list of arguments change.

service
Implementation of service interface which is used to access database or send/receove XMPP stanzas.
callback
Callback closures needs to be called to return data. However they only accept one argument of type string,byte[],Map. If data is Map tupe, it will be encoded to JSON or XML depending on Content-Type header.
user
Is passed only if requiredRole is not null. Otherwise this argument will not be in the argument list.
content
Parsed content of the request. This will not be in the list of arguments if Content-Lengeth of request is empty. If Content-Type is set to XML or JSON the return result will be as Map, otherwise it will be an instance of HttpServletRequest.
x
Additional arguments passed to callback are groups from regular expression matching the URI. Groups are not passed as a list, but are added to the list of arguments and next arguments.

If a property for corresponding HTTP action is not set, the component will return an HTTP 404 error.

Example Script

Lets have a look at a script that is included with the install package to get a better idea of how these scripts work. This script will GET a list of all registered account and output them according to an HTML file we will look at later.

import tigase.http.rest.Service
import tigase.xmpp.BareJID

/**
 * Class implements ability to retrieve by service administrator list of registered accounts
 * Handles requests for /rest/users/
 *
 * Example format of content of response:
 * <users><items><item>user1@domain</item><item>user2@domain</item></items><count>2</count></users>
 */
class UsersHandler extends tigase.http.rest.Handler {

    public UsersHandler() {
		description = [
			regex : "/",
			GET : [ info:'Retrieve list of registered user jids',
				description: """Request do not require any parameters and returns list of all registered user accounts on this server (for all vhosts).

Example response will look like this:
\${util.formatData([users:[items:['user1@example.com','user2@example.com','user1@example2.com'],count:3]])}
"""]
		];
        regex = /\//
        requiredRole = "admin"
        isAsync = false
        execGet = { Service service, callback, jid ->
            def users = service.getUserRepository().getUsers()
            callback([users:[items:users, count:users.size()]]);
        }
    }

}

As we can see, it’s a fairly short code. First it calls the rest service (required for all of the REST activity), and the BareJID handler. Next we extend out custom class to extend tigase.http.rest.Handler. Our author has provided a helpful description of the code to better describe it’s operation and expected result. The last section is the actual code that defines what will match our query, in this case anything, a requirement that an admin make the command, that the connection will terminate with results, and what commands will be passed.

The matching HTML, which will shape the output of the code is included here.

${ util.include('header', [title:'All users']) }
<table style="margin: auto;">
<tr>
<th>Avatar</th>
<th>User JID</th>
</tr>
<% result.users.items.each { user -> %>
<tr>
<td>
<img style="max-height: 50px; max-width: 50px;" src="${util.link("/avatar/" + user)}" />
</td>
<td>
<a href="${util.link("/user/"+user)}">${user}</a>
</td>
</tr>
<% } %>
</table>
${ util.include('footer') }

This file builds a table using the user fields from the GET request. NOTE: Not all scripts need a matching HTML file, basic requests may not need special handling.