cancel
Showing results for 
Search instead for 
Did you mean: 

Using BloX to call Sisense APIs

wforw
7 - Data Storage
7 - Data Storage

Hello,

Has anybody managed to use a BloX action button to call the Sisense API, such as build ElastiCube?

I have tried searching online and see that I need to use type "Action.Submit", action is "post" and provide the url and data parameters. I think the issue I am having is exactly how I provide the data. My code is this:

{

"actions": [
{
"type": "Action.Submit",
"action": "post",
"title": "Refresh Data",
"url": "https://[url].com/api/v2/builds/",
"data": {
"datamodelId": "fb836d1a-2337-475a-a664-39dc58b49268",
"buildType": "full",
"rowLimit": 0,
"schemaOrigin": "latest"
}
}
]
}
 
but I have also experimented with other values in the data. I have included "headers" in the data with "accept": "application/json", "Content-Type": "application/json" and "X-XSRF-TOKEN:" with bearer token, but every thing I do gives me 500 error in the dev console.
 
So my questions is, has anybody managed to get this working? And able to provide guidance on where I've gone wrong? I am using Linux 2022.2.
 
Thanks in advance
 
1 ACCEPTED SOLUTION

sneak
8 - Cloud Apps
8 - Cloud Apps

Hi @wforw, how's it going? This topic is something I've been meaning to figure out for a while, so when I saw your question, I decided to give it a go. I've found a working solution, but it is a little complicated, I'll be honest. I'll try to lay it out as simply as possible. In any case, let's dig in:

