At the Analytics & Information Management service line at Deloitte, we are working a lot with QlikView and Qlik Sense. After a recent training on the engine API, one of the things that I have been working on is trying to connect a simple HTML5 web app to a Qlik Sense Server using the Enigma.js javascript library.

All of the documentation I found so far seems to deal with the situation where you connect your web app with a locally running Qlik Sense Desktop instance. In this case, you don't have to worry about authentication and getting a qlikTicket to be used in the communication.

Connecting to dashboard on Qlik Sense Desktop

When using Enigma.js for connecting to a running instance of Qlik Sense Desktop, you need to have a copy of enigma.js or enigma.min.js and typically just have the following code:

//The base config with all details filled in 
var config = {
    //Take the schema object
    schema: schema,
    //The ID of your app on the qlik sense server
    //If not provided, you can manually open an app later on
    appId: "My Demo App.qvf",
    session:{
        //Use localhost:4848 to connect to local Qlik Sense Desktop
        host:"localhost",
        port: 4848,
        prefix: "",
        unsecure: true,
    },
}



//Now that we have a config, use that to connect to the QIX service.
enigma.getService("qix" , config).then(function(qlik){

    //In case you did not provide appID in the config, now you can open
    //an app by using the openApp function on the qlik object
    //    qlik.global.openApp( [APP_ID] )
    //
    //In case you did provide an appID in the config, you can directly
    //access it via the app object in the qlik object and use this to 
    //create session objects with the createSessionObject function
}

With the above code and having a local Qlik Sense Desktop instance running on your computer within a very short time, you can have your own custom visuals using libraries like amCharts or D3 using the data from the Qlik Sense Dashboard.

Connecting to dashboard on Qlik Sense Server

After successfully being able to create some custom D3 visualizations around a local Qlik Sense dashboard, I wanted to see if I could connect to a dashboard running on a Qlik Sense Server. This turned out to take a little bit more effort... :)

First of all, it is important that you make sure you have a recent version of the enigma.js library. A big part of my struggle to get this working was caused by the fact I was using an older version of the enigma.min.js library, more about this later.

An easy way to get the latest version of the enigma.min.js library file is to use npm as follows:

npm install enigma.js --save

After that, you can find the enigma.min.js in the directory node_modules/enigma.js/dist of the directory where you executed the npm command in.

To connect to a dashboard on a Qlik Sense Server, the original config object we used above needs to be extended a bit to deal with the authentication. A full description of the authentication and dealing with tickets can be found on the website of Qlik. In short, it boils down to the following process:

  1. The user opens your web app in the browser
  2. Enigma uses information from the config object to create WebSocket to the dashboard.
  3. Enigma fires a request to receive data over this WebSocket
  4. In a reply message over the WebSocket, the server indicates that the user must authenticate and provides the URL where the user can do so
  5. The browser will have to do a redirect to the URL obtained Step 4. After this new page is loaded, the user will have to provide the credentials on this page.
  6. The authentication service will validate these credentials and if successful, will provide a redirect to the reloadUri that is provided in the config supplied to enigma by the web app. However, the redirect will include an additional GET request parameter in the URL, namely a value for qlikTicket
  7. Your web app will need to retrieve the value of the qlikTicket from the URL after it is loaded and add this as a new piece of information to the config object that is used by Enigma. It must be added to the urlParams part of the config object.
  8. Using this updated version of the config object (i.e. with the qlikTicket information), you must again make a connection to the Qlik Server. The enigma library will add the qlikTicket value in the urlParams part of the config to the URL of the WebSocket, proving that you are authenticated.
  9. Get all data from the Qlik Sense dashboard and display it will cool D3 visuals :)

The problem for me to get this working was that I could not find any clear explanation about Step 7 and 8, especially in combination with Enigma.js.

Furthermore, it turned out that when I started I had an older version of enigma.min.js library file which did not append the additional items under urlParams to the WebSocket URL. This resulted in the situation where it was not possible to prove I was authenticated and therefore it went into and endless loop of trying to authenticate.

After getting the latest version of the library, checking the source code and looking at all requests in the developer console of Chrome, I felt like Cartman:
It all makes sense now

Combining all of the information above, i.e. adding the additional parameters to the config as well as the code required to add qlikTicket information if available, is given in the code block below.

//Helper function to get the value of a given key in a GET request
//We use this to get the value of the qlikTicket key in the current
//URL (i.e. after the authentication service redirected us back and
//added the qlikTicket parameter)
function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[\[\]]/g, "\\$&");
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
}



//The location of the current app. Note that it would also be possible
//to use location.href for this to just get the current value
//
//We need this URI to be able to tell the authentication service where
//the user needs to be redirected back to after the authentication has
//been done
//Please adjust this to the URL for your own web app
var currentApplicationURI = "http://localhost:8383" ; 


//Based on whether we have the details in the URL, we create the
//url parameters object
if (getParameterByName( "qlikTicket" , location.href) !== null ){
    var myURLParams = {
            reloadUri: currentApplicationURI,
            qlikTicket: getParameterByName( "qlikTicket" , location.href) ,
  
    }
}
else{
    var myURLParams = {
            reloadUri: currentApplicationURI,  
    }
}

//The base config with all details filled in 
var config = {
    //Take the schema object (see enigma.js website for details on how
    //to obtain this)
    schema: schema,
    //The ID of your app on the qlik sense server
    //If not provided, you can manually open an app later on after
    //you created the connection to the qix service
    //Please update this to the ID of your app on the Qlik Sense Server
    appId: "4a53b97c-73ed-48a2-9299-b479999f8791",
    session:{
        //Hostname and port of your qlik sense server
        //Update this to your own server details
        host:"Hostname.Of.Your.Qlik.Sense.Server",
        port: 80,
        prefix: "",
        unsecure: true,


        urlParams: myURLParams,
    },

    //Provide an authentication listener that will automatically redirect
    //to the authentication URL in case authentcation is required
    listeners: {
        "notification:OnAuthenticationInformation":  function ( authInfo ) {

            //If the message on the WebSocket indicates that the user must
            //authenticate, the message also provides a loginUri where the
            //authentication can be done. In order to do the actual 
            //authentication, we will redirect the current browser window
            //to the URI. After the authentication, the authenticator 
            //service will provide a redirect again to the provided 
            //reloadUri in the session urlParams
            if ( authInfo.mustAuthenticate ) {
                location.href = authInfo.loginUri;
            }
        },
    },
};

//Depending on whether the user is authenticated or not, the config will
//contain the qlikTicket information in the urlParams. If not, the call
//to getService will result in a redirect to authenticate. If the 
//qlikTicket is present, it will allow the user to get the data
enigma.getService("qix" , config).then(function(qlik){
    //In case you did not provide appID in the config, now you can open
    //an app by using the openApp function on the qlik object
    //    qlik.global.openApp( [APP_ID] )
    //
    //In case you did provide an appID in the config, you can directly
    //access it via the app object in the qlik object and use this to 
    //create session objects with the createSessionObject function
}

Instead of keeping the qlikTicket in the URL, another option for making it a bit cleaner would be to check for a qlikTicket parameter in the URL. If it exists, put this in a session cookie and then redirect again to the same page without the additional parameter in the URL and take the ticket information from the cookie.

Curious to hear whether there are other solutions on how to do the authentication and connect with a Qlik Sense server using Enigma. If you do have another way, let me know via a comment.