Today our special guest blogger is Øystein Kolsrud - a Software Architect at Qlik, part of the Customer First team which serves as a bridge between support and R&D. He is one of the main developers behind the .NET SDK, and has extensive experience with the Qlik Sense APIs.
Øystein has prepared a series of technical posts that outline the Qlik Engine API fundamentals. That we will release over the next coming weeks in the Qlik Design Blog.
Part 1 covers JSON-RPC fundamentals. Please post your comments and questions below and I'll make sure that Øystein is aware so he can respond appropriately.
Over the years I have answered a number of questions on Qlik Community relating to the Qlik Sense APIs. My main focus has typically been on the .NET SDK, but many times my answers to questions have not really concern the .NET implementation all, but are in fact relevant for all interactions with the engine, no matter what technology is used for the communication. With this and future blog posts, I will try to explain some of the fundamentals of how the engine API works. My hope is that these posts will be of interest to anyone who would like to become more familiar with the inner workings of the engine API. And though most libraries used for handling engine communication do their best to hide many of the low level concepts I will describe, it is not uncommon that a certain behavior is difficult to explain without going back to the basics. At least, I hope I will in the future be able to reference to these posts as the need arises. So let's start, and the first thing I'll do is to go back to the absolute basics: JSON-RPC
RPC is short for "Remote Procedure Call", and JSON (much like XML) is a way to structure data as strings. I will not be covering more information about JSON, as there are plenty of resources on the web for doing that, suffice to say that a well structured JSON string can take any of four types: String, Number, Array or Object. A JSON object consists of a set of properties that have a name and an associated JSON string, while an array is simply a list of JSON strings. The very first thing that happens when you connect to the engine is that you will receive a message from the engine with the following text over the websocket:
This message is a JSON object describing a method call, or a "Remote Procedure Call" if you like. It is a request sent from the server to the connecting client telling it to perform a particular operation. The three top level properties in the message are all part of the standard specification for the JSON-RPC protocol and are not Qlik-specific constructs. In fact the whole message conforms to the JSON-RPC Request object specification of the JSON-RPC specification. More details about that standard can be found here:
The first property named "jsonrpc" simply specifies which version of the JSON-RPC protocol that is being used and will always by "2.0" when interacting with Qlik Sense. The two other properties, "method" and "parameters", contain information about what operation the sender is requesting the receiver to perform. Now it is totally up to the receive to choose what do do with this message, but the most important information in this specific call is the "mustAuthenciate" property of the parameters, which, as the name implies, states whether or not the client must take any particular actions in order to become authentication. The fact that the value is "false" here is a good thing as it means that we are all good to go and ready to start sending requests to the server.
Note that the values of the parameters of the "OnAuthenticationInformation" call will differ depending on what authentication mechanism is used to connect to the server. The particular format show above is what you get when you connect successfully through a virtual proxy using static header authentication. I will not delve deeper into the authentication flow in these blog posts, as that is a quite big area and would probably benefit from a set of blog posts on its own. I chose static header authentication for this post simply because it is usually one of the easiest ones to get up and running.
Sending our first message
Now that we have an open connection to the engine, we are ready to send our first message. When connection through the .NET SDK, the first message to be sent will always be a particular RPC that simply ensures that the connection is open. This is necessary since, for some authentication methods, the "OnAuthenticationInformation" message will be sent by the server only after a valid message has been received from the client. Doing this first call therefore has the effect that we capture any authentication failures as early as possible after opening the connection. The call looks like this:
We have already discussed the "method" and "params" properties in the previous section, and their values in this case conform to the specification of the engine API method called "QTProduct" as defined in the engine API reference documentation:
But the call also introduces two new concepts that are very important to understand how the engine API works. The first of these concepts is the "request id".
RPC request ID
The "id" property of an RPC request object is used as an identifier to connect an RPC request with a response. Upon receiving an RPC request, the engine will start processing that request, and when it is done, it will send back a response to the client with the result. This response will contain the same value for the "id" property as the one used in the request. For the "QTProduct" request above, the response will typically look like this:
The contents of this message is called an "RPC response object" and also conforms to the JSON-RPC specification. Note that the value of the property "id" is the same as the value used in the request in the previous section. This is important as specifying the id makes it possible to have multiple request active in parallel without worrying about the order in which the responses are returned. We could for instance send the two following requests to the engine:
It is up to the client to connect the responses to the correct requests based on he "id" property, and handling this is one of the key things that libraries like Enigma and the .NET SDK take care of. A user of such libraries can safely perform concurrent operations and let the libraries take care of the low level matching of requests and responses due to this "id" property.
I mentioned earlier that the call to "QTProduct" introduced two important concept out of which the "id" is the first. The second important concept is the "handle", and handles will be the primary topic of the next blog post in this series.