Key notes:

  1. My implementation was with an Elasticube, not a Live model. There are a lot of nuances I didn't even scratch the surface on, which you can read about here: (https://sisense.dev/guides/rest/datamodels.v2.html#resources)
  2. My implementation uses local accounts, not Single Sign On (SSO). For some info on the differences for Sisense API usage, see here: (https://support.sisense.com/kb/en/article/sso-sisense-api-faqs)
  3. Without insight into the JavaScript that's powering the BloX "Submit" Action Type, it's hard for me to tell how to include the right data in the right format for the call (if it's possible, I'm not sure).
  4. BloX let's us create custom actions, which is how I managed to make this on-demand ElastiCube build functionality work from a BloX button.
  5. All Sisense REST API calls require an API token to be included in the call
  6. Each Sisense user has a unique associated API token, which updates any time a new session is started -- newer versions of Sisense allow you to see API tokens directly from the Admin/Users section. See here: (https://sisense.dev/guides/rest/using-rest-api.html#getting-the-api-token-from-user-profiles)
  7. Testing the APIs via the swaggerUI interface in the Admin tab is extremely useful. See here: (https://sisense.dev/guides/rest/using-rest-api.html#using-the-api-documentation)
  8. The swaggerUI also let's you get your API token (if you're not on a Sisense version as mentioned in bullet 4). See here: (https://sisense.dev/guides/rest/using-rest-api.html#getting-the-api-token-from-the-authentication-ap...)

 

Requirements for making a JavaScript API fetch function:

  1. API Request Method (e.g. POST, GET, PUT, PATCH, DELETE)
  2. API Request URL (https://[your-url]/api/v2/builds)
  3. Headers
    1. X-XSRF-TOKEN: See 'API Token' above
    2. Accept parameter: What type of data the API call should interpret
    3. Content-Type parameter: What type of data the API call should return
  4. Body

 

Custom Action:

If you've not made a custom BloX action yet, start here: (https://support.sisense.com/kb/en/article/sisense-blox-custom-actions-guide)

Our custom action uses the following JavaScript to perform its duties:

 

 

//Store information from the BloX "Editor" panel into local variables we can pass to the API fetch function

var modelUid = payload.modelUid
var buildType = payload.buildType
var rowLimit = payload.rowLimit
var schemaOrigin = payload.schemaOrigin
var apiAccept =payload.apiAccept
var apiContentType = payload.apiContentType
var apiToken = payload.apiToken
var apiUrl = payload.apiUrl
var apiMethod = payload.apiMethod 
var xhr = new XMLHttpRequest();


//Uncomment the following section to debug variable storage via the browser dev console
/*
console.log(modelUid)
console.log(buildType)
console.log(rowLimit)
console.log(schemaOrigin)
console.log(apiAccept)
console.log(apiContentType)
console.log(apiToken)
console.log(apiUrl)
console.log(apiMethod)
*/

//Now let's create the fetching function

//We're using an async function to listen for the API response, that way we can tell the user whether it's successful or not

async function makeRequest() {
//try to fetch, using all the variables we've stored from the BloX editor panel. Catch errors below

    try {
        const response = await fetch(apiUrl, {
            method: apiMethod,
            headers: {
                'accept': `${apiAccept}`, //must be passed with the backticks and dollar sign convention which tells JS to bring in the variable as a string literal
                'Content-Type': `${apiContentType}`,
                'X-XSRF-TOKEN': apiToken  //note this variable is not called as a string literal the way the others are, with backticks and dollar signs.
            },
            body: JSON.stringify({
                'datamodelId': `${modelUid}`,
                'buildType': `${buildType}`,
                'rowLimit': rowLimit, //also not a string literal, because this is an integer variable
                'schemaOrigin': `${schemaOrigin}`
            })
        });
        
//Now show an alert based on how the API call went, pulling the API method (call type) from our local variables, and user info from the prism global variables.
        window.alert(apiMethod + " call initiated by " + prism.user.firstName + ", " + prism.user.roleName + ". Response: " + response.status);
    } catch (err) { //and catch errors here
        window.alert(apiMethod + " call initiated by " + prism.user.firstName + ", " + prism.user.roleName + ". Response: " + err);;
    }
}

//now call the cool function we just created above
makeRequest()

 

 

Next, BloX will have you create the JSON structure that allows the designer to use this action in a BloX widget. Ours will look like this:

 

 

{
    "type": "rest_api_js",
    "title": "title",
    "modelUid": "modelUid",
    "buildType": "buildType",
    "rowLimit": 0,
    "schemaOrigin": "schemaOrigin",
    "apiAccept": "apiAccept",
    "apiContentType": "apiContentType",
    "apiToken": "apiToken",
    "apiUrl": "apiUrl",
    "apiMethod": "apiMethod"
}

 

 

Now add your specific details to the "Editor" panel in your BloX widget:

 

 

{
    "style": "",
    "script": "",
    "title": "",
    "showCarousel": true,
    "body": [],
    "actions": [
        {
            "type": "rest_api_js",
            "title": "Your Button Text",
            "modelUid": "your-datamodel-oid",
            "buildType": "full",
            "rowLimit": 0,
            "schemaOrigin": "latest",
            "apiAccept": "application/json",
            "apiContentType": "application/json",
            "apiToken": "your-very-long-hashed-api-token-here",
            "apiUrl": "https:your_url.com/api/v2/builds",
            "apiMethod": "POST"
        }
    ]
}

 

 

Note: You can play around with the buildType, rowLimit, schemaOrigin, and modelUid as suits your needs (see here: https://sisense.dev/guides/rest/datamodels.v2.html#building-extract-datamodels). But, in this use case, apiAccept, apiContentType, and apiMethod should be used exactly as above. apiToken and apiUrl will be up to you to determine and input based on your environment.

 

Final notes/caveats:

  1. Live models vs. Elasticubes, Sisense accounts vs. SSO, these differences are not trivial and may render my solution useless without debugging. But, with local accounts and Elasticubes, this works for me. 
  2. Passing the API Token as a string into the BloX editor is not optimal, because the token will (I think) update on a new session (meaning you would need to replace the token in the BloX widget design each session). However: if you are successfully authenticated with SSO, Sisense documentation indicates that an API Token is not necessary for making calls. Additionally: with local accounts, you can use the authentication API (api/v1/authentication/login) to get a users API Token by passing username and password. See here: (https://sisense.dev/guides/rest/using-rest-api.html#getting-the-api-token-from-the-authentication-ap...). So, using similar methods as above, you could make a custom action to make an initial API call that gets the users apiToken, then feed that to the custom action above (or ideally, do both tasks in one custom function, by building on above my script)
  3. I'm not a JavaScript expert, by any means. There is always a better way, and that is especially true when I'm the one writing in JS. But, I hope this helps get you further down the road towards a solution that enhances your environment 🙂

Let's build up this developer community!

View solution in original post

2 REPLIES 2

sneak
8 - Cloud Apps
8 - Cloud Apps

Hi @wforw, how's it going? This topic is something I've been meaning to figure out for a while, so when I saw your question, I decided to give it a go. I've found a working solution, but it is a little complicated, I'll be honest. I'll try to lay it out as simply as possible. In any case, let's dig in:

Key notes:

  1. My implementation was with an Elasticube, not a Live model. There are a lot of nuances I didn't even scratch the surface on, which you can read about here: (https://sisense.dev/guides/rest/datamodels.v2.html#resources)
  2. My implementation uses local accounts, not Single Sign On (SSO). For some info on the differences for Sisense API usage, see here: (https://support.sisense.com/kb/en/article/sso-sisense-api-faqs)
  3. Without insight into the JavaScript that's powering the BloX "Submit" Action Type, it's hard for me to tell how to include the right data in the right format for the call (if it's possible, I'm not sure).
  4. BloX let's us create custom actions, which is how I managed to make this on-demand ElastiCube build functionality work from a BloX button.
  5. All Sisense REST API calls require an API token to be included in the call
  6. Each Sisense user has a unique associated API token, which updates any time a new session is started -- newer versions of Sisense allow you to see API tokens directly from the Admin/Users section. See here: (https://sisense.dev/guides/rest/using-rest-api.html#getting-the-api-token-from-user-profiles)
  7. Testing the APIs via the swaggerUI interface in the Admin tab is extremely useful. See here: (https://sisense.dev/guides/rest/using-rest-api.html#using-the-api-documentation)
  8. The swaggerUI also let's you get your API token (if you're not on a Sisense version as mentioned in bullet 4). See here: (https://sisense.dev/guides/rest/using-rest-api.html#getting-the-api-token-from-the-authentication-ap...)

 

Requirements for making a JavaScript API fetch function:

  1. API Request Method (e.g. POST, GET, PUT, PATCH, DELETE)
  2. API Request URL (https://[your-url]/api/v2/builds)
  3. Headers
    1. X-XSRF-TOKEN: See 'API Token' above
    2. Accept parameter: What type of data the API call should interpret
    3. Content-Type parameter: What type of data the API call should return
  4. Body

 

Custom Action:

If you've not made a custom BloX action yet, start here: (https://support.sisense.com/kb/en/article/sisense-blox-custom-actions-guide)

Our custom action uses the following JavaScript to perform its duties:

 

 

//Store information from the BloX "Editor" panel into local variables we can pass to the API fetch function

var modelUid = payload.modelUid
var buildType = payload.buildType
var rowLimit = payload.rowLimit
var schemaOrigin = payload.schemaOrigin
var apiAccept =payload.apiAccept
var apiContentType = payload.apiContentType
var apiToken = payload.apiToken
var apiUrl = payload.apiUrl
var apiMethod = payload.apiMethod 
var xhr = new XMLHttpRequest();


//Uncomment the following section to debug variable storage via the browser dev console
/*
console.log(modelUid)
console.log(buildType)
console.log(rowLimit)
console.log(schemaOrigin)
console.log(apiAccept)
console.log(apiContentType)
console.log(apiToken)
console.log(apiUrl)
console.log(apiMethod)
*/

//Now let's create the fetching function

//We're using an async function to listen for the API response, that way we can tell the user whether it's successful or not

async function makeRequest() {
//try to fetch, using all the variables we've stored from the BloX editor panel. Catch errors below

    try {
        const response = await fetch(apiUrl, {
            method: apiMethod,
            headers: {
                'accept': `${apiAccept}`, //must be passed with the backticks and dollar sign convention which tells JS to bring in the variable as a string literal
                'Content-Type': `${apiContentType}`,
                'X-XSRF-TOKEN': apiToken  //note this variable is not called as a string literal the way the others are, with backticks and dollar signs.
            },
            body: JSON.stringify({
                'datamodelId': `${modelUid}`,
                'buildType': `${buildType}`,
                'rowLimit': rowLimit, //also not a string literal, because this is an integer variable
                'schemaOrigin': `${schemaOrigin}`
            })
        });
        
//Now show an alert based on how the API call went, pulling the API method (call type) from our local variables, and user info from the prism global variables.
        window.alert(apiMethod + " call initiated by " + prism.user.firstName + ", " + prism.user.roleName + ". Response: " + response.status);
    } catch (err) { //and catch errors here
        window.alert(apiMethod + " call initiated by " + prism.user.firstName + ", " + prism.user.roleName + ". Response: " + err);;
    }
}

//now call the cool function we just created above
makeRequest()

 

 

Next, BloX will have you create the JSON structure that allows the designer to use this action in a BloX widget. Ours will look like this:

 

 

{
    "type": "rest_api_js",
    "title": "title",
    "modelUid": "modelUid",
    "buildType": "buildType",
    "rowLimit": 0,
    "schemaOrigin": "schemaOrigin",
    "apiAccept": "apiAccept",
    "apiContentType": "apiContentType",
    "apiToken": "apiToken",
    "apiUrl": "apiUrl",
    "apiMethod": "apiMethod"
}

 

 

Now add your specific details to the "Editor" panel in your BloX widget:

 

 

{
    "style": "",
    "script": "",
    "title": "",
    "showCarousel": true,
    "body": [],
    "actions": [
        {
            "type": "rest_api_js",
            "title": "Your Button Text",
            "modelUid": "your-datamodel-oid",
            "buildType": "full",
            "rowLimit": 0,
            "schemaOrigin": "latest",
            "apiAccept": "application/json",
            "apiContentType": "application/json",
            "apiToken": "your-very-long-hashed-api-token-here",
            "apiUrl": "https:your_url.com/api/v2/builds",
            "apiMethod": "POST"
        }
    ]
}

 

 

Note: You can play around with the buildType, rowLimit, schemaOrigin, and modelUid as suits your needs (see here: https://sisense.dev/guides/rest/datamodels.v2.html#building-extract-datamodels). But, in this use case, apiAccept, apiContentType, and apiMethod should be used exactly as above. apiToken and apiUrl will be up to you to determine and input based on your environment.

 

Final notes/caveats:

  1. Live models vs. Elasticubes, Sisense accounts vs. SSO, these differences are not trivial and may render my solution useless without debugging. But, with local accounts and Elasticubes, this works for me. 
  2. Passing the API Token as a string into the BloX editor is not optimal, because the token will (I think) update on a new session (meaning you would need to replace the token in the BloX widget design each session). However: if you are successfully authenticated with SSO, Sisense documentation indicates that an API Token is not necessary for making calls. Additionally: with local accounts, you can use the authentication API (api/v1/authentication/login) to get a users API Token by passing username and password. See here: (https://sisense.dev/guides/rest/using-rest-api.html#getting-the-api-token-from-the-authentication-ap...). So, using similar methods as above, you could make a custom action to make an initial API call that gets the users apiToken, then feed that to the custom action above (or ideally, do both tasks in one custom function, by building on above my script)
  3. I'm not a JavaScript expert, by any means. There is always a better way, and that is especially true when I'm the one writing in JS. But, I hope this helps get you further down the road towards a solution that enhances your environment 🙂

Let's build up this developer community!

wforw
7 - Data Storage
7 - Data Storage

Hi @sneak, this is great, thank you so much! I didn't think about creating a new action but seems like it's the way to go. This has helped me a lot