Qlik Community

Qlik Design Blog

All about product and Qlik solutions: scripting, data modeling, visual design, extensions, best practices, etc.

Employee
Employee

My friend, Øystein Kolsrud - Software Architect at Qlik, is back with part 2 of the Qlik Engine API fundamentals.

Introduction

During Part 1 of this series, the focus was on the basic JSON-RPC aspects of the engine API.

This post will focus on a part of the API that is engine specific and fundamental to how the engine API works: Handles.

The "Global" handle

Let's go back to the call to "QTProduct" that was used as example in Part 1. The request object for that method looks like this:

{
"jsonrpc": "2.0",
"id": 0,
"method": "QTProduct",
"handle": -1,
"params": []
}

The four properties "jsonrpc", "id", "method" and "params" are all part of the standard JSON-RPC protocol, but the fifth one, "handle" is not. The engine API uses this property to define the context in which the method should be executed. '-1' is a special handle that identifies what is called the "Global" context which is a context that is associated with the Qlik Sense system it self. The methods available in this context are all relates to system wide behavior such as properties of the server where the engine runs. It is also the entry point for accessing Qlik Sense functionality, and arguably the most important endpoint in the global context is the method "OpenDoc". This endpoint tells the engine to make a specified app ready for interaction, which means loading the app from disk into memory on the server (if it is has not already done so). The specification for this method can be found here:

https://help.qlik.com/en-US/sense-developer/June2020/APIs/EngineAPI/services-Global-OpenDoc.html 

And a typical RPC request object for this method can look like this:

{
"jsonrpc": "2.0",
"id": 1,
"method": "OpenDoc",
"handle": -1,
"params": ["eca1e963-d9e1-405c-9262-1179f19f0de3"]
}

The handle is set to '-1' to ensure that the method is executed in the global context, and the value passed as parameter is the ID of the App that is to be opened. (The name "OpenDoc" stems from QlikView where the entity corresponding to a Qlik Sense App is called a Document.)

Working with handles

Upon successfully opening the requested app, the server will respond with a response object that looks something like this:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"qReturn": {
"qType": "Doc",
"qHandle": 1,
"qGenericId": "eca1e963-d9e1-405c-9262-1179f19f0de3"
}
},
"change": [1]
}

The value of the property "qReturn" is an object of the type "ObjectInterface" as defined in the engine reference documentation for the method. The most important member of that object is the one named "qHandle" which informs the client that the engine has assigned a handle to the newly opened app and that this handle should be used when interacting with the engine in the context of that app. If we for instance want to evaluate the sum of sales for the app, then we can use the following method:

https://help.qlik.com/en-US/sense-developer/June2020/APIs/EngineAPI/services-Doc-Evaluate.html 

The request object for this method would look like this:

{
"jsonrpc": "2.0",
"id": 2,
"method": "Evaluate",
"handle": 1,
"params": ["Sum(Sales)"]
}

Notice that the "handle" property is now set to '1' instead of '-1' which was the case for previous calls. It does not make sense to run this method in the server context, as all evaluations must be done in the context of an app. And correspondingly, calling the "OpenDoc" method with the handle '1' would not work, as it does not make sense to open an app in the context of an app. If you try to do so, then the engine will simply return an error.

But given that the app was successfully opened and that there is indeed a field with the name "Sales" in that app, then the engine would respond to the "Evaluate" method with an object like this where the "qReturn" value is the result of calculating the sum of sales:

{
"jsonrpc": "2.0",
"id": 2,
"result": {
"qReturn": "240491"
}
}

Our first selection

The above call to "Evaluate" computed the sum of sales for the app. However, it is typically of more interest to perform computations given a specific subset of the data of the app, for instance per sales person. There are many ways to accomplish this in Qlik Sense, but I will in this example first perform a selection in a specific field, and then use the "Evaluate" method.

To perform a selection in a field, we will first need to get a handle to use for expressing the context of the field where we want to perform the selection. This is exactly the purpose of the method "GetField":

https://help.qlik.com/en-US/sense-developer/June2020/APIs/EngineAPI/services-Doc-GetField.html 

A call to this method would look like this:

{
"jsonrpc": "2.0",
"id": 3,
"method": "GetField",
"handle": 1,
"params": ["SalesRep"]
}

And the engine will reply to this message by sending a new object interface for the field named "SalesRep":

{
"jsonrpc": "2.0",
"id": 3,
"result": {
"qReturn": {
"qType": "Field",
"qHandle": 2
}
}
}

We can now use the handle '2' for interacting with this field, and I will use the method "Select" to perform a selection of a particular employee:

https://help.qlik.com/en-US/sense-developer/June2020/APIs/EngineAPI/services-Field-Select.html 

The call will look like this, and notice that the handle is now set to '2' as that was the handle we got in response to the "GetField" method:

{
"jsonrpc": "2.0",
"id": 4,
"method": "Select",
"handle": 2,
"params": ["Amalia Craig"]
}

If the selection is executed successfully, then the engine will reply with the return value 'true' like this:

{
"jsonrpc": "2.0",
"id": 4,
"result": {
"qReturn": true
},
"change": [1, 2]
}

Selection state

The "Select" method has an important side effect, namely to change the state of the app to reflect that a certain value has been selected. This was, of course, the whole purpose of performing the call in the first place. The property in the response named "change" contains a list of handles that might have been affected by his action, and in our case, the affected handles are '1' which is the handle for the app, and '2' which is the handle for the field where we performed the selection. Now that we have performed the selection, we can call the "Evaluate" method in this new state:

{
"jsonrpc": "2.0",
"id": 5,
"method": "Evaluate",
"handle": 1,
"params": ["Sum(Sales)"]
}

Which will return us the sum of sales computed for the sales rep "Amanda Craig":

{
"jsonrpc": "2.0",
"id": 5,
"result": {
"qReturn": "27552"
}
}

Summary

Handles are used in the engine API to provide method context. Methods such as "OpenDoc" and "GetField" return a handle that the client can use to access functionality associated with the requested object. These handles are used both by the client to request operations on the server, but also by the server to inform the client that certain events have occurred that affects the state of the objects the handles represent.